不要问为啥要使用国际化(vue-i18n),现在很多主流的UI框架,上去你会发现他们的文档都是支持多语言的。那我们在工作中难免会有潜在的客户是来自其他国家的。随着时间的推移可能还会支持更多的语言种类,那程序的设计就很至关重要,不可能说多一个国家的客户,我们就去改一次程序。合理的方案应该是运维或者不懂开发的同事直接通过编辑文件或者数据库数据就能增加一种语言才是最实际的。至于采用存储文件还是数据库。根据自己的需求合理安排就好。
程序我已经搭建好了Vite4+Pinia2+vue-router4+ElmentPlus搭建Vue3项目(组件、图标等按需引入)
一、安装vue-i18n和js-cookie 🌟 🌟 🌟
这里用到cookie的原因是,比如我们设置的是中文,那么我们后续进入系统默认的就应该是中文。
yarn add vue-i18n js-cookie -S
yarn add @types/js-cookie -D
二、开始编写cook和i18n逻辑 ✨ ✨ ✨
1、新建文件src/utils/cookies.ts
// @ts-ignore
import Cookies from 'js-cookie'
// key自己随意设置
const languageKey = 'ts_EtcEnd_language'
export const getLanguage = () => Cookies.get(languageKey)
export const setLanguage = (language: string) => Cookies.set(languageKey, language)
2、新建文件src/basics/lang/zh.ts和en.ts分别代表中文和英文
我是做了分类,如果全写在一个对象里面,难免会有重复的key。所以分类能够尽量去减少这个问题的出现。rouer代表路由,system代表系统,login代表登录。
export default {
route: {
home: '首页',
home1: '首页1'
},
system: {
title: 'Etc.End的演示案例',
},
login: {
userName: '请输入用户名',
passWord: '请输入密码',
logIn: '登录',
userNameMessage: '请输入正确的用户名',
passWordMessage: '密码长度为6~30'
}
}
export default {
route: {
home: 'Home',
home1: 'Home1',
},
system: {
title: 'Etc.End Demonstrate Case',
},
login: {
userName: 'Please input userName',
passWord: 'Please input password',
logIn: 'Login',
userNameMessage: 'Please enter the correct user name',
passWordMessage: 'The password length is 6 ~ 30'
}
}
3、新建文件src/basics/lang/index.ts
// @ts-ignore
import { createI18n } from 'vue-i18n'
import { getLanguage } from '@/utils/cookies'
import enLocale from './en'
import zhLocale from './zh'
const messages = {
en: {
...enLocale
},
'zh-cn': {
...zhLocale
}
}
export const getLocale = () => {
//读取cookie存入的当前语言
const cookieLanguage = getLanguage()
//如果有返回当前语言
if (cookieLanguage) {
return cookieLanguage
}
//如果没有,获取系统语言
const language = navigator.language.toLowerCase()
//获取messages 语言 遍历
const locales = Object.keys(messages)
for (const locale of locales) {
//如果messages 包里面有系统语言返回
if (language.indexOf(locale) > -1) {
return locale
}
}
// 默认语言 简体中文
return 'zh-cn'
}
//注册i8n实例并引入语言文件
const i18n = createI18n({
legacy: false, // 是否启用传统模式,默认true启用,需要在Composition API中使用则设为false
globalInjection: true, // 全局注入,页面内调用全局i18n需要用$t('xxx'),而非t('xxx')
locale: getLocale(), //默认显示的语言
messages //引入语言文件
})
export default i18n;
三、使用 💥 💥 💥
1、修改src/main.ts
引入刚刚写好的src/basics/lang/index.ts文件
import { createApp } from 'vue'
import App from './App.vue'
import 'animate.css'
import { createPinia } from 'pinia';
import { registerStore } from '@/pinia';
import router from '@/router';
import '@/router/permission'
import 'element-plus/theme-chalk/dark/css-vars.css'
import "element-plus/theme-chalk/el-message.css";
import "element-plus/theme-chalk/el-message-box.css";
// 国际化
import i18n from '@/basics/lang'
const app = createApp(App);
app.use(i18n)
app.use(router)
app.use(createPinia())
registerStore()
app.mount('#app')
2、修改src/App.vue
<template>
<el-config-provider :locale="language === 'zh-cn' ? elementZhLocale : elementEnLocale" :key="language === 'zh-cn' ? 'zh-cn' : 'en'">
<router-view />
</el-config-provider>
</template>
<script lang="ts">
import appStore from "@/pinia";
import elementZhLocale from 'element-plus/lib/locale/lang/zh-cn'
import elementEnLocale from 'element-plus/lib/locale/lang/en'
export default defineComponent({
setup() {
// 因为我这里只有两种语言,如果你的语种比较多,可以把element-plus的语种放入数组中遍历,通过elementZhLocale.name去匹配输出
// el-config-provider应该是有bug,切换语种的时候组件内部的子组件语种不会随之刷新,所以必须要上key强制刷新组件.
const language = computed(() => (appStore.appModule.language))
return {
language,
elementZhLocale,
elementEnLocale,
}
}
})
</script>
<style>
html,body,#app {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
</style>
3、新建文件src/views/login.vue
<template>
<div>
<el-select v-model="language" @change="handleSetLanguage">
<el-option v-for="locale in $i18n.availableLocales"
:key="locale"
:disabled="language===locale" :value="locale">{{ locale }}</el-option>
</el-select>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
class="login-form"
autocomplete="on"
label-position="left"
>
<div class="title-container tracking-in-contract-bck">
<h3 class="title heartbeat">
{{ $t('system.title') }}
</h3>
</div>
<el-form-item prop="userName">
<el-input
v-model="loginForm.userName"
:placeholder="$t('login.userName')"
name="userName"
/>
</el-form-item>
<el-form-item prop="passWord">
<el-input
:key="passwordType"
v-model="loginForm.passWord"
:placeholder="$t('login.passWord')"
:type="passwordType"
name="passWord"
@blur="capsTooltip = false"
/>
</el-form-item>
<el-button
:loading="loading"
type="primary"
>
{{ $t('login.logIn') }}
</el-button>
</el-form>
<el-select v-model="selectValue" style="margin-top: 20px;">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</template>
<script lang="ts">
import { useRoute } from "vue-router";
import { useI18n } from 'vue-i18n'
import appStore from "@/pinia";
export default defineComponent({
setup() {
const route = useRoute()
const { proxy }:any = getCurrentInstance()
const language = ref<string>(appStore.appModule.language)
const loginFormRef = ref(null)
const { t } = useI18n()
const state = reactive({
loginForm: {
userName: '',
passWord: '',
},
selectValue: '',
options: [
{
value: 'Option1',
label: 'Option1',
},
{
value: 'Option2',
label: 'Option2',
},
{
value: 'Option3',
label: 'Option3',
},
{
value: 'Option4',
label: 'Option4',
},
{
value: 'Option5',
label: 'Option5',
},
],
loginRules: {
userName: [{ message: computed(()=> t('login.userNameMessage')), required: true, trigger: 'blur' }],
passWord: [{ message: computed(()=> t('login.passWordMessage')), required: true, trigger: 'blur', min: 4, max: 30 }]
},
passwordType: 'passWord',
loading: false,
capsTooltip: false
})
const handleSetLanguage = (lang: string) => {
proxy.$i18n.locale = lang
appStore.appModule.setLanguage(lang)
// 这里是修改当前页面在浏览器的标签名称
const title = route.meta.title ? `${t(`route.${route.meta.title}`)} - ${t('system.title')}` : `${t('system.title')}`
document.title = title
}
return {
...toRefs(state),
loginFormRef,
language,
handleSetLanguage,
}
}
})
</script>
3、新建src/pinia/modules/app.ts文件
import { defineStore } from 'pinia';
import { getLocale } from "@/basics/lang";
import { setLanguage } from "@/utils/cookies";
interface IAppState {
language: string
}
export const appModule = defineStore({
id: 'app',
state(): IAppState{
return {
language: getLocale(),
}
},
getters:{},
actions:{
setLanguage(language: string) {
this.language = language
setLanguage(language)
},
}
})
4、修改src/pinia/modules/permission.ts
增加login路由,因为我是动态路由,没有做的小伙伴自己手动去增加以下login路由就好。Vite4 + Vue3 + vue-router4 动态路由
const list:MenuType[] = [
{
path: '/',
title: 'ts-super-web',
component: 'Layout',
redirect: '/home',
children: [
{
title: 'home',
path: 'home',
component: 'home'
},
{
title: 'home1',
path: 'home1',
component: 'home1'
},
{
title: 'login',
path: 'login',
component: 'login'
}
]
}
]
四、最终效果 ⚡️ ⚡️ ⚡️
我是Etc.End。如果文章对你有所帮助,能否帮我点个免费的赞和收藏😍。