前端国际化自动化方案实践与原理
一、前端国际化的痛点与挑战
1.1 业务背景
随着互联网产品的全球化,前端国际化(i18n)已成为大型项目的标配。无论是SaaS平台、B端管理系统,还是C端应用,支持多语言切换、动态翻译、内容本地化,都是提升用户体验和市场竞争力的关键。
1.2 传统国际化的常见痛点
- 人工替换繁琐:早期做法通常是手动查找、替换代码中的中文文本,包裹国际化函数,极易遗漏且效率低。
- key维护混乱:国际化key往往需要人工命名、维护,容易重复、冲突或丢失。
- 语言包同步难:新增/修改文案后,语言包与代码难以同步,漏翻、错翻频发。
- 历史遗留代码改造成本高:老项目动辄成千上万行代码,手动国际化几乎不现实。
- 多语言协作低效:开发、测试、翻译、产品多角色协作时,缺乏自动化工具支撑,流程割裂。
二、react-translate-cli:一站式前端国际化自动化工具
2.1 项目定位
translate-cli 是一款专为 React/TypeScript 项目设计的国际化自动化命令行工具,支持批量扫描、自动替换、key生成、语言包同步、diff预览等全流程,极大提升国际化开发效率。
2.2 主要特性
- 支持递归扫描指定目录下所有
.js
、.jsx
、.ts
、.tsx
文件 - 自动识别并替换代码中的中文为国际化函数调用(如
getI18nText({ key, zh })
) - 自动生成唯一且无重复的 key,格式为
相对路径_4位随机码
+ Set去重 - 支持自定义国际化函数名、导入路径、输出语言包目录等
- 支持自定义翻译API、缓存等高级功能
- 支持命令行一键处理和自动化脚本集成
- 详细的单元测试,保证核心功能稳定
- diff 预览,安全可控
2.3 典型使用流程
2.3.1 初始化配置
bash
# 生成默认配置文件
t-cli init
2.3.2 生成国际化代码和语言包
bash
# 自动扫描并替换纯中文为国际化函数,生成语言包
t-cli gen
2.3.3 替换未带key的国际化调用
bash
# 自动为 getI18nText({ zh: 'xxx' }) 生成 key 并替换源码
t-cli replace
2.3.4 diff 预览与保存
bash
# 预览所有变更,确认无误后批量保存
t-cli preview
2.4 代码转换前后对比
转换前
js
const label = '你好,世界';
const title = $t('table.pagination.total_number', { values: { count: total } }); // 旧的国际化语法中,源码中只保留了table.pagination.total_number这种形式,难以理解其含义,
//旧项目中存在的国际化调用,徐改改写为新国际化调用规范
<FormattedTableHeaderMessage text="标题" /> // Antd Table 中的header组件,中文提取为国际化
<FormattedTableHeaderMessage id="table.title.status" /> // 旧的国际化语法升级为国际化调用规范,id属性将被移除,
FormattedTableHeaderMessage 为Antd Table CustomHeader组件,业务代码中针对语种长度做了特殊处理
转换后
js
import { getI18nText } from '@/i8n/intl';
const label = getI18nText({ key: 'src/pages/xxx_abcd', zh: '你好,世界' });
const title = getI18nText({ key: 'table.pagination.total_number', zh: '共{count}条数据', values: { count: total } }); //中文提取到zh中
// 新国际化调用规范
<FormattedTableHeaderMessage text={getI18nText({ key: 'src/pages/zzz_ijkl', zh: '标题' })} />
<FormattedTableHeaderMessage text={getI18nText({ key: 'table.title.status', zh: '状态' })} /> // 中文提取到zh中,id字段填充到key字段中
配置文件说明
translate-cli 通过根目录下的 translate-config.js
文件进行高度自定义配置。主要配置项如下:
js
module.exports = {
// 扫描目录配置
scanDirs: [
'./test/entry/**/*.{js,jsx,ts,tsx}', // 支持 glob pattern
],
// 翻译配置
translate: {
// 翻译API配置
translateApi: {
/**
* 自定义翻译函数
*
* @param {Object} payload - 翻译请求参数
* @param {Object} payload.texts - 待翻译的文本对象,key为文本标识,value为待翻译文本
* @param {string|string[]} payload.targetLang - 目标语言,可以是单个语言代码或语言代码数组
* @param {string} [payload.sourceLang='zh'] - 源语言,默认为中文
*
* @returns {Promise<Object>} 翻译结果对象
* @returns {Object.<string, Object>} 返回对象的key为文本标识,value为各语言的翻译结果
*
* @example
* // 单目标语言翻译
* const result = await customFn({
* texts: {
* 'welcome': '欢迎使用',
* 'hello': '你好'
* },
* targetLang: 'en',
* sourceLang: 'zh'
* });
* // 返回结果:
* // {
* // 'welcome': { en: 'Welcome to use' },
* // 'hello': { en: 'Hello' }
* // }
*
* @example
* // 多目标语言翻译
* const result = await customFn({
* texts: {
* 'welcome': '欢迎使用',
* 'hello': '你好'
* },
* targetLang: ['en', 'th'],
* sourceLang: 'zh'
* });
* // 返回结果:
* // {
* // 'welcome': {
* // en: 'Welcome to use',
* // th: 'ยินดีต้อนรับ'
* // },
* // 'hello': {
* // en: 'Hello',
* // th: 'สวัสดี'
* // }
* // }
*/
customFn: async function (payload) {
const axios = require('axios');
const { texts, targetLang, sourceLang } = payload;
// 构造批量翻译请求数据
const dataArr = Object.entries(texts).map(([key, text]) => ({
key,
text,
source: sourceLang || 'zh',
targetLang
}));
// 调用翻译服务
const res = await axios.post('https://test-fe.aiadfly.com/tool/ai/translate', { data: dataArr });
const resultArr = res.data.data;
// 处理翻译结果
const langs = Array.isArray(targetLang) ? targetLang : [targetLang];
const results = {};
for (const item of resultArr) {
if (item && item.text && item.results) {
const key = Object.keys(texts).find(k => texts[k] === item.text);
if (key) {
results[key] = {};
for (const lang of langs) {
results[key][lang] = item.results[lang] || '';
}
}
}
}
// 处理未翻译的文本
for (const key of Object.keys(texts)) {
if (!(key in results)) {
results[key] = {};
for (const lang of langs) {
results[key][lang] = texts[key];
}
}
}
return results;
}
}
},
// 输出配置
output: {
dir: './src/i18nData', // 语言包输出目录
langs: ['zh', 'en', 'th'], // 支持的语言列表
translateFn: 'getI18nText', // 翻译函数名
textKey: 'zh', // 默认文本的key名
libPath: '@/i8n/intl', // 导入路径配置
replaceOldFn: '$t' // 需要替换的旧函数名
},
// Babel 生成代码的配置
babel: {
semicolons: false, // 是否使用分号,默认为 false
quotes: 'single' // 字符串引号类型,可选值:'single' | 'double',默认为 'single'
}
}
主要配置项说明:
-
scanDirs:指定需要国际化的目录和文件类型,支持 glob pattern。
-
translate:
- cache :翻译缓存配置
enabled
:是否启用缓存file
:缓存文件路径expireTime
:缓存过期时间
- translateApi :翻译服务配置
customFn
:自定义翻译函数,支持批量翻译和多目标语言
- cache :翻译缓存配置
-
output:
dir
:语言包输出目录langs
:支持的语言列表translateFn
:翻译函数名textKey
:默认文本的key名libPath
:导入路径配置replaceOldFn
:需要替换的旧函数名
-
babel:代码生成配置
semicolons
:是否使用分号quotes
:字符串引号类型
主要命令与使用场景
gen 指令
- 作用:扫描源码中的纯中文字符串,并将其替换为 getI18nText({ key, zh }) 形式,同时生成国际化 key。
- 典型使用场景 :
- 新项目或老项目首次批量国际化改造。
- 需要将所有硬编码中文批量替换为国际化函数调用。
- 支持的转换类型 :
- 纯中文字符串(如 '你好')
- 流程 :
- 扫描指定目录下的所有代码文件。
- 仅提取和替换纯中文字符串。
- 生成国际化 key,并输出到资源文件。
- 示例 :
-
转换前:
jsconst label = '你好,世界';
-
转换后:
jsconst label = getI18nText({ key: 'src/pages/xxx_abcd', zh: '你好,世界' });
-
生成的资源文件(如 zh.json):
json{ "src/pages/xxx_abcd": "你好,世界" }
-
注意:gen 指令不会处理组件调用(如 FormattedTableHeaderMessage)和 $t 旧调用形式。
-
replace 指令
-
作用:自动为源码中未带 key 的国际化内容生成 key,并替换源码。
-
典型使用场景:
- 已经部分国际化的项目,需补全 key 或升级国际化调用规范。
- 需要将旧的 $t 调用、未带 key 的 getI18nText、组件属性等批量升级。
-
支持的转换类型:
- 纯中文字符串 → getI18nText({ key, zh })
- 旧的 $t('xxx') 调用 → getI18nText({ key, zh })
- getI18nText({ zh: 'xxx' }) → getI18nText({ key: 'xxx', zh: 'xxx' })
- → <FormattedTableHeaderMessage text={getI18nText({ key: 'xxx', zh: 'xxx' })} />
-
流程:
- 扫描源码,查找所有未国际化的中文、未带 key 的国际化函数/组件、旧的 $t 调用。
- 为每个文案生成唯一 key(规则见 help.js 的 generateKey)。
- 替换为带 key 的国际化调用或组件。
- 结果体现在 diff 中,便于代码 review。
-
示例:
-
纯中文字符串
-
转换前:
jsconst label = '你好,世界';
-
转换后:
jsconst label = getI18nText({ key: 'src/pages/xxx_abcd', zh: '你好,世界' });
-
-
$t 调用
-
转换前:
jsconst title = $t('table.pagination.total_number', { values: { count: total } });
-
转换后:
jsconst title = getI18nText({ key: 'table.pagination.total_number', zh: '共{count}条数据', values: { count: total } });
-
-
FormattedTableHeaderMessage 组件 (文本转换)
-
转换前:
js<FormattedTableHeaderMessage text="标题" />
-
转换后:
js<FormattedTableHeaderMessage text={getI18nText({ key: 'src/pages/zzz_ijkl', zh: '标题' })} />
-
-
FormattedTableHeaderMessage 组件 (id转换)
-
转换前:
js<FormattedTableHeaderMessage id="table.title.status" />
-
转换后:
js<FormattedTableHeaderMessage text={getI18nText({ key: 'table.title.status', zh: '状态' })} />
-
-
注:FormattedTableHeaderMessage 为 Antd Table CustomHeader 组件,业务代码中针对语种长度做了特殊处理
三、技术原理与实现细节
3.1 AST 解析与代码转换
translate-cli 基于 Babel 的 parser/traverse/generator 实现源码的自动化转换。
3.1.1 解析源码为 AST
- 使用
@babel/parser
支持 TypeScript/JSX - 递归遍历所有文件,parse 为 AST
3.1.2 AST 遍历与节点识别
- 识别所有字符串字面量(纯中文)
- 识别 getI18nText({ zh: ... })、 <math xmlns="http://www.w3.org/1998/Math/MathML"> t ( ′ k e y ′ ) 、 t('key')、 </math>t(′key′)、t('key', { values })
- 识别 FormattedTableHeaderMessage 组件的 id/text 属性
3.1.3 自动 key 生成与替换
- 对于纯中文,调用 generateKey 生成唯一 key
- 对于 getI18nText({ zh: ... }),如无 key 自动补全
- 对于 $t('key'),查找语言包自动补全 zh
- 对于 FormattedTableHeaderMessage,优先 id,否则 text
- 所有 key/zh 自动收集,便于生成语言包
3.1.4 代码生成与格式化
- 使用
@babel/generator
生成新代码 - 保证 import 语句自动插入且不重复
- 支持自定义引号、分号、缩进等格式
3.1.5 语言包同步
- 自动收集所有 key-zh 映射,生成/更新 zh.json、en.json 等
- 支持自定义翻译API,自动批量翻译
3.2 关键流程图
3.3 代码核心片段
3.3.1 自动 key 化 getI18nText({ zh: ... })
js
traverse(ast, {
CallExpression(path) {
if (t.isIdentifier(path.node.callee, { name: fnMain })) {
const arg = path.node.arguments[0];
if (t.isObjectExpression(arg)) {
let hasZh = false, hasKey = false, zhValue = '';
arg.properties.forEach(prop => {
if (t.isObjectProperty(prop) && prop.key.name === 'zh' && t.isStringLiteral(prop.value)) {
hasZh = true;
zhValue = prop.value.value;
}
if (t.isObjectProperty(prop) && prop.key.name === 'key') {
hasKey = true;
}
});
if (hasZh && !hasKey) {
const key = generateKey(zhValue, filePath);
arg.properties.unshift(
t.objectProperty(t.identifier('key'), t.stringLiteral(key, { quote }))
);
}
}
}
}
});
3.3.2 FormattedTableHeaderMessage 自动替换
js
traverse(ast, {
JSXElement(path) {
const opening = path.node.openingElement;
if (!t.isJSXIdentifier(opening.name, { name: 'FormattedTableHeaderMessage' })) return;
// ...省略,见 convert.js
}
});
3.4 配置与扩展性
- 支持 translateFn、libPath、scanDirs、output.dir、langs 等高度自定义
- 支持自定义翻译API、缓存、key生成规则
- 支持多种国际化场景,兼容历史代码
3.5 典型应用场景
- 新项目一键国际化
- 老项目批量改造
- 语言包同步与翻译自动化
- 代码review与diff可视化
3.6 语言包生成示例
translate-cli 会自动生成结构化的语言包文件,支持多语言同步。以下是生成的语言包示例:

