vue国际化教程

 需求背景

项目需求要做国际化,结果网上找了好几篇文章,没有一个可以一次性搞定,现在这里总结一下。首先,我们分为两部分处理,一个是前端页面的静态文字,这个由前端vue.json自行处理。第二部分就是后端的错误消息和日志部分,我们由springboot的拦截器来处理。

i18n介绍

i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数)是“国际化”的简称。

vue前端国际化部分

1、安装i18n插件

安装插件时候,注意必须指定版本号,不然安装会报错。

npm i vue-i18n@8.22.2

2、新建文件夹i18n

文件路径依次是RUOYI-UI/src/utils/i18n。具体的文件结构看图

3、新建i18n.js文件

文件的位置请看上图。

// I18n
import VueI18n from 'vue-i18n'
import Vue from 'vue'
import locale from 'element-ui/lib/locale'// 引入 elementui 的多语言
import enLocale from 'element-ui/lib/locale/lang/en'
import zhCnLocale from 'element-ui/lib/locale/lang/zh-CN'
import zhTwLocale from 'element-ui/lib/locale/lang/zh-TW'
// 如果还有新的语言在下面继续添加// 引入自己定义的 I18n 文件
import myI18nEn from './i18n-en-US.json'
import myI18nZh from './i18n-zh-CN.json'
import myI18nTw from './i18n-zh-TW.json'
// 如果还有新的语言在下面继续添加// 注册 vue-i18n
Vue.use(VueI18n)// 默认中文
const lang = 'zh-CN'
const i18n = new VueI18n({locale: lang,messages: {// 会把myI18nZh的所有内容拷贝到zhCnLocale文件中'zh-CN': Object.assign(zhCnLocale, myI18nZh),'en-US': Object.assign(enLocale, myI18nEn),'zh-TW': Object.assign(zhTwLocale, myI18nTw),// 如果还有新的语言在下面继续添加}
})locale.i18n((key, value) => i18n.t(key, value))
export default i18n

4、修改main.js文件

import Vue from 'vue'import Cookies from 'js-cookie'import Element from 'element-ui'// i18n js
import i18n from './utils/i18n/i18n.js'
// 其余的信息不用修改,就增加上面的i18n js,然后在new Vue中把i18n添加进去new Vue({el: '#app',router,store,i18n,render: h => h(App)
})

5、 页面显示的语法

// title
:title="$t('btnBulkOperations')"// js 
this.$i18n.t('username')// 标签,注意冒号
:label="$t('username')"// 输入框中的占位符,注意冒号
:placeholder="this.$t('username')"// 表格标题
:label="$t('username')"// div语法
<div>{{$t("username")}}</div>// 多个key拼接
:placeholder="`${this.$t('userInput')}${this.$t('userPhone')}`"
:label="`${this.$t('indexTablePrimaryKey')} • ${this.$t('wordName')}`"

6、vue登录页面源码

大家可以参照一下这个登录页面的实现方法

