feat(webui): optimize frontend terminology and i18n keys

This commit is contained in:
Wha1eChai
2026-01-09 19:55:34 +08:00
parent 07a9586aee
commit 8e221e3fc7
6 changed files with 65 additions and 61 deletions

View File

@@ -165,11 +165,9 @@ document.addEventListener('alpine:init', () => {
clearInterval(pollInterval);
Alpine.store('global').oauthProgress.active = false;
const actionKey = reAuthEmail ? 'reauthenticated' : 'added';
const action = Alpine.store('global').t(actionKey);
const successfully = Alpine.store('global').t('successfully');
const actionKey = reAuthEmail ? 'accountReauthSuccess' : 'accountAddedSuccess';
Alpine.store('global').showToast(
`${Alpine.store('global').t('accounts')} ${action} ${successfully}`,
Alpine.store('global').t(actionKey),
'success'
);
document.getElementById('add_account_modal')?.close();

View File

@@ -257,7 +257,7 @@
<!-- Add Account Modal -->
<dialog id="add_account_modal" class="modal backdrop-blur-sm">
<div class="modal-box bg-space-900 border border-space-border text-gray-300 shadow-[0_0_50px_rgba(0,0,0,0.5)]">
<h3 class="font-bold text-lg text-white" x-text="$store.global.t('addNode')">Add New Account</h3>
<h3 class="font-bold text-lg text-white" x-text="$store.global.t('addAccount')">Add New Account</h3>
<div class="py-6 flex flex-col gap-4">
<p class="text-sm text-gray-400" x-text="$store.global.t('connectGoogleDesc')">Connect a Google

View File

@@ -36,7 +36,7 @@ document.addEventListener('alpine:init', () => {
systemConfig: "System Configuration",
language: "Language",
pollingInterval: "Polling Interval",
logBufferSize: "Log Buffer Size",
maxDisplayLogs: "Max Displayed Logs",
showExhausted: "Show Exhausted Models",
showExhaustedDesc: "Display models even if they have 0% remaining quota.",
compactMode: "Compact Mode",
@@ -44,13 +44,13 @@ document.addEventListener('alpine:init', () => {
saveChanges: "Save Changes",
autoScroll: "Auto-scroll",
clearLogs: "Clear Logs",
accessCredentials: "Access Credentials",
manageTokens: "Manage OAuth tokens and session states",
addNode: "Add Node",
accountManagement: "Account Management",
manageTokens: "Manage Google Account tokens and authorization states",
addAccount: "Add Account",
status: "STATUS",
enabled: "ENABLED",
health: "HEALTH",
identity: "IDENTITY (EMAIL)",
health: "STATUS",
accountEmail: "ACCOUNT (EMAIL)",
source: "SOURCE",
projectId: "PROJECT ID",
sessionState: "SESSION STATE",
@@ -62,6 +62,8 @@ document.addEventListener('alpine:init', () => {
reauthenticated: "re-authenticated",
added: "added",
successfully: "successfully",
accountAddedSuccess: "Account added successfully",
accountReauthSuccess: "Account re-authenticated successfully",
failedToGetAuthUrl: "Failed to get auth URL",
failedToStartOAuth: "Failed to start OAuth flow",
oauthInProgress: "OAuth in progress. Please complete authentication in the popup window...",
@@ -72,13 +74,13 @@ document.addEventListener('alpine:init', () => {
tabInterface: "Interface",
tabClaude: "Claude CLI",
tabModels: "Models",
tabServer: "Server Info",
tabServer: "Server Settings",
// Dashboard
registeredNodes: "Registered Nodes",
linkedAccounts: "Linked Accounts",
noSignal: "NO SIGNAL DETECTED",
establishingUplink: "ESTABLISHING UPLINK...",
// Settings - Models
modelsDesc: "Configure model visibility, pinning, and request redirection.",
modelsDesc: "Configure model visibility, pinning, and request routing.",
modelsPageDesc: "Real-time quota and status for all available models.",
showHidden: "Show Hidden Models",
modelId: "Model ID",
@@ -86,24 +88,24 @@ document.addEventListener('alpine:init', () => {
pinToTop: "Pin to top",
toggleVisibility: "Toggle Visibility",
noModels: "NO MODELS DETECTED",
modelMappingHint: "Server-side model redirection. Claude Code users: see 'Claude CLI' tab for easier setup.",
modelMapping: "Mapping (Target Model)",
modelMappingHint: "Server-side model routing. Claude Code users: see 'Claude CLI' tab for client-side setup.",
modelMapping: "Mapping (Target Model ID)",
// Settings - Claude
proxyConnection: "Proxy Connection",
modelSelection: "Model Selection",
aliasOverrides: "ALIAS OVERRIDES",
defaultModelAliases: "DEFAULT MODEL ALIASES",
opusAlias: "Opus Alias",
sonnetAlias: "Sonnet Alias",
haikuAlias: "Haiku Alias",
claudeSettingsAlert: "Settings below directly modify ~/.claude/settings.json. Restart Claude CLI to apply.",
writeToConfig: "Write to Config",
applyToClaude: "Apply to Claude CLI",
// Settings - Server
port: "Port",
uiVersion: "UI Version",
debugMode: "Debug Mode",
environment: "Environment",
serverReadOnly: "Server settings are read-only. Modify config.json or .env and restart the server to change.",
dangerZone: "Danger Zone / Advanced",
serverReadOnly: "Settings managed via config.json. Restart server to apply changes.",
advancedSettings: "Advanced Settings",
reloadConfigTitle: "Reload Account Config",
reloadConfigDesc: "Force reload accounts.json from disk",
reload: "Reload",
@@ -185,8 +187,8 @@ document.addEventListener('alpine:init', () => {
totalColon: "Total:",
todayColon: "Today:",
hour1Colon: "1H:",
smart: "Smart",
smartTitle: "Select Top 5 most used models (24h)",
frequentModels: "Frequent",
smartTitle: "Auto-select top 5 most used models (24h)",
activeCount: "{count} Active",
allCaps: "ALL",
claudeCaps: "CLAUDE",
@@ -200,14 +202,14 @@ document.addEventListener('alpine:init', () => {
maxRetries: "Max Retries",
retryBaseDelay: "Retry Base Delay (ms)",
retryMaxDelay: "Retry Max Delay (ms)",
persistTokenCache: "Persist Token Cache",
persistTokenDesc: "Save OAuth tokens to disk for faster restarts",
persistentSessions: "Persistent Sessions",
persistTokenDesc: "Save OAuth sessions to disk for faster restarts",
rateLimiting: "Account Rate Limiting & Timeouts",
defaultCooldown: "Default Cooldown Time",
maxWaitThreshold: "Max Wait Threshold (Sticky)",
maxWaitDesc: "Maximum time to wait for a sticky account to reset before failing or switching.",
maxWaitDesc: "Maximum time to wait for a sticky account to reset before switching.",
saveConfigServer: "Save Configuration",
serverRestartAlert: "Some changes may require server restart. Config is saved to {path}",
serverRestartAlert: "Changes saved to {path}. Restart server to apply some settings.",
changePassword: "Change WebUI Password",
changePasswordDesc: "Update the password for accessing this dashboard",
currentPassword: "Current Password",
@@ -249,6 +251,7 @@ document.addEventListener('alpine:init', () => {
gemini1mMode: "Gemini 1M Context Mode",
gemini1mDesc: "Appends [1m] suffix to Gemini models for 1M context window support.",
gemini1mWarning: "⚠ Large context may reduce Gemini-3-Pro performance.",
clickToSet: "Click to configure...",
},
zh: {
dashboard: "仪表盘",
@@ -273,7 +276,7 @@ document.addEventListener('alpine:init', () => {
systemConfig: "系统配置",
language: "语言设置",
pollingInterval: "数据轮询间隔",
logBufferSize: "日志缓冲大小",
maxDisplayLogs: "最大日志显示行数",
showExhausted: "显示耗尽模型",
showExhaustedDesc: "即使配额为 0% 也显示模型。",
compactMode: "紧凑模式",
@@ -281,13 +284,13 @@ document.addEventListener('alpine:init', () => {
saveChanges: "保存更改",
autoScroll: "自动滚动",
clearLogs: "清除日志",
accessCredentials: "访问凭证",
manageTokens: "管理 OAuth 令牌和会话状态",
addNode: "添加节点",
accountManagement: "账号管理",
manageTokens: "管理已授权的 Google 账号及其状态",
addAccount: "添加账号",
status: "状态",
enabled: "启用",
health: "健康度",
identity: "身份 (邮箱)",
health: "状态",
accountEmail: "账号 (邮箱)",
source: "来源",
projectId: "项目 ID",
sessionState: "会话状态",
@@ -299,6 +302,8 @@ document.addEventListener('alpine:init', () => {
reauthenticated: "已重新认证",
added: "已添加",
successfully: "成功",
accountAddedSuccess: "账号添加成功",
accountReauthSuccess: "账号重新认证成功",
failedToGetAuthUrl: "获取认证链接失败",
failedToStartOAuth: "启动 OAuth 流程失败",
oauthInProgress: "OAuth 授权进行中,请在弹出窗口中完成认证...",
@@ -310,13 +315,13 @@ document.addEventListener('alpine:init', () => {
tabInterface: "界面设置",
tabClaude: "Claude CLI",
tabModels: "模型管理",
tabServer: "服务器信息",
tabServer: "服务器设置",
// Dashboard
registeredNodes: "已注册节点",
linkedAccounts: "已关联账号",
noSignal: "无信号连接",
establishingUplink: "正在建立上行链路...",
// Settings - Models
modelsDesc: "配置模型的可见性、置顶和请求重定向。",
modelsDesc: "配置模型的可见性、置顶和请求路由。",
modelsPageDesc: "所有可用模型的实时配额和状态。",
showHidden: "显示隐藏模型",
modelId: "模型 ID",
@@ -324,24 +329,24 @@ document.addEventListener('alpine:init', () => {
pinToTop: "置顶",
toggleVisibility: "切换可见性",
noModels: "未检测到模型",
modelMappingHint: "服务端模型重定向功能。Claude Code 用户请使用 'Claude CLI' 标签页以便捷配置。",
modelMapping: "映射 (目标模型)",
modelMappingHint: "服务端模型路由功能。Claude Code 用户请使用 'Claude CLI' 标签页以便捷配置。",
modelMapping: "映射 (目标模型 ID)",
// Settings - Claude
proxyConnection: "代理连接",
modelSelection: "模型选择",
aliasOverrides: "别名覆盖",
defaultModelAliases: "默认模型映射 (别名)",
opusAlias: "Opus 别名",
sonnetAlias: "Sonnet 别名",
haikuAlias: "Haiku 别名",
claudeSettingsAlert: "以下设置直接修改 ~/.claude/settings.json。重启 Claude CLI 生效。",
writeToConfig: "写入配置",
applyToClaude: "应用到 Claude CLI",
// Settings - Server
port: "端口",
uiVersion: "UI 版本",
debugMode: "调试模式",
environment: "运行环境",
serverReadOnly: "服务器设置只读。修改 config.json 或 .env 并重启服务器以生效。",
dangerZone: "危险区域 / 高级",
serverReadOnly: "配置由 config.json 管理。重启服务器以应用更改。",
advancedSettings: "高级设置",
reloadConfigTitle: "重载账号配置",
reloadConfigDesc: "强制从磁盘重新读取 accounts.json",
reload: "重载",
@@ -423,8 +428,8 @@ document.addEventListener('alpine:init', () => {
totalColon: "总计:",
todayColon: "今日:",
hour1Colon: "1小时:",
smart: "智能选择",
smartTitle: "自动选过去 24 小时最常用的 5 个模型",
frequentModels: "常用推荐",
smartTitle: "自动选过去 24 小时最常用的 5 个模型",
activeCount: "{count} 活跃",
allCaps: "全部",
claudeCaps: "CLAUDE",
@@ -438,14 +443,14 @@ document.addEventListener('alpine:init', () => {
maxRetries: "最大重试次数",
retryBaseDelay: "重试基础延迟 (毫秒)",
retryMaxDelay: "重试最大延迟 (毫秒)",
persistTokenCache: "持久化令牌缓存",
persistTokenDesc: "将 OAuth 令牌保存到磁盘以实现快速重启",
persistentSessions: "持久化登录会话",
persistTokenDesc: "将登录会话保存到磁盘以实现快速重启",
rateLimiting: "账号限流与超时",
defaultCooldown: "默认冷却时间",
maxWaitThreshold: "最大等待阈值 (粘性会话)",
maxWaitDesc: "粘性账号在失败或切换前等待重置的最长时间。",
saveConfigServer: "保存配置",
serverRestartAlert: "部分更改可能需要重启服务器。配置已保存至 {path}",
serverRestartAlert: "配置已保存至 {path}。部分更改可能需要重启服务器。",
changePassword: "修改 WebUI 密码",
changePasswordDesc: "更新访问此仪表盘的密码",
currentPassword: "当前密码",
@@ -466,7 +471,7 @@ document.addEventListener('alpine:init', () => {
// 账号页面
searchAccounts: "搜索账号...",
noAccountsYet: "还没有添加任何账号",
noAccountsDesc: "点击上方的 \"添加节点\" 按钮通过 OAuth 添加 Google 账号,或者使用 CLI 命令导入凭证。",
noAccountsDesc: "点击上方的 \"添加账号\" 按钮通过 OAuth 添加 Google 账号,或者使用 CLI 命令导入凭证。",
addFirstAccount: "添加第一个账号",
noSearchResults: "没有找到匹配的账号",
clearSearch: "清除搜索",
@@ -487,6 +492,7 @@ document.addEventListener('alpine:init', () => {
gemini1mMode: "Gemini 1M 上下文模式",
gemini1mDesc: "为 Gemini 模型添加 [1m] 后缀以支持 1M 上下文窗口。",
gemini1mWarning: "⚠ 大上下文可能降低 Gemini-3-Pro 性能。",
clickToSet: "点击进行配置...",
}
},

View File

@@ -3,12 +3,12 @@
<div class="flex items-center justify-between gap-4 mb-6">
<!-- Title with inline subtitle -->
<div class="flex items-baseline gap-3">
<h1 class="text-2xl font-bold text-white tracking-tight" x-text="$store.global.t('accessCredentials')">
Access Credentials
<h1 class="text-2xl font-bold text-white tracking-tight" x-text="$store.global.t('accountManagement')">
Account Management
</h1>
<span class="text-[10px] font-mono text-gray-600 uppercase tracking-[0.15em]"
x-text="$store.global.t('manageTokens')">
Manage OAuth tokens and session states
Manage Google Account tokens and authorization states
</span>
</div>
@@ -37,7 +37,7 @@
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<span x-text="$store.global.t('addNode')">Add Node</span>
<span x-text="$store.global.t('addAccount')">Add Account</span>
</button>
</div>
</div>
@@ -48,7 +48,7 @@
<thead x-show="$store.data.accounts.length > 0">
<tr class="bg-space-900/50 border-b border-space-border/50">
<th class="pl-6 py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-16" x-text="$store.global.t('enabled')">Enabled</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider flex-1 min-w-[200px]" x-text="$store.global.t('identity')">Identity (Email)</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider flex-1 min-w-[200px]" x-text="$store.global.t('accountEmail')">Account (Email)</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-20" x-text="$store.global.t('source')">Source</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-24" x-text="$store.global.t('health')">Health</th>
<th class="py-3 pr-6 text-right text-[10px] font-bold text-gray-500 uppercase tracking-wider w-32" x-text="$store.global.t('operations')">Operations</th>

View File

@@ -45,7 +45,7 @@
<div class="stat-title text-gray-500 font-mono text-xs uppercase tracking-wider truncate"
x-text="$store.global.t('totalAccounts')"></div>
<div class="stat-desc text-cyan-400/60 text-[10px] truncate flex items-center gap-1">
<span x-text="$store.global.t('registeredNodes')"></span>
<span x-text="$store.global.t('linkedAccounts')"></span>
<svg class="w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
@@ -219,7 +219,7 @@
x-text="displayMode === 'family' ? $store.global.t('selectFamilies') : $store.global.t('selectModels')"></span>
<div class="flex gap-1">
<button @click="autoSelectTopN(5)" class="text-[10px] text-neon-purple hover:underline"
:title="$store.global.t('smartTitle')" x-text="$store.global.t('smart')">
:title="$store.global.t('smartTitle')" x-text="$store.global.t('frequentModels')">
Smart
</button>
<span class="text-gray-600">|</span>

View File

@@ -114,7 +114,7 @@
<!-- Log Buffer -->
<div class="form-control col-span-full">
<label class="label">
<span class="label-text text-gray-300" x-text="$store.global.t('logBufferSize')">Log Buffer
<span class="label-text text-gray-300" x-text="$store.global.t('maxDisplayLogs')">Log Buffer
Size</span>
<span class="label-text-alt font-mono text-neon-purple"
x-text="$store.settings.logLimit + ' ' + $store.global.t('lines')"></span>
@@ -303,7 +303,7 @@
</div>
<div class="divider text-xs font-mono text-gray-600 my-2"
x-text="$store.global.t('aliasOverrides')">ALIAS OVERRIDES</div>
x-text="$store.global.t('defaultModelAliases')">DEFAULT MODEL ALIASES</div>
<!-- Overrides -->
<div class="space-y-4">
@@ -460,7 +460,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
</svg>
<span x-show="!loading" x-text="$store.global.t('writeToConfig')">Write to Config</span>
<span x-show="!loading" x-text="$store.global.t('applyToClaude')">Apply to Claude CLI</span>
<span x-show="loading" class="loading loading-spinner loading-xs"></span>
</button>
</div>
@@ -524,7 +524,7 @@
<div x-show="!isEditing(modelId)"
class="flex items-center gap-2 group-hover:text-white transition-colors cursor-pointer py-2"
@click="startEditing(modelId); newMapping = config.mapping || ''; $nextTick(() => $refs['input-' + modelId]?.focus())">
<span x-text="config.mapping || 'Click to set...'"
<span x-text="config.mapping || $store.global.t('clickToSet')"
:class="{'text-gray-600 italic': !config.mapping, 'text-gray-300': config.mapping}"
class="text-xs font-mono"></span>
<svg class="w-3 h-3 text-gray-600 opacity-0 group-hover:opacity-100 transition-opacity"
@@ -698,7 +698,7 @@
<div class="flex flex-col gap-1">
<span class="text-sm font-medium text-gray-200"
:class="serverConfig.persistTokenCache ? 'text-neon-green' : ''"
x-text="$store.global.t('persistTokenCache')">Persist Token Cache</span>
x-text="$store.global.t('persistentSessions')">Persist Token Cache</span>
<span class="text-[11px] text-gray-500"
x-text="$store.global.t('persistTokenDesc')">Save OAuth tokens to disk for faster
restarts</span>
@@ -729,9 +729,9 @@
</div>
<div>
<span class="text-sm font-semibold text-gray-200"
x-text="$store.global.t('dangerZone')">Advanced Tuning</span>
x-text="$store.global.t('advancedSettings')">Advanced Settings</span>
<span class="text-[10px] text-gray-600 block"
x-text="$store.global.t('serverReadOnly')">Experienced users only</span>
x-text="$store.global.t('serverReadOnly')">Settings managed via config.json</span>
</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg"