feat(frontend): 控制台 next-intl 中英双语

接入无 URL 前缀的 zh-CN/en,顶栏与外观设置可切换语言;选择写入 cookie 后刷新生效。
This commit is contained in:
ryan
2026-08-16 16:49:16 +08:00
parent 64fbaa7ef1
commit 5a8722ff07
219 changed files with 21437 additions and 5212 deletions
@@ -9,8 +9,15 @@ import type { FileImportFormValues, ManualImportFormValues } from './schemas';
export type StatusTone = 'success' | 'warning' | 'danger' | 'info';
export function getErrorMessage(error: unknown) {
return error instanceof Error ? error.message : '请求失败,请稍后重试。';
export type TranslateFn = (
key: string,
values?: Record<string, string | number>,
) => string;
export function getErrorMessage(error: unknown, fallback?: string) {
return error instanceof Error
? error.message
: (fallback ?? '请求失败,请稍后重试。');
}
export function getMatchTypeMeta(domain: string): {
@@ -22,7 +29,10 @@ export function getMatchTypeMeta(domain: string): {
: { label: '精确匹配', tone: 'info' };
}
export function getCertificateStatus(certificate: TlsCertificateItem): {
export function getCertificateStatus(
certificate: TlsCertificateItem,
t: TranslateFn,
): {
label: string;
tone: StatusTone;
} {
@@ -31,18 +41,18 @@ export function getCertificateStatus(certificate: TlsCertificateItem): {
const days = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
if (Number.isNaN(expiresAt)) {
return { label: '有效期未知', tone: 'warning' };
return { label: t('certStatus.unknownExpiry'), tone: 'warning' };
}
if (days < 0) {
return { label: '已过期', tone: 'danger' };
return { label: t('certStatus.expired'), tone: 'danger' };
}
if (days <= 30) {
return { label: `${days} 天内到期`, tone: 'warning' };
return { label: t('certStatus.expiresInDays', { days }), tone: 'warning' };
}
return { label: '有效', tone: 'success' };
return { label: t('certStatus.valid'), tone: 'success' };
}
export function buildCertificateLabel(certificate: TlsCertificateItem) {
@@ -66,9 +76,10 @@ export function toFilePayload(
values: FileImportFormValues,
certFile: File | null,
keyFile: File | null,
missingFilesMessage: string,
): TlsCertificateFileImportPayload {
if (!certFile || !keyFile) {
throw new Error('请选择证书文件和私钥文件。');
throw new Error(missingFilesMessage);
}
return {