node - 实现多语言脚本快速生成

648 阅读4分钟

node - 实现多语言脚本快速生成

前言

多个项目做了多语言,需要多个多语言表(也就是.xlsx文件)来维护。前端需要从一个文件中根据key 来 读取值(值可能对应中/英两种情况),怎么将多个.xlsx 文件,合并成一个前端可以根据key 来读取的文件,这就是今天我要写的内容

1.png

配置文件

做一个 lang-config.json ,里面做input / outout 的配置文件

  1. input 配置表名/工作博
  2. output 配置 中/英 的key / value

egg:

{
  "input": [
    {
      "fileName": "project1.xlsx",
      "sheetNames": ["Sheet1"]
    },
    {
      "fileName": "project2.xlsx",
      "sheetNames": ["Sheet1"]
    },
    {
      "fileName": "error-code.xlsx",
      "sheetNames": ["Sheet1"]
    }
  ],
  "output": [
    { "key": "zh-CN", "value": "中文" },
    { "key": "en-US", "value": "英文" }
  ]
}

实现流程

  1. 读取配置文件,根据配置文件中的input,获取配置文件所有的fileName
[ 'project1.xlsx', 'project2.xlsx', 'error-code.xlsx']
  1. 循环fileName,为之拼接相对应的文件路径,使用xlsx.utils.sheet_to_json,根据工作簿,做文件内容读取
  • 文件路径
    /Users/Desktop/vue-project/tools/lang/project1.xlsx
    /Users/Desktop/vue-project/tools/lang/project2.xlsx
    /Users/Desktop/vue-project/tools/lang/error-code.xlsx
  • 文件内容读取

主要代码

xlsx.utils.sheet_to_json(workbook.Sheets[sheetName])

输入产物: sheet_to_json的产物

[
 {
    key: 'error_code_502',
    '含义': '服务器无响应,请稍后重试',
    '中文': '服务器无响应,请稍后重试',
    '英文': 'The server has no response, please try it later'
  },
  {
    key: 'error_code_503',
    '含义': '服务器未就绪,请稍后重试',
    '中文': '服务器未就绪,请稍后重试',
    '英文': 'The server is not ready, please try again later'
  }
 ]

