易翻译前端集成 Logo 易翻译前端集成

把翻译写进构建流水线

翻译不是上线前临时跑一次的脚本。把它变成 CI/CD 的一个环节、变成组件树中的一个 Provider、变成 Webpack 的一个 loader——这是前端团队做国际化的正确方式。

i18n 框架怎么选,看这里

react-i18next
v15.x / React 18+
周下载 2M+ Bundle 18KB gzip ICU 格式

React 生态中事实上的 i18n 标准。基于 i18next 内核,提供 useTranslation Hook、Trans 组件和命名空间隔离。插值、复数和上下文翻译能力完整,SSR 支持通过 next-i18next 桥接。我们推荐在 React 新项目中优先评估它。

vue-i18n
v10.x / Vue 3
周下载 1.2M+ Bundle 12KB gzip SFC 内联

Vue 官方维护的国际化库。支持 Composition API 的 useI18n()、模板中的 $t() 语法和 Legacy Options API 兼容模式。v10 最大的改进是编译时优化——在构建阶段就把翻译函数内联到模板中,减少了运行时的字符串查找开销。

Angular i18n
Angular 18+
编译器内置 AOT 编译 XLIFF 原生

Angular 的 i18n 方案跟其他框架的思路不太一样:它是在编译时把翻译文本烘焙进模板的,运行时零成本。代价是每个语种要单独构建一次。如果你的 Angular 项目已经做了语种按需加载的架构设计,这套方案非常高效。

FormatJS / react-intl
v6.x
ICU MessageFormat 日期/数字原生 Polyfill 齐全

FormatJS 的卖点是 ICU MessageFormat 的完整实现——复数、性别、选择器和富文本插值都比 i18next 的语法更标准。缺点是 Bundle 体积偏大(~25KB gzip),而且学习曲线比 react-i18next 陡一点。如果你的产品有复杂的复数规则需求(如阿拉伯语的六种复数形式),FormatJS 是更安全的选择。

SDK 怎么用

@traneasy/web-sdk

一个 8KB(gzip)的 TypeScript SDK,封装了翻译 API 调用、缓存策略、离线回退和翻译文本的运行时注入。设计理念是"框架无关"——核心逻辑独立,再通过框架适配器接入 React / Vue / Angular。

  • Tree-shakable:只打包你实际使用的模块
  • 内置 LRU 缓存,容量可在初始化时配置
  • 离线模式:预下载语种包,断网时自动切换
  • TypeScript 类型推断:翻译 key 的自动补全
  • 批量翻译接口:一次请求最多 128 条文本
// 安装 npm install @traneasy/web-sdk // 初始化 import { createTraneasy } from '@traneasy/web-sdk'; const traneasy = createTraneasy({ apiKey: process.env.TRANEASY_KEY, sourceLang: 'zh', targetLang: 'en', cache: { strategy: 'lru', maxSize: 2000 }, offline: { enabled: true }, }); // 单条翻译 const result = await traneasy.translate('你好世界'); console.log(result); // "Hello world" // 批量翻译 const batch = await traneasy.translateBatch([ '提交订单', '取消订单', '查看物流' ]);

三大框架,三种接法

React Vue Angular
⚛ React

useTraneasy Hook

一个 React Context Provider 包裹整个应用,子组件通过 Hook 获取翻译函数。支持 Suspense——如果翻译还没加载完,组件会显示 fallback,不会闪现原文。

// App.tsx import { TraneasyProvider } from '@traneasy/react'; function App() { return ( <TraneasyProvider apiKey={key} targetLang="ja"> <Dashboard /> </TraneasyProvider> ); } // Dashboard.tsx import { useTraneasy } from '@traneasy/react'; const { t, loading } = useTraneasy(); return <h1>{t('欢迎回来')}</h1>;
💚 Vue

Composition API + Plugin

Vue 插件在 app.use() 时注入全局 $t() 方法和响应式语种状态。切换语言后所有使用 $t() 的模板自动重新渲染——因为 locale ref 是被 Vue 的响应式系统追踪的。

