likes
comments
collection
share

i18n业务辅助Node脚本工具

作者站长头像
站长
· 阅读数 16

背景

如今前端项目都会涉及国际化功能,项目初始会集成i18n功能,但在开发初期重点也是以功能为主导,直到项目处于一定的稳步迭代的阶段才会着手进行语言包翻译工作,此时往往要求开发人员提供一份完整的待翻译的数据以及后续每个迭代同步待翻译的数据。而实际开发中语言包大多以jsonjsy(a)ml存放于项目中,而翻译的人员大概率不会是开发人员,这个时候往往提供一份Excel给相关人员翻译更具普遍性。由此背景而编写用于辅助此过程的Node脚本便诞生了,主要为了节省开发人员处理国际化数据的时间。

功能说明

此脚本工具用于优化处理国际化文件的过程,其功能主要包括:

  • 指定语言国际化 json 数据转 Excel
  • 已翻译 Excel 生成指定语言国际化 json
  • 每次迭代新增指定语言的国际化转 Excel
  • 每次迭代最新翻译的 `Excel 对比上一版本国际化数据生成最新国际化数据
  • 合并对照两种语言 json 文件至 Excel,用于翻译校验

文件结构

├── i18n-tool-script 
  ├── config # 配置
  │ ├── config.yaml
  ├── docs  # 文档
  │ ├── xxx
  ├── lib  # 脚本源码
  │ ├── xxx
  ├── source # 运行结果输出目录(可自定义配置)
  │ ├── xxx
  ├── utils 工具集合
  │ ├── xxx
  | index.js # 入口(命令式交互)
  | ...

运行

  • 安装项目所需依赖,推荐使用pnpmyarn安装依赖
pnpm install || yarn install
  • config/config.yaml中配置相关功能的数据

i18n业务辅助Node脚本工具

  • 项目根目录在执行命令node index.js或者在lib目录下直接运行相关脚本(eg: node jsonToExcel.js --auto=true

i18n业务辅助Node脚本工具

结果

如选择对比json生成全量的excel数据

  • 对比数据

i18n业务辅助Node脚本工具

  • 脚本执行输出

i18n业务辅助Node脚本工具

生成的Excel数据,生成modulekey是为了后续再次方便生成对应的json数据 i18n业务辅助Node脚本工具

代码

脚本工具代码地址i18n-tool-script。 以下为主要公用逻辑代码: 写入至excel、读取excel数据、读取json数据、写入至json

/*
 * @Date: 2022-05-22 09:45:38
 * @LastEditors: 93eryi@gmail.com
 * @LastEditTime: 2023-03-16 22:19:36
 * @Description: 
 */
const fs = require("fs-extra");
const xlsx = require("node-xlsx").default;
// const json2xls = require("json2xls");
const ExcelJS = require('exceljs')
const colors = require("picocolors");
const utils = require("../utils/index");
const consola = require('consola')
// exit process 
const exit = (code=0) => {
  process.exit(code)
}
// 判断是否存在文件
const existsFile = (filePath) => { 
  const allPath = utils.filePathAll(filePath)
  if(!filePath || !fs.pathExistsSync(allPath)){
    consola.error(colors.red(`[Error] File path ${filePath} does not exist!`));
    exit()
    return false
  }
  return true;
}

// 写入excel
function writeToExcel(data, outPath){
  if(!outPath){
    consola.error(colors.red(`[Error] File path cannot be empty!`));
    return;
  }
  // 处理为完整绝对路径
  outPath = utils.filePathAll(outPath)
  const fileName = utils.getFileName(outPath)
  // ----------- excelJs -----
  const startTime = +new Date()
  const workbook = new ExcelJS.Workbook()
  // 添加工作表
  const workSheet = workbook.addWorksheet('Sheet');
  const columns = ['module', 'key', 'zh-cn', 'en'];
  workSheet.columns = columns.map(col => ({header: col, key: col}))
  workSheet.addRows(data, 'n')
  const cellBorderStyle =  {
    style:'thin',
    color: {
      argb: 'FFC3CBDD'
    }
  }
  workSheet.getColumn(4).eachCell( function(cell){
    if(!cell.value){
      cell.fill = {
        type: 'pattern',
        pattern: 'solid',
        fgColor:  { argb: 'FFFFFF00'}
      }
    }
    cell.font = {
        name: 'SimSun',
        color: {
          argb: 'FF000000'
        },
        family: 3,
        size: 11,
        bold: false,
        outline: false,
        italic: false,
    }
    cell.border = {
      top: cellBorderStyle,
      right: cellBorderStyle,
      bottom: cellBorderStyle,
      left: cellBorderStyle
    }
  })

  workbook.xlsx.writeFile(outPath)
  consola.info(colors.blue(`[Info] Data writing to excel execution time: ${+new Date() - startTime}ms.`))
  consola.success(colors.green(`[Success] Convert json to excel successfully! Output file name is [${fileName}]`));
  // -----------json2xls ------
  // TODO json2xls也可生成excel数据 但是暂未找到定义单元格样式操作
  // const xls = json2xls(data)
  // fs.outputFileSync(outPath, xls, "binary");
  // console.log(colors.green(`[Success] Convert json to excel successfully! Output file name is [${fileName}]`));
}
// 获取Json文件数据
function readJsonFile(path){
  if(!existsFile(path)) return;
  path = utils.filePathAll(path)
  return fs.readJsonSync(path)
}
// 获取excel文件数据
function readExcelFile(path, sheetName='Sheet'){
  if(!existsFile(path)) return;

  path = utils.filePathAll(path)
  const workSheetsFromFile = xlsx.parse(`${path}`);
  const sheetInfo = workSheetsFromFile.find(item => item.name === sheetName)
  if(!sheetInfo){
    consola.error(colors.red(`[Error] Not found sheet name is [${sheetName}]!`));
  }
  const { data } = sheetInfo;
  if (!data || !data.length) {
    consola.warning(colors.yellow("[Error] Excel is empty!"));
    exit()
    return;
  }
  return data
}
// write to json 
function writeToJson(path, lang, data) {
  if(!path){
    consola.error(colors.red(`[Error] File path cannot be empty!`));
    return;
  }
  path = utils.filePathAll(path)
  fs.outputFileSync(
    path,
    JSON.stringify(data),
    "utf-8"
  );
  const fileName = utils.getFileName(path)
  consola.success(
    colors.green(
      `[Success] Convert Excel to language [${lang ? lang : 'unknown'}] version json successfully! Output file name is [${fileName}]`
    )
  );
}
// excel数据转为目标json数据
function convertExcelToJson(path, lang='zh-cn', langMap){
  const data = readExcelFile(path);
  if(!data) return
  // 英文转化数据结
  const resJson = {};
  function fmtTargeKeyValue(data) {
    const tmpDeepObj = {};
    for (let j = 0; j < data.length; j++) {
      // 生成对应语言excel的数据对应index
      const langValIndex = langMap[lang] + 2;
      tmpDeepObj[data[1]] = utils.strToUpperCase(data[langValIndex]);
    }
    return tmpDeepObj;
  }
  for (let i = 1; i < data.length; i++) {
    const tmpVal = data[i];
    const key = tmpVal[0];
    if (!resJson[key]) {
      resJson[key] = {};
    }
    const tmpObj = fmtTargeKeyValue(tmpVal);
    resJson[key] = { ...resJson[key], ...tmpObj };
  }
  return resJson;
}
module.exports = {
  exit,
  writeToExcel,
  writeToJson,
  readJsonFile,
  readExcelFile,
  existsFile,
  convertExcelToJson
}