<template><div class="login"><div class="login-title-image"><div style="font-size: 45px;color: #00549e;padding-left: 15px;font-weight: 700;">{{$t("loginLeftWord1")}}</div><div style="font-size: 45px;color: #00549e;padding-left: 80px;font-weight: 700;">{{$t("loginLeftWord2")}}</div></div><el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"><div class="title"><div style="float: left;"><img src="../assets/images/scr_title_logo.png" style="width: 100px;" /></div><div style="float: left;float: left;color: #00549e;font-size: 20px;font-weight: 700;height: 50px;padding-top: 8px;">{{$t("loginFormTitle")}}</div></div><el-form-item prop="username"><el-inputv-model="loginForm.username"type="text"auto-complete="off":placeholder="this.$t('formAccount')"><svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="loginForm.password"type="password"auto-complete="off":placeholder="this.$t('formPassword')"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="code" v-if="captchaEnabled"><el-inputtype="text"v-model="loginForm.code"auto-complete="off":placeholder="this.$t('formCaptcha')"oninput="if(value.length>4)value=value.slice(0,4)"style="width: 63%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div></el-form-item><div style="width: 100%; height: 30px;color: #00549e;"><el-dropdown trigger="click" style="cursor: pointer;"><span class="el-dropdown-link"><div style="float: left;"><img src="../assets/images/changeLanguage.png" style="width: 20px;" /></div><div style="float: left;">{{$t("changeLanguage")}}</div></span><el-dropdown-menu slot="dropdown"><el-dropdown-item v-for="item in language":disabled="item.key==chooseLanguage?true:false":key="item.key":label="item.value":value="item.key"@click.native="handleChangeLanguage(item.key)">{{item.value}}</el-dropdown-item></el-dropdown-menu></el-dropdown></div><el-form-item style="width:100%;"><el-button:loading="loading"size="medium"type="primary"style="width:100%;"@click.native.prevent="handleLogin"><span v-if="!loading">{{$t("btnLogin")}}</span><span v-else>{{$t("btnLogining")}}</span></el-button></el-form-item></el-form><!--  底部  --><div class="el-login-footer"><span>{{$t("loginCopyRight")}}</span></div></div>
</template><script>
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'export default {name: "Login",data() {return {language:[{"key":"zh-CN","value":"简体中文"},{"key":"zh-TW","value":"繁體中文"},{"key":"en-US","value":"English"}],chooseLanguage:"zh-CN",codeUrl: "",loginForm: {username: "",password: "",rememberMe: false,code: "",uuid: "",language:""},loading: false,// 验证码开关captchaEnabled: true,// 注册开关register: false,redirect: undefined};},watch: {$route: {handler: function(route) {this.redirect = route.query && route.query.redirect;},immediate: true}},computed: {loginRules() {let loginRules = {username: [{ required: true, trigger: "blur", message: this.$i18n.t('formAccountRules') }],password: [{ required: true, trigger: "blur", message: this.$i18n.t('formPasswordRules') }],code: [{ required: true, trigger: "blur", message: this.$i18n.t('formCaptchaRules') }],};// 清空表单验证信息this.$nextTick( () => {// 这里不要使用resetFields()方法,否则input无法输入this.$refs['loginForm'].clearValidate();});return loginRules;}},created() {this.setWebLanguage();this.getCode();},methods: {setWebLanguage(){// cookies中是刚才用户选择的语言let language = Cookies.get("flowinnIotLanguage");if(language != null && language != "" && language != undefined){this.chooseLanguage = language;}else{// 如果cookies中没有,说明用户第一次打开系统,直接获取系统的语言let systemLanguage = (navigator.language || navigator.userLanguage).substring(0, 2);if(systemLanguage != null && systemLanguage != "" && systemLanguage != undefined){// 这里获取到的是zh和es,不是zh-CN需要单独处理this.chooseLanguage = systemLanguage;}}if(this.chooseLanguage == "zh" || this.chooseLanguage == "zh-CN"){this.chooseLanguage = "zh-CN";}else if(this.chooseLanguage == "zh-TW"){this.chooseLanguage = "zh-TW";}else{this.chooseLanguage = "en-US";}var time = new Date().getTime();// 设置一个3年的有效期let timeNum = time + (3 * 365 * 24 * 60 * 60);Cookies.set("flowinnIotLanguage", this.chooseLanguage,{ expires: timeNum });// 保存到全局变量中,后面可以使用localStorage.setItem("language", this.chooseLanguage);// 页面刷新语言进行显示this.$i18n.locale = this.chooseLanguage;// 修改页面标题document.title = this.$i18n.t('loginFormTitle');},handleChangeLanguage(languageKey){// 用户修改了显示语言,重新设置语言this.chooseLanguage = languageKey;// 临时保存到cookies中,后面由具体的方法去处理Cookies.set("flowinnIotLanguage", this.chooseLanguage,{ expires: 30 });this.setWebLanguage();},getCode() {getCodeImg().then(res => {this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;if (this.captchaEnabled) {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;}});},handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = true;// 登录的时候把language传递给后台this.loginForm.language = this.chooseLanguage;this.$store.dispatch("Login", this.loginForm).then(() => {this.$router.push({ path: this.redirect || "/" }).catch(()=>{});}).catch(() => {this.loading = false;});}});}}
};
</script>

