近一個月的時間都在忙離職和入職的事情,git上面的項目這幾天才開始重新維護。修復了之前的幾個issue,又加了幾個新的功能組件的應用。今天剛好下午得空,覺得新項目會用到vue的國際化多語言,所以把vue-i18n這個組件的文檔過了一遍,總結了一下,寫了個小demo包含了基本上項目常用的需求,供參考。
組件git地址:vue-i18n的github
組件文檔地址:vue-i18n的文檔
安裝與初體驗
可以直接在html中直接引入:
<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>
如果你的項目是npm管理包的,那么也可以直接使用npm安裝:
npm install vue-i18n
接下來,只要在main.js實例化組件並使用就可以了。
不過這里要注意的是,Vue.use(VueI18n) 要放在實例化之前,不然會報錯。
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: 'zh',
messages:{
'zh':{
hello:'你好'
},
'en':{
hello:'hello'
}
}
});
new Vue({
el: '#app',
router,
i18n,
store,
template: '<App/>',
components: { App }
})
ok,我們可以新建一個頁面來測試我們目前為止的結果如何了。
<template> <div class="i18n"> <mt-header fixed title="國際化測試"> <router-link to="/tool" slot="left"> <mt-button icon="back">返回</mt-button> </router-link> </mt-header> <div class="content"> {{ $t('hello') }} </div> </div> </template> <script></script> <style></style>
現在看看運行結果:
完美~
在SPA應用的組件中使用
第一步看上去很好,但是我們很多的vue項目都是基於vue-cli的SPA應用,這種在main.js中引入國際化資源的方式顯然給我們的開發帶來不便。所以,我們需要將國際化資源放在每一個組件中,這樣的代碼應該是更加合理並且易於維護的。
要實現這樣的效果,也很簡單,首先你需要一個loader來處理這種場景:
npm i --save-dev @kazupon/vue-i18n-loader
安裝完畢后,需要在webpack的配置中,把這個loader添加到.vue文件的解析中:
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
// you need to specify `i18n` loaders key with `vue-i18n-loader` (https://github.com/kazupon/vue-i18n-loader)
i18n: '@kazupon/vue-i18n-loader'
}
}
},
// ...
]
如果你是vue-cli的腳手架項目,可能這里的配置有些不一樣,我當前版本的是這樣的:
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
...
]
我們只需要沿着這個依賴關系找到vueLoaderconfig就可以了,在build/目錄下,修改此處代碼即可:
loaders: Object.assign(utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),{
i18n: '@kazupon/vue-i18n-loader'
}),
這樣就把vue-i18n-loader添加到了vue文件解析的loaders中了。現在我們可以在組件內使用國際化了:
<template> <div class="i18n"> <mt-header fixed title="國際化測試"> <router-link to="/tool" slot="left"> <mt-button icon="back">返回</mt-button> </router-link> </mt-header> <div class="content"> {{ $t('helloworld') }} </div> </div> </template> <i18n> { "en": { "helloworld": "hello world!" }, "zh": { "helloworld": "你好,世界!" } } </i18n> <script></script> <style></style>
看看效果:
這樣我們編碼的時候,就不用兩頭維護代碼了。
但是這還不是最佳實踐,因為項目中有些時候是有相同的文字或其他需要國際化處理的資源的,這時候如果每個組件都維護,就會出現很多重復的代碼,不美觀。所以,最好的方法是全局與局部(組件)共同使用。
要想達到這種效果,首先得修改i18n的構造器:
new VueI18n({ locale:'zh', messages:{ zh: require('./common/i18n/app-zh.json'), en: require('./common/i18n/app-en.json'), jp: require('./common/i18n/app-jp.json') }
})
可以看到,這里的messages引用了三個國際化的資源,如圖所示:
我們來測試一下是否可行:
<template> <div class="i18n"> <mt-header fixed :title="$t('title')"> <router-link to="/tool" slot="left"> <mt-button icon="back">返回</mt-button> </router-link> </mt-header> <div class="content"> <div>來自全局:<h4>{{ $t("person.name") }}</h4></div> <div>來自組件:<h4>{{ $t("helloworld") }}</h4></div> </div> </div> </template> <i18n> { "en": { "helloworld": "hello world!" }, "ja": { "helloworld": "こんにちは、世界!" }, "zh": { "helloworld": "你好,世界!" } } </i18n> <script></script> <style></style>
運行一下:
可以看到,全局的和組件內的國際化資源都正確的輸出了~
使用說明
上面已經介紹了i18n的基本使用以及項目中的配置了,現在可以開始學習它的其他有趣且實用的功能了。
格式化
html格式化
簡言之,就是可以在值中添加html標簽,但是不推薦這種方式,因為會帶來安全性問題(xss),建議使用組件插值(后面介紹)來實現類似需求。
......
<div>
輸出html:<h4 v-html="$t('html')"></h4>
</div>
......
<i18n>
{
"en": {
"helloworld": "hello world!"
},
"ja": {
"helloworld": "こんにちは、世界!"
},
"zh": {
"helloworld": "你好,世界!",
"html": "湖人 <span style='color:blue'>總冠軍</span>"
}
}
</i18n>
.....
運行結果:
命名格式化
簡言之,就是傳遞一個命名參數過去給引用者。
...
<div>
命名格式化:<h4>{{ $t('named-formatting',{ name: '朱辰迪'}) }}</h4>
</div>
...
<i18n>
{
...
"zh":{
....
"named-formatting": "{name} 很好"
}
}
</i18n>
運行結果:
列表格式化
跟上面的命名格式化差不多,只是傳入的參數有所不同:
<div>
列表格式化1:<h4>{{ $t('list-formatting',['朱','辰','迪']) }}</h4>
</div>
<div>
列表格式化2:<h4>{{ $t('list-formatting',{ 0:'朱',1:'辰',2:'迪'}) }}</h4>
</div>
...
<i18n>
{
...
"zh":{
....
"list-formatting": "{0} {1} {2} 很好呀~"
}
}
</i18n>
運行結果:
自定義格式化
這個有點復雜,要重寫構造函數,后續有時間再研究,有興趣的朋友可以看看文檔。
復數形式
個人覺得這個用處不怎么大,不細說,看代碼:
<div> 復數形式: <h4>{{ $tc('apple',11, { count:11 } ) }}</h4>/ <h4>{{ $tc('apple',1) }}</h4>/ <h4>{{ $tc('apple',0 ) }}</h4> </div> .... <i18n> { ... "zh":{ .... "apple": "no apples | one apple | {count} apples" } } </i18n>
運行結果:
日期格式化
感覺這個作用也不大,很少有對日期格式嚴格要求的(個人想法)。也不多介紹了,直接拷了官網的例子僅作參考:
還是一樣先修改構造器:
let dateTimeFormats = {
'en-US': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric'
}
},
'zh-CN': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric',
hour12: true
}
}
}
return new VueI18n({
locale:lang,
fallbackLocale: 'en',
dateTimeFormats,
messages:{
zh: require('./common/i18n/app-zh.json'),
en: require('./common/i18n/app-en.json'),
jp: require('./common/i18n/app-jp.json')
}
})
使用:
<div> 日期國際化: <h4>{{ $d(new Date(), 'short','en-US') }}</h4> <h4>{{ $d(new Date(), 'long', 'zh-CN') }}</h4> </div>
運行結果:
Fallback
什么是fallback呢?這里的意思是指,根據指定的locale沒有找到對應的資源的情況下,使用的locale。這個要在構造函數中指定。比如我這里制定了”en”作為fallback策略。
new VueI18n({ locale:lang, fallbackLocale: 'en', messages:{ zh: require('./common/i18n/app-zh.json'), en: require('./common/i18n/app-en.json'), jp: require('./common/i18n/app-jp.json') }
})
這里引用一個中文環境下沒有的資源:
<div>fallback :<h4>{{ $t("fromEn") }}</h4></div>
...
{
"en":{
"fromEn": "this is from English"
},
"zh":{
//no fromeEn key
}
}
這樣它就會自動根據 fallbackLocale 指定的語言來尋找對應的key,找到后輸出。如果都沒有找到,那么會原樣輸出key值:fromEn
使用自定義指令:v-t
這個使用了vue中的自定義指令,也就是將國際化用指令實現。就是將path傳遞給v-t指令就可以了,但是要注意,這個path要是 字符串 類型或者 對象 類型的。
字符串類型的,直接將key傳遞給指令即可。
對象類型的,path 代表了key,也可以使用args來傳遞參數。
<div>
v-t指令國際化-1:
<h4>
<p v-t="'hello'"></p>
</h4>
</div>
<div>
v-t指令國際化-2:
<h4>
<p v-t="{path,args:{param:'KOBE'}}"></p>
</h4>
</div>
...
<i18n>
{
"en": {
...
},
"ja": {
...
},
"zh": {
"hello": "你好,世界!",
"directive": "來自指令 參數:{param}",
...
}
}
</i18n>
運行結果:
組件插值(component interpolation)
這個在前文中也提到過了。當我們的國際化資源中包含html語言時,直接使用會導致xss安全問題。另外,這種方式也有其他的優點。
比如下例:
<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>
正常情況下,如果想對上述語句使用國際化,你可能會這么做:
<p>{{ $t('term1') }}<a href="/term">{{ $t('term2') }}</a></p> <i18n> { en: { term1: 'I Accept xxx\'s', term2: 'Terms of Service Agreement' } } </i18n>
但是可以看出,這種方式非常不優雅。我們可以試一下vue-i18n推薦的方式:
<div>
組件插值:
<i18n path="term" tag="label" for="tos">
<a href="#" target="_blank">{{ $t('tos') }}</a>
</i18n>
</div>
...
<i18n>
{
....
"zh":{
"tos": "服務條款",
"term": "我接受 xxx {0}."
}
}
</i18n>
運行結果:
關於這個插值的,還有一些高級用法,因為感覺平時不太可能會涉及到這塊的東西,就不寫了。想看的話可以參考:組件插值的高級用法
其他
除了上述中的常用特性以外,還有一些其他的功能,比如熱替換,懶加載國際化資源,修改本地語言等,都是比較簡單的操作,順着文檔復制粘貼就可以了~
以上代碼均已提交至github:Jerry的github 如果對你有幫助,歡迎star~