我所负责的项目是服务于多国家的,便有了国际化这一需求,以我自己项目为例,为大家详细介绍jeecg如何实现前端部分的国际化。
一、需求
页面上要有个切换语种的下拉框,并且每次切换后,下次在登陆进来要默认显示上次切换过的,由于每次切换后,部分内容翻译不完全,所以每次切换我都进行了页面刷新:location.reload()
看到评论区有同行问关于菜单的问题,就在这里补充一下,以上所说的部分内容翻译不完全所指的便是菜单了,因为菜单数据是后端接口(sys/permission/getUserPermissionByToken)返回的。这里可以给大家说一下我们团队当时的解决办法:
前端项目结构里的\src\store\modules\user.js这个文件,获取菜单信息的queryPermissionsByUser请求里多加一个语种参数给后端,每次前端切换语种之后刷新页面都会重新进到这个请求,后端收到请求之后会根据前端给到的语种参数来返回当前设定的语种相关的菜单。
二、官方文档参考
jeecg国际化改造方案:国际化改造方案 · JeecgBoot 开发文档 · 看云
UI组件国际化说明:https://www.antdv.com/docs/vue/i18n-cn/
三、业务需求部分的国际化
1)、安装i18n(可以看一下自己的package.json文件里有没有i18n,不出意外的话,Jeecg有)
npm install vue-i18n
2)、创建js语种文件
一般是在 src/components/lang/ 中 创建语言js:en-US.js 和 zh-CN.js,个人觉得json格式比js严谨,所以我用的json,这个看个人习惯了~
{
"textPrompt":{
"fillIn":"Please fill in"
},
"language":"Language",
"button":{
"searchQuery":"Search"
}
}
如果习惯用js:
export default{
"textPrompt":{
"fillIn":"Please fill in"
},
"language":"Language",
"button":{
"searchQuery":"Search"
}
}
3)、main.js
// 注册i18n实例并引入语言文件
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
function main() {
let lang = '';
//获取缓存有没有语种(为什么获取呢?因为用户上次登陆如果切换了语种,那么再次登陆默认语种应该维持上次选的)
//而只有切换过语种 才会记录到缓存里 如果该用户从来都没有切换过 就走else 读取用户浏览器的默认语种
if(localStorage.getItem(config.storageOptions.namespace+'language')){
lang = JSON.parse(localStorage.getItem(config.storageOptions.namespace+'language')).value.key;
}else{
lang = navigator.language||navigator.userLanguage;//常规浏览器语言和IE浏览器
}
const i18n = new VueI18n({
locale: lang,//读取当前语种
messages: {//这里的内容 有多少语言文件就要写多少,路径自己定义,名字建议就这样,后期使用方便,内容后面会贴上
'zh-CN': require('@/components/lang/zh-CN.json'),
'en-US': require('@/components/lang/en-US.json'),
'zh-TW': require('@/components/lang/zh-TW.json')
}
});
//这一块 是为了做缓存的监听 原因是 我们只能通过选项去翻译我们自己的代码
//而antd的框架翻译要靠app.vue里的语种设置 而import不能引入变量 所以这里要监听缓存 因为每次切换语言缓存都会发生改变
//具体监听下面的app.vue会提到
//全局获取缓存数据
Vue.prototype.resetSetItem = function (key, newVal) {
// 创建一个StorageEvent事件
var newStorageEvent = document.createEvent('StorageEvent');
const storage = {
setItem: function (k, val) {
localStorage.setItem(k, val);
// 初始化创建的事件
newStorageEvent.initStorageEvent('setItem', false, false, k, null, val, null, null);
// 派发对象
window.dispatchEvent(newStorageEvent)
}
}
return storage.setItem(key, newVal);
}
new Vue({
i18n
})
}
4)、App.vue
<a-config-provider :locale="localeLange">
<div id="app">
<keep-alive>
<router-view v-if="keepAlive" />
</keep-alive>
<router-view v-if="!keepAlive" />
</div>
</a-config-provider>
export default {
data () {
return {
localeLange: enUS,
}
},
methods: {
langFun(str){
console.log(str)
//这里循环一定要用switch 因为我们缓存拿到的是字符串 不能作为变量直接给this.localeLange,要把import进来的给它才行
switch(str) {
case 'idID':
this.localeLange=idID;
break;
case 'enUS':
this.localeLange=enUS;
break;
case 'zhTW':
this.localeLange=zhTW;
break;
case 'viVN':
this.localeLange=viVN;
break;
default:
this.localeLange=enUS;
}
},
},
created () {
let lang = "";
if(localStorage.getItem(config.storageOptions.namespace+'language')){
lang = JSON.parse(localStorage.getItem(config.storageOptions.namespace+'language')).value.key
}else{
lang = "en-US"
}
let str = lang.split('-')[0]+lang.split('-')[1];
console.log(str)
this.langFun(str);
},
//这里做监听 一旦更改了语言 就会触发这里的方法 从而翻译antd框架内置的文字
mounted(){
window.addEventListener('setItem', ()=> {
let lang = JSON.parse(localStorage.getItem(config.storageOptions.namespace+'language')).value.key;
let str = lang.split('-')[0]+lang.split('-')[1];
console.log("監聽的輸出",lang )
this.langFun(str);
})
},
}
5)、添加切换语种的select——components/tools/UserMenu.vue
<div class="user-wrapper" :class="theme">
<span>Language</span>
<a-select
label-in-value
:default-value="{ key: langs }"
style="width: 150px;margin-left:10px;"
@change="handleSetLanguage"
>
<a-select-option v-for="(item, key) in langListData" :key="key" :value="item.value">
{{ item.text }}
</a-select-option>
</a-select>
</div>
import Vue from 'vue'
import config from '@/defaultSettings'
data(){
return{
langs:navigator.language||navigator.userLanguage,//读取浏览器默认语言
}
},
created() {
//获取缓存有没有语种(为什么获取呢?因为用户上次登陆如果切换了语种,那么再次登陆默认语种应该维持上次选的)
//而只有切换过语种 才会记录到缓存里 如果该用户从来都没有切换过 就走else 读取用户浏览器的默认语种
if(localStorage.getItem(config.storageOptions.namespace+'language')){
this.langs = JSON.parse(localStorage.getItem(config.storageOptions.namespace+'language')).value.key
}else{a
//这个地方如果不想用读取浏览器的方式 也可以地址栏url带参 拿到参数后存到缓存 读缓存就行 具体就不写了
this.langs = navigator.language||navigator.userLanguage
}
},
methods: {
handleSetLanguage(lang) {
this.$i18n.locale = lang.key; //改变当前语言
Vue.ls.set("language", lang);//将lang 语言存在localStorage里,main里面就会根据属性值进行判断 locale: Vue.ls.get("language", "zh-CN")
this.resetSetItem(lang.key,'false');//监听缓存
}
}
四、使用($t和this.$t)
$t('...')和this. $t('...')里面的“...”的内容,即文字翻译的维护,见本文开头第二点,这里不再做介绍了
1)、html
$t('...')
<a-form-item :label="$t('language')">
<a-input
:placeholder="$t('textPrompt.fillIn')+' '+$t('language')"
v-model="queryParam.xxx">
</a-input>
</a-form-item>
<a-button
type="primary"
@click="searchQuery"
icon="search"
>
{{$t('button.searchQuery')}}
</a-button>
2)、js
this.$t('...')
columns: [
{
title: this.$t('language'),
align: 'center',
}
]
this.$message.warning(this.$t('textPrompt.fillIn')+' '+this.$t('language'));
3)、补充
关于table分页 ,1-20 共 30 条 这类的字眼,“共”和“条”是在JeecgListMixin.js是在里写死的,所以国际化记得改这里
showTotal: (total, range) => {
return range[0] + "-" + range[1] + " " +this.$t("ipagination.total")+ " " + total+ " " + this.$t("ipagination.items")
},
}