若依框架i18n国际化

需求背景

项目需求要做国际化,结果网上找了好几篇文章,没有一个可以一次性搞定,现在这里总结一下。首先,我们分为两部分处理,一个是前端页面的静态文字,这个由前端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、 页面显示的语法

// js 
this.$i18n.t('username')// 标签,注意冒号
:label="$t('username')"// 输入框中的占位符,注意冒号
:placeholder="this.$t('username')"// 表格标题
:label="$t('username')"// div语法
<div>{{$t("username")}}</div>

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、后端国际化结束

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

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

相关文章

Qt下SVG格式图片应用

SVG格式图片介绍 svg格式图片又称矢量图&#xff0c;该种格式的图片不同于png等格式的图片&#xff0c;采用的并不是位图的形式来组织图片&#xff0c;而是采用线条等组织图片&#xff0c;svg格式是图片的文件格式是xml&#xff0c;可以通过文件编译器打开查看svg格式内容。 …

使用Vagrant创建和管理本地Kubernetes(K8s)集群的步骤是什么

文章目录 步骤1&#xff1a;准备环境步骤2&#xff1a;创建Vagrantfile步骤3&#xff1a;启动虚拟机步骤4&#xff1a;安装Kubernetes步骤5&#xff1a;配置Kubernetes网络插件步骤6&#xff1a;将Worker节点加入集群步骤7&#xff1a;验证集群步骤8&#xff1a;部署应用步骤9&…

综合续航达1040公里:腾势计划2024年在香港上市,售价60-100 万

腾势汽车表示&#xff0c;他们计划于2024年在香港地区上市全新的D9车型。这款中大型高端新能源MPV是通过DM-i超级混动技术打造的&#xff0c;由于综合续航能力达到1040公里&#xff0c;且纯电续航最大可达190公里&#xff0c;这款车已经引起了广泛关注。据腾势销售事业部总经理…

[小尾巴 UI 组件库] 全屏响应式轮播背景图(基于 Vue 3 与 Element Plus)

文章归档于&#xff1a;https://www.yuque.com/u27599042/row3c6 组件库地址 npm&#xff1a;https://www.npmjs.com/package/xwb-ui?activeTabreadme小尾巴 UI 组件库源码 gitee&#xff1a;https://gitee.com/tongchaowei/xwb-ui小尾巴 UI 组件库测试代码 gitee&#xff1a…

在ExoPlayer中使用协程:构建强大的Android媒体播放器

在ExoPlayer中使用协程&#xff1a;构建强大的Android媒体播放器 现今的移动应用世界中&#xff0c;媒体消费是用户体验的核心部分。无论是流媒体视频、音乐播放还是处理自适应媒体格式&#xff0c;强大的媒体播放器对于提供无缝和愉悦的用户体验至关重要。而在安卓平台上&…

C高级day4循环语句

1&#xff0c;思维导图 运行结果为&#xff1a; 运行结果为&#xff1a;

CSS读书笔记

——————————————精华部分—————————————— 1、选择器 &#xff08;1&#xff09;基本选择器&#xff1a; 标签选择器 body{} 类选择器 class .class名称{} ID选择器 id #id名称{} 优先级&#xff1a;ID选择器 > 类选择器 > 标签选择器 &am…

cf 交互题

今天cf遇到了交互题&#xff0c;这个交互题的算法很很很简单&#xff0c;但是在交互上卡了&#xff0c;导致交上的代码都不算罚时。&#xff08;更伤心了。 所以&#xff0c;现在写一下交互题的做法&#xff0c;印象深刻嘛。 交互题&#xff0c;就是跟机器进行交互。你代码运…

道路积水监测-路面积水监测系统

随着城市化的不断发展&#xff0c;城市面临着越来越多的交通挑战&#xff0c;其中之一就是道路积水问题。道路积水不仅影响了交通安全&#xff0c;还会引发交通堵塞、交通事故和城市洪涝等问题。因此&#xff0c;开展道路积水监测是十分必要的。 城市排水、供水、燃气、供热、桥…

基于大规模MIMO通信系统的半盲信道估计算法matlab性能仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 %EM算法收敛所需的迭代 nIter 1; Yp Y(:,1:L_polit,:); %与导频序列相对应的部分 q…

