Files
tautkan-kanban/client/src/utils/i18n.ts
T
Power BI Dev 88c7c653c2 first init
2026-08-17 10:52:09 +07:00

418 lines
17 KiB
TypeScript

import { getRetroMode } from './mode.js';
export type Language = 'id' | 'en';
export const modernTranslations = {
id: {
// Branding
appName: 'TautKan',
appTagline: 'Satu tautan untuk semua urusan.',
appBadge: 'AGILE KANBAN',
// Controls
lightMode: 'Mode Terang',
darkMode: 'Mode Gelap',
retroModeOn: '🕹️ 8-Bit Mode',
retroModeOff: '✨ Modern Mode',
muteAudio: 'Matikan Suara Audio',
unmuteAudio: 'Nyalakan Suara Audio',
changeName: 'Ubah Nama Pengguna',
// Roles
superadmin: 'Superadmin',
member: 'Anggota',
roleSuperadminDesc: 'Pemilik Papan (Akses Penuh)',
roleMemberDesc: 'Anggota Tim (Kolaborator)',
// Hero Section
heroBadge: 'TANPA PASSWORD • TANPA EMAIL • 100% REAL-TIME',
heroTitle: 'Papan Kanban Real-Time Modern',
heroSubtitle: 'Kolaborasi manajemen proyek instan berbasis URL Fragment Hash. Tanpa registrasi akun, aman, cepat, dan terhubung secara langsung antar anggota tim.',
// Create Board
createBoardHeader: 'Buat Papan Kanban Baru',
boardTitleLabel: 'Judul Proyek / Papan',
boardTitlePlaceholder: 'Contoh: Pengembangan Fitur Sprint 1...',
createBoardBtn: 'Buat Papan Sekarang',
creatingBoardBtn: 'Sedang Membuat Papan...',
rateLimitNote: 'Batas: 5 papan per jam per IP',
rbacNote: 'Otorisasi Aman URL Fragment',
// Saved Boards
savedBoardsTitle: 'Papan Tersimpan',
savedBoardsSub: 'Riwayat tersimpan di browser Anda',
noSavedBoards: 'Belum ada papan yang tersimpan.',
removeSaved: 'Hapus dari daftar',
// Board View Header
onlineUsers: 'Pengguna Online',
shareInviteBtn: 'Bagikan & Undang',
rotateKeyBtn: 'Putar Kunci Anggota',
rotateKeyTooltip: 'Regenerasi tautan anggota untuk mencabut akses lama',
deleteBoardTooltip: 'Hapus papan ini secara permanen',
boardSettings: 'Pengaturan Papan',
autoMoveStaleSetting: 'Otomatis kembalikan kartu macet (> batas jam) ke Backlog',
backToHome: 'Kembali ke Beranda',
connectingToRealm: 'Menghubungkan ke papan...',
accessForbidden: 'Akses Ditolak',
accessForbiddenDesc: 'Kunci otorisasi tidak valid atau sudah tidak berlaku.',
reEnterKey: 'Masukkan Kunci Kembali',
boardLogsBtn: 'Riwayat Log',
activityLogTitle: 'Riwayat Perpindahan & Log Aktivitas',
noActivityLogs: 'Belum ada riwayat pergerakan kartu yang tercatat.',
// Columns & WIP
addColumn: 'Tambah Kolom',
columnTitlePlaceholder: 'Nama kolom (misal: QA Testing)...',
addNewTask: 'Tambah Tugas',
taskTitlePlaceholder: 'Tulis judul tugas...',
enterToAdd: 'Tekan Enter ↵ untuk simpan',
cancel: 'Batal',
add: 'Tambah',
save: 'Simpan',
noTasksInCol: 'Belum ada tugas di kolom ini',
dragColTooltip: 'Geser untuk mengatur urutan kolom',
deleteColConfirm: 'Hapus kolom ini beserta semua tugas di dalamnya?',
editColSettings: 'Pengaturan Kolom & WIP Limit',
wipLimitLabel: 'WIP Limit (Maksimal Kartu):',
wipUnlimited: '0 = Tanpa Batas',
autoStaleHoursLabel: 'Batas Macet / Stale (Jam Inaktivitas):',
wipLimitReached: 'WIP Limit Tercapai',
wipWarningModal: 'Kolom ini telah mencapai batas kapasitas maksimum kartu (WIP Limit). Selesaikan tugas yang ada terlebih dahulu!',
// Ticket Modal / Editor
taskTitle: 'Judul Tugas',
taskDesc: 'Deskripsi Tugas / Spesifikasi',
taskDescPlaceholder: 'Tuliskan rincian tugas, kriteria penerimaan, atau catatan penting...',
priorityLabel: 'Prioritas / Urgensi',
tagsLabel: 'Label / Tags (Tekan Enter)',
tagsPlaceholder: 'Contoh: FRONTEND, BUG, BACKEND...',
assigneeLabel: 'Penanggung Jawab (Assignee)',
assigneePlaceholder: 'Contoh: Budi Santoso',
dueDateLabel: 'Tenggat Waktu (Due Date)',
subtasksLabel: 'Checklist Sub-tugas',
addSubtaskPlaceholder: 'Tambah sub-tugas baru... (Enter)',
blockedLabel: 'Status Macet / Tertahan (Blocked)',
markBlocked: 'Tandai Sebagai Macet (Blocked)',
blockedReasonLabel: 'Alasan Kendala / Blocker:',
blockedReasonPlaceholder: 'Contoh: Menunggu API pihak ketiga selesai / Butuh approval...',
staleIndicator: 'Kartu Macet (Stale)',
staleSince: 'Tidak ada pergerakan selama',
overdue: 'Terlambat',
dueToday: 'Hari Ini',
autoSaving: 'Menyimpan otomatis...',
synced: 'Tersinkronisasi',
isTypingTitle: 'sedang mengetik judul tugas...',
isTypingDesc: 'sedang mengetik deskripsi...',
isEditingGeneral: 'sedang mengedit tugas ini...',
deleteTask: 'Hapus Tugas',
close: 'Tutup',
deleteTaskConfirm: 'Apakah Anda yakin ingin menghapus tugas ini?',
cardMovementHistory: 'Riwayat Perpindahan Kartu (Database Log)',
// Priorities
prioLow: 'Rendah',
prioMedium: 'Sedang',
prioHigh: 'Tinggi',
prioUrgent: 'Mendesak',
// Success Share Modal
boardCreatedTitle: 'Papan TautKan Berhasil Dibuat!',
boardName: 'Nama Papan',
adminLinkTitle: 'Tautan Admin (Superadmin)',
adminLinkWarning: 'JANGAN BAGIKAN! HANYA UNTUK ANDA',
adminLinkSavedNote: 'Tautan ini tersimpan di browser Anda. Bookmark tautan ini untuk mengelola papan.',
memberLinkTitle: 'Tautan Undangan Anggota Tim',
memberLinkBadge: 'UNTUK REKAN TIM',
copied: 'Tersalin!',
copy: 'Salin',
shareWhatsApp: 'Bagikan ke WhatsApp / Aplikasi',
enterBoardBtn: 'Buka Papan Kanban',
// Key Rotation Modal
keyRotationTitle: 'Rotasi Kunci Anggota',
keyRotationSubtitle: 'Buat Ulang Tautan Undangan Anggota Tim',
keyRotationWarningTitle: 'Peringatan: Menonaktifkan Tautan Lama!',
keyRotationWarningDesc: 'Membuat kunci anggota baru akan secara instan mencabut izin seluruh anggota yang menggunakan tautan lama dan memutuskan sesi mereka secara real-time.',
regenerateBtn: 'Buat Ulang Tautan',
rotatingBtn: 'Memproses...',
newLinkActiveTitle: 'Tautan Baru Berhasil Diaktifkan!',
newLinkActiveDesc: 'Tautan anggota lama telah dinonaktifkan. Bagikan tautan baru ini kepada rekan tim Anda:',
done: 'Selesai',
// Key Prompt Modal
keyPromptTitle: 'Kunci Otorisasi Diperlukan',
keyPromptDesc: 'Papan ini terlindungi oleh URL Fragment Key. Silakan tempel tautan lengkap (#admin=... atau #member=...) atau kunci rahasia Anda di bawah ini.',
keyPromptPlaceholder: 'Tempel tautan #admin=... atau #member=... atau kunci Anda',
unlockBoardBtn: 'Buka Papan',
keyPromptError: 'Silakan masukkan kunci atau tautan undangan',
// Revoked Alert
sessionRevokedMsg: 'Superadmin telah membuat ulang tautan anggota. Sesi Anda telah dihentikan.',
movementFailed: 'Gagal memindahkan kartu:',
footerText: 'TautKan • Satu tautan untuk semua urusan • Dual-Code URL Fragment Real-Time Agile',
},
en: {
// Branding
appName: 'TautKan',
appTagline: 'One link to manage it all.',
appBadge: 'AGILE KANBAN',
// Controls
lightMode: 'Light Mode',
darkMode: 'Dark Mode',
retroModeOn: '🕹️ 8-Bit Mode',
retroModeOff: '✨ Modern Mode',
muteAudio: 'Mute Audio',
unmuteAudio: 'Unmute Audio',
changeName: 'Change User Name',
// Roles
superadmin: 'Superadmin',
member: 'Member',
roleSuperadminDesc: 'Board Owner (Full Access)',
roleMemberDesc: 'Team Member (Collaborator)',
// Hero Section
heroBadge: 'ZERO PASSWORDS • ZERO EMAILS • 100% REAL-TIME',
heroTitle: 'Modern Real-Time Agile Kanban',
heroSubtitle: 'Instant project collaboration powered by URL Fragment Hashes. Zero sign-up required, ultra-secure, fast, and connected in real time.',
// Create Board
createBoardHeader: 'Create a New Kanban Board',
boardTitleLabel: 'Project / Board Title',
boardTitlePlaceholder: 'e.g. Sprint 1 - Core Feature Development...',
createBoardBtn: 'Create Board Now',
creatingBoardBtn: 'Creating Board...',
rateLimitNote: 'Limit: 5 boards/hour per IP',
rbacNote: 'Secure URL Fragment Key Auth',
// Saved Boards
savedBoardsTitle: 'Saved Boards',
savedBoardsSub: 'History stored securely in your browser',
noSavedBoards: 'No saved boards found.',
removeSaved: 'Remove from history',
// Board View Header
onlineUsers: 'Online Teammates',
shareInviteBtn: 'Share & Invite',
rotateKeyBtn: 'Rotate Member Key',
rotateKeyTooltip: 'Regenerate member key and revoke all previous member links',
deleteBoardTooltip: 'Permanently delete this board',
boardSettings: 'Board Settings',
autoMoveStaleSetting: 'Automatically move stale cards (> threshold hours) back to Backlog',
backToHome: 'Back to Home',
connectingToRealm: 'Connecting to board realm...',
accessForbidden: 'Access Denied',
accessForbiddenDesc: 'The authorization key in this URL is invalid or has expired.',
reEnterKey: 'Re-Enter Key',
boardLogsBtn: 'Activity Logs',
activityLogTitle: 'Movement & Activity History',
noActivityLogs: 'No card movements logged yet.',
// Columns & WIP
addColumn: 'Add Column',
columnTitlePlaceholder: 'Column name (e.g., QA Testing)...',
addNewTask: 'Add Task',
taskTitlePlaceholder: 'Write task title...',
enterToAdd: 'Press Enter ↵ to save',
cancel: 'Cancel',
add: 'Add',
save: 'Save',
noTasksInCol: 'No tasks in this column yet',
dragColTooltip: 'Drag to reorder column',
deleteColConfirm: 'Delete this column and all of its tasks?',
editColSettings: 'Column Settings & WIP Limit',
wipLimitLabel: 'WIP Limit (Max Cards):',
wipUnlimited: '0 = Unlimited',
autoStaleHoursLabel: 'Stale Inactivity Limit (Hours):',
wipLimitReached: 'WIP Limit Reached',
wipWarningModal: 'This column has reached its maximum Work-In-Progress (WIP) limit. Please finish active tasks first!',
// Ticket Modal / Editor
taskTitle: 'Task Title',
taskDesc: 'Task Description / Specs',
taskDescPlaceholder: 'Write specifications, acceptance criteria, or key implementation notes...',
priorityLabel: 'Priority / Urgency',
tagsLabel: 'Tags (Press Enter)',
tagsPlaceholder: 'e.g. FRONTEND, BUG, BACKEND...',
assigneeLabel: 'Assignee',
assigneePlaceholder: 'e.g. Alex Morgan',
dueDateLabel: 'Due Date',
subtasksLabel: 'Subtask Checklist',
addSubtaskPlaceholder: 'Add a new subtask... (Enter)',
blockedLabel: 'Blocker Status (Blocked)',
markBlocked: 'Mark as Blocked',
blockedReasonLabel: 'Blocker Explanation:',
blockedReasonPlaceholder: 'e.g. Waiting for 3rd-party API approval...',
staleIndicator: 'Stale Card',
staleSince: 'No movement for',
overdue: 'Overdue',
dueToday: 'Due Today',
autoSaving: 'Auto-saving...',
synced: 'Synced',
isTypingTitle: 'is typing title...',
isTypingDesc: 'is typing description...',
isEditingGeneral: 'is editing this task...',
deleteTask: 'Delete Task',
close: 'Close',
deleteTaskConfirm: 'Are you sure you want to delete this task?',
cardMovementHistory: 'Card Movement History (Database Log)',
// Priorities
prioLow: 'Low',
prioMedium: 'Medium',
prioHigh: 'High',
prioUrgent: 'Urgent',
// Success Share Modal
boardCreatedTitle: 'TautKan Board Created!',
boardName: 'Board Name',
adminLinkTitle: 'Admin Key Link (Superadmin)',
adminLinkWarning: 'DO NOT SHARE! ADMIN ACCESS ONLY',
adminLinkSavedNote: 'This link is saved in your browser. Bookmark it to maintain management permissions.',
memberLinkTitle: 'Team Member Invite Link',
memberLinkBadge: 'FOR TEAMMATES',
copied: 'Copied!',
copy: 'Copy',
shareWhatsApp: 'Share via WhatsApp / Apps',
enterBoardBtn: 'Open Kanban Board',
// Key Rotation Modal
keyRotationTitle: 'Member Key Rotation',
keyRotationSubtitle: 'Regenerate Team Member Invite Link',
keyRotationWarningTitle: 'Warning: Invalidates Old Links!',
keyRotationWarningDesc: 'Generating a new member key will immediately revoke access for all members using old links and disconnect active sessions in real time.',
regenerateBtn: 'Regenerate Link',
rotatingBtn: 'Rotating...',
newLinkActiveTitle: 'New Member Link Active!',
newLinkActiveDesc: 'Old member links have been successfully invalidated. Share this new link with your team:',
done: 'Done',
// Key Prompt Modal
keyPromptTitle: 'Secret Key Required',
keyPromptDesc: 'This board is protected by a URL Fragment Key. Please paste your link (#admin=... or #member=...) or secret key below.',
keyPromptPlaceholder: 'Paste #admin=... or #member=... link or key',
unlockBoardBtn: 'Unlock Board',
keyPromptError: 'Please enter your key or invite link',
// Revoked Alert
sessionRevokedMsg: 'The superadmin regenerated the member invite link. Your session has been revoked.',
movementFailed: 'Task movement failed:',
footerText: 'TautKan • One link to manage it all • Dual-Code URL Fragment Real-Time Agile',
},
};
export const retroTranslations = {
id: {
...modernTranslations.id,
appBadge: '🕹️ 8-BIT RPG',
superadmin: 'OVERLORD / GUILD MASTER',
member: 'PARTY MEMBER',
heroTitle: 'QUEST REALM 8-BIT',
heroSubtitle: 'Bangun petualangan manajemen quest tim secara instan! Otorisasi URL Fragment Hash tanpa registrasi, chip-audio 8-bit, dan kolaborasi waktu nyata.',
createBoardHeader: 'FORGE QUEST REALM BARU',
boardTitleLabel: 'NAMA PETUALANGAN / REALM',
boardTitlePlaceholder: 'Contoh: SPRINT NETHERS / BOSS RAID...',
createBoardBtn: '⚔️ FORGE REALM BARU',
creatingBoardBtn: 'FORGING REALM...',
savedBoardsTitle: 'REALM TERSIMPAN SAYA',
onlineUsers: 'PARTY ONLINE',
shareInviteBtn: '📜 UNDANG GUILD',
rotateKeyBtn: '🗝️ ROTASI KUNCI',
boardLogsBtn: '📜 LOG QUEST',
activityLogTitle: 'GULUNGAN LOG RIWAYAT & PERGERAKAN QUEST',
addColumn: 'TAMBAH TAHAPAN QUEST',
addNewTask: 'TAMBAH QUEST',
taskTitlePlaceholder: 'Nama misi / quest...',
taskTitle: 'NAMA MISI / QUEST',
taskDesc: 'GULUNGAN DESKRIPSI & SPESIFIKASI',
taskDescPlaceholder: 'Tuliskan detail misi, item reward, atau catatan petualangan...',
priorityLabel: 'TINGKAT BAHAYA / URGENSI',
prioLow: 'WOOD (RENDAH)',
prioMedium: 'IRON (SEDANG)',
prioHigh: 'GOLD (TINGGI)',
prioUrgent: 'NETHERITE (MENDESAK)',
subtasksLabel: 'CHECKLIST SUB-QUEST',
addSubtaskPlaceholder: 'Tambah sub-quest baru... (Enter)',
dueDateLabel: 'TENGGAT WAKTU BOSS RAID',
blockedLabel: 'JEBAKAN / STATUS TERTAHAN (TRAPPED)',
markBlocked: '🚩 TERKENA JEBAKAN (BLOCKED)',
staleIndicator: 'QUEST TERTIDUR (STALE)',
wipLimitReached: 'MANA WIP LIMIT PENUH',
wipWarningModal: 'Tahapan ini kehabisan Mana (WIP Limit Penuh)! Selesaikan quest yang aktif sebelum memulai quest baru.',
cardMovementHistory: 'GULUNGAN PERJALANAN QUEST (LOG DB)',
footerText: 'TautKan • 8-Bit Retro Mode • Satu tautan untuk semua urusan • Real-Time RPG Quest Board',
},
en: {
...modernTranslations.en,
appBadge: '🕹️ 8-BIT RPG',
superadmin: 'OVERLORD / GUILD MASTER',
member: 'PARTY MEMBER',
heroTitle: '8-BIT QUEST REALM',
heroSubtitle: 'Forge your team quest adventure instantly! URL Fragment Hash authorization, 8-bit chip audio, and real-time multiplayer board synchronization.',
createBoardHeader: 'FORGE NEW QUEST REALM',
boardTitleLabel: 'REALM / ADVENTURE NAME',
boardTitlePlaceholder: 'e.g. SPRINT NETHER / BOSS RAID...',
createBoardBtn: '⚔️ FORGE REALM',
creatingBoardBtn: 'FORGING REALM...',
savedBoardsTitle: 'SAVED QUEST REALMS',
onlineUsers: 'PARTY ONLINE',
shareInviteBtn: '📜 SUMMON PARTY',
rotateKeyBtn: '🗝️ ROTATE KEY',
boardLogsBtn: '📜 QUEST LOGS',
activityLogTitle: 'QUEST JOURNEY & ACTIVITY SCROLL',
addColumn: 'ADD QUEST STAGE',
addNewTask: 'ADD QUEST',
taskTitlePlaceholder: 'Quest / Mission name...',
taskTitle: 'QUEST / MISSION TITLE',
taskDesc: 'QUEST SCROLL & SPECIFICATIONS',
taskDescPlaceholder: 'Describe quest requirements, loot rewards, or tactical notes...',
priorityLabel: 'DANGER / URGENCY TIER',
prioLow: 'WOOD (LOW)',
prioMedium: 'IRON (MEDIUM)',
prioHigh: 'GOLD (HIGH)',
prioUrgent: 'NETHERITE (URGENT)',
subtasksLabel: 'SUB-QUEST CHECKLIST',
addSubtaskPlaceholder: 'Add new sub-quest... (Enter)',
dueDateLabel: 'BOSS RAID DUE DATE',
blockedLabel: 'TRAP / BLOCKED STATUS',
markBlocked: '🚩 TRAPPED IN BLOCKER',
staleIndicator: 'DORMANT QUEST (STALE)',
wipLimitReached: 'MANA WIP LIMIT FULL',
wipWarningModal: 'This stage has reached its maximum Mana capacity (WIP Limit)! Finish ongoing quests first.',
cardMovementHistory: 'QUEST MOVEMENT SCROLL (DB LOG)',
footerText: 'TautKan • 8-Bit Retro Mode • One link to manage it all • Real-Time RPG Quest Board',
},
};
const LANG_KEY = 'tautkan_lang';
export function getLanguage(): Language {
try {
const saved = localStorage.getItem(LANG_KEY) as Language;
if (saved === 'id' || saved === 'en') return saved;
} catch {}
return 'id'; // Default to Bahasa Indonesia
}
export function setLanguage(lang: Language): void {
try {
localStorage.setItem(LANG_KEY, lang);
} catch {}
}
export function useTranslation() {
const currentLang = getLanguage();
const isRetro = getRetroMode();
const activeDict = isRetro ? retroTranslations[currentLang] : modernTranslations[currentLang];
const fallbackDict = modernTranslations['id'];
const t = (key: keyof typeof modernTranslations['id']): string => {
return (activeDict as any)?.[key] || (fallbackDict as any)?.[key] || (key as string);
};
return { t, currentLang, isRetro };
}