React 生态中事实上的 i18n 标准。基于 i18next 内核,提供 useTranslation Hook、Trans 组件和命名空间隔离。插值、复数和上下文翻译能力完整,SSR 支持通过 next-i18next 桥接。我们推荐在 React 新项目中优先评估它。
Vue 官方维护的国际化库。支持 Composition API 的 useI18n()、模板中的 $t() 语法和 Legacy Options API 兼容模式。v10 最大的改进是编译时优化——在构建阶段就把翻译函数内联到模板中,减少了运行时的字符串查找开销。
Angular 的 i18n 方案跟其他框架的思路不太一样:它是在编译时把翻译文本烘焙进模板的,运行时零成本。代价是每个语种要单独构建一次。如果你的 Angular 项目已经做了语种按需加载的架构设计,这套方案非常高效。
FormatJS 的卖点是 ICU MessageFormat 的完整实现——复数、性别、选择器和富文本插值都比 i18next 的语法更标准。缺点是 Bundle 体积偏大(~25KB gzip),而且学习曲线比 react-i18next 陡一点。如果你的产品有复杂的复数规则需求(如阿拉伯语的六种复数形式),FormatJS 是更安全的选择。
一个 8KB(gzip)的 TypeScript SDK,封装了翻译 API 调用、缓存策略、离线回退和翻译文本的运行时注入。设计理念是"框架无关"——核心逻辑独立,再通过框架适配器接入 React / Vue / Angular。
一个 React Context Provider 包裹整个应用,子组件通过 Hook 获取翻译函数。支持 Suspense——如果翻译还没加载完,组件会显示 fallback,不会闪现原文。
Vue 插件在 app.use() 时注入全局 $t() 方法和响应式语种状态。切换语言后所有使用 $t() 的模板自动重新渲染——因为 locale ref 是被 Vue 的响应式系统追踪的。
Angular 集成通过 InjectionToken 提供 TRANSLATE_LOADER,按需加载语种文件。配合一个 TranslatePipe,在模板中写 {{ 'key' | translate }},干净利落。支持 OnPush 变更检测策略——翻译数据变化时只触发受影响组件的检查。
写代码时用 t('key') 标记新增文本
→构建时通过 Babel/SWC 插件提取新 key
→增量推送至翻译引擎,仅翻译新增条目
→翻译结果自动生成 Pull Request 供审校
→审校通过后合并,随应用一起构建上线
在 workflow 中添加一个 traneasy/translate-action Step,每次 Push 到 main 分支时自动扫描新增的翻译 key、调用 API 获取译文、并以 PR 形式提交到仓库。如果翻译覆盖率低于阈值(比如 < 95%),CI 会标黄但不会阻止构建。
在构建阶段通过插件提取源码中的 t('key') 调用,生成待翻译的 JSON 清单,调用 API,将译文写入构建产物。Vite 版本利用了 esbuild 的 transform hook,比 Webpack 插件快 3-5 倍。
在 CI 中配置一个覆盖率阈值——比如要求每个目标语言至少覆盖 98% 的翻译 key。低于阈值的 PR 自动打上"needs-translation"标签,通知国际化负责人。这个机制对 50+ 人的前端团队尤其重要,靠人工盯翻译完整性是不现实的。
最简单的 key-value 结构。适合小项目或 proto 阶段快速上手。缺点是不支持命名空间,key 多了之后文件会很长——500 行以上建议切换到嵌套 JSON。
按模块/页面组织 key 层级:`{"nav": {"home": "首页", "about": "关于"}}`。大部分 i18n 库原生支持,Git diff 友好——改一个模块不会影响其他模块的 diff 记录。
可读性比 JSON 好,支持注释——这一点在大型项目中价值巨大。翻译人员可以在 YAML 里直接写备注,解释某个短语的使用场景。适合翻译团队而非纯开发者维护的场景。
翻译行业标准格式,CAT 工具(Trados、memoQ)原生支持。如果你的翻译工作流涉及外部翻译公司,XLIFF 是最少摩擦的选择。Angular i18n 的默认输出格式就是 XLIFF。
处理复数、性别、选择器等复杂语法。一个经典例子:`{count, plural, =0 {无消息} =1 {1 条消息} other {# 条消息}}`。在需要精细控制语法形态的语种中(阿拉伯语、俄语、波兰语),ICU 格式几乎是必须的。
刚开始做 i18n 的团队经常直接把中文作为 key 传进 t('你好世界')。这在初期看起来省事,但一旦做了 200 个 key,你会发现自己完全不知道哪些已经翻译过了、哪些只是一段还没处理的硬编码文本。用结构化 key(如 'greeting.hello_world')是更可持续的做法。
单个翻译 JSON 文件超过 100KB 之后,不仅加载慢,Git merge conflict 概率也急剧上升。按路由或模块拆分——`locales/zh/dashboard.json`、`locales/zh/settings.json`——然后在运行时按需加载。Webpack 的 dynamic import 和 Vue-Router 的 lazy loading 天然支持这种做法。
翻译人员看到的只有 "Submit" 这个单词,但它是一个按钮标签还是一个页面标题?中文翻译可能是"提交"也可能是"呈递"。在翻译文件中加一个 `_context` 字段或使用 YAML 注释描述使用场景——这点额外的工作量会大幅减少翻译返工。
写一个简单的 Node 脚本,对比各语言 JSON 文件的 key 列表。任何一个语种缺少的 key 都会以非零退出码报告——把它挂到 pre-commit hook 或 CI 上。我们在三个项目上因为缺乏这个检查,上线后才发现日文版少了 40 个翻译 key,用户体验可想而知。
把 /products 翻译成 /produits(法语)或 /productos(西语)听起来很酷,但实际上 SEO 收益微乎其微,维护成本却大了很多。Google 早已不靠 URL 关键词来判断页面语种——hreflang 标签才是正确的信号。保持 URL 路径在英语或原语言不变,可以避免大量的路由配置和 301 重定向问题。
用 Git 管理翻译文件不是为了炫技——是因为翻译错误和代码错误一样需要 bisect。当一个用户报告"这个按钮的日文翻译让人困惑",你应该能快速找到这条翻译是谁在什么时间提交的、有没有经过审校。把翻译文件纳入 Code Review 流程不是苛求,是必要的质量防线。