Vue3以及element-plus最新版,國際化迅栅,多語言殊校,高度封裝。按模塊區(qū)分語言文件读存,適應(yīng)大型項目为流。減少沖突。

一让簿、下載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)吧


image.png

先看看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
不了解的可以點擊了解
打斷點讓大家看看里面各值,幫助大家理解

image.png

image.png

image.png

@/locales/lang@是我配置的路徑別名,下面貼出我配置的路徑別名何陆,希望后面看代碼的時候你不會再有路徑問題的疑問

image.png

接下來我們再看 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)容

image.png
image.png
image.png

image.png

注意幾個地方: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ù)制到項目中就可以跑起來了婶博。


下面貼出使用文檔,以及一些說明

約定:

  1. 已集成 element-plus 因為用了文件名來做 key荧飞,所以語言文件格式命名按照文末語言列表里面的來命名凡人,如果如下列表的語言都不存在,則需要自行添加 element-plus語言表叹阔,參考 element-plus 語言文件 <a>https://element-plus.org/#/zh-CN/component/i18n</a>

  2. 語言文件定義挠轴,為了盡可能適用大型項目,以減少合并沖突問題耳幢,已將將語言文件按模塊劃分岸晦。共通的語言文件在common模塊中定義

  3. 使用者不需要關(guān)注如何實現(xiàn),有多少種語言在對應(yīng)的模塊建對應(yīng)的語言文件就可以了

  4. 語言文件的定義如下:(要注意最外層為唯一的模塊名)

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>

  1. 每新增一種語言則需要在src/locales/lang文件夾中增加一種碧绞,寫法參照里面的en.js
  1. 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

ConfigProvider鏈接文檔地址

// 加載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>

關(guān)鍵點就是el-config-provider組件接收一個locale對象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載汰寓,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末苹粟,一起剝皮案震驚了整個濱河市有滑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嵌削,老刑警劉巖毛好,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異苛秕,居然都是意外死亡肌访,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門想帅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懂拾,“玉大人麦备,你說我怎么就攤上這事腹缩〕” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵浅缸,是天一觀的道長轨帜。 經(jīng)常有香客問我,道長衩椒,這世上最難降的妖魔是什么蚌父? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮毛萌,結(jié)果婚禮上苟弛,老公的妹妹穿的比我還像新娘。我一直安慰自己阁将,他們只是感情好膏秫,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著做盅,像睡著了一般缤削。 火紅的嫁衣襯著肌膚如雪窘哈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天亭敢,我揣著相機與錄音滚婉,去河邊找鬼。 笑死帅刀,一個胖子當(dāng)著我的面吹牛让腹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劝篷,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼哨鸭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了娇妓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤活鹰,失蹤者是張志新(化名)和其女友劉穎哈恰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體志群,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡着绷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锌云。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荠医。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖桑涎,靈堂內(nèi)的尸體忽然破棺而出彬向,到底是詐尸還是另有隱情,我是刑警寧澤攻冷,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布娃胆,位于F島的核電站,受9級特大地震影響等曼,放射性物質(zhì)發(fā)生泄漏里烦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一禁谦、第九天 我趴在偏房一處隱蔽的房頂上張望胁黑。 院中可真熱鬧,春花似錦州泊、人聲如沸丧蘸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽触趴。三九已至氮发,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冗懦,已是汗流浹背爽冕。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留披蕉,地道東北人颈畸。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像没讲,于是被迫代替她去往敵國和親眯娱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容