7、修改request.js

这样就可以保证,每一次请求都从cookies中读取出来用户设置的语言,然后传递到后台

let language = localStorage.getItem("language");
if(!(language != null && language != "" && language != undefined) ){language = 'zh-CN';
}
config.headers['language'] = language;

8、 前端国际化结束

到此前端的国际化就结束了,大家可以试试看。

springboot2后端国际化

1、保存messages.properties

大家先把messages.properties文件中的内容保存到别的地方。

2、删除messages.properties

然后我们需要删除这个文件,重新生成。

3、新建messages.properties

在ruoyi-admin/src/main/resources/i18n这个文件夹上右键New-Resource Bundle

 然后输入messages,个人建议就使用这个固定的messages名字,不然后面的配置中要多一个配置,然后点击右边的加号,添加自己需要的国际化语言,如果要添加中文就输入zh_CN,英文就输入en_US。因为我这里已经存在文件了,所以会有错误提示。

 新建好了之后,显示就会变成下面这个样子,如果大家不是这种结构,那就要删除重新搞,必须是这样的结构才可以。经过上面的操作之后,就会自动把对应的文件都生成。

4、编辑messages.properties

大家双击messages.properties文件,然后在右边点击Resource Bundle,然后在右边就可以一个一个编辑了。

 5、国际化地区语言码对照表

在线国际化地区语言码对照表 - UU在线工具在线国际化地区语言码对照表,提供完整的DNN3支持的多语言地区语言码对照表速查,列出了每个国家对应的语言Locale和国家代码对照表。icon-default.png?t=N7T8https://uutool.cn/info-lang/

6、I18nConfig.java

package com.ruoyi.common.config;import com.ruoyi.common.filter.MyI18nInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@Slf4j
public class I18nConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册拦截器MyI18nInterceptor myHandlerInterceptor = new MyI18nInterceptor();InterceptorRegistration loginRegistry = registry.addInterceptor(myHandlerInterceptor);// 拦截路径loginRegistry.addPathPatterns("/**");}}

7、MyI18nInterceptor.java

package com.ruoyi.common.filter;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;@Slf4j
public class MyI18nInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {final String key = "language";String language = request.getHeader(key);// 前端传递的language必须是zh-CN格式的,中间的-必须要完整,不能只传递zh或enlog.info("当前语言={}",language);Locale locale = new Locale(language.split("-")[0],language.split("-")[1]);// 这样赋值以后,MessageUtils.message方法就不用修改了LocaleContextHolder.setLocale(locale);return true;}/*** 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {}/*** 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}}

8、解决乱码

在线ASCII编码汉字互转icon-default.png?t=N7T8https://www.ip138.com/ascii/打开网站后,把文件中的文字全部复制粘贴,然后勾选不转换字母和数字,点击转换ASCII就行,然后在复制粘贴回去。

9、自定义properties文件名

如果你的名字不叫messages.properties,而是i18nMessage.properties或者其余的名字,那么配置文件中需要修改。注意以下的配置没有经过验证。

@Configuration
public class I18nConfig implements WebMvcConfigurer {/*** 配置 MessageSource 其实这个可以不配置如果不配置请注意 message 多语言文件的位置** @return*/@Beanpublic ResourceBundleMessageSource messageSource() {Locale.setDefault(Locale.CHINESE);ResourceBundleMessageSource source = new ResourceBundleMessageSource();// 这里设置自己的文件名source.setBasenames("i18nMessage");source.setUseCodeAsDefaultMessage(true);source.setDefaultEncoding("UTF-8");return source;}
}

10、后端国际化结束

数据库菜单国际化

1、菜单国际化翻译

把数据库菜单的id和名字都翻译一遍,然后messages.properties文件中的key可以自定义,我这里的规则是menu+id。

