1.1.1 • Published 6 months ago

jdy-intl-babel v1.1.1

Weekly downloads
-
License
MIT
Repository
-
Last release
6 months ago

babel 国际化插件

通用前端国际化转译工具,基于 babel v7

它能做什么?

它能识别泛 js 代码(js、ts、jsx、tsx、vue...)中的硬编码中文,参照一定的格式进行转换并保存识别到的中文。

  • 可以指定路径范围,支持 glob 模式解析路径。
  • 自定义国际化转译函数。
  • 指定中文提取包保存路径。
  • 性能优化,提供批量编译配置。

它没有做什么...

该库仅对 babel 插件做了两层封装,只做转换工作,对于其他工程化工具不做限制。在使用该工具的同时,项目中应该还要存在:

  1. 代码格式化工具(prettier 等):babel 在转换过程中并不会刻意保留源代码的格式,在转换后的代码格式会出现较大改动,请参考下面的示例对转换后的代码进行格式化后保存。
  2. (可选)提交流检查工具(lint-staged 等):该工具可集成到各种提交流工具中,方便提交时自动对修改处国际化。

安装

该插件目前位于公开npm源中,可直接执行任意安装命令

yarn add @jdy/intl-babel

随后安装依赖:(推荐 yarn or pnpm)

yarn install

使用

场景 1 - 项目整体国际化

// 这里以node的commonJs为例,使用esm请自行进行转换。
const fs = require("fs");
const path = require("path");
const prettier = require("prettier");
const { default: I18nPlugin, writeFileAsync } = require("@jdy/intl-babel");
// 常量
const projectPath = path.resolve(__dirname, "./"),
  prettierConfPath = path.join(projectPath, ".prettierrc"),
  saveJsonPath = path.join(projectPath, "lang", "zh_CN.json");
// prettier 配置项
const prettierConf = JSON.parse(
  fs.readFileSync(prettierConfPath, { encoding: "utf-8" })
);

I18nPlugin({
  //这里是解析路径的规则
  pathOption: {
    // 解析匹配到下面路径的文件
    pattern: ["src/**/*.{js,ts,tsx,jsx}"],
    // 传递给Glob的参数
    options: {
      // 忽略配置文件、文档文件、测试文件(默认不以./开头)
      ignore: ["src/**/*.{config,stories,test}.{js,ts,jsx,tsx}"],
    },
  },
  // 此次转换收集到的所有国际化字典的回调,需要自行执行保存
  dictCallback:(Intldict)=>saveJson(IntlDict).then(....)
  // 国际化转译相关配置
  i18nOptions: {
    // 插件配置
    pluginOptions: {
      /**
       * 翻译函数的导入形式,请使用alias形式的导入。以下是判断注入方式:
       * 1. 当前文件内不存在需要翻译的地方,不注入
       * 2. 当前文件内存在需要翻译的地方
       *  2.1 若已存在相同导入,则不再注入
       *  2.2 未导入(该文件第一次被翻译)则在文件首行注入
       */
      injectIntlImport: 'import  {getLangMsg}  from "@/utils/intl";',
      /**
       * 项目内中文的替换形式,请注意必须满足翻译函数(hash).默认值(code)的形式;
       * 其中hash是中文的md5值,code则是被替换的中文;
       * t是翻译库的翻译函数,d则是当t函数传参hash得到空字符串时的默认值(也用于帮助开发了解该文本内容)
       */
      getIntlFunc: (hash, sourceMsg) => `getLangMsg(${hash}).d(${sourceMsg})`,
    },
    // 默认为["jsx", "typescript"],当项目中用到dva等高阶HOC时使用下面的配置
    parserPlugins: ["jsx", "typescript", "decorators-legacy"],
  },
  callback: (dict) => {
    // 插件不会默认替换掉目标文件
    const { code, filePath } = dict;
    // 这里假设你使用prettier v3进行格式化代码
    return prettier
      .format(code, { ...prettierConf, filepath: filePath })
      .then((formatCode) => writeFileAsync(filePath, formatCode));
  },
}).then((dict) => {
  /** 在这里dict表示此次转换拿到的对应映射
   * dict 是一个对象,里面类似:
   * {
   *  "02ddbbf19885bc7": "已还款",
   *  "02ec202bc579881": "请输入",
   *  "041b02ca833756e": "事由",
   *  "049ca0b50228c4c": "预计还款日期:{arg0}",
   * }
   * 这里需要对一些情况进行说明:
   * 1. 当一份文件第一次被翻译时,可以拿到该文件内所有被翻译的映射
   * 2. 当该文件没有有关中文的改动再次被翻译时,返回的是空的对象
   * 3. 当该文件内有部分中文被改动,执行翻译后,拿到的是改动过后的映射对象。
   */
});

