by yugasun from yugasun.com/post/you-ma… 本文可全文轉載,但需要保留原作者和出處。
相信大多數使用 Vue 專案都會面臨國際化的問題,而 vue-i18n 便是國際化的不二之選,它用起來非常簡單,但是同時也會帶來一些問題和挑戰。本篇是個人在專案上國際化時一些經驗的總結,希望能在國際化的道路上幫到你。
基本使用
vue-i18n
官方文件介紹的很清楚,先在 src/lang
目錄下分別建立三個檔案 index.js
,zh.js
,en.js
,
然後在 index.js
中建立 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
等格式化函式。使用時我們只需要在元件中直接呼叫就行,比如:this.$t
。
$t
接受兩個引數,第一個為我們在語言包中訪問的屬性路徑,各級屬性用 .
符號連結,第二個引數可以動態傳參,比如我們將 index.header.subtitle
的模板修改為 You will learn how to use {name}
,那麼我們可以像下面這樣使用:
$t('index.header.subtitle', {name: 'vue-i18n})
複製程式碼
小技巧
獲取鍵值集合
相信大家已經知道 vue-i18n
如何使用了,建議通讀 官方文件,自己動手配置熟悉下。
大多數情況下,我們只需要使用 $t
就可以滿足我們的需求了。但是當我們的的語言包層級越來越深時,你會發現屬性路徑越來越長,而且在某些元件內,需要書寫很多遍,比如上面的 index.header.title
和 index.header.subtitle
,它們的字首都是 index.header
,在這裡寫兩遍我們能接受,如果是100遍呢?作為一名懶惰的程式設計師,怎麼能接受同樣的事情做3遍呢,更何況是100遍,簡直不敢想象!
其實我們可以先獲取字首的物件集合,然後通過這個集合物件來訪問,節省臃長的字首的重複書寫,使用的時候我們需要先在 src/views/index.vue
元件的 computed
中定義國際化集合:
export default {
name: 'Index',
computed: {
indexMsg() {
return this.$t('index.header')
}
},
};
複製程式碼
接下來修改下模板中的書寫方式:
<h1>{{ indexMsg.title }}</h1>
<h3>{{ indexMsg.subtitle }} </h3>
複製程式碼
注意:這裡必須定義為計算屬性,不然在切換語言時,檢視國際化將無法自動更新。
多元化的訊息
曾經在做一個登入錯誤資訊提示的時候,遇到個需求:使用者多次輸入錯誤後,會提示多長時間後重試,但是介面只是返回個剩餘秒數,需要根據這個秒數計算出剩餘的時分秒,當大於1小時時,提示 請 xx 小時後重試
,當小於1小時時,提示 請 xx 分 xx 秒後重試
。一般碰到這種需求,我們是肯定需要結合模板變數來實現,最簡單的方式就是直接定義兩個國際化鍵值,比如 msg1,msg2
,然後通過計算出的小時數來做 if
判斷就行。
但是當專案越來越龐大,專案中類似的需求越來越多的時候,你會發現你的語言包的鍵值對越來越多,到了最後,取個屬性名就要想半天,不知道大家有沒有跟我類似的痛點,所以我對於這種類似需求用 $tc
函式來實現的。
$tc
函式允許我們一個國際化鍵值可以通過管道符 |
來分割多元化的資訊,如下:
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>
複製程式碼
對於上面的需求,我只需要根據介面返回的秒數,計算下 hour
值,然後寫一個三目運算就解決了:
hour >= 1 ? this.$tc('login.tips', 2, {hour: 1}) : this.$tc('login.tips', 1, {minute: 30, second: 29})
複製程式碼
可選陣列訊息
其實對於可選的國際化訊息需求,我們還可以通過陣列來實現,比如我們的某個訂單狀態需要國際化,0-5
分別對應不同的狀態,我們只需要定義簡單的陣列就可以搞定了。
首先定義語言包:
export default {
//...
order: {
status: [
'待付款',
'待發貨',
'已發貨',
'已簽收',
'已取消',
],
},
};
複製程式碼
然後我們只需要根據不同狀態,來讀取鍵值就行:
<span>{{ $t(`order.status[orderStatus]`) }}</span>
<script>
export default {
name: 'Index',
data() {
return {
orderStatus: 3,
};
},
//...
};
</script>
複製程式碼
當然這裡也可以通過多元化的方式來實現,大家可以自行嘗試下。
模組化
隨著專案越來越大,你會發現 src/lang/zh.js
檔案越來越臃腫,每次改個國際化文案,需要在上千行的物件中,找半天對應的鍵值對,而且那深不可測的屬性層級,著實讓人眼花繚亂。
既然 src/lang/zh.js
是 js
檔案,我們當然就可以對其進行拆分,然後把不同的模組拆分成獨立的 js 檔案進行維護,是不是找起來會輕鬆好多,比如我們嘗試將例項中的 zh.js
產分成 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: [
'待付款',
'待發貨',
'已發貨',
'已簽收',
'已取消',
],
},
};
複製程式碼
然後在 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,
};
複製程式碼
這樣就變得清晰很多,下次產品需要你修改登入相關的文案,你只需要修改 login.js
模組就行。
但是按模組拆分後,有人就會有疑問了,我這麼拆分後,語言模組檔案會隨著語言種類成倍數增加,應該怎麼辦?
這裡就說說我的解決辦法,就是將語言檔案按模組合併。所謂 天下大勢,合久必分,分久必合
。我們將相同的模組按檔案合併起來就可以了,比如 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;
複製程式碼
此時你會發現我們已經不需要 src/lang/zh.js
和 src/lang/en.js
檔案了。而且這裡有個好處是,我們在讓翻譯幫忙的事後,只需要在同一個檔案中對我們的相同鍵值進行修改和編輯了,是不是方便很多,趕緊動手嘗試下吧~
總結
本文只是個人在開發過程中,關於國際化方案的經驗總結,我相信大家也有很多提高效率的方案。希望本篇能夠解決你在開發中遇到的一些疑惑和痛點,也非常歡迎評論或來信交流你的優化方案。