多语言翻译你还在一个个改?我不允许你不知道这个工具

发布于:2024-04-27 ⋅ 阅读:(19) ⋅ 点赞:(0)

最近在做项目的多语言翻译,由于是老项目,里面需要翻译的文本太多了,如果一个个翻译的话,一个人可能一个月也做不完,因此考虑使用自动化工具实现。

我也从网上搜索了很多现有的自动化翻译工具,有vsc插件、webpack或vite插件、webpack loader等前人实现的方案,但是安装之后发现,要不脱离不了一个个点击生成的繁琐,要不插件安装太麻烦,安装报错依赖报错等等问题层出不穷。因此,决定自己写一个,它需要满足:

1.不需要手动改代码,自动运行生成

2.不需要查翻译,自动调用翻译接口生成翻译内容

3.不需要安装,避免安装问题、环境问题等

i18n-cli 工具的产生

经过一个星期左右的开发和调试,i18n-cli自动化翻译工具实现了,详情可以看。先来看看使用效果:

转换前:

<template>
  <div class="empty-data">
    <div class="name">{{ name }}</div>
    <template>
      <div class="empty-image-wrap">
        <img class="empty-image" :src="emptyImage" />
      </div>
      <div class="empty-title">暂无数据</div>
    </template>
  </div>
</template>

<script lang="js">
import Vue from "vue";
export default Vue.extend({
  data(){
    return {
      name: "测试"
    }
  },
});
</script>

转换后:

<template>
  <div class="empty-data">
    <div class="name">{{ name }}</div>
    <template>
      <div class="empty-image-wrap">
        <img class="empty-image" :src="emptyImage" />
      </div>
      <div class="empty-title">{{ $t("zan-wu-shu-ju") }}</div>
    </template>
  </div>
</template>

<script lang="js">
import { i18n } from 'i18n';
import Vue from "vue";
export default Vue.extend({
  data() {
    return {
      name: i18n.t('ce-shi')
    };
  }
});
</script>

@tenado/i18n-cli翻译,不受语言类型限制,目前vue、react、vue3等代码都能完美的支持,它通过分析语法树,自动匹配中文内容,并生成翻译后的代码。

如何使用 i18n-cli 工具

1.下载@tenado/i18n-cli项目代码,例如存作i18n-cli

2.将需要翻译的代码文件,拷贝到i18n-cli项目下的目录下

3.修改 i18n.config.js 配置,修改入口entry为你刚复制的文件的位置,修改你需要翻译的语言列表langs,例如英文、繁体['en-US', 'zh-TW'],修改引入i18n的方法i18nImport、i18nObject、i18nMethod,修改翻译的类型和秘钥,一个简单的配置如下:

module.exports = {
  // 入口位置
  entry: ['example/transform-i-tag'],
  // 翻译后的文件存放位置
  localPath: './example/transform-i-tag/locales',
  // 需要翻译的语言列表
  langs: ['en-US'],
  // 引入i18n
  i18nImport: "import { t } from 'i18n';",
  i18nObject: '',
  i18nMethod: 't',
  // 翻译配置,例如百度
  translate: {
    type: 'baidu',
    appId: '2023088292121',
    secretKey: 'J1ArqOof1s8kree',
    interval: 1000,
  },
};

4.在i18n-cli项目下执行命令,npm run sync,将会修改你刚复制的文件里面的代码,并在locales下生成翻译内容,这里如果没有百度翻译api key,那你可以先收集,后面在翻译,先执行 npm run extract,再执行 npm run translate

5.将修改后的文件复制回你的项目下

当然,i18n-cli 的配置不是仅仅这些,更多配置你可以去 对应的 github 仓库上查看

i18n-cli 是怎么实现的

1、收集文件

根据入口,获取需要处理的文件列表,主要代码如下:

