文章首发于飞书知识库:极简化前端 - 为什么我要重新写一个国际化提取脚本 Code-Extractor
最近入职以后的第一份内容是完成项目的国际化工作,为此写了一个 Code-Extractor
工具。
仓库地址是:github.com/Wetoria/Cod...
说明:
最新代码我并没有提交上去,因为还有些内容正在调整当中。
未来什么时候有心情把代码优化完了,我再推上去。
这篇文章算是用来记录一些,在这个过程中思考及实现思路。
为什么我要重新写一个提取脚本
原因:我不信任任何一个现有的库脚本。
我测试过几个包括库,它们的提取逻辑,目测只是简单的搜索项目中的中文并进行提取。
无论是哪个库,在以下几个点上,或多或少的存在我认为不够好的地方。
-
哪些文件里包含了中文?
-
提取出来的内容是哪个文件里的、哪部分内容,提取结果是否正确?
-
格式化后的结果是什么?是否正确?
-
提取出来的内容,如何方便的查找到对应的翻译结果?
并且有些地方,我觉得可以有更好的做法,并且我也实现了部分。
注:
当我编写这篇文章的时候,离我开发这个脚本,已经过去了几周时间。
许多细节我不打算再重新测试并确认,仅凭印象编写的该文章。
哪些文件里包含了中文?
在这个问题上,我在使用 kiwi-cli
的时候,有显示在哪个文件中提取出来了中文,但是仅仅显示了文件路径。
而我在这一点上,增加了 行
和 列
的提取,然后利用 VS Code 的路径跳转功能,如果你是在 VSC 的终端里执行脚本,那么你可以利用 opt + click
跳转到精确位置。
你可以按照以下步骤进行测试:
- 用 VSC 随便打开一个项目,并复制一个文件的路径。
- 然后在路径后面拼上
:行号:列号
- 在 VSC 的终端里输入
echo [上一步的路径]
,你就可以用opt + click
跳转了。
这算是一个国际化提取脚本的基操,我只是加了一个小优化。
接着看提取部分。
提取出来的内容是哪个文件里的、哪部分内容,提取结果是否正确?
这里直接展示一下结果吧。
先是对一些注释内容进行过滤,然后利用终端输出的格式化语法,对命中的内容进行高亮,并显示对应的文件位置信息。
如果出现了提取错误的地方,可以对提取规则进行调整,或者是快速跳转到文件的位置对文件内容进行调整。两者皆可。
之所以这样做的原因是。
项目已经开发过一段时间了,提取的结果肯定足够多。
利用 kiwi 这样的工具,虽然也能在提取完成以后,利用 git diff 的对比,对提取结果进行确认,但是整个确认工作还是稍显麻烦的。
PS:写的时候,想起来终端里用 diff 直接查看的话,可以类似图中这样的展示效果。
格式化后的结果是什么?是否正确?
先不说结果是否正确,光是一个个确认提取的地方,工作量已经足够多了。
提取出来的内容,如何方便的查找到对应的翻译结果?
我测试的几个库,提取过程都类似,先是 init 初始化配置,然后进行提取得到配置文件。
并且有些类库增加了 VS Code 插件的支持,但是在使用上给我一种很蠢的感觉。没错,就是蠢。
比如,提示文件中是否有未提取的中文。
那我是不是要打开每个文件确认才能知道?
比如,kiwi 的提取后的key,采用 文件路径 + 翻译结果 的形式,也就是 ppath.spath.file.key
。
并且说是提供了搜索功能,快速查看翻译结果,但是光复制粘贴和输入这段 key,就已经足够麻烦了。
这里我在调研飞书的做法时,想起来下划线是个好东西,所以最后我用的是 path_filename_中文内容
的方式,作为配置文件的 key。
好处是,中文直接能知道这里是什么意思。
如果需要确认翻译结果,双击 key,即可选中复制,然后打开对应语言的配置文件,进行查找即可。
当然,像 kiwi 等插件的做法,增加一个插件读取并展示也可以。只不过没有接触过 VSC 的插件开发,所以暂不考虑。
其他几个我考虑的地方
因为我完成这个 React 项目的国际化提取工作以后,可能还需要完成另一个 Vue 项目的国际化。
所以我这一点上我也需要留意。
然后这些提取脚本,基本上都是接入了第三方库,这一点上也让我特别难受。
我目前除了对接百度翻译需要用到 MD5 加密,引入了 crypto,其余的只用到了 js 和 nodejs 环境。
因为本质上就是一个文本提取并替换的功能,为此顺便把正则半通关了一遍。
几个知识点
key 需要文件路径 + key 的格式
虽然绝大多数时候,使用中文作为 key 就已经足够,但是有些页面上,虽然中文内容相同,但是翻译后的结果需要调整。
比如"周一",有的页面需要显示 Monday,有的页面可能只需要显示 Mon。
终端输出彩色字
用 \x1b[色号m
就可以在输出的时候带颜色了。
VS Code 路径跳转
前面提过了。opt + click
进行,前提是路径规则对了,并且是在 VSC 的终端里。
系统自带终端也有办法,只不过不能用这个快捷键。
文件读写
基本的按行读写文件,然后进行处理,最后整个文件清空再写入即可。
正则
我是按正则进行匹配的,而不是像其他库一样,用 Babel 转 AST 然后再处理,我觉得没必要。
哦对了,学正则的时候,看了菜鸟的解释,我只想说,说的真垃圾,真不容易懂。
JS 中,方法的 name
为了让我开发起来的时候更方便,写了个链式调用的方法。
为了指示正在运行的方法,我在上面的方法里加了 func.name
的输出。
这里就有意思了。
如果你是用 const func = () => {}
声明的方法,那么 func
就是 name
。
但是有些方法,有很多相同的逻辑,所以我做了包装,比如像下面这样:
javascript
function wrapper(fn) {
return (...args) => {
// do sth like console.log
const result = fn(...args)
// record result
return result
}
}
这样包装了以后,返回的是一个箭头函数,它不带 name
。
前面的 func.name
输出的时候,值是 undefined
。
为了解决这个问题,我创建了下面这样的方法。
javascript
function getNamedFunction(key, fn = () => {}) {
const coloredKey = `${key}`
const obj = {
[coloredKey]: (...args) => {
return fn(...args)
}
}
return obj[coloredKey]
}
利用上面的方法,我可以给方法配置一个名称,比如:
输入的时候,会得到这样的结果:
任意增加"中间件"
在某个过程前后想插入方法的时候,直接写个方法,然后再进行处理就好。
比如下图中的方法列表,删除哪个方法,那就不会执行相应的逻辑。
差不多就这些吧。
一进来就搞这个国际化,我是觉得挺好玩的。
正则半通关、玩了终端"高亮"、文件读写替换等等。