某天正在疯狂CV
中,CV
着刚翻译好的多语言:左侧屏幕是代码,右侧屏幕是云文档。头从左转到右,又从右转到左,眼花手酸脖子酸,人都不好了😵。
同事瞅了一眼:卧槽,你咋还手动CV啊,写个脚本自动替换不香吗?
工作流程
- 写代码的时候,定义多语言的kye和value
- 复制到其他语言文件中
- 把需要翻译的文本,填到云文档的表格中
- 产品翻译
- 翻译好后,开始CV
手动的时候就是这么个流程,我们主要是把第5步通过脚本实现。
- 下载表格
- 读取表格,数据转成需要的格式
- 读取多语言文件夹下的文件
- 文本替换
- 重新写入
替换
读取表格
读取表格,使用的是xlsx
,做过读取表格、导出表格的应该都使用过或听说过这个库,表格内容:
mjs
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import XLSX from 'xlsx'
// 表头和文件名,根据实际情况定义
const langMap = {
中文: 'zh',
英文: 'en',
葡萄牙语: 'pt',
法语: 'fr',
德语: 'de',
西班牙语: 'es',
荷兰语: 'nl',
中文繁体: 'zh-TW',
意大利语: 'it',
日语: 'ja',
俄语: 'ru',
波兰语: 'pl',
}
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// 翻译文档地址
const excelPath = path.resolve(__dirname, '翻译文档.xlsx')
const langData = sheetToJson(excelPath)
const zhLang = Object.keys(langData)
// 读取表格文件
function sheetToJson(filePath) {
const workbook = XLSX.readFile(filePath)
// 获取工作表名称
const sheetName = workbook.SheetNames[0] // 获取第一个工作表的名称,通常有多个版本的多张表
const worksheet = workbook.Sheets[sheetName] // 获取工作表
// 将工作表转换为JSON格式
const data = XLSX.utils.sheet_to_json(worksheet)
return formatJson(data)
}
function formatJson(data) {
let json = {}
data.forEach(item => {
let o = {}
Object.entries(item).forEach(([key, value]) => {
const lang = langMap[key]
if (lang) {
o[lang] = value
}
})
json[item['中文']] = o
})
return json
}
得到的数据格式,是这个样子:
文本替换
项目中多语言文件,用的ts文件,如果是json文件,其实是一样的
mjs
//...
// 多语言文件目录(修改为你的)
const langDirectory = 'E:/project-demo/src/lang'
readJsonFilesInDirectory(langDirectory)
function readJsonFilesInDirectory(directoryPath) {
fs.readdir(directoryPath, (err, files) => {
if (err) {
console.log('读取失败:', err)
return
}
files.forEach(file => {
const filePath = path.join(directoryPath, file)
fs.stat(filePath, (err, stats) => {
if (err) {
console.log(`${file}检查失败:`, err)
return
}
if (stats.isDirectory()) {
readJsonFilesInDirectory(filePath) // 递归处理子目录
} else if (['index.ts', 'zh.ts'].includes(file)) {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(`${file}读取失败:`, err)
return
}
// console.log(file) en.ts
const targetLang = file.split('.')[0]
try {
zhLang.forEach((zh, i) => {
const text = langData[zh][targetLang]
if (text) {
// 项目中,string用的singleQuote
data = data.replaceAll(`'${zh}'`, `'${text.replace(/(['"])/g, '\\$1')}'`)
}
})
fs.writeFile(filePath, data, 'utf8', err => {
if (err) {
console.error(`${file}写入失败:`, err)
return
}
console.log(`${file}完成翻译`)
})
} catch (error) {
console.error('出了点问题:', error)
}
})
}
})
})
})
}
结果:
文本是完全匹配的,引号也没有问题
自动添加
如果我们只想在zh.ts
文件中定义多语言,其他多语言文件自动添加上定义好的key、value
,我们的表格中就要加上一列关于key
的值:
我们把代码改下:
diff
// 翻译文档地址
const excelPath = path.resolve(__dirname, '翻译文档.xlsx')
const langData = sheetToJson(excelPath)
- const zhLang = Object.keys(langData)
+ const keys = Object.keys(langData)
diff
function formatJson(data) {
let json = {}
data.forEach(item => {
let o = {}
Object.entries(item).forEach(([key, value]) => {
const lang = langMap[key]
if (lang) {
o[lang] = value
}
})
- json[item['中文']] = o
+ json[item['KEY']] = o
})
return json
}
diff
function readJsonFilesInDirectory(directoryPath) {
// ...
try {
- zhLang.forEach((zh, i) => {
- const text = langData[zh][targetLang]
- if (text) {
- // 项目中,string用的singleQuote
- data = data.replaceAll(`'${zh}'`, `'${text.replace(/(['"])/g, '\\$1')}'`)
- }
- })
+ // 文件内容本身,是以 export defualt 开头的
+ // json文件的话,直接JSON.parse(data)就行了
+ const jsonObj = new Function('return ' + data.slice(15))()
+ keys.forEach(key => {
+ convertToNestedJSON(jsonObj, key, langData[key][targetLang])
+ })
+ data = 'export default ' + JSON.stringify(jsonObj, null, 2)
fs.writeFile(filePath, data, 'utf8', err => {
if (err) {
console.error(`${file}写入失败:`, err)
return
}
console.log(`${file}完成翻译`)
})
}
// ...
}
+ function convertToNestedJSON(nestedObject = {}, keys, value) {
+ const keysArray = keys.split('.') // 将字符串分割为数组
+ let current = nestedObject
+ for (let i = 0; i < keysArray.length - 1; i++) {
+ if (!current[keysArray[i]]) current[keysArray[i]] = {} // 创建当前层级的键
+ current = current[keysArray[i]] // 进入下一层
+ }
+ current[keysArray[keysArray.length - 1]] = value // 设置最后一个键的值
+ return nestedObject
+ }
这样,我们只需要在表格中定义好就行了。
i8n Ally
这个vscode插件默认只识别json
和yaml
文件,我们需要在配置中,添加下识别的文件类型
或者直接在setting.json
文件中配置也可以:"i18n-ally.enabledParsers": ["json", "js", "ts"]