现在已经根据配置文件(input配置),将所有的xlsx 读取完毕。接下来就是怎样将这些数据,输入到en-us.ts/zh-us.ts 文件中,那自然是根据output配置了

  1. 输出文件数据配置,需要哪些字段 ?
  • key(en-us/zh-us): (因为要用key,做输出文件路径的后缀
  • value(中文/英文): 来和sheet_to_json的产物 做匹配。如果是中文文件,匹配sheet_to_json里的中文,英文文件,匹配sheet_to_json里的英文文,
  • path: 输出的文件,要存放的位置
  • object: 输出文件的内容

输出产物: 根据output 文件输出的产物

[
  {
    key: 'zh-CN',
    value: '中文',
    path: '/Users/Desktop/vue-project/src/generated/lang/zh-CN.ts',
    object: {}
  },
  {
    key: 'en-US',
    value: '英文',
    path: '/Users/Desktop/vue-project/src/generated/lang/en-US.ts',
    object: {}
  }
]
  1. 如何将输入产物,放到指定的输出产物里呢?
  • 输入产物和输出产物,都是两个数组对象,最粗暴的方式,就是对二者进行双重for 循环。 将输入产物的key 作为输出产物的key,根据输出产物的value 去和输入产物的中文/英文做匹配

主要代码

jsonData.forEach((json)=>{
    result.forEach((item)=>{
        item.object[json['key']] =  json[item.value] // ⚠️⚠️⚠️⚠️
    })
})

产物

[
  {
    key: 'zh-CN',
    value: '中文',
    path: '/Users/Desktop/vue-project/src/generated/lang/zh-CN.ts',
    object: {
      error_code_502: '服务器无响应,请稍后重试',
      error_code_502: '服务器未就绪,请稍后重试',
    },
  {
    key: 'en-US',
    value: '英文',
    path: '/Users/Desktop/vue-project/src/generated/lang/zh-CN.ts',
    object: {
      error_code_502: 'The server has no response, please try it later',
      error_code_503: 'The server is not ready, please try again later',
   },
]
  1. 如何将这个输出产物,导出为 en-US.ts / zh-US.ts 文件呢?

主要代码

result.forEach((config) => {
    const { key, value, path, object } = config;
    //const content = `export default\n{ \n${toSingleQuotedValues(object)}};`;
     const content = `export default ${JSON.stringify(object, null, 2)};`;
    fs.writeFileSync(path, content);
  });

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

边界/细节处理

如何导出en-US.ts/zh-US.ts 文件格式怎么处理

我们要读取en-US.ts/zh-US.ts ,里面需要导出一个对象:export default {},对象里面是key:value。 说到底,.ts 也是一个文件,里面的文件内容,自己做拼接就好

可以使用 JSON.stringify

export default ${JSON.stringify(object, null, 2)};

也可以自己做一层数据格式转换

`export default\n{ \n${toSingleQuotedValues(object)}};`

const entries = Object.entries(obj).map(([key, value]) => {
  const quotedValue = `'${value}'`;
  return `  ${key}: ${quotedValue}`;
});
return entries.join(',\n');
}

image.png

多语言如何传参数

动态传参,在多语言中是必不可少的。比如还有xx天过期。没必要还有 天过期分开做多语言,直接使用 还有${params}天过期 ,参数按照项目定义即可,比如我们用的是 %1$@代表参数

在赋值的时候,只需要将item.value 根据正则匹配,做一个替换即可

jsonData.forEach((json)=>{
   let newValue = json[item.value] // +++
   newValue = newValue.replace( // +++
           /%(\d+)\$@/g,
           (match, index) => `{param${index}}`
         );
   result.forEach((item)=>{
       item.object[json['key']] =  json[newValue] // +++
   })
})

vue-i18n中 动态传值,采用{}即可

egg:

{ "welcome": "Welcome, {username}!" } 如果是多个变量呢,

{ "welcome": "Welcome, {username1} {username1} {username3}!" }

我们可以利用 replace的第个参数,对每一个特殊参数进行匹配替换,为保证参数唯一性,可以拼接 index

重复key 问题

在excel 文件中,由于多人协作,可能会出现重复key的情况,那就会出现覆盖,导致key 设置的value 不生效,我们在脚本中对这种情况也要做处理

// 存储已处理的 key
const processedKeys = new Set();
// 在循环json 数据的时候,要一个判断,如果已经存在了key,则chalk抛异常
if (processedKeys.has(json['key'])) {
        handleError(
          chalk.red.bold(
            `文件 ${fileName} 的工作表 ${sheetName} 存在重复的 key: ${json['key']}`
          )
        );
      }
      // 否则就将key 添加到map 结构中
processedKeys.add(json['key']);

异常终中断

出现异常,终断process.exit(1); 当前进程 退出

/**
 * 异常处理
 * @param errorMessage
 */
function handleError(errorMessage) {
  spinner.fail(chalk.red.bold(errorMessage));
  process.exit(1);
}

文件读取异常处理

let directoryPath = path.dirname(outputPath)
/**
 * 辅助函数,确保输出目录存在,不存在则创建
 * @param directoryPath 路径
 */
function ensureDirectoryExists(directoryPath) {
  try {
    fs.mkdirSync(directoryPath, { recursive: true });
  } catch (error) {
    handleError(chalk.red.bold(`创建目录失败: ${directoryPath}: ${error}`));
  }
}

显示脚本进度条和任务状态

import ora from 'ora';
const spinner = ora()
spinner.start();
spinner.text = 'language脚本生成中...';
// ora('language脚本生成中...').start()

image.png

给脚本增加彩色样式(成功/ 失败 )特殊提示

import chalk from 'chalk';
import ora from 'ora';
const spinner = ora()

spinner.succeed(chalk.green.bold(`文件已保存:${path}`))
spinner.succeed(chalk.red(`文件已保存:${path}`))

image.png

相关三方插件

更多详细内容,可根据插件详细配置

项目地址

欢迎 Star ⭐️⭐️⭐️⭐️

gitee.com/Big_Cat-AK-…