场景 2 - 接入提交流

首先新建一个文件 intl-plugin.js

// scripts/intl-plugin.js
const path = require("path");
const fs = require("fs");
const prettier = require("prettier");
const { default: I18nPlugin } = require("@jdy/intl-babel");
// 常量
const projectPath = path.resolve(__dirname, "../"),
  prettierConfPath = path.join(projectPath, ".prettierrc"),
  srcPath = path.join(projectPath, "src"),
  saveJsonPath = path.join(projectPath, "lang", "zh_CN.json");
// prettier 配置项
const prettierConf = JSON.parse(
  fs.readFileSync(prettierConfPath, { encoding: "utf-8" })
);
//获取命令行传递过来的参数
const args = process.argv.slice(2);
const fileList = args
  .map((filePath) => path.resolve(filePath))
  // 确保是在src内的文件,甚至可以更上一步,确保是tsx等类型文件
  .filter((filePath) => filePath.startsWith(srcPath));
I18nPlugin({
  pathOption: {
    pattern: fileList,
    options: {
      // 忽略配置文件、文档文件、测试文件
      ignore: ["src/**/*.{config,stories,test}.{js,ts,jsx,tsx}"],
    },
  },
  i18nOptions: {
    pluginOptions: {
      injectIntlImport: 'import {t} from "@/utils/intl"',
      getIntlFunc: (hash, code) => `t(${hash}).d(${code})`,
    },
  },
  callback: (dict) => {
    const { code, filePath } = dict;
    // 这里prettier可格式化可不格式化,如果你配置了提交自动格式化的话
    return prettier
      .format(code, { ...prettierConf, filepath: filePath })
      .then((formatCode) =>
        fs.writeFileSync(filePath, formatCode, { encoding: "utf-8" })
      );
  },
}).then(() => {
  // 这里格式化一下json文件
  const jsonData = fs.readFileSync(saveJsonPath, { encoding: "utf-8" });
  prettier
    .format(jsonData, { ...prettierConf, filepath: saveJsonPath })
    .then((formatCode) =>
      fs.writeFileSync(saveJsonPath, formatCode, { encoding: "utf-8" })
    );
});
// 这里假设你使用lint-staged作为提交流检查工具
//lintstagedrc.js
const handler18n = (filenames) =>
  `node ./scripts/intl-plugin.js ${filenames.join(" ")}`;
module.exports = {
  // 这里会按照顺序执行
  "*.{js,ts,tsx,md,json}": [
    handler18n,
    "git add ./lang/zh_CN.json",
    "prettier --write",
  ],
};

提交流注意事项

  1. 提交流不会删除 json 内不再使用的字段,只能新增字段。
  2. 插件本身会对 json 内字段进行排序(需确保在 es3 以上),尽量避免合并冲突。
  3. 若需要发布时对 json 进行 shaking,可参考以下方案:
  • 以 zh_CN 为准,删除 en_US 内多余的字段(en_US 只在 release 时更新)。
  • 以 en_US 为准,找出 zh_CN 内多余的字段,与上面提取的多余字段做差集。

编译提速

插件内实现了线程池,由于线程之间数据交换需要序列化和解析,请酌情使用。

I18nPlugin({
  threadOptions: {
    // 必须显示开启
    enable: true,
    // 请务必提供一个大于0的数
    num: 6,
  },
  //...其他配置项
});

或者可以使用 bun 替代 node,具体示例如下:

# 之前是这样执行的
node your-script.js
# 改成这样即可
bun your-script.js

参考案例1:销售助手(全面扫描 800+文件,提取 3700+词条)

配置 m1 pro 16gb

编译模式编译速度
普通编译 (node 16.20.0)13~15s
普通编译(node 20.11.0)10~11s
thread-6 (node 16.20.0)9.247s
thread-6(node 20.11.0)8.5s
bun 1.0.2910.9s
thread-6 & bun7.3s

参考案例2:pos-h5(全面扫描1000+文件,提取词条6600+词条)

配置 m1 pro 16gb

编译模式编译速度
普通编译 (node 16.20.0)21~25s
普通编译(node 20.11.0)19~21s
thread-6 (node 16.20.0)16~18.5s
thread-6(node 20.11.0)15~16s
bun 1.1.3018~21s
thread-6 & bun14~15s

调试

I18nPlugin({
  _debugger: {
    // 测速
    timer: true,
    // json相关操作结果打印
    console: true,
  },
  //...其他配置项
});

接入其他框架

  • 目前已支持vue2
1.1.1

6 months ago

1.1.0

8 months ago

1.0.2

8 months ago

1.0.1

8 months ago

1.0.0

8 months ago