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 { Chart } from 'chart.js/auto'
import i18n from '@/i18n'
export function useChartConfig() { export function useChartConfig() {
// 设置Chart.js默认配置 // 设置Chart.js默认配置
@@ -51,7 +52,9 @@ export function useChartConfig() {
label += ': ' label += ': '
} }
if (context.parsed.y !== null) { 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 return label
} }

View File

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

View File

@@ -1,6 +1,8 @@
// API Stats 专用 API 客户端 // API Stats 专用 API 客户端
// 与管理员 API 隔离,不需要认证 // 与管理员 API 隔离,不需要认证
import i18n from '@/i18n'
class ApiStatsClient { class ApiStatsClient {
constructor() { constructor() {
this.baseURL = window.location.origin this.baseURL = window.location.origin
@@ -26,7 +28,9 @@ class ApiStatsClient {
const data = await response.json() const data = await response.json()
if (!response.ok) { 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 return data

View File

@@ -1,4 +1,47 @@
export default { 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: { common: {
save: 'Save', save: 'Save',
cancel: 'Cancel', cancel: 'Cancel',
@@ -10,7 +53,89 @@ export default {
update: 'Update', update: 'Update',
search: 'Search', search: 'Search',
reset: 'Reset', 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: { language: {
zh: '简体中文', zh: '简体中文',
@@ -210,7 +335,15 @@ export default {
securityNoticeMulti: 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.', '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: 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 // Login page
@@ -221,7 +354,9 @@ export default {
password: 'Password', password: 'Password',
passwordPlaceholder: 'Please enter password', passwordPlaceholder: 'Please enter password',
loginButton: 'Login', loginButton: 'Login',
loggingIn: 'Logging in...' loggingIn: 'Logging in...',
loginFailed: 'Login failed',
loginFailedCheck: 'Login failed, please check username and password'
}, },
// Dashboard page // Dashboard page
@@ -262,6 +397,12 @@ export default {
tokensPerMinute: 'Tokens per Minute', tokensPerMinute: 'Tokens per Minute',
historicalData: 'Historical Data', historicalData: 'Historical Data',
minutes: 'minutes', minutes: 'minutes',
// Uptime display formats
uptimeFormat: {
daysHours: '{days} days {hours} hours',
hoursMinutes: '{hours} hours {minutes} minutes',
minutes: '{minutes} minutes'
},
// Charts section // Charts section
modelDistributionAndTrend: 'Model Usage Distribution & Token Usage Trends', modelDistributionAndTrend: 'Model Usage Distribution & Token Usage Trends',
@@ -269,6 +410,7 @@ export default {
// Date filter presets // Date filter presets
today: 'Today', today: 'Today',
yesterday: 'Yesterday', yesterday: 'Yesterday',
dayBefore: 'Day before yesterday',
last7Days: 'Last 7 Days', last7Days: 'Last 7 Days',
last30Days: 'Last 30 Days', last30Days: 'Last 30 Days',
thisWeek: 'This Week', thisWeek: 'This Week',
@@ -322,7 +464,53 @@ export default {
time: 'Time', time: 'Time',
date: 'Date', date: 'Date',
tokenQuantity: 'Token Quantity', 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 // Accounts page
@@ -1316,6 +1504,52 @@ export default {
loadStatsFailed: 'Failed to load API keys stats' 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 // User Login
login: { login: {
title: 'User Sign In', title: 'User Sign In',
@@ -1331,7 +1565,50 @@ export default {
// Validation and error messages // Validation and error messages
requiredFields: 'Please enter both username and password', requiredFields: 'Please enter both username and password',
loginSuccess: 'Login successful!', 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 // User Management
@@ -1501,6 +1778,124 @@ export default {
// Success message // Success message
roleUpdated: 'User role updated to {role}' 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', removeIcon: 'Remove',
iconFormats: 'Supports .ico, .png, .jpg, .svg formats, max 350KB', iconFormats: 'Supports .ico, .png, .jpg, .svg formats, max 350KB',
iconPreview: 'Icon preview', 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', adminEntry: 'Admin Entry',
adminEntryDescription: 'Login button display', adminEntryDescription: 'Login button display',

View File

@@ -1,4 +1,47 @@
export default { 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: { common: {
save: '保存', save: '保存',
cancel: '取消', cancel: '取消',
@@ -10,7 +53,89 @@ export default {
update: '更新', update: '更新',
search: '搜索', search: '搜索',
reset: '重置', 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: { language: {
zh: '简体中文', zh: '简体中文',
@@ -19,6 +144,38 @@ export default {
current: '当前语言', current: '当前语言',
switch: '切换语言' 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: { header: {
adminPanel: '管理后台', adminPanel: '管理后台',
userMenu: '用户菜单', userMenu: '用户菜单',
@@ -204,7 +361,15 @@ export default {
securityNoticeSingle: '您的 API Key 仅用于查询自己的统计数据,不会被存储或用于其他用途', securityNoticeSingle: '您的 API Key 仅用于查询自己的统计数据,不会被存储或用于其他用途',
securityNoticeMulti: securityNoticeMulti:
'您的 API Keys 仅用于查询统计数据,不会被存储。聚合模式下部分个体化信息将不显示。', '您的 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 // Login page
@@ -215,7 +380,9 @@ export default {
password: '密码', password: '密码',
passwordPlaceholder: '请输入密码', passwordPlaceholder: '请输入密码',
loginButton: '登录', loginButton: '登录',
loggingIn: '登录中...' loggingIn: '登录中...',
loginFailed: '登录失败',
loginFailedCheck: '登录失败,请检查用户名和密码'
}, },
// Dashboard page // Dashboard page
@@ -256,6 +423,12 @@ export default {
tokensPerMinute: '每分钟Token数', tokensPerMinute: '每分钟Token数',
historicalData: '历史数据', historicalData: '历史数据',
minutes: '分钟', minutes: '分钟',
// Uptime display formats
uptimeFormat: {
daysHours: '{days}天 {hours}小时',
hoursMinutes: '{hours}小时 {minutes}分钟',
minutes: '{minutes}分钟'
},
// Charts section // Charts section
modelDistributionAndTrend: '模型使用分布与Token使用趋势', modelDistributionAndTrend: '模型使用分布与Token使用趋势',
@@ -263,8 +436,9 @@ export default {
// Date filter presets (would be populated from dateFilter.presetOptions) // Date filter presets (would be populated from dateFilter.presetOptions)
today: '今日', today: '今日',
yesterday: '昨日', yesterday: '昨日',
last7Days: '迗 7 天', dayBefore: '天',
last30Days: '迗 30 天', last7Days: '近7天',
last30Days: '近30天',
thisWeek: '本周', thisWeek: '本周',
lastWeek: '上周', lastWeek: '上周',
thisMonth: '本月', thisMonth: '本月',
@@ -316,7 +490,53 @@ export default {
time: '时间', time: '时间',
date: '日期', date: '日期',
tokenQuantity: 'Token数量', 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 // Accounts page
@@ -1285,6 +1505,52 @@ export default {
loadStatsFailed: 'Failed to load API keys stats' 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 // User Login
login: { login: {
title: 'User Sign In', title: 'User Sign In',
@@ -1300,7 +1566,50 @@ export default {
// Validation and error messages // Validation and error messages
requiredFields: 'Please enter both username and password', requiredFields: 'Please enter both username and password',
loginSuccess: 'Login successful!', 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 // User Management
@@ -1469,6 +1778,123 @@ export default {
// Success message // Success message
roleUpdated: '用户角色已更新为 {role}' 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: '删除', removeIcon: '删除',
iconFormats: '支持 .ico, .png, .jpg, .svg 格式,最大 350KB', iconFormats: '支持 .ico, .png, .jpg, .svg 格式,最大 350KB',
iconPreview: '图标预览', iconPreview: '图标预览',
validation: {
iconTooLarge: '图标文件大小不能超过 350KB',
iconTypeNotSupported: '不支持的文件类型,请选择 .ico, .png, .jpg 或 .svg 文件'
},
adminEntry: '管理入口', adminEntry: '管理入口',
adminEntryDescription: '登录按钮显示', adminEntryDescription: '登录按钮显示',

View File

@@ -1,4 +1,47 @@
export default { 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: { common: {
save: '保存', save: '保存',
cancel: '取消', cancel: '取消',
@@ -10,7 +53,89 @@ export default {
update: '更新', update: '更新',
search: '搜尋', search: '搜尋',
reset: '重置', 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: { language: {
zh: '簡體中文', zh: '簡體中文',
@@ -204,7 +329,15 @@ export default {
securityNoticeSingle: '您的 API Key 僅用於查詢自己的統計資料,不會被儲存或用於其他用途', securityNoticeSingle: '您的 API Key 僅用於查詢自己的統計資料,不會被儲存或用於其他用途',
securityNoticeMulti: securityNoticeMulti:
'您的 API Keys 僅用於查詢統計資料,不會被儲存。彙整模式下部分個體化資訊將不顯示。', '您的 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 // Login page
@@ -215,7 +348,9 @@ export default {
password: '密碼', password: '密碼',
passwordPlaceholder: '請輸入密碼', passwordPlaceholder: '請輸入密碼',
loginButton: '登錄', loginButton: '登錄',
loggingIn: '登錄中...' loggingIn: '登錄中...',
loginFailed: '登入失敗',
loginFailedCheck: '登入失敗,請檢查使用者名稱與密碼'
}, },
// Dashboard page // Dashboard page
@@ -255,7 +390,13 @@ export default {
requestsPerMinute: '每分钟請求數', requestsPerMinute: '每分钟請求數',
tokensPerMinute: '每分钟Token數', tokensPerMinute: '每分钟Token數',
historicalData: '歷史資料', historicalData: '歷史資料',
minutes: '分', minutes: '分',
// Uptime display formats
uptimeFormat: {
daysHours: '{days}天 {hours}小時',
hoursMinutes: '{hours}小時 {minutes}分鐘',
minutes: '{minutes}分鐘'
},
// Charts section // Charts section
modelDistributionAndTrend: '模型使用分佈與Token使用趋勢', modelDistributionAndTrend: '模型使用分佈與Token使用趋勢',
@@ -263,6 +404,7 @@ export default {
// Date filter presets // Date filter presets
today: '今日', today: '今日',
yesterday: '昨日', yesterday: '昨日',
dayBefore: '前天',
last7Days: '近7天', last7Days: '近7天',
last30Days: '近30天', last30Days: '近30天',
thisWeek: '本週', thisWeek: '本週',
@@ -280,6 +422,13 @@ export default {
dateSeparator: '至', dateSeparator: '至',
maxHours24: '最多24小時', maxHours24: '最多24小時',
// Errors
errors: {
rangeTooLongHour: '小時粒度下日期範圍不能超過24小時',
rangeTooLongDay: '日期範圍不能超過 31 天',
rangeTooLongHourSwitched: '小時粒度超過24小時已切換為近24小時'
},
// Auto refresh controls // Auto refresh controls
autoRefresh: '自動刷新', autoRefresh: '自動刷新',
refresh: '刷新', refresh: '刷新',
@@ -316,7 +465,48 @@ export default {
time: '時間', time: '時間',
date: '日期', date: '日期',
tokenQuantity: 'Token數量', 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 // Accounts page
@@ -1246,6 +1436,52 @@ export default {
loadStatsFailed: 'Failed to load API keys stats' 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 // User Login
login: { login: {
title: 'User Sign In', title: 'User Sign In',
@@ -1261,7 +1497,50 @@ export default {
// Validation and error messages // Validation and error messages
requiredFields: 'Please enter both username and password', requiredFields: 'Please enter both username and password',
loginSuccess: 'Login successful!', 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 // User Management
@@ -1432,6 +1711,75 @@ export default {
roleUpdated: '使用者角色已更新為 {role}' 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 // Usage Detail Modal
usageDetailModal: { usageDetailModal: {
title: '使用統計詳情', title: '使用統計詳情',
@@ -1470,6 +1818,54 @@ export default {
// Progress indicators // Progress indicators
usedPercentage: '已使用 {percentage}%' 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: '刪除', removeIcon: '刪除',
iconFormats: '支援 .ico, .png, .jpg, .svg 格式,最大 350KB', iconFormats: '支援 .ico, .png, .jpg, .svg 格式,最大 350KB',
iconPreview: '圖標預覽', iconPreview: '圖標預覽',
validation: {
iconTooLarge: '圖標文件大小不能超過 350KB',
iconTypeNotSupported: '不支援的文件類型,請選擇 .ico, .png, .jpg 或 .svg 文件'
},
adminEntry: '管理入口', adminEntry: '管理入口',
adminEntryDescription: '登入按鈕顯示', adminEntryDescription: '登入按鈕顯示',

View File

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