描述在 grunt 中实现模板国际化注入
解读
“模板国际化注入”指的是:在构建阶段,把多语言资源(通常是 JSON 或 YAML 文件)注入到 HTML/JS 模板中,让页面在打包后就能直接带上对应语种的文案,而无需前端再发 Ajax 请求语言包。
国内项目普遍要求“构建即产出多语言包”,因此面试官想确认你是否:
- 熟悉 Grunt 的“多任务 + 文件对象格式”配置套路;
- 能自定义 Grunt 任务或组合社区插件完成“读语言包 → 按语种循环 → 渲染模板 → 输出到 dist/{lang}/**”的完整链路;
- 知道如何缓存语言包、避免重复 IO以及与后续压缩、CDN 上传任务保持顺序。
知识点
- grunt.file.readJSON / grunt.file.readYAML:同步读取语言包,避免回调地狱。
- grunt.template.process:使用 Lodash 模板语法,把 <%= key %> 替换成对应文案。
- grunt.registerMultiTask:创建“i18n”多任务,一次注册即可循环所有语种。
- this.files.forEach + grunt.file.write:手动控制目标目录结构,保证 dist/zh、dist/en 等并行产出。
- grunt.task.run([‘clean’, ‘i18n’, ‘usemin’, ‘cdn’]):利用任务队列保证国际化注入在压缩、加 hash 之前完成。
- locale 回退策略:若当前语种缺失某 key,需回退到 zh-CN 或 en-US,防止页面出现空白。
- 性能细节:
- 用 grunt.file.preserveBOM: false 避免 UTF-8 BOM 导致模板解析失败;
- 大项目语言包 2000+ key 时,用 grunt.log.writeln 打印进度,防止 CI 日志超时。
答案
-
目录约定
locales/
├── zh-CN.json
├── en-US.json
src/
└── index.html (含 <%= i18n.welcome %> 占位符) -
安装依赖
npm i -D grunt grunt-contrib-clean grunt-contrib-copy -
注册自定义多任务(Gruntfile.js 节选)
module.exports = function(grunt) {
grunt.initConfig({
clean: { i18n: ['dist'] },
i18nInject: {
options: {
localeRoot: 'locales',
fallback: 'zh-CN'
},
files: [{
expand: true,
cwd: 'src',
src: '**/*.html',
dest: 'dist'
}]
}
});grunt.registerMultiTask('i18nInject', function() {
const path = require('path');
const fallback = this.options().fallback;
const localeRoot = this.options().localeRoot;
const locales = grunt.file.expand(path.join(localeRoot, '*.json'))
.map(f => ({
code: path.basename(f, '.json'),
dict: grunt.file.readJSON(f)
}));this.files.forEach(fGroup => { const tpl = grunt.file.read(fGroup.src[0]); locales.forEach(({code, dict}) => { const outFile = path.join(fGroup.dest, code, fGroup.src[0]); const missing = {}; const i18n = new Proxy(dict, { get(t, key) { if (key in t) return t[key]; missing[key] = true; return grunt.file.readJSON(path.join(localeRoot, fallback))[key] || key; } }); const html = grunt.template.process(tpl, { data: { i18n } }); grunt.file.write(outFile, html); if (Object.keys(missing).length) { grunt.log.warn(`[${code}] 缺失 key:${Object.keys(missing).join(', ')}`); } }); });});
grunt.registerTask('i18n', ['clean', 'i18nInject']);
}; -
运行
npx grunt i18n
产出
dist/zh-CN/index.html
dist/en-US/index.html -
与后续流程衔接
grunt.registerTask('build', ['i18n', 'useminPrepare', 'concat', 'uglify', 'filerev', 'usemin']);
保证注入 → 合并 → 压缩 → 加 hash 顺序正确,避免 hash 计算时文案还未写入。
拓展思考
- 动态语种:若运营随时新增语种,可让 i18nInject 任务在运行时扫描 locales/,无需改 Gruntfile;结合 grunt-contrib-watch,新增 JSON 后自动重跑 i18n。
- 与 React/Vue 同构:模板注入只解决首屏,客户端仍需加载语言包做二次替换;可在构建阶段同时产出 locale-manifest.json,告诉运行时当前语种已注入哪些 key,避免重复下载。
- 与 CI 量化:在 GitLab-CI 中把 grunt i18n --verbose 输出重定向到文件,用正则提取“缺失 key”数量,当缺失率 > 1% 时让流水线失败,强制翻译达标才能上线。