建议收藏!TCP协议面试灵魂12 问

先亮出这篇文章的思维导图: TCP 作为传输层的协议&#xff0c;是一个IT工程师素养的体现&#xff0c;也是面试中经常被问到的知识点。在此&#xff0c;我将 TCP 核心的一些问题梳理了一下&#xff0c;希望能帮到各位。 001. 能不能说一说 TCP 和 UDP 的区别&#xff1f; 首先…

如何查询成绩或工资

为什么每次查询成绩或者工资的时候都觉得麻烦又耗时呢&#xff1f;在过去&#xff0c;我们可能需要去学校或公司的相关部门&#xff0c;填写繁琐的表格&#xff0c;然后等待工作人员进行查询和处理。这不仅浪费了我们宝贵的时间&#xff0c;还可能出现查询结果不准确或者遗漏的…

芯科蓝牙BG27开发笔记4-SSV5 IDE的使用

1. 如何转移工作区的项目文件到新的文件夹&#xff0c;并且可以继续使用ssv5编辑、编译&#xff1f; 从默认的工作区将目标工程整体拷贝出来 目标文件夹&#xff1a; 进入ssv5点击导入工程&#xff0c;并选择目标文件夹 继续下一步&#xff0c;修改项目文件夹所在位置为其源码…

tcp与udp

tcp 服务端回复完SYNACK之后&#xff0c;就建立连接 1.为什么是三次&#xff0c;而不是两次&#xff1f;服务端回复完SYNACK之后&#xff0c;就建立连接 这是为了防止因为已失效的请求报文&#xff0c;突然又传到服务器引起错误 意思就是&#xff1a;假设采用两次握手建立连…

聚观早报|华为Mate 60 Pro支持面容支付;特斯拉重回底特律车展

【聚观365】9月8日消息 华为Mate 60 Pro已支持面容支付 特斯拉将重回底特律车展 iPhone在美国有1.67亿用户 韩国半导体8月份出口85.6亿美元 比亚迪元PLUS冠军版将于9月15日上市 华为Mate 60 Pro已支持面容支付 毫无预热的华为Mate 60 Pro突然在华为商城首批开售&#xf…

kafka增加磁盘或者分区,topic重分区

场景&#xff1a;kafka配置文件log.dirs增加了几个目录&#xff0c;但是新目录没有分区数据写入&#xff0c;所以打算进行重分区一下。 1.生成迁移计划 进入kafka/bin目录 新建 topic-reassign.json,把要重分区的topic按下面格式写。 { "topics": [{ …

云原生Kubernetes:Kubeadm部署K8S单Master架构

目录 一、理论 1.kubeadm 2.Kubeadm部署K8S单Master架构 3.环境部署 4.所有节点安装docker 5.所有节点安装kubeadm&#xff0c;kubelet和kubectl 6.部署K8S集群 7.安装dashboard 8.安装Harbor私有仓库 9.内核参数优化方案 二、实验 1.Kubeadm部署K8S单Master架构 …

三维模型3DTile格式轻量化压缩处理效率提高的技术方浅析

三维模型3DTile格式轻量化压缩处理效率提高的技术方浅析 随着三维模型在各个领域的广泛应用&#xff0c;对于其格式的轻量化压缩处理和效率提高的需求也越发迫切。本文将介绍一些技术方法&#xff0c;帮助实现三维模型3DTile格式的轻量化压缩处理并提高处理效率。 首先&#x…

Python + Jmeter 实现自动化性能压测

Step01: Python脚本开发 文件路径&#xff1a;D://wl//testproject//Fone-grpc//project1//test_client.py Python 脚本作用&#xff1a; 1.通过 grpc 调用底层 c 的接口&#xff0c;做数据库的数据插入与查询操作&#xff0c;然后将返回的结果进行拼接与输出。 2.代码里面…

华为OD七日集训第4期 - 按算法分类,由易到难,循序渐进,玩转OD

目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第4期五、精心挑选21道高频100分经典题目&#xff0c;作为入门。第1天、数据结构第2天、滑动窗口第3天、贪心算法第4天、二分查找第5天、分治递归第6天、深度优先搜索dfs算法第7天、宽度优选算法&#xff0c;回溯法 六…