更新补全i18n国际化文本

This commit is contained in:
Supra4E8C
2025-10-26 14:54:07 +08:00
parent e92784f951
commit 7509a1eddc
3 changed files with 136 additions and 93 deletions

142
app.js
View File

@@ -182,12 +182,12 @@ class CLIProxyManager {
// 如果有完整的连接信息且之前已登录,尝试自动登录
if (savedBase && savedKey && wasLoggedIn) {
try {
console.log('检测到本地连接数据,尝试自动登录...');
console.log(i18n.t('auto_login.title'));
this.showAutoLoginLoading();
await this.attemptAutoLogin(savedBase, savedKey);
return; // 自动登录成功,不显示登录页面
} catch (error) {
console.log('自动登录失败:', error.message);
console.log(`${i18n.t('notification.login_failed')}: ${error.message}`);
// 清除无效的登录状态
localStorage.removeItem('isLoggedIn');
this.hideAutoLoginLoading();
@@ -232,7 +232,7 @@ class CLIProxyManager {
this.hideAutoLoginLoading();
this.showMainPage();
console.log('自动登录成功');
console.log(i18n.t('auto_login.title'));
return true;
} catch (error) {
console.error('自动登录失败:', error);
@@ -807,7 +807,8 @@ class CLIProxyManager {
// 更新按钮提示文本
const toggleBtn = document.getElementById('sidebar-toggle-btn-desktop');
if (toggleBtn) {
toggleBtn.setAttribute('title', isCollapsed ? '展开侧边栏' : '收起侧边栏');
toggleBtn.setAttribute('data-i18n-title', isCollapsed ? 'sidebar.toggle_expand' : 'sidebar.toggle_collapse');
toggleBtn.title = i18n.t(isCollapsed ? 'sidebar.toggle_expand' : 'sidebar.toggle_collapse');
}
}
}
@@ -828,7 +829,8 @@ class CLIProxyManager {
// 更新按钮提示文本
const toggleBtn = document.getElementById('sidebar-toggle-btn-desktop');
if (toggleBtn) {
toggleBtn.setAttribute('title', '展开侧边栏');
toggleBtn.setAttribute('data-i18n-title', 'sidebar.toggle_expand');
toggleBtn.title = i18n.t('sidebar.toggle_expand');
}
}
}
@@ -1420,7 +1422,7 @@ class CLIProxyManager {
// 加载所有数据 - 使用新的 /config 端点一次性获取所有配置
async loadAllData(forceRefresh = false) {
try {
console.log('使用新的 /config 端点加载所有配置...');
console.log(i18n.t('system_info.real_time_data'));
// 使用新的 /config 端点一次性获取所有配置
const config = await this.getConfig(forceRefresh);
@@ -2213,10 +2215,10 @@ class CLIProxyManager {
<div class="item-value">${this.maskApiKey(key)}</div>
<div class="item-stats">
<span class="stat-badge stat-success">
<i class="fas fa-check-circle"></i> 成功: ${keyStats.success}
<i class="fas fa-check-circle"></i> ${i18n.t('stats.success')}: ${keyStats.success}
</span>
<span class="stat-badge stat-failure">
<i class="fas fa-times-circle"></i> 失败: ${keyStats.failure}
<i class="fas fa-times-circle"></i> ${i18n.t('stats.failure')}: ${keyStats.failure}
</span>
</div>
</div>
@@ -2238,14 +2240,14 @@ class CLIProxyManager {
const modalBody = document.getElementById('modal-body');
modalBody.innerHTML = `
<h3>添加Gemini API密钥</h3>
<h3>${i18n.t('ai_providers.gemini_add_modal_title')}</h3>
<div class="form-group">
<label for="new-gemini-key">API密钥:</label>
<input type="text" id="new-gemini-key" placeholder="请输入Gemini API密钥">
<label for="new-gemini-key">${i18n.t('ai_providers.gemini_add_modal_key_label')}</label>
<input type="text" id="new-gemini-key" placeholder="${i18n.t('ai_providers.gemini_add_modal_key_placeholder')}">
</div>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="manager.closeModal()">取消</button>
<button class="btn btn-primary" onclick="manager.addGeminiKey()">添加</button>
<button class="btn btn-secondary" onclick="manager.closeModal()">${i18n.t('common.cancel')}</button>
<button class="btn btn-primary" onclick="manager.addGeminiKey()">${i18n.t('common.add')}</button>
</div>
`;
@@ -2257,7 +2259,7 @@ class CLIProxyManager {
const newKey = document.getElementById('new-gemini-key').value.trim();
if (!newKey) {
this.showNotification('请输入Gemini API密钥', 'error');
this.showNotification(i18n.t('notification.please_enter') + ' ' + i18n.t('notification.gemini_api_key'), 'error');
return;
}
@@ -2274,9 +2276,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadGeminiKeys();
this.showNotification('Gemini密钥添加成功', 'success');
this.showNotification(i18n.t('notification.gemini_key_added'), 'success');
} catch (error) {
this.showNotification(`添加Gemini密钥失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.add_failed')}: ${error.message}`, 'error');
}
}
@@ -2286,14 +2288,14 @@ class CLIProxyManager {
const modalBody = document.getElementById('modal-body');
modalBody.innerHTML = `
<h3>编辑Gemini API密钥</h3>
<h3>${i18n.t('ai_providers.gemini_edit_modal_title')}</h3>
<div class="form-group">
<label for="edit-gemini-key">API密钥:</label>
<label for="edit-gemini-key">${i18n.t('ai_providers.gemini_edit_modal_key_label')}</label>
<input type="text" id="edit-gemini-key" value="${currentKey}">
</div>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="manager.closeModal()">取消</button>
<button class="btn btn-primary" onclick="manager.updateGeminiKey('${currentKey}')">更新</button>
<button class="btn btn-secondary" onclick="manager.closeModal()">${i18n.t('common.cancel')}</button>
<button class="btn btn-primary" onclick="manager.updateGeminiKey('${currentKey}')">${i18n.t('common.update')}</button>
</div>
`;
@@ -2305,7 +2307,7 @@ class CLIProxyManager {
const newKey = document.getElementById('edit-gemini-key').value.trim();
if (!newKey) {
this.showNotification('请输入Gemini API密钥', 'error');
this.showNotification(i18n.t('notification.please_enter') + ' ' + i18n.t('notification.gemini_api_key'), 'error');
return;
}
@@ -2318,9 +2320,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadGeminiKeys();
this.showNotification('Gemini密钥更新成功', 'success');
this.showNotification(i18n.t('notification.gemini_key_updated'), 'success');
} catch (error) {
this.showNotification(`更新Gemini密钥失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error');
}
}
@@ -2332,9 +2334,9 @@ class CLIProxyManager {
await this.makeRequest(`/generative-language-api-key?value=${encodeURIComponent(key)}`, { method: 'DELETE' });
this.clearCache(); // 清除缓存
this.loadGeminiKeys();
this.showNotification('Gemini密钥删除成功', 'success');
this.showNotification(i18n.t('notification.gemini_key_deleted'), 'success');
} catch (error) {
this.showNotification(`删除Gemini密钥失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.delete_failed')}: ${error.message}`, 'error');
}
}
@@ -2381,10 +2383,10 @@ class CLIProxyManager {
${config['proxy-url'] ? `<div class="item-subtitle">${i18n.t('common.proxy_url')}: ${this.escapeHtml(config['proxy-url'])}</div>` : ''}
<div class="item-stats">
<span class="stat-badge stat-success">
<i class="fas fa-check-circle"></i> 成功: ${keyStats.success}
<i class="fas fa-check-circle"></i> ${i18n.t('stats.success')}: ${keyStats.success}
</span>
<span class="stat-badge stat-failure">
<i class="fas fa-times-circle"></i> 失败: ${keyStats.failure}
<i class="fas fa-times-circle"></i> ${i18n.t('stats.failure')}: ${keyStats.failure}
</span>
</div>
</div>
@@ -2461,9 +2463,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadCodexKeys();
this.showNotification('Codex配置添加成功', 'success');
this.showNotification(i18n.t('notification.codex_config_added'), 'success');
} catch (error) {
this.showNotification(`添加Codex配置失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.add_failed')}: ${error.message}`, 'error');
}
}
@@ -2523,9 +2525,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadCodexKeys();
this.showNotification('Codex配置更新成功', 'success');
this.showNotification(i18n.t('notification.codex_config_updated'), 'success');
} catch (error) {
this.showNotification(`更新Codex配置失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error');
}
}
@@ -2537,9 +2539,9 @@ class CLIProxyManager {
await this.makeRequest(`/codex-api-key?api-key=${encodeURIComponent(apiKey)}`, { method: 'DELETE' });
this.clearCache(); // 清除缓存
this.loadCodexKeys();
this.showNotification('Codex配置删除成功', 'success');
this.showNotification(i18n.t('notification.codex_config_deleted'), 'success');
} catch (error) {
this.showNotification(`删除Codex配置失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.delete_failed')}: ${error.message}`, 'error');
}
}
@@ -2586,10 +2588,10 @@ class CLIProxyManager {
${config['proxy-url'] ? `<div class="item-subtitle">${i18n.t('common.proxy_url')}: ${this.escapeHtml(config['proxy-url'])}</div>` : ''}
<div class="item-stats">
<span class="stat-badge stat-success">
<i class="fas fa-check-circle"></i> 成功: ${keyStats.success}
<i class="fas fa-check-circle"></i> ${i18n.t('stats.success')}: ${keyStats.success}
</span>
<span class="stat-badge stat-failure">
<i class="fas fa-times-circle"></i> 失败: ${keyStats.failure}
<i class="fas fa-times-circle"></i> ${i18n.t('stats.failure')}: ${keyStats.failure}
</span>
</div>
</div>
@@ -2666,9 +2668,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadClaudeKeys();
this.showNotification('Claude配置添加成功', 'success');
this.showNotification(i18n.t('notification.claude_config_added'), 'success');
} catch (error) {
this.showNotification(`添加Claude配置失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.add_failed')}: ${error.message}`, 'error');
}
}
@@ -2728,9 +2730,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadClaudeKeys();
this.showNotification('Claude配置更新成功', 'success');
this.showNotification(i18n.t('notification.claude_config_updated'), 'success');
} catch (error) {
this.showNotification(`更新Claude配置失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error');
}
}
@@ -2742,9 +2744,9 @@ class CLIProxyManager {
await this.makeRequest(`/claude-api-key?api-key=${encodeURIComponent(apiKey)}`, { method: 'DELETE' });
this.clearCache(); // 清除缓存
this.loadClaudeKeys();
this.showNotification('Claude配置删除成功', 'success');
this.showNotification(i18n.t('notification.claude_config_deleted'), 'success');
} catch (error) {
this.showNotification(`删除Claude配置失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.delete_failed')}: ${error.message}`, 'error');
}
}
@@ -2829,10 +2831,10 @@ class CLIProxyManager {
${this.renderOpenAIModelBadges(models)}
<div class="item-stats">
<span class="stat-badge stat-success">
<i class="fas fa-check-circle"></i> 成功: ${totalSuccess}
<i class="fas fa-check-circle"></i> ${i18n.t('stats.success')}: ${totalSuccess}
</span>
<span class="stat-badge stat-failure">
<i class="fas fa-times-circle"></i> 失败: ${totalFailure}
<i class="fas fa-times-circle"></i> ${i18n.t('stats.failure')}: ${totalFailure}
</span>
</div>
</div>
@@ -2927,9 +2929,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadOpenAIProviders();
this.showNotification('OpenAI提供商添加成功', 'success');
this.showNotification(i18n.t('notification.openai_provider_added'), 'success');
} catch (error) {
this.showNotification(`添加OpenAI提供商失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.add_failed')}: ${error.message}`, 'error');
}
}
@@ -3021,9 +3023,9 @@ class CLIProxyManager {
this.clearCache(); // 清除缓存
this.closeModal();
this.loadOpenAIProviders();
this.showNotification('OpenAI提供商更新成功', 'success');
this.showNotification(i18n.t('notification.openai_provider_updated'), 'success');
} catch (error) {
this.showNotification(`更新OpenAI提供商失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error');
}
}
@@ -3035,9 +3037,9 @@ class CLIProxyManager {
await this.makeRequest(`/openai-compatibility?name=${encodeURIComponent(name)}`, { method: 'DELETE' });
this.clearCache(); // 清除缓存
this.loadOpenAIProviders();
this.showNotification('OpenAI提供商删除成功', 'success');
this.showNotification(i18n.t('notification.openai_provider_deleted'), 'success');
} catch (error) {
this.showNotification(`删除OpenAI提供商失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.delete_failed')}: ${error.message}`, 'error');
}
}
@@ -3124,10 +3126,10 @@ class CLIProxyManager {
<div class="item-subtitle">${i18n.t('auth_files.file_modified')}: ${new Date(file.modtime).toLocaleString(i18n.currentLanguage === 'zh-CN' ? 'zh-CN' : 'en-US')}</div>
<div class="item-stats">
<span class="stat-badge stat-success">
<i class="fas fa-check-circle"></i> 成功: ${fileStats.success}
<i class="fas fa-check-circle"></i> ${i18n.t('stats.success')}: ${fileStats.success}
</span>
<span class="stat-badge stat-failure">
<i class="fas fa-times-circle"></i> 失败: ${fileStats.failure}
<i class="fas fa-times-circle"></i> ${i18n.t('stats.failure')}: ${fileStats.failure}
</span>
</div>
</div>
@@ -3189,7 +3191,7 @@ class CLIProxyManager {
this.loadAuthFiles();
this.showNotification(i18n.t('auth_files.upload_success'), 'success');
} catch (error) {
this.showNotification(`文件上传失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.upload_failed')}: ${error.message}`, 'error');
}
// 清空文件输入
@@ -3219,7 +3221,7 @@ class CLIProxyManager {
this.showNotification(i18n.t('auth_files.download_success'), 'success');
} catch (error) {
this.showNotification(`文件下载失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.download_failed')}: ${error.message}`, 'error');
}
}
@@ -3233,7 +3235,7 @@ class CLIProxyManager {
this.loadAuthFiles();
this.showNotification(i18n.t('auth_files.delete_success'), 'success');
} catch (error) {
this.showNotification(`文件删除失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.delete_failed')}: ${error.message}`, 'error');
}
}
@@ -3247,7 +3249,7 @@ class CLIProxyManager {
this.loadAuthFiles();
this.showNotification(`${i18n.t('auth_files.delete_all_success')} ${response.deleted} ${i18n.t('auth_files.files_count')}`, 'success');
} catch (error) {
this.showNotification(`删除文件失败: ${error.message}`, 'error');
this.showNotification(`${i18n.t('notification.delete_failed')}: ${error.message}`, 'error');
}
}
@@ -3313,12 +3315,12 @@ class CLIProxyManager {
if (urlInput && urlInput.value) {
try {
await navigator.clipboard.writeText(urlInput.value);
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
} catch (error) {
// 降级方案:使用传统的复制方法
urlInput.select();
document.execCommand('copy');
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
}
}
}
@@ -3326,7 +3328,7 @@ class CLIProxyManager {
// 开始轮询 OAuth 状态
startCodexOAuthPolling(state) {
if (!state) {
this.showNotification('无法获取认证状态参数', 'error');
this.showNotification(i18n.t('auth_login.missing_state'), 'error');
return;
}
@@ -3458,12 +3460,12 @@ class CLIProxyManager {
if (urlInput && urlInput.value) {
try {
await navigator.clipboard.writeText(urlInput.value);
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
} catch (error) {
// 降级方案:使用传统的复制方法
urlInput.select();
document.execCommand('copy');
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
}
}
}
@@ -3471,7 +3473,7 @@ class CLIProxyManager {
// 开始轮询 Anthropic OAuth 状态
startAnthropicOAuthPolling(state) {
if (!state) {
this.showNotification('无法获取认证状态参数', 'error');
this.showNotification(i18n.t('auth_login.missing_state'), 'error');
return;
}
@@ -3612,12 +3614,12 @@ class CLIProxyManager {
if (urlInput && urlInput.value) {
try {
await navigator.clipboard.writeText(urlInput.value);
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
} catch (error) {
// 降级方案:使用传统的复制方法
urlInput.select();
document.execCommand('copy');
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
}
}
}
@@ -3625,7 +3627,7 @@ class CLIProxyManager {
// 开始轮询 Gemini CLI OAuth 状态
startGeminiCliOAuthPolling(state) {
if (!state) {
this.showNotification('无法获取认证状态参数', 'error');
this.showNotification(i18n.t('auth_login.missing_state'), 'error');
return;
}
@@ -3757,12 +3759,12 @@ class CLIProxyManager {
if (urlInput && urlInput.value) {
try {
await navigator.clipboard.writeText(urlInput.value);
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
} catch (error) {
// 降级方案:使用传统的复制方法
urlInput.select();
document.execCommand('copy');
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
}
}
}
@@ -3770,7 +3772,7 @@ class CLIProxyManager {
// 开始轮询 Qwen OAuth 状态
startQwenOAuthPolling(state) {
if (!state) {
this.showNotification('无法获取认证状态参数', 'error');
this.showNotification(i18n.t('auth_login.missing_state'), 'error');
return;
}
@@ -3902,12 +3904,12 @@ class CLIProxyManager {
if (urlInput && urlInput.value) {
try {
await navigator.clipboard.writeText(urlInput.value);
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
} catch (error) {
// 降级方案:使用传统的复制方法
urlInput.select();
document.execCommand('copy');
this.showNotification('链接已复制到剪贴板', 'success');
this.showNotification(i18n.t('notification.link_copied'), 'success');
}
}
}
@@ -3915,7 +3917,7 @@ class CLIProxyManager {
// 开始轮询 iFlow OAuth 状态
startIflowOAuthPolling(state) {
if (!state) {
this.showNotification('无法获取认证状态参数', 'error');
this.showNotification(i18n.t('auth_login.missing_state'), 'error');
return;
}

46
i18n.js
View File

@@ -38,6 +38,8 @@ const i18n = {
'common.base_url': '地址',
'common.proxy_url': '代理',
'common.alias': '别名',
'common.failure': '失败',
'common.unknown_error': '未知错误',
// 页面标题
'title.main': 'CLI Proxy API Management Center',
@@ -275,6 +277,7 @@ const i18n = {
'auth_login.qwen_oauth_status_error': '认证失败:',
'auth_login.qwen_oauth_start_error': '启动 Qwen OAuth 失败:',
'auth_login.qwen_oauth_polling_error': '检查认证状态失败:',
'auth_login.missing_state': '无法获取认证状态参数',
// iFlow OAuth
'auth_login.iflow_oauth_title': 'iFlow OAuth',
@@ -308,6 +311,8 @@ const i18n = {
'usage_stats.tokens_count': 'Token数量',
'usage_stats.models': '模型统计',
'usage_stats.success_rate': '成功率',
'stats.success': '成功',
'stats.failure': '失败',
// 日志查看
'logs.title': '日志查看',
@@ -347,6 +352,7 @@ const i18n = {
'config_management.status_save_failed': '保存失败',
'config_management.save_success': '配置已保存',
'config_management.error_yaml_not_supported': '服务器未返回 YAML 格式,请确认 /config.yaml 接口可用',
'config_management.editor_placeholder': 'key: value',
// 系统信息
'system_info.title': '系统信息',
@@ -402,6 +408,7 @@ const i18n = {
'notification.gemini_api_key': 'Gemini API密钥',
'notification.codex_api_key': 'Codex API密钥',
'notification.claude_api_key': 'Claude API密钥',
'notification.link_copied': '链接已复制到剪贴板',
// 语言切换
'language.switch': '语言',
@@ -416,6 +423,10 @@ const i18n = {
'theme.switch_to_dark': '切换到暗色模式',
'theme.auto': '跟随系统',
// 侧边栏
'sidebar.toggle_expand': '展开侧边栏',
'sidebar.toggle_collapse': '收起侧边栏',
// 页脚
'footer.version': '版本',
'footer.author': '作者'
@@ -453,6 +464,8 @@ const i18n = {
'common.base_url': 'Address',
'common.proxy_url': 'Proxy',
'common.alias': 'Alias',
'common.failure': 'Failure',
'common.unknown_error': 'Unknown error',
// Page titles
'title.main': 'CLI Proxy API Management Center',
@@ -689,6 +702,7 @@ const i18n = {
'auth_login.qwen_oauth_status_error': 'Authentication failed:',
'auth_login.qwen_oauth_start_error': 'Failed to start Qwen OAuth:',
'auth_login.qwen_oauth_polling_error': 'Failed to check authentication status:',
'auth_login.missing_state': 'Unable to retrieve authentication state parameter',
// iFlow OAuth
'auth_login.iflow_oauth_title': 'iFlow OAuth',
@@ -722,6 +736,8 @@ const i18n = {
'usage_stats.tokens_count': 'Token Count',
'usage_stats.models': 'Model Statistics',
'usage_stats.success_rate': 'Success Rate',
'stats.success': 'Success',
'stats.failure': 'Failure',
// Logs viewer
'logs.title': 'Logs Viewer',
@@ -761,6 +777,7 @@ const i18n = {
'config_management.status_save_failed': 'Save failed',
'config_management.save_success': 'Configuration saved successfully',
'config_management.error_yaml_not_supported': 'Server did not return YAML. Verify the /config.yaml endpoint is available.',
'config_management.editor_placeholder': 'key: value',
// System info
'system_info.title': 'System Information',
@@ -816,6 +833,7 @@ const i18n = {
'notification.gemini_api_key': 'Gemini API key',
'notification.codex_api_key': 'Codex API key',
'notification.claude_api_key': 'Claude API key',
'notification.link_copied': 'Link copied to clipboard',
// Language switch
'language.switch': 'Language',
@@ -830,6 +848,10 @@ const i18n = {
'theme.switch_to_dark': 'Switch to dark mode',
'theme.auto': 'Follow system',
// Sidebar
'sidebar.toggle_expand': 'Expand sidebar',
'sidebar.toggle_collapse': 'Collapse sidebar',
// Footer
'footer.version': 'Version',
'footer.author': 'Author'
@@ -879,6 +901,30 @@ const i18n = {
}
});
// 更新所有包含 data-i18n-placeholder 的输入框占位符
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
const key = element.getAttribute('data-i18n-placeholder');
element.placeholder = this.t(key);
});
// 更新 data-i18n-title
document.querySelectorAll('[data-i18n-title]').forEach(element => {
const key = element.getAttribute('data-i18n-title');
element.title = this.t(key);
});
// 更新 data-i18n-tooltip
document.querySelectorAll('[data-i18n-tooltip]').forEach(element => {
const key = element.getAttribute('data-i18n-tooltip');
element.setAttribute('data-tooltip', this.t(key));
});
// 更新 data-i18n-text常用于按钮或标签
document.querySelectorAll('[data-i18n-text]').forEach(element => {
const key = element.getAttribute('data-i18n-text');
element.textContent = this.t(key);
});
// 更新所有带有 data-i18n-html 属性的元素支持HTML
document.querySelectorAll('[data-i18n-html]').forEach(element => {
const key = element.getAttribute('data-i18n-html');

View File

@@ -77,8 +77,7 @@
<div class="form-group">
<label for="login-api-base" data-i18n="login.custom_connection_label">自定义连接地址:</label>
<div class="input-group">
<input type="text" id="login-api-base" data-i18n="login.custom_connection_placeholder"
placeholder="例如: https://example.com:8317">
<input type="text" id="login-api-base" data-i18n-placeholder="login.custom_connection_placeholder">
<button type="button" id="login-reset-api-base"
class="btn btn-secondary connection-reset-btn">
<i class="fas fa-location-arrow"></i>
@@ -91,8 +90,7 @@
<div class="form-group">
<label for="login-management-key" data-i18n="login.management_key_label">管理密钥:</label>
<div class="input-group">
<input type="password" id="login-management-key"
data-i18n="login.management_key_placeholder" placeholder="请输入管理密钥" required>
<input type="password" id="login-management-key" data-i18n-placeholder="login.management_key_placeholder" required>
<button type="button" class="btn btn-secondary toggle-key-visibility">
<i class="fas fa-eye"></i>
</button>
@@ -123,7 +121,7 @@
<button class="mobile-menu-btn" id="mobile-menu-btn">
<i class="fas fa-bars"></i>
</button>
<button class="sidebar-toggle-btn-desktop" id="sidebar-toggle-btn-desktop" title="收起/展开侧边栏">
<button class="sidebar-toggle-btn-desktop" id="sidebar-toggle-btn-desktop" data-i18n-title="sidebar.toggle_collapse">
<i class="fas fa-bars"></i>
</button>
<div class="top-navbar-brand">
@@ -161,29 +159,29 @@
<nav class="sidebar" id="sidebar">
<!-- 导航菜单 -->
<ul class="nav-menu">
<li data-tooltip="基础设置"><a href="#basic-settings" class="nav-item active"
<li data-i18n-tooltip="nav.basic_settings"><a href="#basic-settings" class="nav-item active"
data-section="basic-settings">
<i class="fas fa-sliders-h"></i> <span data-i18n="nav.basic_settings">基础设置</span>
</a></li>
<li data-tooltip="API 密钥"><a href="#api-keys" class="nav-item" data-section="api-keys">
<li data-i18n-tooltip="nav.api_keys"><a href="#api-keys" class="nav-item" data-section="api-keys">
<i class="fas fa-key"></i> <span data-i18n="nav.api_keys">API 密钥</span>
</a></li>
<li data-tooltip="AI 提供商"><a href="#ai-providers" class="nav-item" data-section="ai-providers">
<li data-i18n-tooltip="nav.ai_providers"><a href="#ai-providers" class="nav-item" data-section="ai-providers">
<i class="fas fa-robot"></i> <span data-i18n="nav.ai_providers">AI 提供商</span>
</a></li>
<li data-tooltip="认证文件"><a href="#auth-files" class="nav-item" data-section="auth-files">
<li data-i18n-tooltip="nav.auth_files"><a href="#auth-files" class="nav-item" data-section="auth-files">
<i class="fas fa-file-alt"></i> <span data-i18n="nav.auth_files">认证文件</span>
</a></li>
<li data-tooltip="使用统计"><a href="#usage-stats" class="nav-item" data-section="usage-stats">
<li data-i18n-tooltip="nav.usage_stats"><a href="#usage-stats" class="nav-item" data-section="usage-stats">
<i class="fas fa-chart-line"></i> <span data-i18n="nav.usage_stats">使用统计</span>
</a></li>
<li data-tooltip="配置管理"><a href="#config-management" class="nav-item" data-section="config-management">
<li data-i18n-tooltip="nav.config_management"><a href="#config-management" class="nav-item" data-section="config-management">
<i class="fas fa-cog"></i> <span data-i18n="nav.config_management">配置管理</span>
</a></li>
<li id="logs-nav-item" data-tooltip="日志查看" style="display: none;"><a href="#logs" class="nav-item" data-section="logs">
<li id="logs-nav-item" data-i18n-tooltip="nav.logs" style="display: none;"><a href="#logs" class="nav-item" data-section="logs">
<i class="fas fa-scroll"></i> <span data-i18n="nav.logs">日志查看</span>
</a></li>
<li data-tooltip="系统信息"><a href="#system-info" class="nav-item" data-section="system-info">
<li data-i18n-tooltip="nav.system_info"><a href="#system-info" class="nav-item" data-section="system-info">
<i class="fas fa-info-circle"></i> <span data-i18n="nav.system_info">系统信息</span>
</a></li>
</ul>
@@ -230,9 +228,7 @@
<label for="proxy-url" data-i18n="basic_settings.proxy_url_label">代理
URL:</label>
<div class="input-group">
<input type="text" id="proxy-url"
data-i18n="basic_settings.proxy_url_placeholder"
placeholder="例如: socks5://user:pass@127.0.0.1:1080/">
<input type="text" id="proxy-url" data-i18n-placeholder="basic_settings.proxy_url_placeholder">
<button id="update-proxy" class="btn btn-primary"
data-i18n="basic_settings.proxy_update">更新</button>
<button id="clear-proxy" class="btn btn-danger"
@@ -533,8 +529,7 @@
data-i18n="auth_login.gemini_cli_project_id_label">Google Cloud 项目 ID
(可选):</label>
<input type="text" id="gemini-cli-project-id"
data-i18n="auth_login.gemini_cli_project_id_placeholder"
placeholder="输入 Google Cloud 项目 ID (可选)">
data-i18n-placeholder="auth_login.gemini_cli_project_id_placeholder">
<div class="form-hint" data-i18n="auth_login.gemini_cli_project_id_hint">
如果指定了项目 ID将使用该项目的认证信息。
</div>
@@ -794,7 +789,7 @@
<div class="card-content">
<p class="form-hint" data-i18n="config_management.description">查看并编辑服务器上的 config.yaml 配置文件。保存前请确认语法正确。</p>
<div class="yaml-editor-container">
<textarea id="config-editor" class="yaml-editor" spellcheck="false" placeholder="key: value"></textarea>
<textarea id="config-editor" class="yaml-editor" spellcheck="false" data-i18n="config_management.editor_placeholder"></textarea>
<div id="config-editor-status" class="editor-status" data-i18n="config_management.status_idle">等待操作</div>
</div>
</div>