feat: add i18n framework with Indonesian translation support (#124)

* feat: add i18n support with separate translation files

- Extract translations from store.js to separate files for easier management
- Add translation files for English (en.js), Indonesian (id.js), Turkish (tr.js), and Chinese (zh.js)
- Load translations via window.translations object before Alpine store initialization
- Add Bahasa Indonesia option to language selector

* feat: translate remaining hardcoded UI strings

- Update index.html to use t() for Menu and GitHub labels
- Update views to translate Tier, Quota, Live, tier badges, and close button
- Update components to use translated error messages and confirmation dialogs
- Update utils to use translated validation and error messages
- Update app-init.js to use translated OAuth success/error messages
This commit is contained in:
Irvan Fauziansyah
2026-01-15 22:33:38 +07:00
committed by GitHub
parent 9ffb83ab74
commit e2d03f9b25
17 changed files with 1413 additions and 890 deletions

View File

@@ -0,0 +1,371 @@
/**
* Indonesian (Bahasa Indonesia) Translations
*
* Panduan Terminologi IT:
* - Istilah teknis umum tetap dalam bahasa Inggris (OAuth, API, token, proxy, debug, cache, dll.)
* - Istilah UI umum diterjemahkan (Simpan, Hapus, Batal, dll.)
* - Pesan error dan status menggunakan campuran yang konsisten
*/
window.translations = window.translations || {};
window.translations.id = {
// Navigation
dashboard: "Dashboard",
models: "Model",
accounts: "Akun",
logs: "Log",
settings: "Pengaturan",
// Status
online: "ONLINE",
offline: "OFFLINE",
totalAccounts: "TOTAL AKUN",
active: "AKTIF",
operational: "Berjalan Normal",
rateLimited: "DIBATASI",
quotasDepleted: "{count}/{total} Kuota Habis",
quotasDepletedTitle: "KUOTA HABIS",
outOfTracked: "Dari {total} Terpantau",
cooldown: "Cooldown",
// Search & Filters
searchPlaceholder: "Cari model...",
allAccounts: "Semua Akun",
stat: "STATUS",
modelIdentity: "IDENTITAS MODEL",
globalQuota: "KUOTA GLOBAL",
nextReset: "RESET BERIKUTNYA",
distribution: "DISTRIBUSI AKUN",
// System Configuration
systemConfig: "Konfigurasi Sistem",
language: "Bahasa",
pollingInterval: "Interval Polling",
maxDisplayLogs: "Maks. Log Ditampilkan",
showExhausted: "Tampilkan Model Habis",
showExhaustedDesc: "Tampilkan model meskipun kuotanya sudah 0%.",
compactMode: "Mode Ringkas",
compactModeDesc: "Tampilan tabel lebih padat untuk informasi lebih banyak.",
saveChanges: "Simpan Perubahan",
// Logs
autoScroll: "Auto-scroll",
clearLogs: "Hapus Log",
// Account Management
accountManagement: "Kelola Akun",
manageTokens: "Kelola token akun Google dan status otorisasi",
addAccount: "Tambah Akun",
status: "STATUS",
enabled: "AKTIF",
health: "STATUS",
accountEmail: "AKUN (EMAIL)",
source: "SUMBER",
projectId: "PROJECT ID",
sessionState: "STATUS SESI",
operations: "AKSI",
delete: "Hapus",
confirmDelete: "Yakin ingin menghapus akun ini?",
cannotDeleteDatabase: "Tidak bisa dihapus: Akun ini dari database Antigravity (read-only)",
connectGoogle: "Hubungkan Akun Google",
reauthenticated: "diautentikasi ulang",
added: "ditambahkan",
successfully: "berhasil",
accountAddedSuccess: "Akun berhasil ditambahkan",
accountReauthSuccess: "Akun berhasil diautentikasi ulang",
failedToGetAuthUrl: "Gagal mendapatkan URL autentikasi",
failedToStartOAuth: "Gagal memulai proses OAuth",
oauthInProgress: "Proses OAuth sedang berjalan. Silakan selesaikan autentikasi di jendela popup...",
family: "Jenis",
model: "Model",
activeSuffix: "Aktif",
manualReload: "Muat Ulang Konfigurasi",
// Tabs
tabInterface: "Tampilan",
tabClaude: "Claude CLI",
tabModels: "Model",
tabServer: "Server",
// Dashboard
linkedAccounts: "Akun Terhubung",
noSignal: "TIDAK ADA SINYAL",
establishingUplink: "MENGHUBUNGKAN...",
goToAccounts: "Lihat Akun",
// Settings - Models
modelsDesc: "Atur visibilitas model, pin, dan routing request.",
modelsPageDesc: "Kuota dan status real-time untuk semua model yang tersedia.",
showHidden: "Tampilkan Model Tersembunyi",
hideHidden: "Sembunyikan Model Tersembunyi",
hiddenOn: "Tersembunyi: YA",
hiddenOff: "Tersembunyi: TIDAK",
modelId: "ID Model",
actions: "Aksi",
pinToTop: "Pin ke atas",
toggleVisibility: "Tampilkan/Sembunyikan",
noModels: "MODEL TIDAK DITEMUKAN",
modelMappingHint: "Routing model di sisi server. Pengguna Claude Code: lihat tab 'Claude CLI' untuk pengaturan di sisi klien.",
modelMapping: "Mapping (Target Model ID)",
// Settings - Claude
proxyConnection: "Koneksi Proxy",
modelSelection: "Pilihan Model",
defaultModelAliases: "ALIAS MODEL DEFAULT",
opusAlias: "Alias Opus",
sonnetAlias: "Alias Sonnet",
haikuAlias: "Alias Haiku",
claudeSettingsAlertPrefix: "Pengaturan di bawah langsung mengubah",
claudeSettingsAlertSuffix: "Restart Claude CLI untuk menerapkan.",
applyToClaude: "Simpan ke Claude CLI",
// Presets
configPresets: "Preset Konfigurasi",
saveAsPreset: "Simpan sebagai Preset",
deletePreset: "Hapus Preset",
loadPreset: "Muat preset",
load: "Muat",
presetHint: "Pilih preset untuk dimuat. Klik \"Simpan ke Claude CLI\" untuk menyimpan.",
presetLoaded: "Preset dimuat. Klik \"Simpan ke Claude CLI\" untuk menyimpan.",
presetSaved: "Preset tersimpan",
presetDeleted: "Preset terhapus",
unsavedChangesTitle: "Ada Perubahan Belum Disimpan",
unsavedChangesMessage: "Konfigurasi saat ini tidak cocok dengan preset tersimpan. Jika beralih, perubahan akan hilang.",
loadAnyway: "Tetap Muat",
savePresetTitle: "Simpan Preset",
savePresetDesc: "Simpan konfigurasi saat ini sebagai preset.",
presetName: "Nama Preset",
presetNamePlaceholder: "contoh: Setup Kantor",
savePreset: "Simpan Preset",
// Settings - Server
port: "Port",
uiVersion: "Versi UI",
debugMode: "Mode Debug",
environment: "Environment",
serverReadOnly: "Pengaturan dikelola melalui config.json. Restart server untuk menerapkan.",
advancedSettings: "Pengaturan Lanjutan",
reloadConfigTitle: "Muat Ulang Konfigurasi Akun",
reloadConfigDesc: "Paksa muat ulang accounts.json dari disk",
reload: "Muat Ulang",
// Config Specific
primaryModel: "Model Utama",
subAgentModel: "Model Sub-agent",
advancedOverrides: "Timpa Model Default",
opusModel: "Model Opus",
sonnetModel: "Model Sonnet",
haikuModel: "Model Haiku",
authToken: "Token Auth",
saveConfig: "Simpan ke Claude CLI",
envVar: "Env",
// System
systemName: "ANTIGRAVITY",
systemDesc: "CLAUDE PROXY SYSTEM",
connectGoogleDesc: "Hubungkan akun Google Workspace untuk meningkatkan kuota API. Akun akan digunakan untuk meneruskan request Claude via Antigravity.",
useCliCommand: "Gunakan Perintah CLI",
close: "Tutup",
requestVolume: "Volume Request",
filter: "Filter",
all: "Semua",
none: "Tidak Ada",
noDataTracked: "Belum ada data tercatat",
selectFamilies: "Pilih jenis model untuk ditampilkan",
selectModels: "Pilih model untuk ditampilkan",
noLogsMatch: "Tidak ada log yang sesuai filter",
connecting: "MENGHUBUNGKAN",
main: "Utama",
system: "Sistem",
refreshData: "Refresh Data",
connectionLost: "Koneksi Terputus",
lastUpdated: "Terakhir Diperbarui",
grepLogs: "cari log...",
noMatchingModels: "Tidak ada model yang cocok",
typeToSearch: "Ketik untuk mencari...",
or: "ATAU",
// Account Operations
refreshingAccount: "Memperbarui {email}...",
refreshedAccount: "{email} berhasil diperbarui",
refreshFailed: "Gagal memperbarui",
accountToggled: "Akun {email} {status}",
toggleFailed: "Gagal mengubah status",
reauthenticating: "Autentikasi ulang {email}...",
authUrlFailed: "Gagal mendapatkan URL autentikasi",
deletedAccount: "{email} berhasil dihapus",
deleteFailed: "Gagal menghapus",
accountsReloaded: "Akun berhasil dimuat ulang",
reloadFailed: "Gagal memuat ulang",
// Claude Config
claudeConfigSaved: "Konfigurasi Claude tersimpan",
claudeConfigRestored: "Claude CLI dikembalikan ke default",
saveConfigFailed: "Gagal menyimpan konfigurasi",
restoreConfigFailed: "Gagal mengembalikan konfigurasi",
restoreDefault: "Reset ke Default",
confirmRestoreTitle: "Konfirmasi Reset",
confirmRestoreMessage: "Yakin ingin mengembalikan Claude CLI ke pengaturan default? Konfigurasi proxy akan dihapus.",
confirmRestore: "Ya, Reset",
// Status Labels
claudeActive: "Claude Aktif",
claudeEmpty: "Claude Kosong",
geminiActive: "Gemini Aktif",
geminiEmpty: "Gemini Kosong",
synced: "SINKRON",
syncing: "SINKRONISASI...",
// Time range labels
last1Hour: "1 Jam Terakhir",
last6Hours: "6 Jam Terakhir",
last24Hours: "24 Jam Terakhir",
last7Days: "7 Hari Terakhir",
allTime: "Semua Waktu",
groupBy: "Kelompokkan",
// Additional
reloading: "Memuat ulang...",
reloaded: "Dimuat ulang",
lines: "baris",
enabledSeeLogs: "Aktif (Lihat Log)",
production: "Produksi",
configSaved: "Konfigurasi Tersimpan",
enterPassword: "Masukkan Password Web UI:",
ready: "SIAP",
depleted: "Habis",
timeH: "j",
timeM: "m",
familyClaude: "Claude",
familyGemini: "Gemini",
familyOther: "Lainnya",
enabledStatus: "diaktifkan",
disabledStatus: "dinonaktifkan",
logLevelInfo: "INFO",
logLevelSuccess: "OK",
logLevelWarn: "WARN",
logLevelError: "ERROR",
totalColon: "Total:",
todayColon: "Hari Ini:",
hour1Colon: "1j:",
frequentModels: "Sering Dipakai",
smartTitle: "Otomatis pilih 5 model paling sering dipakai (24 jam)",
activeCount: "{count} Aktif",
allCaps: "SEMUA",
claudeCaps: "CLAUDE",
geminiCaps: "GEMINI",
// System Info
systemInfo: "Informasi Sistem",
refresh: "Refresh",
runtimeConfig: "Konfigurasi Runtime",
debugDesc: "Aktifkan log detail (Lihat tab Log)",
networkRetry: "Pengaturan Retry Jaringan",
maxRetries: "Maks. Retry",
retryBaseDelay: "Jeda Awal Retry (ms)",
retryMaxDelay: "Jeda Maks. Retry (ms)",
persistentSessions: "Sesi Persisten",
persistTokenDesc: "Simpan sesi OAuth ke disk agar startup lebih cepat",
rateLimiting: "Rate Limiting & Timeout Akun",
defaultCooldown: "Cooldown Default",
defaultCooldownDesc: "Cooldown bawaan jika API tidak memberikan waktu reset.",
maxWaitThreshold: "Batas Tunggu Maksimal",
maxWaitDesc: "Jika semua akun terkena rate limit lebih lama dari ini, langsung gagal.",
saveConfigServer: "Simpan Konfigurasi",
serverRestartAlert: "Tersimpan ke {path}. Restart server untuk menerapkan.",
// Password
changePassword: "Ubah Password WebUI",
changePasswordDesc: "Ubah password untuk akses dashboard ini",
currentPassword: "Password Saat Ini",
newPassword: "Password Baru",
confirmNewPassword: "Konfirmasi Password Baru",
passwordEmptyDesc: "Kosongkan jika belum ada password",
passwordLengthDesc: "Minimal 6 karakter",
passwordConfirmDesc: "Masukkan ulang password baru",
cancel: "Batal",
passwordsNotMatch: "Password tidak cocok",
passwordTooShort: "Password minimal 6 karakter",
// Dashboard drill-down
clickToViewAllAccounts: "Klik untuk lihat semua akun",
clickToViewModels: "Klik untuk lihat halaman Model",
clickToViewLimitedAccounts: "Klik untuk lihat akun yang dibatasi",
clickToFilterClaude: "Klik untuk filter model Claude",
clickToFilterGemini: "Klik untuk filter model Gemini",
// Accounts page
searchAccounts: "Cari akun...",
noAccountsYet: "Belum Ada Akun",
noAccountsDesc: "Mulai dengan menambahkan akun Google via OAuth, atau gunakan perintah CLI untuk import kredensial.",
addFirstAccount: "Tambah Akun Pertama",
noSearchResults: "Tidak ada akun yang cocok",
clearSearch: "Reset Pencarian",
disabledAccountsNote: "<strong>Akun nonaktif</strong> tidak akan digunakan untuk routing tapi tetap tersimpan di konfigurasi. Statistik dashboard hanya mencakup akun aktif.",
dangerousOperation: "⚠️ Operasi Berbahaya",
confirmDeletePrompt: "Yakin ingin menghapus akun",
deleteWarning: "⚠️ Tindakan ini tidak bisa dibatalkan. Semua pengaturan dan riwayat akan dihapus permanen.",
// OAuth progress
oauthWaiting: "Menunggu otorisasi OAuth...",
oauthWaitingDesc: "Silakan selesaikan autentikasi di jendela popup. Proses ini bisa memakan waktu hingga 2 menit.",
oauthCancelled: "Otorisasi OAuth dibatalkan",
oauthTimeout: "⏱️ Waktu otorisasi OAuth habis. Silakan coba lagi.",
oauthWindowClosed: "Jendela OAuth ditutup. Otorisasi mungkin tidak lengkap.",
cancelOAuth: "Batal",
// MCP CLI & Gemini 1M
mcpCliExperimental: "MCP CLI (Eksperimental)",
mcpCliDesc: "Aktifkan integrasi MCP eksperimental untuk penggunaan tool yang lebih stabil dengan konsumsi konteks lebih rendah.",
gemini1mMode: "Mode Konteks Gemini 1M",
gemini1mDesc: "Tambahkan suffix [1m] ke model Gemini untuk context window 1M.",
gemini1mWarning: "⚠ Konteks besar dapat menurunkan performa Gemini-3-Pro.",
clickToSet: "Klik untuk atur...",
// Quota Distribution
quotaDistribution: "Distribusi Kuota",
resetsIn: "Reset dalam {time}",
noQuotaData: "Data kuota belum tersedia untuk akun ini.",
// Completed TODOs
pageTitle: "Antigravity Console",
live: "Live",
tier: "Tier",
quota: "Kuota",
tierUltra: "Ultra",
tierPro: "Pro",
tierFree: "Gratis",
menu: "Menu",
github: "GitHub",
noData: "Tidak ada data",
fix: "Perbaiki",
// Error Messages
operationFailed: "Operasi gagal",
unknownError: "Error tidak diketahui",
presetNameRequired: "Nama preset wajib diisi",
saveFailed: "Gagal menyimpan",
failedToSavePreset: "Gagal menyimpan preset",
noPresetSelected: "Tidak ada preset dipilih",
deletePresetConfirm: "Hapus preset \"{name}\"?",
deleteFailed: "Gagal menghapus",
failedToDeletePreset: "Gagal menghapus preset",
failedToChangePassword: "Gagal mengubah password",
passwordChangedSuccess: "Password berhasil diubah",
debugModeToggled: "Mode debug {status}",
tokenCacheToggled: "Cache token {status}",
failedToUpdateTokenCache: "Gagal memperbarui cache token",
failedToUpdateDebugMode: "Gagal memperbarui mode debug",
failedToRefreshAccount: "Gagal memperbarui akun",
failedToDeleteAccount: "Gagal menghapus akun",
failedToReloadAccounts: "Gagal memuat ulang akun",
failedToUpdateModelConfig: "Gagal memperbarui konfigurasi model",
fieldUpdated: "{displayName} diubah menjadi {value}",
failedToUpdateField: "Gagal memperbarui {displayName}",
// Validation
mustBeValidNumber: "{fieldName} harus berupa angka valid",
mustBeAtLeast: "{fieldName} minimal {min}",
mustBeAtMost: "{fieldName} maksimal {max}",
cannotBeEmpty: "{fieldName} tidak boleh kosong",
mustBeTrueOrFalse: "Nilai harus true atau false",
};