Vue项目中需要用到国际化,使用的是vue-i18n插件,一般我们在页面中使用是通过 如下方式:
<div>中文</div> 原文
<div>{{$t('key')}}</div> 使用国际化后
这样使用国际化,有个缺点页面中可读性比较差,我们在阅读代码时不知道$t('key')代表什么意思。于是便开始思考如何去解决这个问题,最终决定使用 webpack loader来处理转换代码原文,达到代码中还是照常写中文,国际化功能照常使用的目的。
主要的思路是:维护一个中文和国际化key的映射文件,匹配src下.vue和js文件中的中文,替换成$t方法。主要用到string.replace方法和正则表达式。由于正则表达式不是很好,弄得也是磕磕碰碰,不过最终还是满足了项目的需要,做完一大收获是正则的使用提高了那么一些。
我把页面需要国际化处理的页面分为4个场景,如下:
1.模板标签内部: <tag> 中文 </tag> => 中文=> {{$t('key')}}
2.模板标签上的属性中文 如placeholder="中文" => :placeholder="$t(key)"
3.模板内部 {{ 中文}} => {{ $t('key')}}
4.export default 之内的中文 => this.$t('key)
loader接受的第一个参数是文件的原文字符串,根据上面的场景 定义不同的正则 用replace来处理。loader代码如下:
var loaderUtils = require("loader-utils");
// 目前支持Vue文件国际化5种情况:
// 1.模板标签内部: <tag> 中文 </tag> => 中文=> {{$t('key')}}
// 2.模板标签上的属性中文 如placeholder="中文" => :placeholder="$t(key)"
// 3. export default 之内的中文 => this.$t('key)
// 4. 模板内部 {{ 中文}} => {{ $t('key')}}
module.exports = function(source) {
const opts = loaderUtils.getOptions(this) || {};
const { keyMaps } = opts;
if (!keyMaps) {
return source;
}
let newsource = source;
Object.keys(keyMaps).forEach(key => {
//$t(key) 返回的值是key表明翻译失败 不相等表明翻译成功
//1.模板标签内部: <tag> 中文 </tag> => 中文=> {{$t('key')}}
const tagReg = new RegExp(
`(<template>(.|\n|\r)*/*>\\s*)${key}(((:|:)?(\\{\\{.+\\}\\})?)\\s*</*(.|\n|\r)*</template>)`,
"g"
);
while (newsource.match(tagReg)) {
newsource = newsource.replace(
tagReg,
`$1{{ $t('${keyMaps[key]}') !== '${keyMaps[key]}' ? $t('${keyMaps[key]}') : '${key}'}}$3`
);
}
//2.模板标签上的属性中文 如placeholder="中文" => :placeholder="$t(key)"
const tagPropertyReg = new RegExp(
`(<template>(.|\n|\r)*)\\s+([a-zA-Z\\-]+)="${key}:*\\s*("(.|\n|\r)*</template>)`,
"g"
);
while (newsource.match(tagPropertyReg)) {
newsource = newsource.replace(
tagPropertyReg,
`$1 :$3=" $t('${keyMaps[key]}') !== '${keyMaps[key]}' ? $t('${keyMaps[key]}') : '${key}' $4`
);
}
//3. export default 之内的中文
const exportDefaultReg = new RegExp(
`(export\\s*default\\s*{(.|\n|\r)*(?=(data\\s*\\(\\))|computed)((?!<style).|\n|\r)*)"${key}"`,
"g"
);
while (newsource.match(exportDefaultReg)) {
//一个文件中可能存在多个相同的中文 所以用了while语句
newsource = newsource.replace(
exportDefaultReg,
`$1 (this.$t('${keyMaps[key]}') !== '${keyMaps[key]}' ? this.$t('${keyMaps[key]}') : '${key}')`
);
}
//4. 模板内部 {{ 中文}} => {{ $t('key')}}
const templateBracketReg = new RegExp(
`(<template>(.|\n|\r)*\\{\\{(.|\n|\r)*)"${key}"((.|\n|\r)*\\}\\}(.|\n|\r)*</template>)`,
"g"
);
newsource = newsource.replace(
templateBracketReg,
`$1 $t('${keyMaps[key]}') !== '${keyMaps[key]}' ? $t('${keyMaps[key]}') : '${key}' $4`
);
});
return newsource;
};
映射文件如下:
{
"登录": "04-002-002.1.1",
"请输入验证码": "04-002-002.1.2",
"新增分类": "04-002-002.2.1",
"请输入分类名称": "04-002-002.2.2"
}
这个loader也已发布npm包,vue-i18n-webpack-loader,详情可点击查看。
用了这个loader之后会增加打包编译的时间 可以通过 thread-loader,cache-loader来优化
实现得比较粗糙,有什么问题欢迎留言讨论。