使用 vve-i18n-cli 来一键式自动化实现国际化

这篇具有很好参考价值的文章主要介绍了使用 vve-i18n-cli 来一键式自动化实现国际化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Github:vue-viewer-editor/vve-i18n-cli】

这是我同事开发的国际化自动处理脚本,我进行过一次扩展,让其也支持我们一个 jQuery 老项目的国际化日常维护

至此,我们团队内的国际化均是使用该脚本来进行日常维护

该自动化脚本极大的为我们提效,基本将国际化的词条相关工作降低到 0 了,这意味着我们基本上不用特意留出太多时间来处理国际化方面的工作

但是,国际化其实不只有词条相关的工作,至于还有哪些工作,我之前发表过一篇《项目国际化的难点痛点是什么》里面吐槽得很清晰了

不可否认的是,如果没有这个自动化脚本,根本就没法在领导期望的时间内完成国际化的工作

自从有了这个脚本后,从以前的跟领导评估说要 4 天的国际化工作量到现在只评估了 1 天工作量,实际上跑下脚本分分钟就解决了,我还可以愉快的滑一天水,领导开心,我也开心~

自动化脚本能力

检查项目里是否存在不合理的编程方式,如中文做 key 值等

使用 vve-i18n-cli 来一键式自动化实现国际化

上述示例命令运行结果呈现:

{"type":"script-pre","text":"这里也可能有中文,还用不了this上下文"}
{"type":"props","text":"这里的中文也用不了this上下文"}
{"type":"zh-key","text":"中文"}

script-pre 场景是说发现有中文存在于 script 标签内,这部分代码运行在 js 模块作用域内,this 指向不是 vue 组件,包裹 this.$t 的话会导致程序异常,所以要先手动处理下,能下沉就下沉,否则就先手动用全局函数包裹,然后忽略这个处理

props 场景是说发现有中文存在于 vue 的 props 字段里,这里也无法访问 this,会报异常,建议这块更改成 computed 用法

zh-key 场景是说发现中文做 key 值,需要用户确认是否能被翻译处理

这个命令可以提前主动的发现代码里的国际化处理问题,避免将问题遗留到测试或线上阶段

标记无需处理的词条

总有些场景,你希望这个中文词条不要被国际化处理,这时候可以类似 es-lint 的忽略配置一样,既可以忽略整个文件,也可以忽略文件中的某个词条

vve-i18n-cli.config.js 里增加下忽略配置规则:

