mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 18:07:47 +08:00
refactor(frontend): modularize i18n architecture
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { getI18nLocale } from '@/i18n'
|
||||
|
||||
const COMPACT_NUMBER_UNITS = [
|
||||
{ value: 1_000_000_000_000, suffix: 'T' },
|
||||
{ value: 1_000_000_000, suffix: 'B' },
|
||||
@@ -145,14 +147,14 @@ export function formatNumber(num: number | undefined | null): string {
|
||||
if (num === undefined || num === null) {
|
||||
return '0'
|
||||
}
|
||||
return num.toLocaleString('zh-CN')
|
||||
return num.toLocaleString(getI18nLocale())
|
||||
}
|
||||
|
||||
// Date formatting
|
||||
export function formatDate(dateString: string | undefined | null): string {
|
||||
if (!dateString) return '未知'
|
||||
if (!dateString) return getI18nLocale() === 'en-US' ? 'Unknown' : '未知'
|
||||
|
||||
return new Date(dateString).toLocaleDateString('zh-CN', {
|
||||
return new Date(dateString).toLocaleDateString(getI18nLocale(), {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
@@ -177,6 +179,15 @@ export function formatModelPrice(price: number | undefined | null): string {
|
||||
|
||||
// Billing type formatting
|
||||
export function formatBillingType(type: string | undefined | null): string {
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishTypeMap: Record<string, string> = {
|
||||
'pay_as_you_go': 'Pay as you go',
|
||||
'monthly_quota': 'Monthly quota',
|
||||
'free_tier': 'Free tier',
|
||||
}
|
||||
return englishTypeMap[type || ''] || type || 'Pay as you go'
|
||||
}
|
||||
|
||||
const typeMap: Record<string, string> = {
|
||||
'pay_as_you_go': '按量付费',
|
||||
'monthly_quota': '月卡配额',
|
||||
@@ -198,13 +209,14 @@ export function formatUsageCount(count: number): string {
|
||||
|
||||
// Format remaining time from unix timestamp
|
||||
export function formatRemainingTime(expireAt: number | undefined, currentTime: number): string {
|
||||
if (!expireAt) return '未知'
|
||||
const isEnglish = getI18nLocale() === 'en-US'
|
||||
if (!expireAt) return isEnglish ? 'Unknown' : '未知'
|
||||
const remaining = expireAt - currentTime
|
||||
if (remaining <= 0) return '已过期'
|
||||
if (remaining <= 0) return isEnglish ? 'Expired' : '已过期'
|
||||
|
||||
const minutes = Math.floor(remaining / 60)
|
||||
const seconds = Math.floor(remaining % 60)
|
||||
return `${minutes}分${seconds}秒`
|
||||
return isEnglish ? `${minutes}m ${seconds}s` : `${minutes}分${seconds}秒`
|
||||
}
|
||||
|
||||
// Cache hit rate formatting
|
||||
@@ -215,14 +227,14 @@ export function formatHitRate(rate: number | undefined): string {
|
||||
|
||||
// Rate limit formatting (supports "inherit" semantics: null = inherit system default)
|
||||
export function formatRateLimitInheritable(rateLimit?: number | null): string {
|
||||
if (rateLimit == null) return '跟随系统'
|
||||
if (rateLimit === 0) return '不限速'
|
||||
if (rateLimit == null) return getI18nLocale() === 'en-US' ? 'Use system default' : '跟随系统'
|
||||
if (rateLimit === 0) return getI18nLocale() === 'en-US' ? 'No limit' : '不限速'
|
||||
return `${rateLimit}/min`
|
||||
}
|
||||
|
||||
// Rate limit formatting (simple: null/0 both mean unlimited)
|
||||
export function formatRateLimitSimple(rateLimit?: number | null): string {
|
||||
if (rateLimit == null || rateLimit === 0) return '不限速'
|
||||
if (rateLimit == null || rateLimit === 0) return getI18nLocale() === 'en-US' ? 'No limit' : '不限速'
|
||||
return `${rateLimit}/min`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { getI18nLocale } from '@/i18n'
|
||||
import { translateLegacyText } from '@/i18n/messages'
|
||||
|
||||
export type PasswordPolicyLevel = 'weak' | 'medium' | 'strong'
|
||||
export const PASSWORD_MAX_BYTES = 72
|
||||
|
||||
const textEncoder = new TextEncoder()
|
||||
|
||||
function tr(value: string): string {
|
||||
return translateLegacyText(value, getI18nLocale())
|
||||
}
|
||||
|
||||
function getPasswordByteLength(password: string): number {
|
||||
return textEncoder.encode(password).length
|
||||
}
|
||||
@@ -39,24 +46,24 @@ export function normalizePasswordPolicyLevel(value: unknown): PasswordPolicyLeve
|
||||
export function getPasswordPolicyHint(level: unknown): string {
|
||||
switch (normalizePasswordPolicyLevel(level)) {
|
||||
case 'medium':
|
||||
return '至少 8 个字符,且需包含字母和数字'
|
||||
return tr('至少 8 个字符,且需包含字母和数字')
|
||||
case 'strong':
|
||||
return '至少 8 个字符,且需包含大写字母、小写字母、数字和特殊字符'
|
||||
return tr('至少 8 个字符,且需包含大写字母、小写字母、数字和特殊字符')
|
||||
case 'weak':
|
||||
default:
|
||||
return '至少 6 个字符'
|
||||
return tr('至少 6 个字符')
|
||||
}
|
||||
}
|
||||
|
||||
export function getPasswordPolicyPlaceholder(level: unknown): string {
|
||||
switch (normalizePasswordPolicyLevel(level)) {
|
||||
case 'medium':
|
||||
return '至少 8 位,含字母和数字'
|
||||
return tr('至少 8 位,含字母和数字')
|
||||
case 'strong':
|
||||
return '至少 8 位,含大小写字母、数字和特殊字符'
|
||||
return tr('至少 8 位,含大小写字母、数字和特殊字符')
|
||||
case 'weak':
|
||||
default:
|
||||
return '至少 6 个字符'
|
||||
return tr('至少 6 个字符')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +71,7 @@ export function getPasswordPolicyPlaceholder(level: unknown): string {
|
||||
* 返回所有未满足的密码策略条件。
|
||||
* 空数组 = 密码合规。
|
||||
*/
|
||||
export function getPasswordPolicyErrors(password: string, level: unknown): string[] {
|
||||
function getPasswordPolicyErrorSources(password: string, level: unknown): string[] {
|
||||
if (!password) return []
|
||||
|
||||
const normalized = normalizePasswordPolicyLevel(level)
|
||||
@@ -96,15 +103,23 @@ export function getPasswordPolicyErrors(password: string, level: unknown): strin
|
||||
return errors
|
||||
}
|
||||
|
||||
export function getPasswordPolicyErrors(password: string, level: unknown): string[] {
|
||||
return getPasswordPolicyErrorSources(password, level).map(tr)
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容旧接口:返回单条错误字符串,空字符串表示通过。
|
||||
* 多条未满足条件时用顿号连接。
|
||||
*/
|
||||
export function validatePasswordByPolicy(password: string, level: unknown): string {
|
||||
const errors = getPasswordPolicyErrors(password, level)
|
||||
const rawErrors = getPasswordPolicyErrorSources(password, level)
|
||||
const errors = rawErrors.map(tr)
|
||||
if (errors.length === 0) return ''
|
||||
if (errors.length === 1 && errors[0].startsWith('长度不能超过')) {
|
||||
return `密码${ errors[0]}`
|
||||
if (rawErrors.length === 1 && rawErrors[0].startsWith('长度不能超过')) {
|
||||
return tr(`密码${rawErrors[0]}`)
|
||||
}
|
||||
return `密码需要:${ errors.join('、')}`
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
return `Password requires: ${errors.join(', ')}`
|
||||
}
|
||||
return `密码需要:${errors.join('、')}`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { formatCompactNumber } from '@/utils/format'
|
||||
import { getI18nLocale } from '@/i18n'
|
||||
|
||||
export function walletStatusLabel(status: string | null | undefined): string {
|
||||
const labels: Record<string, string> = {
|
||||
@@ -7,6 +8,14 @@ export function walletStatusLabel(status: string | null | undefined): string {
|
||||
closed: '已关闭',
|
||||
}
|
||||
if (!status) return '未知'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
active: 'Active',
|
||||
suspended: 'Frozen',
|
||||
closed: 'Closed',
|
||||
}
|
||||
return englishLabels[status] || status
|
||||
}
|
||||
return labels[status] || status
|
||||
}
|
||||
|
||||
@@ -34,10 +43,20 @@ export function walletTransactionCategoryLabel(category: string | null | undefin
|
||||
refund: '退款',
|
||||
}
|
||||
if (!category) return '未知'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
recharge: 'Top-up',
|
||||
gift: 'Grant',
|
||||
adjust: 'Adjustment',
|
||||
refund: 'Refund',
|
||||
}
|
||||
return englishLabels[category] || category
|
||||
}
|
||||
return labels[category] || category
|
||||
}
|
||||
|
||||
export function dailyUsageCategoryLabel(isToday = false): string {
|
||||
if (getI18nLocale() === 'en-US') return isToday ? 'Today usage' : 'Daily usage'
|
||||
return isToday ? '今日消费' : '每日消费'
|
||||
}
|
||||
|
||||
@@ -61,6 +80,21 @@ export function walletTransactionReasonLabel(reasonCode: string | null | undefin
|
||||
refund_revert: '退款回补',
|
||||
}
|
||||
if (!reasonCode) return '未知'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
topup_admin_manual: 'Manual top-up',
|
||||
topup_gateway: 'Payment top-up',
|
||||
topup_card_code: 'Card code top-up',
|
||||
gift_initial: 'Initial grant',
|
||||
gift_campaign: 'Campaign grant',
|
||||
gift_expire_reclaim: 'Grant reclaim',
|
||||
adjust_admin: 'Manual adjustment',
|
||||
adjust_system: 'System adjustment',
|
||||
refund_out: 'Refund deduction',
|
||||
refund_revert: 'Refund reversal',
|
||||
}
|
||||
return englishLabels[reasonCode] || reasonCode
|
||||
}
|
||||
return labels[reasonCode] || reasonCode
|
||||
}
|
||||
|
||||
@@ -82,6 +116,25 @@ export function paymentMethodLabel(method: string | null | undefined): string {
|
||||
offline: '线下转账',
|
||||
}
|
||||
if (!method) return '-'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
alipay: 'Alipay',
|
||||
wechat: 'WeChat Pay',
|
||||
wxpay: 'WeChat Pay',
|
||||
wechat_pay: 'WeChat Pay',
|
||||
epay: 'E-Pay',
|
||||
stripe: 'Stripe',
|
||||
card: 'Bank card / credit card',
|
||||
link: 'Stripe Link',
|
||||
admin_manual: 'Manual top-up',
|
||||
card_code: 'Top-up code',
|
||||
gift_code: 'Gift code',
|
||||
card_recharge: 'Card code top-up',
|
||||
bank_transfer: 'Bank transfer',
|
||||
offline: 'Offline transfer',
|
||||
}
|
||||
return englishLabels[method] || method
|
||||
}
|
||||
return labels[method] || method
|
||||
}
|
||||
|
||||
@@ -96,6 +149,18 @@ export function paymentStatusLabel(status: string | null | undefined): string {
|
||||
refunded: '已退款',
|
||||
}
|
||||
if (!status) return '未知'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
pending: 'Pending',
|
||||
paid: 'Paid',
|
||||
credited: 'Credited',
|
||||
failed: 'Failed',
|
||||
expired: 'Expired',
|
||||
refunding: 'Refunding',
|
||||
refunded: 'Refunded',
|
||||
}
|
||||
return englishLabels[status] || status
|
||||
}
|
||||
return labels[status] || status
|
||||
}
|
||||
|
||||
@@ -109,6 +174,17 @@ export function walletLinkTypeLabel(type: string | null | undefined): string {
|
||||
usage: '用量记录',
|
||||
}
|
||||
if (!type) return '-'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
payment_order: 'Top-up order',
|
||||
refund_request: 'Refund request',
|
||||
admin_action: 'Admin action',
|
||||
system_task: 'System task',
|
||||
campaign: 'Campaign batch',
|
||||
usage: 'Usage record',
|
||||
}
|
||||
return englishLabels[type] || 'Other'
|
||||
}
|
||||
return labels[type] || '其他'
|
||||
}
|
||||
|
||||
@@ -127,6 +203,13 @@ export function refundModeLabel(mode: string | null | undefined): string {
|
||||
offline_payout: '线下打款',
|
||||
}
|
||||
if (!mode) return '-'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
original_channel: 'Original channel',
|
||||
offline_payout: 'Offline payout',
|
||||
}
|
||||
return englishLabels[mode] || mode
|
||||
}
|
||||
return labels[mode] || mode
|
||||
}
|
||||
|
||||
@@ -140,6 +223,17 @@ export function refundStatusLabel(status: string | null | undefined): string {
|
||||
cancelled: '已取消',
|
||||
}
|
||||
if (!status) return '未知'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
pending_approval: 'Pending approval',
|
||||
approved: 'Approved',
|
||||
processing: 'Processing',
|
||||
succeeded: 'Succeeded',
|
||||
failed: 'Failed',
|
||||
cancelled: 'Cancelled',
|
||||
}
|
||||
return englishLabels[status] || status
|
||||
}
|
||||
return labels[status] || status
|
||||
}
|
||||
|
||||
@@ -160,6 +254,16 @@ export function callbackStatusLabel(status: string | null | undefined): string {
|
||||
error: '处理失败',
|
||||
}
|
||||
if (!status) return '未知'
|
||||
if (getI18nLocale() === 'en-US') {
|
||||
const englishLabels: Record<string, string> = {
|
||||
processed: 'Processed',
|
||||
duplicate: 'Duplicate callback',
|
||||
ignored: 'Ignored',
|
||||
invalid_signature: 'Invalid signature',
|
||||
error: 'Failed',
|
||||
}
|
||||
return englishLabels[status] || status
|
||||
}
|
||||
return labels[status] || status
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user