在多语言的项目中,我们会遇到一个挺头疼的问题,那就是如何良好的去实现多语言的翻译,今天恰好写了个脚本,来总结一下,希望能帮到有需要的人~
1.新建一个工具类项目
先新建一个项目,取名为 translateTools,然后进行npm初始化
mkdir translateTools
cd translateTools
npm init -y
接着我们在根目录下,准备一个en.js文件,也就是需要进行翻译的源文件。
可以看到js中的结构,是各种嵌套的对象组合起来的,在实际的项目中,我们也不得不这样去写,这样才方便以后的维护。那么问题来了,如何将后面的英文 value,给批量翻译成我们想要的语言呢?
2. 良好的解决方案
首先,我们想一个事情,如果按照递归的方式去遍历这个对象,给对象最后的value调用翻译的接口,这种方案行不行。答案是可行的,但缺点是特别明显的,就是会阻塞。那么我们如何做到,每个翻译都不会发生阻塞呢?以下我分为几步来进行:
1. 先获取对象中所有value值的完整路径
2. 书写深拷贝,对象取值和设置值相关函数
当然了,我们还不能够对引入的en.js文件进行直接的修改,需要深拷贝一份,因为我们知道我们的目标数据比较简单且明确,所以可以先实现一个简易版的深拷贝。
然后书写对象取值和设置值的函数~
3.结合async模块,goolgle translate模块进行最后的操作
async模块能够更好地帮助我们去实现异步的操作,详见 npm地址
npm i async
import fs from 'fs'
import async from 'async'
import enFile from './locales/en.js'
import translate from '@imlinhanchao/google-translate-api'
// // 需要翻译的语种
const languageList = ['zh-cn','vi']
// 获取所有节点的完整路径
const allPaths = []
const visitNodes = (obj,stack = []) => {
if (typeof obj === 'object' && obj !== null ) {
for (let key in obj) {
visitNodes(obj[key],[...stack, key]);
}
} else {
allPaths.push(stack)
}
}
visitNodes(enFile)
async.map(languageList,(language,done)=>{
console.log('language: ', language);
const originData = deepClone(enFile) // import enFile from './locales/en.js'
function getPathValue(arr){
let node = originData
for (var i = 0; i < arr.length; i ++) {
var key = arr[i];
node = node[key];
}
return node;
}
function setPathValue(arr,value){
let node = originData;
for (var i = 0; i < arr.length - 1; i ++) {
var key = arr[i];
node = node[key];
}
node[arr[i]] = value;
return value;
}
async.map(allPaths,(path,done)=>{
translate(getPathValue(path),{to: language}).then(res=>{
setPathValue(path,res.text)
done()
})
},()=>{
fs.writeFile(`./output/${language}.js`,`export default ${JSON.stringify(originData)}`, function (err) {
if (err) throw err;
console.log(`${language}已经翻译成功`)
})
})
})
// 简易版深拷贝
function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
4.翻译结果
3.小结
因为该问题在网上的解决方案较少,我偏偏就喜欢写一些其他人少写的东西,这样才有意思。欢迎大佬们一起交流~