使用 grunt-jsonlint 校验并压缩 i18n 语言包

解读

在国内一线/二线互联网公司的前端面试中,**“用 Grunt 做 i18n 语言包校验+压缩”**常被当作“工程化基本功”考点。
面试官真正想验证的是:

  1. 你是否理解 “先校验后压缩” 的构建顺序,避免脏数据进入产物;
  2. 能否把 grunt-jsonlintgrunt-contrib-uglify(或 grunt-json-minify)串成一条可重用的任务链;
  3. 是否熟悉 多语言目录结构规范(zh-CN.json、en-US.json…)及 CI 卡点配置;
  4. Gruntfile.js 的函数式写法、目标-任务-选项三要素是否手到擒来。
    回答时切忌只贴代码,要体现“工程落地思维”:目录规范、错误码返回、增量构建、并行加速、产物指纹、回滚策略,都是加分项。

知识点

  • grunt-jsonlint 原理:基于 jsonlint 库做词法/语法扫描,一旦 JSON 存在 BOM、尾随逗号、单引号、非法转义等“浏览器容忍但 JSON 标准不允许”的问题,立即抛出致命错误并返回非 0 exit code,CI 流程直接中断,防止脏包流入生产。
  • 压缩选型
    – 纯 JSON 文件优先用 grunt-json-minify,可删除空白符、缩短 key(需开启 safe-keys 选项),体积收益 10%–25%;
    – 若公司规范要求统一走 uglify,可 把 json 当 js 模块压,但要关闭 mangling 防止 key 被改写。
  • 多目录通配:使用 cwd/src/dest 三元组一次性扫描 src/i18n/**/*.json,保持目录层级,方便运维按语言维度灰度。
  • 任务编排
    – 注册 “i18n” 别名任务,顺序执行 jsonlint → json_minify → filerev(打指纹)→ copy → clean:tmp;
    – 在 watch 任务里监听 src/i18n 变更,增量跑 i18n,配合 livereload 刷新预览。
  • 性能优化
    – 使用 grunt-concurrent 把不同语言目录并行压缩,8 核 CPU 可缩短 40% 耗时;
    – 产物输出到 dist/i18n/lang.{lang}.{hash}.json,并在 HTML 里注入 <%= grunt.filerev.summary['i18n/zh-CN.json'] %>,实现 强缓存+版本回滚
  • 异常治理
    – 在 jsonlint 选项里开启 format: true,错误信息可定位到行列;
    – 结合 grunt-log-adapter 把错误输出到 junit.xml,对接阿里 Aone、腾讯蓝盾、GitLab CI 的测试报告面板。

答案

以下是一份可直接落地到国内 Webpack 老项目(仍需 Grunt 兜底)的 Gruntfile.js 精简示范,演示“校验+压缩+指纹”完整闭环:

module.exports = function(grunt) {
  grunt.initConfig({
    // 1. 校验 i18n 语言包
    jsonlint: {
      i18n: {
        src: ['src/i18n/**/*.json'],
        options: {
          format: true,          // 打印行列号
          cjson: false           // 不允许注释
        }
      }
    },

    // 2. 压缩 JSON
    json_minify: {
      i18n: {
        files: [{
          expand: true,
          cwd: 'src/i18n/',
          src: '**/*.json',
          dest: '.tmp/i18n/',
          ext: '.json'
        }]
      }
    },

    // 3. 打指纹
    filerev: {
      i18n: {
        src: '.tmp/i18n/**/*.json',
        dest: 'dist/i18n/'
      }
    },

    // 4. 清理临时目录
    clean: {
      tmp: ['.tmp']
    }
  });

  grunt.loadNpmTasks('grunt-jsonlint');
  grunt.loadNpmTasks('grunt-json-minify');
  grunt.loadNpmTasks('grunt-filerev');
  grunt.loadNpmTasks('grunt-contrib-clean');

  // 注册复合任务
  grunt.registerTask('i18n', ['jsonlint:i18n', 'json_minify:i18n', 'filerev:i18n', 'clean:tmp']);
  grunt.registerTask('default', ['i18n']);
};

关键解释

  1. jsonlint 必须放在最前,一旦校验失败后续任务不会执行,CI 脚本直接 exit 1,防止脏包上线。
  2. json_minify 输出到 .tmp,避免污染 src;随后 filerev 把文件打到 dist 并追加 hash,解决 CDN 缓存问题
  3. 通过 grunt.registerTask 把 4 个子任务封装成 “i18n” 命令,研发同学只需执行 grunt i18n 即可,降低心智负担
  4. 若公司 Jenkins 采用 Node 16 镜像,需在 package.json 里锁定 "grunt-jsonlint": "^2.1.3",该版本已移除旧版 lodash 漏洞,过安全扫描

拓展思考

  1. 渐进迁移:如果团队正从 Grunt 迁往 Vite/Rollup,可把 grunt-jsonlint 独立成 lint-staged 钩子,在 pre-commit 阶段只校验本次提交的 JSON,不再阻塞主构建,实现 “双轨并行”
  2. 多环境差异化:预发环境允许 warning 级别非致命错误,但生产环境必须 strict=true。可在 Gruntfile 里读取 process.env.BUILD_ENV,动态写入 jsonlint.options.format一套代码适配多环境卡点
  3. 压缩率监控:在 CI 末端追加 grunt-bytesize,把 dist/i18n 总体积上报到 阿里 SLS 或腾讯 CLS连续三次构建体积增长 >5% 自动告警,防止翻译团队误传高清图或冗余字段。
  4. 回滚策略:利用 filerev.summary 生成的 manifest.json,在 Node 服务端做 “灰度 5% → 50% → 100%” 阶梯发布;一旦错误率飙升,通过配置中心秒级回滚到上一版 hash无需重新打包