// main.ts import { createTraneasyPlugin } from '@traneasy/vue'; app.use(createTraneasyPlugin({ apiKey, sourceLang: 'zh' })); // Component.vue <script setup> import { useI18n } from '@traneasy/vue'; const { t, setLocale } = useI18n(); </script> <template> <p>{{ t('订单已提交') }}</p> </template>
🔴 Angular

DI Token + Pipe

Angular 集成通过 InjectionToken 提供 TRANSLATE_LOADER,按需加载语种文件。配合一个 TranslatePipe,在模板中写 {{ 'key' | translate }},干净利落。支持 OnPush 变更检测策略——翻译数据变化时只触发受影响组件的检查。

// app.config.ts import { provideTraneasy } from '@traneasy/angular'; export const appConfig = { providers: [provideTraneasy({ apiKey, defaultLang: 'zh' })] }; // template <h2>{{ 'product.description' | translate }}</h2> <button (click)="switchLang('fr')"> {{ 'nav.switch_lang' | translate }} </button>

CI/CD 翻译流水线

📝

开发者提交

写代码时用 t('key') 标记新增文本

📤

CI 提取

构建时通过 Babel/SWC 插件提取新 key

🌐

API 翻译

增量推送至翻译引擎,仅翻译新增条目

PR 生成

翻译结果自动生成 Pull Request 供审校

🚀

合并部署

审校通过后合并,随应用一起构建上线

GitHub Actions 集成

在 workflow 中添加一个 traneasy/translate-action Step,每次 Push 到 main 分支时自动扫描新增的翻译 key、调用 API 获取译文、并以 PR 形式提交到仓库。如果翻译覆盖率低于阈值(比如 < 95%),CI 会标黄但不会阻止构建。

Webpack / Vite 插件

在构建阶段通过插件提取源码中的 t('key') 调用,生成待翻译的 JSON 清单,调用 API,将译文写入构建产物。Vite 版本利用了 esbuild 的 transform hook,比 Webpack 插件快 3-5 倍。

翻译覆盖率门禁

在 CI 中配置一个覆盖率阈值——比如要求每个目标语言至少覆盖 98% 的翻译 key。低于阈值的 PR 自动打上"needs-translation"标签,通知国际化负责人。这个机制对 50+ 人的前端团队尤其重要,靠人工盯翻译完整性是不现实的。

翻译文件管理

JSON (Flat)

最简单的 key-value 结构。适合小项目或 proto 阶段快速上手。缺点是不支持命名空间,key 多了之后文件会很长——500 行以上建议切换到嵌套 JSON。

JSON (Nested)

按模块/页面组织 key 层级:`{"nav": {"home": "首页", "about": "关于"}}`。大部分 i18n 库原生支持,Git diff 友好——改一个模块不会影响其他模块的 diff 记录。

YAML

可读性比 JSON 好,支持注释——这一点在大型项目中价值巨大。翻译人员可以在 YAML 里直接写备注,解释某个短语的使用场景。适合翻译团队而非纯开发者维护的场景。

XLIFF 2.0

翻译行业标准格式,CAT 工具(Trados、memoQ)原生支持。如果你的翻译工作流涉及外部翻译公司,XLIFF 是最少摩擦的选择。Angular i18n 的默认输出格式就是 XLIFF。

ICU MessageFormat

处理复数、性别、选择器等复杂语法。一个经典例子:`{count, plural, =0 {无消息} =1 {1 条消息} other {# 条消息}}`。在需要精细控制语法形态的语种中(阿拉伯语、俄语、波兰语),ICU 格式几乎是必须的。

我们踩过的坑,你绕过去

别用中文做翻译 Key

刚开始做 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,用户体验可想而知。

不要试图翻译 URL 路径

把 /products 翻译成 /produits(法语)或 /productos(西语)听起来很酷,但实际上 SEO 收益微乎其微,维护成本却大了很多。Google 早已不靠 URL 关键词来判断页面语种——hreflang 标签才是正确的信号。保持 URL 路径在英语或原语言不变,可以避免大量的路由配置和 301 重定向问题。

