花裤衩前辈的vue-element-admin模块在4.x的大版本中去除了对i18n国际化的支持,本次因项目需要,在一个基于 vue-element-admin V4.2.1 版本模板开发的项目中,需要加入中英文切换的支持,此处添加参考了 vue-element-admin 模板早期支持国际化版本的语言切换组件,具体步骤如下。
安装 vue-i18n 插件
npm install --save vue-i18n
安装后 package.json 中如下: node_modules中,如下:
添加自定义配置
创建 src/lang/index.js,其中 elementEnLocale 和 elementZhLocale 是 Element UI 自带的多语言配置,enLocale 和 zhLocale 是自定义的中英文配置,与 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'
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'
import enLocale from './en'
import zhLocale from './zh'
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: Cookies.get('language') || 'zh',
messages: {
en: {
...enLocale,
...elementEnLocale
},
zh: {
...zhLocale,
...elementZhLocale
}
}
});
export default i18n
en.js示例
export default {
route:{
dashboard: 'Dashboard',
},
loginPage:{
username: 'Username',
password: 'Password',
login:'Login',
}
}
zh.js示例
export default {
route: {
dashboard: '首页',
},
loginPage:{
username: '请输入用户名',
password: '请输入密码',
login:'登录',
}
}
在 main.js 中挂载 i18n 插件
import i18n from "@/lang/i18n";
Vue.use(Element, {
size: Cookies.get('size') || 'medium',
i18n: (key, value) => i18n.t(key, value)
});
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
});
修改模板(组件)渲染
当我们引入VueI18n语言插件之后,每个组件实例都拥有了一个$t() 方法,这个方法可以帮助我们进行语言转换,可以根据当前的语言类型,使用传入的key去寻找当前key对应的文本。 用$t('属性名') 来访问配置文件(en.js和ch.js)对象里的属性。这里的$?t() 是引入了i18n之后,自动挂载在vue实例上的功能。
vuex组件 的store模块添加封装方法
src/store/modules/app.js 中添加以下内容
import Cookies from 'js-cookie'
const state = {
language: Cookies.get('language') || 'zh'
}
const mutations = {
SET_LANGUAGE: (state, language) => {
state.language = language
Cookies.set('language', language)
}
}
const actions = {
setLanguage({ commit }, language) {
commit('SET_LANGUAGE', language)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
注意: app.js 的属性中中配置了命名空间 namespaced: true ,其他组件在通过 this.$store.dispatch() 方法调用时,要在 action 方法名之前加上 前缀路径 app/ ,不然会报错 unknown action type:XXX(未知的操作类型:) 。
src/store/getters.js 中添加以下内容
const getters = {
sidebar: state => state.app.sidebar,
size: state => state.app.size,
language: state => state.app.language,
}
export default getters
添加实现中英文切换的封装组件
组件名:LangSelect
<template>
<el-dropdown trigger="click" class='international' @command="handleSetLanguage">
<div>
<svg-icon class-name='international-icon' icon-class="language"/>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="zh" :disabled="language==='zh'">中文</el-dropdown-item>
<el-dropdown-item command="en" :disabled="language==='en'">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
name: "LangSelect",
computed: {
language() {
return this.$store.getters.language
}
},
methods: {
handleSetLanguage(lang) {
this.$i18n.locale = lang
this.$store.dispatch('app/setLanguage', lang)
this.$message.success('switch language success')
location.reload()
}
}
}
</script>
<style scoped>
.international-icon {
font-size: 20px;
cursor: pointer;
vertical-align: -5px!important;
}
</style>
登录页面添加中英文切换组件
登录页位置 src/views/login/index.vue ,添加中英文切换后,登录页面全局代码
<template>
<div class="fullscreen" :style="'background-color: red;background: url('+loginImgJpgUrl+') ;background-size: cover'">
<div class="login-logo" :style="'background: url('+loginLogoImgPngUrl+') no-repeat 10px center'"></div>
<div class="login-box" :style="'background: url('+loginImgPngUrl+') no-repeat center'">
<el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on"
label-position="left" style="display: block;">
<div class="m-list-group" style="">
<el-form-item prop="username">
<el-input ref="usernameRef" v-model="loginForm.username"
:placeholder="$t('loginPage.username')" name="username" type="text"
tabindex="1" autocomplete="on" style="width: 280px;margin-left: 412px">
<template slot="prepend">
<svg-icon style="height: 18px;width: 15px;" color="green" icon-class="user"></svg-icon>
</template>
</el-input>
</el-form-item>
<el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="bottom" manual
style="width: 280px;margin-left: 412px">
<el-form-item prop="password">
<el-input ref="passwordRef" :key="passwordType" v-model="loginForm.password" :type="passwordType"
:placeholder="$t('loginPage.password')"
name="password" tabindex="2" autocomplete="on"
@keyup.native="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter.native="handleLogin"
>
<template slot="prepend">
<svg-icon style="height: 18px;width: 15px;" color="green" icon-class="password"></svg-icon>
</template>
<template slot="suffix">
<div style="color: black;margin-top: 8px;margin-right: 5px" @click="showPwd">
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"></svg-icon>
</div>
</template>
</el-input>
</el-form-item>
</el-tooltip>
<el-form-item>
<el-button :loading="loading" type="primary" style="width: 200px;margin-left: 422px;margin-top: 40px"
@click.native.prevent="handleLogin">{{$t('loginPage.login')}}
</el-button>
<lang-select class="set-language"></lang-select>
</el-form-item>
</div>
</el-form>
</div>
<user-update-pwd
ref="UserUpdatePwdRef"
:first-login="ifFirstLogin"
></user-update-pwd>
</div>
</template>
<script>
import {validUsername} from '@/utils/validate'
import UserUpdatePwd from "@/components/UpdatePwd/UserUpdatePwd";
import LangSelect from '@/components/LangSelect'
export default {
name: 'Login',
components: {
UserUpdatePwd,
LangSelect
},
data() {
const validateUsername = (rule, value, callback) => {
if (!validUsername(value)) {
callback(new Error('Please enter the correct user name'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (value.length < 1) {
callback(new Error('The password can not be less than 1 digits'))
} else {
callback()
}
}
return {
loginImgJpgUrl: require('./components/login.jpg'),
loginImgPngUrl: require('./components/login.png'),
loginLogoImgPngUrl: require('./components/logo.png'),
ifFirstLogin: false,
loginForm: {
username: '',
password: ''
},
loginRules: {
username: [{required: true, trigger: 'blur', validator: validateUsername}],
password: [{required: true, trigger: 'blur', validator: validatePassword}]
},
passwordType: 'password',
capsTooltip: false,
loading: false,
redirect: undefined,
otherQuery: {}
}
},
watch: {
$route: {
handler: function (route) {
const query = route.query
if (query) {
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
}
},
created() {
},
mounted() {
if (this.loginForm.username === '') {
this.$refs.usernameRef.focus()
} else if (this.loginForm.password === '') {
this.$refs.passwordRef.focus()
}
},
destroyed() {
},
methods: {
checkCapslock(e) {
const {key} = e
this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')
},
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.passwordRef.focus()
})
},
handleLogin() {
this.$refs.loginFormRef.validate(valid => {
if (valid) {
this.loading = true
this.$store.dispatch('user/login', this.loginForm)
.then(res => {
console.log('login.vue--user/login--:', res)
let account = res.account
this.ifFirstLogin = res.ifFirstLogin
if (this.ifFirstLogin) {
this.$refs.UserUpdatePwdRef.showDialog(account)
} else {
let ifNotifyUpdatePwd = res.ifNotifyUpdatePwd
if (ifNotifyUpdatePwd) {
this.$notify({
title: '密码更新提示',
dangerouslyUseHTMLString: true,
message: '账户' + '<span style="color: red;font-weight: bolder">' + '长时间未修改密码' + '</span>' + ',请定时更新,确保账户安全!',
type: 'warning',
offset: 100,
duration: 30000
})
}
this.$router.push({path: this.redirect || '/', query: this.otherQuery})
this.loading = false
}
}).catch(error => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
},
getOtherQuery(query) {
return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
}
}
}
</script>
<style lang="scss">
.fullscreen {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.m-list-group {
border-radius: 3px;
padding: 0;
margin: 120px 0 20px;
}
.set-language {
left: 30px;
}
.login-box {
float: right;
position: relative;
width: 720px;
height: 420px;
margin: 0 auto;
padding: 0px 15px;
}
.login-logo {
position: absolute;
width: 80px;
height: 35px;
top: 80px;
left: 120px;
}
.login-box .m-input {
padding: 10px;
border: none;
outline: none;
box-sizing: border-box;
}
.login-box .m-btn {
border-radius: 5px;
font-size: 15px;
height: 35px;
color: #fff;
background-color: #256ddb;
display: inline-block;
text-align: center;
cursor: pointer;
outline: none;
border: 1px solid #256ddb;
box-sizing: border-box;
text-decoration: none;
}
.login-box .m-btn:hover {
background-color: #1e4bae;
}
.login-box .m-btn:active {
opacity: 0.8;
}
@media (max-width: 768px) {
.login-box {
width: auto;
}
}
</style>
至此,登录页面添加中英文切换组件完成, 可正常替换,全局的自定义中英文对照文档zh.js 和en.js 需要一点点的完善。原理是将设置的语言种类存储在浏览器的Cookies中作为全局配置参数,每次渲染页面之前i18n插件从浏览器Cookies中获取此配置参数,转换成对应是语言。 同理登录之后,导航栏的右上角页也需要添加一个中英文切换组件,此处就不再赘述了。
|