工做筆記八——vue項目的多語言/國際化插件vue-i18n詳解

近一個月的時間都在忙離職和入職的事情,git上面的項目這幾天纔開始從新維護。修復了以前的幾個issue,又加了幾個新的功能組件的應用。今天恰好下午得空,以爲新項目會用到vue的國際化多語言,因此把vue-i18n這個組件的文檔過了一遍,總結了一下,寫了個小demo包含了基本上項目經常使用的需求,供參考。
組件git地址:vue-i18n的github
組件文檔地址:vue-i18n的文檔javascript

安裝與初體驗

能夠直接在html中直接引入:css

<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>

若是你的項目是npm管理包的,那麼也能夠直接使用npm安裝:html

npm install vue-i18n

接下來,只要在main.js實例化組件並使用就能夠了。
不過這裏要注意的是,Vue.use(VueI18n) 要放在實例化以前,否則會報錯。vue

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,咱們能夠新建一個頁面來測試咱們目前爲止的結果如何了。java

<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> 

如今看看運行結果:
運行結果1webpack

完美~git

在SPA應用的組件中使用

第一步看上去很好,可是咱們不少的vue項目都是基於vue-cli的SPA應用,這種在main.js中引入國際化資源的方式顯然給咱們的開發帶來不便。因此,咱們須要將國際化資源放在每個組件中,這樣的代碼應該是更加合理而且易於維護的。
要實現這樣的效果,也很簡單,首先你須要一個loader來處理這種場景:github

npm i --save-dev @kazupon/vue-i18n-loader

安裝完畢後,須要在webpack的配置中,把這個loader添加到.vue文件的解析中:web

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的腳手架項目,可能這裏的配置有些不同,我當前版本的是這樣的: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>

看看效果:
運行結果2
這樣咱們編碼的時候,就不用兩頭維護代碼了。
可是這還不是最佳實踐,由於項目中有些時候是有相同的文字或其餘須要國際化處理的資源的,這時候若是每一個組件都維護,就會出現不少重複的代碼,不美觀。因此,最好的方法是全局與局部(組件)共同使用。
要想達到這種效果,首先得修改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引用了三個國際化的資源,如圖所示:
項目截圖1
咱們來測試一下是否可行:

<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>

運行一下:
運行結果3
能夠看到,全局的和組件內的國際化資源都正確的輸出了~

使用說明

上面已經介紹了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>
.....

運行結果:
運行結果5

命名格式化

簡言之,就是傳遞一個命名參數過去給引用者。

...
<div>
    命名格式化:<h4>{{ $t('named-formatting',{ name: '朱辰迪'}) }}</h4>
</div>
...
<i18n>
    {
        ...
        "zh":{
            ....
            "named-formatting": "{name} 很好"
        }
    }
</i18n>

運行結果:
運行結果5

列表格式化

跟上面的命名格式化差很少,只是傳入的參數有所不一樣:

<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>

運行結果:
運行結果6

自定義格式化

這個有點複雜,要重寫構造函數,後續有時間再研究,有興趣的朋友能夠看看文檔。

複數形式

我的以爲這個用處不怎麼大,不細說,看代碼:

<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>

運行結果:
運行結果7

日期格式化

感受這個做用也不大,不多有對日期格式嚴格要求的(我的想法)。也很少介紹了,直接拷了官網的例子僅做參考:
仍是同樣先修改構造器:

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>

運行結果:
運行結果9

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

運行結果8

使用自定義指令: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>

運行結果:

運行結果10

組件插值(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>

運行結果:
運行結果11

關於這個插值的,還有一些高級用法,由於感受平時不太可能會涉及到這塊的東西,就不寫了。想看的話能夠參考:組件插值的高級用法

其餘

除了上述中的經常使用特性之外,還有一些其餘的功能,好比熱替換懶加載國際化資源修改本地語言等,都是比較簡單的操做,順着文檔複製粘貼就能夠了~

以上代碼均已提交至github:Jerry的github 若是對你有幫助,歡迎star~

相關文章
相關標籤/搜索