背景
相信很多的前端同学在项目中有过做国际化的经验,我来猜猜大家伙做国际化的步骤:是不是先在locale\cn
里面定义好中文,然后马上去谷歌/百度翻译
查好相应的翻译,然后copy
到locale\en
中,一开始我也是诸如这样的步骤去实现,久而久之,发现特别低效,如果你有跟我一样的体验和经历,不妨耐心的继续看下去。
目标
如何去优化我们的国际化开发体验,我们先来想象/预设我们的理想工作状态,我们得出了以下的实现目标。
- 只需要编写
locale\cn
,也就是说,我只需要关注/维护我的中文内容 - 每当我打包/dev/
locale\cn
变化时,自动调用翻译,把locale\en
给更新,然后打包反馈给生产/开发模式下
我认为,我只要实现了以上2个点,我的国际化开发体验将会大大提升,因为不需要频繁得去手动翻译了,专注在我愉快的开发之中~
实现
首先,核心是翻译能力,我选取的是百度翻译,没有为什么,因为他开源又免费
这里贴上百度通用文本翻译api的地址百度翻译开放平台
只要你完成自由开发者注册通过后,拿到自己的appid/key就可以按照api接入文档,实现服务调用翻译能力。
所以,我开始对接api
api翻译
首先,实现的目标是翻译locale\cn
中定义的所有中文
locale\cn
中写好了我们项目需要用到的中文,接着我们开始编写node.js
来实现翻译
在项目根目录
下新建translate.js
,这是我们的node源码
js
// 随机生成10位随机数
function randomNum() {
var num = "";
for (var i = 0; i < 10; i++) {
num += Math.floor(Math.random() * 10);
}
return num;
}
const axios = require("axios");
const md5 = require('md5');
const appId = "xxx"; // 你的开发者appid
const key = "xxx"; // 你的开发者密钥
const api = "https://fanyi-api.baidu.com/api/trans/vip/translate";
const qs = require("qs");
const get = require("lodash/get");
const trim = require("lodash/trim");
async function translateText2En(txt) {
const salt = randomNum();
// 先生成sign
const strLine = `${appId}${txt}${salt}${key}`;
const sign = md5(strLine);
// 设置utf-8
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
const res = await axios.post(api + "?" + qs.stringify({
q: txt,
from: "zh",
to: "en",
appid: appId,
salt,
sign
}));
return get(res.data.trans_result, "[0].dst");
}
function translateLocale() {
// 读取locale下的cn.ts 内容
const fs = require("fs");
const path = require("path");
const cnPath = path.join(__dirname, "/src/locale/cn.ts");
fs.readFile(cnPath, "utf-8", (err, data) => {
if (err) {
console.log("读取cn.ts文件失败");
return;
}
// 读取成功
// console.log(data);
// 正则匹配中文
const reg = /"(.+)": "(.+)",?/g;
const res = data.match(reg);
data = data.replace(/cn/g, "en");
Promise.all(res.map(async (item, index) => {
return new Promise(async (resolve, rej) => {
const [key, value] = item.split(":").map(v => trim(v).replace(/"/g, "").replace(/,/g, ""));
const enValue = await translateText2En(value);
// 将中文替换成英文
data = data.replace(value, enValue);
resolve(data);
})
})).then(() => {
// 写入en.ts文件
const enPath = path.join(__dirname, "/src/locale/en.ts");
fs.writeFile(enPath, data, (err) => {
if (err) {
console.log("写入en.ts文件失败");
return;
}
console.log("写入en.ts文件成功");
});
})
});
}
translateLocale();
实现原理非常简单,正则匹配出对象键值对,然后把中文批量请求百度翻译api
然后把英文和key一起替换cn中的文本内容,最后覆盖locale\en
这一步初步完成了目标1,因为我再也不需要去手动查翻译了
vite插件
接着我们实现目标2,设计vite插件,让这个能力运用到我们的生产开发过程中。
接着我们命名一个translatePlugin.js
这是我们vite插件的源码
javascript
const { exec } = require('child_process');
const { watch } = require('chokidar');
const path = require('path');
function executeCommand(command) {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing the command: ${error.message}`);
reject(error);
}
if (stderr) {
console.error(`Command stderr: ${stderr}`);
reject(stderr);
}
console.log(`Command stdout: ${stdout}`);
resolve(stdout);
});
});
}
export default function translatePlugin() {
let isTranslating = false;
return {
name: 'translate-plugin',
config(config) {
const filePath = path.join(process.cwd(), 'src/locale/cn.ts');
const watcher = watch(filePath);
console.log(filePath)
watcher.on('change', async () => {
if (!isTranslating) {
isTranslating = true;
console.log(`File ${filePath} has changed. Running "node translate" command...`);
await executeCommand('node translate');
isTranslating = false;
console.log('Translation complete. Restarting the Vite server...');
}
});
},
async buildStart() {
console.log('Running "node translate" command...');
await executeCommand('node translate');
}
}
}
主要是2个点需要注意的点
- 监听文件变化
- buildStart钩子
使用我们的插件
javascript
import translatePlugin from './vitePlugin/translatePlugin';
成果展示
这是我的测试页面源码
tsx
import "./index.less";
import useI18n from '@/hooks/useI18n';
import { useLanguageStore } from '@/store/useLanguage';
export default function Test() {
const t = useI18n();
const { toggleLanguage } = useLanguageStore();
return <div>
<div>测试多语言:{t("title")}</div>
<button onClick={toggleLanguage}>改变语言模式</button>
</div>
}
这是我的中文 locale
ts
const cn = {
"title": "关于分享我的Vite 前端国际化方案",
"time": "2024年1月8日",
"author": "Lemon",
};
export default cn;
这是我的英文locale,啥也没有
ts
const en = {
};
export default en;
我们启动项目,一起来看看吧
我们来改动一下locale\cn
总结
目前为止,我们已经初步实现了愉快的国际化开发了
但是优化点还很多
- 缓存翻译结果,避免重复翻译相同的key
- 更合理的watch diff 只翻译更新的内容
- 解决一些语法报错导致的node脚本崩溃问题
如果我的内容有帮助到你,不妨点个赞~
github star or not?