关于分享我的Vite 前端国际化方案

背景

相信很多的前端同学在项目中有过做国际化的经验,我来猜猜大家伙做国际化的步骤:是不是先在locale\cn里面定义好中文,然后马上去谷歌/百度翻译查好相应的翻译,然后copylocale\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?

相关推荐
深情废杨杨3 分钟前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js
GHUIJS3 分钟前
【vue3】vue3.3新特性真香
前端·javascript·vue.js
众生回避9 分钟前
鸿蒙ms参考
前端·javascript·vue.js
洛千陨9 分钟前
Vue + element-ui实现动态表单项以及动态校验规则
前端·vue.js
GHUIJS1 小时前
【vue3】vue3.5
前端·javascript·vue.js
&白帝&1 小时前
uniapp中使用picker-view选择时间
前端·uni-app
魔术师卡颂1 小时前
如何让“学源码”变得轻松、有意义
前端·面试·源码
谢尔登2 小时前
Babel
前端·react.js·node.js
ling1s2 小时前
C#基础(13)结构体
前端·c#
卸任2 小时前
使用高阶组件封装路由拦截逻辑
前端·react.js