记一次前端Vue项目国际化解决方案

这篇具有很好参考价值的文章主要介绍了记一次前端Vue项目国际化解决方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

有一个vue项目,要实现国际化功能,能够切换中英文显示,因为该项目系统的用户包括了国内和国外用户。

需求

1、页面表单上的所有中文标签要国际化,包括表单属性标签、表格列头标签等, title=“数量”;

2、输入框的提示内容需要国际化,如 placeholder=“选择日期”

3、js代码中的提示信息需要国际化,如 message(“请勾选批量设置”)、confirm(‘您确定要设置业务损耗吗?’)、title: ‘删除错误’ 等;

解决方案

1、开发流程,一开始开发过程中,我们不考虑国际化,等代码基本完成后,最后再进行国际化;

2、考虑日后还可能由其他语种,所以这里我们做国际化词语库时,国际化编码使用5位数字,对应多种语言值,即一对多;

3、前端我们重新封装一个全局方法 $lang(param1, param2) 来支持国际化,param1是国际化编码,param2是默认值(如果国际化编码没找到对应的语言单词,则默认用param2,且去掉左右两边的 ‘~’符号);

其实后来又分析了下,如果一开始前端开发人员把所有需要国际化的中文词语,都写成 $lang(‘中文词语’) , $lang方法逻辑再修改下,如果没有第二个参数并且第一个参数对应的国际化词语也没有,则直接显示第一个参数字符串,而且这样的话,到后面再提取代码中的需要国际化的内容时就会很精确了。

4、国际化流程:

  • 从前端代码文件中将所有的中文提取出来,形成一个数组放到一个json文件中,并且数组需要去重一下;
  • 使用第三方的翻译接口,来对导出的中文进行翻译,生成一个中英文对照键值对json文件;
  • 校对中英文对照表,因为有的翻译不一定准确;
  • 根据校对后的中英文对照表,生成国际化编码库,并创建两个国际化文件;
  • 根据校对后的中英文对照表,并分析代码规则,将程序代码中的中文进行国际化处理;

国际化流程实施

在国际化流程实施中,我使用编写js脚本代码来实现相关的处理,使用node环境来执行脚本;

1、提取中文

从前端代码文件中将所有的中文提取出来,形成一个数组放到一个json文件中,并且数组需要去重一下;

下面的代码,是用来提取文件代码中的中文的,我们可以将代码文件命名为extractChinese.js,使用node来执行该脚本;

代码中要国际化的路径设置的是当前目录下的src下的 components和pages文件夹

const fs = require('fs');  
const path = require('path');    
const chineseRegex = /[\u4e00-\u9fa5]+/g;  
  
function extractChineseFromFile(filePath) {  
  const content = fs.readFileSync(filePath, 'utf-8');  
  const chineseWords = content.match(chineseRegex);  
  return chineseWords || [];  
}  
  
function processDirectory(directoryPath) {  
  const files = fs.readdirSync(directoryPath);  
  const chineseSentences = [];  
  
  files.forEach((fileName) => {  
    const filePath = path.join(directoryPath, fileName);  
    const stats = fs.statSync(filePath);
  
    if (stats.isDirectory()) {  
      chineseSentences.push(...processDirectory(filePath));  
    } else if (stats.isFile() && ['.js', '.vue'].indexOf(path.extname(filePath)) > -1) {
      const chineseWords = extractChineseFromFile(filePath);  
      chineseSentences.push(...chineseWords);  
    }  
  });  
  
  return chineseSentences;  
}  
  
function main() {  
  const srcDirectory = path.join(__dirname, 'src');  
  const componentsDirectory = path.join(srcDirectory, 'components');  
  const pagesDirectory = path.join(srcDirectory, 'pages');  
  
  const componentsChineseSentences = processDirectory(componentsDirectory);  
  const pagesChineseSentences = processDirectory(pagesDirectory);  
  const allChineseSentences = [...componentsChineseSentences, ...pagesChineseSentences];  
  
  //const allChineseSentences = componentsChineseSentences;  
  
  const outputPath = path.join(__dirname, 'output.json'); 
  // 使用 Set 对象来去重  
  let backString = Array.from(new Set(allChineseSentences)); 
  // 对去重后的数组进行排序  
  backString.sort();
  fs.writeFileSync(outputPath, JSON.stringify(backString, null, 2), 'utf-8');  
  
  console.log('提取到的中文单词或语句已保存到output.json文件中。');  
}  
  
