1616 lines
102 KiB
JavaScript
1616 lines
102 KiB
JavaScript
// 多语言配置
|
||
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.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 四个字段。',
|
||
'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': '凭据文件可在上传配置管理中查看和管理',
|
||
|
||
// Orchids OAuth
|
||
'oauth.orchids.title': 'Orchids 凭据导入',
|
||
'oauth.orchids.formatToken': 'JWT Token 格式',
|
||
'oauth.orchids.tokenLabel': 'JWT Token 字符串',
|
||
'oauth.orchids.tokenPlaceholder': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImNsaWVudF94eHgiLCJyb3RhdGluZ190b2tlbiI6Inh4eCJ9.xxx',
|
||
'oauth.orchids.tokenInstructions': '粘贴 JWT Token 字符串(支持纯 JWT 格式,rotating_token 会从 JWT payload 中自动提取)',
|
||
'oauth.orchids.getSteps': '获取步骤:',
|
||
'oauth.orchids.tokenStep1': '访问 www.orchids.app 并登录',
|
||
'oauth.orchids.tokenStep2': '按 F12 打开开发者工具 → Application 标签',
|
||
'oauth.orchids.tokenStep3': '在左侧找到 Cookies → www.orchids.app',
|
||
'oauth.orchids.tokenStep4': '找到 __client cookie 的值',
|
||
'oauth.orchids.tokenStep5': '复制完整的 JWT 值(以 eyJ 开头的字符串)',
|
||
'oauth.orchids.tokenFormat': '格式:eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImNsaWVudF94eHgiLCJyb3RhdGluZ190b2tlbiI6Inh4eCJ9.xxx',
|
||
'oauth.orchids.confirmImport': '确认导入',
|
||
'oauth.orchids.importing': '导入中...',
|
||
'oauth.orchids.success': 'Orchids 凭据导入成功',
|
||
'oauth.orchids.parseSuccess': 'Token 解析成功',
|
||
'oauth.orchids.detectedToken': '检测到有效的 JWT Token',
|
||
'oauth.orchids.errorEmpty': '请输入凭据',
|
||
'oauth.orchids.errorTokenInvalid': 'Token 格式错误,请输入有效的 JWT Token',
|
||
'oauth.orchids.errorJwtParse': 'JWT 解析失败',
|
||
'oauth.orchids.errorMissingRotating': 'JWT payload 中缺少 rotating_token 字段',
|
||
'oauth.orchids.importFailed': '导入失败',
|
||
'oauth.orchids.clientId': 'Client ID',
|
||
'oauth.orchids.rotatingToken': 'Rotating Token',
|
||
|
||
// Config
|
||
'config.title': '配置管理',
|
||
'config.apiKey': 'API密钥',
|
||
'config.apiKeyPlaceholder': '请输入API密钥',
|
||
'config.host': '监听地址',
|
||
'config.port': '端口',
|
||
'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.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': '默认: 3',
|
||
'config.advanced.maxErrorCountNote': '提供商连续错误达到此次数后将被标记为不健康,默认为 3 次',
|
||
'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.orchids': 'Orchids 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.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.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.providers.orchids.desc': '通过 Orchids 平台免费使用 Claude Sonnet 4.5 等模型',
|
||
'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.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.',
|
||
'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',
|
||
|
||
// Orchids OAuth
|
||
'oauth.orchids.title': 'Orchids Credential Import',
|
||
'oauth.orchids.formatToken': 'JWT Token Format',
|
||
'oauth.orchids.tokenLabel': 'JWT Token String',
|
||
'oauth.orchids.tokenPlaceholder': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImNsaWVudF94eHgiLCJyb3RhdGluZ190b2tlbiI6Inh4eCJ9.xxx',
|
||
'oauth.orchids.tokenInstructions': 'Paste JWT Token string (rotating_token will be automatically extracted from JWT payload)',
|
||
'oauth.orchids.getSteps': 'How to get:',
|
||
'oauth.orchids.tokenStep1': 'Visit www.orchids.app and log in',
|
||
'oauth.orchids.tokenStep2': 'Press F12 to open Developer Tools → Application tab',
|
||
'oauth.orchids.tokenStep3': 'Find Cookies → www.orchids.app on the left',
|
||
'oauth.orchids.tokenStep4': 'Find the __client cookie value',
|
||
'oauth.orchids.tokenStep5': 'Copy the full JWT value (string starting with eyJ)',
|
||
'oauth.orchids.tokenFormat': 'Format: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImNsaWVudF94eHgiLCJyb3RhdGluZ190b2tlbiI6Inh4eCJ9.xxx',
|
||
'oauth.orchids.confirmImport': 'Confirm Import',
|
||
'oauth.orchids.importing': 'Importing...',
|
||
'oauth.orchids.success': 'Orchids credentials imported successfully',
|
||
'oauth.orchids.parseSuccess': 'Token parsed successfully',
|
||
'oauth.orchids.detectedToken': 'Valid JWT Token detected',
|
||
'oauth.orchids.errorEmpty': 'Please enter credentials',
|
||
'oauth.orchids.errorTokenInvalid': 'Token format error, please enter a valid JWT Token',
|
||
'oauth.orchids.errorJwtParse': 'JWT parse failed',
|
||
'oauth.orchids.errorMissingRotating': 'Missing rotating_token field in JWT payload',
|
||
'oauth.orchids.importFailed': 'Import failed',
|
||
'oauth.orchids.clientId': 'Client ID',
|
||
'oauth.orchids.rotatingToken': 'Rotating Token',
|
||
|
||
// Config
|
||
'config.title': 'Configuration Management',
|
||
'config.apiKey': 'API Key',
|
||
'config.apiKeyPlaceholder': 'Please enter API key',
|
||
'config.host': 'Listen Address',
|
||
'config.port': 'Port',
|
||
'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.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: 3',
|
||
'config.advanced.maxErrorCountNote': 'Provider will be marked as unhealthy after consecutive errors reach this count, default is 3',
|
||
'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.orchids': 'Orchids 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.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.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.providers.orchids.desc': 'Free access to Claude Sonnet 4.5 via Orchids platform',
|
||
'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
|
||
};
|