⌨🖱CV大法好,i18n CV就不好了

某天正在疯狂CV中,CV着刚翻译好的多语言:左侧屏幕是代码,右侧屏幕是云文档。头从左转到右,又从右转到左,眼花手酸脖子酸,人都不好了😵。

同事瞅了一眼:卧槽,你咋还手动CV啊,写个脚本自动替换不香吗?

工作流程

  1. 写代码的时候,定义多语言的kye和value
  2. 复制到其他语言文件中
  3. 把需要翻译的文本,填到云文档的表格中
  4. 产品翻译
  5. 翻译好后,开始CV

手动的时候就是这么个流程,我们主要是把第5步通过脚本实现。

  1. 下载表格
  2. 读取表格,数据转成需要的格式
  3. 读取多语言文件夹下的文件
  4. 文本替换
  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插件默认只识别jsonyaml文件,我们需要在配置中,添加下识别的文件类型

或者直接在setting.json文件中配置也可以:"i18n-ally.enabledParsers": ["json", "js", "ts"]

结束

相关推荐
yqcoder12 分钟前
Commander 一款命令行自定义命令依赖
前端·javascript·arcgis·node.js
前端Hardy28 分钟前
HTML&CSS :下雪了
前端·javascript·css·html·交互
醉の虾35 分钟前
VUE3 使用路由守卫函数实现类型服务器端中间件效果
前端·vue.js·中间件
码上飞扬1 小时前
Vue 3 30天精进之旅:Day 05 - 事件处理
前端·javascript·vue.js
火烧屁屁啦2 小时前
【JavaEE进阶】应用分层
java·前端·java-ee
程序员小寒2 小时前
由于请求的竞态问题,前端仔喜提了一个bug
前端·javascript·bug
赵不困888(合作私信)3 小时前
npx和npm 和pnpm的区别
前端·npm·node.js
很酷的站长4 小时前
一个简单的自适应html5导航模板
前端·css·css3
python算法(魔法师版)6 小时前
React应用深度优化与调试实战指南
开发语言·前端·javascript·react.js·ecmascript
阿芯爱编程10 小时前
vue3 vue2区别
前端·javascript·vue.js