关于分享我的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?

相关推荐
我要洋人死1 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼3 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風7 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#