Help us understand the problem. What is going on with this article?

【i18n-js】Railsアプリのi18n(tメソッド)をVue.jsでも使用する

Railsアプリ開発の際、フロントをVue.jsで書いているのですが、
「i18nをフル活用し、ソースコードに日本語のベタ書きは限りなくゼロにしていこう!」
とした場合、
Rails側で使えるt('create')のようなtメソッド
「Vue.jsでも使いたい場合どうすればいいの」
となったので実装の記録を書き残します。

前提

  • macOS Mojave 10.14.6
  • Ruby 2.5.5
  • Rails 5.2.3
  • Vue 2.6.10
  • webpacker 4.0.7

gem「i18n-js」を導入

Gemfile
gem 'i18n-js'
terminal
$ bundle install

JavaScriptでI18nを利用する場合、config/locales/ja.ymlを直接使える訳ではなく、.jsに変換してそれを読み込んで利用します。
i18n-jsはその変換出力のためのgemです。

i18n-js.ymlを作成

どのように変換出力するかのルールを設定するファイルであるi18n-js.ymlを作成します。
以下のコマンドでconfigディレクトリに新規作成されます。

terminal
rails generate i18n:js:config
config/i18n-js.yml
translations:
- file: "app/javascript/packs/i18n/%{locale}.js"
  prefix: "import I18n from './i18n';\n"

fileオプションで変換出力する場所を指定するパスと、言語別に出力されるよう設定します。
(公式などを見ると一般的に使われる設定ではtranslation.jsという1つのファイルに全ての言語の設定を詰め込むみたいですが、ファイルが重くなってしまうため今回は言語別にしています。)

webpackerを導入しているので、app/javascript/packs配下にi18nディレクトリを作成してその中で管理することとします。

さらにprefixオプションをつけると生成されるjsファイルの先頭に好きな文字列を追加できます。
付けた意味は後述します。 

許可するlocaleを限定

