feat(i18n): 完善多模块多语言适配与发版验证

扩展前后端多语言文案与共享词典。增加多模块 i18n 回归测试与 guard。收口外部 SQL 菜单和弹窗多语言文案。
This commit is contained in:
tianqijiuyun-latiao
2026-06-17 13:17:33 +08:00
parent 76b0163bd3
commit 9364c48ef0
135 changed files with 8401 additions and 1325 deletions

View File

@@ -40,6 +40,7 @@ type SecureConfigBootstrapArgs = {
storage?: StorageLike;
replaceConnections: (connections: SavedConnection[]) => void;
replaceGlobalProxy: (proxy: GlobalProxyConfig) => void;
t?: SecureConfigBootstrapTranslator;
};
type SecureConfigBootstrapResult = {
@@ -57,8 +58,16 @@ type StartSecurityUpdateResult = {
type MergeSecurityUpdateStatusOptions = {
previousStatus?: Partial<SecurityUpdateStatus> | null;
t?: SecureConfigBootstrapTranslator;
};
type SecureConfigBootstrapTranslator = (key: string) => string;
const secureConfigBootstrapText = (
key: string,
t?: SecureConfigBootstrapTranslator,
): string => (t ? t(key) : key);
const defaultSummary = () => ({
total: 0,
updated: 0,
@@ -75,7 +84,10 @@ const hasMeaningfulSummary = (summary: SecurityUpdateSummary): boolean => (
|| summary.failed > 0
);
const buildLegacyPendingDetails = (rawPayload: string | null): {
const buildLegacyPendingDetails = (
rawPayload: string | null,
t?: SecureConfigBootstrapTranslator,
): {
hasLegacyItems: boolean;
summary: SecurityUpdateSummary;
issues: SecurityUpdateIssue[];
@@ -90,19 +102,19 @@ const buildLegacyPendingDetails = (rawPayload: string | null): {
status: 'pending',
reasonCode: 'migration_required',
action: 'open_connection',
message: '该连接仍保存在当前应用的本地配置中,完成安全更新后会迁入新的安全存储。',
message: secureConfigBootstrapText('security_update.bootstrap.legacy.connection.message', t),
}));
if (legacy.globalProxy) {
issues.push({
id: 'legacy-global-proxy-default',
scope: 'global_proxy',
title: '全局代理',
title: secureConfigBootstrapText('security_update.bootstrap.legacy.global_proxy.title', t),
severity: 'medium',
status: 'pending',
reasonCode: 'migration_required',
action: 'open_proxy_settings',
message: '全局代理仍保存在当前应用的本地配置中,完成安全更新后会迁入新的安全存储。',
message: secureConfigBootstrapText('security_update.bootstrap.legacy.global_proxy.message', t),
});
}
@@ -197,7 +209,7 @@ export const mergeSecurityUpdateStatusWithLegacySource = (
const hasActiveMigrationRound = String(base.migrationId || '').trim() !== '';
const baseNonLegacyIssues = base.issues.filter((issue) => !isLocalLegacyIssue(issue));
const legacy = buildLegacyPendingDetails(rawPayload);
const legacy = buildLegacyPendingDetails(rawPayload, options?.t);
const legacySummary = deriveLegacySummary(base, legacy.issues.length, options?.previousStatus);
if (!legacySummary.hasContribution) {
@@ -326,7 +338,7 @@ export async function finalizeSecurityUpdateStatus(
): Promise<SecurityUpdateStatus> {
const storage = resolveStorage(args.storage);
const rawPayload = storage?.getItem(LEGACY_PERSIST_KEY) ?? null;
const status = mergeSecurityUpdateStatusWithLegacySource(rawStatus, rawPayload);
const status = mergeSecurityUpdateStatusWithLegacySource(rawStatus, rawPayload, { t: args.t });
if (status.overallStatus === 'completed') {
await refreshVisibleConfigFromBackend(args.backend, args.replaceConnections, args.replaceGlobalProxy, true);
@@ -346,7 +358,7 @@ export async function bootstrapSecureConfig(args: SecureConfigBootstrapArgs): Pr
const backendStatus = typeof args.backend?.GetSecurityUpdateStatus === 'function'
? await args.backend.GetSecurityUpdateStatus()
: undefined;
const status = mergeSecurityUpdateStatusWithLegacySource(backendStatus, rawPayload);
const status = mergeSecurityUpdateStatusWithLegacySource(backendStatus, rawPayload, { t: args.t });
if (!hasLegacySensitiveItems) {
await refreshVisibleConfigFromBackend(args.backend, args.replaceConnections, args.replaceGlobalProxy, true);
@@ -374,7 +386,7 @@ export async function startSecurityUpdateFromBootstrap(args: SecureConfigBootstr
if (typeof args.backend?.StartSecurityUpdate !== 'function') {
return {
status: null,
error: new Error('安全更新能力不可用'),
error: new Error(secureConfigBootstrapText('security_update.error.capability_unavailable', args.t)),
};
}
@@ -387,7 +399,7 @@ export async function startSecurityUpdateFromBootstrap(args: SecureConfigBootstr
writeBackup: true,
},
});
const status = mergeSecurityUpdateStatusWithLegacySource(rawStatus, rawPayload);
const status = mergeSecurityUpdateStatusWithLegacySource(rawStatus, rawPayload, { t: args.t });
if (status.overallStatus === 'completed') {
await refreshVisibleConfigFromBackend(args.backend, args.replaceConnections, args.replaceGlobalProxy, true);