工做笔记八——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~

相关文章
相关标签/搜索