vue-i18n を使ってみました

vue-i18n は Vue.js 向けの国際化(多言語化)プラグインです。

今回アプリのアップデート版を作るにあたり、Monaca の Onsen UI + Vue プロジェクトで vue-i18n を使ってみました。

以下に備忘録を兼ねた使用例を記します。

インストール

npm install vue-i18n

ファイル構成

  • 対応する言語は日本語、英語、韓国語、中国語(簡体)、中国語(繁体)、スペイン語です。それぞれ JSON ファイルに分けて記述します。
  • i18n.js では vue-i18n のインスタンスを作成します。
  • main.js では Vue 本体のインスタンスを作成しています。そこに i18n.js で作成した vue-i18n のインスタンスを追加します。

以下、ファイルごとに説明します。

言語ファイル

日本語用の言語ファイル ja.json の一部を以下に記します。

{
  "cancel": "キャンセル",
  "set": "設定",
  "del": "削除",
  "sound": {
    "title": "通知音",
    "type": [
      "バイブレーション",
      "鐘(1)",
      "鐘(2)",
      "鐘(3)",
      "お鈴"
    ]
  },
  .....
  "background": {
    "title": "背景",
    "type": [
      "森林",
      "蓮の花",
      "空",
      "星"
    ]
  },
  .....
}

i18n.js

Vue と vue-i18n プラグインをインポートします。

import Vue from 'vue';
import VueI18n from 'vue-i18n';

ブラウザの言語バージョン(window.navigator.language)からアプリで使用する言語ファイルを決定し、require() で読み込みます。

let locale = "en", message = {};    // default: "en"
const language = window.navigator.language;
if (language) {
  const lang = language.toLowerCase().split("-");
  switch (lang[0]) {
    case "ja":    // 日本語: ja, ja-JP
    case "es":    // スペイン語: es, es-*
    case "ko":    // 韓国語
    case "zhcn":  // 中国語 (簡体)
    case "zhtw":  // 中国語 (繁体)
      locale = lang[0];
      break;
    case "zh":    // 中国語: zh-*
      switch (lang[1]) {
        case "cn":  // 中国
        case "sg":  // シンガポール
        case "hans":
          locale = "zhcn";  // 中国語 (簡体)
          break;
        case "tw":  // 台湾
        case "hk":  // 香港
        case "mo":  // マカオ
        case "hant":
          locale = "zhtw";  // 中国語 (繁体)
          break;
      }
      break;
  }
}

message[locale] = require(`./assets/i18n/${locale}.json`)

vue-i18n のインストールおよびインスタンスの作成を行います。作成したインスタンスはエクスポートしておきます。

Vue.use(VueI18n);

export const i18n = new VueI18n({
  locale,
  messages: message   // 必要な言語ファイルのみ設定する
});

main.js

Vue と i18n.js からエクスポートされた vue-i18n インスタンスをインポートします。

import Vue from 'vue';
import { i18n } from './i18n.js'

Vue インスタンスを作成します。このとき Vue のオプションオブジェクトとして vue-i18n インスタンスを渡します。

new Vue({
  el: '#app',
  i18n,
  template: '<app></app>',
  components: { App },
  .....
});

Vue ファイル

テンプレートに直接 $t() メソッドを記述することができます。

<v-ons-list-header>{{ $t('sound.title') }}</v-ons-list-header>
<v-ons-list-item
  v-for="(soundType, $index) in $t('sound.type')"
  :key="soundType"
  tappable
>
  <label class="left">
    <v-ons-radio
      :input-id="'radio-sound-' + $index"
      :value="$index"
      v-model="tmp_soundType"
      @click="clickedSoundType($index)"
    >
    </v-ons-radio>
  </label>
  <label
    :for="'radio-sound-' + $index"
    class="center"
  >{{ soundType }}</label>
</v-ons-list-item>

$t() メソッドはスクリプトでも使うことができます。

const a = this.$t('background.type')
console.log(a)    // ["森林","蓮の花","空","星"]

これを応用して算出プロパティで使ってみました。this.state.backgroundType(文字列の0から3)の値に対応する文字が表示されます。

<div>{{ image }}</div>
computed: {
  image () {
    return this.$t(`background.type[${Number(this.state.backgroundType)}]`)
  }
}

また、vue-i18n のインスタンス作成時に設定した locale にアクセスすることができます。

onHelp () {
  window.open('http://senmyou.xyz/app/mnote/mnote_help_' + this.$i18n.locale, '_system');
},
onPrivacy () {
  if (this.$i18n.locale === "ja") {
    window.open('http://senmyou.xyz/app/mnote_privacy_ja', '_system');
  } else {
    window.open('http://senmyou.xyz/app/mnote_privacy_en', '_system');
  }
}

v-ons-list-item のリップルエフェクト

Onsen UI の v-ons-list-item コンポーネントに tappable 属性をつけると、タップしたことが視覚的にわかるようなエフェクトを表示する要素が追加されます。Android ではタップしたときにリップル(波紋)エフェクトを表示します。

また、v-ons-list-item コンポーネントでは left、center、right、expandable-content の4つのセクションが提供されています。

以下のサンプルは、left、center、right のセクションを組合せたものです。上段の「leftのみ」や「rightのみ」、「left right」をタップするとリップルエフェクトがリストアイテムの一部にのみかかるケースがあるのがわかります。

See the Pen Onsen UI by senmyou (@senmyou) on CodePen.

Chrome DevTools で確認したところ、center セクションの要素がない場合は自動的に center セクションの要素が追加されるようです。この center セクションの要素は子要素に ons-ripple コンポーネントを持っており、タップするとこの部分にのみリップルエフェクトがかかります。

このような部分的にかかるリップルエフェクトを防ぎたい場合は、空の center セクションの要素を追加します(前述のサンプルの下段参照)。

<v-ons-list-item tappable>
  <div class="left">left</div>
  <div class="center"></div>
</v-ons-list-item>

<v-ons-list-item tappable>
  <div class="center"></div>
  <div class="right">right</div>
</v-ons-list-item>

<v-ons-list-item tappable>
  <div class="left">left</div>
  <div class="center"></div>
  <div class="right">right</div>
</v-ons-list-item>

v-ons-list のアコーディオン

Onsen UI の v-ons-list コンポーネントでコンテンツを開閉するアコーディオン機能を実装したいときは、v-ons-list-item コンポーネントに expandable 属性を記述します。この v-ons-list-item コンポーネントをタップすると、 expandable-content クラスを付けたdiv要素の表示が切り替わります。

<v-ons-list-item expandable>
  Tap to expand
  <div class="expandable-content">This is shown when expanded</div>
</v-ons-list-item>

expandable 属性は v-ons-list-item コンポーネントでのみ有効です。そこでv-ons-list-header コンポーネントで動作するアコーディオン機能を自作してみました。

以下のサンプルは2つの v-ons-list からなっています。上段は v-ons-list-item に expandable 属性をつけたもの、下段は今回自作したものになります。「Tap to expand 」 をタップするとコンテンツが開閉します。

See the Pen Onsen UI by senmyou (@senmyou) on CodePen.

Vue のトランジションフック を利用しています。リストヘッダでアコーディオンを行いたい方の参考になればと思います。