
by yugasun from https://yugasun.com/post/you-may-not-know-vuejs-15.html
本文可全文轉載,但需要保留原作者和出處。
相信大多數(shù)使用 Vue 項目都會面臨國際化的問題,而 vue-i18n 便是國際化的不二之選,它用起來非常簡單,但是同時也會帶來一些問題和挑戰(zhàn)。本篇是個人在項目上國際化時一些經(jīng)驗的總結,希望能在國際化的道路上幫到你。
基本使用
vue-i18n 官方文檔介紹的很清楚,先在 src/lang 目錄下分別創(chuàng)建三個文件 index.js,zh.js,en.js,
然后在 index.js 中創(chuàng)建 i18n 實例并導出,供 Vue 使用,代碼如下:
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import zhMsg from './zh';
import enMsg from './en';
Vue.use(VueI18n);
const messages = {
zh: {
...zhMsg,
},
en: {
...enMsg,
},
};
const i18n = new VueI18n({
locale: 'zh',
messages,
});
export default i18n;
接著編寫語言包文件:
// zh.js
export default {
index: {
header: {
title: 'Vue-I18n 示例',
subtitle: '你將學會如何使用 vue-i18n',
},
},
};
// en.js
export default {
index: {
header: {
title: 'Vue-I18n Demo',
subtitle: 'You will learn how to use vue-i18n',
},
},
};
接下來在入口文件 src/main.js 文件中引入:
// ...
import i18n from './lang';
// ...
new Vue({
i18n,
router,
el: '#app',
template: '<App/>',
components: { App },
});
到這里就配置好了,于是我們就可以在組件中使用了,使用方法如下 src/views/index.vue;
<h1>{{ $t('index.header.title') }} </h1>
<h3>{{ $t('index.header.subtitle') }} </h3>
看起來是不是很簡單,引入 vue-i18n 后,它會在 Vue 實例上掛載它提供的國際化的 api,比如 $t、$tc、$td 等格式化函數(shù)。使用時我們只需要在組件中直接調用就行,比如:this.$t。
$t 接受兩個參數(shù),第一個為我們在語言包中訪問的屬性路徑,各級屬性用 . 符號鏈接,第二個參數(shù)可以動態(tài)傳參,比如我們將 index.header.subtitle 的模板修改為 You will learn how to use {name},那么我們可以像下面這樣使用:
$t('index.header.subtitle', {name: 'vue-i18n})
小技巧
獲取鍵值集合
相信大家已經(jīng)知道 vue-i18n 如何使用了,建議通讀 官方文檔,自己動手配置熟悉下。
大多數(shù)情況下,我們只需要使用 $t 就可以滿足我們的需求了。但是當我們的的語言包層級越來越深時,你會發(fā)現(xiàn)屬性路徑越來越長,而且在某些組件內,需要書寫很多遍,比如上面的 index.header.title 和 index.header.subtitle,它們的前綴都是 index.header,在這里寫兩遍我們能接受,如果是100遍呢?作為一名懶惰的程序員,怎么能接受同樣的事情做3遍呢,更何況是100遍,簡直不敢想象!
其實我們可以先獲取前綴的對象集合,然后通過這個集合對象來訪問,節(jié)省臃長的前綴的重復書寫,使用的時候我們需要先在 src/views/index.vue 組件的 computed 中定義國際化集合:
export default {
name: 'Index',
computed: {
indexMsg() {
return this.$t('index.header')
}
},
};
接下來修改下模板中的書寫方式:
<h1>{{ indexMsg.title }}</h1>
<h3>{{ indexMsg.subtitle }} </h3>
注意:這里必須定義為計算屬性,不然在切換語言時,視圖國際化將無法自動更新。
多元化的消息
曾經(jīng)在做一個登錄錯誤信息提示的時候,遇到個需求:用戶多次輸入錯誤后,會提示多長時間后重試,但是接口只是返回個剩余秒數(shù),需要根據(jù)這個秒數(shù)計算出剩余的時分秒,當大于1小時時,提示 請 xx 小時后重試,當小于1小時時,提示 請 xx 分 xx 秒后重試。一般碰到這種需求,我們是肯定需要結合模板變量來實現(xiàn),最簡單的方式就是直接定義兩個國際化鍵值,比如 msg1,msg2,然后通過計算出的小時數(shù)來做 if 判斷就行。
但是當項目越來越龐大,項目中類似的需求越來越多的時候,你會發(fā)現(xiàn)你的語言包的鍵值對越來越多,到了最后,取個屬性名就要想半天,不知道大家有沒有跟我類似的痛點,所以我對于這種類似需求用 $tc 函數(shù)來實現(xiàn)的。
$tc 函數(shù)允許我們一個國際化鍵值可以通過管道符 | 來分割多元化的信息,如下:
export default {
// ...
login: {
tips: '請{minute}分{second}秒后再重試 | 請{hour}小時后再重試',
},
};
然后在模板中使用:
<p>{{ $tc('login.tips', 1, {minute: 30, second: 29}) }}</p>
<p>{{ $tc('login.tips', 2, {hour: 1}) }}</p>
對于上面的需求,我只需要根據(jù)接口返回的秒數(shù),計算下 hour 值,然后寫一個三目運算就解決了:
hour >= 1 ? this.$tc('login.tips', 2, {hour: 1}) : this.$tc('login.tips', 1, {minute: 30, second: 29})
可選數(shù)組消息
其實對于可選的國際化消息需求,我們還可以通過數(shù)組來實現(xiàn),比如我們的某個訂單狀態(tài)需要國際化,0-5 分別對應不同的狀態(tài),我們只需要定義簡單的數(shù)組就可以搞定了。
首先定義語言包:
export default {
//...
order: {
status: [
'待付款',
'待發(fā)貨',
'已發(fā)貨',
'已簽收',
'已取消',
],
},
};
然后我們只需要根據(jù)不同狀態(tài),來讀取鍵值就行:
<span>{{ $t(`order.status[orderStatus]`) }}</span>
<script>
export default {
name: 'Index',
data() {
return {
orderStatus: 3,
};
},
//...
};
</script>
當然這里也可以通過多元化的方式來實現(xiàn),大家可以自行嘗試下。
模塊化
隨著項目越來越大,你會發(fā)現(xiàn) src/lang/zh.js 文件越來越臃腫,每次改個國際化文案,需要在上千行的對象中,找半天對應的鍵值對,而且那深不可測的屬性層級,著實讓人眼花繚亂。
既然 src/lang/zh.js 是 js 文件,我們當然就可以對其進行拆分,然后把不同的模塊拆分成獨立的 js 文件進行維護,是不是找起來會輕松好多,比如我們嘗試將實例中的 zh.js 產(chǎn)分成 src/lang/zh-modules/index.js、src/lang/zh-modules/login.js、 src/lang/zh-modules/order.js 三個模塊文件:
// index.js
export default {
index: {
header: {
title: 'Vue-I18n 示例',
subtitle: '你將學會如何使用 vue-i18n',
},
},
};
// login.js
export default {
login: {
tips: '請{minute}分{second}秒后再重試 | 請{hour}小時后再重試',
},
};
// order.js
export default {
order: {
status: [
'待付款',
'待發(fā)貨',
'已發(fā)貨',
'已簽收',
'已取消',
],
},
};
然后在 src/lang/zh.js 文件中一次引入:
import index from './zh-modules/index';
import login from './zh-modules/login';
import order from './zh-modules/order';
export default {
...index,
...login,
...order,
};
這樣就變得清晰很多,下次產(chǎn)品需要你修改登錄相關的文案,你只需要修改 login.js 模塊就行。
但是按模塊拆分后,有人就會有疑問了,我這么拆分后,語言模塊文件會隨著語言種類成倍數(shù)增加,應該怎么辦?
這里就說說我的解決辦法,就是將語言文件按模塊合并。所謂 天下大勢,合久必分,分久必合。我們將相同的模塊按文件合并起來就可以了,比如 index 模塊:
const zh = {
index: {
header: {
title: 'Vue-I18n 示例',
subtitle: '你將學會如何使用 vue-i18n',
},
},
};
const en = {
index: {
header: {
title: 'Vue-I18n Demo',
subtitle: 'You will learn how to use vue-i18n',
},
},
}
export default {
zh,
en,
};
然后只需要在 src/lang/index.js 引入,稍作修改就行:
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import indexMsg from './modules/index';
import loginMsg from './modules/login';
import orderMsg from './modules/order';
Vue.use(VueI18n);
const messages = {
zh: {
...indexMsg.zh,
...loginMsg.zh,
...orderMsg.zh,
},
en: {
...indexMsg.en,
...loginMsg.en,
...orderMsg.en,
},
};
const i18n = new VueI18n({
locale: 'zh',
messages,
});
export default i18n;
此時你會發(fā)現(xiàn)我們已經(jīng)不需要 src/lang/zh.js 和 src/lang/en.js 文件了。而且這里有個好處是,我們在讓翻譯幫忙的事后,只需要在同一個文件中對我們的相同鍵值進行修改和編輯了,是不是方便很多,趕緊動手嘗試下吧~
總結
本文只是個人在開發(fā)過程中,關于國際化方案的經(jīng)驗總結,我相信大家也有很多提高效率的方案。希望本篇能夠解決你在開發(fā)中遇到的一些疑惑和痛點,也非常歡迎評論或來信交流你的優(yōu)化方案。