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,332 @@
/**
* Chinese (中文) Translations
*/
window.translations = window.translations || {};
window.translations.zh = {
dashboard: "仪表盘",
models: "模型列表",
accounts: "账号管理",
logs: "运行日志",
settings: "系统设置",
online: "在线",
offline: "离线",
totalAccounts: "账号总数",
active: "活跃状态",
operational: "运行中",
rateLimited: "受限状态",
quotasDepleted: "{count}/{total} 配额耗尽",
quotasDepletedTitle: "配额耗尽数",
outOfTracked: "共追踪 {total} 个",
cooldown: "冷却中",
searchPlaceholder: "搜索模型...",
allAccounts: "所有账号",
stat: "状态",
modelIdentity: "模型标识",
globalQuota: "全局配额",
nextReset: "重置时间",
distribution: "账号分布",
systemConfig: "系统配置",
language: "语言设置",
pollingInterval: "数据轮询间隔",
maxDisplayLogs: "最大日志显示行数",
showExhausted: "显示耗尽模型",
showExhaustedDesc: "即使配额为 0% 也显示模型。",
compactMode: "紧凑模式",
compactModeDesc: "减少表格间距以显示更多信息。",
saveChanges: "保存更改",
autoScroll: "自动滚动",
clearLogs: "清除日志",
accountManagement: "账号管理",
manageTokens: "管理已授权的 Google 账号及其状态",
addAccount: "添加账号",
status: "状态",
enabled: "启用",
health: "状态",
accountEmail: "账号 (邮箱)",
source: "来源",
projectId: "项目 ID",
sessionState: "会话状态",
operations: "操作",
delete: "删除",
confirmDelete: "确定要移除此账号吗?",
cannotDeleteDatabase: "无法删除:此账号来自 Antigravity 数据库(只读)",
connectGoogle: "连接 Google 账号",
reauthenticated: "已重新认证",
added: "已添加",
successfully: "成功",
accountAddedSuccess: "账号添加成功",
accountReauthSuccess: "账号重新认证成功",
failedToGetAuthUrl: "获取认证链接失败",
failedToStartOAuth: "启动 OAuth 流程失败",
oauthInProgress: "OAuth 授权进行中,请在弹出窗口中完成认证...",
family: "系列",
model: "模型",
activeSuffix: "活跃",
manualReload: "重新加载配置",
// Tabs
tabInterface: "界面设置",
tabClaude: "Claude CLI",
tabModels: "模型管理",
tabServer: "服务器设置",
// Dashboard
linkedAccounts: "已关联账号",
noSignal: "无信号连接",
establishingUplink: "正在建立上行链路...",
goToAccounts: "前往账号管理",
// Settings - Models
modelsDesc: "配置模型的可见性、置顶和请求路由。",
modelsPageDesc: "所有可用模型的实时配额和状态。",
showHidden: "显示隐藏模型",
hideHidden: "隐藏被屏蔽模型",
hiddenOn: "隐藏模型: 显示",
hiddenOff: "隐藏模型: 隐藏",
modelId: "模型 ID",
actions: "操作",
pinToTop: "置顶",
toggleVisibility: "切换可见性",
noModels: "未检测到模型",
modelMappingHint: "服务端模型路由功能。Claude Code 用户请使用 'Claude CLI' 标签页以便捷配置。",
modelMapping: "映射 (目标模型 ID)",
// Settings - Claude
proxyConnection: "代理连接",
modelSelection: "模型选择",
defaultModelAliases: "默认模型映射 (别名)",
opusAlias: "Opus 别名",
sonnetAlias: "Sonnet 别名",
haikuAlias: "Haiku 别名",
claudeSettingsAlertPrefix: "以下设置直接修改",
claudeSettingsAlertSuffix: "重启 Claude CLI 生效。",
applyToClaude: "应用到 Claude CLI",
// Presets
configPresets: "配置预设",
saveAsPreset: "另存为预设",
deletePreset: "删除预设",
loadPreset: "加载预设到表单",
load: "加载",
presetHint: "选择预设以加载。点击“应用到 Claude CLI”以保存更改。",
presetLoaded: "预设已加载。点击“应用到 Claude CLI”以保存。",
presetSaved: "预设已保存",
presetDeleted: "预设已删除",
unsavedChangesTitle: "未保存的更改",
unsavedChangesMessage: "当前配置与任何已保存的预设都不匹配。如果切换预设,当前未保存的设置将会丢失。",
loadAnyway: "仍然加载",
savePresetTitle: "保存预设",
savePresetDesc: "将当前配置保存为可重复使用的预设。",
presetName: "预设名称",
presetNamePlaceholder: "例如:工作配置",
savePreset: "保存预设",
// Settings - Server
port: "端口",
uiVersion: "UI 版本",
debugMode: "调试模式",
environment: "运行环境",
serverReadOnly: "配置由 config.json 管理。重启服务器以应用更改。",
advancedSettings: "高级设置",
reloadConfigTitle: "重载账号配置",
reloadConfigDesc: "强制从磁盘重新读取 accounts.json",
reload: "重载",
// Config Specific
primaryModel: "主模型",
subAgentModel: "子代理模型",
advancedOverrides: "默认模型覆盖 (高级)",
opusModel: "Opus 模型",
sonnetModel: "Sonnet 模型",
haikuModel: "Haiku 模型",
authToken: "认证令牌",
saveConfig: "保存到 Claude CLI 设置",
envVar: "环境变量",
// New Keys
systemName: "ANTIGRAVITY",
systemDesc: "CLAUDE 代理系统",
connectGoogleDesc: "连接 Google Workspace 账号以增加 API 配额。该账号将用于通过 Antigravity 代理 Claude 请求。",
useCliCommand: "使用命令行",
close: "关闭",
requestVolume: "请求量",
filter: "筛选",
all: "全选",
none: "清空",
noDataTracked: "暂无追踪数据",
selectFamilies: "选择要显示的系列",
selectModels: "选择要显示的模型",
noLogsMatch: "没有符合过滤条件的日志",
connecting: "正在连接",
main: "主菜单",
system: "系统",
refreshData: "刷新数据",
connectionLost: "连接已断开",
lastUpdated: "最后更新",
grepLogs: "过滤日志...",
noMatchingModels: "没有匹配的模型",
typeToSearch: "输入以搜索或选择...",
or: "或",
refreshingAccount: "正在刷新 {email}...",
refreshedAccount: "已完成刷新 {email}",
refreshFailed: "刷新失败",
accountToggled: "账号 {email} 已{status}",
toggleFailed: "切换失败",
reauthenticating: "正在重新认证 {email}...",
authUrlFailed: "获取认证链接失败",
deletedAccount: "已删除 {email}",
deleteFailed: "删除失败",
accountsReloaded: "账号配置已重载",
reloadFailed: "重载失败",
claudeConfigSaved: "Claude 配置已保存",
claudeConfigRestored: "Claude CLI 已恢复默认设置",
saveConfigFailed: "保存配置失败",
restoreConfigFailed: "恢复配置失败",
restoreDefault: "恢复默认",
confirmRestoreTitle: "确认恢复",
confirmRestoreMessage: "确定要将 Claude CLI 恢复为默认设置吗?这将移除代理配置。",
confirmRestore: "确认恢复",
claudeActive: "Claude 活跃",
claudeEmpty: "Claude 耗尽",
geminiActive: "Gemini 活跃",
geminiEmpty: "Gemini 耗尽",
synced: "已同步",
syncing: "正在同步...",
// 时间范围标签
last1Hour: "最近 1 小时",
last6Hours: "最近 6 小时",
last24Hours: "最近 24 小时",
last7Days: "最近 7 天",
allTime: "最后全部",
groupBy: "分组方式",
// Additional
reloading: "正在重载...",
reloaded: "已重载",
lines: "行",
enabledSeeLogs: "已启用 (见日志)",
production: "生产环境",
configSaved: "配置已保存",
enterPassword: "请输入 Web UI 密码:",
ready: "就绪",
depleted: "已耗尽",
timeH: "时",
timeM: "分",
familyClaude: "Claude 系列",
familyGemini: "Gemini 系列",
familyOther: "其他系列",
enabledStatus: "已启用",
disabledStatus: "已禁用",
logLevelInfo: "信息",
logLevelSuccess: "成功",
logLevelWarn: "警告",
logLevelError: "错误",
totalColon: "总计:",
todayColon: "今日:",
hour1Colon: "1小时:",
frequentModels: "常用推荐",
smartTitle: "自动选出过去 24 小时最常用的 5 个模型",
activeCount: "{count} 活跃",
allCaps: "全部",
claudeCaps: "CLAUDE",
geminiCaps: "GEMINI",
modelMapping: "映射 (目标模型 ID)",
systemInfo: "系统信息",
refresh: "刷新",
runtimeConfig: "运行时配置",
debugDesc: "启用详细日志记录 (见运行日志)",
networkRetry: "网络重试设置",
maxRetries: "最大重试次数",
retryBaseDelay: "重试基础延迟 (毫秒)",
retryMaxDelay: "重试最大延迟 (毫秒)",
persistentSessions: "持久化登录会话",
persistTokenDesc: "将登录会话保存到磁盘以实现快速重启",
rateLimiting: "账号限流与超时",
defaultCooldown: "默认冷却时间",
defaultCooldownDesc: "当 API 未提供重置时间时的备用冷却时间。",
maxWaitThreshold: "最大等待阈值",
maxWaitDesc: "如果所有账号的限流时间超过此阈值,立即返回错误而非等待。",
saveConfigServer: "保存配置",
serverRestartAlert: "配置已保存至 {path}。部分更改可能需要重启服务器。",
changePassword: "修改 WebUI 密码",
changePasswordDesc: "更新访问此仪表盘的密码",
currentPassword: "当前密码",
newPassword: "新密码",
confirmNewPassword: "确认新密码",
passwordEmptyDesc: "如果未设置密码请留空",
passwordLengthDesc: "至少 6 个字符",
passwordConfirmDesc: "请再次输入新密码",
cancel: "取消",
passwordsNotMatch: "密码不匹配",
passwordTooShort: "密码至少需要 6 个字符",
// Dashboard drill-down
clickToViewAllAccounts: "点击查看所有账号",
clickToViewModels: "点击查看模型页面",
clickToViewLimitedAccounts: "点击查看受限账号",
clickToFilterClaude: "点击筛选 Claude 模型",
clickToFilterGemini: "点击筛选 Gemini 模型",
// 账号页面
searchAccounts: "搜索账号...",
noAccountsYet: "还没有添加任何账号",
noAccountsDesc: "点击上方的 \"添加账号\" 按钮通过 OAuth 添加 Google 账号,或者使用 CLI 命令导入凭证。",
addFirstAccount: "添加第一个账号",
noSearchResults: "没有找到匹配的账号",
clearSearch: "清除搜索",
disabledAccountsNote: "<strong>已禁用的账号</strong>不会用于请求路由,但仍保留在配置中。仪表盘统计数据仅包含已启用的账号。",
dangerousOperation: "⚠️ 危险操作",
confirmDeletePrompt: "确定要删除账号",
deleteWarning: "⚠️ 此操作不可撤销,账号的所有配置和历史记录将永久删除。",
// OAuth 进度
oauthWaiting: "等待 OAuth 授权中...",
oauthWaitingDesc: "请在弹出窗口中完成认证。此过程最长可能需要 2 分钟。",
oauthCancelled: "已取消 OAuth 授权",
oauthTimeout: "⏱️ OAuth 授权超时,请重试。",
oauthWindowClosed: "OAuth 窗口已关闭,授权可能未完成。",
cancelOAuth: "取消",
// MCP CLI & Gemini 1M
mcpCliExperimental: "实验性 MCP CLI",
mcpCliDesc: "启用实验性 MCP 集成,减少上下文消耗,提高工具调用可靠性。",
gemini1mMode: "Gemini 1M 上下文模式",
gemini1mDesc: "为 Gemini 模型添加 [1m] 后缀以支持 1M 上下文窗口。",
gemini1mWarning: "⚠ 大上下文可能降低 Gemini-3-Pro 性能。",
clickToSet: "点击进行配置...",
none: "无",
// Quota Distribution
quotaDistribution: "配额分布",
resetsIn: "{time} 后重置",
noQuotaData: "暂无此账号的配额数据。",
// TODO: Missing translations - Hardcoded strings from HTML
// pageTitle: "Antigravity Console",
// live: "Live",
// tier: "Tier",
// quota: "Quota",
// tierUltra: "Ultra",
// tierPro: "Pro",
// tierFree: "Free",
// menu: "Menu",
// github: "GitHub",
// noData: "No data",
// fix: "Fix",
// TODO: Missing translations - Hardcoded strings from JS (Error Messages)
// operationFailed: "Operation failed",
// unknownError: "Unknown error",
// presetNameRequired: "Preset name is required",
// saveFailed: "Save failed",
// failedToSavePreset: "Failed to save preset",
// noPresetSelected: "No preset selected",
// deletePresetConfirm: "Delete preset \"{name}\"?",
// deleteFailed: "Delete failed",
// failedToDeletePreset: "Failed to delete preset",
// failedToChangePassword: "Failed to change password",
// passwordChangedSuccess: "Password changed successfully",
// debugModeToggled: "Debug mode {status}",
// tokenCacheToggled: "Token cache {status}",
// failedToUpdateTokenCache: "Failed to update token cache",
// failedToUpdateDebugMode: "Failed to update debug mode",
// failedToRefreshAccount: "Failed to refresh account",
// failedToDeleteAccount: "Failed to delete account",
// failedToReloadAccounts: "Failed to reload accounts",
// failedToUpdateModelConfig: "Failed to update model config",
// fieldUpdated: "{displayName} updated to {value}",
// failedToUpdateField: "Failed to update {displayName}",
// TODO: Missing translations - Validation messages from validators.js
// mustBeValidNumber: "{fieldName} must be a valid number",
// mustBeAtLeast: "{fieldName} must be at least {min}",
// mustBeAtMost: "{fieldName} must be at most {max}",
// cannotBeEmpty: "{fieldName} cannot be empty",
// mustBeTrueOrFalse: "Value must be true or false",
};