// vve-i18n-cli.config.js
module.exports = {
  // 国际化文本包裹相关
  zhWrap: {
    // 需要过滤的文件
    ignoreI18nFileRules: [],
    // 需要处理的文件
    i18nFileRules: ["!(node_modules|config|statsvnTmp)/**/*.+(js|vue)"],
    // 当词条前缀出现以下正则时,该词条过滤不处理
    ignorePreReg: [
      /t\s*\([\s\n]*$/, //  词条在 t( 方法内的不处理,$t() 符合该规则
      /console\.(?:log|error|warn|info|debug)\s*\(\s*$/, //  词条在 console.xxx 方法内的不处理,过滤掉日志内的中文处理
      new RegExp("//.+"), // 注释中的词条不处理
      new RegExp("i18n-disabled.+"), // 词条前面出现 i18n-disabled 关键词的不处理
    ],
  },
};

然后代码中这些场景就不被处理了:

const ZH_KEY = /*i18n-disabled*/ "出现忽略处理的关键词的词条也不会被处理";
// 注释里中文不会被处理
console.error("日志里的中文也不会被处理");

自动对各种场景的中文词条包裹翻译函数

当检查完代码基本没问题,也配置了需要忽略处理的词条文件后,就可以通过命令自动对中文词条进行包裹翻译函数处理了:

使用 vve-i18n-cli 来一键式自动化实现国际化

不管你项目文件有成百上千个,都是一个命令就自动完成国际化翻译函数包裹词条处理

包裹哪些文件、包含的翻译函数名,不同场景(js 里,vue 里)用什么函数包裹,js 里是否需要加入 import 引入包含函数的代码,哪些文件不处理,哪些词条不处理,哪些前缀标记的不处理等等

以上场景都是通过 vve-i18n-cli.config.js 配置文件处理,详情查看下面章节,有默认的配置,你也可以根据你项目需要进行自己诉求的配置

脚本不是写死的行为,通过不同配置,可以适应到各种项目里去使用,目前我们团队的老项目,新项目,各种项目就通过各自项目的配置来接入了这个国际化自动处理脚本

自动将所有词条提取到 json 文件中(按模块维护)

当项目完成的国际化包裹词条处理后,就可以接着下一步,把词条提取到 json 文件里了:

使用 vve-i18n-cli 来一键式自动化实现国际化

想机翻,可以,默认不翻译,只做提取
想按模块提取到不同 json 文件里,可以,配置下模块规则
想生成多份语言的 json,可以,默认只有 zh.json, en.json


国际化的词条工作无外乎就是词条包裹处理,词条提取,词条翻译

这些工作难度不大,但工作量大,借助这类国际化自动处理脚本,就可以极大的提高效率,开心的滑水了

如何使用

安装

npm install vve-i18n-cli -D

package 里添加脚本命令,简化命令使用

{
  "scripts": {
    "i18n": "vve-i18n-cli",
    "i18n-wrap": "vve-i18n-zh-wrap-cli",
    "i18n-check": "vve-i18n-zh-check-cli"
  }
}

根目录下创建配置文件 vve-i18n-cli.config.js

// vve-i18n-cli.config.js
module.exports = {
  // 工作目录
  cwd: ".",
  // 根目录,国际文本所在的根目录
  rootDir: "src",
  // 默认所有模块,如果有传module参数,就只处理某个模块
  // '**/module-**/**/index.js'
  moduleIndexRules: ["."],
  // 忽略模块
  ignoreModuleIndexRules: [],
  // 匹配含有国际化文本的文件规则
  i18nFileRules: ["**/*.+(vue|js)"],
  // 不匹配含有国际化文本的文件规则
  ignoreI18nFileRules: [],
  // 国际化文本的正则表达式,正则中第一个捕获对象当做国际化文本
  i18nTextRules: [/(?:[\$.])t\([\s\n]*['"](.+?)['"]/g],
  // 模块的国际化的json文件需要被保留下的key,即使这些组件在项目中没有被引用
  // 规则可以是一个字符串,正则,或者是函数
  keepKeyRules: [
    /^G\/+/, // G/开头的会被保留
  ],
  // 忽略国际化KEY的规则
  // 规则可以是一个字符串,正则,或者是函数
  ignoreKeyRules: [],
  // 生成的国际化资源包的输出目录
  outDir: "lang",
  // 生成的国际化的语言
  i18nLanguages: [
    "zh", // 中文
    "en", // 英文
  ],
  // 配置文件的路径,没有配置,默认路径是在${cwd}/vve-i18n-cli.config.js
  config: undefined,
  // 是否取配置文件
  disableConfigFile: false,
  // 是否翻译
  translate: false,
  // 翻译的基础语言,默认是用中文翻译
  translateFromLang: "zh",
  // 是否强制翻译,即已翻译修改的内容,也重新用翻译生成
  forceTranslate: false,
  // 翻译的语言
  translateLanguage: ["zh", "en"],
  // 非中文使用拼音来来翻译
  translateUsePinYin: false,
  // 模块下${outDir}/index.js文件不存在才拷贝index.js
  copyIndex: false,
  // 是否强制拷贝最新index.js
  forceCopyIndex: false,

  // 国际化文本包裹相关
  zhWrap: {
    cwd: ".",
    // 根目录,国际文本所在的根目录
    rootDir: ".",
    ignoreI18nFileRules: [],
    i18nFileRules: ["!(node_modules|config|statsvnTmp)/**/*.+(js|vue)"],
    ignorePreReg: [
      /t\s*\([\s\n]*$/,
      /tl\s*\([\s\n]*$/,
      /console\.(?:log|error|warn|info|debug)\s*\(\s*$/,
      /\/\/\s*$/,
      new RegExp("//.+"),
      new RegExp("i18n-disabled.+"),
    ],
    ignoreText: ["^[\\u4e00-\\u9fa5a-zA-Z0-9“._=,':;*#!”-]+$"],
    // js相关文件需要引入的国际化文件
    i18nImportForJs: "",
    // js相关文件需要使用国际化方法
    jsI18nFuncName: "$i18n.$t",
    // vue相关文件需要使用的国际化方法
    vueI18nFuncName: "$t",
  },
  zhCheck: {
    cwd: ".",
    // 根目录,国际文本所在的根目录
    rootDir: ".",
    ignoreI18nFileRules: [],
    i18nFileRules: ["!(node_modules|config|statsvnTmp)/**/*.+(vue|js)"],
    // 反引号中需要忽略的文本规则,可以是正则或者字符串
    ignoreTextInQuoteRules: [/t\(/],
  },
};

先检查是否存在不合理的代码实现

npm run i18n-check

再执行自动对词条包裹翻译函数的命令

npm run i18n-wrap

最后把这些被翻译函数包裹的词条提取到 json 文件里

npm run i18n


这份脚本很通用化,根据各自配置规则,可以适应到各种项目里面,实在不行,代码也是开源的,拉下来修修改改得了

扩展

我们团队的翻译不是机翻,而是有专门的翻译团队进行翻译,因此提取完 json 词条后,还需要用 excel 跟翻译团队打交道

所以可以来扩展下几个脚本文章来源地址https://www.toymoban.com/news/detail-776934.html

提取未翻译词条到 excel 文件中

/**
 * 抽取未翻译的词条到excel文件中
 */
const map = require("map-stream");
const path = require("path");
const vfs = require("vinyl-fs");
const XLSX = require("xlsx");

const ROOT_DIR = path.resolve("./");
const fileRules = [
  "**/*/i18n/en.json",
  // "**/eweb-setting-planningDeployment/i18n/en.json",
];

const writeExcel = (arr, name = "未翻译词条") => {
  const sheet_data = arr.map((v) => {
    return {
      中文: v,
      English: "",
    };
  });
  const new_sheet = XLSX.utils.json_to_sheet(sheet_data);
  // // 创新一个新的excel对象,就是workbook
  const new_workbook = XLSX.utils.book_new();
  // // 将表的内容写入workbook
  XLSX.utils.book_append_sheet(new_workbook, new_sheet, "sheet1");
  XLSX.writeFile(new_workbook, `${name}.xlsx`);
};

function run() {
  const zhList = [];
  console.log("================================>start", ROOT_DIR);
  vfs
    .src(
      fileRules.map((item) => path.resolve(ROOT_DIR, item)),
      {
        ignore: ["node_modules/**/*", "statsvnTmp/**/*"],
      }
    )
    .pipe(
      map((file, cb) => {
        console.log("处理文件 =========================>", file.path);

        let fileContent = file.contents.toString();
        fileContent = JSON.parse(fileContent);
        Object.keys(fileContent).map((zh) => {
          if (zh.match(/[\u4E00-\u9FFF]/)) {
            if (zh === fileContent[zh]) {
              // 未翻译
              zhList.push(zh);
            }
          }
        });
        cb();
      })
    )
    .on("end", () => {
      const uniZh = Array.from(new Set(zhList));
      writeExcel(uniZh);
      console.log("未翻译词条数量:", uniZh.length);
      console.log(
        "================================>end",
        "根目录下生成 excle 文件"
      );
    });
}

run();

将 excel 中的词条回填到 json 文件中

/**
 * 将翻译后的内容替换到en.json文件中
 */
const map = require("map-stream");
const path = require("path");
const vfs = require("vinyl-fs");
const fs = require("fs");
const XLSX = require("xlsx");

const ROOT_DIR = path.resolve("./");
const fileRules = ["**/*/i18n/en.json"];
// 文件名称 默认名称 resource.json
const fileName = "resource";
const fileJsonName = fileName + ".json";
const fileXlsName = fileName + ".xlsx";

const excelReader = (exlcePathArray = []) => {
  if (!Array.isArray(exlcePathArray)) {
    exlcePathArray = [exlcePathArray];
  }
  const obj = {};
  for (const i in exlcePathArray) {
    if (Object.hasOwnProperty.call(exlcePathArray, i)) {
      const excleFilePath = exlcePathArray[i];
      console.log("读取excle " + excleFilePath);
      const workbook = XLSX.readFileSync(excleFilePath, {
        type: "binary",
      });
      for (const sheet in workbook.Sheets) {
        const dataArray = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]);
        obj[sheet] = dataArray;
      }
    }
  }
  return obj;
};

// 如果未找到 resource.json 查找 excel文件
if (!fs.existsSync(path.resolve(ROOT_DIR, fileJsonName))) {
  // 判断resource.excel是否存在
  if (fs.existsSync(path.resolve(ROOT_DIR, fileXlsName))) {
    let fileObj = excelReader(path.resolve(ROOT_DIR, fileXlsName));
    let dataObj = {};
    if (fileObj.sheet1) {
      // 中文列存在 例子: {"中文":"下载中...","English":"down…"}
      for (let i = 0; i < fileObj.sheet1.length; i++) {
        let itemZhKey = fileObj.sheet1[i]["中文"];
        let itemEnKey = fileObj.sheet1[i]["English"];
        if (itemZhKey && itemEnKey) {
          dataObj[itemZhKey] = itemEnKey;
        }
      }
    }
    // 获取sheet1的内容
    const data = JSON.stringify(dataObj);
    try {
      fs.writeFileSync(path.resolve(ROOT_DIR, fileJsonName), data);
    } catch (error) {
      console.log("生成文件 resource.xlsx 异常");
      throw error;
    }
  } else {
    console.log("不存在文件 resource.xlsx");
    throw new Error("不存在文件 resource.xlsx");
  }
}

const originResource = require(path.resolve(ROOT_DIR, fileJsonName));
let resource = Object.assign({}, originResource);
Object.keys(originResource).map((key) => {
  resource[`${key}:`] = `${originResource[key]}:`;
  resource[`${key}:`] = `${originResource[key]}:`;
  resource[`${key})`] = `${originResource[key]})`;
  resource[`${key})`] = `${originResource[key]})`;
  resource[`${key} `] = `${originResource[key]} `;
  resource[` ${key}`] = ` ${originResource[key]}`;
  resource[`(${key}`] = `(${originResource[key]}`;
  resource[`(${key}`] = `(${originResource[key]}`;
  resource[` ${key} `] = ` ${originResource[key]} `;
});

function run() {
  console.log("================================>start", ROOT_DIR);
  let failedCount = 0;
  let successCount = 0;
  let failedZhs = [];
  vfs
    .src(
      fileRules.map((item) => path.resolve(ROOT_DIR, item)),
      {
        ignore: ["node_modules/**/*", "statsvnTmp/**/*"],
      }
    )
    .pipe(
      map((file, cb) => {
        console.log("处理文件 =========================>", file.path);

        let fileContent = file.contents.toString();
        fileContent = JSON.parse(fileContent);
        let hasChange = false;
        Object.keys(fileContent).map((zh) => {
          if (zh.match(/[\u4E00-\u9FFF]/)) {
            if (zh === fileContent[zh]) {
              // 未翻译
              if (resource[zh] && resource[zh] !== zh) {
                hasChange = true;
                fileContent[zh] = resource[zh];
                successCount++;
              } else {
                failedCount++;
                failedZhs.push(zh);
              }
            }
          }
        });
        if (hasChange) {
          fs.writeFileSync(
            file.path,
            JSON.stringify(fileContent, " ", 2) + "\n"
          );
        }
        cb();
      })
    )
    .on("end", () => {
      fs.writeFileSync(
        "unHandle.json",
        JSON.stringify(failedZhs, " ", 2) + "\n"
      );
      console.log("本次翻译成功词条数量:", successCount);
      console.log("还剩余未翻译词条数量:", failedCount);
      console.log("================================>end");
    });
}

run();

到了这里,关于使用 vve-i18n-cli 来一键式自动化实现国际化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【uniapp】Uniapp cli 自动化打包脚本实现

    通常使用uniapp开发app时,大多数会使用项目的云服务打包,否则的话再借助原生会变得极其复杂,还要去安装对应大内存的环境。如果恰好此时,你有一个需求是,可以随意的更换logo和封面、标题切换成另外一个app,那么很明显你的HBuilderX维护起来是非常复杂的,如果我们想

    2024年02月04日
    浏览(47)
  • Vue3中,国际化插件vue-i18n使用记录,配合VSCode自动化翻译插件i18n Ally

    说明文档: vue-i18n 版本说明: vue: 3.0.0+ vue-i18n: 9.x版 src 目录下新建目录 lang ,存放 i18n 的配置。 新建目录名称: lang (语言)、 locales (语系设定)、 i18n ,这些名称都可被VSCode图标插件( vscode-icons )检测到并美化。 lang 目录下,新建 index.js 文件,引入 vue-i18n 。 语言的配置信

    2024年02月12日
    浏览(42)
  • Jenkins+Docker 实现一键自动化部署项目

    1.安装Jenkins 注:因为Jenkins容器里的用户是Jenkins,而主机用户不是Jenkins,就算是root也一样会报错:/var/jenkins_home/copy_reference_file.log: Permission denied,这个时候就需要在主机上面给主机地址赋予访问Jenkins容器的权限,Jenkins内部用的是uid 1000的user。 -privileged=true让容器具有root权限

    2024年02月16日
    浏览(51)
  • Python 自动化办公:一键批量生成 PPT

    Stata and Python 数据分析 一、导读 在实际工作中,经常需要批量处理Office文件,比如需要制作一个几十页的PPT进行产品介绍时,一页一页地制作不仅麻烦而且格式可能不统一。那么有什么办法可以一键生成PPT呢?Python提供的pptx 包就可以用来自动化处理ppt。 pytho****n-pptx 是一个

    2024年01月17日
    浏览(76)
  • Python自动化小技巧18——自动化资产月报(word设置字体表格样式,查找替换文字)

    案例背景 每月都要写各种月报,经营管理月报,资产月报.....这些报告文字目标都是高度相似的,只是需要替换为每个月的实际数据就行,如下:   (打码是怕信息泄露.....) 可以看到,这个报告的都是高度模板化,我们只需要对里面的某些文字进行替换,例如2023年7月换成2

    2024年02月12日
    浏览(56)
  • 自动化脚本一键安装 jdk,hadoop,hive

    网盘资源如下 链接: https://pan.baidu.com/s/1wKHRjcqJHRTcvmHOxsn0Bw?pwd=qh8h 提取码: qh8h 使用该脚本有几个地方需要修改成自己设备相应属性,还有一些注意事项 (1)脚本开头 分别对应 jdk、hadoop、hive 安装 false 是不安装 true 是安装 (2)安装 hive 这条语句里的 ip 换成你自己的 这条语句

    2024年02月09日
    浏览(49)
  • 【Python 自动化】小说推文一键生成思路概述

    最近看了一下小说推文成品软件的思路,发现可以完全迁移到我的 BookerAutoVideo 上面来。这篇短文里面,我试着分析一下整个推文视频生成的流程,以及简要阐述一下有什么工具。 整体流程是这样: 原文是按照段落组织的,我们可能希望按照句子生成图片。于是我们需要把段

    2024年02月09日
    浏览(57)
  • Jenkins + Docker 一键自动化部署 SpringBoot 应用最精简流程

    本文章实现最简单全面的 Jenkins + Docker + Spring Boot 一键自动部署项目。步骤齐全,少走坑路。 环境:CentOS7 + Git (Gitee) 实现步骤:在 Docker 安装 Jenkins,配置 Jenkins 基本信息,利用 Dockerfile 和 Shell 脚本实现项目自动拉取打包并运行。 1安装 Docker 安装社区版本 Docker CE 确保 yum 包

    2024年04月26日
    浏览(55)
  • 通过ETLCloud自动化数据处理:用友U8数据一键同步

    用友U8是一款成熟的企业管理软件,是一套适用于企业全面管理的ERP(Enterprise Resource Planning)软件。主要用于管理企业的财务、人力资源、供应链、生产制造等业务。它具有模块化设计和高度可定制化的特点,可以根据企业的实际需求进行配置和部署。同时,用友U8还提供了

    2024年02月09日
    浏览(41)
  • Jenkins+Docker 实现一键自动化部署项目!步骤齐全,少走坑路

    大家好,我是互联网架构师! 本文章实现最简单全面的Jenkins+docker+springboot 一键自动部署项目,步骤齐全,少走坑路。 环境 :centos7+git(gitee) 简述实现步骤:在docker安装jenkins,配置jenkins基本信息,利用Dockerfile和shell脚本实现项目自动拉取打包并运行。 docker 安装社区版本CE

    2024年02月09日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包