文章目录
- 国际化实现原理
- [基于 vue-i18n V9 的国际化实现方案分析](#基于 vue-i18n V9 的国际化实现方案分析)
- [封装 langSelect 组件](#封装 langSelect 组件)
- [element-plus 国际化处理(旧版本)](#element-plus 国际化处理(旧版本))
- 自定义语言包国际化处理
- 处理项目国际化内容
- 国际化缓存处理(刷新页面后国际化丢失)
- 国际化方案总结
- [关于 element-plus 国际化问题更新](#关于 element-plus 国际化问题更新)
国际化实现原理
我们有一个变量
msg
,但是这个msg
有且只能有两个值:
- hello world
- 你好世界
要求:根据需要切换 msg
的值
js 实现:
- 定义 msg 值的数据源
定义一个对象【messages】,属性是 locale 为语言比如 zh、en 等,value 是一个对象(key、value 的形式),比如定义一个 msg 属性,里面就是对应的值。
-
定义切换变量【定义locale】
-
定义赋值函数 t 【参数为 key,返回messages[locale][key]】
-
为 msg 赋值 【调用 t 函数,传递 key,比如 zh】
html
<script>
// 1. 定义 msg 值的数据源
const messages = {
en: {
msg: 'hello world'
},
zh: {
msg: '你好世界'
}
}
// 2. 定义切换变量
let locale = 'en'
// 3. 定义赋值函数
function t(key) {
return messages[locale][key]
}
// 4. 为 msg 赋值
let msg = t('msg')
console.log(msg);
// 修改 locale, 重新执行 t 方法,获取不同语言环境下的值
</script>
总结:
- 通过一个变量来 控制 语言环境【locale】
- 所有语言环境下的数据源要 预先 定义好
- 通过一个方法来获取 当前语言 下 指定属性 的值 (如上t函数)
- 该值即为国际化下展示值
基于 vue-i18n V9 的国际化实现方案分析
使用 vue-i18n 进行实现(V9 版本)
vue-i18n 的使用可以分为四个部分:
- 创建
messages
数据源 - 创建
locale
语言变量 - 初始化
i18n
实例 - 注册
i18n
实例 (注册到Vue实例上 )
1、 安装
npm install vue-i18n@next
2、 创建 i18n/index.js
文件
创建 messages
数据源 (对象)
javascript
const messages = {
en: {
msg: {
test: 'hello world'
}
},
zh: {
msg: {
test: '你好世界'
}
}
}
3、创建 locale
语言变量 (默认英文)
const locale = 'en'
4、初始化 i18n
实例
lang/index.js
javascript
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
// 使用 Composition API 模式,则需要将其设置为false
legacy: false,
// 全局注入 $t 函数
globalInjection: true,
locale,
messages
})
export default i18n; // 方便其他js文件使用,可以import导入
5、把 i18n
注册到 vue
实例
javascript
export default i18n
6、在 main.js
中导入
javascript
// i18n (PS:导入放到 APP.vue 导入之前,因为后面我们会在 app.vue 中使用国际化内容)
import i18n from '@/i18n'
...
app.use(i18n)
项目中完成国际化分成以下几步进行
- 封装
langSelect
组件用于修改locale
- 导入
el-locale
语言包- 语言包分为两种
- elementUI 语言包
- 自定义的语言(比如一些特殊名称等等)
- 语言包分为两种
- 创建自定义语言包
封装 langSelect 组件
国际化最终会在全局做国际化的内容修改,因此应当把国际化保存到vuex中,还有对应的本地缓存中。
首先在 vuex 中创建国际化相关的变量
javascript
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: {}
}
使用el-dropdown组件
如果当前语言是中文,选型中已经选择的"中文"是灰色的不能点击的。
html
<template>
<el-dropdown
trigger="click" // 点击事件
class="international"
@command="handleSetLanguage" // 点击事件回调方法,当用户选择下拉菜单中的选项时,触发 handleSetLanguage 方法,该方法根据用户选择的语言进行处理。
>el-dropdown上会有提示
<div> // 实现鼠标移入到el-dropdown上会有提示
<el-tooltip content="国际化" :effect="effect"> // effect实现对应样式,tooltip的样式
<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 { defineProps, computed } from 'vue'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus'
defineProps({ // 接收父组件的props
// tooltip的样式
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 => { // 根据el-dropdown-item的command属性
// 切换i18n的locale
i18n.locale.value = lang
// 修改vuex保存的language
store.commit('app/setLanguage', lang)
// 对应的提示
ElMessage.success('更新成功')
}
</script>
具名插槽在组件内部是这样定义的, 在组件内部,el-dropdown
可能像这样定义了 dropdown
插槽:
html
<slot name="dropdown"></slot>
在 navbar
中导入 LangSelect
使用
html
<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-plus
语言包:用来处理element
组件的国际化功能- 自定义语言包:用来处理 非
element
组件的国际化功能
目前最新版本的element-plus
支持 vue-i18n
功能,以前是不支持的,需要自己单独处理。【暂时不管】
在 plugins/index
中导入 element
的中文、英文语言包:
javascript
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import en from 'element-plus/lib/locale/lang/en'
注册 element
时,根据当前语言选择使用哪种语言包
javascript
import store from '@/store'
export default app => {
app.use(ElementPlus, {
locale: store.getters.language === 'en' ? en : zhCn
})
}
自定义语言包国际化处理
自定义语言包我们使用了 commonJS
导出了一个对象,这个对象就是所有的 自定义语言对象
中文语言包
javascript
export default {
login: {
title: '用户登录',
loginBtn: '登录',
usernameRule: '用户名为必填项',
passwordRule: '密码不能少于6位',
desc: `
测试权限账号:<br />
提供三种权限账号:<br />
1. 超级管理员账号: super-admin <br />
2. 管理员账号:admin <br />
3. 测试可配置账号:test <br />
密码统一为:123456 <br />
<br />
导入用户账号:<br />
可使用导入的用户名登录 <br />
密码统一为:123456 <br />
<b>注意:导入用户区分中英文库!!!!</b>
`
},
route: {
profile: '个人中心',
chart: '数据可视化', // ...
英文语言包
javascript
export default {
login: {
title: 'User Login',
loginBtn: 'Login',
usernameRule: 'Username is required',
passwordRule: 'Password cannot be less than 6 digits',
desc: `
Test authority account:<br />
Provide three kinds of authority accounts:<br />
1. Super administrator account: super-admin <br />
2. Administrator account: admin <br />
3. Test configurable account: test <br />
The uniform password is: 123456 <br />
<br />
Import user account:<br />
You can log in with the imported username <br />
The password is unified as: 123456 <br />
<b>Note: Import user-discriminatory Chinese and English libraries! ! ! ! </b>
`
},
route: {
profile: 'Profile',
chart: 'chart',
在 lang/index
中,导入语言包
javascript
import mZhLocale from './lang/zh'
import mEnLocale from './lang/en'
const messages = {
en: {
msg: {
...mEnLocale
}
},
zh: {
msg: {
...mZhLocale
}
}
}
处理项目国际化内容
在 Vue 的模版中使用国际化:
html
<div class="title-container">
<h3 class="title">{{ $t('msg.login.title') }}</h3>
<lang-select class="lang-select" effect="light"></lang-select>
</div>
在 Vue 的 js 代码中使用的话【组件中使用 i18n】:
javascript
// 验证规则
const i18n = useI18n()
const loginRules = ref({
username: [
{
required: true,
trigger: 'blur',
message: computed(() => {
return i18n.t('msg.login.usernameRule')
})
}
],
在 js 代码中直接导入即可
code\src\views\login\rules.js
javascript
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()
}
}
}
国际化缓存处理(刷新页面后国际化丢失)
问题:切换英文后,没有保留,所以刷新页面后又变成默认中文的界面了。
我们希望在 刷新页面后,当前的国际化选择可以被保留 ,所以想要实现这个功能,那么就需要进行 国际化的缓存处理
此处的缓存,我们依然通过两个方面进行:
**vuex**
** 缓存****LocalStorage**
** 缓存**
在 **i18n/index**
中,创建 **getLanguage**
方法:(之前已经缓存过了,这里只需要读取即可)
javascript
import store from '@/store'
/**
* 返回当前 lang
*/
function getLanguage() {
return store && store.getters && store.getters.language
}
修改 createI18n
的 locale
为 getLanguage()
javascript
const i18n = createI18n({
...
locale: getLanguage()
})
国际化方案总结
国际化是前端项目中的一个非常常见的功能,那么在前端项目中实现国际化主要依靠的就是 vue-i18n
这个第三方的包。
而 i18n
的使用,整体来说就分为这么四步:
- 创建
messages
数据源 - 创建
locale
语言变量 - 初始化
i18n
实例 - 注册
i18n
实例
核心的内容其实就是 数据源的部分,但是大家需要注意,如果你的项目中使用了 第三方组件库 ,那么不要忘记 第三方组件库的数据源 需要 单独 进行处理!
关于 element-plus 国际化问题更新
现在 element-plus
已经提供了 国际化的处理方案,我们可以直接通过 el-config-provider
组件中的 locale
属性来指定当前国际化环境。
html
<template>
<el-config-provider :locale="zhCn">
<app />
</el-config-provider>
</template>
<script setup lang="ts">
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
</script>
code\src\App.vue
html
<template>
<el-config-provider :locale="store.getters.language === 'en' ? en : zhCn">
<router-view />
</el-config-provider>
</template>
<script setup>
// ......
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
import en from 'element-plus/lib/locale/lang/en'
// ......
</script>