main();

2、翻译中文

使用第三方的翻译接口,来对导出的中文进行翻译,生成一个中英文对照键值对json文件;

翻译接口,这里我们用的是百度翻译,至于如何去使用百度翻译,这里就不再说了,自己去百度看吧;

该步骤需要用到第一步生成的 output.json 文件,然后翻译结果是存在 translated_zh_en.json 中。

const fs = require('fs');  
const axios = require('axios');  
const appId = '123456789'; // 替换成你的百度翻译的APP ID  
const secretKey = '999999999'; // 替换成你的百度翻译的密钥  
  

const crypto = require('crypto');  
axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8";
  
function md5Hash(input) {  
  // 创建一个哈希对象  
  const hash = crypto.createHash('md5');  
  
  // 更新哈希对象的内容  
  hash.update(input);  
  
  // 获取哈希值的二进制表示  
  const hashBuffer = hash.digest();  
  
  // 将二进制转换为十六进制表示  
  const hashHex = hashBuffer.toString('hex');  
  
  // 返回小写的哈希值  
  return hashHex.toLowerCase();  
}  
  
  
// 使用百度翻译API进行翻译  
async function translateToEnglish(text) { 
    const params = {  
        q: text,  
        appid: appId,  
        salt: Date.now(),  
        from: 'zh',  
        to: 'en',  
        sign: ''  
    };
        
    // 计算签名  
    params.sign = md5Hash(params.appid + params.q + params.salt + secretKey);
    
    // 请求翻译  
    const url = `http://api.fanyi.baidu.com/api/trans/vip/translate?q=${encodeURIComponent(params.q)}&from=zh&to=en&appid=${params.appid}&salt=${params.salt}&sign=${params.sign}`;
    const response = await axios.get(url); 
    //console.log(url);
    //console.log(response.data)
    // 返回翻译结果  
    return response.data.trans_result[0].dst;  
}  

function sleep(ms) {  
  return new Promise(resolve => setTimeout(resolve, ms));  
}  
  
async function mysleep() {  
  console.log('休息1秒......................');  
  await sleep(1000); // 暂停 1 秒  
  console.log('休息完成...');  
} 
  
async function process() {  
    // 读取json文件  
    const data = JSON.parse(fs.readFileSync('output.json', 'utf8'));  
    
    // 存储翻译结果的对象  
    let translationData = {};  
    
    let execNumber = 1;
    // 遍历中文字符串数组,进行翻译
    for (let i = 0; i < data.length; i++) {  
        const chineseString = data[i];  
        const englishString = await translateToEnglish(chineseString);  
        // 将原中文字符串和英文字符串形成键值对存储到translationData对象中  
        translationData[chineseString] = englishString;
        
        if (execNumber >= 120) {   // 如果不想全部执行,则执行多少场退出
            break;
        } else if (i == execNumber*20) { // 每执行20次接口调用,就休息1秒
            execNumber++;
            await mysleep()
        }
    }
     
    // 将翻译结果写入translate.json文件中  
    fs.writeFileSync('translated_zh_en.json', JSON.stringify(translationData, null, 2));  
}  
  
process().catch(error => {  
    console.error(error);  
});

3、校对翻译

校对中英文对照表,因为有的翻译不一定准确;(找个行业英语水平高点的人,自己去校对吧)

4、创建国际化库

根据校对后的中英文对照表,生成国际化编码库,并创建两个国际化文件;

const fs = require('fs');  
  
// 读取原始 JSON 文件  
const data = JSON.parse(fs.readFileSync('translated_zh_en.json', 'utf8'));  
  
// 中文和英文的 JSON 文件  
const chineseData = {};  
const englishData = {};  
  
let serialNumber = 00001;  
  
// 遍历原始数据,生成新的键值对  
for (let chinese in data) {  
  const english = data[chinese];  
  
  // 生成新的键值对,序号为 5 位数字  
  const key = `N${String(serialNumber).padStart(5, '0')}`;  
    
  chineseData[key] = chinese;  
  englishData[key] = english;  
  
  serialNumber++;  
}  
  
// 将中文和英文的 JSON 数据写入文件  
fs.writeFileSync('cn.json', JSON.stringify(chineseData, null, 2));  
fs.writeFileSync('en.json', JSON.stringify(englishData, null, 2));

