Rails with i18n(多言語化対応)

## Rails with i18n

Railsアプリを作成する際、validateのメッセージや、ボタンの文言などは通常英語で表示されています（viewの雛形が英語であるためですが…）

日本人なので、当然日本語表示にはしたいところですが、そのまま埋め込んでしまうのは、やはり保守性に欠けます。作成する人による、文言の揺れなどが発生するのも問題です。

また、多言語化対応も視野にすると、同じ画面を言語に合わせて作成するのも、保守性に欠けます。

Railsの多言語化対応を盛り込みます。

## i18n
[Wikipedia](https://ja.wikipedia.org/wiki/%E5%9B%BD%E9%9A%9B%E5%8C%96%E3%81%A8%E5%9C%B0%E5%9F%9F%E5%8C%96)によると


> 情報処理における国際化と地域化（こくさいか と ちいきか）は、ソフトウェアを開発した環境とは異なる環境、特に外国や異文化に適合させる手段である。
> 
> 国際化（アメリカ英語: internationalization イギリス英語: internationalisation、i18n) は、ソフトウェアに技術的な変更を加えることなく多様な言語や地域に適合できるようにする、ソフトウェア設計の工程である。

とあります。「技術的な変更を加えることなく多様な言語や地域に適合する」ことが、重要ですね。

## 多言語化の導入

### Gemfile

[Rails用の多言語ライブラリrails-i18n](https://github.com/svenfuchs/rails-i18n)を**Gemfile**にGemを追加する記述をします。

```:Gemfile
# rails-i18n
gem 'rails-i18n'
```

でインストール

```bash:terminal
$ bundle install
```

### config/application.rb

**config/application.rb**に設定情報を追加します。

```rb:config/application.rb
...
module Habilidad
  class Application < Rails::Application
...
    # 言語ファイルを階層ごとに設定するための記述
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]

    # アプリケーションが対応している言語のホワイトリスト(ja = 日本語, en = 英語)
    config.i18n.available_locales = %i(ja en)

    # 上記の対応言語以外の言語が指定された場合、エラーとするかの設定
    config.i18n.enforce_available_locales = true

    # デフォルトの言語設定
    # config.i18n.default_locale = :en
    config.i18n.default_locale = :ja
...
  end
end
```

### config/locales

**config/locales**に多言語設定ファイル(*.yml)を配置する。

**config.i18n.load_path**の設定で、階層的に設定ファイルを管理することができる。

ちなみに作成中のアプリはこんな感じ

```:config/locales
/my_application/config/locales
|--defaults          ## 共通系
|  |--en.yml
|  |--ja.yml
|--models            ## モデル系
|  |--model_name_A   ## 各モデルのディレクトリ
|  |  |--en.yml
|  |  |--ja.yml
|  |--model_name_B
|  |  |--en.yml
|  |  |--ja.yml
|--views             ## ビュー系
|  |--view_name_A    ## 各ビューのディレクトリ
|  |  |--en.yml
|  |  |--ja.yml
|  |--view_name_B
|  |  |--en.yml
|  |  |--ja.yml
```

こうしてモデルごと画面ごとに多言語設定ファイルを管理できるので、複数人で開発していても問題が起こりにくい。

viewも**index.ja.yml**など画面単位でも切り分けられるのですが、流石に冗長すぎると感じたので、私は採用していません。(複雑になったら採用するかもしれませんが)

### .yml

ファイル名は対応言語。
[こちら](http://alfa.hatenablog.jp/entry/2013/12/03/221308)を参考に作成しました。

```yml:default/ja.yml
ja:                              ## 言語名
  dictionary:                    ## 共通系であることを示す
    title:                       ## 画面のタイトル
      create: "新規作成"
      update: "更新"
      destory:
        confirm: "削除確認"
    message:                     ## 固定メッセージ
      create:
        complete: "登録しました"
      update:
        complete: "更新しました"
      destory:
        confirm: "削除しますか？"
    button:                      ## 表示するボタン
      create: "新規作成"
      search: "検索"
      clear: "クリア"
      update: "更新"
      destory: "削除"
      cancel: "キャンセル"
  time:                          ## 表示する時間の形式
    formats:
      default: ! "%Y-%m-%d %H:%M:%S"
```

```yml:models/model_name_A/ja.yml
ja:
  activerecord:                 ## モデルの開始はactiverecord
    models:                     ## モデル
      model_name_A: "モデルA"
    attributes:                 ## 属性（カラム)
      model_name_A:
        id: "ID"
        name: "名称"
        created_at: "登録日時"
        updated_at: "更新日時"
```

```yml:views/view_name_A/ja.yml
ja:
  view_name_A:
    index:
      title: "画面A"
```

## 埋め込み
viewに埋め込みます。
呼び出し方は以下のような感じ

```ruby:ruby

## モデル
$ Model_name_A.model_name.human
=> モデルA

$ Model_name_A.human_attribute_name(:name)
=> 名称

## ロケール指定
$ t('dictionary.button.search')
=> 検索

$ t('view_name_A.index.title')
=> 画面A

## フォーマット
$ l(model_name_A.created_at, format: :default)
=> 2019-01-01
```

以上のようにすると、**config/application.rb**で設定した、言語に応じて**.yml**で設定した文言を表示します。

## 動的に切り替える

今までは、都度設定ファイルの言語を更新することで言語を変更してきましたが、動的に切り替えるためるための方法を説明します。

### controller/application_controller.rb

```rb:controller/application_controller.rb
class ApplicationController < ActionController::Base

    before_action :set_locale

    def set_locale
        I18n.locale = locale
    end

    def locale
        @locale ||= params[:locale] ||= I18n.default_locale
    end

    def default_url_options(options={})
        options.merge(locale: locale)
    end
end
```

上記設定を入れることで、localeを動的に変更します。

### config/routes.rb

```rb:config/routes.rb
Rails.application.routes.draw do
  scope '(:locale)', locale: /#{I18n.available_locales.map(&:to_s).join('|')}/ do
    # For details on the DSL available within this file, see 
    resources :resource_As
  end
end
```

動的多言語対応するアクションを**scope**で囲むと[domain]/ja/hogehoge/といった記述で表示する言語をパラメタとしてアクションに渡すことができます。渡されたパラメタは、**application_controller.rb**内で、判断され、対応言語の場合はその言語で画面描画します。

### 結果

![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75277/636802e7-0c6f-e89b-6fea-54ed39833f56.gif)

jaを指定すると日本語に、enを指定すると英語に表示を切り替えることができました！