Files
testing-tool/utils/chromeI18n.ts
T
雨霖铃 aa23a263fe refactor: 迁移 i18n 系统从 react-i18next 到 chrome.i18n
- 移除 react-i18next、i18next 及相关依赖
- 删除旧的 i18n/ 目录和 useLazyTranslation 工具
- 新增 utils/chromeI18n.ts 类型安全 wrapper(useI18n Hook + getMessage)
- 生成 public/_locales/{zh,en}/messages.json(298 个翻译 key)
- 批量更新 39+ 组件文件的导入和翻译调用
- 转换翻译键格式:namespace:key → namespace_key
- 修复 ErrorBoundary/PageErrorBoundary 从 withTranslation HOC 改为直接调用 getMessage
- 更新 vitest.setup.ts mock 加载实际翻译文本
- 修复 11 个测试文件的断言以匹配中文翻译
- TypeScript、ESLint、547 项测试全部通过

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 19:21:54 +08:00

103 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* chrome.i18n 类型安全 wrapper
* 提供与 react-i18next 兼容的接口
*/
/**
* 获取翻译文本
* @param msgId 翻译 key(如 'timestamp_pageTitle'
* @param substitutions 占位符替换值(可选)
* @returns 翻译后的文本
*/
export function getMessage(msgId: string, substitutions?: string[]): string {
try {
return chrome.i18n.getMessage(msgId, substitutions);
} catch (error) {
console.warn(`[chrome.i18n] 无法获取翻译: ${msgId}`, error);
return msgId; // 回退到 key 本身
}
}
/**
* 获取当前浏览器语言
* @returns 语言代码(如 'zh', 'en'
*/
export function getLanguage(): string {
const lang = chrome.i18n.getUILanguage();
return lang.toLowerCase().startsWith('zh') ? 'zh' : 'en';
}
/**
* 支持的语言列表
*/
export const SUPPORTED_LANGUAGES: readonly ('zh' | 'en')[] = ['zh', 'en'] as const;
/**
* 规范化语言代码
*/
export function normalizeLanguage(lng: string): 'zh' | 'en' {
return lng.toLowerCase().startsWith('zh') ? 'zh' : 'en';
}
/**
* react-i18next 兼容的 Hook
* 返回 t 函数和相关信息
*/
export function useI18n(namespace?: string | string[]) {
const namespaces = Array.isArray(namespace) ? namespace : namespace ? [namespace] : [];
const t = (key: string, options?: Record<string, unknown>): string => {
let msgId = key;
// 处理 namespace:key 格式(兼容原 i18next 用法)
if (key.includes(':')) {
msgId = key.replace(':', '_').replace(/\./g, '_');
}
// 先尝试直接查找 key
let message = getMessage(msgId);
// 如果直接查找未命中,尝试命名空间前缀(使用转换后的 msgId)
if (message === msgId && namespaces.length > 0) {
for (const ns of namespaces) {
const candidate = `${ns}_${msgId}`;
const result = getMessage(candidate);
if (result !== candidate) {
message = result;
break;
}
}
}
// 处理插值
if (options) {
for (const [placeholder, value] of Object.entries(options)) {
message = message.replace(`{{${placeholder}}}`, String(value));
}
}
return message;
};
return {
t,
i18n: {
language: getLanguage(),
changeLanguage: (_lng?: string) => {
// chrome.i18n 无法动态切换语言,需要刷新页面
console.warn('[chrome.i18n] 无法动态切换语言,需要刷新页面');
return Promise.resolve();
},
},
isLoaded: true,
};
}
/**
* 预加载命名空间(无操作,兼容 useLazyTranslation
*/
export async function preloadNamespaces(_namespaces: string[]): Promise<void> {
// chrome.i18n 是同步的,无需预加载
return Promise.resolve();
}