chore: initial project import
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
This commit is contained in:
225
lib/i18n.ts
Normal file
225
lib/i18n.ts
Normal file
@ -0,0 +1,225 @@
|
||||
export type Locale = "id" | "en";
|
||||
|
||||
export const SUPPORTED_LOCALES: readonly Locale[] = ["id", "en"] as const;
|
||||
export const DEFAULT_LOCALE: Locale = "id";
|
||||
export const LOCALE_COOKIE = "wa_locale";
|
||||
|
||||
export type I18nSection = keyof typeof MESSAGES;
|
||||
export type MessageKey<S extends I18nSection> = keyof typeof MESSAGES[S];
|
||||
export type NavKey = MessageKey<"nav">;
|
||||
|
||||
const MESSAGES = {
|
||||
meta: {
|
||||
title: { id: "ZappCare Business Suite", en: "ZappCare Business Suite" },
|
||||
description: {
|
||||
id: "Platform WhatsApp Business inbox multi-tenant",
|
||||
en: "Multi-tenant WhatsApp business inbox platform"
|
||||
}
|
||||
},
|
||||
shell: {
|
||||
admin_title: { id: "Ruang Kerja Admin", en: "Admin Client Workspace" },
|
||||
admin_subtitle: {
|
||||
id: "Kelola inbox, contact, broadcast, dan operasional tim.",
|
||||
en: "Manage inbox, contacts, broadcasts, and team operations."
|
||||
},
|
||||
agent_title: { id: "Ruang Kerja Agent", en: "Agent Workspace" },
|
||||
agent_subtitle: {
|
||||
id: "Tangani conversation, follow-up, dan performa pribadi.",
|
||||
en: "Handle conversations, follow-ups, and personal performance."
|
||||
},
|
||||
super_admin_title: {
|
||||
id: "Platform Control Center",
|
||||
en: "Platform Control Center"
|
||||
},
|
||||
super_admin_subtitle: {
|
||||
id: "Kelola tenant, channel, billing, dan kesehatan platform.",
|
||||
en: "Manage tenants, channels, billing, and platform health."
|
||||
}
|
||||
},
|
||||
roles: {
|
||||
super_admin: { id: "Super Admin", en: "Super Admin" },
|
||||
admin_client: { id: "Admin Client", en: "Admin Client" },
|
||||
agent: { id: "Agent", en: "Agent" }
|
||||
},
|
||||
nav: {
|
||||
dashboard: { id: "Dashboard", en: "Dashboard" },
|
||||
shared_inbox: { id: "Shared Inbox", en: "Shared Inbox" },
|
||||
inbox: { id: "Inbox", en: "Inbox" },
|
||||
contacts: { id: "Kontak", en: "Contacts" },
|
||||
broadcast: { id: "Broadcast", en: "Broadcast" },
|
||||
templates: { id: "Template", en: "Templates" },
|
||||
team: { id: "Tim", en: "Team" },
|
||||
reports: { id: "Laporan", en: "Reports" },
|
||||
settings: { id: "Pengaturan", en: "Settings" },
|
||||
billing: { id: "Tagihan", en: "Billing" },
|
||||
audit_log: { id: "Audit Log", en: "Audit Log" },
|
||||
tenants: { id: "Tenant", en: "Tenants" },
|
||||
channels: { id: "Channel", en: "Channels" },
|
||||
security_events: { id: "Security Events", en: "Security Events" },
|
||||
webhook_logs: { id: "Webhook Logs", en: "Webhook Logs" },
|
||||
alerts: { id: "Peringatan", en: "Alerts" },
|
||||
quick_tools: { id: "Quick Tools", en: "Quick Tools" },
|
||||
performance: { id: "Performa", en: "Performance" },
|
||||
"new_chat": { id: "Obrolan Baru", en: "New Chat" },
|
||||
search: { id: "Cari", en: "Search" },
|
||||
logout: { id: "Keluar", en: "Logout" },
|
||||
global_search: { id: "Pencarian Global", en: "Global Search" },
|
||||
campaign: { id: "Kampanye", en: "Campaigns" }
|
||||
},
|
||||
common: {
|
||||
zappcare: { id: "ZappCare", en: "ZappCare" },
|
||||
business_suite: { id: "Business Suite", en: "Business Suite" },
|
||||
back_to_login: { id: "Kembali ke login", en: "Back to login" },
|
||||
search_placeholder: { id: "Cari wawasan, kontak, atau pesan...", en: "Search insights, contacts, or messages..." },
|
||||
new_chat_button: { id: "Obrolan Baru", en: "New Chat" },
|
||||
notifications: { id: "Notifikasi", en: "Notifications" },
|
||||
help: { id: "Bantuan", en: "Help" },
|
||||
shortcut: { id: "Aplikasi", en: "App shortcuts" }
|
||||
},
|
||||
login: {
|
||||
title: { id: "Selamat datang kembali", en: "Welcome back" },
|
||||
signin_subtitle: {
|
||||
id: "Masuk ke workspace Anda",
|
||||
en: "Sign in to your ZappCare Business Suite"
|
||||
},
|
||||
signin_label: { id: "Masuk", en: "Sign In" },
|
||||
signin_help: {
|
||||
id: "Gunakan akun yang sudah di-seed untuk masuk.",
|
||||
en: "Use seeded account credentials to sign in."
|
||||
},
|
||||
email_label: { id: "Email kerja", en: "Work email" },
|
||||
password_label: { id: "Password", en: "Password" },
|
||||
remember_label: { id: "Lupa akun?", en: "Forgot password?" },
|
||||
no_account_label: { id: "Belum punya akun?", en: "Don’t have an account?" },
|
||||
sso_button: { id: "Masuk dengan SSO", en: "Sign in with SSO" },
|
||||
contact_admin: { id: "Hubungi administrator", en: "Contact Administrator" },
|
||||
accept_invitation: { id: "Terima undangan", en: "Accept invitation" },
|
||||
work_email_placeholder: { id: "name@company.com", en: "name@company.com" },
|
||||
password_placeholder: { id: "••••••••", en: "••••••••" },
|
||||
sign_in_button: { id: "Masuk", en: "Sign In" },
|
||||
error_credentials_required: { id: "Email dan password wajib diisi.", en: "Email and password are required." },
|
||||
error_invalid_credentials: { id: "Email / password salah atau akun belum aktif.", en: "Invalid email/password or inactive account." },
|
||||
error_demo_disabled: { id: "Rute demo dinonaktifkan. Gunakan login biasa.", en: "Demo route is disabled. Use normal login." },
|
||||
error_rate_limited: { id: "Terlalu banyak percobaan login. Coba lagi beberapa saat.", en: "Too many login attempts. Try again in a few minutes." },
|
||||
forgot_action: { id: "Kirim tautan reset", en: "Send reset link" },
|
||||
forgot_success: {
|
||||
id: "Jika email valid dan aktif, kami sudah menyiapkan tautan reset.",
|
||||
en: "If this account exists and is active, we have sent a reset link."
|
||||
},
|
||||
reset_invalid_token: { id: "Token tidak valid atau sudah dipakai.", en: "This token is invalid or already used." },
|
||||
reset_expired: { id: "Token sudah kedaluwarsa. Silakan minta tautan baru.", en: "The token expired. Please request a new one." },
|
||||
password_mismatch: { id: "Konfirmasi password tidak cocok.", en: "Password confirmation doesn't match." },
|
||||
missing_email: { id: "Email wajib diisi.", en: "Email is required." }
|
||||
},
|
||||
placeholders: {
|
||||
operational_overview_title: { id: "Ikhtisar Operasional", en: "Operational overview" },
|
||||
operational_overview_desc: { id: "Ringkasan kerja harian tim dan inbox.", en: "Daily team and inbox operations summary." },
|
||||
operation_chart_note: {
|
||||
id: "Bagian grafik untuk volume message, workload agent, dan trend resolusi.",
|
||||
en: "Chart area for message volume, agent workload, and resolution trend."
|
||||
},
|
||||
priority_queue_title: { id: "Antrian Prioritas", en: "Priority queue" },
|
||||
priority_queue_desc: { id: "Conversation yang perlu tindakan cepat.", en: "Conversations that need quick action." },
|
||||
conversation_list_title: { id: "Daftar percakapan", en: "Conversation list" },
|
||||
conversation_list_desc: { id: "Semua, belum ditugaskan, ditugaskan, selesai.", en: "All, unassigned, assigned, resolved." },
|
||||
no_conversation_found: { id: "Tidak ada conversation ditemukan.", en: "No conversation found." },
|
||||
conversation_detail_title: { id: "Detail percakapan", en: "Conversation detail" },
|
||||
conversation_detail_desc: { id: "Header, timeline, composer, template, catatan.", en: "Header, timeline, composer, templates, notes." },
|
||||
select_to_reply: { id: "Pilih percakapan dari kiri untuk mulai menanggapi.", en: "Select a conversation from the left to start responding." },
|
||||
no_messages: { id: "Belum ada pesan.", en: "No messages yet." },
|
||||
reply_label: { id: "Balasan", en: "Reply" },
|
||||
reply_placeholder: { id: "Tulis balasan...", en: "Write a reply..." },
|
||||
send_reply: { id: "Kirim balasan", en: "Send reply" },
|
||||
context_panel_title: { id: "Panel kontekstual", en: "Context panel" },
|
||||
context_panel_desc: { id: "Penugasan, status, notes, tags, aktivitas.", en: "Assignment, status, notes, tags, activity." },
|
||||
select_context: { id: "Pilih percakapan untuk melihat konteks.", en: "Select a conversation to view context." },
|
||||
contact_label: { id: "Kontak", en: "Contact" },
|
||||
tags_label: { id: "Tags", en: "Tags" },
|
||||
assign_label: { id: "Assign", en: "Assign" },
|
||||
unassign_label: { id: "Hapus tugas", en: "Unassign" },
|
||||
take_assignment: { id: "Ambil tugas", en: "Take assignment" },
|
||||
reassign: { id: "Reassign", en: "Reassign" },
|
||||
status_label: { id: "Status", en: "Status" },
|
||||
status_open: { id: "Open", en: "Open" },
|
||||
status_pending: { id: "Pending", en: "Pending" },
|
||||
status_resolved: { id: "Resolved", en: "Resolved" },
|
||||
status_archived: { id: "Arsip", en: "Archived" },
|
||||
status_spam: { id: "Spam", en: "Spam" },
|
||||
update_status: { id: "Perbarui status", en: "Update status" },
|
||||
add_note_label: { id: "Tambah catatan", en: "Add note" },
|
||||
add_note_placeholder: { id: "Catatan internal...", en: "Internal note..." },
|
||||
save_note: { id: "Simpan catatan", en: "Save note" },
|
||||
save_tags: { id: "Simpan tags", en: "Save tags" },
|
||||
notes_label: { id: "Catatan", en: "Notes" },
|
||||
no_notes: { id: "Belum ada notes.", en: "No notes." },
|
||||
tags_placeholder: { id: "high-priority, billing", en: "high-priority, billing" }
|
||||
},
|
||||
tables: {
|
||||
total_contacts: { id: "Total kontak", en: "Total contacts" },
|
||||
total_users: { id: "Total pengguna", en: "Total users" },
|
||||
opted_in: { id: "Sudah opt-in", en: "Opted in" },
|
||||
tagged_contacts: { id: "Kontak bertag", en: "Tagged contacts" },
|
||||
agents: { id: "Agen", en: "Agents" },
|
||||
invited: { id: "Diundang", en: "Invited" },
|
||||
total_value: { id: "Total", en: "Total" }
|
||||
},
|
||||
status: {
|
||||
open: { id: "Open", en: "Open" },
|
||||
pending: { id: "Pending", en: "Pending" },
|
||||
resolved: { id: "Resolved", en: "Resolved" }
|
||||
},
|
||||
pages: {
|
||||
unauthorized_title: { id: "Tidak diizinkan", en: "Unauthorized" },
|
||||
unauthorized_desc: {
|
||||
id: "Role ini belum memiliki akses ke halaman yang Anda buka.",
|
||||
en: "You don't have access to the page you opened."
|
||||
},
|
||||
unauthorized_subtitle: {
|
||||
id: "Akses ditolak",
|
||||
en: "Access denied"
|
||||
},
|
||||
forgot_password: { id: "Lupa password", en: "Forgot password" },
|
||||
reset_password: { id: "Reset password", en: "Reset password" },
|
||||
reset_desc: { id: "Atur password baru untuk akun Anda.", en: "Set a new password for your account." },
|
||||
invalid_token: { id: "Token tidak valid atau sudah tidak berlaku.", en: "The token is invalid or no longer valid." },
|
||||
reset_token_expired: {
|
||||
id: "Token reset sudah kedaluwarsa. Minta link baru.",
|
||||
en: "Your reset token expired. Request a new one."
|
||||
},
|
||||
password_mismatch: { id: "Konfirmasi password tidak cocok.", en: "Password confirmation does not match." }
|
||||
}
|
||||
} as const;
|
||||
|
||||
type LocaleMap = Record<Locale, string>;
|
||||
|
||||
type MessageCatalog = {
|
||||
[Section in keyof typeof MESSAGES]: {
|
||||
[Key in keyof typeof MESSAGES[Section]]: LocaleMap;
|
||||
};
|
||||
};
|
||||
|
||||
export const messages = MESSAGES as MessageCatalog;
|
||||
|
||||
export function isLocale(value: string | null | undefined): value is Locale {
|
||||
return value === "id" || value === "en";
|
||||
}
|
||||
|
||||
export async function getLocale(): Promise<Locale> {
|
||||
const { cookies } = await import("next/headers");
|
||||
const localeCookie = (await cookies()).get(LOCALE_COOKIE)?.value;
|
||||
if (isLocale(localeCookie)) {
|
||||
return localeCookie;
|
||||
}
|
||||
|
||||
return DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
export function t<S extends I18nSection>(locale: Locale, section: S, key: MessageKey<S>): string {
|
||||
return messages[section][key][locale];
|
||||
}
|
||||
|
||||
export function getTranslator(locale: Locale) {
|
||||
return function translate<S extends I18nSection>(section: S, key: MessageKey<S>) {
|
||||
return t(locale, section, key);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user