翻译 BUG 要像代码 BUG 一样追踪

用 Git 管理翻译文件不是为了炫技——是因为翻译错误和代码错误一样需要 bisect。当一个用户报告"这个按钮的日文翻译让人困惑",你应该能快速找到这条翻译是谁在什么时间提交的、有没有经过审校。把翻译文件纳入 Code Review 流程不是苛求,是必要的质量防线。

常见问题

SDK 的包体积多大,会不会拖慢首屏?

核心 SDK 约 8KB gzip,加上 React 适配器约 2KB,总共 10KB 左右。我们花了大量精力做 tree-shaking——如果你只用了 translate() 函数而没用到离线模式,离线模块的代码会被完全剔除。首屏加载方面,翻译调用是异步的,不会阻塞页面渲染。你可以在组件挂载后再触发翻译请求,用户在翻译内容加载出来之前看到的是原文(或一个 skeleton),不会白屏。

我用的不是 React/Vue/Angular,SDK 还能用吗?

能。@traneasy/web-sdk 的核心是完全框架无关的——它就是一个带缓存的 HTTP 客户端包装。你在 Svelte、Solid.js、甚至 vanilla JS 项目里都可以直接用。框架适配器只是加了一层响应式绑定和组件封装。如果你用的框架比较小众,我们建议直接 import 核心 SDK,然后自己封装一个 20 行的 helper 函数,工作量很小。

翻译 API 的延迟会影响页面交互体验吗?

单条翻译的 API 延迟中位数是 80ms(P50)和 200ms(P99),在用户感知的阈值之下。更大的延迟来源是首次加载需要翻译的内容较多——比如一个页面有 30 个未缓存的翻译 key。这种情况下我们推荐在构建时预翻译(通过 CI 流水线),而不是在浏览器端实时调用。对于确实需要运行时翻译的场景,SDK 的批量翻译接口可以把 30 个 key 合并为一次 HTTP 请求,总体延迟控制在 300ms 以内。

怎么处理翻译文件的 Git 冲突?

这是翻译文件管理中最常见的痛点。我们推荐三个做法:一,按模块拆分文件,减少多人同时改同一个文件的概率;二,使用 YAML 替代 JSON——YAML 的逐行 diff 比 JSON 的一行 diff 容易读得多;三,在 CI 中增加一个 merge 后的 key 完整性检查,确保合并后的文件没有丢失或重复的 key。如果冲突不可避免,Git 的三路合并对 YAML 的效果比对 JSON 好很多。

CI/CD 翻译流水线适合什么规模的项目?

10 个页面以下的小项目,手动管理翻译文件完全可行,不一定要上 CI 流水线。但一旦翻译 key 超过 200 个、支持的语种超过 3 种、或者有 3 个以上的开发者同时在写前端代码,人工维护翻译文件的出错率就会快速上升。我们的建议是:当翻译文件让你感到痛苦的那一天,就是该上 CI 流水线的那一天。这个时间点通常在项目启动后的 3-6 个月。

翻译质量在 UI 短文本上会不会特别差?

UI 短文本(按钮标签、表单占位符、错误提示)恰恰是机器翻译最容易出错的地方——因为没有上下文,一个 "Submit" 在表单场景中应该是"提交"而不是"呈递"。我们在 API 里专门增加了一个 `context` 参数,你可以传一个简短的描述(比如 "button: submit form"),引擎会据此选择正确的译法。大量测试表明,加上 context 后 UI 短文本的翻译准确率从 78% 提升到了 93%。

我想迁移现有的 i18n 方案到你们的 SDK,有什么需要注意的?

如果你们的翻译文件已经是标准 JSON/YAML 格式,迁移很简单——直接把文件内容作为"种子翻译"导入控制台,SDK 初始化时指定 `seedTranslations` 参数即可。最大的挑战在于 key 命名体系的不兼容:比如你之前的 key 是中文自然语言而我们的方案推荐结构化 key。可以在迁移时写一个脚本做 key 映射,但我们建议趁这个机会重整 key 命名体系——长痛不如短痛。