一让簿、下載il8n插件敬察。
目前通過npm install vue-il8n下載的il8n版本是無法支持vue3.0,因此要使用npm install vue-i18n@next 來獲取最新的版本尔当。
二莲祸、引入組件
適用于vue3 的vuei18文檔在這里,目前還沒有中文版椭迎。如果大家看到的中文版本應(yīng)該是適用vue2.x的版本锐帜。
vue3+以上的vue-i18n文檔
vue3+以上的使用方式改變了⌒蠛牛可能是為了跟vue3保持一致缴阎,所以作者也搞了個createI18n,
所以在入口文件main.js中這么使用简软。
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
// something vue-i18n options here ...
})
const app = createApp({
// something vue options here ...
})
app.use(i18n)
app.mount('#app')
注意:
如果以上引入報錯的話蛮拔,請將import { createI18n } from 'vue-i18n'
改一下。
import { createI18n } from 'vue-i18n/index'
在源碼中可以找到createI18n 在index.js里面替饿。一般應(yīng)該不會報錯语泽。默認(rèn)都會找index的。
三视卢、最簡單的使用方式
// 1. Ready translated locale messages
// The structure of the locale message is the hierarchical object structure with each locale as the top property
const messages = {
en: {
message: {
hello: 'hello world'
}
},
ja: {
message: {
hello: 'こんにちは踱卵、世界'
}
}
}
// 2. Create i18n instance with options
const i18n = VueI18n.createI18n({
locale: 'ja', // set locale
fallbackLocale: 'en', // set fallback locale
messages, // set locale messages
// If you need to specify other options, you can set other options
// ...
})
// 3. Create a vue root instance
const app = Vue.createApp({
// set something options
// ...
})
// 4. Install i18n instance to make the whole app i18n-aware
app.use(i18n)
// 5. Mount
app.mount('#app')
// Now the app has started!
createI18n這個函數(shù)最主要的就是需要一個messages 對象。即語言翻譯對象据过。不過以上這么簡單的使用方式惋砂,顯然這么使用肯定不太適合我們大型項目。我們接下來要基于以上繼續(xù)進行封裝绳锅。
四西饵、集成element-plus
之前element-plus文檔上有兼容vue-i18n 9.x版本的使用文檔,目前找不到了鳞芙。只在別人博客里面找到了部分內(nèi)容眷柔。element-plus兼容vue-i18n 9.x
主要就是兼容element-plus去構(gòu)建vue-i18n所需要的message
關(guān)鍵代碼:
import enLocale from 'element-plus/lib/locale/lang/en'
import zhLocale from 'element-plus/lib/locale/lang/zh-cn'
const messages = {
[enLocale.name]: {
// el 這個屬性很關(guān)鍵期虾,一定要保證有這個屬性,
el: enLocale.el,
// 定義您自己的字典驯嘱,但是請不要和 `el` 重復(fù)镶苞,這樣會導(dǎo)致 ElementPlus 內(nèi)部組件的翻譯失效.
message: {
hello: 'hello world',
},
},
[zhLocale.name]: {
el: zhLocale.el,
// 定義您自己的字典,但是請不要和 `el` 重復(fù)鞠评,這樣會導(dǎo)致 ElementPlus 內(nèi)部組件的翻譯失效.
message: {
hello: '你好茂蚓,世界',
},
},
}
五、基于vue-i18n封裝剃幌、抽離聋涨、將語言文件按模塊區(qū)分,適應(yīng)大型項目负乡。減少沖突
先思考一下要實現(xiàn)什么樣的效果
1.需要兼容element-plus
2.語言文件按照模塊來區(qū)分牍白,將語言文件抽離出來,不寫在一個文件里面
3.每定義一種語言敬鬓,不需要再手動在代碼中引入語言文件
...奔著這些目的我們來看下面我是如何實現(xiàn)的吧
1.首先先看下我項目里面的目錄結(jié)構(gòu)吧
先看看i18n目錄下的index
/**
* author: fzs 2021-6-30
*/
import { createI18n } from 'vue-i18n/index'
import { loadLocaleMessages, getLanguage } from './i18n-utils'
export default createI18n({
locale: getLanguage(),
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', // 備用語言
messages: loadLocaleMessages(),
})
這個文件主要就是做 createI18n淹朋,一開始我們已經(jīng)說過了的。只是現(xiàn)在再進一步封裝钉答。
主要還是i18n-utils.js
/**
* author: fzs 2021-6-30
*/
import storage from 'lib@/utils/storage'
const LOCALE_KEY = 'locale'
const config = [
{
name: LOCALE_KEY,
type: 'string',
},
]
const windowStorage = new storage('sessionStorage', window.localStorage, config) // 本地存儲
// 構(gòu)建vue-i18n所需 messages
export function loadLocaleMessages() {
try {
const locales = require.context('@/locales/lang', true, /[A-Za-z0-9-_,\s]+\.js$/i)
const messages = {}
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key).default[locale]
}
})
return messages
} catch (error) {
throw new Error(error)
}
}
// 獲取所有模塊對應(yīng)的localeName語言對象
export function getLangObjBylocaleName(localeName) {
try {
const locales = require.context('@/locales/modules', true, /[A-Za-z0-9-_,\s]+\.js$/i)
let tempObj = {}
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
if (locale === localeName) {
const obj = locales(key)
if (obj && obj.default) {
tempObj = {
...tempObj,
...obj.default,
}
}
}
}
})
return tempObj
} catch (error) {
throw new Error(error)
}
}
export function getLanguage() {
// 先取用戶設(shè)置的础芍,如果沒有則取系統(tǒng)設(shè)置的,最后取環(huán)境變量中配置的
const locale = windowStorage.get(LOCALE_KEY)
if (locale) {
return locale
}
return (
navigator.language ||
navigator.userLanguage ||
process.env.VUE_APP_I18N_LOCALE
).toLowerCase()
}
/**
* 獲取選中的語言label
* @param list 語言集合
* @returns
*/
export function getDefaultLanguageLabel(list){
try {
return list.filter((o) => o.value === getLanguage())[0].label
} catch (error) {
throw new Error(error)
}
}
// 獲取所有語言列表
export function getLanguageLabelList() {
try {
const locales = require.context('@/locales/lang', true, /[A-Za-z0-9-_,\s]+\.js$/i)
const arr = []
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const label = locales(key).default.language
const value = matched[1]
arr.push({ value: value, label: label })
}
})
return arr
} catch (error) {
throw new Error(error)
}
}
/**
* 設(shè)置語言并且刷新頁面
* @param {string} lang 語言value
*/
export function setLanguage(lang) {
windowStorage.set(LOCALE_KEY, lang)
// 此方式直接刷新頁面数尿,最便捷仑性,但是體驗可能不好
window.location.reload(location.href + '?time=' + (new Date().getTime()))
}
我們主要來看loadLocaleMessages函數(shù),此函數(shù)就是為了解決已經(jīng)將語言文件拆分不同的語言文件再統(tǒng)一引入右蹦。主要是借助 require.context
這個方法.統(tǒng)一引入所有語言文件诊杆。動態(tài)構(gòu)建vue-i18n需要的message
不了解的可以點擊了解
打斷點讓大家看看里面各值,幫助大家理解
@/locales/lang
@是我配置的路徑別名,下面貼出我配置的路徑別名何陆,希望后面看代碼的時候你不會再有路徑問題的疑問
接下來我們再看 locales目錄里面的內(nèi)容
lang目錄下(兼容element-plus晨汹,并構(gòu)建message):
1.en.js
/**
* author: fzs 2021-6-30
*/
import enLocale from 'element-plus/lib/locale/lang/en'
import {getLangObjBylocaleName} from 'lib@/utils/i18n/i18n-utils'
export default {
language:'English', // 必須
[enLocale.name]: {
// el 這個屬性很關(guān)鍵,一定要保證有這個屬性贷盲,
el: enLocale.el,
// 定義您自己的字典淘这,但是請不要和 `el` 重復(fù),這樣會導(dǎo)致 ElementPlus 內(nèi)部組件的翻譯失效.
...getLangObjBylocaleName(enLocale.name)
},
}
getLangObjBylocaleName
這個函數(shù)在上面有巩剖,干的事就是把不同模塊的語言文件內(nèi)容再合并到同一個對象铝穷。看不懂大家打斷點佳魔,再理解就好了
注意:lang里面的語言文件曙聂,名字必須按照文末的命名方式來起名字,因為我在構(gòu)建message的時候用了文件名稱來做key值鞠鲜。具體看 loadLocaleMessages
函數(shù)
zh-cn.js
/**
* author: fzs 2021-6-30
*/
import zhLocale from 'element-plus/lib/locale/lang/zh-cn'
import {getLangObjBylocaleName} from 'lib@/utils/i18n/i18n-utils'
export default {
language: '簡體中文', // 必須
[zhLocale.name]: {
el: zhLocale.el,
// 定義您自己的字典宁脊,但是請不要和 `el` 重復(fù)断国,這樣會導(dǎo)致 ElementPlus 內(nèi)部組件的翻譯失效.
...getLangObjBylocaleName(zhLocale.name)
},
}
以后每增加一種文件,則只需要在lang文件夾里面構(gòu)建類似的內(nèi)容即可朦佩。因為懶并思,其實這個也可以再進一步封裝,不需要每增加一種語言就重復(fù)差不多的代碼语稠。不過就是需要提前將所有element-plus支持的語言引用先定義好。思路就是構(gòu)建一個map對象弄砍。然后動態(tài)引入仙畦。
接下來我們看locales/modules各模塊的內(nèi)容
注意幾個地方:1、模塊名稱(即最外層對象的key)一定要是唯一的音婶,比如a模塊慨畸,那么他所有語言都是在a這個對象里面。層級關(guān)系就是這樣衣式。因為最終這些不同模塊的語言文件都是要合并到一個對象里面的寸士。2、為了避免重復(fù)定義所以定義了個公共模塊碴卧。
接下來看在入口main.js文件中怎么使用
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import CSvgIcon from 'lib@/components/c-svg-icon'
import 'assets@/icons' // icon
import 'assets@/styles/transition.less' // transition css
import VerifyEmojiDirective from 'lib@/directives/verify-emoji'
import Errorhandler from 'lib@/utils/errorhandler.js'
import i18n from 'lib@/utils/i18n/index'
import 'lib@/utils/prototype'
import * as echarts from 'echarts'
const app = createApp(App)
// 使用i18n
app.use(i18n)
// 加載element
app.use(ElementPlus, { i18n: i18n.global.t, size: 'small' })
// 全局注冊 svg icon組件
app.component('c-svg-icon', CSvgIcon)
// 注冊指令
app.use(VerifyEmojiDirective)
// 全局統(tǒng)一錯誤處理
app.use(Errorhandler)
app.provide('echarts', echarts)
// 初始化等
app.use(store)
app.use(router)
app.mount('#app')
好了弱卡,以上就是多語言封裝的所有內(nèi)容了。代碼都有住册。直接復(fù)制到項目中就可以跑起來了婶博。
下面貼出使用文檔,以及一些說明
約定:
已集成 element-plus 因為用了文件名來做 key荧飞,所以語言文件格式命名按照文末語言列表里面的來命名凡人,如果如下列表的語言都不存在,則需要自行添加 element-plus語言表叹阔,參考 element-plus 語言文件 <a>https://element-plus.org/#/zh-CN/component/i18n</a>
語言文件定義挠轴,為了盡可能適用大型項目,以減少合并沖突問題耳幢,已將將語言文件按模塊劃分岸晦。共通的語言文件在common模塊中定義
使用者不需要關(guān)注如何實現(xiàn),有多少種語言在對應(yīng)的模塊建對應(yīng)的語言文件就可以了
語言文件的定義如下:(要注意最外層為唯一的模塊名)
export default {
// 最外層定義 a則為模塊名稱
a:{
test: '這是a模塊的翻譯英文的'
}
}
使用的時候就是 模塊名.xxx
{{$t('a.test')}} 'test' 是a模塊語言文件里面的 key
// 在vue2寫法中帅掘,已經(jīng)注冊全局 $i18n對象委煤, 使用只需要 this.$i18n.t('key')
this.$i18n.t('hello')
// 在setup中使用
<template>
<div>{{t('name')}}</div>
</template>
<script>
import { useI18n } from "vue-i18n";
export default defineComponent({
setup() {
const { t } = useI18n();
return{
t
}
}
})
</script>
// 在setup中的模板中雖然也可以{{$t('hello')}}這么使用,
// 但是比較不推薦咯修档,畢竟你都return了翻譯的函數(shù) t
<template>
<div>{{$t('name')}}</div>
</template>
- 每新增一種語言則需要在src/locales/lang文件夾中增加一種碧绞,寫法參照里面的en.js
- I18N_FALLBACK_LOCALE 后置語言,即備用語言吱窝,什么意思讥邻,就是當(dāng)你的中文語言文件里面并沒有定義這個key迫靖,
但是還是會被翻譯了而沒有報錯,則有可能你在英文語言文件中定義了,如果都未定義則直接顯示key
import { createI18n } from 'vue-i18n/index'
import { loadLocaleMessages, getLanguage } from './i18n-utils'
export default createI18n({
locale: getLanguage(),
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', // 備用語言
messages: loadLocaleMessages(),
})
單文件使用方式不建議使用兴使,因為有多少種語言你需要在<i18n>標(biāo)簽里面建立多少種語言
<template>
<p>{{ $t('hello') }}</p>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'HelloI18n'
})
</script>
<i18n>
{
"en": {
"hello": "Hello i18n in SFC!"
}
}
</i18n>
語言列表如下
- 簡體中文(zh-cn)
- 英語(en)
- 德語(de)
- 葡萄牙語(pt)
- 西班牙語(es)
- 丹麥語(da)
- 法語(fr)
- 挪威語(nb-no)
- 繁體中文(zh-tw)
- 意大利語(it)
- 韓語(ko)
- 日語(ja)
- 荷蘭語(nl)
- 越南語(vi)
- 俄語(ru)
- 土耳其語(tr)
- 巴西葡萄牙語(pt-br )
- 波斯語(fa)
- 泰語(th)
- 印尼語(id)
- 保加利亞語(bg)
- 波蘭語(pl)
- 芬蘭語(fi)
- 瑞典語(sv)
- 希臘語(el)
- 斯洛伐克語(sk)
- 加泰羅尼亞語(ca)
- 捷克語(cs)
- 烏克蘭語(uk)
- 土庫曼語(tk)
- 泰米爾語(ta)
- 拉脫維亞語(lv)
- 南非荷蘭語(af)
- 愛沙尼亞語(et)
- 斯洛文尼亞語(sl)
- 阿拉伯語(ar)
- 希伯來語(he)
- 立陶宛語(lt)
- 蒙古語(mn)
- 哈薩克斯坦語(kk)
- 匈牙利語(hu)
- 羅馬尼亞語(ro)
- 庫爾德語(ku)
- 維吾爾語(ug-cn)
- 高棉語(km)
- 塞爾維亞語(sr)
- 巴斯克語(eu)
- 吉爾吉斯語(ky)
- 亞美尼亞語 (hy-am)
- 克羅地亞 (hr)
- 世界語 (eo)
11.19日更新
由于element-plus更新版本之后系宜,本國際化方案需要稍微改造一下才能適用。
element-plus正式版發(fā)布更新后只需稍微改動下发魄,主要去掉main.js中的全局引入方式盹牧。element-plus提供了一個全局配置組件Config Provider
// 加載element
app.use(ElementPlus, { i18n: i18n.global.t, size: 'small' }) // main.js這種引入方式廢棄了。
新版也簡單:在根app.vue組件中励幼,如下
<template>
<el-config-provider v-if="show" :locale="locale">
<router-view />
</el-config-provider>
</template>
<script>
import { defineComponent, ref, onBeforeMount } from 'vue'
import { ElConfigProvider } from 'element-plus'
import i18n from 'lib@/utils/i18n/index'
import isLoginControl from 'lib@/compostion-api/is-login-control'
export default defineComponent({
components: {
ElConfigProvider,
},
setup() {
const locale = i18n.global.locale
let show = ref(false)
onBeforeMount(async () => {
await isLoginControl()
show.value = true
})
return {
locale: i18n.global.messages[locale],
show
}
},
})
</script>