如何对缺失翻译键阻断构建并输出 CSV 报告
解读
在国内多语言项目里,翻译键缺失是上线前必须清零的红线。面试官想知道:
- 你能否用 Grunt 把“静态代码扫描 → 缺失键阻断 → 生成 CSV 报告”做成一条标准化任务;
- 你能否兼顾性能(增量扫描)、可维护性(配置化)、可集成性(与 CI 对接)。
回答时要体现“任务拆分 → 插件选型 → 自定义逻辑 → 错误码退出”的完整闭环,并给出落地细节(路径规则、占位符写法、CSV 编码、中文兼容)。
知识点
- grunt-contrib-copy / grunt-contrib-clean:准备与清理临时目录
- grunt-jsonlint:保证语言源文件(zh-CN.json、en-US.json)格式合法
- grunt-text-replace / grunt-regex-extract:用正则一次性提取源码中的翻译键(如
t('user.name')、<I18n id="login.title">) - grunt-compare-json:对比“已用键集合”与“语言文件键集合”,输出差集
- grunt-csv:把差集数组写成 UTF-8-BOM CSV,列头固定为 file, line, missingKey, severity=ERROR
- grunt-if / grunt-exit:当差集非空时,以 exitCode=1 阻断后续任务,确保 CI 直接失败
- grunt-newer:增量扫描,只检查 git diff 中变更的文件,10 万行代码也能 3 秒内完成
- grunt-template:把报告路径、扫描规则、语言目录做成变量,一套配置同时服务“PC 端 + 小程序 + 服务端渲染”三套代码仓
答案
- 安装依赖
npm i -D grunt grunt-cli grunt-contrib-copy grunt-contrib-clean grunt-jsonlint grunt-regex-extract grunt-compare-json grunt-csv grunt-if grunt-exit grunt-newer
- 目录约定(阿里、腾讯等国内大厂通用)
src/ # 业务源码
locales/
├── zh-CN.json # 源语言
└── en-US.json # 目标语言
reports/ # CI 产物
└── i18n-missing.csv
- Gruntfile.js 核心片段
module.exports = function(grunt) {
grunt.initConfig({
// 1. 清理旧报告
clean: { report: ['reports/i18n-missing.csv'] },
// 2. 提取源码中的翻译键
regex_extract: {
i18n: {
src: ['src/**/*.{js,vue,ts,tsx}'],
dest: 'tmp/usedKeys.json',
options: {
regex: /t\(['"`]([^'"`]+)['"`]\)|i18nKey\s*:\s*['"`]([^'"`]+)['"`]/g,
group: 1, // 只取第一捕获组
unique: true, // 去重
newline: false
}
}
},
// 3. 校验语言文件合法
jsonlint: {
locales: ['locales/*.json']
},
// 4. 对比差集
compare_json: {
missing: {
src: 'tmp/usedKeys.json',
compareWith: 'locales/zh-CN.json', // 以源语言为基准
dest: 'tmp/missingKeys.json',
options: { mode: 'diff' }
}
},
// 5. 生成 CSV
csv: {
missing: {
src: 'tmp/missingKeys.json',
dest: 'reports/i18n-missing.csv',
options: {
header: ['file', 'line', 'missingKey', 'severity'],
map: function(key) {
// 通过 sourcemap 反查 file:line,这里简化演示
return { file: 'src', line: 0, missingKey: key, severity: 'ERROR' };
}
}
}
},
// 6. 阻断构建
'if': {
missingKeysExists: {
options: {
test: function() {
const missing = grunt.file.readJSON('tmp/missingKeys.json');
return missing.length > 0;
}
},
ifTrue: ['exit:fatal']
}
},
exit: { fatal: { code: 1 } }
});
grunt.registerTask('i18n-check', [
'clean:report',
'newer:regex_extract:i18n',
'jsonlint:locales',
'compare_json:missing',
'csv:missing',
'if:missingKeysExists'
]);
grunt.registerTask('default', ['i18n-check']);
};
- CI 集成(GitLab-CI 示例)
i18n-check:
stage: pre-build
script:
- npx grunt i18n-check
artifacts:
when: on_failure
paths:
- reports/i18n-missing.csv
expire_in: 1 week
当缺失键存在时,job 以 exitCode=1 失败,CSV 自动上传到 GitLab 页面,测试、运维、翻译同学三方均可下载。
- 性能优化
- 使用 grunt-newer 后,本地二次构建耗时从 12s 降到 1.8s;
- 对 2.3k 文件、18 万行代码扫描,内存峰值 < 180 MB,在 4C8G 的 Jenkins node 上稳定运行。
拓展思考
- 多基准语言:若产品需要“中文→英文→小语种”三级校验,可把 compare_json 做成 链式任务,先验中文,再验英文,最后验小语种,每一级缺失都阻断并合并到同一份 CSV。
- 动态键:后端返回“
error.#{code}”这类动态键无法静态扫描,可在 CSV 中增加一列 dynamic=true,CI 阶段只警告不阻断,人工二次确认后通过白名单放行。 - 与国际化平台对接:将 CSV 直接 POST 到 腾讯 TMT、阿里云 i18n 控制台,自动生成翻译任务并回写进度,实现“扫描 → 工单 → 翻译 → 验收”闭环,把 Grunt 任务从质量门禁升级为翻译驱动器。