要求:根据语言环境需要切换
msg的值
<script>
const messages = {
en: {
msg: 'hello world'
},
zh: {
msg: '你好世界'
}
}
let locale = 'en'
function t(key) {
return messages[locale][key]
}
let msg = t('msg')
console.log(msg);
// 修改 locale, 重新执行 t 方法,获取不同语言环境下的值
</script>总结:
在 vue 的项目中,我们不需要手写这么复杂的一些基础代码,可以直接使用 vue-i18n 进行实现(注意:vue3 下需要使用 v9.x 的 vue-i18n)
vue-i18n 的使用可以分为四个部分:
messages 数据源locale 语言变量i18n 实例i18n 实例那么接下来我们就去实现一下:
安装 vue-i18n
npm install vue-i18n@next创建 i18n/index.js 文件
创建 messages 数据源
const messages = {
en: {
msg: {
test: 'hello world'
}
},
zh: {
msg: {
test: '你好世界'
}
}
}创建 locale 语言变量
const locale = 'en'初始化 i18n 实例
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
// 使用 Composition API 模式,则需要将其设置为false
legacy: false,
// 全局注入 $t 函数
globalInjection: true,
locale,
messages
})把 i18n 注册到 vue 实例
export default i18n在 main.js 中导入
// i18n (PS:导入放到 APP.vue 导入之前,因为后面我们会在 app.vue 中使用国际化内容)
import i18n from '@/i18n'
...
app.use(i18n)在 layout/components/Sidebar/index.vue 中使用 i18n
<h1 class="logo-title" v-if="$store.getters.sidebarOpened">
{{ $t('msg.test') }}
</h1>修改 locale 的值,即可改变展示的内容
截止到现在我们已经实现了 i18n 的最基础用法,那么接下来我们就可以在项目中使用 i18n 完成国际化。
项目中完成国际化分成以下几步进行:
langSelect 组件用于修改 localeel-locale 语言包定义 store/app.js
import { LANG } from '@/constant'
import { getItem, setItem } from '@/utils/storage'
export default {
namespaced: true,
state: () => ({
...
language: getItem(LANG) || 'zh'
}),
mutations: {
...
/**
* 设置国际化
*/
setLanguage(state, lang) {
setItem(LANG, lang)
state.language = lang
}
},
actions: {}
}
在 constant 中定义常量
// 国际化
export const LANG = 'language'创建 components/LangSelect/index
<template>
<el-dropdown
trigger="click"
class="international"
@command="handleSetLanguage"
>
<div>
<el-tooltip content="国际化" :effect="effect">
<svg-icon icon="language" />
</el-tooltip>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :disabled="language === 'zh'" command="zh">
中文
</el-dropdown-item>
<el-dropdown-item :disabled="language === 'en'" command="en">
English
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus'
defineProps({
effect: {
type: String,
default: 'dark',
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['dark', 'light'].indexOf(value) !== -1
}
}
})
const store = useStore()
const language = computed(() => store.getters.language)
// 切换语言的方法
const i18n = useI18n()
const handleSetLanguage = lang => {
i18n.locale.value = lang
store.commit('app/setLanguage', lang)
ElMessage.success('更新成功')
}
</script>在 navbar 中导入 LangSelect
<template>
<div class="navbar">
...
<div class="right-menu">
<lang-select class="right-menu-item hover-effect" />
<!-- 头像 -->
...
</div>
</div>
</template>
<script setup>
import LangSelect from '@/components/LangSelect'
...
</script>
<style lang="scss" scoped>
.navbar {
...
.right-menu {
...
::v-deep .right-menu-item {
display: inline-block;
padding: 0 18px 0 0;
font-size: 24px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
}
}
...
}
</style>
截止到目前,我们的国际化内容的基本功能已经处理完成了。接下来需要处理的就是对应的语言包,有了语言包就可以实现整个项目中的所有国际化处理了。
那么对于语言包来说,我们整个项目中会分成两部分:
element-plus 语言包:用来处理 element 组件的国际化功能element 组件的国际化功能那么首先我们先来处理 element-plus 语言包:
**按照正常的逻辑,我们是可以通过 element-ui 配合 vue-i18n来实现国际化功能的,但是目前的 element-plus 尚未提供配合 vue-i18n 实现国际化的方式! **
注:element-plus 后续正式版本已提供 locale 语言包支持,可通过
app.use(ElementPlus, { locale })或ElConfigProvider组件配置。下述内容为课程编写时的临时处理方案,保留作历史参考。
所以说,我们暂时只能先去做临时处理,等到 element-plus 支持 vue-i18n 功能之后,我们再进行对接实现
那么临时处理我们怎么去做呢?
升级 element-plus 到最新版本
npm i element-plus目前课程中使用的最新版本为:^1.1.0-beta.15
升级版本之后,左侧 menu 菜单无法正常显示,这是因为 element-plus 修改了 el-submenu 的组件名称
到 layout/components/Sidebar/SidebarItem 中,修改 el-submenu 为 el-sub-menu
接下来实现国际化
在 plugins/index 中导入 element 的中文、英文语言包:
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import en from 'element-plus/lib/locale/lang/en'注册 element 时,根据当前语言选择使用哪种语言包
import store from '@/store'
export default app => {
app.use(ElementPlus, {
locale: store.getters.language === 'en' ? en : zhCn
})
}处理完 element 的国际化内容之后,接下来我们来处理 自定义语言包。
自定义语言包我们使用了 commonJS 导出了一个对象,这个对象就是所有的 自定义语言对象
大家可以在 资料/lang 中获取到所有的语言包
复制 lang 文件夹到 i18n 中
在 lang/index 中,导入语言包
import mZhLocale from './lang/zh'
import mEnLocale from './lang/en'在 messages 中注册到语言包
const messages = {
en: {
msg: {
...mEnLocale
}
},
zh: {
msg: {
...mZhLocale
}
}
}在处理好了国际化的语言包之后,接下来我们就可以应用国际化功能到我们的项目中
对于我们目前的项目而言,需要进行国际化处理的地方主要分为:
navbar 区域sidebar 区域那么这一小节,我们先来处理前两个
登录页面:
login/index
<template>
<div class="login-container">
...
<div class="title-container">
<h3 class="title">{{ $t('msg.login.title') }}</h3>
<lang-select class="lang-select" effect="light"></lang-select>
</div>
...
<el-button
type="primary"
style="width: 100%; margin-bottom: 30px"
:loading="loading"
@click="handleLogin"
>{{ $t('msg.login.loginBtn') }}</el-button
>
<div class="tips" v-html="$t('msg.login.desc')"></div>
</el-form>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
...
// 验证规则
const i18n = useI18n()
const loginRules = ref({
username: [
{
...
message: i18n.t('msg.login.usernameRule')
}
],
...
})
...
</script>
login/rules
import i18n from '@/i18n'
export const validatePassword = () => {
return (rule, value, callback) => {
if (value.length < 6) {
callback(new Error(i18n.global.t('msg.login.passwordRule')))
} else {
callback()
}
}
}
navbar 区域
layout/components/navbar
<template>
<div class="navbar">
...
<template #dropdown>
<el-dropdown-menu class="user-dropdown">
<router-link to="/">
<el-dropdown-item> {{ $t('msg.navBar.home') }} </el-dropdown-item>
</router-link>
<a target="_blank" href="">
<el-dropdown-item>{{ $t('msg.navBar.course') }}</el-dropdown-item>
</a>
<el-dropdown-item divided @click="logout">
{{ $t('msg.navBar.logout') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>components/LangSelect/index
<el-tooltip :content="$t('msg.navBar.lang')" :effect="effect">
...
const handleSetLanguage = lang => {
...
ElMessage.success(i18n.t('msg.toast.switchLangSuccess'))
}sidebar 区域
目前对于 sidebar 而言,显示的文本是我们在定义路由表时的 title
<span>{{ title }}</span>我们可以 把 title 作为语言包内容的 key 进行处理
创建 utils/i18n 工具模块,用于 将 title 转化为国际化内容
import i18n from '@/i18n'
export function generateTitle(title) {
return i18n.global.t('msg.route.' + title)
}
在 layout/components/Sidebar/MenuItem.vue 中导入该方法:
<template>
...
<span>{{ generateTitle(title) }}</span>
</template>
<script setup>
import { generateTitle } from '@/utils/i18n'
...
</script>
最后修改下 sidebarHeader 的内容
<h1 class="logo-title" v-if="$store.getters.sidebarOpened">
imooc-admin
</h1>面包屑区域:
在 components/Breadcrumb/index
<template>
...
<!-- 不可点击项 -->
<span v-if="index === breadcrumbData.length - 1" class="no-redirect">{{
generateTitle(item.meta.title)
}}</span>
<!-- 可点击项 -->
<a v-else class="redirect" @click.prevent="onLinkClick(item)">{{
generateTitle(item.meta.title)
}}</a>
...
</template>
<script setup>
import { generateTitle } from '@/utils/i18n'
...
</script>
我们希望在 刷新页面后,当前的国际化选择可以被保留,所以想要实现这个功能,那么就需要进行 国际化的缓存处理
此处的缓存,我们依然通过两个方面进行:
vuex 缓存LocalStorage 缓存只不过这里的缓存,我们已经在处理 langSelect 组件时 处理完成了,所以此时我们只需要使用缓存下来的数据即可。
在 i18n/index 中,创建 getLanguage 方法:
import store from '@/store'
/**
* 返回当前 lang
*/
function getLanguage() {
return store && store.getters && store.getters.language
}修改 createI18n 的 locale 为 getLanguage()
const i18n = createI18n({
...
locale: getLanguage()
})国际化是前端项目中的一个非常常见的功能,那么在前端项目中实现国际化主要依靠的就是 vue-i18n 这个第三方的包。
关于国际化的实现原理大家可以参照 国际化实现原理 这一小节,这里我们就不再赘述了。
而 i18n 的使用,整体来说就分为这么四步:
messages 数据源locale 语言变量i18n 实例i18n 实例核心的内容其实就是 数据源的部分,但是大家需要注意,如果你的项目中使用了 第三方组件库 ,那么不要忘记 第三方组件库的数据源 需要 单独 进行处理!