// 根据入口获取文件列表
const getSourceFiles = (entry, exclude) => {
  return glob.sync(`${entry}/**/*.{js,ts,tsx,jsx,vue}`, {
    ignore: exclude || [],
  })
}
// 例如 getSourceFiles('src/components')
// 结果 ['src/components/Select/index.vue', 'src/components/Select/options.vue',  'src/components/Select/index.js']

2、转换文件

根据文件类型,生成不同文件的语法树,例如.vue文件分别解析vue的template、style、script三个部分,例如.ts、.tsx文件,例如html,解析成ast语法树后,针对不同类型的中文分别处理,如下是babel转换ast时候里面的一部分核心代码:

const { declare } = require("@babel/helper-plugin-utils");
const generate = require("@babel/generator").default;
module.exports = declare((api, options) => {
  return {
    visitor: {
      // 针对不同类型的中文,进行转换
      // 代码太多,这里不贴全部,具体的可以去github上查看源码
      DirectiveLiteral() {},
      StringLiteral() {},
      TemplateLiteral() {},
      CallExpression() {},
      ObjectExpression() {},
    },
  };
});

3、调用接口翻译

根据locales文件存放位置,把收集到的中文都存在./locales/zh-CN.json里面,收集中文和key是在文件转换过程处理的。

这个过程,会根据生成的中文json,去请求接口,拿到中文对应语言的翻译,实现代码如下:

const fetch = require("node-fetch");
const md5 = require('md5');
const createHttpError = require('http-errors');
const langMap = require("./langMap.js");
const defaultOptions = {
  from: "auto",
  to: "en",
  appid: "",
  salt: "wgb236hj",
  sign: "",
}
module.exports = async (text, lang, options) => {
  const hostUrl =  "http://api.fanyi.baidu.com/api/trans/vip/translate";
  let _options = {
    q: text,
    ...defaultOptions,
  }
  const { local } = options ?? {};
  const { appId, secretKey } = options?.translate ?? {};
  if(local) {
    _options.from = langMap('baidu', local);
  }
  if(lang) {
    _options.to = langMap('baidu', lang);
  }
  _options.appid = appId;
  const str = `${_options.appid}${_options.q}${_options.salt}${secretKey}`;
  _options.sign = md5(str);
  const buildBody = () => {
    return new URLSearchParams(_options).toString();
  }
  const buildOption = () => {
    const opt = {};
    opt.method = 'POST';
    opt.headers = {
      'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
    }
    opt.body = buildBody();
    return opt;
  }
  const buildError = async (res) => {
    const extractTooManyRequestsInfo = (html) => {
      const ip = html.match(/IP address: (.+?)<br>/)?.[1] || '';
      const time = html.match(/Time: (.+?)<br>/)?.[1] || '';
      const url = (html.match(/URL: (.+?)<br>/)?.[1] || '').replace(/&amp;/g, '&');
      return { ip, time, url };
    }
    if (res.status === 429) {
      const text = await res.text();
      const { ip, time, url } = extractTooManyRequestsInfo(text);
      const message = `${res.statusText} IP: ${ip}, Time: ${time}, Url: ${url}`;
      return createHttpError(res.status, message);
    } else {
      return createHttpError(res.status, res.statusText);
    }
  }
  const buildText = ({ error_code, error_msg, trans_result }) => {
    if(!error_code) {
      return trans_result?.map(item => item.dst);
    } else {
      console.error(`百度翻译报错: ${error_code}, ${error_msg}`)
      return '';
    }
  }
  const fetchOption = buildOption();
  const res = await fetch(hostUrl, fetchOption)
  if(!res.ok) {
    throw await buildError(res)
  }
  const raw = await res.json();
  const _text = buildText(raw);
  return _text;
}

总结

i18n-cli 是一个全自动的国际化插件,可以一键翻译多国语言,同时不会影响项目的业务代码,对于国际化场景是一个很强大的工具。

使用 i18n-cli 可以大大减少项目多语言翻译的工作量,这个插件已经在我们项目中使用很久了,是一个成熟的方案,欢迎大家使用,欢迎提交issues,欢迎star。