语言包采用 JSON 格式,包含以下特点:
- 按模块/页面组织 key 结构
- 支持嵌套对象,便于管理大量文案
- 自动同步所有配置的语言版本
- 支持增量更新,保留已有翻译
3.7 翻译服务集成
translate-cli 支持灵活配置外部翻译服务,通过自定义翻译接口实现与各种翻译服务的集成。目前主要集成了基于 Qwen 大模型的批量翻译服务。
3.7.1 翻译接口设计
translate-cli 提供了统一的翻译接口抽象,支持自定义翻译实现。通过配置文件中的 translateApi.customFn
可以灵活配置外部翻译服务:
js
// translate-config.js 配置示例
module.exports = {
translate: {
translateApi: {
/**
* 自定义翻译函数
*
* @param {Object} payload - 翻译请求参数
* @param {Object} payload.texts - 待翻译的文本对象,key为文本标识,value为待翻译文本
* @param {string|string[]} payload.targetLang - 目标语言,可以是单个语言代码或语言代码数组
* @param {string} [payload.sourceLang='zh'] - 源语言,默认为中文
*
* @returns {Promise<Object>} 翻译结果对象
* @returns {Object.<string, Object>} 返回对象的key为文本标识,value为各语言的翻译结果
*
* @example
* // 单目标语言翻译
* const result = await customFn({
* texts: {
* 'welcome': '欢迎使用',
* 'hello': '你好'
* },
* targetLang: 'en',
* sourceLang: 'zh'
* });
* // 返回结果:
* // {
* // 'welcome': { en: 'Welcome to use' },
* // 'hello': { en: 'Hello' }
* // }
*
* @example
* // 多目标语言翻译
* const result = await customFn({
* texts: {
* 'welcome': '欢迎使用',
* 'hello': '你好'
* },
* targetLang: ['en', 'th'],
* sourceLang: 'zh'
* });
* // 返回结果:
* // {
* // 'welcome': {
* // en: 'Welcome to use',
* // th: 'ยินดีต้อนรับ'
* // },
* // 'hello': {
* // en: 'Hello',
* // th: 'สวัสดี'
* // }
* // }
*/
customFn: async function (payload) {
const axios = require('axios');
const { texts, targetLang, sourceLang } = payload;
// 构造批量翻译请求数据
const dataArr = Object.entries(texts).map(([key, text]) => ({
key,
text,
source: sourceLang || 'zh',
targetLang
}));
// 调用翻译服务
const res = await axios.post('https://test-fe.aiadfly.com/tool/ai/translate', { data: dataArr });
const resultArr = res.data.data;
// 处理翻译结果
const langs = Array.isArray(targetLang) ? targetLang : [targetLang];
const results = {};
for (const item of resultArr) {
if (item && item.text && item.results) {
const key = Object.keys(texts).find(k => texts[k] === item.text);
if (key) {
results[key] = {};
for (const lang of langs) {
results[key][lang] = item.results[lang] || '';
}
}
}
}
// 处理未翻译的文本
for (const key of Object.keys(texts)) {
if (!(key in results)) {
results[key] = {};
for (const lang of langs) {
results[key][lang] = texts[key];
}
}
}
return results;
}
}
}
}
3.7.2 Qwen 大模型翻译服务
translate-cli 集成了基于 Qwen 大模型的翻译服务,通过封装 Qwen API 实现高质量的批量翻译。以下是核心实现:
js
// 服务端翻译接口实现
import OpenAI from 'openai'
const client = new OpenAI({
apiKey: 'your-api-key',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
})
// 支持的语言列表
const ALLOWED_LANGS = ['zh', 'en', 'th', 'jp']
// 批量翻译接口
router.post('/translate', async function (ctx) {
const { data } = ctx.request.body
// 构建批量翻译的提示词
const translationPrompts = data.map(item => {
const { text, source, targetLang } = item
const targetLangs = Array.isArray(targetLang) ? targetLang : [targetLang]
return {
text,
source,
targetLangs,
prompt: `将文本"${text}"从${source}翻译成${targetLangs.join(',')}`
}
})
// 调用 Qwen 模型进行翻译
const response = await client.chat.completions.create({
model: 'qwen-max',
messages: [
{
role: 'system',
content: `你是一个专业的翻译助手。请严格只返回JSON,格式为:
{"translations": [{"text": "原文", "source": "源语言", "targetLangs": ["目标语言1", "目标语言2"],
"results": {"语言代码1": "翻译结果1", "语言代码2": "翻译结果2"}}]}`
},
{
role: 'user',
content: JSON.stringify(translationPrompts)
}
]
})
// 处理翻译结果
const result = JSON.parse(response.choices[0].message.content)
return result.translations
})
翻译服务的主要特点:
-
批量翻译能力
- 支持一次性翻译多个文本
- 自动处理多目标语言翻译
- 保持上下文连贯性
-
多语言支持
- 支持中文、英文、泰语、日语等
- 自动识别源语言
- 支持多语言互译
3.9 diff 预览与人工审核平台
translate-cli 不仅在命令行自动生成 diff 数据,还配套了前端可视化 diff 审核平台,如图所示:

