在 Vue 2 中配置國際化总寒,您可以使用 Vue I18n 插件扶歪。Vue I18n 是 Vue.js 官方推薦的國際化插件,它可以幫助您輕松地實現(xiàn)多語言支持摄闸。
安裝 vue-i18n
項目根目錄下打開終端或命令行工具善镰,運行以下命令來安裝相關依賴包:
npm install vue-i18n@8.27.1 --save
使用
1. 在 src/components
目錄中新增一個名為 i18n
的目錄,并添加以下3個文件:
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)
// 各個國家的key
const localeKeys = ['en', 'zh-CN']
// 各個國家語言包
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實例:
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 實例掛載到 Vue 實例上炫欺,在 Vue 組件中直接使用 $t 方法,可以通過指定鍵(key)來獲取對應語言的翻譯文本熏兄。這個鍵可以是簡單的字符串品洛,也可以是一個對象,用于支持更復雜的翻譯需求摩桶。
以下是使用 $t 方法獲取翻譯文本的示例:
<template>
<div>
<p>{{ $t('Language') }}</p>
</div>
</template>
<script>
export default {
mounted() {
console.log(this.$t('Language'))
}
}
</script>
Element 語言包
完成上述步驟后桥状,我們在使用 Element 組件時可能會遇到一個問題:雖然我們成功切換了自定義的語言,在應用中自定義的文本已經(jīng)被正確翻譯典格,但是 Element UI 組件的顯示內(nèi)容并沒有隨之切換岛宦。
這是因為 Element UI 組件庫本身并不直接集成 vue-i18n 插件台丛,因此它并不會自動根據(jù)我們設置的語言環(huán)境來翻譯組件的顯示文本耍缴。
解決這個問題的辦法是,我們需要手動為 Element UI 組件進行國際化配置和翻譯挽霉。
以下是解決方案的基本步驟:
1. 創(chuàng)建 Element 語言包文件
在 src/components/i18n
目錄下添加一個名為 element
的目錄防嗡,并在目錄中添加以下兩個文件:
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: '選擇時間',
startDate: '開始日期',
startTime: '開始時間',
endDate: '結(jié)束日期',
endTime: '結(jié)束時間',
prevYear: '前一年',
nextYear: '后一年',
prevMonth: '上個月',
nextMonth: '下個月',
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: '請選擇'
},
cascader: {
noMatch: '無匹配數(shù)據(jù)',
loading: '加載中',
placeholder: '請選擇',
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: '合計'
},
tree: {
emptyText: '暫無數(shù)據(jù)'
},
transfer: {
noMatch: '無匹配數(shù)據(jù)',
noData: '無數(shù)據(jù)',
titles: ['列表 1', '列表 2'],
filterPlaceholder: '請輸入搜索內(nèi)容',
noCheckedFormat: '共 {total} 項',
hasCheckedFormat: '已選 {checked}/{total} 項'
},
image: {
error: '加載失敗'
},
pageHeader: {
title: '返回'
},
popconfirm: {
confirmButtonText: '確定',
cancelButtonText: '取消'
},
empty: {
description: '暫無數(shù)據(jù)'
}
}
}
以上兩個文件來源于 Element官方。在當前項目中侠坎,將Element語言包放到本地的目的是為了便于后期對語言包進行修改拓展蚁趁。
如果不想在本地創(chuàng)建這兩個文件,也可以通過引入的方式直接獲取到對應的文件实胸,例如:import elementLang from 'element-ui/src/locale/lang/en'
他嫡。
2. 在語言包入口文件引入 Element 語言包
在 src/components/i18n/index.js
文件中找到以下代碼塊:
// 各個國家語言包
const messages = {}
for (const key of localeKeys) {
messages[key] = require(`./locales/${key}/index.js`).default
}
將其修改為:
// 各個國家語言包
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)
// 各個國家的key
const localeKeys = ['en', 'zh-CN']
// 各個國家語言包
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 實例中時將語言包注入
在 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 相關組件就能正常的顯示翻譯后的內(nèi)容了钢属。
切換組件的封裝
在我們的UI組件中徘熔,我們使用了 Element 中的下拉菜單組件。如果想了解更多關于下拉菜單組件的用法淆党,請訪問 Dropdown下拉菜單文檔地址 查看詳細文檔內(nèi)容酷师。
新建組件文件
在src/components/i18n
目錄中,新建一個名為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>
然后山孔,我們可以找一個Vue文件,在其中使用這個組件進行觀察荷憋。使用方式如下(以Vue單文件組件為例):
- 引入組件
import ChangeLanguage from '@/components/i18n/change-language'
- 注冊組件
components: {
ChangeLanguage
}
- 使用
<ChangeLanguage />
這樣一個基本的語言包下拉單組件樣式就完成了台颠,之后還需要對組件進行功能上的開發(fā)。
切換語言
在組件handle
函數(shù)代碼中添加代碼台谊,如下:
handle(value) {
this.$i18n.locale = value
}
通過將value
賦給this.$i18n.locale
蓉媳,我們可以動態(tài)地切換當前語言為下拉菜單中所選中的語言。
現(xiàn)在雖然基本的切換功能已經(jīng)完成锅铅,但是當我們在網(wǎng)頁中切換語言包并刷新頁面后酪呻,之前選擇的語言包不會被保留,而是重新加載頁面時返回到默認的語言包盐须。這是因為在刷新頁面時玩荠,瀏覽器會重新加載整個應用程序,并重置Vue實例的狀態(tài)贼邓,包括設置的語言包阶冈。這時候我們就需要做切換數(shù)據(jù)的持久化處理,來保證我們切換后的內(nèi)容顯示不會出錯塑径。
數(shù)據(jù)持久化
數(shù)據(jù)持久化的方式有很多種女坑,這里我們采用了瀏覽器的本地存儲 LocalStorage
來實現(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
值會先從本地存儲中獲取誉简,如果獲取不到則設置為默認值'zh-CN'
碉就。
這樣,我們切換語言包再刷新頁面后闷串,仍然可以正確地顯示之前選擇的語言包瓮钥。
后端語言包
在一般情況下,僅僅使用前端的語言包是無法涵蓋整個系統(tǒng)的翻譯需求的。例如碉熄,后端接口返回的菜單桨武、下拉列表的數(shù)據(jù)等。針對這種情況锈津,我們可以采取一種方案玻募,即在切換語言后調(diào)用location.reload()
方法刷新頁面以重新獲取后端的翻譯數(shù)據(jù)。
使用這種方案時一姿,我們需要與后端約定好國際化的翻譯key
的格式七咧,并在語言切換時通過前端與后端接口進行數(shù)據(jù)交互。前端可以將當前選擇的語言作為參數(shù)傳遞給后端叮叹,后端將返回相應語言的翻譯數(shù)據(jù)艾栋。然后在前端刷新頁面時,通過重新加載頁面來獲取更新后的翻譯內(nèi)容蛉顽。
請注意蝗砾,使用location.reload()
方法會導致整個頁面重新加載,這可能會對用戶的體驗產(chǎn)生一定影響携冤,特別是在數(shù)據(jù)量較大或網(wǎng)絡較慢的情況下悼粮。因此,在實施這種方案之前曾棕,請確保綜合考慮用戶體驗和性能方面的因素扣猫。
總結(jié)來說,為了在整個系統(tǒng)中實現(xiàn)翻譯需求翘地,我們可以通過與后端接口協(xié)作申尤,傳遞語言選擇,并在需要時重新加載頁面來獲取最新的翻譯內(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)容可以復用簡化。
組件代碼中的以下部分:
list: [
{ key: 'en', name: 'English' }, // 英語
{ key: 'zh-CN', name: '中文' } // 中文
]
src/components/i18n/index.js
代碼中的以下部分:
// 各個國家的key
const localeKeys = ['en', 'zh-CN']
以上兩部分代碼對比厅瞎,就能夠發(fā)現(xiàn)他們有兩個相同點:都是數(shù)組饰潜、內(nèi)容中都有一樣的值。
這樣我們就能將組件代碼中的內(nèi)容移動到 src/components/i18n/index.js
文件中磁奖,避免后期維護需要新增國家時囊拜,還要同時維護兩個文件的列表了某筐。
根據(jù)以下步驟進行優(yōu)化:
- 找到
src/components/i18n/index.js
文件中以下內(nèi)容:
// 各個國家的key
const localeKeys = ['en', 'zh-CN']
// 各個國家語言包
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 : {}
}
}
- 將其修改為:
// 各個國家的key
export const localeKeys = [
{ key: 'en', name: 'English' }, // 英語
{ key: 'zh-CN', name: '中文' } // 中文
]
// 各個國家語言包
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 : {}
}
}
- 在組件中引入
src/components/i18n/index.js
文件中的localeKeys
:
import { localeKeys } from './index'
- 將
localeKeys
賦值給list
:
return {
list: localeKeys
}
這樣比搭,這個小小的優(yōu)化就完成了。
完整代碼
src/components/i18n/change-language.vue
組件代碼:
<!--
@Descripttion 國際化語言切換
@version 1.0.0
@Author Bell
@ 使用
引入組件
import ChangeLanguage from '@/components/i18n/change-language'
注冊組件
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)
// 各個國家的key
export const localeKeys = [
{ key: 'en', name: 'English' }, // 英語
{ key: 'zh-CN', name: '中文' } // 中文
]
// 各個國家語言包
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 // 忽略翻譯警告
})
框架搭建整體流程
-
第一步 Vue2 使用 Vue 腳手架 Vue CLI 搭建一個 Vue.js 前端項目框架
-
第二步 Vue2 vue.config.js 基礎配置,路徑別名alias
-
第三步 Vue2 vue.config.js 集成 Less 配置 sourceMap+全局變量
-
第四步 Vue2 配置ESLint
-
第五步 Vue2 vue.config.js 使用image-minimizer-webpack-plugin配置圖片壓縮
-
第六步 Vue2 集成全家桶 vue-router vuex axios 和 element-ui
-
第七步 Webpack 配置多環(huán)境和全局變量 cross-env 和 webpack.DefinePlugin