使用 grunt-i18n-extract 扫描代码中的翻译标记

解读

在国内前端团队的日常迭代中,国际化(i18n)往往被“后置”:需求上线前才发现硬编码中文散落在各组件里,手工搜集不仅耗时,还容易漏掉模板字符串、动态 key 或 JSX 属性。grunt-i18n-extract 正是 Grunt 生态里专门“把代码里的翻译标记一次性扫出来”的插件,它通过静态 AST 分析,把符合规则的调用(如 i18n('key')$t('key'))提取成 JSON/PO/CSV 等格式,供翻译平台或运营同学直接接手。面试时,考官想确认的是:你不仅知道怎么配任务,还能解决真实业务中的冲突、性能、增量扫描、多语言分支合并等痛点,而不是跑通 demo 就结束。

知识点

  1. grunt-i18n-extract 核心配置项src(支持 glob 数组,如 ['src/**/*.{js,vue}'])、dest(输出目录)、marker(正则/函数,匹配翻译函数名)、defaultLang(默认语言,国内一般 zh-CN)、module(输出格式 json|po|csv)、flatten(是否拍平 key 路径)、namespace(防止 key 冲突的前缀)。
  2. AST 与正则双策略:插件默认用正则快速提取,遇到 ES6 模板字符串、TS 类型、Vue SFC 时自动回退到Babel AST,保证 <template>{{ $t(order.status.${type}) }}</template> 也能被正确捕获。
  3. 增量扫描:通过 grunt-newer 组合,先对比 git diff 或文件 mtime,只扫描变更文件,10 万行代码项目也能 2 秒内完成提取
  4. 冲突消解:同一 key 在不同文件出现不同默认值时,插件会生成 warning.logCI 阶段可配置 failOnWarning=true 强制阻断合并,防止“中文覆盖英文”。
  5. 多分支合并:利用 grunt-merge-i18n(社区二次封装)把 feature 分支提取出的 zh-CN.json 自动合并到主干,支持运营锁 Key,已翻译条目不会被覆盖。
  6. 国内常见坑
    • 微信小程序 wxs 标签被误识别为 JS,需在 src 里加 !**/*.wxs 排除;
    • Ant Design ProformatMessage 被 TreeShaking 后变量名压缩,需把 marker 写成 /\bformatMessage\s*\(\s*['"]([^'"]+)['"]/g`;
    • 服务端渲染项目,提取阶段没有 window,需在 Gruntfile 里加 node: true 环境变量,防止插件报 navigator is not defined 而中断。

答案

  1. 安装
    npm i -D grunt-i18n-extract
    
  2. Gruntfile.js 最小可运行配置
    module.exports = function(grunt) {
      grunt.initConfig({
        i18n_extract: {
          options: {
            marker: /(?<=\$t\(|i18n\(|formatMessage\()\s*['"`]([^'"`]+)['"`]/g,
            module: 'json',
            defaultLang: 'zh-CN',
            dest: 'locales',
            flatten: false,
            namespace: false,
            failOnWarning: true   // 国内 CI 强制质量门禁
          },
          src: ['src/**/*.{js,vue,ts,tsx}'],
        }
      });
      grunt.loadNpmTasks('grunt-i18n-extract');
      grunt.registerTask('i18n', ['newer:i18n_extract']); // 增量
    };
    
  3. 运行
    grunt i18n
    
    输出目录 locales/zh-CN.json 示例:
    {
      "order.status.waitPay": "待支付",
      "order.status.done": "已完成"
    }
    
  4. 接入翻译平台(以腾讯 TMT 为例)
    • zh-CN.json push 到企业微信机器人,运营同学在线翻译后回传 en-US.json
    • grunt-contrib-copy 里加一条任务,把回传文件直接覆盖 src/locales/lang无需手动拷贝

拓展思考

  1. 性能极限:当项目膨胀到 2000+ 页面、50 万行代码时,正则回溯会导致 CPU 飙到 100%。可临时关闭正则,强制走 @babel/parser + traverse,并开启 cacheDirectory: '.grunt/cache'二次扫描耗时从 90s 降到 8s
  2. key 规范治理:在 pre-commit 钩子中调用 grunt i18n,若发现 key 包含中文空格或特殊符号,自动阻断提交并提示“请用 camelCase”,从源头保证翻译平台能正确匹配。
  3. 微前端场景:主应用与多个子应用共用一套 locales。可在主应用 Gruntfile 里配置 i18n_extract:combine把各子应用产物合并成单文件,再上传到 CDN,避免运营重复翻译
  4. SSR 运行时注入:提取完成后,利用 grunt-replacewindow.__INITIAL_I18N__ = <%= zhCN %> 注入到 index.ejs首屏直接渲染中文,省去一次 Ajax 请求,在国内 4G 弱网环境可提升 200ms+。