作者:qiangwu,腾讯CSIG Web开发工程师
i18n-helper-cli 是一个 Web 国际化整体解决方案,包含自动包裹词条,提取词条, 翻译词条,词条翻译统计,节省人力预估统计,网页多语言显示异常检测(Coming soon)等功能。可以大大减低开发,测试,翻译各个角色的人力成本,减少重复劳动,低级错误。
简单来说可以分为以下 5 个步骤
通过上面 5 步,可以完成站点国际化。大多数场景大家就是这么做的,但这里充斥着大量人工劳动,大量人工劳动意味着重复低效,出错几率提高。让我们从以下三个阶段分析下这些问题
所以这里最大的问题是上面这些工作都需人工操作,问题清楚了,那接下来我们要做的就是把这些人工操作能够交给机器,实现自动化,提高效率,降低出错几率。
先上结论,i18n-helper-cli 可以很好的解决上述问题。
接下来我们详细分析下词条包裹的方案。我们要实现的是类似你好 => t('你好'),所以:
哈哈,刚说的就像网上的经典问题: 如何把大象放到冰箱?
回答:先打冰箱门,然后把大象放进去,在关上冰箱门
听起来没问题,好像很有道理的样子,但没有任何实际价值。言归正传,我们来探讨下实际解决方案:
针对匹配到中文,这里我们第一个想法应该就是正则表达式了。/[u4e00-u9fa5]可以匹配中文。至此,我们完成了第一步找到需要包裹的文字。接下来就是把包裹上了'你好'.replace(/([u4e00-u9fa5]+)/gi,'t('$1')'),搞定。慢着,真的这么简单吗,想想我们真实代码的情况,注释,各种复杂的模板字符串,换行等等。这意味着这个正则到后面会巨复杂,到后面会面对一个晦涩难懂,难于维护的正则表达式。
另外还有个很蛋疼的事情,比方说我们的调试,上报等等代码,如console.log('不需要提取'),怎么办?感觉脑子不够用了。
我们希望只匹配我们想要的词条。比如下如下代码,我们预期匹配你好(注释和 console.log 的里的都不需要),如果有个方式只给到我们你好,然后我再判断它是不是包含中文,再包裹就再好不过了。
// 这是一段注释
const word = '你好';
console.log('世界');
有没有这样的好事?答案是还真有。是时候上这张神图了
Babel 的工作流程主要分为以下 3 个阶段:
这里我们着重说下Transform阶段,AST 处理的核心要素:
通过上述要素,我们既可以完成对 AST 的修改。下面我们看下这里的核心代码:
return {
visitor: {
StringLiteral(path: NodePath<tt.StringLiteral>) {
let { value } = path.node;
value = replaceLineBreak(value);
if (needWrap(wrapCharacter, value)) {
let newNode = t.CallExpression(t.Identifier(T_WRAppER), [
combine(value),
]);
path.replaceWith(newNode);
}
},
CallExpression(path: NodePath<tt.CallExpression>) {
switch (path.node.callee.type) {
case 'MemberExpression': {
const excludeFuncName = i18nConf.parsedExcludeWrapperFuncName;
if (excludeFuncName.length > 0) {
const names: string[] = [];
const me = path.node.callee as tt.MemberExpression;
getName(me, names);
const MEName = names.reverse().join('.');
if (excludeFuncName.includes(MEName)) {
path.skip();
}
}
break;
}
default:
break;
}
},
}
针对上面我们诉求的例子,当我们得到 AST 后
至此,我们即可完成我们的诉求,完美的对符合我们需要的词条就行包裹。
通过上面 AST 的方案,我们可以看得出这里的功能很强大,业界eslint,prettier,webpack等等都是通过对源码进行分析,转换,生成实现各种各样的功能。
我们可以开发自己的插件,去做各种有意思的事情,比如说代码埋点,国际化方案等等。看到这里我想大家一定会有个问题:
答:https://astexplorer.net/
2.另外这些类型如何构造新的节点?
答:https://babeljs.io/docs/en/babel-types
请参考 example
注意:请确保 Nodejs 版本大于 14!!!
# npm 安装
npm install i18n-helper-cli -D
# yarn 安装
yarn add i18n-helper-cli —dev
# 交互式命令行
i18n-helper init
# 生成默认配置文件,具体参见【配置说明】( 推荐大家用这个哈,交互方式的的后面加了不少配置还没来得及补齐)
i18n-helper init -y
# 包裹 & 提取 & 翻译 & 统计 i18n.config.json 中 srcPath 文件中的中文词条
i18n-helper scan -wetc
# cli 默认为中文,支持语言切换,目前支持zh & en
i18n-helper switch en
# 包裹 & 提取 & 翻译 & 统计 i18n.config.json 中 srcPath 文件中的中文词条
# w:wrap e:extract t:translate tm: translate machine c:count
# l:language
# 这 5 个操作可以随意组合 e.g. i18n-helper scan -we 则只会翻译 & 提取
i18n-helper scan -wetc
i18n-helper scan -we -tm -c
# 包裹 & 提取 & 翻译 & 统计 指定路径,指定语言内符合规则的词条
# e.g i18n-helper scan -wetc -l en ./src/test/index.js
i18n-helper scan -wetc -l [language] [filepath]
i18n-helper scan -we -tm -c -l [language] [filepath]
# 包裹 i18n.config.json 中 srcPath 文件中的中文词条
i18n-helper wrap
i18n-helper scan -w
# 包裹指定文件中的中文词条
i18n-helper wrap [filepath]
i18n-helper scan -w [filepath]
# 提取 i18n.config.json 中 srcPath 文件中的中文词条到所有配置语言文件
i18n-helper extract
i18n-helper scan -e
# 提取指定文件中文词条到指定语言文件
# e.g i18n-helper extract -l en ./src/test/index.js
i18n-helper extract -l [language] [filepath]
i18n-helper scan -e -l [language] [filepath]
# 翻译 i18n.config.json 中配置翻译文件词条, -m 腾讯翻译君机器翻译
# 从翻译源文件文件中翻译
i18n-helper translate
i18n-helper scan -t
# 腾讯翻译君自动翻译
i18n-helper translate -m
i18n-helper scan -tm
# 翻译指定语言
# 从翻译源文件文件中翻译
i18n-helper translate [language]
i18n-helper scan -t -l [language]
# 腾讯翻译君自动翻译指定语言文件
i18n-helper translate -m [language]
i18n-helper scan -tm -l [language]
# 统计 i18n.config.json 中翻译文件的翻译情况
i18n-helper count
i18n-helper scan -c
# 统计指定语言翻译文件的翻译情况,多个语言用,分隔
i18n-helper count [language]
i18n-helper scan -c -l [language]
module.exports = {
// cli 语言
cliLang: 'zh',
// 项目类型:react | vue | js
projectType: '[react]',
// 默认包裹和提取词条的目录
srcPath: './',
// 扫描文件格式
fileExt: 'js,ts,tsx',
// 包裹的字符集,下面是中文
wrapCharacter: '[u4e00-u9fa5]',
// 包裹词条的名字
wrapperFuncName: 't',
// 忽略掉包裹的方法,多个用,分隔
excludeWrapperFuncName: 'console.log,console.error',
// jsx中的文字包裹方式,true用<trans></trans>, false用【wrapperFuncName】的value包裹
jsx2Trans: false,
// 当文件需要翻译时引入的文件
importStr: `import {t} from './i18n;';n`,
// 排除目录,此目录下的不会不会执行包裹和提取词条操作
exclude: 'node_modules,dist,git',
// 翻译词条目录
localeDir: './locales',
// 翻译语种
languages: 'zh,en',
// 源语言
sourceLanguage: 'zh',
// 翻译词条文件名
transFileName: 'translation',
// 翻译词条文件格式: json, po
transFileExt: 'json',
// 翻译词库目录(自动翻译目录)
targetTransDir: './translations',
// 翻译词库文件名
targetTransFile: 'sourceTranslation.json',
// 腾讯云 secretId
secretId: '',
// 腾讯云 secretKey
secretKey: '',
};
https://github.com/wuqiang1985/i18n-helper
https://www.npmjs.com/package/i18n-helper-cli
目前还在完善中,欢迎大家试用,大家有问题可以提 issue。