好久没上掘金了,之前一直在忙着新业务,没时间(其实自己清楚这些都只是借口,就是犯懒了),今天又带来了一个小插件,用于提取项目中所有的i18n语言文本并且翻译生成所有语种文件,如果觉得翻译接口翻的不好的可以手动修改,插件不会覆盖已有的翻译语言,完美兼容旧项目代码,希望可以提升些工作效率,早点下班,哈哈哈。
因为新项目全都是需要中英双语的,之前的同事都是自己手写如this.$t(“项目”)的形式,然后在中文和英文语言配置文件中写入语言配置,新需求下来,我手都快写断了,然而测试还在不停的报有些地方漏掉了没翻译,于是便做了个插件自动生成语言配置文件了,早点下班不香吗。
想看代码的可以直接前往pikaz-translate仓库
插件思路很简单,首先递归遍历需要翻译的目录,找出目录下面的所有文件,然后提取所有文件中的内容,通过正则匹配获取类似this.$t(""),i18n.t("")的i18n插件写法中的文本,然后生成json文件,并且通过翻译接口将中文json文件,翻译成英文json文件,话不多说,上代码。
const path = require('path')
const fs = require('fs')
// node执行路径
const dirPath = process.cwd()
// 语言json文件key
const langKey = []
/**
* @description: 文件夹遍历
* @param {content} content/文件夹路径
* @return {type}
*/
const fileTra = (content) => {
//根据文件路径读取文件,返回文件列表
return new Promise((resolve, reject) => {
fs.readdir(content, async function (err, files) {
if (err) {
console.warn(err)
reject(err)
} else {
//遍历读取到的文件列表
for (let i = 0; i < files.length; i++) {
//获取当前文件的绝对路径
const filedir = path.join(content, files[i])
//根据文件路径获取文件信息
const isFile = await fileRead(filedir)
// 如果是文件夹,递归遍历该文件夹下面的文件
if (!isFile) {
await fileTra(filedir)
} else {
// 读取文件
const fileContent = fs.readFileSync(filedir, 'utf-8')
// 提取i18n语言文字
const lang = getTranslateKey(fileContent)
lang.forEach((item) => {
if (langKey.indexOf(item) === -1 || item === '') {
langKey.push(item)
}
})
}
}
resolve(files)
}
})
})
}
/**
* @description: 匹配t('')或t("")里的内容
* @param {type}
* @return {type}
*/
const getTranslateKey = (source) => {
let result = []
const reg = /(\$|\.)t\((\'|\")([^\)\'\"]+)(\'|\")(,([^\)\'\"]+))?\)/gm
let matchKey
while ((matchKey = reg.exec(source))) {
result.push(matchKey[3])
}
return result
}
/**
* @description: 判断是文件还是文件夹
* @param {String} filedir/文件路径
* @return {type}
*/
const fileRead = (filedir) => {
return new Promise((resolve, reject) => {
fs.stat(filedir, function (err, stats) {
if (err) {
console.warn('获取文件stats失败')
reject(err)
} else {
//文件
const isFile = stats.isFile()
// 文件夹
const isDir = stats.isDirectory()
if (isFile) {
resolve(true)
}
if (isDir) {
resolve(false)
}
}
})
})
}
/**
* @description: 将中文json文件翻译写入英文json文件
* @param {String} zh/中文语言文件路径
* @param {String} en/英文语言文件路径
* @param {String} lang/语言种类,默认英文
* @return {type}
*/
const pikazI18nTranslate = async (zh, en, lang = 'en') => {
const zhPath = path.join(dirPath, zh)
let zhJson = fs.readFileSync(zhPath, 'utf-8')
zhJson = zhJson ? JSON.parse(zhJson) : {}
const enPath = path.join(dirPath, en)
let enJson = fs.readFileSync(enPath, 'utf-8')
enJson = enJson ? JSON.parse(enJson) : {}
const key = []
Object.keys(zhJson).forEach((k) => {
if (Object.keys(enJson).indexOf(k) === -1) {
key.push(k)
}
})
for (let i = 0; i < key.length; i++) {
const e = await translate(key[i], lang)
enJson[key[i]] = e
}
return new Promise((resolve, reject) => {
const err = fs.writeFileSync(enPath, JSON.stringify(enJson), 'utf8')
if (err) {
reject(err)
}
resolve()
})
}
const axios = require('axios')
/**
* @description: 翻译接口
* @param {String} zh/翻译文本
* @param {String} lang/翻译语种
* @param {String} i/请求次数,超过三次不再请求
* @return {type}
*/
const translate = (zh, lang) => {
return new Promise((resolve, reject) => {
axios
.get('http://fanyi.youdao.com/translate', {
params: {
doctype: 'json',
type: lang,
i: zh,
},
})
.then(async (res) => {
resolve(res.data.translateResult[0][0].tgt)
})
.catch((err) => {
reject(err)
})
})
}
/**
* @description: 将中文json文件翻译写入英文json文件
* @param {String} zh/中文语言文件路径
* @param {String} en/英文语言文件路径
* @param {String} lang/语言种类,默认英文
* @return {type}
*/
const pikazI18nTranslate = async (zh, en, lang = 'en') => {
const zhPath = path.join(dirPath, zh)
let zhJson = fs.readFileSync(zhPath, 'utf-8')
zhJson = zhJson ? JSON.parse(zhJson) : {}
const enPath = path.join(dirPath, en)
let enJson = fs.readFileSync(enPath, 'utf-8')
enJson = enJson ? JSON.parse(enJson) : {}
const key = []
Object.keys(zhJson).forEach((k) => {
if (Object.keys(enJson).indexOf(k) === -1) {
key.push(k)
}
})
for (let i = 0; i < key.length; i++) {
const e = await translate(key[i], lang)
enJson[key[i]] = e
}
return new Promise((resolve, reject) => {
const err = fs.writeFileSync(enPath, JSON.stringify(enJson), 'utf8')
if (err) {
reject(err)
}
resolve()
})
}
主要逻辑大致就是这样,需要注意的点有有几个:
第一:项目中的i18n语言文本最好使用中文,如this.$t(“使用中文”),这样语义化更好,也更方便查看项目以及生成语言配置文件,如果旧项目中使用的是英文,也不用担心,只需要调用提取文本的函数pikazI18nLang,将文本提取出来,再自己写入对应的中英文即可,当然,会比较麻烦,但是旧项目,也只能忍了。。。
第二:翻译接口使用的是公开的翻译接口,对于同一个ip请求频率有做限制,如果一次翻译不成功,可以试着执行第二次,或者开代理更换自己的ip,当然,想要追求更好的翻译质量,付费的翻译api当然是更好的了,可以fork本项目自己简单的改一下翻译函数即可。
在vscode编辑器中,打开用户片段
选择新建全局代码片段文件,起一个文件名比如i18n
在片段文件中写入
{
"Print to $t": {
"prefix": "t",
"body": [
"$$t('$1')",
],
"description": "Log output to $t"
}
}
使用效果如下,输入t,回车选择第一个代码片段即可输出$t(’’),配合本插件使用,就这个feel倍儿爽:
需提取的目标目录下文件中的i18n文本
在一个js文件中调用插件函数
执行该脚本文件
最后效果:
生成的中文json文件内容
生成的英文json文件内容
pikaz-translate
本插件已经上传至npm,直接搜索pikaz-translate即可,也有一如既往比较完善的文档,以及示例代码,使用起来也不难。
七夕了,祝愿大家七夕快乐,单身狗只能继续coding了,苦中作乐,哈哈。