feat: 基础本地化支持与通用键补充(useConfirm/useChartConfig/format/apiStats 回退 + common.time/errors 等 i18n 键)

This commit is contained in:
Wangnov
2025-09-10 18:04:09 +08:00
parent 97b94eeff9
commit 5f5826ce56
7 changed files with 1274 additions and 30 deletions

View File

@@ -1,4 +1,5 @@
import { Chart } from 'chart.js/auto'
import i18n from '@/i18n'
export function useChartConfig() {
// 设置Chart.js默认配置
@@ -51,7 +52,9 @@ export function useChartConfig() {
label += ': '
}
if (context.parsed.y !== null) {
label += new Intl.NumberFormat('zh-CN').format(context.parsed.y)
const localeMap = { 'zh-cn': 'zh-CN', 'zh-tw': 'zh-TW', en: 'en-US' }
const currentLocale = localeMap[i18n.global.locale.value] || 'en-US'
label += new Intl.NumberFormat(currentLocale).format(context.parsed.y)
}
return label
}

View File

@@ -1,16 +1,22 @@
import { ref } from 'vue'
import i18n from '@/i18n'
const showConfirmModal = ref(false)
const confirmOptions = ref({
title: '',
message: '',
confirmText: '继续',
cancelText: '取消'
confirmText: i18n.global.t('common.confirmModal.continue'),
cancelText: i18n.global.t('common.confirmModal.cancel')
})
const confirmResolve = ref(null)
export function useConfirm() {
const showConfirm = (title, message, confirmText = '继续', cancelText = '取消') => {
const showConfirm = (
title,
message,
confirmText = i18n.global.t('common.confirmModal.continue'),
cancelText = i18n.global.t('common.confirmModal.cancel')
) => {
return new Promise((resolve) => {
confirmOptions.value = {
title,

View File

@@ -1,6 +1,8 @@
// API Stats 专用 API 客户端
// 与管理员 API 隔离,不需要认证
import i18n from '@/i18n'
class ApiStatsClient {
constructor() {
this.baseURL = window.location.origin
@@ -26,7 +28,9 @@ class ApiStatsClient {
const data = await response.json()
if (!response.ok) {
throw new Error(data.message || `请求失败: ${response.status}`)
throw new Error(
data.message || i18n.global.t('common.errors.requestFailed', { status: response.status })
)
}
return data

View File

@@ -1,4 +1,47 @@
export default {
layout: {
mainLayout: {
comments: {
topNavigation: 'Top Navigation',
mainContentArea: 'Main Content Area',
tabBar: 'Tab Bar',
contentArea: 'Content Area'
},
routing: {
routeChangeError: 'Route change failed:',
routeNotFound: 'Route not found',
defaultToTab: 'Default to dashboard'
}
},
tabBar: {
tabs: {
dashboard: {
name: 'Dashboard',
shortName: 'Dashboard'
},
apiKeys: {
name: 'API Keys',
shortName: 'API'
},
accounts: {
name: 'Account Management',
shortName: 'Accounts'
},
userManagement: {
name: 'User Management',
shortName: 'Users'
},
tutorial: {
name: 'Tutorial',
shortName: 'Tutorial'
},
settings: {
name: 'System Settings',
shortName: 'Settings'
}
}
}
},
common: {
save: 'Save',
cancel: 'Cancel',
@@ -10,7 +53,89 @@ export default {
update: 'Update',
search: 'Search',
reset: 'Reset',
locale: 'en'
locale: 'en',
toastNotification: {
defaultTitles: {
success: 'Success',
error: 'Error',
warning: 'Warning',
info: 'Information'
}
},
confirmDialog: {
confirm: 'Confirm',
cancel: 'Cancel'
},
confirmModal: {
continue: 'Continue',
cancel: 'Cancel'
},
themeToggle: {
light: {
label: 'Light Mode',
shortLabel: 'Light'
},
dark: {
label: 'Dark Mode',
shortLabel: 'Dark'
},
auto: {
label: 'Follow System',
shortLabel: 'Auto'
},
toggleTheme: 'Toggle Theme',
clickToSwitch: 'Click to switch theme'
},
logoTitle: {
logoAlt: 'Logo'
},
languageSwitch: {
zhCnName: 'Simplified Chinese',
zhTwName: 'Traditional Chinese',
enName: 'English',
zhCnFlag: 'CN',
zhTwFlag: 'TW',
enFlag: 'EN'
},
accountSelector: {
searchPlaceholder: 'Search account name...',
schedulingGroups: 'Scheduling Groups',
membersUnit: ' members',
claudeOAuthAccounts: 'Claude OAuth Dedicated Accounts',
oauthAccounts: 'OAuth Dedicated Accounts',
claudeConsoleAccounts: 'Claude Console Dedicated Accounts',
noResultsFound: 'No matching accounts found',
selectAccount: 'Please select an account',
useSharedPool: 'Use shared account pool',
accountStatus: {
unknown: 'Unknown',
unauthorized: 'Unauthorized',
tokenError: 'Token Error',
pending: 'Pending',
rateLimited: 'Rate Limited',
error: 'Error',
active: 'Active'
},
dateFormat: {
today: 'Created today',
yesterday: 'Created yesterday',
daysAgo: ' days ago'
}
},
customDropdown: {
placeholder: 'Please select'
},
// Common time and errors
time: {
justNow: 'Just now',
minutesAgo: '{minutes} minutes ago',
hoursAgo: '{hours} hours ago',
daysAgo: '{days} days ago'
},
errors: {
requestFailed: 'Request failed: {status}',
loadSupportedClientsFailed: 'Failed to load supported clients'
}
},
language: {
zh: '简体中文',
@@ -210,7 +335,15 @@ export default {
securityNoticeMulti:
'Your API Keys are only used to query statistical data and will not be stored. Some individual information will not be displayed in aggregate mode.',
multiKeyTip:
'Tip: Supports querying up to 30 API Keys simultaneously. Use Ctrl+Enter for quick query.'
'Tip: Supports querying up to 30 API Keys simultaneously. Use Ctrl+Enter for quick query.',
errors: {
queryStatsFailed: 'Failed to query statistics, please check your API Key',
enterAtLeastOneKey: 'Please enter at least one valid API Key',
batchQueryFailed: 'Batch query failed',
batchModelStatsFailed: 'Failed to load batch model statistics',
loadModelStatsFailed: 'Failed to load model statistics',
allInvalidKeys: 'All API Keys are invalid'
}
},
// Login page
@@ -221,7 +354,9 @@ export default {
password: 'Password',
passwordPlaceholder: 'Please enter password',
loginButton: 'Login',
loggingIn: 'Logging in...'
loggingIn: 'Logging in...',
loginFailed: 'Login failed',
loginFailedCheck: 'Login failed, please check username and password'
},
// Dashboard page
@@ -262,6 +397,12 @@ export default {
tokensPerMinute: 'Tokens per Minute',
historicalData: 'Historical Data',
minutes: 'minutes',
// Uptime display formats
uptimeFormat: {
daysHours: '{days} days {hours} hours',
hoursMinutes: '{hours} hours {minutes} minutes',
minutes: '{minutes} minutes'
},
// Charts section
modelDistributionAndTrend: 'Model Usage Distribution & Token Usage Trends',
@@ -269,6 +410,7 @@ export default {
// Date filter presets
today: 'Today',
yesterday: 'Yesterday',
dayBefore: 'Day before yesterday',
last7Days: 'Last 7 Days',
last30Days: 'Last 30 Days',
thisWeek: 'This Week',
@@ -322,7 +464,53 @@ export default {
time: 'Time',
date: 'Date',
tokenQuantity: 'Token Quantity',
requestsQuantity: 'Requests Count'
requestsQuantity: 'Requests Count',
// Usage Trend component
usageTrend: {
title: 'Usage Trend',
granularity: {
byDay: 'By Day',
byHour: 'By Hour'
},
periodOptions: {
last24Hours: '24 Hours',
last7Days: '7 Days',
last30Days: '30 Days',
recentDays: 'Last {days} Days'
},
chartLabels: {
requests: 'Request Count',
tokens: 'Token Usage',
requestsAxis: 'Request Count',
tokensAxis: 'Token Usage'
}
},
// Model Distribution component
modelDistribution: {
title: 'Model Usage Distribution',
periods: {
daily: 'Today',
total: 'Total'
},
noData: 'No model usage data available',
units: {
requests: 'requests',
tokens: 'tokens'
},
chart: {
tooltip: {
requests: 'Requests',
tokens: 'Tokens'
}
}
},
errors: {
rangeTooLongHour: 'For hourly granularity, date range cannot exceed 24 hours',
rangeTooLongDay: 'Date range cannot exceed 31 days',
rangeTooLongHourSwitched: 'Hourly range cannot exceed 24 hours, switched to last 24 hours'
}
},
// Accounts page
@@ -1316,6 +1504,52 @@ export default {
loadStatsFailed: 'Failed to load API keys stats'
},
// User API Keys Manager
userApiKeysManager: {
title: 'My API Keys',
description: 'Manage your API keys to access Claude Relay services',
loading: 'Loading API keys...',
warnings: {
maxKeysReached:
'You have reached the maximum number of API keys ({maxApiKeys}). Please delete an existing key to create a new one.'
},
status: {
deleted: 'Deleted',
noDescription: 'No description',
neverUsed: 'Never used'
},
dateLabels: {
created: 'Created',
deleted: 'Deleted',
lastUsed: 'Last used',
expires: 'Expires'
},
usage: {
requests: 'requests'
},
actions: {
viewApiKey: 'View API Key',
deleteApiKey: 'Delete API Key'
},
buttons: {
createApiKey: 'Create API Key',
delete: 'Delete'
},
emptyState: {
title: 'No API keys',
description: 'Get started by creating your first API key.'
},
confirmDelete: {
title: 'Delete API Key',
message: "Are you sure you want to delete '{name}'? This action cannot be undone."
},
messages: {
loadFailed: 'Failed to load API keys',
deleteSuccess: 'API key deleted successfully',
deleteFailed: 'Failed to delete API key'
}
},
// User Login
login: {
title: 'User Sign In',
@@ -1331,7 +1565,50 @@ export default {
// Validation and error messages
requiredFields: 'Please enter both username and password',
loginSuccess: 'Login successful!',
loginFailed: 'Login failed'
loginFailed: 'Login failed',
accountDisabled: 'Your account has been disabled'
},
// View API Key Modal
viewApiKeyModal: {
title: 'API Key Details',
fields: {
name: 'Name',
description: 'Description',
apiKey: 'API Key',
status: 'Status',
usageStatistics: 'Usage Statistics'
},
apiKeyDisplay: {
notAvailable: 'Not available',
keyPreview: 'cr_****',
fullKeyNotice: 'Full API key is only shown when first created or regenerated'
},
buttons: {
hide: 'Hide',
show: 'Show',
copy: 'Copy',
close: 'Close'
},
status: {
active: 'Active',
disabled: 'Disabled'
},
usageStats: {
requests: 'Requests',
inputTokens: 'Input Tokens',
outputTokens: 'Output Tokens',
totalCost: 'Total Cost'
},
timestamps: {
created: 'Created',
lastUsed: 'Last Used',
expires: 'Expires'
},
messages: {
copySuccess: 'Copied to clipboard!',
copyFailed: 'Failed to copy to clipboard'
}
},
// User Management
@@ -1501,6 +1778,124 @@ export default {
// Success message
roleUpdated: 'User role updated to {role}'
},
// User Usage Statistics
userUsageStats: {
// Page header
title: 'Usage Statistics',
subtitle: 'View your API usage statistics and costs',
// Time period selection
periodSelection: {
day: 'Last 24 Hours',
week: 'Last 7 Days',
month: 'Last 30 Days',
quarter: 'Last 90 Days'
},
// Loading state
loadingStats: 'Loading usage statistics...',
// Statistics cards
statsCards: {
totalRequests: 'Total Requests',
inputTokens: 'Input Tokens',
outputTokens: 'Output Tokens',
totalCost: 'Total Cost'
},
// Daily usage trend chart
usageTrend: {
title: 'Daily Usage Trend',
chartTitle: 'Usage Chart',
dailyTrendsDescription: 'Daily usage trends would be displayed here',
chartIntegrationNote:
'(Chart integration can be added with Chart.js, D3.js, or similar library)'
},
// Usage by model section
modelUsage: {
title: 'Usage by Model',
requests: 'requests',
requestsCount: '{count} requests'
},
// Usage by API key table
apiKeyUsage: {
title: 'Usage by API Key',
headers: {
apiKey: 'API Key',
requests: 'Requests',
inputTokens: 'Input Tokens',
outputTokens: 'Output Tokens',
cost: 'Cost',
status: 'Status'
},
status: {
active: 'Active',
disabled: 'Disabled',
deleted: 'Deleted'
}
},
// No data state
noData: {
title: 'No usage data',
description:
"You haven't made any API requests yet. Create an API key and start using the service to see usage statistics."
},
// Error messages
loadFailed: 'Failed to load usage statistics'
},
// Create API Key Modal
createApiKeyModal: {
title: 'Create New API Key',
// Form labels and placeholders
form: {
nameLabel: 'Name',
nameRequired: '*',
namePlaceholder: 'Enter API key name',
descriptionLabel: 'Description',
descriptionPlaceholder: 'Optional description'
},
// Button text
buttons: {
cancel: 'Cancel',
creating: 'Creating...',
createApiKey: 'Create API Key',
copy: 'Copy',
done: 'Done'
},
// Success state
success: {
title: 'API Key Created Successfully!',
warning: {
important: 'Important:',
message: "Copy your API key now. You won't be able to see it again!"
}
},
// Error and validation messages
validation: {
nameRequired: 'API key name is required'
},
errors: {
createFailed: 'Failed to create API key'
},
// Toast messages
messages: {
createSuccess: 'API key created successfully!',
copySuccess: 'API key copied to clipboard!',
copyFailed: 'Failed to copy to clipboard'
}
}
},
@@ -1527,6 +1922,10 @@ export default {
removeIcon: 'Remove',
iconFormats: 'Supports .ico, .png, .jpg, .svg formats, max 350KB',
iconPreview: 'Icon preview',
validation: {
iconTooLarge: 'Icon file size must not exceed 350KB',
iconTypeNotSupported: 'Unsupported file type, please choose .ico, .png, .jpg or .svg'
},
adminEntry: 'Admin Entry',
adminEntryDescription: 'Login button display',

View File

@@ -1,4 +1,47 @@
export default {
layout: {
mainLayout: {
comments: {
topNavigation: '顶部导航',
mainContentArea: '主内容区域',
tabBar: '标签栏',
contentArea: '内容区域'
},
routing: {
routeChangeError: '路由切换失败:',
routeNotFound: '路由未找到',
defaultToTab: '默认选中仪表板'
}
},
tabBar: {
tabs: {
dashboard: {
name: '仪表板',
shortName: '仪表板'
},
apiKeys: {
name: 'API Keys',
shortName: 'API'
},
accounts: {
name: '账户管理',
shortName: '账户'
},
userManagement: {
name: '用户管理',
shortName: '用户'
},
tutorial: {
name: '使用教程',
shortName: '教程'
},
settings: {
name: '系统设置',
shortName: '设置'
}
}
}
},
common: {
save: '保存',
cancel: '取消',
@@ -10,7 +53,89 @@ export default {
update: '更新',
search: '搜索',
reset: '重置',
locale: 'zh-CN'
locale: 'zh-CN',
toastNotification: {
defaultTitles: {
success: '成功',
error: '错误',
warning: '警告',
info: '信息'
}
},
confirmDialog: {
confirm: '确认',
cancel: '取消'
},
confirmModal: {
continue: '继续',
cancel: '取消'
},
themeToggle: {
light: {
label: '浅色模式',
shortLabel: '浅色'
},
dark: {
label: '深色模式',
shortLabel: '深色'
},
auto: {
label: '跟随系统',
shortLabel: '自动'
},
toggleTheme: '切换主题',
clickToSwitch: '点击切换主题'
},
logoTitle: {
logoAlt: '标志'
},
languageSwitch: {
zhCnName: '简体中文',
zhTwName: '繁体中文',
enName: '英语',
zhCnFlag: '简',
zhTwFlag: '繁',
enFlag: 'EN'
},
accountSelector: {
searchPlaceholder: '搜索账号名称...',
schedulingGroups: '调度分组',
membersUnit: '个成员',
claudeOAuthAccounts: 'Claude OAuth 专属账号',
oauthAccounts: 'OAuth 专属账号',
claudeConsoleAccounts: 'Claude Console 专属账号',
noResultsFound: '没有找到匹配的账号',
selectAccount: '请选择账号',
useSharedPool: '使用共享账号池',
accountStatus: {
unknown: '未知',
unauthorized: '未授权',
tokenError: 'Token错误',
pending: '待验证',
rateLimited: '限流中',
error: '异常',
active: '正常'
},
dateFormat: {
today: '今天创建',
yesterday: '昨天创建',
daysAgo: '天前'
}
},
customDropdown: {
placeholder: '请选择'
},
// 通用时间与错误
time: {
justNow: '刚刚',
minutesAgo: '{minutes}分钟前',
hoursAgo: '{hours}小时前',
daysAgo: '{days}天前'
},
errors: {
requestFailed: '请求失败: {status}',
loadSupportedClientsFailed: '加载支持的客户端失败'
}
},
language: {
zh: '简体中文',
@@ -19,6 +144,38 @@ export default {
current: '当前语言',
switch: '切换语言'
},
layout: {
tabBar: {
tabs: {
dashboard: {
name: '仪表板',
shortName: '仪表板'
},
apiKeys: {
name: 'API Keys',
shortName: 'API'
},
accounts: {
name: '账户管理',
shortName: '账户'
},
userManagement: {
name: '用户管理',
shortName: '用户'
},
tutorial: {
name: '使用教程',
shortName: '教程'
},
settings: {
name: '系统设置',
shortName: '设置'
}
}
}
},
header: {
adminPanel: '管理后台',
userMenu: '用户菜单',
@@ -204,7 +361,15 @@ export default {
securityNoticeSingle: '您的 API Key 仅用于查询自己的统计数据,不会被存储或用于其他用途',
securityNoticeMulti:
'您的 API Keys 仅用于查询统计数据,不会被存储。聚合模式下部分个体化信息将不显示。',
multiKeyTip: '提示:最多支持同时查询 30 个 API Keys。使用 Ctrl+Enter 快速查询。'
multiKeyTip: '提示:最多支持同时查询 30 个 API Keys。使用 Ctrl+Enter 快速查询。',
errors: {
queryStatsFailed: '查询统计数据失败,请检查您的 API Key 是否正确',
enterAtLeastOneKey: '请输入至少一个有效的 API Key',
batchQueryFailed: '批量查询失败',
batchModelStatsFailed: '加载批量模型统计失败',
loadModelStatsFailed: '加载模型统计失败',
allInvalidKeys: '所有 API Key 都无效'
}
},
// Login page
@@ -215,7 +380,9 @@ export default {
password: '密码',
passwordPlaceholder: '请输入密码',
loginButton: '登录',
loggingIn: '登录中...'
loggingIn: '登录中...',
loginFailed: '登录失败',
loginFailedCheck: '登录失败,请检查用户名和密码'
},
// Dashboard page
@@ -256,6 +423,12 @@ export default {
tokensPerMinute: '每分钟Token数',
historicalData: '历史数据',
minutes: '分钟',
// Uptime display formats
uptimeFormat: {
daysHours: '{days}天 {hours}小时',
hoursMinutes: '{hours}小时 {minutes}分钟',
minutes: '{minutes}分钟'
},
// Charts section
modelDistributionAndTrend: '模型使用分布与Token使用趋势',
@@ -263,8 +436,9 @@ export default {
// Date filter presets (would be populated from dateFilter.presetOptions)
today: '今日',
yesterday: '昨日',
last7Days: '迗 7 天',
last30Days: '迗 30 天',
dayBefore: '天',
last7Days: '近7天',
last30Days: '近30天',
thisWeek: '本周',
lastWeek: '上周',
thisMonth: '本月',
@@ -316,7 +490,53 @@ export default {
time: '时间',
date: '日期',
tokenQuantity: 'Token数量',
requestsQuantity: '请求次数'
requestsQuantity: '请求次数',
// Usage Trend component
usageTrend: {
title: '使用趋势',
granularity: {
byDay: '按天',
byHour: '按小时'
},
periodOptions: {
last24Hours: '24小时',
last7Days: '7天',
last30Days: '30天',
recentDays: '最近{days}天'
},
chartLabels: {
requests: '请求次数',
tokens: 'Token使用量',
requestsAxis: '请求次数',
tokensAxis: 'Token使用量'
}
},
// Model Distribution component
modelDistribution: {
title: '模型使用分布',
periods: {
daily: '今日',
total: '累计'
},
noData: '暂无模型使用数据',
units: {
requests: '请求',
tokens: 'tokens'
},
chart: {
tooltip: {
requests: '请求',
tokens: 'Tokens'
}
}
},
errors: {
rangeTooLongHour: '小时粒度下日期范围不能超过24小时',
rangeTooLongDay: '日期范围不能超过 31 天',
rangeTooLongHourSwitched: '小时粒度下日期范围不能超过24小时已切换到近24小时'
}
},
// Accounts page
@@ -1285,6 +1505,52 @@ export default {
loadStatsFailed: 'Failed to load API keys stats'
},
// User API Keys Manager
userApiKeysManager: {
title: '我的 API Keys',
description: '管理您的 API Keys 以访问 Claude Relay 服务',
loading: '正在加载 API Keys...',
warnings: {
maxKeysReached:
'您已达到 API Keys 的最大数量限制({maxApiKeys} 个)。请删除现有的 Key 以创建新的。'
},
status: {
deleted: '已删除',
noDescription: '无描述',
neverUsed: '从未使用'
},
dateLabels: {
created: '创建时间',
deleted: '删除时间',
lastUsed: '最后使用',
expires: '到期时间'
},
usage: {
requests: '次请求'
},
actions: {
viewApiKey: '查看 API Key',
deleteApiKey: '删除 API Key'
},
buttons: {
createApiKey: '创建 API Key',
delete: '删除'
},
emptyState: {
title: '无 API Keys',
description: '创建您的第一个 API Key 开始使用。'
},
confirmDelete: {
title: '删除 API Key',
message: "确定要删除 '{name}' 吗?此操作无法撤销。"
},
messages: {
loadFailed: '加载 API Keys 失败',
deleteSuccess: 'API Key 删除成功',
deleteFailed: '删除 API Key 失败'
}
},
// User Login
login: {
title: 'User Sign In',
@@ -1300,7 +1566,50 @@ export default {
// Validation and error messages
requiredFields: 'Please enter both username and password',
loginSuccess: 'Login successful!',
loginFailed: 'Login failed'
loginFailed: 'Login failed',
accountDisabled: '您的账号已被禁用'
},
// View API Key Modal
viewApiKeyModal: {
title: 'API Key 详情',
fields: {
name: '名称',
description: '描述',
apiKey: 'API Key',
status: '状态',
usageStatistics: '使用统计'
},
apiKeyDisplay: {
notAvailable: '不可用',
keyPreview: 'cr_****',
fullKeyNotice: '完整 API Key 仅在首次创建或重新生成时显示'
},
buttons: {
hide: '隐藏',
show: '显示',
copy: '复制',
close: '关闭'
},
status: {
active: '启用',
disabled: '禁用'
},
usageStats: {
requests: '请求次数',
inputTokens: '输入令牌',
outputTokens: '输出令牌',
totalCost: '总费用'
},
timestamps: {
created: '创建时间',
lastUsed: '最后使用',
expires: '过期时间'
},
messages: {
copySuccess: '已复制到剪贴板!',
copyFailed: '复制到剪贴板失败'
}
},
// User Management
@@ -1469,6 +1778,123 @@ export default {
// Success message
roleUpdated: '用户角色已更新为 {role}'
},
// User Usage Statistics
userUsageStats: {
// Page header
title: '使用统计',
subtitle: '查看您的 API 使用统计和费用',
// Time period selection
periodSelection: {
day: '最近24小时',
week: '最近7天',
month: '最近30天',
quarter: '最近90天'
},
// Loading state
loadingStats: '正在加载使用统计...',
// Statistics cards
statsCards: {
totalRequests: '总请求数',
inputTokens: '输入Token',
outputTokens: '输出Token',
totalCost: '总费用'
},
// Daily usage trend chart
usageTrend: {
title: '日使用趋势',
chartTitle: '使用图表',
dailyTrendsDescription: '这里将显示日使用趋势',
chartIntegrationNote: '(可集成 Chart.js、D3.js 或类似图表库)'
},
// Usage by model section
modelUsage: {
title: '按模型使用情况',
requests: '请求',
requestsCount: '{count} 请求'
},
// Usage by API key table
apiKeyUsage: {
title: '按 API Key 使用情况',
headers: {
apiKey: 'API Key',
requests: '请求数',
inputTokens: '输入Token',
outputTokens: '输出Token',
cost: '费用',
status: '状态'
},
status: {
active: '活跃',
disabled: '已禁用',
deleted: '已删除'
}
},
// No data state
noData: {
title: '暂无使用数据',
description:
'您还没有发起任何 API 请求。创建一个 API Key 并开始使用服务后,就能看到使用统计了。'
},
// Error messages
loadFailed: '加载使用统计失败'
},
// Create API Key Modal
createApiKeyModal: {
title: '创建新的 API Key',
// 表单标签和占位符
form: {
nameLabel: '名称',
nameRequired: '*',
namePlaceholder: '为您的 API Key 取一个名称',
descriptionLabel: '备注',
descriptionPlaceholder: '可选的备注信息'
},
// 按钮文本
buttons: {
cancel: '取消',
creating: '创建中...',
createApiKey: '创建 API Key',
copy: '复制',
done: '完成'
},
// 成功状态
success: {
title: 'API Key 创建成功!',
warning: {
important: '重要提示:',
message: '请立即复制您的 API Key您将无法再次查看'
}
},
// 错误和验证消息
validation: {
nameRequired: 'API Key 名称是必填项'
},
errors: {
createFailed: '创建 API Key 失败'
},
// Toast 消息
messages: {
createSuccess: 'API Key 创建成功!',
copySuccess: 'API Key 已复制到剪贴板!',
copyFailed: '复制到剪贴板失败'
}
}
},
@@ -1495,6 +1921,10 @@ export default {
removeIcon: '删除',
iconFormats: '支持 .ico, .png, .jpg, .svg 格式,最大 350KB',
iconPreview: '图标预览',
validation: {
iconTooLarge: '图标文件大小不能超过 350KB',
iconTypeNotSupported: '不支持的文件类型,请选择 .ico, .png, .jpg 或 .svg 文件'
},
adminEntry: '管理入口',
adminEntryDescription: '登录按钮显示',

View File

@@ -1,4 +1,47 @@
export default {
layout: {
mainLayout: {
comments: {
topNavigation: '頂部導航',
mainContentArea: '主內容區域',
tabBar: '標籤欄',
contentArea: '內容區域'
},
routing: {
routeChangeError: '路由切換失敗:',
routeNotFound: '路由未找到',
defaultToTab: '預設選中儀表板'
}
},
tabBar: {
tabs: {
dashboard: {
name: '儀表板',
shortName: '儀表板'
},
apiKeys: {
name: 'API Keys',
shortName: 'API'
},
accounts: {
name: '帳戶管理',
shortName: '帳戶'
},
userManagement: {
name: '用戶管理',
shortName: '用戶'
},
tutorial: {
name: '使用教程',
shortName: '教程'
},
settings: {
name: '系統設置',
shortName: '設置'
}
}
}
},
common: {
save: '保存',
cancel: '取消',
@@ -10,7 +53,89 @@ export default {
update: '更新',
search: '搜尋',
reset: '重置',
locale: 'zh-TW'
locale: 'zh-TW',
toastNotification: {
defaultTitles: {
success: '成功',
error: '錯誤',
warning: '警告',
info: '資訊'
}
},
confirmDialog: {
confirm: '確認',
cancel: '取消'
},
confirmModal: {
continue: '繼續',
cancel: '取消'
},
themeToggle: {
light: {
label: '淺色模式',
shortLabel: '淺色'
},
dark: {
label: '深色模式',
shortLabel: '深色'
},
auto: {
label: '跟隨系統',
shortLabel: '自動'
},
toggleTheme: '切換主題',
clickToSwitch: '點擊切換主題'
},
logoTitle: {
logoAlt: '標誌'
},
languageSwitch: {
zhCnName: '簡體中文',
zhTwName: '繁體中文',
enName: '英語',
zhCnFlag: '簡',
zhTwFlag: '繁',
enFlag: 'EN'
},
accountSelector: {
searchPlaceholder: '搜尋帳號名稱...',
schedulingGroups: '調度分組',
membersUnit: '個成員',
claudeOAuthAccounts: 'Claude OAuth 專屬帳號',
oauthAccounts: 'OAuth 專屬帳號',
claudeConsoleAccounts: 'Claude Console 專屬帳號',
noResultsFound: '沒有找到匹配的帳號',
selectAccount: '請選擇帳號',
useSharedPool: '使用共享帳號池',
accountStatus: {
unknown: '未知',
unauthorized: '未授權',
tokenError: 'Token錯誤',
pending: '待驗證',
rateLimited: '限流中',
error: '異常',
active: '正常'
},
dateFormat: {
today: '今天建立',
yesterday: '昨天建立',
daysAgo: '天前'
}
},
customDropdown: {
placeholder: '請選擇'
},
// 通用時間與錯誤
time: {
justNow: '剛剛',
minutesAgo: '{minutes}分鐘前',
hoursAgo: '{hours}小時前',
daysAgo: '{days}天前'
},
errors: {
requestFailed: '請求失敗: {status}',
loadSupportedClientsFailed: '載入支援的客戶端失敗'
}
},
language: {
zh: '簡體中文',
@@ -204,7 +329,15 @@ export default {
securityNoticeSingle: '您的 API Key 僅用於查詢自己的統計資料,不會被儲存或用於其他用途',
securityNoticeMulti:
'您的 API Keys 僅用於查詢統計資料,不會被儲存。彙整模式下部分個體化資訊將不顯示。',
multiKeyTip: '提示:最多支援同時查詢 30 個 API Keys。使用 Ctrl+Enter 快速查詢。'
multiKeyTip: '提示:最多支援同時查詢 30 個 API Keys。使用 Ctrl+Enter 快速查詢。',
errors: {
queryStatsFailed: '查詢統計資料失敗,請檢查您的 API Key 是否正確',
enterAtLeastOneKey: '請輸入至少一個有效的 API Key',
batchQueryFailed: '批次查詢失敗',
batchModelStatsFailed: '載入批次模型統計失敗',
loadModelStatsFailed: '載入模型統計失敗',
allInvalidKeys: '所有 API Key 都無效'
}
},
// Login page
@@ -215,7 +348,9 @@ export default {
password: '密碼',
passwordPlaceholder: '請輸入密碼',
loginButton: '登錄',
loggingIn: '登錄中...'
loggingIn: '登錄中...',
loginFailed: '登入失敗',
loginFailedCheck: '登入失敗,請檢查使用者名稱與密碼'
},
// Dashboard page
@@ -255,7 +390,13 @@ export default {
requestsPerMinute: '每分钟請求數',
tokensPerMinute: '每分钟Token數',
historicalData: '歷史資料',
minutes: '分',
minutes: '分',
// Uptime display formats
uptimeFormat: {
daysHours: '{days}天 {hours}小時',
hoursMinutes: '{hours}小時 {minutes}分鐘',
minutes: '{minutes}分鐘'
},
// Charts section
modelDistributionAndTrend: '模型使用分佈與Token使用趋勢',
@@ -263,8 +404,9 @@ export default {
// Date filter presets
today: '今日',
yesterday: '昨日',
last7Days: '近 7 天',
last30Days: '近 30 天',
dayBefore: '天',
last7Days: '近7天',
last30Days: '近30天',
thisWeek: '本週',
lastWeek: '上週',
thisMonth: '本月',
@@ -280,6 +422,13 @@ export default {
dateSeparator: '至',
maxHours24: '最多24小時',
// Errors
errors: {
rangeTooLongHour: '小時粒度下日期範圍不能超過24小時',
rangeTooLongDay: '日期範圍不能超過 31 天',
rangeTooLongHourSwitched: '小時粒度超過24小時已切換為近24小時'
},
// Auto refresh controls
autoRefresh: '自動刷新',
refresh: '刷新',
@@ -316,7 +465,48 @@ export default {
time: '時間',
date: '日期',
tokenQuantity: 'Token數量',
requestsQuantity: '請求次數'
requestsQuantity: '請求次數',
// Usage Trend component
usageTrend: {
title: '使用趨勢',
granularity: {
byDay: '按天',
byHour: '按小時'
},
periodOptions: {
last24Hours: '24小時',
last7Days: '7天',
last30Days: '30天',
recentDays: '最近{days}天'
},
chartLabels: {
requests: '請求次數',
tokens: 'Token使用量',
requestsAxis: '請求次數',
tokensAxis: 'Token使用量'
}
},
// Model Distribution component
modelDistribution: {
title: '模型使用分佈',
periods: {
daily: '今日',
total: '累計'
},
noData: '暫無模型使用資料',
units: {
requests: '請求',
tokens: 'tokens'
},
chart: {
tooltip: {
requests: '請求',
tokens: 'Tokens'
}
}
}
},
// Accounts page
@@ -1246,6 +1436,52 @@ export default {
loadStatsFailed: 'Failed to load API keys stats'
},
// User API Keys Manager
userApiKeysManager: {
title: '我的 API Keys',
description: '管理您的 API Keys 以存取 Claude Relay 服務',
loading: '正在載入 API Keys...',
warnings: {
maxKeysReached:
'您已達到 API Keys 的最大數量限制({maxApiKeys} 個)。請刪除現有的 Key 以建立新的。'
},
status: {
deleted: '已刪除',
noDescription: '無描述',
neverUsed: '從未使用'
},
dateLabels: {
created: '建立時間',
deleted: '刪除時間',
lastUsed: '最後使用',
expires: '到期時間'
},
usage: {
requests: '次請求'
},
actions: {
viewApiKey: '檢視 API Key',
deleteApiKey: '刪除 API Key'
},
buttons: {
createApiKey: '建立 API Key',
delete: '刪除'
},
emptyState: {
title: '無 API Keys',
description: '建立您的第一個 API Key 開始使用。'
},
confirmDelete: {
title: '刪除 API Key',
message: "確定要刪除 '{name}' 嗎?此操作無法撤銷。"
},
messages: {
loadFailed: '載入 API Keys 失败',
deleteSuccess: 'API Key 刪除成功',
deleteFailed: '刪除 API Key 失败'
}
},
// User Login
login: {
title: 'User Sign In',
@@ -1261,7 +1497,50 @@ export default {
// Validation and error messages
requiredFields: 'Please enter both username and password',
loginSuccess: 'Login successful!',
loginFailed: 'Login failed'
loginFailed: 'Login failed',
accountDisabled: '您的帳號已被停用'
},
// View API Key Modal
viewApiKeyModal: {
title: 'API Key 詳情',
fields: {
name: '名稱',
description: '描述',
apiKey: 'API Key',
status: '狀態',
usageStatistics: '使用統計'
},
apiKeyDisplay: {
notAvailable: '不可用',
keyPreview: 'cr_****',
fullKeyNotice: '完整 API Key 僅在首次建立或重新產生時顯示'
},
buttons: {
hide: '隱藏',
show: '顯示',
copy: '複製',
close: '關閉'
},
status: {
active: '啟用',
disabled: '停用'
},
usageStats: {
requests: '請求次數',
inputTokens: '輸入權杖',
outputTokens: '輸出權杖',
totalCost: '總費用'
},
timestamps: {
created: '建立時間',
lastUsed: '最後使用',
expires: '過期時間'
},
messages: {
copySuccess: '已複製到剪貼簿!',
copyFailed: '複製到剪貼簿失敗'
}
},
// User Management
@@ -1432,6 +1711,75 @@ export default {
roleUpdated: '使用者角色已更新為 {role}'
},
// User Usage Statistics
userUsageStats: {
// Page header
title: '使用統計',
subtitle: '檢視您的 API 使用統計和費用',
// Time period selection
periodSelection: {
day: '最近24小時',
week: '最近7天',
month: '最近30天',
quarter: '最近90天'
},
// Loading state
loadingStats: '正在載入使用統計...',
// Statistics cards
statsCards: {
totalRequests: '總請求數',
inputTokens: '輸入Token',
outputTokens: '輸出Token',
totalCost: '總費用'
},
// Daily usage trend chart
usageTrend: {
title: '日使用趨勢',
chartTitle: '使用圖表',
dailyTrendsDescription: '這裡將顯示日使用趨勢',
chartIntegrationNote: '(可整合 Chart.js、D3.js 或類似圖表庫)'
},
// Usage by model section
modelUsage: {
title: '按模型使用情況',
requests: '請求',
requestsCount: '{count} 請求'
},
// Usage by API key table
apiKeyUsage: {
title: '按 API Key 使用情況',
headers: {
apiKey: 'API Key',
requests: '請求數',
inputTokens: '輸入Token',
outputTokens: '輸出Token',
cost: '費用',
status: '狀態'
},
status: {
active: '活躍',
disabled: '已停用',
deleted: '已刪除'
}
},
// No data state
noData: {
title: '暫無使用資料',
description:
'您還沒有發起任何 API 請求。建立一個 API Key 並開始使用服務後,就能看到使用統計了。'
},
// Error messages
loadFailed: '載入使用統計失敗'
},
// Usage Detail Modal
usageDetailModal: {
title: '使用統計詳情',
@@ -1470,6 +1818,54 @@ export default {
// Progress indicators
usedPercentage: '已使用 {percentage}%'
},
// Create API Key Modal
createApiKeyModal: {
title: '建立新的 API Key',
// 表單標籤和占位符
form: {
nameLabel: '名稱',
nameRequired: '*',
namePlaceholder: '為您的 API Key 取一個名稱',
descriptionLabel: '備註',
descriptionPlaceholder: '可選的備註資訊'
},
// 按鈕文本
buttons: {
cancel: '取消',
creating: '建立中...',
createApiKey: '建立 API Key',
copy: '複製',
done: '完成'
},
// 成功狀態
success: {
title: 'API Key 建立成功!',
warning: {
important: '重要提示:',
message: '請立即複製您的 API Key您將無法再次查看'
}
},
// 錯誤和驗證訊息
validation: {
nameRequired: 'API Key 名稱是必填項'
},
errors: {
createFailed: '建立 API Key 失敗'
},
// Toast 訊息
messages: {
createSuccess: 'API Key 建立成功!',
copySuccess: 'API Key 已複製到剪貼簿!',
copyFailed: '複製到剪貼簿失敗'
}
}
},
@@ -1496,6 +1892,10 @@ export default {
removeIcon: '刪除',
iconFormats: '支援 .ico, .png, .jpg, .svg 格式,最大 350KB',
iconPreview: '圖標預覽',
validation: {
iconTooLarge: '圖標文件大小不能超過 350KB',
iconTypeNotSupported: '不支援的文件類型,請選擇 .ico, .png, .jpg 或 .svg 文件'
},
adminEntry: '管理入口',
adminEntryDescription: '登入按鈕顯示',

View File

@@ -37,7 +37,9 @@ export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
.replace('ss', seconds)
}
// 相对时间格式化
// 相对时间格式化(使用 i18n
import i18n from '@/i18n'
export function formatRelativeTime(date) {
if (!date) return ''
@@ -50,13 +52,13 @@ export function formatRelativeTime(date) {
const diffDays = Math.floor(diffHours / 24)
if (diffDays > 0) {
return `${diffDays}天前`
return i18n.global.t('common.time.daysAgo', { days: diffDays })
} else if (diffHours > 0) {
return `${diffHours}小时前`
return i18n.global.t('common.time.hoursAgo', { hours: diffHours })
} else if (diffMins > 0) {
return `${diffMins}分钟前`
return i18n.global.t('common.time.minutesAgo', { minutes: diffMins })
} else {
return '刚刚'
return i18n.global.t('common.time.justNow')
}
}