後述するrake i18n:js:exportをこのまま実行してしまうと、許可されている全ての言語のjsファイルが生成されてしまいます。(私の場合90個ぐらいでした:imp:
日本で開発されている方であれば、日本語と英語だけで十分かと思いますので今回はその2言語のみ生成されるようにします。

config/application.rb
# デフォルトのlocaleを日本語(:ja)にする
config.i18n.default_locale = :ja

# 許可するlocaleを日本語(:ja)と英語(:en)に限定する
config.i18n.available_locales = %i[ja en]

これでja.ymlja.jsに、en.ymlen.jsに変換出力されるようになります。

rake i18n:js:exportで出力

gem「i18n-js」が用意するjs用localeファイルに変換するためのコマンドを実行します。

terminal
$ rake i18n:js:export

先ほどi18n-js.ymlファイルにfileオプションで指定したパスに対して出力されます

config/i18n-js.yml
translations:
- file: "app/javascript/packs/i18n/%{locale}.js" # ←これ
  prefix: "import I18n from './i18n';\n"

スクリーンショット 2020-04-03 11.44.15.png

ja.jsen.jsは確認できましたか?

app/javascript/packs/i18n/ja.js
import I18n from './i18n';
I18n.translations || (I18n.translations = {});
I18n.translations["ja"] = I18n.extend((I18n.translations["ja"] || {}), {"403_forbidden":"ページを表示できません", ......(中略)..... ,"zip":"郵便番号"});

中を見てみると
1行目でi18n-js.ymlファイルのprefixオプションで付け加えたものがあります。
3行目でRailsのi18nで使っているja.ymlファイルの中身が1行に凝縮されています。
最終的にtメソッドはこの中からマッチするものを探してきます。

i18n.jsを編集

同時にpublic/javascript/i18n.jsも出来ました。
機能の根幹となるメソッドがたくさん書いてあるファイルです。

この中からdefaultLocalelocaleを必要に応じて変更します。
railsのgemi18nとは別物なので、別途定義が必要です。
スクリーンショット 2019-11-01 10.45.08.png
日本語であれば"ja"ですね。

i18n.jsを移動

今編集したpublic/javascript/i18n.jsapp/javascript/i18n/i18n.jsに移動してください。
スクリーンショット 2020-04-03 11.45.32.png
rake i18n:js:exportコマンドを実行すると、毎回初期化されたi18n.jsがpublic/packsに作成されてしまうため、中身を変更したi18n.jsは別の場所に移動しなければいけないのです。(本来そのような必要はないかもしれませんが解決策がわからずこのように運用しています。)

今回はja.jsたちと同じ場所に移動することで、管理しやすくします。

そうすることでprefixオプションでつけたja.jsの1行目の記述が活きてきます。

app/javascript/packs/i18n/ja.js
import I18n from './i18n';
I18n.translations || (I18n.translations = {});
I18n.translations["ja"] = I18n.extend((I18n.translations["ja"] || {}), {"403_forbidden":"ページを表示できません", ......(中略)..... ,"zip":"郵便番号"});

2行目からI18nというモジュールを呼び出そうとしていますが、importしていないものは呼び出すことができないので、先にimportさせているのです。
同じディレクトリに置いたのでパスも相対パスでOK。

webpackに(locale).jsを読み込ませる

方法は2通りあります。

1.エントリーポイントで%{locale}.jsファイルをimport

application.js
// 〜(中略)〜
import './i18n/ja'

webpackerのエントリーポイントで各localeごとのjsファイルをimportします。(enが必要な方はenもimportしてください)
webpackはimportしたファイルの中でさらにimportされているものは芋づる式にバンドル(まとめる)対象になるので、ここではメソッド本体であるi18n.jsはimportしなくて大丈夫です。

2.各Vueコンポーネント内でimport

sample.vue
<template>
</template>

<script>
import I18n from "../i18n/i18n";
import "../i18n/ja";

export default {
}
</script>

この方法はVueコンポーネントがを作る度に書き加えないといけないので面倒なのですが、
そもそもI18nを使いたいVueコンポーネントが全Vueファイルの中で一部しかない場合などはこの方法でいいでしょう。

先ほど1番では本体であるimport I18n from "../i18n/i18n";は呼び出す必要はないと言いましたが、
後述するおまけ2:script内でもI18nを使いたい場合は?の中で
'I18n' is not defined
とLinterに怒られてしまう可能性があるため、便宜的に本体も呼び出しています。

(※2通りの方法を紹介しましたが、以下こちらの2番の方法で記述を進めていきます)

consoleでI18nコマンドが使えるか確認

現時点でI18nが使えるかどうか、
rails serverを立ち上げてローカルでRailsアプリにアクセスします。
chromeのデベロッパーツールで画像のように打ち込んで確認してみましょう。
スクリーンショット 2019-11-01 10.35.10.png
2つ目はご自身のアプリで定義してあるものを適当に呼び出してみてくださいね。

vueファイルでI18nコマンドを省略するためのmixinを作成

今のままではI18n.t('hoge')のように毎回先頭にI18nを付けないと呼び出せないので面倒です。
そこでmixinを作成し、t('hoge')と省略形で呼び出せるようにします。

app/javascript/packs/mixins/i18n.js
export const i18n = {
  methods: {
    t: (...args) => I18n.t(...args),
    currentLocale: () => I18n.currentLocale()
  }
};

コンポーネントでmixinをimport

sample.vue
<template>
</template>

<script>
import I18n from "../i18n/i18n";
import "../i18n/ja";
import { i18n } from "../mixins/i18n";

export default {
  mixins: [i18n]
}
</script>

これでvueファイル内で呼び出すt('hoge')I18n.t('hoge')であると理解してくれます。

vueファイルでtメソッドが使えるか確認

sample.vue
<template>
  <p>{{ t('create') }}</p>
</template>

<script>
import I18n from "../i18n/i18n";
import "../i18n/ja";
import { i18n } from "../mixins/i18n";


export default {
  mixins: [i18n]
}
</script>

上記vueファイルが使われているページにアクセスし、
I18nで定義された日本語(今回の場合は「登録」)が表示されます。

以上です。お疲れ様でした!

おまけ1:Rails側のi18nのyamlファイルを修正した場合は?

terminal
$ rake i18n:js:export

修正を加える都度変換してください。

おまけ2:script内でもI18nを使いたい場合は?

mixinはtemplate内でt('hoge')を使うことを可能にするのですが、
scriptの中では省略ができません。

sample.vue
<template>
  <p>{{ title }}</p>
</template>

<script>
import I18n from "../i18n/i18n";  // ←敢えて読み込んでいます
import "../i18n/ja";
import { i18n } from "../../mixins/i18n";

export default {
  mixins: [i18n],
  data() {
    return {
      title: I18n.t("title")  // ←ここで使うために
    };
  }
}
</script>

おまけ3:引数の渡し方は?

{}(中括弧)で囲みます

config/locale/ja.yml
ja:
  mypage: "マイページ"
  msg:
    update_successful: "%{v}を更新しました。"

jsファイルなら
スクリーンショット 2019-11-13 13.24.10.png

vueファイルなら

sample.vue
<template>
  <p>{{ t('msg.update_successful', { v: t('mypage') }) }}</p>
  <p>{{ message }}</p>
  // どちらも出力結果は同じ
</template>

<script>
import { i18n } from "../mixins/i18n";

export default {
  mixins: [i18n],
  data() {
    return {
      message: I18n.t('msg.update_successful', { v: I18n.t('mypage') })
    };
  }
}
</script>

参考記事

・i18n-js 公式ドキュメント
 https://github.com/fnando/i18n-js
・[stackoverflow]単一のリポジトリでロケールを共有するRails + Vue.jsインスタンスにI18nがありますか?
 https://stackoverflow.com/questions/48950685/is-there-a-i18n-on-a-railsvue-js-instance-which-share-locales-in-a-single-repos
・[Qiita]i18n-jsで必要な言語のみロードしてパフォーマンスUP
 https://qiita.com/konifar/items/9886c7da97f20d3206cc
・[Qiita]react_railsとi18n-jsをwebpackerで動かす
 https://qiita.com/Atsuyoshi-N/items/48f8b5d5802b9ac47732#i18n%E3%81%AE%E8%A8%AD%E5%AE%9Arails%E5%81%B4

yosaprog
Rails + Vue.js でBtoBサービスの開発に携わっています。
assign-navi
株式会社アサインナビは、日本最大級のエンジニア・IT企業とIT案件のマッチングサイト「アサインナビ」を運営する企業です。
https://assign-navi.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした