Vue2 element-ui 框架中集成國際化 vue-i18n 并封裝成可切換語言的組件

在 Vue 2 中配置國際化,您可以使用 Vue I18n 插件。Vue I18n 是 Vue.js 官方推薦的國際化插件,它可以幫助您輕松地實(shí)現(xiàn)多語言支持。

安裝 vue-i18n

項(xiàng)目根目錄下打開終端或命令行工具,運(yùn)行以下命令來安裝相關(guān)依賴包:

npm install vue-i18n@8.27.1 --save

使用

1. 在 src/components 目錄中新增一個(gè)名為 i18n 的目錄,并添加以下3個(gè)文件:

src/components/i18n/locales/en/index.js 英語語言包:

export default {
  'Language': 'English'
}

src/components/i18n/locales/zh-CN/index.js 中文語言包:

export default {
  'Language': '中文'
}

src/components/i18n/index.js 語言包入口文件:

import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

// 各個(gè)國家的key
const localeKeys = ['en', 'zh-CN']

// 各個(gè)國家語言包
const messages = {}
for (const key of localeKeys) {
  messages[key] = require(`./locales/${key}/index.js`).default
}

export default new VueI18n({
  locale: 'en',
  messages,
  silentTranslationWarn: true // 忽略翻譯警告
})
2. 打開 src/main.js 文件,掛載到Vue實(shí)例:
import i18n from './components/i18n'

new Vue({
  i18n
})

修改后的代碼如下:

import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

import App from './App.vue'
import router from './router'
import store from './store'
import i18n from './components/i18n'
import '@css/index.less'

// 禁用生產(chǎn)環(huán)境提示
Vue.config.productionTip = false

// Element掛載到Vue
Vue.$message = Element.Message
Vue.use(Element)

new Vue({
  router,
  store,
  i18n,
  render: (h) => h(App)
}).$mount('#app')
3. 在頁面、js代碼中使用:

一旦將 VueI18n 實(shí)例掛載到 Vue 實(shí)例上,在 Vue 組件中直接使用 $t 方法,可以通過指定鍵(key)來獲取對(duì)應(yīng)語言的翻譯文本。這個(gè)鍵可以是簡單的字符串,也可以是一個(gè)對(duì)象,用于支持更復(fù)雜的翻譯需求。

以下是使用 $t 方法獲取翻譯文本的示例:

<template>
  <div>
    <p>{{ $t('Language') }}</p>
  </div>
</template>

<script>
export default {
  mounted() {
    console.log(this.$t('Language'))
  }
}
</script>

Element 語言包

完成上述步驟后,我們?cè)谑褂?Element 組件時(shí)可能會(huì)遇到一個(gè)問題:雖然我們成功切換了自定義的語言,在應(yīng)用中自定義的文本已經(jīng)被正確翻譯,但是 Element UI 組件的顯示內(nèi)容并沒有隨之切換。

這是因?yàn)?Element UI 組件庫本身并不直接集成 vue-i18n 插件,因此它并不會(huì)自動(dòng)根據(jù)我們?cè)O(shè)置的語言環(huán)境來翻譯組件的顯示文本。

解決這個(gè)問題的辦法是,我們需要手動(dòng)為 Element UI 組件進(jìn)行國際化配置和翻譯。

以下是解決方案的基本步驟:

1. 創(chuàng)建 Element 語言包文件

src/components/i18n 目錄下添加一個(gè)名為 element 的目錄,并在目錄中添加以下兩個(gè)文件:

en.js

// 英語
exports.default = {
  el: {
    colorpicker: {
      confirm: 'OK',
      clear: 'Clear'
    },
    datepicker: {
      now: 'Now',
      today: 'Today',
      cancel: 'Cancel',
      clear: 'Clear',
      confirm: 'OK',
      selectDate: 'Select date',
      selectTime: 'Select time',
      startDate: 'Start Date',
      startTime: 'Start Time',
      endDate: 'End Date',
      endTime: 'End Time',
      prevYear: 'Previous Year',
      nextYear: 'Next Year',
      prevMonth: 'Previous Month',
      nextMonth: 'Next Month',
      year: '',
      month1: 'January',
      month2: 'February',
      month3: 'March',
      month4: 'April',
      month5: 'May',
      month6: 'June',
      month7: 'July',
      month8: 'August',
      month9: 'September',
      month10: 'October',
      month11: 'November',
      month12: 'December',
      week: 'week',
      weeks: {
        sun: 'Sun',
        mon: 'Mon',
        tue: 'Tue',
        wed: 'Wed',
        thu: 'Thu',
        fri: 'Fri',
        sat: 'Sat'
      },
      months: {
        jan: 'Jan',
        feb: 'Feb',
        mar: 'Mar',
        apr: 'Apr',
        may: 'May',
        jun: 'Jun',
        jul: 'Jul',
        aug: 'Aug',
        sep: 'Sep',
        oct: 'Oct',
        nov: 'Nov',
        dec: 'Dec'
      }
    },
    select: {
      loading: 'Loading',
      noMatch: 'No matching data',
      noData: 'No data',
      placeholder: 'Select'
    },
    cascader: {
      noMatch: 'No matching data',
      loading: 'Loading',
      placeholder: 'Select',
      noData: 'No data'
    },
    pagination: {
      goto: 'Go to',
      pagesize: '/page',
      total: 'Total {total}',
      pageClassifier: ''
    },
    messagebox: {
      title: 'Message',
      confirm: 'OK',
      cancel: 'Cancel',
      error: 'Illegal input'
    },
    upload: {
      deleteTip: 'press delete to remove',
      delete: 'Delete',
      preview: 'Preview',
      continue: 'Continue'
    },
    table: {
      emptyText: 'No Data',
      confirmFilter: 'Confirm',
      resetFilter: 'Reset',
      clearFilter: 'All',
      sumText: 'Sum'
    },
    tree: {
      emptyText: 'No Data'
    },
    transfer: {
      noMatch: 'No matching data',
      noData: 'No data',
      titles: ['List 1', 'List 2'], // to be translated
      filterPlaceholder: 'Enter keyword', // to be translated
      noCheckedFormat: '{total} items', // to be translated
      hasCheckedFormat: '{checked}/{total} checked' // to be translated
    },
    image: {
      error: 'FAILED'
    },
    pageHeader: {
      title: 'Back' // to be translated
    },
    popconfirm: {
      confirmButtonText: 'Yes',
      cancelButtonText: 'No'
    },
    empty: {
      description: 'No Data'
    }
  }
}

zh-CN.js

// 中文
exports.default = {
  el: {
    colorpicker: {
      confirm: '確定',
      clear: '清空'
    },
    datepicker: {
      now: '此刻',
      today: '今天',
      cancel: '取消',
      clear: '清空',
      confirm: '確定',
      selectDate: '選擇日期',
      selectTime: '選擇時(shí)間',
      startDate: '開始日期',
      startTime: '開始時(shí)間',
      endDate: '結(jié)束日期',
      endTime: '結(jié)束時(shí)間',
      prevYear: '前一年',
      nextYear: '后一年',
      prevMonth: '上個(gè)月',
      nextMonth: '下個(gè)月',
      year: '年',
      month1: '1 月',
      month2: '2 月',
      month3: '3 月',
      month4: '4 月',
      month5: '5 月',
      month6: '6 月',
      month7: '7 月',
      month8: '8 月',
      month9: '9 月',
      month10: '10 月',
      month11: '11 月',
      month12: '12 月',
      // week: '周次',
      weeks: {
        sun: '日',
        mon: '一',
        tue: '二',
        wed: '三',
        thu: '四',
        fri: '五',
        sat: '六'
      },
      months: {
        jan: '一月',
        feb: '二月',
        mar: '三月',
        apr: '四月',
        may: '五月',
        jun: '六月',
        jul: '七月',
        aug: '八月',
        sep: '九月',
        oct: '十月',
        nov: '十一月',
        dec: '十二月'
      }
    },
    select: {
      loading: '加載中',
      noMatch: '無匹配數(shù)據(jù)',
      noData: '無數(shù)據(jù)',
      placeholder: '請(qǐng)選擇'
    },
    cascader: {
      noMatch: '無匹配數(shù)據(jù)',
      loading: '加載中',
      placeholder: '請(qǐng)選擇',
      noData: '暫無數(shù)據(jù)'
    },
    pagination: {
      goto: '前往',
      pagesize: '條/頁',
      total: '共 {total} 條',
      pageClassifier: '頁'
    },
    messagebox: {
      title: '提示',
      confirm: '確定',
      cancel: '取消',
      error: '輸入的數(shù)據(jù)不合法!'
    },
    upload: {
      deleteTip: '按 delete 鍵可刪除',
      delete: '刪除',
      preview: '查看圖片',
      continue: '繼續(xù)上傳'
    },
    table: {
      emptyText: '暫無數(shù)據(jù)',
      confirmFilter: '篩選',
      resetFilter: '重置',
      clearFilter: '全部',
      sumText: '合計(jì)'
    },
    tree: {
      emptyText: '暫無數(shù)據(jù)'
    },
    transfer: {
      noMatch: '無匹配數(shù)據(jù)',
      noData: '無數(shù)據(jù)',
      titles: ['列表 1', '列表 2'],
      filterPlaceholder: '請(qǐng)輸入搜索內(nèi)容',
      noCheckedFormat: '共 {total} 項(xiàng)',
      hasCheckedFormat: '已選 {checked}/{total} 項(xiàng)'
    },
    image: {
      error: '加載失敗'
    },
    pageHeader: {
      title: '返回'
    },
    popconfirm: {
      confirmButtonText: '確定',
      cancelButtonText: '取消'
    },
    empty: {
      description: '暫無數(shù)據(jù)'
    }
  }
}

以上兩個(gè)文件來源于 Element官方。在當(dāng)前項(xiàng)目中,將Element語言包放到本地的目的是為了便于后期對(duì)語言包進(jìn)行修改拓展。

如果不想在本地創(chuàng)建這兩個(gè)文件,也可以通過引入的方式直接獲取到對(duì)應(yīng)的文件,例如:import elementLang from 'element-ui/src/locale/lang/en'

2. 在語言包入口文件引入 Element 語言包

src/components/i18n/index.js 文件中找到以下代碼塊:

// 各個(gè)國家語言包
const messages = {}
for (const key of localeKeys) {
  messages[key] = require(`./locales/${key}/index.js`).default
}

將其修改為:

// 各個(gè)國家語言包
const messages = {}
for (const key of localeKeys) {
  const langObj = require(`./locales/${key}/index.js`).default
  const langElement = require(`./element/${key}`)
  messages[key] = {
    ...langObj,
    ...langElement ? langElement.default : {}
  }
}

修改后的代碼如下:

import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

// 各個(gè)國家的key
const localeKeys = ['en', 'zh-CN']

// 各個(gè)國家語言包
const messages = {}
for (const key of localeKeys) {
  const langObj = require(`./locales/${key}/index.js`).default
  const langElement = require(`./element/${key}`)
  messages[key] = {
    ...langObj,
    ...langElement ? langElement.default : {}
  }
}

export default new VueI18n({
  locale: 'en',
  messages,
  silentTranslationWarn: true // 忽略翻譯警告
})

通過這種方式,Element語言包與我們自定義的語言包便合并在一起了。

3. 掛載 Element 到 Vue 實(shí)例中時(shí)將語言包注入

src/main.js 中找到以下代碼塊

Vue.use(Element)

將其修改為:

Vue.use(Element, {
  i18n: (key, value) => i18n.t(key, value)
})

修改后的代碼如下:

import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

import App from './App.vue'
import router from './router'
import store from './store'
import i18n from './components/i18n'
import '@css/index.less'

// 禁用生產(chǎn)環(huán)境提示
Vue.config.productionTip = false

// Element掛載到Vue
Vue.$message = Element.Message
Vue.use(Element, {
  i18n: (key, value) => i18n.t(key, value)
})

new Vue({
  router,
  store,
  i18n,
  render: (h) => h(App)
}).$mount('#app')

完成后 Element 相關(guān)組件就能正常的顯示翻譯后的內(nèi)容了。

切換組件的封裝

在我們的UI組件中,我們使用了 Element 中的下拉菜單組件。如果想了解更多關(guān)于下拉菜單組件的用法,請(qǐng)?jiān)L問 Dropdown下拉菜單文檔地址 查看詳細(xì)文檔內(nèi)容。

新建組件文件

src/components/i18n目錄中,新建一個(gè)名為change-language.vue 的文件,并添加以下內(nèi)容:

<template>
  <el-dropdown @command="handle">
    <span class="el-dropdown-link">
      {{$t('Language')}}<i class="el-icon-caret-bottom el-icon--right"></i>
    </span>
    <el-dropdown-menu slot="dropdown">
      <el-dropdown-item v-for="(item, index) of list" :key="index" :command="item.key">{{item.name}}</el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</template>

<script>
export default {
  name: 'change-language',
  data() {
    return {
      list: [
        { key: 'en', name: 'English' }, // 英語
        { key: 'zh-CN', name: '中文' } // 中文
      ]
    }
  },
  methods: {
    handle(value) {
    }
  }
}
</script>
<style scoped lang="less">
</style>

然后,我們可以找一個(gè)Vue文件,在其中使用這個(gè)組件進(jìn)行觀察。使用方式如下(以Vue單文件組件為例):

  • 引入組件
import ChangeLanguage from '@/components/i18n/change-language'
  • 注冊(cè)組件
components: {
  ChangeLanguage
}
  • 使用
<ChangeLanguage />

這樣一個(gè)基本的語言包下拉單組件樣式就完成了,之后還需要對(duì)組件進(jìn)行功能上的開發(fā)。

切換語言

在組件handle函數(shù)代碼中添加代碼,如下:

handle(value) {
  this.$i18n.locale = value
}

通過將value賦給this.$i18n.locale,我們可以動(dòng)態(tài)地切換當(dāng)前語言為下拉菜單中所選中的語言。

現(xiàn)在雖然基本的切換功能已經(jīng)完成,但是當(dāng)我們?cè)诰W(wǎng)頁中切換語言包并刷新頁面后,之前選擇的語言包不會(huì)被保留,而是重新加載頁面時(shí)返回到默認(rèn)的語言包。這是因?yàn)樵谒⑿马撁鏁r(shí),瀏覽器會(huì)重新加載整個(gè)應(yīng)用程序,并重置Vue實(shí)例的狀態(tài),包括設(shè)置的語言包。這時(shí)候我們就需要做切換數(shù)據(jù)的持久化處理,來保證我們切換后的內(nèi)容顯示不會(huì)出錯(cuò)。

數(shù)據(jù)持久化

數(shù)據(jù)持久化的方式有很多種,這里我們采用了瀏覽器的本地存儲(chǔ) LocalStorage 來實(shí)現(xiàn)。

在組件handle函數(shù)代碼中添加代碼,如下:

handle(value) {
  this.$i18n.locale = value
  localStorage.setItem('change-language', value)
}

找到 src/components/i18n/index.js 文件中以下代碼塊:

export default new VueI18n({
  locale: 'en',
  messages,
  silentTranslationWarn: true // 忽略翻譯警告
})

將其中 locale 寫死的值 'en' 改為 localStorage 獲取的值,如下:

export default new VueI18n({
  locale: localStorage.getItem('change-language') || 'zh-CN',
  messages,
  silentTranslationWarn: true // 忽略翻譯警告
})

上述代碼中,語言包的key值會(huì)先從本地存儲(chǔ)中獲取,如果獲取不到則設(shè)置為默認(rèn)值'zh-CN'。

這樣,我們切換語言包再刷新頁面后,仍然可以正確地顯示之前選擇的語言包。

后端語言包

在一般情況下,僅僅使用前端的語言包是無法涵蓋整個(gè)系統(tǒng)的翻譯需求的。例如,后端接口返回的菜單、下拉列表的數(shù)據(jù)等。針對(duì)這種情況,我們可以采取一種方案,即在切換語言后調(diào)用location.reload()方法刷新頁面以重新獲取后端的翻譯數(shù)據(jù)。

使用這種方案時(shí),我們需要與后端約定好國際化的翻譯key的格式,并在語言切換時(shí)通過前端與后端接口進(jìn)行數(shù)據(jù)交互。前端可以將當(dāng)前選擇的語言作為參數(shù)傳遞給后端,后端將返回相應(yīng)語言的翻譯數(shù)據(jù)。然后在前端刷新頁面時(shí),通過重新加載頁面來獲取更新后的翻譯內(nèi)容。

請(qǐng)注意,使用location.reload()方法會(huì)導(dǎo)致整個(gè)頁面重新加載,這可能會(huì)對(duì)用戶的體驗(yàn)產(chǎn)生一定影響,特別是在數(shù)據(jù)量較大或網(wǎng)絡(luò)較慢的情況下。因此,在實(shí)施這種方案之前,請(qǐng)確保綜合考慮用戶體驗(yàn)和性能方面的因素。

總結(jié)來說,為了在整個(gè)系統(tǒng)中實(shí)現(xiàn)翻譯需求,我們可以通過與后端接口協(xié)作,傳遞語言選擇,并在需要時(shí)重新加載頁面來獲取最新的翻譯內(nèi)容。這樣就能夠在前后端協(xié)同工作下,正確顯示經(jīng)過翻譯的內(nèi)容。

在組件handle函數(shù)中添加 location.reload()

handle(value) {
  this.$i18n.locale = value
  localStorage.setItem('change-language', value)
  location.reload()
}
小小的優(yōu)化一下

上述步驟完成,我們的組件整體功能就已經(jīng)開發(fā)完畢了,但是代碼中有一部部分內(nèi)容可以復(fù)用簡化。

組件代碼中的以下部分:

list: [
  { key: 'en', name: 'English' }, // 英語
  { key: 'zh-CN', name: '中文' } // 中文
]

src/components/i18n/index.js 代碼中的以下部分:

// 各個(gè)國家的key
const localeKeys = ['en', 'zh-CN']

以上兩部分代碼對(duì)比,就能夠發(fā)現(xiàn)他們有兩個(gè)相同點(diǎn):都是數(shù)組、內(nèi)容中都有一樣的值。

這樣我們就能將組件代碼中的內(nèi)容移動(dòng)到 src/components/i18n/index.js 文件中,避免后期維護(hù)需要新增國家時(shí),還要同時(shí)維護(hù)兩個(gè)文件的列表了。

根據(jù)以下步驟進(jìn)行優(yōu)化:

  1. 找到 src/components/i18n/index.js 文件中以下內(nèi)容:
// 各個(gè)國家的key
const localeKeys = ['en', 'zh-CN']

// 各個(gè)國家語言包
const messages = {}
for (const key of localeKeys) {
  const langObj = require(`./locales/${key}/index.js`).default
  const langElement = require(`./element/${key}`)
  messages[key] = {
    ...langObj,
    ...langElement ? langElement.default : {}
  }
}
  1. 將其修改為:
// 各個(gè)國家的key
export const localeKeys = [
  { key: 'en', name: 'English' }, // 英語
  { key: 'zh-CN', name: '中文' } // 中文
]

// 各個(gè)國家語言包
const messages = {}
for (const item of localeKeys) {
  const key = item.key
  const langObj = require(`./locales/${key}/index.js`).default
  const langElement = require(`./element/${key}`)
  messages[key] = {
    ...langObj,
    ...langElement ? langElement.default : {}
  }
}
  1. 在組件中引入 src/components/i18n/index.js 文件中的 localeKeys
import { localeKeys } from './index'
  1. localeKeys賦值給 list
return {
  list: localeKeys
}

這樣,這個(gè)小小的優(yōu)化就完成了。

完整代碼

src/components/i18n/change-language.vue 組件代碼:

<!--
@Descripttion 國際化語言切換
@version 1.0.0
@Author Bell
@ 使用
  引入組件
    import ChangeLanguage from '@/components/i18n/change-language'
  注冊(cè)組件
    components: {
      ChangeLanguage
    }
  使用
    <ChangeLanguage />
 -->
<template>
  <el-dropdown @command="handle">
    <span class="el-dropdown-link">
      {{$t('Language')}}<i class="el-icon-caret-bottom el-icon--right"></i>
    </span>
    <el-dropdown-menu slot="dropdown">
      <el-dropdown-item v-for="(item, index) of list" :key="index" :command="item.key">{{item.name}}</el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</template>

<script>
import { localeKeys } from './index'

export default {
  name: 'change-language',
  data() {
    return {
      list: localeKeys
    }
  },
  methods: {
    handle(value) {
      this.$i18n.locale = value
      localStorage.setItem('change-language', value)
      location.reload()
    }
  }
}
</script>
<style scoped lang="less">
</style>

src/components/i18n/index.js 國際化入口文件:

import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

// 各個(gè)國家的key
export const localeKeys = [
  { key: 'en', name: 'English' }, // 英語
  { key: 'zh-CN', name: '中文' } // 中文
]

// 各個(gè)國家語言包
const messages = {}
for (const item of localeKeys) {
  const key = item.key
  const langObj = require(`./locales/${key}/index.js`).default
  const langElement = require(`./element/${key}`)
  messages[key] = {
    ...langObj,
    ...langElement ? langElement.default : {}
  }
}

export default new VueI18n({
  locale: localStorage.getItem('change-language') || 'zh-CN',
  messages,
  silentTranslationWarn: true // 忽略翻譯警告
})



框架搭建整體流程

點(diǎn)擊下載步驟 1-7 配置完成的完整 Demo



本框架更多功能 Demo 下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容