平台主要功能:
- 左侧为文件树,支持快速定位和筛选变更文件
- 中间为代码对比视图,左为原始代码,右为转换后代码,所有国际化替换一目了然
- 右上角支持"保存所有/当前文件变更"、"生成语言包"、"预览语言包"等操作
- 支持逐条/批量审核、回滚,极大提升团队协作效率和安全性
典型流程:
- CLI 工具扫描并生成 diff-data.json
- 前端 diff 平台读取 diff-data.json,展示所有变更
- 团队成员可逐条/批量审核、确认、保存或回滚变更
- 审核通过后,变更写回源码并同步语言包
优势:
- 让国际化替换过程"可见、可控、可回溯"
- 降低误操作风险,提升团队协作效率
- 支持大规模项目的安全国际化改造
四、总结与展望
translate-cli 通过 AST 自动化、配置化、流程可视化,极大降低了前端国际化的门槛和成本。
- 效率提升:批量处理,极大减少人工操作
- 质量保障:key唯一、语言包同步、diff可视化
- 易用性强:命令行一键集成,支持自定义
- 可扩展性好:支持多语言、API扩展、key规则自定义
未来可进一步支持:
- 智能检测未国际化文本
- 多人协作与翻译平台对接
- 更丰富的组件/场景自动化支持
欢迎 Star
如果这个工具对您有帮助,欢迎给个 Star 支持一下!您的支持是我持续改进的动力。
GitHub: github.com/huqc2513/tr...