 2、菜单国际化转换

/*** 根据父节点的ID获取所有子节点* * @param list 分类表* @param parentId 传入的父节点ID* @return String*/public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId){List<SysMenu> returnList = new ArrayList<SysMenu>();for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();){// 国际化转换SysMenu t = (SysMenu) iterator.next();t.setMenuName(MessageUtils.message("menu"+t.getMenuId()));// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点if (t.getParentId() == parentId){recursionFn(list, t);returnList.add(t);}}return returnList;}

前端页面自定义菜单国际化

1、前端菜单title处理

把自定义菜单中的内容替换为国际化定义的key。

2、菜单国际化代码处理

然后找到src/layout/components/Navbar.vue,在mounted方法中添加下面的代码,直接设置国际化即可。这样也解决了页面刷新后国际化失效和菜单国际化失效的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/82788.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《计算机视觉中的多视图几何》笔记(5)

5 Algorithm Evaluation and Error Analysis 本章主要讲述对算法的验证和误差分析。 概述了两种计算这种不确定性&#xff08;协方差&#xff09;的方法。第一个基于线性近似值&#xff0c;涉及串联各种雅各布表达式&#xff0c;第二个是更容易实施蒙特卡洛方法。 文章目录 …

Pytorch面试题整理(2023.09.10)

1、pytorch如何微调fine tuning&#xff1f; 在加载了预训练模型参数之后&#xff0c;需要finetuning 模型&#xff0c;可以使用不同方式finetune。 局部微调&#xff1a;加载了模型参数后&#xff0c;只想调节最后几层&#xff0c;其他层不训练&#xff0c;也就是不进行梯度…

从命令行管理文件(二)

1.数据流和重定向 1.2数据流 标准输入 (standard input&#xff0c;简称stdin):默认情况下&#xff0c;标准输入指从键盘获取的输入 标准输出(standard output&#xff0c;简称stdout): 默认情况下&#xff0c;命令执行所回传正确的信息会输出到屏幕上 标准错误输出(standard …

Ui自动化测试上传文件方法都在这里了

前言 实施UI自动化测试的时候&#xff0c;经常会遇见上传文件的操作&#xff0c;那么对于上传文件你知道几种方法呢&#xff1f;今天我们就总结一下几种常用的上传文件的方法&#xff0c;并分析一下每个方法的优点和缺点以及哪种方法效率&#xff0c;稳定性更高 被测HTML代码…

【计算机基础】Git系列2:配置多个SSH

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

大数据课程L7——网站流量项目的操作步骤

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解网站流量项目的Spark与HBase整合&#xff1b; ⚪ 掌握网站流量项目的实时流业务处理&#xff1b; 一、 Spark 与 HBase 整合基础 1. 实现步骤&#xff1a; 1. 启动…

各个国家商品条形码

什么是商品条码&#xff1f; 我们常说的条形码其实就是商品条码&#xff0c;它是由一组规则排列的条、空及其对应代码组成&#xff0c;表示商品代码的条码符号&#xff0c;主要用于零售商品、储运包装商品、物流单元、参与方位置等的代码与条码标识。通俗来讲&#xff0c;商品…

Antmonsido(AMS)早期预售(IDO)如何参与?

Antmonsido是Kucoin两年前开始孵化的项目&#xff0c;第一款全链游戏已经开发完成&#xff0c;项目的NFT已经上过币安&#xff0c;前两周刚完成180万美元融资&#xff0c;投资机构也都很强势&#xff0c;IDO注册人数超过三万&#xff0c;海外热度超高&#xff0c;19号上GATE&am…

Java Semaphore使用例子和流程

目录 Semaphore例子代码和输出semaphore.acquire();semaphore.release(); Semaphore semaphore : 英[ˈseməfɔː(r)] 美[ˈseməfɔːr] n. 旗语; 信号标; v. 打旗语; (用其他类似的信号系统)发信号; [例句]Semaphore was widely used at sea, before the advent of electr…

交换机上的PoE技术

交换机的PoE代表Power over Ethernet&#xff08;以太网供电&#xff09;&#xff0c;它是一种技术&#xff0c;允许通过以太网电缆传输数据的同时为连接的设备提供电力供应。 传统上&#xff0c;网络设备&#xff08;如IP电话、无线接入点、网络摄像机等&#xff09;通常需要…

stu01-IDEA怎么创建一个HTML项目

1.打开idea&#xff0c;依次点击file→new→project 2.点击Java&#xff0c;选择你的jdk&#xff0c;没有下载的点击“Download JDK”/已经下载有JDK但在这里没显示的→点击“Add JDK”&#xff0c;选择你安装的JDK的路径&#xff0c;然后next 3.next 4.起好名字&#xff0c;我…

每日一题~二叉树的最近公共祖先

题目连接&#xff1a;236. 二叉树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 思路分析&#xff1a; 由题可知&#xff0c;我们需要找到 p 和 q 两个二叉树的最近公共祖先节点&#xff0c;首先我们分析一下&#xff0c;这个最近公共祖先节点…

【适用于电力系统和音频系统】计算信号的总谐波失真 (THD)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【力扣-每日一题】2560. 打家劫舍 IV

class Solution { public:bool check(vector<int> &nums,int max_num,int k){//只需要计算可以偷的房间。在满足最大值为max_num下时&#xff0c;能偷的最多的房间&#xff0c;与k值比较//如果大于K&#xff0c;说明max_num还可以缩小//如果小于看&#xff0c;说明ma…

Docker 应用部署

Docker 应用部署 一、部署MySQL 搜索MySQL镜像 拉取MySQL镜像 docker pull mysql:8.0创建容器&#xff0c;设置端口映射&#xff0c;目录映射 # 在root/home/mysql目录下创建MySQL目录用于存储MySQL数据信息 mkdir /root/home/mysql cd /root/home/mysql创建并运行 # 330…

Java中StringBuilder常见的操作方法

文章目录 1.创建字符串2.追加字符串3.替换字符串4.删除字符串5.插入字符串6.字符串反转7.其他操作 在 Java 中字符串不属于基本数据类型&#xff0c;而是对象。Java 提供了 String 类来创建和操作字符串&#xff0c;但String 类是不可变类&#xff0c;一旦一个 String 对象被创…

分类预测 | MATLAB实现WOA-CNN-LSTM-Attention数据分类预测

分类预测 | MATLAB实现WOA-CNN-LSTM-Attention数据分类预测 目录 分类预测 | MATLAB实现WOA-CNN-LSTM-Attention数据分类预测分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现WOA-CNN-LSTM-Attention数据分类预测&#xff0c;运行环境Matlab2021b及以…

电脑更换硬盘的时候怎么迁移系统?

为什么需要迁移系统&#xff1f; 在一些关于电脑DIY或Windows相关的论坛社区中&#xff0c;有很多人发帖询问怎么迁移系统。那么这个系统迁移&#xff0c;究竟是何含义呢&#xff1f;通俗易懂地解释一下&#xff0c;就是创建一个完整无缺的操作系统复制品&#xff0c;它与系…

Android StateFlow初探

Android StateFlow初探 前言&#xff1a; 最近在学习StateFlow&#xff0c;感觉很好用&#xff0c;也很神奇&#xff0c;于是记录了一下. 1.简介&#xff1a; StateFlow 是一个状态容器式可观察数据流&#xff0c;可以向其收集器发出当前状态更新和新状态更新。还可通过其 …

【Android知识笔记】FrameWork中的设计模式

一、FrameWork中有哪些设计巧妙之处 例如: Binder调用,模糊进程边界: 屏蔽跨进程IPC通信的细节,让开发者把精力放在业务上面,无需关心进程之间的通信。Bitmap大图传输,高性能: 只传递Binder句柄,到目标进程后做内存映射,不用做大量数据拷贝,速度非常快。Zygote创建进…