翻译工作流
本页介绍核心翻译操作:翻译单文件、翻译文件夹、一致性检查和回滚。
加载语言
可选:在正式开始翻译生成之前的准备工作,自动加载指定目录下的 json。以下是 webpack 和 vite 的常用方式:
webpack
const enContext = require.context('./module', true, /\/en-US\.json$/)
const zhContext = require.context('./module', true, /\/zh-CN\.json$/)
const contextToModuleMap = (context) => Object.fromEntries(context.keys().map((key) => [key, context(key)]))
const enMds = contextToModuleMap(enContext)
const zhMds = contextToModuleMap(zhContext)
const getLocaleCollect = (m) => Object.values(m).reduce((o, k) => ({ ...o, ...k }), {})
const enUsCollect = getLocaleCollect(enMds)
const zhCnCollect = getLocaleCollect(zhMds)vite
const enMds = import.meta.glob('./module/**/en-US.json', { eager: true, import: 'default' })
const zhMds = import.meta.glob('./module/**/zh-CN.json', { eager: true, import: 'default' })
const getLocaleCollect = (m) => Object.values(m).reduce((o, k) => ({ ...o, ...k }), {})
const enUsCollect = getLocaleCollect(enMds)
const zhCnCollect = getLocaleCollect(zhMds)翻译单文件
hitf tf 用于翻译一个文件。它会提取源语言文案、生成 key、写入语言包,并把源文件中的文案替换为国际化调用。
默认兼容中文源码项目。配置 locales: ["en-US", "zh-CN", "ja-JP"] 后,可以提取英文文案并翻译到 zh-CN 和 ja-JP。
基本命令
hitf tf views\demo\index.vue支持常见文件类型:
| 类型 | 说明 |
|---|---|
.vue | Vue SFC,包含 <template>、<script>、<script setup> |
.js / .ts | JavaScript 和 TypeScript 业务代码 |
.jsx / .tsx | React JSX / TSX |
.json | JSON 文案值 |
基本选项
hitf tf src\views\demo\index.vue demo -d
hitf tf src\views\demo\index.vue demo -i
hitf tf src\views\demo\index.vue demo -r .hitf\reports\demo.json
hitf tf apps\main\src\views\index.vue home -p main| 选项 | 简写 | 说明 |
|---|---|---|
-n, --name <name> | -n | 国际化对象 key(默认使用父级目录名) |
-p, --project <project> | -p | outDir 为对象时指定项目 |
-d, --dry-run | -d | 仅预览翻译计划:逐项列出提取文本与位置,不执行翻译、不写文件 |
-i, --interactive | -i | 树形多选要翻译的文本;未选中的项自动添加忽略注释,不参与翻译 |
-r, --report <file> | -r | 写入 JSON 翻译报告 |
-v, --verbose | -v | 输出详细日志 |
预览模式
-d / --dry-run 仅预览翻译计划:不调用翻译、不写入任何文件。单文件 tf 逐项列出提取的文本及其源码位置(文件:行:列),并附被跳过(未提取)的字符串及原因:
hitf tf src\views\demo\index.vue -d输出示例:
合适 → src/views/demo/index.vue:2:21
测试 → src/views/demo/index.vue:3:21tf -d 不会生成语言包、不会修改源码,适合在正式翻译前核对取词范围。
交互式选择
-i / --interactive 以树形多选框选择需要翻译的文本,只有选中的项会参与翻译并生成 key;未选中的项就地标记为忽略,下次提取自动跳过:
hitf tf src\views\demo\index.vue demo -i按键操作:
| 按键 | 作用 |
|---|---|
↑ / ↓ | 上下移动 |
← / → | 折叠 / 展开分组 |
Space | 勾选 / 取消:叶子切换自身;分组切换整支(◉ 全选 / ◐ 半选 / ◯ 未选) |
Ctrl+A | 全选当前可见叶子 |
Ctrl+I(兼容 Tab) | 反选当前可见叶子 |
Enter | 确认提交(至少选择 1 项) |
| 输入字符 | 按文本过滤搜索,Backspace 删除搜索词 |
确认后仅翻译选中的项,未选中的项会提示「已跳过 N 个文本(将添加忽略注释)」,并在源码中自动插入忽略注释(详见下文忽略注释):
- JS / TS:
/* @hitf-i18n-ignore-start */…/* @hitf-i18n-ignore-end */ - Vue 模板文本:
<!-- @hitf-i18n-ignore-start -->…<!-- @hitf-i18n-ignore-end --> - JSX:
{/* @hitf-i18n-ignore-start */}…{/* @hitf-i18n-ignore-end */} - Vue 模板静态属性:
<!-- @hitf-i18n-ignore:prop:<attrName> -->(置于目标标签正上方) - JSON 文件:不插入注释,而是记录到
.hitf/i18n-ignore.json,后续提取自动过滤
交互中断(如 Ctrl+C)会中止翻译,不产生任何写入。
取词范围
会提取用户可见或运行时展示的文案:
message.success('Save successfully')
toast('Copied')
const item = {
label: 'User Name',
title: 'User Detail',
placeholder: 'Enter username',
description: 'Please select a user'
}支持逻辑表达式里的兜底文案:
const text = data?.text || 'Default text'
const desc = data?.description ?? 'No description'Vue 模板中也支持:
{{ data?.text || 'Default text' }}
{{ data?.description ?? 'No description' }}解构默认值和函数参数默认值默认不取,避免把类型/数据结构兜底误认为 UI 文案:
const { propertyName = 'User name' } = objectToDestruct
function submit(label = 'Save') {}Vue 模板
文本节点会替换为 $t:
<button>登录</button><button>{{ $t('demo.login') }}</button>插值旁边的中文会保留表达式边界:
<h1>留言({{ remarkList ? remarkList.length : 0 }})</h1><h1>{{ $t('demo.leave_a_message') }}({{ remarkList ? remarkList.length : 0 }})</h1>属性
用户可见属性会被翻译:
<input placeholder="请输入用户名" /><input :placeholder="$t('demo.please_enter_username')" />资源路径属性不会翻译,避免破坏文件引用:
<img src="@/assets/images/ssHome/供应商总数量1.png" alt="供应商总数量" /><img src="@/assets/images/ssHome/供应商总数量1.png" :alt="$t('demo.total_number_of_suppliers')" />当前建议跳过的资源属性包括 src、srcset、href、poster。
样式相关内容会跳过,包括 class、动态 class、行内 style 和 <style> 块:
<div class="user-card" :style="{ color: 'red' }">用户详情</div>
<style lang="scss" scoped>
.user-card {}
</style>JavaScript 字符串
脚本中的源语言字符串会替换为翻译函数调用:
const title = '登录'const title = t('demo.login')英文源码示例:
{
"translationSetting": {
"locales": ["en-US", "zh-CN", "ja-JP"]
}
}<button>Login</button><button>{{ $t('demo.login') }}</button>正则中的中文属于匹配规则,不建议自动翻译:
const regex = /中文|English/giu已处理过的 $t(...)、this.$t(...)、t(...) 会跳过,Vue2 的 this.$t 会保留支持。
JSX
JSX 文本会替换为 {t(...)}:
<h1>留言({remarkList ? remarkList.length : 0})</h1><h1>{t('jsxAll.leave_a_message')}({remarkList ? remarkList.length : 0})</h1>长段自然语言应尽量作为一个完整文案处理,避免按逗号、句号拆成过多 key。
JSX/TSX 中 className、style、事件属性、type、role、data-*、aria-* 会跳过;自定义展示属性可以提取,例如:
<Comp title="User Detail" label="User Name" />Vue SFC 中的 <script setup lang="jsx"> 和 <script setup lang="tsx"> 也会按 JSX/TSX 解析,支持 render function 和 defineComponent 中返回的 JSX 文案。
忽略注释
临时不希望处理的代码可以用忽略注释:
// @hitf-i18n-ignore-next
message.success('Keep original')
/* @hitf-i18n-ignore-start */
const a = 'Keep original'
const b = 'Keep original too'
/* @hitf-i18n-ignore-end */Vue 模板:
<!-- @hitf-i18n-ignore-next -->
<div>保持原文</div>整文件忽略:
// @hitf-i18n-ignore-file指令说明:
| 指令 | 作用 |
|---|---|
@hitf-i18n-ignore-next | 忽略下一段可提取文本 |
@hitf-i18n-ignore-start / -end | 忽略区间内的所有文本 |
@hitf-i18n-ignore-file | 忽略整个文件 |
@hitf-i18n-ignore:prop:<attrName> | 忽略 Vue 模板中指定静态属性(如 <!-- @hitf-i18n-ignore:prop:placeholder -->) |
这些注释也可以由 -i 交互取消选中时自动插入:JS / TS 用 /* */、Vue 模板用 <!-- -->、JSX 用 {/* */},Vue 模板静态属性用 <!-- @hitf-i18n-ignore:prop:<attrName> --> 置于标签正上方。JSON 文件不支持注释,取消选中项会记录到 .hitf/i18n-ignore.json,后续提取自动过滤。
输出
翻译后通常会产生两类变化:
- 原文件中的中文被替换为
$t或t调用。 - locale 目录中生成对应语言包。
示例:
src\locale\module\demo\zh-CN.json
src\locale\module\demo\en-US.json翻译文件夹
hitf tfo 用于递归翻译整个文件夹。
基本命令
hitf tfo src\views递归处理目录下所有 .vue、.js、.ts、.jsx、.tsx、.json 文件。
常用选项
# 预览模式
hitf tfo src\views demo -d
# 交互确认
hitf tfo src\views demo -i
# 生成报告
hitf tfo src\views demo -r .hitf\reports\demo.json| 选项 | 简写 | 说明 |
|---|---|---|
-n, --name <name> | -n | 国际化对象 key(默认使用父级目录名) |
-p, --project <project> | -p | outDir 为对象时指定项目 |
-d, --dry-run | -d | 仅预览翻译计划:逐项列出提取文本与位置,不执行翻译、不写文件 |
-i, --interactive | -i | 树形多选要翻译的文本;未选中的项自动添加忽略注释,不参与翻译 |
-r, --report <file> | -r | 写入 JSON 翻译报告 |
-v, --verbose | -v | 输出详细日志 |
文件夹预览(树形浏览)
tfo -d 在扫描完成后以只读树形界面展示「目录 > 文件 > 文本」三级翻译计划(叶子带 [译] 标记,默认全部翻译):
hitf tfo src\views demo -d树形浏览按键:↑/↓ 移动 · ←/→ 展开/折叠 · 输入字符搜索 · Enter 结束。结束后输出将翻译的总条数及各处被跳过的字符串明细。不执行翻译、不写文件。
非 TTY 环境(脚本 / CI / 管道)下
tfo -d不弹交互界面,自动回退为静态树形打印,预览结果同样完整输出。
文件夹交互选择
tfo -i 以树形多选框按「目录 > 文件 > 文本」选择需要翻译的文本,未选中的项自动添加忽略注释(行为与单文件 tf -i 一致,见上文交互式选择):
hitf tfo src\views demo -i排除文件
使用 --exclude 排除特定文件:
hitf tfo src --exclude "**/*.test.js"批量输出
翻译结果写入 outDir 下对应的 namespace 目录:
src\locale\module\demo\zh-CN.json
src\locale\module\demo\en-US.json一致性检查
hitf tcheck 用于检查源码中的 $t(...)、this.$t(...)、t(...) 引用和 locale JSON 是否一致。
基本命令
hitf tcheck src
hitf tcheck src -r .hitf\reports\tcheck.json检查内容
| 类型 | 说明 |
|---|---|
| missing | 源码引用了但所有语言包都不存在的 key |
| unused | 语言包存在但源码没有引用的 key |
| inconsistent | 某个 key 在部分 locale JSON 中缺失 |
| invalid locale files | 无法解析的 JSON 语言包 |
输出示例
Translation check summary
missing: 2
unused: 0
inconsistent: 0
invalid locale files: 0
missing hello
src/App.vue:2:11
missing welcome
src/App.vue:3:9tcheck 适合在批量迁移后执行,也适合放进 CI,阻止缺失语言包或 JSON 损坏进入主分支。
回滚操作
hitf rollback 用于回滚上一次翻译操作。
基本命令
hitf rollback # 回滚最近一次
hitf rollback -l # 查看可用备份
hitf rollback -n xxx # 回滚指定备份
hitf rollback -k 5 # 仅保留最近 5 个备份选项
| 选项 | 简写 | 说明 |
|---|---|---|
-l, --list | -l | 列出所有可用备份 |
-n, --name <name> | -n | 回滚指定名称的备份 |
-k, --keep <count> | -k | 仅保留最近 N 个备份 |
使用场景
- 翻译结果不满意:回滚后调整配置重新翻译
- 误操作恢复:回滚到翻译前的状态
- 备份管理:清理旧备份释放空间
翻译报告
tf 和 tfo 都支持 -r, --report <file> 生成翻译报告:
hitf tf src\views\user\index.vue user -d -r .hitf\reports\user.json
hitf tfo src\views user -r .hitf\reports\user-folder.json报告是 JSON 格式,适合人工审查、CI 上传或后续脚本处理:
{
"mode": "dry-run",
"summary": {
"filesScanned": 1,
"filesWithExtractions": 1,
"replacements": 3,
"written": 0,
"skipped": 1,
"failed": 0
},
"files": [
{
"file": "src/views/user/index.vue",
"outputFile": ".hitf/lang/user/index.vue",
"replacements": [
{
"text": "User Detail",
"key": "user.user_detail",
"context": "attribute",
"start": 42,
"end": 53
}
]
}
],
"failedFiles": []
}字段含义:
| 字段 | 说明 |
|---|---|
mode | dry-run 或 write |
summary.filesScanned | 实际扫描的文件数量 |
summary.filesWithExtractions | 有取词结果的文件数量 |
summary.replacements | 准备替换或已替换的文案数量 |
summary.written | 已写入文件数量 |
summary.skipped | 跳过文件数量 |
summary.failed | 失败文件数量 |
files[].replacements | 每个文件内的取词、key、上下文和位置 |
failedFiles | 语法校验失败或处理失败的文件 |