基于AST实现一键自动提取&替换国际化文案

背景 :在调研 @formatjs/cli 使用(使用 @formatjs/cli 进行国际化文案自动提取 )过程中,发现有以下需求@formatjs/cli 无法满足:

  1. id 需要一定的语义化;
  2. defaultMessage和Id不能直接hash转换;
  3. 需要直接从中文转换为formatMessage
  4. 需要显式注入ID(个人觉得编译时注入还是反直觉了一点);

另外也是希望借助这个机会好好学一下AST相关知识,所以决定自己写一个AST转换工具。

*注意:工具无法满足脱离中文文案和文件名的语义化ID需求。

实现效果

如何使用

www.npmjs.com/package/cor...

安装

css 复制代码
npm i -g core-i18n-cli

CLI 参数

corei18n -i, --init

初始化项目,生成配置文件 corei18n.config.json,方便根据你的项目需求进行配置。

默认配置包括以下参数:

css 复制代码
export type ProjectConfig = {
  /** corei18n文件根目录,用于放置提取的langs文件 */
  corei18nDir: string;
  /** 导出的新增文案目录 */
  tempLangFile: string;
  /** 需要做国际化的文件目录 */
  path: string;
  /** 已有文案入口,用于过滤已经存在id的文案,支持js、ts、json */
  localLangFile?: string;
  /** 忽略的文件 string | string[],参考GlobOptions.ignore */
  ignoreFile?: GlobOptions["ignore"];

  /** 生成id的方式,默认为translate,需要提供baiduApiKey */
  idType: "translate" | "hash";
  /** 百度翻译开放平台配置,参考 https://fanyi-api.baidu.com/product/113 */
  baiduApiKey?: {
    appId: string;
    appKey: string;
  };
  /** 生成id前缀,会以.拼接在id前面 */
  idSuffix?: string;
  /** 替换后是否保留DefaultMessage,默认为false */
  keepDefaultMessage?: boolean;
  /** 格式化代码的选项,参考prettier.options */
  prettierOptions?: Options;
};

例子:

json 复制代码
{
  "corei18nDir": "./.corei18n",
  "tempLangFile": "./.corei18n/tempLang.json",
  "path": "src/pages/**/*.{ts,js,jsx,tsx}",
  "localLangFile": "src/locales/zh-CN.ts",
  "ignoreFile": "src/pages/**/*.d.ts",
  "baiduApiKey": {
    "appId": "",
    "appKey": ""
  },
  "keepDefaultMessage": false,
  "idType": "hash",
  "idSuffix": "tools",
  "prettierOptions": {
    "parser": "typescript",
    "printWidth": 80,
    "singleQuote": true,
    "trailingComma": "all",
    "proseWrap": "never"
  }
}

corei18n -s, --scan

一键扫描指定文件夹下的所有中文文案,新增文案会存放至tempLangFile

corei18n -r, --replace

一键替换指定文件夹下的所有中文文案


实现过程

关于AST

AST explorer:astexplorer.net/

AST(抽象语法树)是源代码的抽象表示形式,它捕捉了代码的结构,而不关心具体的字符格式。AST是在编译器设计和解析源代码时常见的一种数据结构。

在编程语言的编译过程中,源代码首先被解析器解析成一种称为AST的中间表示。AST反映了代码的语法结构,每个节点代表代码中的一个结构元素,如表达式、语句、函数、变量等。这种树状结构使得程序的结构和语法可以被更容易地分析和处理。

操作流程

scan 阶段

  1. 根据pathignoreFile得到所有目标文件
  2. 对于每个文件,读取文件内容,将代码转换为AST
  3. 遍历AST节点,若是StringLiteral或者JSXText,判断是否符合要求(包含中文且不属于default Message),如果是则记录下来
  4. 过滤得到所有新增文案并生成id
  5. 将新增文案导出到目标文件

replace 阶段

  1. 根据pathignoreFile得到所有目标文件
  2. 获取所有文案对;
  3. 对于每个文件,读取文件内容,将代码转换为AST
  4. 遍历AST节点,若是StringLiteral或者JSXText,判断是否符合要求(包含中文且不属于default Message),如果是则替换当前AST节点;
  5. 使用prettier进行格式化;
  6. 根据AST生成代码写入文件路径;

依赖的npm包

babel

  1. @babel/core:负责整个编译过程的调度和控制;
  2. @babel/parser:用于将 JavaScript 源代码解析成抽象语法树(AST);
  3. @babel/traverse:用于遍历和修改 AST 的工具;
  4. @babel/types:用于创建、检查和修改 AST 节点

cli相关

  1. commander:解析命令行参数和生成帮助信息;
  2. inquirer:交互式命令行工具,用于收集用户输入;
  3. glob:匹配文件路径
  4. lodash:工具库
  5. prettier:代码格式化

遇到的问题

解决babel/generater生成中文等特殊字符被转义为Unicode编码

c 复制代码
 const newCode = generator.default(
    ast,
    { retainLines: true, jsescOption: { minimal: true } }, // add this
    code
  ).code;

Error ERR_REQUIRE_ESM: require() of ES Module

json 复制代码
// tsconfig
{
  "compilerOptions": {
    "module": "esnext",
    "target": "esnext",
    "moduleResolution": "node",
  }
}
json 复制代码
// package.json
{
    "type": "module"
}

Error ERR_MODULE_NOT_FOUND: Cannot find module

github.com/microsoft/T...

stackoverflow.com/questions/6...

原因:tsc输出时不会添加文件拓展名,nodejs运行时不会自动匹配文件拓展名

尝试在文件首行添加 --experimental-specifier-resolution=node 无效

使用tsc-alias为导出文件添加js后缀后解决:

csharp 复制代码
npm install --save-dev tsc-alias
json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    ...
  },
  "tsc-alias": {
    "resolveFullPaths": true,
    "verbose": false
  }
}
json 复制代码
"scripts": {
    "compile": "tsc && tsc-alias"
}

参考

相关推荐
Avan_菜菜7 小时前
AI 能写代码了,为什么我反而开始要求它先写文档?
前端·github·ai编程
爱勇宝11 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒14 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen14 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
牧艺15 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
红尘散仙16 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust
袋鼠云数栈UED团队16 小时前
一套 Spec-First 的 AI 编程工作流
前端·人工智能
袋鼠云数栈前端16 小时前
一套 Spec-First 的 AI 编程工作流
前端·ai+
angerdream16 小时前
Android手把手编写儿童手机远程监控App之vue3 路由守卫
前端
不服老的小黑哥17 小时前
AI规范驱动编程-harness工程项目实战
前端