Merge branch 'main' into test

This commit is contained in:
yangjianbo
2026-02-03 22:48:04 +08:00
235 changed files with 25155 additions and 7955 deletions

View File

@@ -69,7 +69,9 @@ export default {
port: 'Port',
password: 'Password (optional)',
database: 'Database',
passwordPlaceholder: 'Password'
passwordPlaceholder: 'Password',
enableTls: 'Enable TLS',
enableTlsHint: 'Use TLS when connecting to Redis (public CA certs)'
},
admin: {
title: 'Admin Account',
@@ -146,7 +148,10 @@ export default {
balance: 'Balance',
available: 'Available',
copiedToClipboard: 'Copied to clipboard',
copied: 'Copied',
copyFailed: 'Failed to copy',
verifying: 'Verifying...',
processing: 'Processing...',
contactSupport: 'Contact Support',
add: 'Add',
invalidEmail: 'Please enter a valid email address',
@@ -182,6 +187,7 @@ export default {
// Navigation
nav: {
dashboard: 'Dashboard',
announcements: 'Announcements',
apiKeys: 'API Keys',
usage: 'Usage',
redeem: 'Redeem',
@@ -203,6 +209,7 @@ export default {
logout: 'Logout',
github: 'GitHub',
mySubscriptions: 'My Subscriptions',
buySubscription: 'Purchase Subscription',
docs: 'Docs'
},
@@ -258,6 +265,13 @@ export default {
promoCodeAlreadyUsed: 'You have already used this promo code',
promoCodeValidating: 'Promo code is being validated, please wait',
promoCodeInvalidCannotRegister: 'Invalid promo code. Please check and try again or clear the promo code field',
invitationCodeLabel: 'Invitation Code',
invitationCodePlaceholder: 'Enter invitation code',
invitationCodeRequired: 'Invitation code is required',
invitationCodeValid: 'Invitation code is valid',
invitationCodeInvalid: 'Invalid or used invitation code',
invitationCodeValidating: 'Validating invitation code...',
invitationCodeInvalidCannotRegister: 'Invalid invitation code. Please check and try again',
linuxdo: {
signIn: 'Continue with Linux.do',
orContinue: 'or continue with email',
@@ -271,7 +285,36 @@ export default {
code: 'Code',
state: 'State',
fullUrl: 'Full URL'
}
},
// Forgot password
forgotPassword: 'Forgot password?',
forgotPasswordTitle: 'Reset Your Password',
forgotPasswordHint: 'Enter your email address and we will send you a link to reset your password.',
sendResetLink: 'Send Reset Link',
sendingResetLink: 'Sending...',
sendResetLinkFailed: 'Failed to send reset link. Please try again.',
resetEmailSent: 'Reset Link Sent',
resetEmailSentHint: 'If an account exists with this email, you will receive a password reset link shortly. Please check your inbox and spam folder.',
backToLogin: 'Back to Login',
rememberedPassword: 'Remembered your password?',
// Reset password
resetPasswordTitle: 'Set New Password',
resetPasswordHint: 'Enter your new password below.',
newPassword: 'New Password',
newPasswordPlaceholder: 'Enter your new password',
confirmPassword: 'Confirm Password',
confirmPasswordPlaceholder: 'Confirm your new password',
confirmPasswordRequired: 'Please confirm your password',
passwordsDoNotMatch: 'Passwords do not match',
resetPassword: 'Reset Password',
resettingPassword: 'Resetting...',
resetPasswordFailed: 'Failed to reset password. Please try again.',
passwordResetSuccess: 'Password Reset Successful',
passwordResetSuccessHint: 'Your password has been reset. You can now sign in with your new password.',
invalidResetLink: 'Invalid Reset Link',
invalidResetLinkHint: 'This password reset link is invalid or has expired. Please request a new one.',
requestNewResetLink: 'Request New Reset Link',
invalidOrExpiredToken: 'The password reset link is invalid or has expired. Please request a new one.'
},
// Dashboard
@@ -459,6 +502,7 @@ export default {
exporting: 'Exporting...',
preparingExport: 'Preparing export...',
model: 'Model',
reasoningEffort: 'Reasoning Effort',
type: 'Type',
tokens: 'Tokens',
cost: 'Cost',
@@ -554,7 +598,46 @@ export default {
passwordsNotMatch: 'New passwords do not match',
passwordTooShort: 'Password must be at least 8 characters long',
passwordChangeSuccess: 'Password changed successfully',
passwordChangeFailed: 'Failed to change password'
passwordChangeFailed: 'Failed to change password',
// TOTP 2FA
totp: {
title: 'Two-Factor Authentication (2FA)',
description: 'Enhance account security with Google Authenticator or similar apps',
enabled: 'Enabled',
enabledAt: 'Enabled at',
notEnabled: 'Not Enabled',
notEnabledHint: 'Enable two-factor authentication to enhance account security',
enable: 'Enable',
disable: 'Disable',
featureDisabled: 'Feature Unavailable',
featureDisabledHint: 'Two-factor authentication has not been enabled by the administrator',
setupTitle: 'Set Up Two-Factor Authentication',
setupStep1: 'Scan the QR code below with your authenticator app',
setupStep2: 'Enter the 6-digit code from your app',
manualEntry: "Can't scan? Enter the key manually:",
enterCode: 'Enter 6-digit code',
verify: 'Verify',
setupFailed: 'Failed to get setup information',
verifyFailed: 'Invalid code, please try again',
enableSuccess: 'Two-factor authentication enabled',
disableTitle: 'Disable Two-Factor Authentication',
disableWarning: 'After disabling, you will no longer need a verification code to log in. This may reduce your account security.',
enterPassword: 'Enter your current password to confirm',
confirmDisable: 'Confirm Disable',
disableSuccess: 'Two-factor authentication disabled',
disableFailed: 'Failed to disable, please check your password',
loginTitle: 'Two-Factor Authentication',
loginHint: 'Enter the 6-digit code from your authenticator app',
loginFailed: 'Verification failed, please try again',
// New translations for email verification
verifyEmailFirst: 'Please verify your email first',
verifyPasswordFirst: 'Please verify your identity first',
emailCode: 'Email Verification Code',
enterEmailCode: 'Enter 6-digit code',
sendCode: 'Send Code',
codeSent: 'Verification code sent to your email',
sendCodeFailed: 'Failed to send verification code'
}
},
// Empty States
@@ -760,6 +843,20 @@ export default {
failedToDeposit: 'Failed to deposit',
failedToWithdraw: 'Failed to withdraw',
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
// Balance History
balanceHistory: 'Recharge History',
balanceHistoryTip: 'Click to open recharge history',
balanceHistoryTitle: 'User Recharge & Concurrency History',
noBalanceHistory: 'No records found for this user',
allTypes: 'All Types',
typeBalance: 'Balance (Redeem)',
typeAdminBalance: 'Balance (Admin)',
typeConcurrency: 'Concurrency (Redeem)',
typeAdminConcurrency: 'Concurrency (Admin)',
typeSubscription: 'Subscription',
failedToLoadBalanceHistory: 'Failed to load balance history',
createdAt: 'Created',
totalRecharged: 'Total Recharged',
roles: {
admin: 'Admin',
user: 'User'
@@ -938,6 +1035,14 @@ export default {
fallbackHint: 'Non-Claude Code requests will use this group. Leave empty to reject directly.',
noFallback: 'No Fallback (Reject)'
},
copyAccounts: {
title: 'Copy Accounts from Groups',
tooltip: 'Select one or more groups of the same platform. After creation, all accounts from these groups will be automatically bound to the new group (deduplicated).',
tooltipEdit: 'Select one or more groups of the same platform. After saving, current group accounts will be replaced with accounts from these groups (deduplicated).',
selectPlaceholder: 'Select groups to copy accounts from...',
hint: 'Multiple groups can be selected, accounts will be deduplicated',
hintEdit: '⚠️ Warning: This will replace all existing account bindings'
},
modelRouting: {
title: 'Model Routing',
tooltip: 'Configure specific model requests to be routed to designated accounts. Supports wildcard matching, e.g., claude-opus-* matches all opus models.',
@@ -1110,6 +1215,7 @@ export default {
overloaded: 'Overloaded',
tempUnschedulable: 'Temp Unschedulable',
rateLimitedUntil: 'Rate limited until {time}',
scopeRateLimitedUntil: '{scope} rate limited until {time}',
overloadedUntil: 'Overloaded until {time}',
viewTempUnschedDetails: 'View temp unschedulable details'
},
@@ -1357,6 +1463,8 @@ export default {
accountUpdated: 'Account updated successfully',
failedToCreate: 'Failed to create account',
failedToUpdate: 'Failed to update account',
mixedChannelWarningTitle: 'Mixed Channel Warning',
mixedChannelWarning: 'Warning: Group "{groupName}" contains both {currentPlatform} and {otherPlatform} accounts. Mixing different channels may cause thinking block signature validation issues, which will fallback to non-thinking mode. Are you sure you want to continue?',
pleaseEnterAccountName: 'Please enter account name',
pleaseEnterApiKey: 'Please enter API Key',
apiKeyIsRequired: 'API Key is required',
@@ -1833,6 +1941,8 @@ export default {
balance: 'Balance',
concurrency: 'Concurrency',
subscription: 'Subscription',
invitation: 'Invitation',
invitationHint: 'Invitation codes are used to restrict user registration. They are automatically marked as used after use.',
unused: 'Unused',
used: 'Used',
columns: {
@@ -1879,6 +1989,7 @@ export default {
balance: 'Balance',
concurrency: 'Concurrency',
subscription: 'Subscription',
invitation: 'Invitation',
// Admin adjustment types (created when admin modifies user balance/concurrency)
admin_balance: 'Balance (Admin)',
admin_concurrency: 'Concurrency (Admin)'
@@ -1896,6 +2007,73 @@ export default {
}
},
// Announcements
announcements: {
title: 'Announcements',
description: 'Create announcements and target by conditions',
createAnnouncement: 'Create Announcement',
editAnnouncement: 'Edit Announcement',
deleteAnnouncement: 'Delete Announcement',
searchAnnouncements: 'Search announcements...',
status: 'Status',
allStatus: 'All Status',
columns: {
title: 'Title',
status: 'Status',
targeting: 'Targeting',
timeRange: 'Schedule',
createdAt: 'Created At',
actions: 'Actions'
},
statusLabels: {
draft: 'Draft',
active: 'Active',
archived: 'Archived'
},
form: {
title: 'Title',
content: 'Content (Markdown supported)',
status: 'Status',
startsAt: 'Starts At',
endsAt: 'Ends At',
startsAtHint: 'Leave empty to start immediately',
endsAtHint: 'Leave empty to never expire',
targetingMode: 'Targeting',
targetingAll: 'All users',
targetingCustom: 'Custom rules',
addOrGroup: 'Add OR group',
addAndCondition: 'Add AND condition',
conditionType: 'Condition type',
conditionSubscription: 'Subscription',
conditionBalance: 'Balance',
operator: 'Operator',
balanceValue: 'Balance threshold',
selectPackages: 'Select packages'
},
operators: {
gt: '>',
gte: '≥',
lt: '<',
lte: '≤',
eq: '='
},
targetingSummaryAll: 'All users',
targetingSummaryCustom: 'Custom ({groups} groups)',
timeImmediate: 'Immediate',
timeNever: 'Never',
readStatus: 'Read Status',
eligible: 'Eligible',
readAt: 'Read at',
unread: 'Unread',
searchUsers: 'Search users...',
failedToLoad: 'Failed to load announcements',
failedToCreate: 'Failed to create announcement',
failedToUpdate: 'Failed to update announcement',
failedToDelete: 'Failed to delete announcement',
failedToLoadReadStatus: 'Failed to load read status',
deleteConfirm: 'Are you sure you want to delete this announcement? This action cannot be undone.'
},
// Promo Codes
promo: {
title: 'Promo Code Management',
@@ -2667,6 +2845,8 @@ export default {
ignoreContextCanceledHint: 'When enabled, client disconnect (context canceled) errors will not be written to the error log.',
ignoreNoAvailableAccounts: 'Ignore no available accounts errors',
ignoreNoAvailableAccountsHint: 'When enabled, "No available accounts" errors will not be written to the error log (not recommended; usually a config issue).',
ignoreInvalidApiKeyErrors: 'Ignore invalid API key errors',
ignoreInvalidApiKeyErrorsHint: 'When enabled, invalid or missing API key errors (INVALID_API_KEY, API_KEY_REQUIRED) will not be written to the error log.',
autoRefresh: 'Auto Refresh',
enableAutoRefresh: 'Enable auto refresh',
enableAutoRefreshHint: 'Automatically refresh dashboard data at a fixed interval.',
@@ -2694,6 +2874,7 @@ export default {
empty: 'No data',
queued: 'Queue {count}',
rateLimited: 'Rate-limited {count}',
scopeRateLimitedTooltip: '{scope} rate-limited ({count} accounts)',
errorAccounts: 'Errors {count}',
loadFailed: 'Failed to load concurrency data'
},
@@ -2760,7 +2941,15 @@ export default {
emailVerification: 'Email Verification',
emailVerificationHint: 'Require email verification for new registrations',
promoCode: 'Promo Code',
promoCodeHint: 'Allow users to use promo codes during registration'
promoCodeHint: 'Allow users to use promo codes during registration',
invitationCode: 'Invitation Code Registration',
invitationCodeHint: 'When enabled, users must enter a valid invitation code to register',
passwordReset: 'Password Reset',
passwordResetHint: 'Allow users to reset their password via email',
totp: 'Two-Factor Authentication (2FA)',
totpHint: 'Allow users to use authenticator apps like Google Authenticator',
totpKeyNotConfigured:
'Please configure TOTP_ENCRYPTION_KEY in environment variables first. Generate a key with: openssl rand -hex 32'
},
turnstile: {
title: 'Cloudflare Turnstile',
@@ -2834,6 +3023,17 @@ export default {
hideCcsImportButton: 'Hide CCS Import Button',
hideCcsImportButtonHint: 'When enabled, the "Import to CCS" button will be hidden on the API Keys page'
},
purchase: {
title: 'Purchase Page',
description: 'Show a "Purchase Subscription" entry in the sidebar and open the configured URL in an iframe',
enabled: 'Show Purchase Entry',
enabledHint: 'Only shown in standard mode (not simple mode)',
url: 'Purchase URL',
urlPlaceholder: 'https://example.com/purchase',
urlHint: 'Must be an absolute http(s) URL',
iframeWarning:
'⚠️ iframe note: Some websites block embedding via X-Frame-Options or CSP (frame-ancestors). If the page is blank, provide an "Open in new tab" alternative.'
},
smtp: {
title: 'SMTP Settings',
description: 'Configure email sending for verification codes',
@@ -2979,6 +3179,42 @@ export default {
retry: 'Retry'
},
// Purchase Subscription Page
purchase: {
title: 'Purchase Subscription',
description: 'Purchase a subscription via the embedded page',
openInNewTab: 'Open in new tab',
notEnabledTitle: 'Feature not enabled',
notEnabledDesc: 'The administrator has not enabled the purchase page. Please contact admin.',
notConfiguredTitle: 'Purchase URL not configured',
notConfiguredDesc:
'The administrator enabled the entry but has not configured a purchase URL. Please contact admin.'
},
// Announcements Page
announcements: {
title: 'Announcements',
description: 'View system announcements',
unreadOnly: 'Show unread only',
markRead: 'Mark as read',
markAllRead: 'Mark all as read',
viewAll: 'View all announcements',
markedAsRead: 'Marked as read',
allMarkedAsRead: 'All announcements marked as read',
newCount: '{count} new announcement | {count} new announcements',
readAt: 'Read at',
read: 'Read',
unread: 'Unread',
startsAt: 'Starts at',
endsAt: 'Ends at',
empty: 'No announcements',
emptyUnread: 'No unread announcements',
total: 'announcements',
emptyDescription: 'There are no system announcements at this time',
readStatus: 'You have read this announcement',
markReadHint: 'Click "Mark as read" to mark this announcement'
},
// User Subscriptions Page
userSubscriptions: {
title: 'My Subscriptions',

View File

@@ -66,7 +66,9 @@ export default {
port: '端口',
password: '密码(可选)',
database: '数据库',
passwordPlaceholder: '密码'
passwordPlaceholder: '密码',
enableTls: '启用 TLS',
enableTlsHint: '连接 Redis 时使用 TLS公共 CA 证书)'
},
admin: {
title: '管理员账户',
@@ -143,7 +145,10 @@ export default {
balance: '余额',
available: '可用',
copiedToClipboard: '已复制到剪贴板',
copied: '已复制',
copyFailed: '复制失败',
verifying: '验证中...',
processing: '处理中...',
contactSupport: '联系客服',
add: '添加',
invalidEmail: '请输入有效的邮箱地址',
@@ -179,6 +184,7 @@ export default {
// Navigation
nav: {
dashboard: '仪表盘',
announcements: '公告',
apiKeys: 'API 密钥',
usage: '使用记录',
redeem: '兑换',
@@ -200,6 +206,7 @@ export default {
logout: '退出登录',
github: 'GitHub',
mySubscriptions: '我的订阅',
buySubscription: '购买订阅',
docs: '文档'
},
@@ -255,6 +262,13 @@ export default {
promoCodeAlreadyUsed: '您已使用过此优惠码',
promoCodeValidating: '优惠码正在验证中,请稍候',
promoCodeInvalidCannotRegister: '优惠码无效,请检查后重试或清空优惠码',
invitationCodeLabel: '邀请码',
invitationCodePlaceholder: '请输入邀请码',
invitationCodeRequired: '请输入邀请码',
invitationCodeValid: '邀请码有效',
invitationCodeInvalid: '邀请码无效或已被使用',
invitationCodeValidating: '正在验证邀请码...',
invitationCodeInvalidCannotRegister: '邀请码无效,请检查后重试',
linuxdo: {
signIn: '使用 Linux.do 登录',
orContinue: '或使用邮箱密码继续',
@@ -268,7 +282,36 @@ export default {
code: '授权码',
state: '状态',
fullUrl: '完整URL'
}
},
// 忘记密码
forgotPassword: '忘记密码?',
forgotPasswordTitle: '重置密码',
forgotPasswordHint: '输入您的邮箱地址,我们将向您发送密码重置链接。',
sendResetLink: '发送重置链接',
sendingResetLink: '发送中...',
sendResetLinkFailed: '发送重置链接失败,请重试。',
resetEmailSent: '重置链接已发送',
resetEmailSentHint: '如果该邮箱已注册,您将很快收到密码重置链接。请检查您的收件箱和垃圾邮件文件夹。',
backToLogin: '返回登录',
rememberedPassword: '想起密码了?',
// 重置密码
resetPasswordTitle: '设置新密码',
resetPasswordHint: '请在下方输入您的新密码。',
newPassword: '新密码',
newPasswordPlaceholder: '输入新密码',
confirmPassword: '确认密码',
confirmPasswordPlaceholder: '再次输入新密码',
confirmPasswordRequired: '请确认您的密码',
passwordsDoNotMatch: '两次输入的密码不一致',
resetPassword: '重置密码',
resettingPassword: '重置中...',
resetPasswordFailed: '重置密码失败,请重试。',
passwordResetSuccess: '密码重置成功',
passwordResetSuccessHint: '您的密码已重置。现在可以使用新密码登录。',
invalidResetLink: '无效的重置链接',
invalidResetLinkHint: '此密码重置链接无效或已过期。请重新请求一个新链接。',
requestNewResetLink: '请求新的重置链接',
invalidOrExpiredToken: '密码重置链接无效或已过期。请重新请求一个新链接。'
},
// Dashboard
@@ -455,6 +498,7 @@ export default {
exporting: '导出中...',
preparingExport: '正在准备导出...',
model: '模型',
reasoningEffort: '推理强度',
type: '类型',
tokens: 'Token',
cost: '费用',
@@ -550,7 +594,46 @@ export default {
passwordsNotMatch: '两次输入的密码不一致',
passwordTooShort: '密码至少需要 8 个字符',
passwordChangeSuccess: '密码修改成功',
passwordChangeFailed: '密码修改失败'
passwordChangeFailed: '密码修改失败',
// TOTP 2FA
totp: {
title: '双因素认证 (2FA)',
description: '使用 Google Authenticator 等应用增强账户安全',
enabled: '已启用',
enabledAt: '启用时间',
notEnabled: '未启用',
notEnabledHint: '启用双因素认证可以增强账户安全性',
enable: '启用',
disable: '禁用',
featureDisabled: '功能未开放',
featureDisabledHint: '管理员尚未开放双因素认证功能',
setupTitle: '设置双因素认证',
setupStep1: '使用认证器应用扫描下方二维码',
setupStep2: '输入应用显示的 6 位验证码',
manualEntry: '无法扫码?手动输入密钥:',
enterCode: '输入 6 位验证码',
verify: '验证',
setupFailed: '获取设置信息失败',
verifyFailed: '验证码错误,请重试',
enableSuccess: '双因素认证已启用',
disableTitle: '禁用双因素认证',
disableWarning: '禁用后,登录时将不再需要验证码。这可能会降低您的账户安全性。',
enterPassword: '请输入当前密码确认',
confirmDisable: '确认禁用',
disableSuccess: '双因素认证已禁用',
disableFailed: '禁用失败,请检查密码是否正确',
loginTitle: '双因素认证',
loginHint: '请输入您认证器应用显示的 6 位验证码',
loginFailed: '验证失败,请重试',
// New translations for email verification
verifyEmailFirst: '请先验证您的邮箱',
verifyPasswordFirst: '请先验证您的身份',
emailCode: '邮箱验证码',
enterEmailCode: '请输入 6 位验证码',
sendCode: '发送验证码',
codeSent: '验证码已发送到您的邮箱',
sendCodeFailed: '发送验证码失败'
}
},
// Empty States
@@ -811,6 +894,20 @@ export default {
failedToDeposit: '充值失败',
failedToWithdraw: '退款失败',
useDepositWithdrawButtons: '请使用充值/退款按钮调整余额',
// 余额变动记录
balanceHistory: '充值记录',
balanceHistoryTip: '点击查看充值记录',
balanceHistoryTitle: '用户充值和并发变动记录',
noBalanceHistory: '暂无变动记录',
allTypes: '全部类型',
typeBalance: '余额(兑换码)',
typeAdminBalance: '余额(管理员调整)',
typeConcurrency: '并发(兑换码)',
typeAdminConcurrency: '并发(管理员调整)',
typeSubscription: '订阅',
failedToLoadBalanceHistory: '加载余额记录失败',
createdAt: '创建时间',
totalRecharged: '总充值',
// Settings Dropdowns
filterSettings: '筛选设置',
columnSettings: '列设置',
@@ -1013,6 +1110,14 @@ export default {
fallbackHint: '非 Claude Code 请求将使用此分组,留空则直接拒绝',
noFallback: '不降级(直接拒绝)'
},
copyAccounts: {
title: '从分组复制账号',
tooltip: '选择一个或多个相同平台的分组,创建后会自动将这些分组的所有账号绑定到新分组(去重)。',
tooltipEdit: '选择一个或多个相同平台的分组,保存后当前分组的账号会被替换为这些分组的账号(去重)。',
selectPlaceholder: '选择分组以复制其账号...',
hint: '可选多个分组,账号会自动去重',
hintEdit: '⚠️ 注意:这会替换当前分组的所有账号绑定'
},
modelRouting: {
title: '模型路由配置',
tooltip: '配置特定模型请求优先路由到指定账号。支持通配符匹配,如 claude-opus-* 匹配所有 opus 模型。',
@@ -1232,6 +1337,7 @@ export default {
overloaded: '过载中',
tempUnschedulable: '临时不可调度',
rateLimitedUntil: '限流中,重置时间:{time}',
scopeRateLimitedUntil: '{scope} 限流中,重置时间:{time}',
overloadedUntil: '负载过重,重置时间:{time}',
viewTempUnschedDetails: '查看临时不可调度详情'
},
@@ -1489,6 +1595,8 @@ export default {
accountUpdated: '账号更新成功',
failedToCreate: '创建账号失败',
failedToUpdate: '更新账号失败',
mixedChannelWarningTitle: '混合渠道警告',
mixedChannelWarning: '警告:分组 "{groupName}" 中同时包含 {currentPlatform} 和 {otherPlatform} 账号。混合使用不同渠道可能导致 thinking block 签名验证问题,会自动回退到非 thinking 模式。确定要继续吗?',
pleaseEnterAccountName: '请输入账号名称',
pleaseEnterApiKey: '请输入 API Key',
apiKeyIsRequired: 'API Key 是必需的',
@@ -1956,6 +2064,7 @@ export default {
balance: '余额',
concurrency: '并发数',
subscription: '订阅',
invitation: '邀请码',
// 管理员在用户管理页面调整余额/并发时产生的记录
admin_balance: '余额(管理员)',
admin_concurrency: '并发数(管理员)'
@@ -1964,6 +2073,8 @@ export default {
balance: '余额',
concurrency: '并发数',
subscription: '订阅',
invitation: '邀请码',
invitationHint: '邀请码用于限制用户注册,使用后自动标记为已使用。',
allTypes: '全部类型',
allStatus: '全部状态',
unused: '未使用',
@@ -2043,6 +2154,73 @@ export default {
failedToDelete: '删除兑换码失败'
},
// Announcements
announcements: {
title: '公告管理',
description: '创建公告并按条件投放',
createAnnouncement: '创建公告',
editAnnouncement: '编辑公告',
deleteAnnouncement: '删除公告',
searchAnnouncements: '搜索公告...',
status: '状态',
allStatus: '全部状态',
columns: {
title: '标题',
status: '状态',
targeting: '展示条件',
timeRange: '有效期',
createdAt: '创建时间',
actions: '操作'
},
statusLabels: {
draft: '草稿',
active: '展示中',
archived: '已归档'
},
form: {
title: '标题',
content: '内容(支持 Markdown',
status: '状态',
startsAt: '开始时间',
endsAt: '结束时间',
startsAtHint: '留空表示立即生效',
endsAtHint: '留空表示永久生效',
targetingMode: '展示条件',
targetingAll: '所有用户',
targetingCustom: '按条件',
addOrGroup: '添加 OR 条件组',
addAndCondition: '添加 AND 条件',
conditionType: '条件类型',
conditionSubscription: '订阅套餐',
conditionBalance: '余额',
operator: '运算符',
balanceValue: '余额阈值',
selectPackages: '选择套餐'
},
operators: {
gt: '>',
gte: '≥',
lt: '<',
lte: '≤',
eq: '='
},
targetingSummaryAll: '全部用户',
targetingSummaryCustom: '自定义({groups} 组)',
timeImmediate: '立即',
timeNever: '永久',
readStatus: '已读情况',
eligible: '符合条件',
readAt: '已读时间',
unread: '未读',
searchUsers: '搜索用户...',
failedToLoad: '加载公告失败',
failedToCreate: '创建公告失败',
failedToUpdate: '更新公告失败',
failedToDelete: '删除公告失败',
failedToLoadReadStatus: '加载已读情况失败',
deleteConfirm: '确定要删除该公告吗?此操作无法撤销。'
},
// Promo Codes
promo: {
title: '优惠码管理',
@@ -2819,7 +2997,9 @@ export default {
ignoreContextCanceled: '忽略客户端断连错误',
ignoreContextCanceledHint: '启用后客户端主动断开连接context canceled的错误将不会写入错误日志。',
ignoreNoAvailableAccounts: '忽略无可用账号错误',
ignoreNoAvailableAccountsHint: '启用后,No available accounts 错误将不会写入错误日志(不推荐,这通常是配置问题)。',
ignoreNoAvailableAccountsHint: '启用后,"No available accounts" 错误将不会写入错误日志(不推荐,这通常是配置问题)。',
ignoreInvalidApiKeyErrors: '忽略无效 API Key 错误',
ignoreInvalidApiKeyErrorsHint: '启用后,无效或缺失 API Key 的错误INVALID_API_KEY、API_KEY_REQUIRED将不会写入错误日志。',
autoRefresh: '自动刷新',
enableAutoRefresh: '启用自动刷新',
enableAutoRefreshHint: '自动刷新仪表板数据,启用后会定期拉取最新数据。',
@@ -2847,6 +3027,7 @@ export default {
empty: '暂无数据',
queued: '队列 {count}',
rateLimited: '限流 {count}',
scopeRateLimitedTooltip: '{scope} 限流中 ({count} 个账号)',
errorAccounts: '异常 {count}',
loadFailed: '加载并发数据失败'
},
@@ -2913,7 +3094,15 @@ export default {
emailVerification: '邮箱验证',
emailVerificationHint: '新用户注册时需要验证邮箱',
promoCode: '优惠码',
promoCodeHint: '允许用户在注册时使用优惠码'
promoCodeHint: '允许用户在注册时使用优惠码',
invitationCode: '邀请码注册',
invitationCodeHint: '开启后,用户注册时需要填写有效的邀请码',
passwordReset: '忘记密码',
passwordResetHint: '允许用户通过邮箱重置密码',
totp: '双因素认证 (2FA)',
totpHint: '允许用户使用 Google Authenticator 等应用进行二次验证',
totpKeyNotConfigured:
'请先在环境变量中配置 TOTP_ENCRYPTION_KEY。使用命令 openssl rand -hex 32 生成密钥。'
},
turnstile: {
title: 'Cloudflare Turnstile',
@@ -2985,6 +3174,17 @@ export default {
hideCcsImportButton: '隐藏 CCS 导入按钮',
hideCcsImportButtonHint: '启用后将在 API Keys 页面隐藏"导入 CCS"按钮'
},
purchase: {
title: '购买订阅页面',
description: '在侧边栏展示“购买订阅”入口,并在页面内通过 iframe 打开指定链接',
enabled: '显示购买订阅入口',
enabledHint: '仅在标准模式(非简单模式)下展示',
url: '购买页面 URL',
urlPlaceholder: 'https://example.com/purchase',
urlHint: '必须是完整的 http(s) 链接',
iframeWarning:
'⚠️ iframe 提示:部分网站会通过 X-Frame-Options 或 CSPframe-ancestors禁止被 iframe 嵌入,出现空白时可引导用户使用“新窗口打开”。'
},
smtp: {
title: 'SMTP 设置',
description: '配置用于发送验证码的邮件服务',
@@ -3129,6 +3329,41 @@ export default {
retry: '重试'
},
// Purchase Subscription Page
purchase: {
title: '购买订阅',
description: '通过内嵌页面完成订阅购买',
openInNewTab: '新窗口打开',
notEnabledTitle: '该功能未开启',
notEnabledDesc: '管理员暂未开启购买订阅入口,请联系管理员。',
notConfiguredTitle: '购买链接未配置',
notConfiguredDesc: '管理员已开启入口,但尚未配置购买订阅链接,请联系管理员。'
},
// Announcements Page
announcements: {
title: '公告',
description: '查看系统公告',
unreadOnly: '仅显示未读',
markRead: '标记已读',
markAllRead: '全部已读',
viewAll: '查看全部公告',
markedAsRead: '已标记为已读',
allMarkedAsRead: '所有公告已标记为已读',
newCount: '有 {count} 条新公告',
readAt: '已读时间',
read: '已读',
unread: '未读',
startsAt: '开始时间',
endsAt: '结束时间',
empty: '暂无公告',
emptyUnread: '暂无未读公告',
total: '条公告',
emptyDescription: '暂时没有任何系统公告',
readStatus: '您已阅读此公告',
markReadHint: '点击"已读"标记此公告'
},
// User Subscriptions Page
userSubscriptions: {
title: '我的订阅',