【Ruby on Rails】Validationのエラーをカスタマイズ＆I18nを使って多言語化対応する！

## 今回成し遂げたいこと
- Validationエラーをカスタマイズしたい！
- ヘッダーの``Accept-Language``を見て、エラーを英語と日本語で表示できるようにしたい！

## 前提

今回は、下記のようなAddressモデルがあると仮定して話をしていきます。addressをcreateするときに、カスタマイズしたエラー、さらに``Accept-Language``が``ja``（または未指定）なら日本語エラー、``en``なら英語エラーを表示させることが目標です。

```ruby:address.rb
class Address < ApplicationRecord
  validates :zip_code, format: { with: /\A[0-9]{3}-[0-9]{4}\z/ }
  validates :residence_name, length: maximum: 250
end
```

コントローラーのcreateメソッドはこんな感じ

```ruby:addresses_controller.rb
class AddressesController < ApiApplicationController
  def create
    address = Address.new(address_params)
    address.save! #例外を発生させたいので！をつける

    hash = { message: I18n.t("addresses.address_registration_success"), data: address }
    render status: 200, json: { data: hash }
  end
end
```

また、今回はAPIを作っていると仮定し、返り値は全てJSONとします。

現時点でcreateメソッドを動かすと、``zip codeがXXX-XXXXの形でないよ``、``residence nameは10文字以下でないといけないよ``、が英語で返ってきます。これを日本語化、さらにエラーメッセージをカスタマイズしていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/6f1a2258-8f7e-9f5c-aa20-1176bcaea15e.png)



## まずはモデルに直接書き込んでみる方法

日本語のエラー文だけで問題ないなら、validationにmessageというパラメターと表示したいメッセージを書いてあげるだけで大丈夫です。

```ruby:address.rb
class Address < ApplicationRecord

  validates :user_id, uniqueness: true
  validates :zip_code, format: {with: /\A[0-9]{3}-[0-9]{4}\z/, message: "はXXX-XXXXのように記入してください (記入例：123-4567)" }
  validates :residence_name, length: { maximum: 10, message: "は10文字以内で入力してください" }
end
```
### 結果：カラム名だけ英語になっちゃう
でもカラム名が英語になっていますね。。。。ここも日本語にしてあげたい。その場合は``I18n``対応が必要です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/0f025c1c-8aa4-3cbd-3520-63e1db67e26c.png)

## I18nとは？
I18nってなんぞやという方のために、[Railsガイド](https://railsguides.jp/i18n.html)の説明文です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/2b13aebb-496b-ac00-06b0-d92dda6921d6.png)

なんかごちゃごちゃしてよくわからん・・・・（読む気にもならない）。
とりあえず、アプリを日本語にも英語にも、その他ドイツ語とかフランス語とか、多言語に対応できるようにしよう！という機能です。

## I18nを入れてみる

I18n用のGemをGemfileに追記して、``bundle install``します。

```ruby:Gemfile
gem 'rails-i18n', '~> 5.1'
```

``I18n``のデフォルト設定を``application.rb``の``Application Class``内に追記します。

```ruby:application.rb
config.i18n.default_locale = :ja #デフォルト言語は日本語にするよ設定
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s] #言語変換表はここにあるよ設定
config.i18n.available_locales = %w(en ja) #変換表は日本語と英語があるよ
```

最後に、``/config/locales``の直下に、``ja.yml``という名前のファイルを作成します。
このファイル内で、変換してほしい言葉を書き書きしていきます。

変換例はこんな感じ。

```yml:ja.yml
ja:
  activerecord:
    models:
      モデル名: "日本語モデル名"
    attributes:
      モデル名: 
        カラム名: "日本語カラム名"
```

なので今回は、``address``の``zip_code``と``residence_name``を日本語に変換したいので、下記のように入力していきます。
このymlファイル、インデントなどが間違っているとうまく動いてくれないので、気をつけてくださいね。

```yml:ja.yml
ja:
  activerecord:
    models:
      address: "住所"
    attributes:
      address: 
        zip_code: "郵便番号"
        residence_name: "建物名"
```

### 結果・・・日本語になった！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/bc5c3754-f53b-94f8-41b9-b7f7f8e96576.png)

### でも英語でも日本語のエラーになっちゃう
今回は日本語のエラー文をmodelのvalidationの中に直接書き込んでいるので、``Accept-Language``を``en``にしても、日本語エラー文になっちゃう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/04b852cc-50ba-b79f-8394-fc386b7a15f4.png)

ということで、validationのエラーメッセージも``I18n``で日本語と英語を変換してくれるように修正していきます。まずは日本語のエラーメッセージをja.ymlファイルの中で用意します。

下記のようにすることで``addresses.zip_code_validation``が呼ばれたら、``"はXXX-XXXXのように記入してください (記入例：123-4567)"``と表示する、という風に表示したいメッセージをカスタマイズできます。

```yml:ja.yml
ja:
  activerecord:
    models:
      モデル名: "日本語モデル名"
    attributes:
      モデル名: 
        カラム名: "日本語カラム名"
  addresses:
    zip_code_validation: "はXXX-XXXXのように記入してください (記入例：123-4567)"
    residence_name_validation: "は10文字以内で入力してください"
```

英語用のエラーメッセージも用意していきましょう。``/config/locales``の直下に、``en.yml``ファイルがあると思うので（なければ作成してね）、下記のように追記します。インデントには注意してくださいね。

```yml:en.yml
en:
  activerecord:
    models:
      address: "address"
    attributes:
      address: 
        zip_code: "zip code "
        residence_name: "residence name "
  addresses:
    zip_code_validation: "must be in XXX-XXXX format. (ex：123-4567)"
    residence_name_validation: "must be less than 10 length"
```

最後に、モデルを下記のように修正します。

```ruby:address.rb
class Address < ApplicationRecord
  validates :zip_code, format: {with: /\A[0-9]{3}-[0-9]{4}\z/, message: I18n.t(:'addresses.zip_code_validation')}
  validates :residence_name, length: { maximum: 10, message: I18n.t(:'addresses.residence_name_validation') }
end
```

これで、下記の設定ができました。``Accept-Language``を見て英語日本語どちらかのカスタマイズされたメッセージを表示してくれます。

- ``zip_code``の``format``のバリデーションが通らなかった場合は、ymlファイルの``addresses.zip_code_validation``のメッセージを表示
- ``residence_name``の``length``のバリデーションが通らなかった場合は、ymlファイルの``addresses.residence_name_validation``のメッセージを表示

### 結果
#### ``Accept-Language``が``ja``の場合
全て日本語になった！！デフォルトは``ja``で設定しているので``Accept-Language``の指定がなければ日本語文が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/54db6241-e8f0-0e3f-8215-46edc4ca6fd0.png)

#### ``Accept-Language``が``en``の場合
全て英語になったー！！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/77093a7e-c594-2142-dcc6-6679d3529dec.png)

##まとめ
ということで、Railsでの多言語化対応と、それに伴うI18nの基本的な使い方の説明でした。
最初は「I18n！？！？名前からして難しそう・・・」って感じですが、慣れてしまえば簡単。