5、代码国际化处理

根据第4步生成的中文国际化文件 cn.json ,并分析代码规则,将程序代码中的中文进行国际化处理;

首先要分析程序需要国际化的代码规则,因为这个替换不是简单的去就把中文替换,可能代码都由变化,我们分析项目代码中目前的规则如下:

场 景 **代码示例**** **查找内容**** **替换内容****
作为组件元素内容的 <vxe-button @click="closeModel">取消</vxe-button> <span style="color: red;">如调整了颜色尺码,保存后请务必核对检查数量和配色数据!</span> <div class="title">尺码信息</div> >取消< >{{$lang(‘10000’, ‘取消’)}}<
作为组件元素属性值的 <vxe-table-column field="odgc_pcs" title="数量" width="100" header-align="center" align="right"> <el-date-picker v-if="row.type == 'date'" type="date" placeholder="选择日期" v-model="row.value"> title="数量"placeholder=“选择日期” :title=“ l a n g ( ′ 1000 1 ′ , ′  数量   ′ ) " : p l a c e h o l d e r = " lang('10001', '~数量~')":placeholder=" lang(10001, 数量 )":placeholder="lang(‘Ph_select_data’, ‘选择日期’)”
组件模板代码中三元运算结果 <el-button size="mini" @click="alterConsumption(row)">{{onlyShow?'查看':'修改'}}</el-button> ‘查看’:‘查看’ ::‘修改’: ‘修改’ l a n g ( ′ 1000 2 ′ , ′  查看   ′ ) 同上 lang('10002', '~查看~')同上 lang(10002, 查看 )同上lang(‘10003’, ‘修改’)同上
js 中方法参数值 this.$XModal.message("请勾选批量设置", "error"); this.$XModal.confirm('您确定要设置吗?') this.$confirm("确定要删除此记录吗 ?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) message("请勾选批量设置"message('请勾选要批量设置’confirm("您确定要设置吗?"confirm(‘您确定要设置吗?’“提示”,confirmButtonText: “确定”,cancelButtonText: “取消”, message(this. l a n g ( ′ 1000 4 ′ , ′  请勾选批量设置   ′ ) 同上 c o n f i r m ( t h i s . lang('10004', '~请勾选批量设置~')同上confirm(this. lang(10004, 请勾选批量设置 )同上confirm(this.lang(‘10005’, ‘您确定要设置吗?’)同上this. l a n g ( ′ 1000 6 ′ , ′  提示   ′ ) , c o n f i r m B u t t o n T e x t : t h i s . lang('10006', '~提示~'),confirmButtonText: this. lang(10006, 提示 ),confirmButtonText:this.lang(‘10007’, ‘确定’)cancelButtonText: this.$lang(‘10008’, ‘取消’)
js 中对象属性赋值 `this. X M o d a l . m e s s a g e ( m e s s a g e : " 保存失败 " , s t a t u s : " e r r o r " ) ; t h i s . XModal.message({ message: "保存失败", status: "error" }); this. XModal.message(message:"保存失败",status:"error");this.message({ message: ‘请选择要设置的物料!’, type: ‘warning’ }); this. X M o d a l . a l e r t ( m e s s a g e : " 请选择附件分类 " , s t a t u s : " w a r n i n g " , ) ; t h i s . XModal.alert({ message: "请选择附件分类", status: "warning", }); this. XModal.alert(message:"请选择附件分类",status:"warning",);this.XModal.alert({ status: “error”, title: “删除错误”, message: response.msg “服务器删除发生错误”, });`
js 中 || 赋值 `this.$XModal.alert({ status: “error”, title: “删除错误”, message: response.msg “服务器删除发生错误”, });`

替换的脚本代码如下:

const fs = require('fs');
const path = require('path');

// 读取 cn.json 文件并解析 JSON 数据  
function loadTranslations() {
    const cnJsonPath = path.join(__dirname, 'src', 'lang', 'cn.json');
    const content = fs.readFileSync(cnJsonPath, 'utf-8');
    return JSON.parse(content);
}

// 判断字符串是否以指定前缀开头  
function startsWith(str, prefix) {
    return str.startsWith(prefix);
}

/**
 * 每个键值对的场景匹配
 * @param {String} fileContent 文件内容
 * @param {String} key 国际化变量名
 * @param {String} value 中文字符串
 */
function replaceAllScene(fileContent, key, value) {
    
    // 场景:>取消<
    let searchValue = `>${value}<`;
    let replaceValue = `>{{$lang('${key}', '~${value}~')}}<`;
    fileContent = fileContent.split(searchValue).join(replaceValue);  
    
    // 场景:title="数量"
    searchValue = `title="${value}"`;
    replaceValue = `:title="$lang('${key}', '~${value}~')"`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    
    // 场景:placeholder="选择日期"
    searchValue = `placeholder="${value}"`;
    replaceValue = `:placeholder="$lang('${key}', '~${value}~')"`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    
    // 场景:message("请勾选批量设置"
    searchValue = `message("${value}"`;
    replaceValue = `message(this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    // 场景:message('请勾选批量设置'
    searchValue = `message('${value}'`;
    replaceValue = `message(this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    
    // 场景:confirm("您确定要设置业务损耗吗?"
    searchValue = `confirm("${value}"`;
    replaceValue = `confirm(this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    // 场景:confirm('您确定要设置业务损耗吗?'
    searchValue = `confirm('${value}'`;
    replaceValue = `confirm(this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    
    // confirmButtonText: "确定",
    searchValue = `confirmButtonText: "${value}",`;
    replaceValue = `confirmButtonText: this.$lang('${key}', '~${value}~'),`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    // cancelButtonText: "取消",
    searchValue = `cancelButtonText: "${value}",`;
    replaceValue = `cancelButtonText: this.$lang('${key}', '~${value}~'),`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    
    // message: "保存失败"
    searchValue = `message: "${value}"`;
    replaceValue = `message: this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    // message: '保存失败''
    searchValue = `message: '${value}'`;
    replaceValue = `message: this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    
    // title: "删除错误"
    searchValue = `title: "${value}"`;
    replaceValue = `title: this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    // title: '删除错误'
    searchValue = `title: '${value}'`;
    replaceValue = `title: this.$lang('${key}', '~${value}~')`;
    fileContent = fileContent.split(searchValue).join(replaceValue);
    
    
    return fileContent;
}

// 在给定文件中替换指定的字符串  
function replaceStringsInFile(filePath, replacements) {
    const content = fs.readFileSync(filePath, 'utf-8');
    let newContent = content;

    for (const [key, value] of Object.entries(replacements)) {

        // 如果匹配到的字符串前面存在 "message(",则去掉左右两边的双引号  
        //const searchValue = startsWith(value, 'message("') ? value.slice(8, -1) : value;

        newContent = replaceAllScene(newContent, key, value);

        //newContent = newContent.split(searchValue).join("$lang('" + key + "',')" + searchValue + "'");
    }

    if (newContent !== content) {
        fs.writeFileSync(filePath, newContent, 'utf-8');
        console.log(`Replaced strings in ${filePath}`);
    }
}

// 在指定目录下处理所有文件  
function processDirectory(directoryPath, replacements) {
    const files = fs.readdirSync(directoryPath);

    files.forEach((fileName) => {
        const filePath = path.join(directoryPath, fileName);
        const stats = fs.statSync(filePath);

        if (stats.isDirectory()) {
            processDirectory(filePath, replacements);
        } else if (stats.isFile()) {
            replaceStringsInFile(filePath, replacements);
        }
    });
}

function main() {
    const translations = loadTranslations();
    const componentsDirectory = path.join(__dirname, 'src', 'components');
    const pagesDirPath = path.join(__dirname, 'src', 'pages');
    
    processDirectory(componentsDirectory, translations);
    processDirectory(pagesDirPath, translations);
}

main();

到此,我们就完成了前端代码的国际化实现;

我们为什么要把原中文作为 国际化方法 $lang 的第二个参数呢?

因为,如果代码文件中看不到中文,修改代码的时候太难找了,你只能看到国际化数字编码。

建议

建议是在前端一开始开发的时候,就把需要国际化的地方都写成 $lang(‘中文’),包括模板代码和js代码中,

这样后期替换更精确,而且一开始开发人员也不用去管国际化,

并且,我们在提取代码中文时,就可以按 $lang(‘中文’) 这个格式精确提取了,国际化处理后就变成 $lang(‘国际化编码’,‘中文’) ,这样我们在第二次再提取时,就不会重复提取已经国际化处理后的代码中文了。文章来源地址https://www.toymoban.com/news/detail-830996.html

到了这里,关于记一次前端Vue项目国际化解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • electron+vue3全家桶+vite项目搭建【六】集成vue-i18n 国际化

    已发现 9.2.2版本的vue-i18n 如果使用cnpm安装,打包会报错,使用npm或者pnpm安装依赖没有问题 如果需要多语言支持,那么最好在项目搭建之初我们就集成好国际化 vue i18n官网 demo项目地址 关于多窗口国际化不同步更新状态的问题解决方案 1.我们现在src下面创建locals目录,里面创

    2023年04月12日
    浏览(87)
  • 如何优雅的实现前端国际化?

    JavaScript 中每个常见问题都有许多成熟的解决方案。当然,国际化 (i18n) 也不例外,有很多成熟的 JavaScript i18n 库可供选择,下面就来分享一些热门的前端国际化库! i18next 是一个用 JavaScript 编写的全面的国际化框架,提供标准的 i18n 功能,包括复数、上下文、插值、格式等。

    2024年01月23日
    浏览(61)
  • vue2+element-ui使用vue-i18n进行国际化的多语言/国际化

    注意:vue2.0要用8版本的,使用9版本的会报错 在src目录下,创建新的文件夹,命名为i18n zh.js en.js index.js main.js 使用方式一 效果图 使用方式二 效果图 使用方式三,在 效果图 ` 注意:这种方式存在更新this.$i18n.locale的值时无法自动切换的问题,需要刷新页面才能切换语言。解

    2024年02月07日
    浏览(73)
  • 项目国际化的难点痛点是什么

    如果有做过项目国际化的应该了解, 国际化的工作项大概包括以下几项: 【词条相关工作】 文本包裹翻译函数,如 $t 提取翻译词条到 json 文件里 翻译并更新 json 文件 【三方库相关工作】 组件库的国际化配置,如 element-ui 组件库 其他有词条场景的三方库的国际化配置 【图片

    2024年02月03日
    浏览(52)
  • 国内项目国际化后金额处理方案

      目标 : 已有的国内项目,需要部署国际化。需要考虑币种、金额货币精度、多语言、汇率、税等一系列问题。这里主要说的就是其中金额精度的处理。 现状 : 日常国内项目里,界面输入的金额是元,然后数据库存储以及与其他系统交互都是用的分,也是就固定的货币精

    2024年02月05日
    浏览(67)
  • 什么是前端国际化(internationalization)和本地化(localization)?

    聚沙成塔·每天进步一点点 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一个系统而

    2024年02月04日
    浏览(49)
  • 前端网页国际化 translate.js,高效率翻译,傻瓜式教学

    什么?你不信?  那先简单实验一下吧,首先我们随便打开一个网站,然后F12——检查——控制台,复制以下代码,回车即可 选择一个语种,就翻译成功了 在网页底部加上以下代码 就像这样   默认他会给我们加一个id为 translate  的div盒子,我们可以把这个盒子放在我们需要

    2024年02月01日
    浏览(58)
  • Vue 国际化之 vue-i18n 的使用

    目录 一、安装 二、使用 1、准备语言包 2、准备翻译的语言环境 3、实现语言翻译 三、整合 ElementUI 语言包 1、扩展中文 2、扩展英文  3、使用扩展语言翻译 四、问题记录 五、扩展 vue3 中使用 vue-i18n 如果在一个模块系统中使用它,你必须通过  Vue.use()  明确地安装  vue-i18n

    2024年03月14日
    浏览(52)
  • 使用uniapp开发国际化---app,vue,nvue

    hello-i18n 示例工程 - DCloud 插件市场  en.json----\\\"自定义key\\\":\\\"英文\\\"  zh-Hans.json----\\\"自定义key\\\":\\\"中文\\\"      注意:json文件中的名称需要中英文对应。 index.js vue页面模板使用---- $t(\\\'\\\') nvue页面模板使用---- t(\\\'\\\') pages.json 使用 ---- %index.title% data中使用--- this.$t(\\\'\\\') 启动项目,就可以成功切

    2024年02月06日
    浏览(44)
  • Vue - i18n 国际化的使用

    参考网址: 使用: https://huaweicloud.csdn.net/638f133edacf622b8df8eb26.html?spm=1001.2101.3001.6650.1utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Eactivity-1-125181861-blog-123845480.235%5Ev38%5Epc_relevant_anti_t3_basedepth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Ea

    2024年02月11日
    浏览(60)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包