Jaasomn
Initial deployment
ceb3821
// 多语言配置
const translations = {
'zh-CN': {
// Header
'header.title': 'AIClient2API 管理控制台',
'header.description': 'AIClient2API 管理控制台 - 统一管理 AI 服务提供商',
'header.github': 'GitHub 仓库',
'header.themeToggle': '切换主题',
'header.status.connecting': '连接中...',
'header.status.connected': '已连接',
'header.status.disconnected': '连接断开',
'header.logout': '登出',
'header.reload': '重载',
'header.reload.confirm': '确定要重载配置吗?这将重新加载所有配置文件。',
'header.reload.requesting': '正在重载配置...',
'header.reload.success': '配置重载成功',
'header.reload.failed': '配置重载失败',
'header.refresh': '重载',
'header.restart': '重启',
'header.restart.confirm': '确定要重启服务吗?服务将短暂中断。',
'header.restart.requesting': '正在请求重启服务...',
'header.restart.success': '重启请求已发送,服务即将重启',
'header.restart.reconnecting': '正在重新连接...',
'header.restart.failed': '重启服务失败',
// Navigation
'nav.main': '主导航',
'nav.dashboard': '仪表盘',
'nav.guide': '使用指南',
'nav.tutorial': '配置教程',
'nav.config': '配置管理',
'nav.providers': '提供商池管理',
'nav.upload': '凭据文件管理',
'nav.usage': '用量查询',
'nav.logs': '实时日志',
'nav.plugins': '插件管理',
'nav.models': '可用模型',
// Dashboard
'dashboard.title': '系统概览',
'dashboard.uptime': '运行时间',
'dashboard.systemInfo': '系统信息',
'dashboard.version': '版本号',
'dashboard.update.check': '检查更新',
'dashboard.update.checkTitle': '检查是否有新版本可用',
'dashboard.update.perform': '立即更新',
'dashboard.update.performTitle': '更新到最新版本',
'dashboard.update.checking': '正在检查...',
'dashboard.update.upToDate': '已是最新',
'dashboard.update.hasUpdate': '发现新版本: {version}',
'dashboard.update.updating': '正在更新...',
'dashboard.update.success': '更新成功',
'dashboard.update.needsRestart': '代码已更新,请点击右上角「重启」按钮使更改生效',
'dashboard.update.restartTitle': '更新完成',
'dashboard.update.restartMsg': '代码已更新到版本 {version},请点击页面右上角的「重启」按钮使新代码生效。',
'dashboard.update.failed': '更新失败: {error}',
'dashboard.update.confirmTitle': '确认更新',
'dashboard.update.confirmMsg': '确定要更新到版本 {version} 吗?更新期间服务可能会短暂不可用。',
'dashboard.nodeVersion': 'Node.js版本',
'dashboard.serverTime': '服务器时间',
'dashboard.memoryUsage': '内存使用',
'dashboard.cpuUsage': 'CPU 使用',
'dashboard.serviceMode': '运行模式',
'dashboard.serviceMode.worker': '子进程模式',
'dashboard.serviceMode.standalone': '独立模式',
'dashboard.serviceMode.canRestart': '支持自动重启',
'dashboard.processPid': '进程 PID',
'dashboard.platform': '操作系统',
'dashboard.routing.title': '路径路由调用示例',
'dashboard.routing.description': '通过不同路径路由访问不同的AI模型提供商,支持灵活的模型切换',
'dashboard.routing.oauth': '突破限制',
'dashboard.routing.official': '官方API/三方',
'dashboard.routing.experimental': '突破限制/实验性',
'dashboard.routing.free': '突破限制/免费使用',
'dashboard.routing.endpoint': '端点路径:',
'dashboard.routing.example': '使用示例',
'dashboard.routing.exampleOpenAI': '使用示例 (OpenAI格式):',
'dashboard.routing.exampleClaude': '使用示例 (Claude格式):',
'dashboard.routing.openai': 'OpenAI协议',
'dashboard.routing.claude': 'Claude协议',
'dashboard.routing.tips': '使用提示',
'dashboard.routing.tip1': '即时切换: 通过修改URL路径即可切换不同的AI模型提供商',
'dashboard.routing.tip2': '客户端配置: 在Cherry-Studio、NextChat、Cline等客户端中设置API端点为对应路径',
'dashboard.routing.tip3': '跨协议调用: 支持OpenAI协议调用Claude模型,或Claude协议调用OpenAI模型',
'dashboard.routing.nodeName.gemini': 'Gemini CLI OAuth',
'dashboard.routing.nodeName.antigravity': 'Gemini Antigravity',
'dashboard.routing.nodeName.claude': 'Claude Custom',
'dashboard.routing.nodeName.kiro': 'Claude Kiro OAuth',
'dashboard.routing.nodeName.openai': 'OpenAI Custom',
'dashboard.routing.nodeName.qwen': 'Qwen OAuth',
'dashboard.routing.nodeName.iflow': 'iFlow OAuth',
'dashboard.routing.nodeName.codex': 'OpenAI Codex OAuth',
'dashboard.contact.title': '联系与赞助',
'dashboard.contact.wechat': '扫码进群,注明来意',
'dashboard.contact.wechatDesc': '添加微信获取更多技术支持和交流',
'dashboard.contact.x': '关注 X.com',
'dashboard.contact.xDesc': '在 X 上关注我们获取最新动态',
'dashboard.contact.sponsor': '扫码赞助',
'dashboard.contact.sponsorDesc': '您的赞助是项目持续发展的动力',
'dashboard.contact.coffee': 'Buy me a coffee',
'dashboard.contact.coffeeDesc': 'If you like this project, buy me a coffee!',
// OAuth
'oauth.modal.title': 'OAuth 授权',
'oauth.modal.provider': '提供商:',
'oauth.modal.requiredPort': '需要开放端口:',
'oauth.modal.portNote': '请确保此端口可被外部访问,用于接收授权回调',
'oauth.modal.steps': '授权步骤:',
'oauth.modal.step1': '点击下方按钮在浏览器中打开授权页面',
'oauth.modal.step2.qwen': '完成授权后,系统会自动获取凭据文件',
'oauth.modal.step2.google': '使用您的Google账号登录并授权',
'oauth.modal.step3': '凭据文件可在上传配置管理中查看和管理',
'oauth.modal.step4.qwen': '授权有效期: {min} 分钟',
'oauth.modal.step4.google': '授权完成后,凭据文件会自动保存',
'oauth.modal.urlLabel': '授权链接:',
'oauth.modal.copyTitle': '复制链接',
'oauth.modal.openInBrowser': '在浏览器中打开',
'oauth.manual.title': '自动监听受阻?',
'oauth.manual.desc': '如果授权窗口重定向后显示“无法访问”,请将该窗口地址栏的 <strong>完整 URL</strong> 粘贴到下方:',
'oauth.manual.placeholder': '粘贴回调 URL (包含 code=...)',
'oauth.manual.submit': '提交',
'oauth.success.msg': '授权链接已复制到剪贴板',
'oauth.window.blocked': '授权窗口被浏览器拦截,请允许弹出窗口',
'oauth.window.opened': '已打开授权窗口,请在窗口中完成操作',
'oauth.processing': '正在完成授权...',
'oauth.invalid.url': '该 URL 似乎不包含有效的授权代码',
'oauth.error.format': '无效的 URL 格式',
'oauth.kiro.selectMethod': '选择认证方式',
'oauth.kiro.google': 'Google 账号登录',
'oauth.kiro.googleDesc': '使用 Google 账号进行社交登录',
'oauth.kiro.github': 'GitHub 账号登录',
'oauth.kiro.githubDesc': '使用 GitHub 账号进行社交登录',
'oauth.kiro.awsBuilder': 'AWS Builder ID',
'oauth.kiro.awsBuilderDesc': '使用 AWS Builder ID 进行设备码授权',
'oauth.kiro.authMethodLabel': '认证方式:',
'oauth.kiro.step1': '点击下方按钮在浏览器中打开授权链接',
'oauth.kiro.step2': '使用您的 {method} 账号登录',
'oauth.kiro.step3': '授权完成后页面会自动关闭',
'oauth.kiro.step4': '刷新本页面查看凭据文件',
'oauth.kiro.batchImport': '批量导入 refreshToken',
'oauth.kiro.batchImportDesc': '批量导入已有的 refreshToken 生成凭据文件,该模式不支持 AWS 账号。',
'oauth.kiro.batchImportInstructions': '请输入 refreshToken,每行一个。系统将自动刷新并生成凭据文件。',
'oauth.kiro.awsImport': '导入 AWS 账号',
'oauth.kiro.awsImportDesc': '从 AWS SSO cache 目录导入凭据文件,适用于 AWS Builder ID 模式。',
'oauth.kiro.awsImportInstructions': '请上传 AWS SSO cache 目录中的 JSON 文件,需包含 clientId、clientSecret、accessToken、refreshToken 四个字段。如果是AWS企业用户,需增加 idcRegion 字段。',
'oauth.kiro.awsModeFile': '文件上传',
'oauth.kiro.awsModeJson': 'JSON 粘贴',
'oauth.kiro.awsUploadFiles': '上传凭据文件',
'oauth.kiro.awsDragDrop': '拖拽文件到此处',
'oauth.kiro.awsClickUpload': '或点击选择文件',
'oauth.kiro.awsFileHint': '如果一个文件不包含全部字段,可以多次上传不同的文件进行补全',
'oauth.kiro.awsSelectedFiles': '已选择的文件',
'oauth.kiro.awsClearFiles': '清空全部',
'oauth.kiro.awsFileReplaced': '已替换同名文件: {filename}',
'oauth.kiro.awsJsonInput': '粘贴 JSON 凭据',
'oauth.kiro.awsJsonPlaceholderSimple': '在此粘贴包含 clientId、clientSecret、accessToken、refreshToken 的 JSON...',
'oauth.kiro.awsJsonExample': '查看 JSON 格式示例',
'oauth.kiro.awsJsonHint': '可以直接粘贴合并后的 JSON,或从 AWS SSO cache 文件复制内容',
'oauth.kiro.awsJsonParseError': 'JSON 格式错误',
'oauth.kiro.awsParseError': '解析文件 {filename} 失败',
'oauth.kiro.awsValidationSuccess': '验证通过!已找到全部必需字段',
'oauth.kiro.awsValidationFailed': '验证失败!缺少必需字段',
'oauth.kiro.awsMissingFields': '缺少 {count} 个字段',
'oauth.kiro.awsUploadMore': '请上传包含缺失字段的文件,或切换到 JSON 模式手动补全',
'oauth.kiro.awsPreviewJson': '合并后的凭据预览',
'oauth.kiro.awsConfirmImport': '确认导入',
'oauth.kiro.awsNoCredentials': '没有可导入的凭据',
'oauth.kiro.awsImporting': '正在导入...',
'oauth.kiro.awsImportSuccess': 'AWS 凭据导入成功!',
'oauth.kiro.awsImportFailed': 'AWS 凭据导入失败',
'oauth.kiro.refreshTokensLabel': 'RefreshToken 列表',
'oauth.kiro.refreshTokensPlaceholder': '每行输入一个 refreshToken\n例如:\naorAxxxxxxxx\naorAyyyyyyyy\naorAzzzzzzzz',
'oauth.kiro.tokenCount': '待导入数量:',
'oauth.kiro.importing': '正在导入中,请稍候...',
'oauth.kiro.importingProgress': '正在导入 {current}/{total}...',
'oauth.kiro.startImport': '开始导入',
'oauth.kiro.noTokens': '请输入至少一个 refreshToken',
'oauth.kiro.importSuccess': '导入成功!共 {count} 个凭据已生成',
'oauth.kiro.importAllFailed': '导入失败!共 {count} 个 token 刷新失败',
'oauth.kiro.importPartial': '部分成功:{success} 个成功,{failed} 个失败',
'oauth.kiro.importError': '导入出错',
'oauth.kiro.duplicateToken': '重复凭据 - 此 refreshToken 已存在',
'oauth.kiro.duplicateCredentials': '该凭据已存在,请勿重复导入',
'oauth.kiro.builderIDStartURL': 'Builder ID Start URL',
'oauth.kiro.builderIDStartURLHint': '如果您使用 AWS IAM Identity Center,请输入您的 Start URL',
'oauth.iflow.step1': '点击下方按钮在浏览器中打开 iFlow 授权页面',
'oauth.iflow.step2': '使用您的 iFlow 账号登录并授权',
'oauth.iflow.step3': '授权完成后,系统会自动获取 API Key',
'oauth.iflow.step4': '凭据文件可在上传配置管理中查看和管理',
// Config
'config.title': '配置管理',
'config.apiKey': 'API密钥',
'config.apiKeyPlaceholder': '请输入API密钥',
'config.host': '监听地址',
'config.hostPlaceholder': '例如: 127.0.0.1',
'config.port': '端口',
'config.portPlaceholder': '3000',
'config.modelProvider': '模型提供商',
'config.modelProviderHelp': '勾选启动时初始化的模型提供商 (必须至少勾选一个)',
'config.modelProviderRequired': '必须至少勾选一个模型提供商',
'config.optional': '(选填)',
'config.gemini.baseUrl': 'Gemini Base URL',
'config.gemini.baseUrlPlaceholder': 'https://cloudcode-pa.googleapis.com',
'config.gemini.projectId': '项目ID',
'config.gemini.projectIdPlaceholder': 'Google Cloud项目ID',
'config.gemini.oauthCreds': 'OAuth凭据',
'config.gemini.credsType.file': '文件路径',
'config.gemini.credsType.base64': 'Base64编码',
'config.gemini.credsBase64': 'OAuth凭据 (Base64)',
'config.gemini.credsBase64Placeholder': '请输入Base64编码的OAuth凭据',
'config.gemini.credsFilePath': 'OAuth凭据文件路径',
'config.gemini.credsFilePathPlaceholder': '例如: ~/.gemini/oauth_creds.json',
'config.antigravity.dailyUrl': 'Daily Base URL',
'config.antigravity.dailyUrlPlaceholder': 'https://daily-cloudcode-pa.sandbox.googleapis.com',
'config.antigravity.autopushUrl': 'Autopush Base URL',
'config.antigravity.autopushUrlPlaceholder': 'https://autopush-cloudcode-pa.sandbox.googleapis.com',
'config.antigravity.credsFilePath': 'OAuth凭据文件路径',
'config.antigravity.credsFilePathPlaceholder': '例如: ~/.antigravity/oauth_creds.json',
'config.antigravity.note': 'Antigravity 使用 Google OAuth 认证,需要提供凭据文件路径',
'config.openai.apiKey': 'OpenAI API Key',
'config.openai.apiKeyPlaceholder': 'sk-...',
'config.openai.baseUrl': 'OpenAI Base URL',
'config.openai.baseUrlPlaceholder': '例如: https://api.openai.com/v1',
'config.claude.apiKey': 'Claude API Key',
'config.claude.apiKeyPlaceholder': 'sk-ant-...',
'config.claude.baseUrl': 'Claude Base URL',
'config.claude.baseUrlPlaceholder': '例如: https://api.anthropic.com',
'config.kiro.baseUrl': 'Base URL',
'config.kiro.baseUrlPlaceholder': 'https://codewhisperer.{{region}}.amazonaws.com/generateAssistantResponse',
'config.kiro.refreshUrl': 'Refresh URL',
'config.kiro.refreshUrlPlaceholder': 'https://prod.{{region}}.auth.desktop.kiro.dev/refreshToken',
'config.kiro.refreshIdcUrl': 'Refresh IDC URL',
'config.kiro.refreshIdcUrlPlaceholder': 'https://oidc.{{region}}.amazonaws.com/token',
'config.kiro.credsFilePath': 'OAuth凭据文件路径',
'config.kiro.credsFilePathPlaceholder': '例如: ~/.aws/sso/cache/kiro-auth-token.json',
'config.kiro.note': '使用 AWS 登录方式时,请确保授权文件中包含 clientId 和 clientSecret 字段',
'config.qwen.baseUrl': 'Qwen Base URL',
'config.qwen.baseUrlPlaceholder': 'https://portal.qwen.ai/v1',
'config.qwen.oauthBaseUrl': 'OAuth Base URL',
'config.qwen.oauthBaseUrlPlaceholder': 'https://chat.qwen.ai',
'config.qwen.credsFilePath': 'OAuth凭据文件路径',
'config.qwen.credsFilePathPlaceholder': '例如: ~/.qwen/oauth_creds.json',
'config.advanced.title': '高级配置',
'config.advanced.systemPromptFile': '系统提示文件路径',
'config.advanced.systemPromptFilePlaceholder': '例如: configs/input_system_prompt.txt',
'config.advanced.systemPromptMode': '系统提示模式',
'config.advanced.systemPromptMode.append': '追加 (append)',
'config.advanced.systemPromptMode.overwrite': '覆盖 (overwrite)',
'config.advanced.promptLogBaseName': '提示日志基础名称',
'config.advanced.promptLogBaseNamePlaceholder': '例如: prompt_log',
'config.advanced.promptLogMode': '提示日志模式',
'config.advanced.promptLogMode.none': '无 (none)',
'config.advanced.promptLogMode.console': '控制台 (console)',
'config.advanced.promptLogMode.file': '文件 (file)',
'config.advanced.maxRetries': '最大重试次数',
'config.advanced.baseDelay': '重试基础延迟(毫秒)',
'config.advanced.credentialSwitchMaxRetries': '坏凭证切换最大重试次数',
'config.advanced.credentialSwitchMaxRetriesNote': '认证错误(401/403)后切换凭证的最大重试次数,默认 5 次',
'config.advanced.warmupTarget': '系统预热节点数',
'config.advanced.warmupTargetNote': '系统启动时自动刷新的节点数量,默认为 0',
'config.advanced.refreshConcurrencyPerProvider': '提供商内刷新并发数',
'config.advanced.refreshConcurrencyPerProviderNote': '每个提供商内部最大并行刷新任务数,默认为 1',
'config.advanced.cronInterval': 'OAuth令牌刷新间隔(分钟)',
'config.advanced.cronEnabled': '启用OAuth令牌自动刷新(需重启服务)',
'config.advanced.poolFilePath': '提供商池配置文件路径(不能为空)',
'config.advanced.poolFilePathPlaceholder': '默认: configs/provider_pools.json',
'config.advanced.poolNote': '使用默认路径配置需添加一个空节点',
'config.advanced.maxErrorCount': '提供商最大错误次数',
'config.advanced.maxErrorCountPlaceholder': '默认: 10',
'config.advanced.maxErrorCountNote': '提供商连续错误达到此次数后将被标记为不健康,默认为 10 次',
'config.advanced.poolSizeLimit': '账号池轮询上限',
'config.advanced.poolSizeLimitPlaceholder': '默认: 0 (不限制)',
'config.advanced.poolSizeLimitNote': '每个提供商类型参与轮询的最大健康凭证数量,0 表示不限制,使用所有健康凭证',
'config.advanced.credentialSwitchMaxRetries': '坏凭证切换最大重试次数',
'config.advanced.credentialSwitchMaxRetriesNote': '认证错误(401/403)后切换凭证的最大重试次数,默认 5 次',
'config.advanced.fallbackChain': '跨类型 Fallback 链配置',
'config.advanced.fallbackChainPlaceholder': '例如:\n{\n "gemini-cli-oauth": ["gemini-antigravity"],\n "gemini-antigravity": ["gemini-cli-oauth"],\n "claude-kiro-oauth": ["claude-custom"]\n}',
'config.advanced.fallbackChainNote': '当某一 Provider Type 所有账号都不健康时,自动切换到配置的 Fallback 类型。JSON 格式,键为主类型,值为 Fallback 类型数组(按优先级排序)',
'config.advanced.fallbackChainInvalid': 'Fallback 链配置格式无效,请输入有效的 JSON',
'config.advanced.modelFallbackMapping': '跨协议模型 Fallback 映射',
'config.advanced.modelFallbackMappingPlaceholder': '例如:\n{\n "gemini-claude-opus-4-5-thinking": {\n "targetProviderType": "claude-kiro-oauth",\n "targetModel": "claude-opus-4-5"\n }\n}',
'config.advanced.modelFallbackMappingNote': '当主 Provider 不可用时,根据模型名映射到其他协议的 Provider 和模型。优先级低于上方的 Fallback 链配置。JSON 格式。',
'config.advanced.modelFallbackMappingInvalid': 'Model Fallback 映射配置格式无效,请输入有效的 JSON',
'config.advanced.systemPrompt': '系统提示',
'config.advanced.systemPromptPlaceholder': '输入系统提示...',
'config.advanced.adminPassword': '后台登录密码',
'config.advanced.adminPasswordPlaceholder': '设置后台登录密码(留空则不修改)',
'config.advanced.adminPasswordNote': '用于保护管理控制台的访问,修改后需要重新登录',
'config.proxy.title': '代理设置',
'config.proxy.url': '代理地址',
'config.proxy.urlPlaceholder': '例如: http://127.0.0.1:7890 或 socks5://127.0.0.1:1080',
'config.proxy.urlNote': '支持 HTTP、HTTPS 和 SOCKS5 代理,留空则不使用代理',
'config.proxy.enabledProviders': '启用代理的提供商',
'config.proxy.enabledProvidersNote': '选择需要通过代理访问的提供商,未选中的提供商将直接连接',
'config.save': '保存配置',
'config.reset': '重置',
'config.placeholder.nodeName': '例如: 我的节点1',
'config.placeholder.model': '例如: gpt-3.5-turbo',
// Upload Config
'upload.title': '凭据文件管理',
'upload.search': '搜索配置',
'upload.searchPlaceholder': '输入文件名',
'upload.providerFilter': '提供商类型',
'upload.providerFilter.all': '全部提供商',
'upload.providerFilter.kiro': 'Kiro OAuth',
'upload.providerFilter.gemini': 'Gemini OAuth',
'upload.providerFilter.qwen': 'Qwen OAuth',
'upload.providerFilter.antigravity': 'Antigravity',
'upload.providerFilter.codex': 'Codex OAuth',
'upload.providerFilter.iflow': 'iFlow OAuth',
'upload.providerFilter.other': '其他/未识别',
'upload.statusFilter': '关联状态',
'upload.statusFilter.all': '全部状态',
'upload.statusFilter.used': '已关联',
'upload.statusFilter.unused': '未关联',
'upload.refresh': '刷新',
'upload.downloadAll': '打包下载',
'upload.listTitle': '配置文件列表',
'upload.count': '共 {count} 个配置文件',
'upload.usedCount': '已关联: {count}',
'upload.unusedCount': '未关联: {count}',
'upload.batchLink': '自动关联oauth',
'upload.noConfigs': '未找到匹配的配置文件',
'upload.detail.path': '文件路径',
'upload.detail.size': '文件大小',
'upload.detail.modified': '最后修改',
'upload.detail.status': '关联状态',
'upload.action.view': '查看',
'upload.action.delete': '删除',
'upload.usage.title': '关联详情 ({type})',
'upload.usage.mainConfig': '主要配置',
'upload.usage.providerPool': '提供商池',
'upload.usage.multiple': '多种用途',
'upload.delete.confirmTitle': '删除配置文件',
'upload.delete.confirmTitleUsed': '删除已关联配置',
'upload.delete.warningUsedTitle': '⚠️ 此配置已被系统使用',
'upload.delete.warningUsedDesc': '删除已关联的配置文件可能会影响系统正常运行。请确保您了解删除的后果。',
'upload.delete.warningUnusedTitle': '🗑️ 确认删除配置文件',
'upload.delete.warningUnusedDesc': '此操作将永久删除配置文件,且无法撤销。',
'upload.delete.fileName': '文件名:',
'upload.delete.usageAlertTitle': '关联详情',
'upload.delete.usageAlertDesc': '此配置文件正在被系统使用,删除后可能会导致:',
'upload.delete.usageAlertItem1': '相关的AI服务无法正常工作',
'upload.delete.usageAlertItem2': '配置管理中的设置失效',
'upload.delete.usageAlertItem3': '提供商池配置丢失',
'upload.delete.usageAlertAdvice': '<strong>建议:</strong>请先在配置管理中解除文件引用后再删除。',
'upload.delete.forceDelete': '强制删除',
'upload.delete.confirmDelete': '确认删除',
'upload.batchLink.confirm': '确定要批量关联 {count} 个配置吗?\n\n{summary}',
'upload.refresh.success': '刷新成功',
'upload.action.view.failed': '查看失败',
'upload.action.delete.failed': '删除失败',
'upload.config.notExist': '配置文件不存在',
'upload.link.identifying': '正在识别提供商类型...',
'upload.link.failed.identify': '无法识别配置文件对应的提供商类型',
'upload.link.processing': '正在关联配置到 {name}...',
'upload.link.success': '配置关联成功',
'upload.link.failed': '关联失败',
'upload.batchLink.none': '没有需要关联的配置文件',
'upload.batchLink.processing': '正在批量关联 {count} 个配置...',
'upload.batchLink.success': '成功关联 {count} 个配置',
'upload.batchLink.partial': '关联完成: 成功 {success} 个, 失败 {fail} 个',
'upload.deleteUnbound': '删除未关联',
'upload.deleteUnbound.none': '没有可删除的未关联配置文件(仅删除 configs/子目录/ 下的文件)',
'upload.deleteUnbound.confirm': '确定要删除 {count} 个未关联的配置文件吗?\n\n注意:仅删除 configs/子目录/ 下的未关联文件,configs/ 根目录下的文件不会被删除。\n\n此操作不可撤销!',
'upload.deleteUnbound.processing': '正在删除未关联的配置文件...',
'upload.deleteUnbound.success': '成功删除 {count} 个未关联的配置文件',
'upload.deleteUnbound.partial': '删除完成: 成功 {success} 个, 失败 {fail} 个',
'upload.deleteUnbound.failed': '删除未关联配置失败',
// Providers
'providers.title': '提供商池管理',
'providers.note': '使用默认路径配置需添加一个空节点',
'providers.activeConnections': '活动连接',
'providers.activeProviders': '活跃提供商',
'providers.healthyProviders': '健康提供商',
'providers.status.healthy': '{healthy}/{total} 健康',
'providers.status.empty': '0/0 节点',
'providers.stat.totalAccounts': '总账户',
'providers.stat.healthyAccounts': '健康账户',
'providers.stat.usageCount': '使用次数',
'providers.stat.errorCount': '错误次数',
'providers.auth.generate': '生成授权',
'providers.auth.importToken': '导入 Token',
// Modal Provider Manager
'modal.provider.manage': '管理 {type} 提供商配置',
'modal.provider.totalAccounts': '总账户数:',
'modal.provider.healthyAccounts': '健康账户:',
'modal.provider.add': '添加新提供商',
'modal.provider.resetHealth': '重置为健康',
'modal.provider.healthCheck': '检测不健康',
'modal.provider.resetHealthConfirm': '确定要将 {type} 的所有节点重置为健康状态吗?\n\n这将清除所有节点的错误计数和错误时间。',
'modal.provider.healthCheckConfirm': '确定要对 {type} 的不健康节点执行健康检测吗?\n\n这将向不健康节点发送测试请求来验证其可用性。',
'modal.provider.deleteConfirm': '确定要删除这个提供商配置吗?此操作不可恢复。',
'modal.provider.disableConfirm': '确定要禁用这个提供商配置吗?禁用后该提供商将不会被选中使用。',
'modal.provider.enableConfirm': '确定要启用这个提供商配置吗?',
'modal.provider.edit': '编辑',
'modal.provider.delete': '删除',
'modal.provider.save': '保存',
'modal.provider.cancel': '取消',
'modal.provider.status.healthy': '正常',
'modal.provider.status.unhealthy': '异常',
'modal.provider.status.disabled': '已禁用',
'modal.provider.status.enabled': '已启用',
'modal.provider.lastError': '最后错误:',
'modal.provider.lastUsed': '最后使用:',
'modal.provider.lastCheck': '最后检测:',
'modal.provider.checkModel': '检测模型:',
'modal.provider.usageCount': '使用次数:',
'modal.provider.errorCount': '失败次数:',
'modal.provider.neverUsed': '从未使用',
'modal.provider.neverChecked': '从未检测',
'modal.provider.noModels': '该提供商类型暂无可用模型列表',
'modal.provider.loadingModels': '加载模型列表...',
'modal.provider.unsupportedModels': '不支持的模型',
'modal.provider.unsupportedModelsHelp': '选择此提供商不支持的模型,系统会自动排除这些模型',
'modal.provider.addTitle': '添加新提供商配置',
'modal.provider.customName': '自定义名称',
'modal.provider.checkModelName': '检查模型名称',
'modal.provider.healthCheckLabel': '健康检查',
'modal.provider.enabled': '启用',
'modal.provider.disabled': '禁用',
'modal.provider.noProviderType': '不支持的提供商类型',
'modal.provider.refreshUuid': '刷新uuid',
'modal.provider.refreshUuidConfirm': '确定要刷新此提供商的uuid吗?\n\n旧uuid: {oldUuid}\n\n刷新后将生成新的uuid,请确保没有其他系统依赖此uuid。',
'modal.provider.refreshUuid.success': 'uuid刷新成功\n\n旧uuid: {oldUuid}\n新uuid: {newUuid}',
'modal.provider.refreshUuid.failed': 'uuid刷新失败',
'modal.provider.field.projectId': '项目 ID',
'modal.provider.field.oauthPath': 'OAuth 凭据文件路径',
'modal.provider.field.baseUrl': 'Base URL',
'modal.provider.field.refreshUrl': 'Refresh URL',
'modal.provider.field.refreshIdcUrl': 'Refresh IDC URL',
'modal.provider.field.oauthBaseUrl': 'OAuth Base URL',
'modal.provider.field.dailyBaseUrl': 'Daily Base URL',
'modal.provider.field.autopushBaseUrl': 'Autopush Base URL',
'modal.provider.field.headerName': 'Header 名称',
'modal.provider.field.headerPrefix': 'Header 值前缀',
'modal.provider.field.useSystemProxy': '使用系统代理',
'modal.provider.field.apiKey': 'API 密钥',
'modal.provider.field.apiKey.placeholder': '请输入 API 密钥',
'modal.provider.field.projectId.placeholder': 'Google Cloud 项目 ID',
'modal.provider.field.projectId.optional.placeholder': 'Google Cloud 项目 ID (留空自动发现)',
'modal.provider.field.oauthPath.gemini.placeholder': '例如: ~/.gemini/oauth_creds.json',
'modal.provider.field.oauthPath.kiro.placeholder': '例如: ~/.aws/sso/cache/kiro-auth-token.json',
'modal.provider.field.oauthPath.qwen.placeholder': '例如: ~/.qwen/oauth_creds.json',
'modal.provider.field.oauthPath.antigravity.placeholder': '例如: ~/.antigravity/oauth_creds.json',
'modal.provider.field.oauthPath.iflow.placeholder': '例如: configs/iflow/oauth_creds.json',
'modal.provider.field.oauthPath.codex.placeholder': '例如: configs/codex/oauth_creds.json',
'modal.provider.field.email': '邮箱',
'modal.provider.field.email.placeholder': '你的邮箱@example.com',
'modal.provider.load.failed': '加载提供商详情失败',
'modal.provider.auth.initializing': '正在初始化凭据生成...',
'modal.provider.auth.success': '凭据已生成并自动填充路径',
'modal.provider.auth.window': '请在打开的窗口中完成授权',
'modal.provider.auth.failed': '初始化凭据生成失败',
'modal.provider.save.success': '保存成功',
'modal.provider.save.failed': '保存失败',
'modal.provider.delete.success': '删除成功',
'modal.provider.delete.failed': '删除失败',
'modal.provider.add.success': '添加成功',
'modal.provider.add.failed': '添加失败',
'modal.provider.resetHealth.success': '成功重置 {count} 个节点的健康状态',
'modal.provider.resetHealth.failed': '重置健康状态失败',
'modal.provider.deleteUnhealthy': '删除不健康节点',
'modal.provider.deleteUnhealthyBtn': '删除不健康',
'modal.provider.deleteUnhealthyConfirm': '确定要删除 {count} 个不健康节点吗?此操作不可恢复。',
'modal.provider.deleteUnhealthy.noUnhealthy': '没有不健康节点',
'modal.provider.deleteUnhealthy.deleting': '正在删除...',
'modal.provider.deleteUnhealthy.success': '已删除 {count} 个节点',
'modal.provider.deleteUnhealthy.failed': '删除失败',
'modal.provider.healthCheck.complete': '健康检查完成: {success} 变为健康',
'modal.provider.healthCheck.abnormal': ', {fail} 异常',
'modal.provider.healthCheck.skipped': ', {skipped} 跳过(未启用)',
'modal.provider.refreshUnhealthyUuids': '刷新不健康UUID',
'modal.provider.refreshUnhealthyUuidsBtn': '刷新UUID',
'modal.provider.refreshUnhealthyUuidsConfirm': '确定要刷新 {count} 个不健康节点的UUID吗?',
'modal.provider.refreshUnhealthyUuids.noUnhealthy': '没有不健康节点',
'modal.provider.refreshUnhealthyUuids.refreshing': '正在刷新...',
'modal.provider.refreshUnhealthyUuids.success': '已刷新 {count} 个节点的UUID',
'modal.provider.refreshUnhealthyUuids.failed': '刷新失败',
'modal.provider.kiroAuthHint': '使用 AWS Builder ID 登录方式时,需要 <code>clientId</code> 和 <code>clientSecret</code> 字段,可在同文件夹下的另一个 JSON 文件中获取',
// Pagination
'pagination.showing': '显示 {start}-{end} / 共 {total} 条',
'pagination.jumpTo': '跳转到',
'pagination.page': '页',
// Usage
'usage.title': '用量查询',
'usage.refresh': '刷新用量',
'usage.lastUpdate': '上次更新: {time}',
'usage.lastUpdateCache': '缓存时间: {time}',
'usage.loading': '正在加载用量数据...',
'usage.empty': '点击"刷新用量"按钮获取授权文件用量信息',
'usage.noData': '暂无用量数据',
'usage.noInstances': '暂无已初始化的服务实例',
'usage.group.instances': '{count} 个实例',
'usage.group.success': '{count}/{total} 成功',
'usage.card.status.disabled': '已禁用',
'usage.card.status.healthy': '健康',
'usage.card.status.unhealthy': '异常',
'usage.card.totalUsage': '总用量',
'usage.card.freeTrial': '免费试用',
'usage.card.bonus': '奖励',
'usage.card.expires': '到期: {time}',
'usage.group.expandAll': '展开所有卡片',
'usage.group.collapseAll': '折叠所有卡片',
// Logs
'logs.title': '实时日志',
'logs.clear': '清空日志',
'logs.autoScroll': '自动滚动',
'logs.autoScroll.on': '自动滚动: 开',
'logs.autoScroll.off': '自动滚动: 关',
// Plugins
'plugins.title': '插件管理',
'plugins.description': '插件系统允许您扩展系统功能,启用或禁用插件需要重启服务才能生效',
'plugins.stats.total': '总插件数',
'plugins.stats.enabled': '已启用',
'plugins.stats.disabled': '已禁用',
'plugins.refresh': '刷新插件列表',
'plugins.loading': '正在加载插件列表...',
'plugins.empty': '暂无已安装的插件',
'plugins.noDescription': '暂无描述',
'plugins.status.enabled': '已启用',
'plugins.status.disabled': '已禁用',
'plugins.badge.middleware.title': '包含中间件',
'plugins.badge.routes.title': '包含路由',
'plugins.badge.hooks.title': '包含钩子',
'plugins.toggle.success': '插件 {name} 已{status}',
'plugins.toggle.failed': '切换插件状态失败',
'plugins.load.failed': '加载插件列表失败',
'plugins.restart.required': '更改已保存',
// Models
'models.title': '可用模型列表',
'models.note': '点击模型名称可直接复制到剪贴板',
'models.empty': '暂无可用模型',
'models.loadError': '加载模型列表失败',
'models.copied': '已复制',
'models.clickToCopy': '点击复制',
// Guide
'guide.title': '使用指南',
'guide.intro.title': '项目简介',
'guide.intro.desc': 'AIClient2API 是一个突破客户端限制的 API 代理服务,将 Gemini、Antigravity、Qwen Code、Kiro 等原本仅限客户端内使用的免费大模型,转换为可供任何应用调用的标准 OpenAI 兼容接口。',
'guide.intro.feature1.title': '统一接入',
'guide.intro.feature1.desc': '通过标准 OpenAI 兼容协议,一次配置即可接入多种大模型',
'guide.intro.feature2.title': '突破限制',
'guide.intro.feature2.desc': '利用 OAuth 授权机制,有效突破免费 API 速率和配额限制',
'guide.intro.feature3.title': '协议转换',
'guide.intro.feature3.desc': '支持 OpenAI、Claude、Gemini 三大协议间的智能转换',
'guide.intro.feature4.title': '账号池管理',
'guide.intro.feature4.desc': '支持多账号轮询、自动故障转移和配置降级',
'guide.providers.title': '支持的模型提供商',
'guide.providers.badge.oauth': 'OAuth 授权',
'guide.providers.badge.experimental': '实验性',
'guide.providers.badge.free': '免费使用',
'guide.providers.badge.official': '官方 API',
'guide.providers.gemini.desc': '通过 Google OAuth 认证访问 Gemini 模型,支持 gemini-2.0-flash-exp 等模型',
'guide.providers.antigravity.desc': '通过 Google 内部接口访问 Gemini 3 Pro、Claude Sonnet 4.5 等模型',
'guide.providers.kiro.desc': '通过 Kiro 客户端免费使用 Claude Opus 4.5、Claude Sonnet 4.5 等模型',
'guide.providers.qwen.desc': '通过阿里云 OAuth 认证访问 Qwen3 Coder Plus 等模型',
'guide.providers.claude.desc': '使用 Claude 官方 API 或第三方代理访问 Claude 系列模型',
'guide.providers.openai.desc': '使用 OpenAI 官方 API 或第三方代理访问 GPT 系列模型',
'guide.providers.iflow.desc': '通过 iFlow OAuth 认证访问 Qwen、Kimi、DeepSeek、GLM 等模型',
'guide.client.title': '客户端配置指南',
'guide.client.desc': '以下是常见 AI 客户端的配置方法,将 API 端点设置为本服务地址即可使用:',
'guide.client.cherry.step1': '打开设置 → 模型服务商',
'guide.client.cherry.step2': '添加自定义服务商',
'guide.client.cherry.step3': '设置 API 地址为: http://localhost:3000/{provider}',
'guide.client.cherry.step4': '填入 API Key(配置文件中的 REQUIRED_API_KEY)',
'guide.client.cline.step1': '打开 VS Code 设置',
'guide.client.cline.step2': '搜索 Cline 或 Continue 配置',
'guide.client.cline.step3': '设置 API Base URL 为: http://localhost:3000/{provider}/v1',
'guide.client.cline.step4': '填入 API Key 和模型名称',
'guide.client.note': '提示:将 {provider} 替换为实际的提供商路径,如 gemini-cli-oauth、claude-kiro-oauth 等。可在仪表盘的路由示例中查看完整路径。',
'guide.ollama.title': 'Ollama 协议使用',
'guide.ollama.desc': '本项目支持 Ollama 协议,可以通过统一接口访问所有支持的模型。',
'guide.ollama.listModels': '列出所有可用模型',
'guide.ollama.chat': '聊天接口',
'guide.faq.title': '常见问题',
'guide.faq.q1': 'Q: 请求返回 404 错误怎么办?',
'guide.faq.a1': 'A: 检查接口路径是否正确。某些客户端会自动在 Base URL 后追加路径,导致路径重复。请查看控制台中的实际请求 URL,移除多余的路径部分。',
'guide.faq.q2': 'Q: 请求返回 429 错误怎么办?',
'guide.faq.a2': 'A: 429 表示请求频率过高。建议配置多个账号到提供商池,启用轮询机制;或配置 Fallback 链实现跨类型降级。',
'guide.faq.q3': 'Q: OAuth 授权失败怎么办?',
'guide.faq.a3': 'A: 确保 OAuth 回调端口可访问(Gemini: 8085, Antigravity: 8086, Kiro: 19876-19880)。Docker 用户需确保已正确映射这些端口。',
'guide.faq.q4': 'Q: 如何查看可用的模型列表?',
'guide.faq.a4': 'A: 在侧边栏点击"可用模型"页面,可以查看所有已配置提供商支持的模型列表。点击模型名称可直接复制。',
'guide.faq.q5': 'Q: 流式响应中断怎么办?',
'guide.faq.a5': 'A: 检查网络稳定性,增加客户端请求超时时间。如使用代理,确保代理支持长连接。',
// Guide - Flow
'guide.flow.title': '操作流程图',
'guide.flow.step1.title': '配置管理',
'guide.flow.step1.desc': '在「配置管理」页面设置基本参数',
'guide.flow.step2.title': '生成授权',
'guide.flow.step2.desc': '在「提供商池管理」页面生成 OAuth 授权',
'guide.flow.step3.title': '管理凭据',
'guide.flow.step3.desc': '在「凭据文件管理」页面查看和管理凭据',
'guide.flow.step4.title': '开始使用',
'guide.flow.step4.desc': '在「仪表盘」查看路由示例并开始调用 API',
// Tutorial
'tutorial.title': '配置教程',
'tutorial.config.title': '配置文件说明',
'tutorial.config.desc': '所有配置文件都存放在 configs/ 目录下。主要配置文件包括:',
'tutorial.config.badge.required': '必需',
'tutorial.config.badge.optional': '可选',
'tutorial.config.file.config': '主配置文件,包含 API Key、端口、模型提供商等核心设置',
'tutorial.config.file.pools': '提供商池配置,用于多账号轮询和故障转移',
'tutorial.config.file.plugins': '插件配置,用于启用或禁用系统插件',
'tutorial.config.file.pwd': '后台登录密码文件,默认密码为 admin123',
'tutorial.main.title': '主配置详解 (config.json)',
'tutorial.main.table.param': '参数',
'tutorial.main.table.type': '类型',
'tutorial.main.table.default': '默认值',
'tutorial.main.table.desc': '说明',
'tutorial.main.basic.title': '基础配置',
'tutorial.main.basic.apikey': '访问本服务所需的 API Key',
'tutorial.main.basic.port': '服务监听端口',
'tutorial.main.basic.host': '服务监听地址',
'tutorial.main.basic.provider': '默认模型提供商',
'tutorial.main.prompt.title': '系统提示配置',
'tutorial.main.prompt.file': '系统提示文件路径',
'tutorial.main.prompt.mode': '系统提示模式:overwrite(覆盖) 或 append(追加)',
'tutorial.main.retry.title': '重试配置',
'tutorial.main.retry.max': '最大重试次数',
'tutorial.main.retry.delay': '重试基础延迟(毫秒)',
'tutorial.main.retry.error': '提供商最大错误次数,超过后标记为不健康',
'tutorial.main.example.title': '配置示例',
'tutorial.pool.title': '提供商池配置 (provider_pools.json)',
'tutorial.pool.desc': '提供商池用于配置多个账号,实现负载均衡和故障转移。每个提供商类型可以配置多个账号节点。',
'tutorial.pool.node.title': '节点配置参数',
'tutorial.pool.node.uuid': '节点唯一标识,自动生成',
'tutorial.pool.node.name': '节点自定义名称',
'tutorial.pool.node.oauth': 'OAuth 凭据文件路径',
'tutorial.pool.node.health': '是否启用健康检查',
'tutorial.pool.node.model': '健康检查使用的模型',
'tutorial.pool.node.unsupported': '该节点不支持的模型列表',
'tutorial.pool.node.disabled': '是否禁用该节点',
'tutorial.pool.example.title': '配置示例',
'tutorial.fallback.title': 'Fallback 降级配置',
'tutorial.fallback.desc': '当某一提供商类型的所有账号都不可用时,可以自动切换到配置的备用提供商。',
'tutorial.fallback.chain.title': '跨类型 Fallback 链',
'tutorial.fallback.chain.desc': '在 config.json 中配置 providerFallbackChain,指定每个提供商类型的备用类型:',
'tutorial.fallback.model.title': '跨协议模型映射',
'tutorial.fallback.model.desc': '当主提供商不可用时,可以将特定模型映射到其他协议的提供商:',
'tutorial.proxy.title': '代理配置',
'tutorial.proxy.desc': '支持为特定提供商配置代理,用于网络受限环境。',
'tutorial.proxy.config.title': '代理配置参数',
'tutorial.proxy.url': '代理地址,支持 HTTP、HTTPS、SOCKS5',
'tutorial.proxy.providers': '启用代理的提供商列表',
'tutorial.proxy.example.title': '配置示例',
'tutorial.proxy.note': '支持的代理类型:HTTP (http://)、HTTPS (https://)、SOCKS5 (socks5://)',
'tutorial.oauth.title': 'OAuth 授权配置',
'tutorial.oauth.desc': '各提供商的 OAuth 凭据文件默认存储位置:',
'tutorial.oauth.note': '推荐通过 Web UI 控制台的"提供商池管理"页面点击"生成授权"按钮进行可视化授权,系统会自动保存凭据文件。',
'tutorial.log.title': '日志配置',
'tutorial.log.prompt.title': '提示日志配置',
'tutorial.log.mode': '日志模式:none(关闭)、console(控制台)、file(文件)',
'tutorial.log.basename': '日志文件基础名称',
'tutorial.log.example.title': '配置示例',
// Common
'common.confirm': '确定',
'common.cancel': '取消',
'common.success': '成功',
'common.error': '错误',
'common.warning': '警告',
'common.info': '信息',
'common.loading': '加载中...',
'common.upload': '上传',
'common.generate': '生成',
'common.optional': '可选',
'common.found': '已找到',
'common.missing': '缺失',
'common.search': '搜索',
'common.welcome': '欢迎使用AIClient2API管理控制台!',
'common.fileType': '不支持的文件类型,请选择 JSON、TXT、KEY、PEM、P12 或 PFX 文件',
'common.fileSize': '文件大小不能超过 5MB',
'common.uploadSuccess': '文件上传成功',
'common.uploadFailed': '文件上传失败',
'common.passwordUpdated': '后台密码已更新,下次登录生效',
'common.configSaved': '配置已保存',
'common.providerPoolRefreshed': '提供商池数据已刷新',
'common.togglePassword': '显示/隐藏密码',
'common.copy.success': '内容已复制到剪贴板',
'common.copy.failed': '复制失败,请手动复制',
'common.refresh.success': '刷新成功',
'common.refresh.failed': '刷新失败',
// Login
'login.title': '登录 - AIClient2API',
'login.heading': '请登录以继续',
'login.password': '密码',
'login.passwordPlaceholder': '请输入密码',
'login.error.empty': '请输入密码',
'login.error.incorrect': '密码错误,请重试',
'login.error.failed': '登录失败,请检查网络连接',
'login.button': '登录',
'login.loggingIn': '登录中...',
},
'en-US': {
// Header
'header.title': 'AIClient2API Management Console',
'header.description': 'AIClient2API Management Console - Unified management of AI service providers',
'header.github': 'GitHub Repository',
'header.themeToggle': 'Toggle Theme',
'header.status.connecting': 'Connecting...',
'header.status.connected': 'Connected',
'header.status.disconnected': 'Disconnected',
'header.logout': 'Logout',
'header.reload': 'Reload',
'header.reload.confirm': 'Are you sure you want to reload the configuration? This will reload all configuration files.',
'header.reload.requesting': 'Reloading configuration...',
'header.reload.success': 'Configuration reloaded successfully',
'header.reload.failed': 'Failed to reload configuration',
'header.refresh': 'Reload',
'header.restart': 'Restart',
'header.restart.confirm': 'Are you sure you want to restart the service? The service will be briefly interrupted.',
'header.restart.requesting': 'Requesting service restart...',
'header.restart.success': 'Restart request sent, service will restart shortly',
'header.restart.reconnecting': 'Reconnecting...',
'header.restart.failed': 'Failed to restart service',
// Navigation
'nav.main': 'Main Navigation',
'nav.dashboard': 'Dashboard',
'nav.guide': 'User Guide',
'nav.tutorial': 'Configuration Tutorial',
'nav.config': 'Configuration',
'nav.providers': 'Provider Pools',
'nav.upload': 'Credential Files',
'nav.usage': 'Usage Query',
'nav.logs': 'Real-time Logs',
'nav.plugins': 'Plugin Management',
'nav.models': 'Available Models',
// Dashboard
'dashboard.title': 'System Overview',
'dashboard.uptime': 'Uptime',
'dashboard.systemInfo': 'System Information',
'dashboard.version': 'Version',
'dashboard.update.check': 'Check Update',
'dashboard.update.checkTitle': 'Check for new version',
'dashboard.update.perform': 'Update Now',
'dashboard.update.performTitle': 'Update to latest version',
'dashboard.update.checking': 'Checking...',
'dashboard.update.upToDate': 'Up to date',
'dashboard.update.hasUpdate': 'New version available: {version}',
'dashboard.update.updating': 'Updating...',
'dashboard.update.success': 'Update successful',
'dashboard.update.needsRestart': 'Code updated, please click the "Restart" button in the top right corner for changes to take effect',
'dashboard.update.restartTitle': 'Update Complete',
'dashboard.update.restartMsg': 'Code has been updated to version {version}. Please click the "Restart" button in the top right corner for the new code to take effect.',
'dashboard.update.failed': 'Update failed: {error}',
'dashboard.update.confirmTitle': 'Confirm Update',
'dashboard.update.confirmMsg': 'Are you sure you want to update to version {version}? Service might be briefly unavailable during update.',
'dashboard.nodeVersion': 'Node.js Version',
'dashboard.serverTime': 'Server Time',
'dashboard.memoryUsage': 'Memory Usage',
'dashboard.cpuUsage': 'CPU Usage',
'dashboard.serviceMode': 'Service Mode',
'dashboard.serviceMode.worker': 'Worker Mode',
'dashboard.serviceMode.standalone': 'Standalone Mode',
'dashboard.serviceMode.canRestart': 'Auto-restart supported',
'dashboard.processPid': 'Process PID',
'dashboard.platform': 'Platform',
'dashboard.routing.title': 'Path Routing Examples',
'dashboard.routing.description': 'Access different AI model providers through different path routes, supporting flexible model switching',
'dashboard.routing.oauth': 'Limit Breakthrough',
'dashboard.routing.official': 'Official/Third-party API',
'dashboard.routing.experimental': 'Limit Breakthrough/Experimental',
'dashboard.routing.free': 'Limit Breakthrough/Free',
'dashboard.routing.endpoint': 'Endpoint Path:',
'dashboard.routing.example': 'Usage Example',
'dashboard.routing.exampleOpenAI': 'Usage Example (OpenAI):',
'dashboard.routing.exampleClaude': 'Usage Example (Claude):',
'dashboard.routing.openai': 'OpenAI Protocol',
'dashboard.routing.claude': 'Claude Protocol',
'dashboard.routing.tips': 'Usage Tips',
'dashboard.routing.tip1': 'Instant Switch: Switch between different AI model providers by modifying the URL path',
'dashboard.routing.tip2': 'Client Configuration: Set API endpoint to corresponding path in clients like Cherry-Studio, NextChat, Cline',
'dashboard.routing.tip3': 'Cross-protocol Calls: Support calling Claude models with OpenAI protocol, or OpenAI models with Claude protocol',
'dashboard.routing.nodeName.gemini': 'Gemini CLI OAuth',
'dashboard.routing.nodeName.antigravity': 'Gemini Antigravity',
'dashboard.routing.nodeName.claude': 'Claude Custom',
'dashboard.routing.nodeName.kiro': 'Claude Kiro OAuth',
'dashboard.routing.nodeName.openai': 'OpenAI Custom',
'dashboard.routing.nodeName.qwen': 'Qwen OAuth',
'dashboard.routing.nodeName.iflow': 'iFlow OAuth',
'dashboard.routing.nodeName.codex': 'OpenAI Codex OAuth',
'dashboard.contact.title': 'Contact & Support',
'dashboard.contact.wechat': 'Scan to Join Group',
'dashboard.contact.wechatDesc': 'Add WeChat for more technical support and communication',
'dashboard.contact.x': 'Follow on X.com',
'dashboard.contact.xDesc': 'Follow us on X for latest updates',
'dashboard.contact.sponsor': 'Scan to Support',
'dashboard.contact.sponsorDesc': 'Your support is the driving force for the project\'s continuous development',
'dashboard.contact.coffee': 'Buy me a coffee',
'dashboard.contact.coffeeDesc': 'If you like this project, buy me a coffee!',
// OAuth
'oauth.modal.title': 'OAuth Authorization',
'oauth.modal.provider': 'Provider:',
'oauth.modal.requiredPort': 'Required Port:',
'oauth.modal.portNote': 'Please ensure this port is accessible externally for receiving authorization callbacks',
'oauth.modal.steps': 'Authorization Steps:',
'oauth.modal.step1': 'Click the button below to open the authorization page in your browser',
'oauth.modal.step2.qwen': 'After authorization, the system will automatically fetch the credentials file',
'oauth.modal.step2.google': 'Log in with your Google account and authorize',
'oauth.modal.step3': 'Credentials files can be viewed and managed in Upload Config',
'oauth.modal.step4.qwen': 'Authorization valid for: {min} minutes',
'oauth.modal.step4.google': 'After authorization, the credentials file will be saved automatically',
'oauth.modal.urlLabel': 'Auth URL:',
'oauth.modal.copyTitle': 'Copy Link',
'oauth.modal.openInBrowser': 'Open in Browser',
'oauth.manual.title': 'Auto-listener blocked?',
'oauth.manual.desc': 'If the auth window shows "Cannot access" after redirect, please paste the <strong>Full URL</strong> from that window\'s address bar below:',
'oauth.manual.placeholder': 'Paste callback URL (contains code=...)',
'oauth.manual.submit': 'Submit',
'oauth.success.msg': 'Authorization link copied to clipboard',
'oauth.window.blocked': 'Auth window was blocked by the browser, please allow pop-ups',
'oauth.window.opened': 'Auth window opened, please complete the process there',
'oauth.processing': 'Completing authorization...',
'oauth.invalid.url': 'This URL does not seem to contain a valid auth code',
'oauth.error.format': 'Invalid URL format',
'oauth.kiro.selectMethod': 'Select Authentication Method',
'oauth.kiro.google': 'Google Account Login',
'oauth.kiro.googleDesc': 'Login with Google account',
'oauth.kiro.github': 'GitHub Account Login',
'oauth.kiro.githubDesc': 'Login with GitHub account',
'oauth.kiro.awsBuilder': 'AWS Builder ID',
'oauth.kiro.awsBuilderDesc': 'Device code authorization via AWS Builder ID',
'oauth.kiro.authMethodLabel': 'Auth Method:',
'oauth.kiro.step1': 'Click the button below to open the authorization link in your browser',
'oauth.kiro.step2': 'Log in with your {method} account',
'oauth.kiro.step3': 'The page will close automatically after authorization',
'oauth.kiro.step4': 'Refresh this page to view the credentials file',
'oauth.kiro.batchImport': 'Batch Import refreshToken',
'oauth.kiro.batchImportDesc': 'Batch import existing refresh tokens to generate credential files. This mode does not support AWS accounts.',
'oauth.kiro.batchImportInstructions': 'Enter refreshTokens, one per line. The system will automatically refresh and generate credential files.',
'oauth.kiro.awsImport': 'Import AWS Account',
'oauth.kiro.awsImportDesc': 'Import credential files from AWS SSO cache directory. For AWS Builder ID mode.',
'oauth.kiro.awsImportInstructions': 'Upload JSON files from AWS SSO cache directory. Must contain clientId, clientSecret, accessToken, and refreshToken. For AWS enterprise users, add the idcRegion field.',
'oauth.kiro.awsModeFile': 'File Upload',
'oauth.kiro.awsModeJson': 'Paste JSON',
'oauth.kiro.awsUploadFiles': 'Upload Credential Files',
'oauth.kiro.awsDragDrop': 'Drag and drop files here',
'oauth.kiro.awsClickUpload': 'or click to select files',
'oauth.kiro.awsFileHint': 'If one file doesn\'t contain all fields, you can upload multiple files to complete them',
'oauth.kiro.awsSelectedFiles': 'Selected Files',
'oauth.kiro.awsClearFiles': 'Clear All',
'oauth.kiro.awsFileReplaced': 'Replaced file: {filename}',
'oauth.kiro.awsJsonInput': 'Paste JSON Credentials',
'oauth.kiro.awsJsonPlaceholderSimple': 'Paste JSON containing clientId, clientSecret, accessToken, refreshToken here...',
'oauth.kiro.awsJsonExample': 'View JSON format example',
'oauth.kiro.awsJsonHint': 'You can paste merged JSON directly, or copy content from AWS SSO cache files',
'oauth.kiro.awsJsonParseError': 'Invalid JSON format',
'oauth.kiro.awsParseError': 'Failed to parse file {filename}',
'oauth.kiro.awsValidationSuccess': 'Validation passed! All required fields found',
'oauth.kiro.awsValidationFailed': 'Validation failed! Required fields missing',
'oauth.kiro.awsMissingFields': '{count} field(s) missing',
'oauth.kiro.awsUploadMore': 'Please upload files containing the missing fields, or switch to JSON mode to complete manually',
'oauth.kiro.awsPreviewJson': 'Merged Credentials Preview',
'oauth.kiro.awsConfirmImport': 'Confirm Import',
'oauth.kiro.awsNoCredentials': 'No credentials to import',
'oauth.kiro.awsImporting': 'Importing...',
'oauth.kiro.awsImportSuccess': 'AWS credentials imported successfully!',
'oauth.kiro.awsImportFailed': 'AWS credentials import failed',
'oauth.kiro.refreshTokensLabel': 'RefreshToken List',
'oauth.kiro.refreshTokensPlaceholder': 'Enter one refreshToken per line\nExample:\naorAxxxxxxxx\naorAyyyyyyyy\naorAzzzzzzzz',
'oauth.kiro.tokenCount': 'Tokens to import:',
'oauth.kiro.importing': 'Importing, please wait...',
'oauth.kiro.importingProgress': 'Importing {current}/{total}...',
'oauth.kiro.startImport': 'Start Import',
'oauth.kiro.noTokens': 'Please enter at least one refreshToken',
'oauth.kiro.importSuccess': 'Import successful! {count} credentials generated',
'oauth.kiro.importAllFailed': 'Import failed! {count} tokens failed to refresh',
'oauth.kiro.importPartial': 'Partial success: {success} succeeded, {failed} failed',
'oauth.kiro.importError': 'Import error',
'oauth.kiro.duplicateToken': 'Duplicate - this refreshToken already exists',
'oauth.kiro.duplicateCredentials': 'This credential already exists, please do not import duplicates',
'oauth.kiro.builderIDStartURL': 'Builder ID Start URL',
'oauth.kiro.builderIDStartURLHint': 'If you use AWS IAM Identity Center, enter your Start URL',
'oauth.iflow.step1': 'Click the button below to open the iFlow authorization page',
'oauth.iflow.step2': 'Log in with your iFlow account and authorize',
'oauth.iflow.step3': 'After authorization, the system will automatically fetch the API Key',
'oauth.iflow.step4': 'Credentials files can be viewed and managed in Upload Config',
// Config
'config.title': 'Configuration Management',
'config.apiKey': 'API Key',
'config.apiKeyPlaceholder': 'Please enter API key',
'config.host': 'Listen Address',
'config.hostPlaceholder': 'e.g.: 127.0.0.1',
'config.port': 'Port',
'config.portPlaceholder': '3000',
'config.modelProvider': 'Model Provider',
'config.modelProviderHelp': 'Check model providers to initialize on startup (must select at least one)',
'config.modelProviderRequired': 'At least one model provider must be selected',
'config.optional': '(Optional)',
'config.gemini.baseUrl': 'Gemini Base URL',
'config.gemini.baseUrlPlaceholder': 'https://cloudcode-pa.googleapis.com',
'config.gemini.projectId': 'Project ID',
'config.gemini.projectIdPlaceholder': 'Google Cloud Project ID',
'config.gemini.oauthCreds': 'OAuth Credentials',
'config.gemini.credsType.file': 'File Path',
'config.gemini.credsType.base64': 'Base64 Encoded',
'config.gemini.credsBase64': 'OAuth Credentials (Base64)',
'config.gemini.credsBase64Placeholder': 'Please enter Base64 encoded OAuth credentials',
'config.gemini.credsFilePath': 'OAuth Credentials File Path',
'config.gemini.credsFilePathPlaceholder': 'e.g.: ~/.gemini/oauth_creds.json',
'config.antigravity.dailyUrl': 'Daily Base URL',
'config.antigravity.dailyUrlPlaceholder': 'https://daily-cloudcode-pa.sandbox.googleapis.com',
'config.antigravity.autopushUrl': 'Autopush Base URL',
'config.antigravity.autopushUrlPlaceholder': 'https://autopush-cloudcode-pa.sandbox.googleapis.com',
'config.antigravity.credsFilePath': 'OAuth Credentials File Path',
'config.antigravity.credsFilePathPlaceholder': 'e.g.: ~/.antigravity/oauth_creds.json',
'config.antigravity.note': 'Antigravity uses Google OAuth authentication, requires credentials file path',
'config.openai.apiKey': 'OpenAI API Key',
'config.openai.apiKeyPlaceholder': 'sk-...',
'config.openai.baseUrl': 'OpenAI Base URL',
'config.openai.baseUrlPlaceholder': 'e.g.: https://api.openai.com/v1',
'config.claude.apiKey': 'Claude API Key',
'config.claude.apiKeyPlaceholder': 'sk-ant-...',
'config.claude.baseUrl': 'Claude Base URL',
'config.claude.baseUrlPlaceholder': 'e.g.: https://api.anthropic.com',
'config.kiro.baseUrl': 'Base URL',
'config.kiro.baseUrlPlaceholder': 'https://codewhisperer.{{region}}.amazonaws.com/generateAssistantResponse',
'config.kiro.refreshUrl': 'Refresh URL',
'config.kiro.refreshUrlPlaceholder': 'https://prod.{{region}}.auth.desktop.kiro.dev/refreshToken',
'config.kiro.refreshIdcUrl': 'Refresh IDC URL',
'config.kiro.refreshIdcUrlPlaceholder': 'https://oidc.{{region}}.amazonaws.com/token',
'config.kiro.credsFilePath': 'OAuth Credentials File Path',
'config.kiro.credsFilePathPlaceholder': 'e.g.: ~/.aws/sso/cache/kiro-auth-token.json',
'config.kiro.note': 'When using AWS login method, ensure the authorization file contains clientId and clientSecret fields',
'config.qwen.baseUrl': 'Qwen Base URL',
'config.qwen.baseUrlPlaceholder': 'https://portal.qwen.ai/v1',
'config.qwen.oauthBaseUrl': 'OAuth Base URL',
'config.qwen.oauthBaseUrlPlaceholder': 'https://chat.qwen.ai',
'config.qwen.credsFilePath': 'OAuth Credentials File Path',
'config.qwen.credsFilePathPlaceholder': 'e.g.: ~/.qwen/oauth_creds.json',
'config.advanced.title': 'Advanced Configuration',
'config.advanced.systemPromptFile': 'System Prompt File Path',
'config.advanced.systemPromptFilePlaceholder': 'e.g.: configs/input_system_prompt.txt',
'config.advanced.systemPromptMode': 'System Prompt Mode',
'config.advanced.systemPromptMode.append': 'Append',
'config.advanced.systemPromptMode.overwrite': 'Overwrite',
'config.advanced.promptLogBaseName': 'Prompt Log Base Name',
'config.advanced.promptLogBaseNamePlaceholder': 'e.g.: prompt_log',
'config.advanced.promptLogMode': 'Prompt Log Mode',
'config.advanced.promptLogMode.none': 'None',
'config.advanced.promptLogMode.console': 'Console',
'config.advanced.promptLogMode.file': 'File',
'config.advanced.maxRetries': 'Max Retries',
'config.advanced.baseDelay': 'Base Retry Delay (ms)',
'config.advanced.credentialSwitchMaxRetries': 'Credential Switch Max Retries',
'config.advanced.credentialSwitchMaxRetriesNote': 'Max retry count for switching credentials after auth errors (401/403), default 5',
'config.advanced.warmupTarget': 'Warmup Target Nodes',
'config.advanced.warmupTargetNote': 'Number of nodes to refresh on startup, default 0',
'config.advanced.refreshConcurrencyPerProvider': 'Refresh Concurrency per Provider',
'config.advanced.refreshConcurrencyPerProviderNote': 'Max parallel refresh tasks per provider, default 1',
'config.advanced.cronInterval': 'OAuth Token Refresh Interval (minutes)',
'config.advanced.cronEnabled': 'Enable OAuth Token Auto Refresh (requires restart)',
'config.advanced.poolFilePath': 'Provider Pool Config File Path (required)',
'config.advanced.poolFilePathPlaceholder': 'Default: configs/provider_pools.json',
'config.advanced.poolNote': 'To use default path configuration, add an empty node',
'config.advanced.maxErrorCount': 'Provider Max Error Count',
'config.advanced.maxErrorCountPlaceholder': 'Default: 10',
'config.advanced.maxErrorCountNote': 'Provider will be marked as unhealthy after consecutive errors reach this count, default is 10',
'config.advanced.poolSizeLimit': 'Pool Size Limit',
'config.advanced.poolSizeLimitPlaceholder': 'Default: 0 (no limit)',
'config.advanced.poolSizeLimitNote': 'Maximum number of healthy credentials per provider type for rotation. 0 means no limit, use all healthy credentials',
'config.advanced.credentialSwitchMaxRetries': 'Credential Switch Max Retries',
'config.advanced.credentialSwitchMaxRetriesNote': 'Maximum retries for switching credentials after authentication errors (401/403), default is 5',
'config.advanced.fallbackChain': 'Cross-Type Fallback Chain Config',
'config.advanced.fallbackChainPlaceholder': 'Example:\n{\n "gemini-cli-oauth": ["gemini-antigravity"],\n "gemini-antigravity": ["gemini-cli-oauth"],\n "claude-kiro-oauth": ["claude-custom"]\n}',
'config.advanced.fallbackChainNote': 'When all accounts of a Provider Type are unhealthy, automatically switch to configured Fallback types. JSON format, key is primary type, value is Fallback type array (sorted by priority)',
'config.advanced.fallbackChainInvalid': 'Invalid Fallback chain config format, please enter valid JSON',
'config.advanced.modelFallbackMapping': 'Cross-Protocol Model Fallback Mapping',
'config.advanced.modelFallbackMappingPlaceholder': 'Example:\n{\n "gemini-claude-opus-4-5-thinking": {\n "targetProviderType": "claude-kiro-oauth",\n "targetModel": "claude-opus-4-5"\n }\n}',
'config.advanced.modelFallbackMappingNote': 'When the primary Provider is unavailable, map to other protocol Providers and models by model name. Priority is lower than the Fallback Chain Config above. JSON format.',
'config.advanced.modelFallbackMappingInvalid': 'Invalid Model Fallback mapping config format, please enter valid JSON',
'config.advanced.systemPrompt': 'System Prompt',
'config.advanced.systemPromptPlaceholder': 'Enter system prompt...',
'config.advanced.adminPassword': 'Admin Password',
'config.advanced.adminPasswordPlaceholder': 'Set admin password (leave empty to keep unchanged)',
'config.advanced.adminPasswordNote': 'Used to protect management console access, requires re-login after modification',
'config.proxy.title': 'Proxy Settings',
'config.proxy.url': 'Proxy URL',
'config.proxy.urlPlaceholder': 'e.g.: http://127.0.0.1:7890 or socks5://127.0.0.1:1080',
'config.proxy.urlNote': 'Supports HTTP, HTTPS and SOCKS5 proxies. Leave empty to disable proxy',
'config.proxy.enabledProviders': 'Providers Using Proxy',
'config.proxy.enabledProvidersNote': 'Select providers that should use the proxy. Unselected providers will connect directly',
'config.save': 'Save Configuration',
'config.reset': 'Reset',
'config.placeholder.nodeName': 'e.g.: My Node 1',
'config.placeholder.model': 'e.g.: gpt-3.5-turbo',
// Upload Config
'upload.title': 'Credential Files Management',
'upload.search': 'Search Config',
'upload.searchPlaceholder': 'Enter filename',
'upload.providerFilter': 'Provider Type',
'upload.providerFilter.all': 'All Providers',
'upload.providerFilter.kiro': 'Kiro OAuth',
'upload.providerFilter.gemini': 'Gemini OAuth',
'upload.providerFilter.qwen': 'Qwen OAuth',
'upload.providerFilter.antigravity': 'Antigravity',
'upload.providerFilter.codex': 'Codex OAuth',
'upload.providerFilter.iflow': 'iFlow OAuth',
'upload.providerFilter.other': 'Other/Unknown',
'upload.statusFilter': 'Association Status',
'upload.statusFilter.all': 'All Status',
'upload.statusFilter.used': 'Associated',
'upload.statusFilter.unused': 'Not Associated',
'upload.refresh': 'Refresh',
'upload.downloadAll': 'Download All (ZIP)',
'upload.listTitle': 'Configuration File List',
'upload.count': 'Total {count} config files',
'upload.usedCount': 'Associated: {count}',
'upload.unusedCount': 'Not Associated: {count}',
'upload.batchLink': 'Auto Link OAuth',
'upload.noConfigs': 'No matching configuration files found',
'upload.detail.path': 'File Path',
'upload.detail.size': 'File Size',
'upload.detail.modified': 'Last Modified',
'upload.detail.status': 'Status',
'upload.action.view': 'View',
'upload.action.delete': 'Delete',
'upload.usage.title': 'Association Details ({type})',
'upload.usage.mainConfig': 'Main Config',
'upload.usage.providerPool': 'Provider Pool',
'upload.usage.multiple': 'Multiple Purposes',
'upload.delete.confirmTitle': 'Delete Config File',
'upload.delete.confirmTitleUsed': 'Delete Associated Config',
'upload.delete.warningUsedTitle': '⚠️ This config is currently in use',
'upload.delete.warningUsedDesc': 'Deleting an associated config file may affect system stability. Please ensure you understand the consequences.',
'upload.delete.warningUnusedTitle': '🗑️ Confirm deletion',
'upload.delete.warningUnusedDesc': 'This operation will permanently delete the config file and cannot be undone.',
'upload.delete.fileName': 'File Name:',
'upload.delete.usageAlertTitle': 'Association Details',
'upload.delete.usageAlertDesc': 'This file is being used by the system. Deletion may cause:',
'upload.delete.usageAlertItem1': 'Related AI services to stop working',
'upload.delete.usageAlertItem2': 'Settings in Config Management to become invalid',
'upload.delete.usageAlertItem3': 'Provider pool configurations to be lost',
'upload.delete.usageAlertAdvice': '<strong>Advice:</strong> Please remove file references in Config Management before deleting.',
'upload.delete.forceDelete': 'Force Delete',
'upload.delete.confirmDelete': 'Confirm Delete',
'upload.batchLink.confirm': 'Are you sure you want to link {count} config files?\n\n{summary}',
'upload.refresh.success': 'Refresh successful',
'upload.action.view.failed': 'View failed',
'upload.action.delete.failed': 'Delete failed',
'upload.config.notExist': 'Configuration file does not exist',
'upload.link.identifying': 'Identifying provider type...',
'upload.link.failed.identify': 'Unable to identify provider type for the config file',
'upload.link.processing': 'Linking configuration to {name}...',
'upload.link.success': 'Configuration linked successfully',
'upload.link.failed': 'Link failed',
'upload.batchLink.none': 'No configuration files to link',
'upload.batchLink.processing': 'Batch linking {count} configurations...',
'upload.batchLink.success': 'Successfully linked {count} configurations',
'upload.batchLink.partial': 'Linking completed: {success} succeeded, {fail} failed',
'upload.deleteUnbound': 'Delete Unbound',
'upload.deleteUnbound.none': 'No unbound config files to delete (only files in configs/subdirectory/ are deleted)',
'upload.deleteUnbound.confirm': 'Are you sure you want to delete {count} unbound config files?\n\nNote: Only unbound files in configs/subdirectory/ will be deleted. Files directly in configs/ root will not be deleted.\n\nThis action cannot be undone!',
'upload.deleteUnbound.processing': 'Deleting unbound config files...',
'upload.deleteUnbound.success': 'Successfully deleted {count} unbound config files',
'upload.deleteUnbound.partial': 'Deletion completed: {success} succeeded, {fail} failed',
'upload.deleteUnbound.failed': 'Failed to delete unbound configs',
// Providers
'providers.title': 'Provider Pool Management',
'providers.note': 'To use default path configuration, add an empty node',
'providers.activeConnections': 'Active Connections',
'providers.activeProviders': 'Active Providers',
'providers.healthyProviders': 'Healthy Providers',
'providers.status.healthy': '{healthy}/{total} Healthy',
'providers.status.empty': '0/0 Nodes',
'providers.stat.totalAccounts': 'Total Accounts',
'providers.stat.healthyAccounts': 'Healthy Accounts',
'providers.stat.usageCount': 'Usage Count',
'providers.stat.errorCount': 'Error Count',
'providers.auth.generate': 'Gen Auth',
'providers.auth.importToken': 'Import Token',
// Modal Provider Manager
'modal.provider.manage': 'Manage {type} Provider Config',
'modal.provider.totalAccounts': 'Total Accounts:',
'modal.provider.healthyAccounts': 'Healthy Accounts:',
'modal.provider.add': 'Add Provider',
'modal.provider.resetHealth': 'Reset Health',
'modal.provider.healthCheck': 'Check Unhealthy',
'modal.provider.resetHealthConfirm': 'Are you sure you want to reset all {type} nodes to healthy status?\n\nThis will clear error counts and timestamps for all nodes.',
'modal.provider.healthCheckConfirm': 'Are you sure you want to perform a health check on unhealthy {type} nodes?\n\nThis will send test requests to unhealthy nodes to verify availability.',
'modal.provider.deleteConfirm': 'Are you sure you want to delete this provider config? This cannot be undone.',
'modal.provider.disableConfirm': 'Are you sure you want to disable this provider? It will no longer be selected for use.',
'modal.provider.enableConfirm': 'Are you sure you want to enable this provider?',
'modal.provider.edit': 'Edit',
'modal.provider.delete': 'Delete',
'modal.provider.save': 'Save',
'modal.provider.cancel': 'Cancel',
'modal.provider.status.healthy': 'Normal',
'modal.provider.status.unhealthy': 'Abnormal',
'modal.provider.status.disabled': 'Disabled',
'modal.provider.status.enabled': 'Enabled',
'modal.provider.lastError': 'Last Error:',
'modal.provider.lastUsed': 'Last Used:',
'modal.provider.lastCheck': 'Last Check:',
'modal.provider.checkModel': 'Check Model:',
'modal.provider.usageCount': 'Usage Count:',
'modal.provider.errorCount': 'Error Count:',
'modal.provider.neverUsed': 'Never Used',
'modal.provider.neverChecked': 'Never Checked',
'modal.provider.noModels': 'No models available for this provider type',
'modal.provider.loadingModels': 'Loading models...',
'modal.provider.unsupportedModels': 'Unsupported Models',
'modal.provider.unsupportedModelsHelp': 'Select models not supported by this provider; they will be excluded automatically',
'modal.provider.addTitle': 'Add New Provider Config',
'modal.provider.customName': 'Custom Name',
'modal.provider.checkModelName': 'Check Model Name',
'modal.provider.healthCheckLabel': 'Health Check',
'modal.provider.enabled': 'Enabled',
'modal.provider.disabled': 'Disabled',
'modal.provider.noProviderType': 'Unsupported provider type',
'modal.provider.refreshUuid': 'Refresh uuid',
'modal.provider.refreshUuidConfirm': 'Are you sure you want to refresh the uuid for this provider?\n\nOld uuid: {oldUuid}\n\nA new uuid will be generated. Make sure no other systems depend on this uuid.',
'modal.provider.refreshUuid.success': 'uuid refreshed successfully\n\nOld uuid: {oldUuid}\nNew uuid: {newUuid}',
'modal.provider.refreshUuid.failed': 'Failed to refresh uuid',
'modal.provider.field.projectId': 'Project ID',
'modal.provider.field.oauthPath': 'OAuth Credentials File Path',
'modal.provider.field.baseUrl': 'Base URL',
'modal.provider.field.refreshUrl': 'Refresh URL',
'modal.provider.field.refreshIdcUrl': 'Refresh IDC URL',
'modal.provider.field.oauthBaseUrl': 'OAuth Base URL',
'modal.provider.field.dailyBaseUrl': 'Daily Base URL',
'modal.provider.field.autopushBaseUrl': 'Autopush Base URL',
'modal.provider.field.headerName': 'Header Name',
'modal.provider.field.headerPrefix': 'Header Value Prefix',
'modal.provider.field.useSystemProxy': 'Use System Proxy',
'modal.provider.field.apiKey': 'API Key',
'modal.provider.field.apiKey.placeholder': 'Please enter API Key',
'modal.provider.field.projectId.placeholder': 'Google Cloud Project ID',
'modal.provider.field.projectId.optional.placeholder': 'Google Cloud Project ID (Leave blank for discovery)',
'modal.provider.field.oauthPath.gemini.placeholder': 'e.g.: ~/.gemini/oauth_creds.json',
'modal.provider.field.oauthPath.kiro.placeholder': 'e.g.: ~/.aws/sso/cache/kiro-auth-token.json',
'modal.provider.field.oauthPath.qwen.placeholder': 'e.g.: ~/.qwen/oauth_creds.json',
'modal.provider.field.oauthPath.antigravity.placeholder': 'e.g.: ~/.antigravity/oauth_creds.json',
'modal.provider.field.oauthPath.iflow.placeholder': 'e.g.: configs/iflow/oauth_creds.json',
'modal.provider.field.oauthPath.codex.placeholder': 'e.g.: configs/codex/oauth_creds.json',
'modal.provider.field.email': 'Email',
'modal.provider.field.email.placeholder': 'your-email@example.com',
'modal.provider.load.failed': 'Failed to load provider details',
'modal.provider.auth.initializing': 'Initializing credential generation...',
'modal.provider.auth.success': 'Credentials generated and path auto-filled',
'modal.provider.auth.window': 'Please complete authorization in the opened window',
'modal.provider.auth.failed': 'Failed to initialize credential generation',
'modal.provider.save.success': 'Save successful',
'modal.provider.save.failed': 'Save failed',
'modal.provider.delete.success': 'Delete successful',
'modal.provider.delete.failed': 'Delete failed',
'modal.provider.add.success': 'Add successful',
'modal.provider.add.failed': 'Add failed',
'modal.provider.resetHealth.success': 'Successfully reset health status for {count} nodes',
'modal.provider.resetHealth.failed': 'Failed to reset health status',
'modal.provider.deleteUnhealthy': 'Delete unhealthy nodes',
'modal.provider.deleteUnhealthyBtn': 'Delete Unhealthy',
'modal.provider.deleteUnhealthyConfirm': 'Delete {count} unhealthy node(s)? This cannot be undone.',
'modal.provider.deleteUnhealthy.noUnhealthy': 'No unhealthy nodes',
'modal.provider.deleteUnhealthy.deleting': 'Deleting...',
'modal.provider.deleteUnhealthy.success': 'Deleted {count} node(s)',
'modal.provider.deleteUnhealthy.failed': 'Delete failed',
'modal.provider.healthCheck.complete': 'Health check complete: {success} became healthy',
'modal.provider.healthCheck.abnormal': ', {fail} abnormal',
'modal.provider.healthCheck.skipped': ', {skipped} skipped (disabled)',
'modal.provider.refreshUnhealthyUuids': 'Refresh unhealthy UUIDs',
'modal.provider.refreshUnhealthyUuidsBtn': 'Refresh UUIDs',
'modal.provider.refreshUnhealthyUuidsConfirm': 'Refresh UUIDs for {count} unhealthy node(s)?',
'modal.provider.refreshUnhealthyUuids.noUnhealthy': 'No unhealthy nodes',
'modal.provider.refreshUnhealthyUuids.refreshing': 'Refreshing...',
'modal.provider.refreshUnhealthyUuids.success': 'Refreshed {count} UUID(s)',
'modal.provider.refreshUnhealthyUuids.failed': 'Refresh failed',
'modal.provider.kiroAuthHint': 'When using AWS Builder ID login, <code>clientId</code> and <code>clientSecret</code> fields are required, which can be found in another JSON file in the same folder',
// Pagination
'pagination.showing': 'Showing {start}-{end} of {total}',
'pagination.jumpTo': 'Jump to',
'pagination.page': 'Page',
// Usage
'usage.title': 'Usage Query',
'usage.refresh': 'Refresh Usage',
'usage.lastUpdate': 'Last Update: {time}',
'usage.lastUpdateCache': 'Cache Time: {time}',
'usage.loading': 'Loading usage data...',
'usage.empty': 'Click "Refresh Usage" button to get authorization file usage information',
'usage.noData': 'No usage data available',
'usage.noInstances': 'No initialized service instances',
'usage.group.instances': '{count} instances',
'usage.group.success': '{count}/{total} Success',
'usage.card.status.disabled': 'Disabled',
'usage.card.status.healthy': 'Healthy',
'usage.card.status.unhealthy': 'Abnormal',
'usage.card.totalUsage': 'Total Usage',
'usage.card.freeTrial': 'Free Trial',
'usage.card.bonus': 'Bonus',
'usage.card.expires': 'Expires: {time}',
'usage.group.expandAll': 'Expand All Cards',
'usage.group.collapseAll': 'Collapse All Cards',
// Logs
'logs.title': 'Real-time Logs',
'logs.clear': 'Clear Logs',
'logs.autoScroll': 'Auto Scroll',
'logs.autoScroll.on': 'Auto Scroll: On',
'logs.autoScroll.off': 'Auto Scroll: Off',
// Plugins
'plugins.title': 'Plugin Management',
'plugins.description': 'The plugin system allows you to extend system functionality. Enabling or disabling plugins requires a service restart to take effect.',
'plugins.stats.total': 'Total Plugins',
'plugins.stats.enabled': 'Enabled',
'plugins.stats.disabled': 'Disabled',
'plugins.refresh': 'Refresh Plugins',
'plugins.loading': 'Loading plugins...',
'plugins.empty': 'No installed plugins',
'plugins.noDescription': 'No description',
'plugins.status.enabled': 'Enabled',
'plugins.status.disabled': 'Disabled',
'plugins.badge.middleware.title': 'Contains Middleware',
'plugins.badge.routes.title': 'Contains Routes',
'plugins.badge.hooks.title': 'Contains Hooks',
'plugins.toggle.success': 'Plugin {name} {status}',
'plugins.toggle.failed': 'Failed to toggle plugin status',
'plugins.load.failed': 'Failed to load plugins list',
'plugins.restart.required': 'Changes saved',
// Models
'models.title': 'Available Models',
'models.note': 'Click model name to copy to clipboard',
'models.empty': 'No models available',
'models.loadError': 'Failed to load models',
'models.copied': 'Copied',
'models.clickToCopy': 'Click to copy',
// Guide
'guide.title': 'User Guide',
'guide.intro.title': 'Introduction',
'guide.intro.desc': 'AIClient2API is an API proxy service that breaks client restrictions, converting free large models like Gemini, Antigravity, Qwen Code, and Kiro into standard OpenAI-compatible interfaces that any application can call.',
'guide.intro.feature1.title': 'Unified Access',
'guide.intro.feature1.desc': 'Access multiple large models with a single configuration through standard OpenAI-compatible protocol',
'guide.intro.feature2.title': 'Break Limits',
'guide.intro.feature2.desc': 'Effectively bypass free API rate and quota limits using OAuth authorization',
'guide.intro.feature3.title': 'Protocol Conversion',
'guide.intro.feature3.desc': 'Support intelligent conversion between OpenAI, Claude, and Gemini protocols',
'guide.intro.feature4.title': 'Account Pool',
'guide.intro.feature4.desc': 'Support multi-account polling, automatic failover, and configuration degradation',
'guide.providers.title': 'Supported Model Providers',
'guide.providers.badge.oauth': 'OAuth',
'guide.providers.badge.experimental': 'Experimental',
'guide.providers.badge.free': 'Free',
'guide.providers.badge.official': 'Official API',
'guide.providers.gemini.desc': 'Access Gemini models via Google OAuth, supporting gemini-2.0-flash-exp and more',
'guide.providers.antigravity.desc': 'Access Gemini 3 Pro, Claude Sonnet 4.5 via Google internal interface',
'guide.providers.kiro.desc': 'Free access to Claude Opus 4.5, Claude Sonnet 4.5 via Kiro client',
'guide.providers.qwen.desc': 'Access Qwen3 Coder Plus via Alibaba Cloud OAuth',
'guide.providers.claude.desc': 'Access Claude models via official API or third-party proxy',
'guide.providers.openai.desc': 'Access GPT models via official API or third-party proxy',
'guide.providers.iflow.desc': 'Access Qwen, Kimi, DeepSeek, GLM via iFlow OAuth',
'guide.client.title': 'Client Configuration Guide',
'guide.client.desc': 'Here are configuration methods for common AI clients. Set the API endpoint to this service address:',
'guide.client.cherry.step1': 'Open Settings → Model Providers',
'guide.client.cherry.step2': 'Add custom provider',
'guide.client.cherry.step3': 'Set API URL to: http://localhost:3000/{provider}',
'guide.client.cherry.step4': 'Enter API Key (REQUIRED_API_KEY from config)',
'guide.client.cline.step1': 'Open VS Code Settings',
'guide.client.cline.step2': 'Search for Cline or Continue configuration',
'guide.client.cline.step3': 'Set API Base URL to: http://localhost:3000/{provider}/v1',
'guide.client.cline.step4': 'Enter API Key and model name',
'guide.client.note': 'Tip: Replace {provider} with the actual provider path, such as gemini-cli-oauth, claude-kiro-oauth, etc. See the routing examples on the dashboard for full paths.',
'guide.ollama.title': 'Ollama Protocol Usage',
'guide.ollama.desc': 'This project supports Ollama protocol, allowing unified access to all supported models.',
'guide.ollama.listModels': 'List all available models',
'guide.ollama.chat': 'Chat interface',
'guide.faq.title': 'FAQ',
'guide.faq.q1': 'Q: What to do if request returns 404 error?',
'guide.faq.a1': 'A: Check if the API path is correct. Some clients automatically append paths to Base URL, causing duplication. Check the actual request URL in the console and remove redundant path parts.',
'guide.faq.q2': 'Q: What to do if request returns 429 error?',
'guide.faq.a2': 'A: 429 means request rate is too high. Configure multiple accounts in the provider pool with polling; or configure Fallback chain for cross-type degradation.',
'guide.faq.q3': 'Q: What to do if OAuth authorization fails?',
'guide.faq.a3': 'A: Ensure OAuth callback ports are accessible (Gemini: 8085, Antigravity: 8086, Kiro: 19876-19880). Docker users need to map these ports correctly.',
'guide.faq.q4': 'Q: How to view available models?',
'guide.faq.a4': 'A: Click "Available Models" in the sidebar to view all models supported by configured providers. Click model name to copy.',
'guide.faq.q5': 'Q: What to do if streaming response is interrupted?',
'guide.faq.a5': 'A: Check network stability, increase client request timeout. If using proxy, ensure it supports long connections.',
// Guide - Flow
'guide.flow.title': 'Operation Flowchart',
'guide.flow.step1.title': 'Configuration',
'guide.flow.step1.desc': 'Set basic parameters in "Configuration" page',
'guide.flow.step2.title': 'Generate Auth',
'guide.flow.step2.desc': 'Generate OAuth authorization in "Provider Pools" page',
'guide.flow.step3.title': 'Manage Credentials',
'guide.flow.step3.desc': 'View and manage credentials in "Credential Files" page',
'guide.flow.step4.title': 'Start Using',
'guide.flow.step4.desc': 'View routing examples in "Dashboard" and start calling API',
// Tutorial
'tutorial.title': 'Configuration Tutorial',
'tutorial.config.title': 'Configuration Files',
'tutorial.config.desc': 'All configuration files are stored in the configs/ directory. Main configuration files include:',
'tutorial.config.badge.required': 'Required',
'tutorial.config.badge.optional': 'Optional',
'tutorial.config.file.config': 'Main config file with API Key, port, model provider settings',
'tutorial.config.file.pools': 'Provider pool config for multi-account polling and failover',
'tutorial.config.file.plugins': 'Plugin config for enabling/disabling system plugins',
'tutorial.config.file.pwd': 'Admin password file, default password is admin123',
'tutorial.main.title': 'Main Config Details (config.json)',
'tutorial.main.table.param': 'Parameter',
'tutorial.main.table.type': 'Type',
'tutorial.main.table.default': 'Default',
'tutorial.main.table.desc': 'Description',
'tutorial.main.basic.title': 'Basic Configuration',
'tutorial.main.basic.apikey': 'API Key required to access this service',
'tutorial.main.basic.port': 'Service listening port',
'tutorial.main.basic.host': 'Service listening address',
'tutorial.main.basic.provider': 'Default model provider',
'tutorial.main.prompt.title': 'System Prompt Configuration',
'tutorial.main.prompt.file': 'System prompt file path',
'tutorial.main.prompt.mode': 'System prompt mode: overwrite or append',
'tutorial.main.retry.title': 'Retry Configuration',
'tutorial.main.retry.max': 'Maximum retry count',
'tutorial.main.retry.delay': 'Base retry delay (milliseconds)',
'tutorial.main.retry.error': 'Max provider error count before marking unhealthy',
'tutorial.main.example.title': 'Configuration Example',
'tutorial.pool.title': 'Provider Pool Config (provider_pools.json)',
'tutorial.pool.desc': 'Provider pool configures multiple accounts for load balancing and failover. Each provider type can have multiple account nodes.',
'tutorial.pool.node.title': 'Node Configuration Parameters',
'tutorial.pool.node.uuid': 'Unique node identifier, auto-generated',
'tutorial.pool.node.name': 'Custom node name',
'tutorial.pool.node.oauth': 'OAuth credentials file path',
'tutorial.pool.node.health': 'Enable health check',
'tutorial.pool.node.model': 'Model used for health check',
'tutorial.pool.node.unsupported': 'List of unsupported models for this node',
'tutorial.pool.node.disabled': 'Whether to disable this node',
'tutorial.pool.example.title': 'Configuration Example',
'tutorial.fallback.title': 'Fallback Configuration',
'tutorial.fallback.desc': 'When all accounts of a provider type are unavailable, automatically switch to configured backup providers.',
'tutorial.fallback.chain.title': 'Cross-Type Fallback Chain',
'tutorial.fallback.chain.desc': 'Configure providerFallbackChain in config.json to specify backup types for each provider:',
'tutorial.fallback.model.title': 'Cross-Protocol Model Mapping',
'tutorial.fallback.model.desc': 'When primary provider is unavailable, map specific models to other protocol providers:',
'tutorial.proxy.title': 'Proxy Configuration',
'tutorial.proxy.desc': 'Support proxy configuration for specific providers in restricted network environments.',
'tutorial.proxy.config.title': 'Proxy Configuration Parameters',
'tutorial.proxy.url': 'Proxy URL, supports HTTP, HTTPS, SOCKS5',
'tutorial.proxy.providers': 'List of providers using proxy',
'tutorial.proxy.example.title': 'Configuration Example',
'tutorial.proxy.note': 'Supported proxy types: HTTP (http://), HTTPS (https://), SOCKS5 (socks5://)',
'tutorial.oauth.title': 'OAuth Configuration',
'tutorial.oauth.desc': 'Default storage locations for OAuth credentials of each provider:',
'tutorial.oauth.note': 'Recommended: Use the "Generate Auth" button in Provider Pool Management page for visual authorization. Credentials will be saved automatically.',
'tutorial.log.title': 'Log Configuration',
'tutorial.log.prompt.title': 'Prompt Log Configuration',
'tutorial.log.mode': 'Log mode: none, console, or file',
'tutorial.log.basename': 'Log file base name',
'tutorial.log.example.title': 'Configuration Example',
// Common
'common.togglePassword': 'Show/Hide Password',
'common.confirm': 'Confirm',
'common.cancel': 'Cancel',
'common.success': 'Success',
'common.enabled': 'Enabled',
'common.disabled': 'Disabled',
'common.error': 'Error',
'common.warning': 'Warning',
'common.info': 'Info',
'common.loading': 'Loading...',
'common.upload': 'Upload',
'common.generate': 'Generate',
'common.optional': 'Optional',
'common.found': 'Found',
'common.missing': 'Missing',
'common.search': 'Search',
'common.welcome': 'Welcome to AIClient2API Management Console!',
'common.fileType': 'Unsupported file type. Please select JSON, TXT, KEY, PEM, P12, or PFX.',
'common.fileSize': 'File size cannot exceed 5MB.',
'common.uploadSuccess': 'File uploaded successfully',
'common.uploadFailed': 'File upload failed',
'common.passwordUpdated': 'Admin password updated, takes effect next login',
'common.configSaved': 'Configuration saved',
'common.providerPoolRefreshed': 'Provider pool data refreshed',
'common.copy.success': 'Content copied to clipboard',
'common.copy.failed': 'Copy failed, please copy manually',
'common.refresh.success': 'Refresh successful',
'common.refresh.failed': 'Refresh failed',
// Login
'login.title': 'Login - AIClient2API',
'login.heading': 'Please login to continue',
'login.password': 'Password',
'login.passwordPlaceholder': 'Please enter password',
'login.error.empty': 'Please enter password',
'login.error.incorrect': 'Incorrect password, please try again',
'login.error.failed': 'Login failed, please check your network connection',
'login.button': 'Login',
'login.loggingIn': 'Logging in...',
}
};
// 当前语言
let currentLanguage = localStorage.getItem('language') || 'zh-CN';
// 获取翻译文本
export function t(key, params = {}) {
let text = translations[currentLanguage]?.[key] || translations['zh-CN']?.[key] || key;
// 替换参数
Object.keys(params).forEach(param => {
text = text.replace(`{${param}}`, params[param]);
});
return text;
}
// 切换语言
export function setLanguage(lang) {
if (translations[lang]) {
currentLanguage = lang;
localStorage.setItem('language', lang);
updatePageLanguage();
// 更新图片
updateDashboardImages(lang);
// 触发语言切换事件
window.dispatchEvent(new CustomEvent('languageChanged', { detail: { language: lang } }));
}
}
// 更新仪表盘图片
function updateDashboardImages(lang) {
const sponsorImg = document.getElementById('sponsor-img');
const sponsorTitle = document.getElementById('sponsor-title');
const sponsorDesc = document.getElementById('sponsor-desc');
const wechatImg = document.getElementById('wechat-img');
const wechatIcon = document.getElementById('wechat-icon');
const wechatTitle = document.getElementById('wechat-title');
const wechatDesc = document.getElementById('wechat-desc');
if (lang === 'en-US') {
// 更新赞助图片
if (sponsorImg) {
sponsorImg.src = 'static/coffee.png';
sponsorImg.alt = 'Buy me a coffee';
if (sponsorTitle) {
sponsorTitle.setAttribute('data-i18n', 'dashboard.contact.coffee');
sponsorTitle.textContent = translations['en-US']['dashboard.contact.coffee'];
}
if (sponsorDesc) {
sponsorDesc.setAttribute('data-i18n', 'dashboard.contact.coffeeDesc');
sponsorDesc.textContent = translations['en-US']['dashboard.contact.coffeeDesc'];
}
}
// 更新联系方式图片 (WeChat -> X.com)
if (wechatImg) {
wechatImg.src = 'static/x.com.png';
wechatImg.alt = 'X.com';
if (wechatIcon) {
wechatIcon.className = 'fab fa-x-twitter';
}
if (wechatTitle) {
wechatTitle.setAttribute('data-i18n', 'dashboard.contact.x');
wechatTitle.textContent = translations['en-US']['dashboard.contact.x'] || 'Follow on X.com';
}
if (wechatDesc) {
wechatDesc.setAttribute('data-i18n', 'dashboard.contact.xDesc');
wechatDesc.textContent = translations['en-US']['dashboard.contact.xDesc'] || 'Follow us on X for latest updates';
}
}
} else {
// 更新赞助图片
if (sponsorImg) {
sponsorImg.src = 'static/sponsor.png';
sponsorImg.alt = '赞助二维码';
if (sponsorTitle) {
sponsorTitle.setAttribute('data-i18n', 'dashboard.contact.sponsor');
sponsorTitle.textContent = translations['zh-CN']['dashboard.contact.sponsor'];
}
if (sponsorDesc) {
sponsorDesc.setAttribute('data-i18n', 'dashboard.contact.sponsorDesc');
sponsorDesc.textContent = translations['zh-CN']['dashboard.contact.sponsorDesc'];
}
}
// 更新联系方式图片 (X.com -> WeChat)
if (wechatImg) {
wechatImg.src = 'static/wechat.png';
wechatImg.alt = '微信二维码';
if (wechatIcon) {
wechatIcon.className = 'fab fa-weixin';
}
if (wechatTitle) {
wechatTitle.setAttribute('data-i18n', 'dashboard.contact.wechat');
wechatTitle.textContent = translations['zh-CN']['dashboard.contact.wechat'];
}
if (wechatDesc) {
wechatDesc.setAttribute('data-i18n', 'dashboard.contact.wechatDesc');
wechatDesc.textContent = translations['zh-CN']['dashboard.contact.wechatDesc'];
}
}
}
}
// 获取当前语言
export function getCurrentLanguage() {
return currentLanguage;
}
// 更新页面语言
function updatePageLanguage() {
// 更新 HTML lang 属性
document.documentElement.lang = currentLanguage;
// 更新所有带 data-i18n 或 data-i18n-xxx 属性的元素
document.querySelectorAll('[data-i18n], [data-i18n-placeholder], [data-i18n-title], [data-i18n-aria-label]').forEach(element => {
// 1. 处理属性翻译 (placeholder, title, aria-label)
const attributes = ['placeholder', 'title', 'aria-label'];
attributes.forEach(attr => {
const attrKey = element.getAttribute(`data-i18n-${attr}`);
if (attrKey) {
const params = element.getAttribute(`data-i18n-${attr}-params`);
const parsedParams = params ? JSON.parse(params) : {};
if (attr === 'aria-label') {
element.setAttribute('aria-label', t(attrKey, parsedParams));
} else {
element[attr] = t(attrKey, parsedParams);
}
}
});
// 2. 处理主文本翻译 (data-i18n)
const key = element.getAttribute('data-i18n');
if (key) {
const params = element.getAttribute('data-i18n-params');
const parsedParams = params ? JSON.parse(params) : {};
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
// 如果没有显式的 data-i18n-placeholder,则 data-i18n 作用于 placeholder
if (!element.hasAttribute('data-i18n-placeholder')) {
element.placeholder = t(key, parsedParams);
}
} else {
element.textContent = t(key, parsedParams);
}
}
});
// 更新所有带 data-i18n-html 属性的元素(支持 HTML 内容)
document.querySelectorAll('[data-i18n-html]').forEach(element => {
const key = element.getAttribute('data-i18n-html');
const params = element.getAttribute('data-i18n-params');
const parsedParams = params ? JSON.parse(params) : {};
element.innerHTML = t(key, parsedParams);
});
}
// 初始化多语言
export function initI18n() {
// 设置初始语言
updatePageLanguage();
// 设置初始图片
updateDashboardImages(currentLanguage);
// 监听 DOM 变化,自动翻译新添加的元素
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) { // 元素节点
// 翻译新添加的元素
if (node.hasAttribute('data-i18n')) {
const key = node.getAttribute('data-i18n');
const params = node.getAttribute('data-i18n-params');
const parsedParams = params ? JSON.parse(params) : {};
if (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA') {
if (node.placeholder !== undefined) {
node.placeholder = t(key, parsedParams);
}
} else {
node.textContent = t(key, parsedParams);
}
}
// 翻译子元素
node.querySelectorAll('[data-i18n]').forEach(element => {
const key = element.getAttribute('data-i18n');
const params = element.getAttribute('data-i18n-params');
const parsedParams = params ? JSON.parse(params) : {};
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
if (element.placeholder !== undefined) {
element.placeholder = t(key, parsedParams);
}
} else {
element.textContent = t(key, parsedParams);
}
});
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// 导出所有函数
export default {
t,
setLanguage,
getCurrentLanguage,
initI18n
};