hitf-cli 翻译功能设计
很多前端项目一开始并不会完整规划国际化。业务推进一段时间后,页面、弹窗、按钮、状态、提示语已经散落在 Vue、React、JSX、工具函数和配置对象里。此时再手工迁移,工作量大,而且容易漏。
hitf-cli 的翻译能力就是为这个阶段设计的:它不是简单地搜索中文再替换,而是尽量理解不同文件里的语法边界,把源语言文案迁移成国际化调用,并生成可维护的语言包。
核心流程
一次 hitf tf 大致会经历四步:
- 读取源文件,判断文件类型。
- 按源码语言提取文案,保留每段文案在源码中的位置。
- 生成 key,查询缓存或翻译服务,写入语言包。
- 把源文件中的中文替换为
$t或t调用。
这个流程看起来直接,但难点都藏在"文案边界"里。
为什么不能只靠正则
中文可能出现在很多地方:
<button>登录</button><input placeholder="请输入用户名" /><h1>留言({remarkList ? remarkList.length : 0})</h1>const message = '保存成功'它们替换后的语法并不一样。Vue 模板文本需要 {{ $t(...) }},属性需要变成动态绑定,JSX 文本需要 {t(...)},JS 字符串需要表达式调用。
如果只用正则,很容易把文件路径、正则表达式、注释、样式类名、TypeScript 类型、插值标点一起误处理。
缓存优先
真实项目里,机器翻译只能解决一部分问题。菜单名、状态名、按钮名、行业术语通常需要统一表达。
因此 hitf-cli 支持先导入公司翻译表:
hitf tci .\.hitf\company.xlsx推荐表格:
| zh-CN | en-US | ja-JP |
|---|---|---|
| 登录 | Login | ログイン |
| 正常状态 | Normal Status | 正常状態 |
导入后,缓存会按语言方向组织:
{
"zh-CN|en-US": {
"登录": "Login"
},
"zh-CN|ja-JP": {
"登录": "ログイン"
}
}这样翻译过程可以优先使用人工词库,缺失时再调用翻译服务或回退原文。
面向边界的取词
翻译迁移真正影响体验的往往是边界细节。
例如:
<h1>留言({{ remarkList ? remarkList.length : 0 }})</h1>理想结果不是把左括号吞进文案:
<h1>{{ $t('demo.leave_a_message') }}{{ remarkList ? remarkList.length : 0 }})</h1>而是:
<h1>{{ $t('demo.leave_a_message') }}({{ remarkList ? remarkList.length : 0 }})</h1>另一个例子是资源路径:
<img src="@/assets/images/ssHome/供应商总数量.png" alt="供应商总数量" />src 是文件引用,不能翻译;alt 是可见语义,可以翻译。
现在的取词会进一步处理:
- JS/TS/JSX/TSX 和 Vue SFC
- Vue
<script setup lang="jsx">与<script setup lang="tsx"> - 模板字符串、可选链、
||和??兜底文案 - JSX/TSX 文本节点和自定义展示属性
- Vue 模板展示文本和展示属性
- 已处理的
$t(...)、this.$t(...)、t(...)跳过 - 注释、正则、样式、class/className、style、资源路径、TS 类型语法跳过
当某段代码确实不希望自动处理时,可以使用:
// @hitf-i18n-ignore-next
/* @hitf-i18n-ignore-start */
/* @hitf-i18n-ignore-end */
// @hitf-i18n-ignore-file报告和一致性检查
批量迁移时,取词是否全面、失败文件有哪些、语言包是否缺 key 都需要机器可读的结果。
tf / tfo 支持输出 JSON 报告:
hitf tfo src\views views --dry-run --report .hitf\reports\views.json-d / --dry-run 仅预览翻译计划:终端环境弹出只读树形界面展示「目录 > 文件 > 文本」三级结构(↑/↓ 移动、←/→ 展开/折叠、输入搜索、Enter 结束),非 TTY 环境自动回退为静态树形打印;不执行翻译、不写文件,报告的 mode 为 dry-run。
翻译后可以执行:
hitf tcheck src --report .hitf\reports\tcheck.json它会检查源码引用了但语言包缺失的 key、语言包中未被源码引用的 key、不同 locale JSON 之间不一致的 key,以及无法解析的 JSON 文件。
忽略注释
当某些文本不希望被自动处理时,可以在源码中添加忽略注释:
<!-- @hitf-i18n-ignore-next -->
<div>{{ brandName }}</div>
<!-- @hitf-i18n-ignore-start -->
<div>
<span>不翻译</span>
<span>也不翻译</span>
</div>
<!-- @hitf-i18n-ignore-end -->// @hitf-i18n-ignore-file
const msg = '整个文件跳过翻译'这些注释也可以由 -i 交互取消选中时自动插入:JS / TS 用 /* @hitf-i18n-ignore-start */ … /* @hitf-i18n-ignore-end */,Vue 模板用 <!-- -->,JSX 用 {/* */},Vue 模板静态属性用 <!-- @hitf-i18n-ignore:prop:<attrName> --> 置于标签正上方。JSON 文件不支持注释,取消选中项会记录到 .hitf/i18n-ignore.json,后续提取自动过滤。
还可以生成 IDE 代码片段来快速插入这些注释:
hitf codesnippets # 自动检测 IDE
hitf codesnippets vscode # 手动指定支持 VS Code、WebStorm、Cursor、Sublime Text。
多项目支持
在 monorepo 场景下,outDir 支持对象格式,按项目名映射输出目录:
{
"translationSetting": {
"outDir": {
"main": "apps/main/src/locale/module",
"web": "apps/web/src/locale/module"
}
}
}使用 -p, --project 参数指定项目:
hitf tf apps/main/src/views/index.vue -p main
hitf tfo apps/web/src/views views -p web不指定时默认使用对象中第一个 key。
CLI 语言切换
hitf 支持中英文界面切换:
hitf lang -l # 查看当前语言
hitf lang zh-CN # 切换为中文
hitf lang en # 切换为英文
一次 `hitf tf` 大致会经历四步:
1. 读取源文件,判断文件类型。
2. 按源码语言提取文案,保留每段文案在源码中的位置。
3. 生成 key,查询缓存或翻译服务,写入语言包。
4. 把源文件中的中文替换为 `$t` 或 `t` 调用。
这个流程看起来直接,但难点都藏在“文案边界”里。
## 为什么不能只靠正则
中文可能出现在很多地方:
```vue
<button>登录</button><input placeholder="请输入用户名" /><h1>留言({remarkList ? remarkList.length : 0})</h1>const message = '保存成功'它们替换后的语法并不一样。Vue 模板文本需要 {{ $t(...) }},属性需要变成动态绑定,JSX 文本需要 {t(...)},JS 字符串需要表达式调用。
如果只用正则,很容易把文件路径、正则表达式、注释、样式类名、TypeScript 类型、插值标点一起误处理。
缓存优先
真实项目里,机器翻译只能解决一部分问题。菜单名、状态名、按钮名、行业术语通常需要统一表达。
因此 hitf-cli 支持先导入公司翻译表:
hitf tci .\.hitf\company.xlsx推荐表格:
| zh-CN | en-US | ja-JP |
|---|---|---|
| 登录 | Login | ログイン |
| 正常状态 | Normal Status | 正常状態 |
导入后,缓存会按语言方向组织:
{
"zh-CN|en-US": {
"登录": "Login"
},
"zh-CN|ja-JP": {
"登录": "ログイン"
}
}这样翻译过程可以优先使用人工词库,缺失时再调用翻译服务或回退原文。
面向边界的取词
翻译迁移真正影响体验的往往是边界细节。
例如:
<h1>留言({{ remarkList ? remarkList.length : 0 }})</h1>理想结果不是把左括号吞进文案:
<h1>{{ $t('demo.leave_a_message') }}{{ remarkList ? remarkList.length : 0 }})</h1>而是:
<h1>{{ $t('demo.leave_a_message') }}({{ remarkList ? remarkList.length : 0 }})</h1>另一个例子是资源路径:
<img src="@/assets/images/ssHome/供应商总数量.png" alt="供应商总数量" />src 是文件引用,不能翻译;alt 是可见语义,可以翻译。
现在的取词会进一步处理:
- JS/TS/JSX/TSX 和 Vue SFC
- Vue
<script setup lang="jsx">与<script setup lang="tsx"> - 模板字符串、可选链、
||和??兜底文案 - JSX/TSX 文本节点和自定义展示属性
- Vue 模板展示文本和展示属性
- 已处理的
$t(...)、this.$t(...)、t(...)跳过 - 注释、正则、样式、class/className、style、资源路径、TS 类型语法跳过
当某段代码确实不希望自动处理时,可以使用:
// @hitf-i18n-ignore-next
/* @hitf-i18n-ignore-start */
/* @hitf-i18n-ignore-end */
// @hitf-i18n-ignore-file报告和一致性检查
批量迁移时,取词是否全面、失败文件有哪些、语言包是否缺 key 都需要机器可读的结果。
tf / tfo 支持输出 JSON 报告:
hitf tfo src\views views --dry-run --report .hitf\reports\views.json-d / --dry-run 仅预览翻译计划:终端环境弹出只读树形界面展示「目录 > 文件 > 文本」三级结构(↑/↓ 移动、←/→ 展开/折叠、输入搜索、Enter 结束),非 TTY 环境自动回退为静态树形打印;不执行翻译、不写文件,报告的 mode 为 dry-run。
翻译后可以执行:
hitf tcheck src --report .hitf\reports\tcheck.json它会检查源码引用了但语言包缺失的 key、语言包中未被源码引用的 key、不同 locale JSON 之间不一致的 key,以及无法解析的 JSON 文件。
源文本导出
在翻译之前,可以先导出源语言文本用于人工审核或公司内部翻译流程:
hitf export-source src/views # 从源码提取
hitf export-translated # 从已翻译语言包读取两个命令共享同一输出文件 .hitf/source-texts-<locale>.xlsx,增量合并去重。输出格式兼容 cache-import,方便后续导入。
回滚机制
翻译操作可能影响大量文件,因此提供了回滚能力:
hitf rollback -l # 查看可用备份
hitf rollback # 回滚最近一次
hitf rollback -n xxx # 回滚指定备份
hitf rollback -k 5 # 仅保留最近 5 个备份每次翻译前会自动创建备份,确保可以恢复到翻译前的状态。
忽略注释
当某些文本不希望被自动处理时,可以在源码中添加忽略注释:
<!-- @hitf-i18n-ignore-next -->
<div>{{ brandName }}</div>
<!-- @hitf-i18n-ignore-start -->
<div>
<span>不翻译</span>
<span>也不翻译</span>
</div>
<!-- @hitf-i18n-ignore-end -->// @hitf-i18n-ignore-file
const msg = '整个文件跳过翻译'这些注释也可以由 -i 交互取消选中时自动插入:JS / TS 用 /* @hitf-i18n-ignore-start */ … /* @hitf-i18n-ignore-end */,Vue 模板用 <!-- -->,JSX 用 {/* */},Vue 模板静态属性用 <!-- @hitf-i18n-ignore:prop:<attrName> --> 置于标签正上方。JSON 文件不支持注释,取消选中项会记录到 .hitf/i18n-ignore.json,后续提取自动过滤。
还可以生成 IDE 代码片段来快速插入这些注释:
hitf codesnippets # 自动检测 IDE
hitf codesnippets vscode # 手动指定支持 VS Code、WebStorm、Cursor、Sublime Text。
多项目支持
在 monorepo 场景下,outDir 支持对象格式,按项目名映射输出目录:
{
"translationSetting": {
"outDir": {
"main": "apps/main/src/locale/module",
"web": "apps/web/src/locale/module"
}
}
}使用 -p, --project 参数指定项目:
hitf tf apps/main/src/views/index.vue -p main
hitf tfo apps/web/src/views views -p web不指定时默认使用对象中第一个 key。
CLI 语言切换
hitf 支持中英文界面切换:
hitf lang -l # 查看当前语言
hitf lang zh-CN # 切换为中文
hitf lang en # 切换为英文