需求:后台管理系统,可以实现语言切换
实现过程:用的i18n来实现的语言切换,网上能看到好多模板,根据自己的需求,修改一下即可使用,大概都是差不多的,因为涉及到后端,所以要跟后端协商一致决定去写,我的设计思路是跟着后端设计更改的,如下:
1.语言是后端接口返回的,不是前端写死的(eg:中文、English),由于我的切换语言,设计到了两个地方,一个是登录页面,一个是登录之后的页面,后端不能给一个接口,要区分两个接口给前端,所以关于这个需求,前端自己加了判断(如果你们后端给一个接口,则可忽略我写的判断)
2.根据自己选择的哪一种语言,需要通过接口传给后端,后端会将其存到某个用户表里,这个接口也是两个,也需写判断
3.如果用户是从来没有选择过语言的用户,则前端需要规定默认语言,且要与后端的默认语言保持一致,于是和后端协商一致决定,其默认语言是后端返回的语言的第一个
4.如果后端有选择是哪个语言,我们将其传给了后端,后端讲其存入了用户表,用户登录之后,会在用户接口里,返回给我们,我们将其存 localStorage,这时候,就算用户再次退出到登录页面,我们就可以将用户默认语言做一个判断,判断存入localStorage的language是否有值,如果有值,则登录页面的语言取localStorage的language,如果没有,则还是取后端语言接口返回的第一个值,这样,就可以把用户已经选择过的语言,在登录页面也能进行判断用户习惯
文档: vue-i18n
一.安装vue-i18n
npm install vue-i18n
二.在mian.js引入
//i18n
import i18n from "@/lang";
Vue.use(Element,{
size:Cookies.get('size') || 'small',
i18n:(key,value)=>i18n.t(key,value)
})
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})
三.在src下创建一个lang文件夹,其中包括en.js、zh.js、index.js文件
1.src/lang/en.js
export default {
login: {
username:'username',//用户名
password:'password',//密码
code:'code',//验证码
login:'login',//登录
logging: 'logging...',//登录...
storage:'remember the password',//记住密码
},
home:{
welcome:'Welcome to use',//欢迎使用
},
route:{
homepage:'Home page',//首页
profile:'Profile',//个人中心
系统管理:"system manage",//系统管理
系统监控:'system monitor',
系统工具:"system tools",
创建运单:"create waybill",
批量运单:'waybill batch',
运单管理:"waybill manage",
审核管理:'audits manage',
充值管理:'recharge manage',
失败订单详情:'failed order details',
运费管理:'freight manage',
账单管理:'bill manage',
仓库管理:'warehouse manage',
用户管理:'user query',
角色管理:'user query',
菜单管理:'menu manage',
部门管理:'dept manage',
岗位管理:'position manage',
字典管理:'dictionary manage',
参数设置:'parameter settings',
通知公告:'notice announcent',
日志管理:'log manage',
操作日志:'operation log',
登录日志:'login log',
在线用户:'online users',
定时任务:'scheduled tasks',
数据监控:'data monitor',
服务监控:'service monitor',
运单审核:'audits order',
充值审核:'audits recharge',
客户充值:'customer recharge',
我的充值:'my recharge',
基础运费:'basic freight',
其他附加费:'other surcharges',
燃油附加费:'fuel surcharge',
旺季附加费:'peak surcharge',
全部账单:'bill whole',
我的账单:'bill my',
订单详情:'order details',
仓库配置:'warehouse configuration',
基础成本价:'basic cost',
其他成本价:'other cost',
旺季成本价:'peak cost',
查询:'query',
新增:'add',
修改:'alter',
导入:'import',
导出:'export',
删除:'remove',
重置密码:'reset passwords',
仓库:'warehouse',
批量强退:'batch forcing',
单条强退:'single strong back',
众拓网通官网:'Zhongtuo Netcom official website',
下载失败订单:'download failed order',
下载面单:'download sheet',
客户查询:'customer query',
已取消导出:'export cancelled',
取消审核中导出:'export cancel audit',
审核:'audit',
审核平账:'audit and balance accounts',
审核平账明细:'review the balance of accounts details',
充值:'top up',
充值记录:'recharge record',
消费记录:'expense calendar',
补交凭证:'resubmit documents',
回显:'echo',
审核明细:'audit detail',
充值明细:'top-up details',
失败订单详情查询:'failed order details query',
复制:'copy',
基础运费列表:'base freight list',
客户列表:'customer list',
},
}
2.src/lang/zh.js
export default {
login: {
username:'用户名',//用户名
password:'密码',//密码
code:'验证码',//验证码
login:'登录',//登录
logging: '登录...',//登录...
storage:'记住密码',//记住密码
},
home:{
welcome:'欢迎使用',//欢迎使用
},
route:{
homepage:'首页',//首页
profile:'个人中心',//个人中心
系统管理:"系统管理",//系统管理
系统监控:'系统监控',
系统工具:"系统工具",
创建运单:"创建运单",
批量运单:'批量运单',
运单管理:"运单管理",
审核管理:'审核管理',
充值管理:'充值管理',
失败订单详情:'失败订单详情',
运费管理:'运费管理',
账单管理:'账单管理',
仓库管理:'仓库管理',
用户管理:'用户管理',
角色管理:'角色管理',
菜单管理:'菜单管理',
部门管理:'部门管理',
岗位管理:'岗位管理',
字典管理:'字典管理',
参数设置:'参数设置',
通知公告:'通知公告',
日志管理:'日志管理',
操作日志:'操作日志',
登录日志:'登录日志',
在线用户:'在线用户',
定时任务:'定时任务',
数据监控:'数据监控',
服务监控:'服务监控',
运单审核:'运单审核',
充值审核:'充值审核',
客户充值:'客户充值',
我的充值:'我的充值',
基础运费:'基础运费',
其他附加费:'其他附加费',
燃油附加费:'燃油附加费',
旺季附加费:'旺季附加费',
全部账单:'全部账单',
我的账单:'我的账单',
订单详情:'订单详情',
仓库配置:'仓库配置',
基础成本价:'基础成本价',
其他成本价:'其他成本价',
旺季成本价:'旺季成本价',
查询:'查询',
新增:'新增',
修改:'修改',
导入:'导入',
导出:'导出',
删除:'删除',
重置密码:'重置密码',
仓库:'仓库',
批量强退:'批量强退',
单条强退:'单条强退',
众拓网通官网:'众拓网通官网',
下载失败订单:'下载失败订单',
下载面单:'下载面单',
客户查询:'客户查询',
已取消导出:'已取消导出',
取消审核中导出:'取消审核中导出',
审核:'审核',
审核平账:'审核平账',
审核平账明细:'审核平账明细',
充值:'充值',
充值记录:'充值记录',
消费记录:'消费记录',
补交凭证:'补交凭证s',
回显:'回显',
审核明细:'审核明细',
充值明细:'充值明细',
失败订单详情查询:'失败订单详情查询',
复制:'复制',
基础运费列表:'基础运费列表',
客户列表:'客户列表',
},
}
3.src/lang/index.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie'
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
import enLocale from './en'
import zhLocale from './zh'
Vue.use(VueI18n)
const messages = {
en: {
...enLocale,
...elementEnLocale
},
zh: {
...zhLocale,
...elementZhLocale
}
}
export function getLanguage() {
const chooseLanguage = Cookies.get('language')
if (chooseLanguage) return chooseLanguage
return 'en'
}
const i18n = new VueI18n({
locale: getLanguage(),
messages
})
export default i18n
三.在src/components下创建到LangSelect/index.vue文件(语言切换组件)
{{item.name}}
四.在src/store/modules/app.js 里将language存入vuex和cookie
import Cookies from 'js-cookie'
import { getLanguage } from '@/lang/index'
const state = {
sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
withoutAnimation: false,
hide: false
},
device: 'desktop',
language: getLanguage(),
size: Cookies.get('size') || 'medium'
}
const mutations = {
TOGGLE_SIDEBAR: state => {
if (state.sidebar.hide) {
return false;
}
state.sidebar.opened = !state.sidebar.opened
state.sidebar.withoutAnimation = false
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
} else {
Cookies.set('sidebarStatus', 0)
}
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 0)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
},
//语言
SET_LANGUAGE: (state, language) => {
state.language = language
Cookies.set('language', language)
},
SET_SIZE: (state, size) => {
state.size = size
Cookies.set('size', size)
},
SET_SIDEBAR_HIDE: (state, status) => {
state.sidebar.hide = status
}
}
const actions = {
toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR')
},
closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
//语言
setLanguage({ commit }, language) {
commit('SET_LANGUAGE', language)
},
setSize({ commit }, size) {
commit('SET_SIZE', size)
},
toggleSideBarHide({ commit }, status) {
commit('SET_SIDEBAR_HIDE', status)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
五.src/modules/user.js,将后端用户信息接口返回的language存入localStorage里
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
const user = {
state: {
token: getToken(),
name: '',
avatar: '',
roles: [],
permissions: [],
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
},
},
actions: {
// 登录
Login({ commit }, userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
//语言
const languageId=userInfo.languageId
return new Promise((resolve, reject) => {
login(username, password, code, uuid,languageId).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(res => {
const user = res.user
const avatar = user.avatar == "" ? require("@/assets/image/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', res.roles)
commit('SET_PERMISSIONS', res.permissions)
sessionStorage.setItem('infoCustomers',JSON.stringify(res.customers))
sessionStorage.setItem('infouser',JSON.stringify(res.user.roles))
//注:用户已进入页面,调用到用户接口,将用户接口里面的语言存储到localStorage,可以方便用户在登录页面的时候判断语言是中文还是英文(登录页面语言应与user接口保持一直)
localStorage.setItem('language',JSON.stringify(res.user.language.language).replace(/\"/g, ""))
} else {
commit('SET_ROLES', ['ROLE_DEFAULT'])
}
commit('SET_NAME', user.userName)
commit('SET_AVATAR', avatar)
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 退出系统
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMISSIONS', [])
removeToken()
resolve()
}).catch(error => {
reject(error)
})
})
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
}
export default user
六.登录页面
效果图:
1.en:
2.English:
{{setName}}
{{$t('login.storage')}}
{{$t('login.login')}}
{{$t('login.logging')}}
七.在utils里面新建一个i18n.js
export function generateTitle(title) {
const hasKey = this.$te('route.' + title)
if (hasKey) {
// $t :this method from vue-i18n, inject in @/lang/index.js
const translatedTitle = this.$t('route.' + title)
return translatedTitle
}
return title
}
七.根据语言的切换,面包屑的语言也随之更新,src/components/Breadcrumb文件里
1.zh:
2.en:
{{generateTitle(item.meta.title)}}
{{generateTitle(item.meta.title)}}
八.src\layout\components\Sidebar\SidebarItem.vue文件
1.zh
2.en
我用的是若依后台管理系统框架,对于路由是后端接口返回的,所以,路由语言切换只有两种实现方式 ,后端接口返回,缺点,每次新增修改路由,后端都要在数据库里面新增、修改两种语言,较为麻烦,所以路由的语言切换就纯前端进行控制。
需要注意的是,对于首页,是前端写的路由,其余的页面路由都是后端接口返回的,所以在前端,针对首页,需要改的是router/index.js
九.菜单管理,和路由同理,都是后端接口返回,接口返回的语言切换,纯前端处理
1.zh
2.en
十.样式处理,可能中文的长度很短,英文长度很长,样式就会错乱,对于这种情况进行处理
getLabelWidth(){
let labelWidth = '80px';
if(this.language=='en'){
labelWidth = '150px';
}
return labelWidth;
},
十一.对于校验的地方,语言切换之后,并不会再次触发校验的语言切换 ,关于这个点,我是进行了监听,语言是否改变,如果语言改变了,我就再次触发校验,缺点,每次切换语言就会出发校验,并不是,我点击了某个提交按钮,进行的校验触发,例如我上面写的登录页面上的校验
以上就是整个实现过程啦,可能有点地方忘记写了,等想到了,再进行完善