一个插件,一键搞定i18n国际化语言配置

2,581 阅读3分钟

好久没上掘金了,之前一直在忙着新业务,没时间(其实自己清楚这些都只是借口,就是犯懒了),今天又带来了一个小插件,用于提取项目中所有的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()
    })
}

翻译中文json文件为英文

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了,苦中作乐,哈哈。