Files
Wira Basalamah adde003fba
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
chore: initial project import
2026-04-21 09:29:29 +07:00

226 lines
11 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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: "Dont 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);
};
}