feat: 全站多语言支持 (i18n),lang=en 显示英文,其余默认中文

新增 src/lib/locale.ts 作为统一多语言入口,覆盖前台支付链路、
管理后台、API/服务层错误文案,共 35 个文件。URL 参数 lang 全链路透传,
包括 Stripe return_url、页面跳转、layout html lang 属性等。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erio
2026-03-09 18:33:57 +08:00
parent 5cebe85079
commit 2492031e13
35 changed files with 1997 additions and 579 deletions

View File

@@ -1,13 +1,34 @@
import type { Locale } from '@/lib/locale';
import { formatStatus, formatCreatedAt, getStatusBadgeClass, getPaymentDisplayInfo, type MyOrder } from '@/lib/pay-utils';
interface OrderTableProps {
isDark: boolean;
locale: Locale;
loading: boolean;
error: string;
orders: MyOrder[];
}
export default function OrderTable({ isDark, loading, error, orders }: OrderTableProps) {
export default function OrderTable({ isDark, locale, loading, error, orders }: OrderTableProps) {
const text =
locale === 'en'
? {
empty: 'No matching orders found',
orderId: 'Order ID',
amount: 'Amount',
payment: 'Payment Method',
status: 'Status',
createdAt: 'Created At',
}
: {
empty: '暂无符合条件的订单记录',
orderId: '订单号',
amount: '金额',
payment: '支付方式',
status: '状态',
createdAt: '创建时间',
};
return (
<div
className={[
@@ -40,7 +61,7 @@ export default function OrderTable({ isDark, loading, error, orders }: OrderTabl
isDark ? 'border-slate-600 text-slate-400' : 'border-slate-300 text-slate-500',
].join(' ')}
>
{text.empty}
</div>
) : (
<>
@@ -50,11 +71,11 @@ export default function OrderTable({ isDark, loading, error, orders }: OrderTabl
isDark ? 'text-slate-300' : 'text-slate-600',
].join(' ')}
>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span>{text.orderId}</span>
<span>{text.amount}</span>
<span>{text.payment}</span>
<span>{text.status}</span>
<span>{text.createdAt}</span>
</div>
<div className="space-y-2 md:space-y-0">
{orders.map((order) => (
@@ -67,19 +88,17 @@ export default function OrderTable({ isDark, loading, error, orders }: OrderTabl
>
<div className="font-medium">#{order.id.slice(0, 12)}</div>
<div className="font-semibold">¥{order.amount.toFixed(2)}</div>
<div>
{getPaymentDisplayInfo(order.paymentType).channel}
</div>
<div>{getPaymentDisplayInfo(order.paymentType, locale).channel}</div>
<div>
<span
className={['rounded-full px-2 py-0.5 text-xs', getStatusBadgeClass(order.status, isDark)].join(
' ',
)}
>
{formatStatus(order.status)}
{formatStatus(order.status, locale)}
</span>
</div>
<div className={isDark ? 'text-slate-300' : 'text-slate-600'}>{formatCreatedAt(order.createdAt)}</div>
<div className={isDark ? 'text-slate-300' : 'text-slate-600'}>{formatCreatedAt(order.createdAt, locale)}</div>
</div>
))}
</div>