Ruby on Rails チュートリアル 拡張機能 ( 1 .エラーメッセージの i18n対応(日本語対応))

前置き

私は普段はインフラエンジニアなのですが、仕事でWebアプリケーションを触る機会があり、Webアプリケーションの基本を勉強しなおそうということで、ある講座でRuby On Rails チュートリアルを受講しました。Rubyを触る機会は仕事ではほぼ無いので、なかなか良い経験になりました。

railstutorial.jp

Railsチュートリアルは14の章からなる構成で、TwitterライクなWebサイトを1から作成するものです。ユーザーのログイン機能や投稿機能などを1から学べるため、Webアプリケーション機能の基礎を学びたい人にとっては良い教材だと思います。

拡張機能内容

14章まで終えると一通りのサイトはできるのですが、講座の最後に課題として機能を追加してみようというのがあり、私は以下の2つの機能を追加してみました。

(1) エラーメッセージの i18n対応(日本語対応)

(2) ユーザー検索・マイクロポスト検索機能追加

(1) のi18n (国際化・多言語化を意味する internationalization を短縮したもの)対応というのは、Rubyのデフォルトは英語なので、エラーメッセージなども全て英語です。それを極力日本語にするための対応です。

railsguides.jp

(2) のユーザー検索・マイクロポスト検索機能追加については、その名の通り、ユーザーとマイクロポストの投稿内容(ツイート内容)を検索できるようにした機能追加です。

動作イメージ

https://yusuke-sampleapp2.herokuapp.com/

testユーザーでログインして動作が確認できます。

  • メールアドレス:test@gmail.com
  • パスワード: foobar

実装

railsのバージョンは6.0.0です

$ rails version
Rails 6.0.0

こんな感じで日本語のエラー or 成功メッセージが出るようにします。また、フォームも日本語に置き換えます。所々コードや実装で抜けていろところがありますが、ご容赦ください。。。

小さくて見にくくてすみません。。

f:id:mukkun0824:20200628185301g:plain

Gemをインストール

i18n対応にはymlファイルで翻訳する語彙を作っていくのですが、一つ一つ単語や文をymlに書いても良いのですが、基本的なところは反映させてしまいたいので、Gemで rails-i18n というGemを入れます。

gem 'rails-i18n', '6.0.0'

bundle install 実施します。

$ bundle install

これでGemが入りました。なお、ymlファイルの階層はこんな感じにしました。これは拡張機能を全て実装した場合の最終的なものです。コントローラやviewごとに階層化します。

$ tree -A config/locales/
config/locales/
├── controllers
│   ├── account_activations_controller
│   │   └── ja.yml
│   ├── microposts
│   │   └── ja.yml
│   ├── password_resets_controller
│   │   └── ja.yml
│   ├── sessions_controller
│   │   └── ja.yml
│   └── users
│       └── ja.yml
├── en.yml
├── ja.yml
├── models
│   ├── micropost
│   │   └── ja.yml
│   └── user
│       └── ja.yml
└── views
    ├── sessions
    │   └── ja.yml
    ├── shared
    │   └── ja.yml
    └── users
        └── ja.yml

初期設定

ロケールや言語の設定をしておく。また、読み込みファイルの設定もしておきます。

module SampleApp2
  class Application < Rails::Application
【中略】
    # i18n対応
    # デフォルトのlocaleを日本語(:ja)にする
    config.i18n.default_locale = :ja

    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local


    # 複数のロケールが読み込まれるようにする
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  end
end

テスト

まずテストを書きます。基本的にflashやエラーメッセージの部分の表示を変更します。エラーメッセージは意外と色々なところに出てくるので、インテグレーションテストの追記が多くなります。

  test "micropost interface" do
    log_in_as(@user)

【中略】

    # 無効な送信
    assert_no_difference 'Micropost.count' do
      post microposts_path, params: { micropost: { content: "" } }
    end
    assert_select 'div#error_explanation'
    assert_select 'div.alert-danger','1個のエラーがあります。'

    assert_select 'a[href=?]', '/?page=2'  # 正しいページネーションリンク

    # 有効な送信
    content = "This micropost really ties the room together"
    assert_difference 'Micropost.count', 1 do
      post microposts_path, params: { micropost: { content: content } }
      assert_equal  '投稿しました!',flash[:success]
    end

【中略】

    # 投稿を削除する

    assert_select 'a', text: 'delete'
    first_micropost = @user.microposts.paginate(page: 1).first
    assert_difference 'Micropost.count', -1 do
      delete micropost_path(first_micropost)
      assert_equal '投稿が削除されました。',flash[:success]
    end

【中略】

  test "password resets" do
    get new_password_reset_path

【中略】

    # メールアドレスが無効
    post password_resets_path, params: { password_reset: { email: "" } }
    assert_not flash.empty?
    assert_select 'div.alert-danger','有効なメールアドレスが見つかりません。'
    assert_template 'password_resets/new'

    # メールアドレスが有効
    assert_not flash.empty?
    assert_equal 'パスワード再設定用のメールを送信しました。',flash[:info]

【中略】

    assert_equal 'パスワードが更新されました。',flash[:success]
    assert_redirected_to user
  end
end
  test "successful edit with friendly forwarding" do

【中略】

    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_equal  'プロフィールが更新されました。',flash[:success]
    assert_redirected_to @user
【中略】
  test "index as admin including pagination and delete links" do
    log_in_as(@admin)

【中略】

    assert_difference 'User.count', -1 do
      delete user_path(@non_admin)
      assert_equal 'ユーザーが削除されました。',flash[:success]
    end
  #誤っているデータ ログインできないはず
  test "login with valid email/invalid password" do

【中略】

    assert_not flash.empty?
    assert_select 'div.alert-danger','メールアドレスとパスワードの組み合わせが間違っています。'
    get root_path
    assert flash.empty?
  end
  test "invalid signup information" do
    get signup_path

【中略】

    assert_select 'div.alert-danger','4個のエラーがあります。'
    assert_select 'div.field_with_errors'
  end

【中略】

  test "valid signup information with account activation" do
    get signup_path
    assert_equal 1, ActionMailer::Base.deliveries.size

    assert_equal 'アカウント有効化に関するメールを確認してください。',flash[:info]

    user = assigns(:user)

【中略】

コントローラーの日本語化

コントローラーに翻訳場所指定するためには、Rails#tヘルパー を使います 。この #tヘルパー に、各訳文の意味を適切に表すキーを与えます(Rails ガイド参照)。

こんな感じでエラーメッセージが日本語化されます。後ほど記載しますが、modelも日本語化しておきます。

基本的にコントローラー内でflashを使うところは全て書き換えます。

【中略】


        flash[:danger] = t('.please log in')
        redirect_to login_url
      end
    end
 【中略】

     flash[:success] = t('.micropost created')

 【中略】

     flash[:success] = t('.micropost deleted')

 【中略】
 【中略】

      flash[:info] = t('.sent email')

 【中略】

      flash[:success] = t('.password reset success')

 【中略】
 【中略】

      flash[:info] = t('.check email')

 【中略】

      flash[:success] = t('.profile updated')
 
 【中略】

    flash[:success] = t('.user deleted')

 【中略】

これらに対して以下のようにymlファイルを作成して対応させる

usersコントローラに対して config/locales/controllers/users/ja.yml

ja:
  users:
    create:
      check email:          'アカウント有効化に関するメールを確認してください。'
    edit:
      please log in:          'ログインしてください。'
    update:
      profile updated:       'プロフィールが更新されました。'
      please log in:          'ログインしてください。'
    destroy:
      user deleted:          'ユーザーが削除されました。'

password_resets_コントローラに対して

ja:
  password_resets:
    create:
      sent email:        'パスワード再設定用のメールを送信しました。'
      cannot sent email: '有効なメールアドレスが見つかりません。'
    update:
      password reset success:   'パスワードが更新されました。'

micropostsコントローラに対して

ja:
  microposts:
    create:
      micropost created:          '投稿しました!'
    destroy:
      micropost deleted:          '投稿が削除されました。'

account_activationsコントローラに対して

ja:
  account_activations:
    edit:
      activated:                     'アカウントが有効化されました'
      invalid activation link:       'アカウント有効化用のリンクが誤っています'

sessionsコントローラに対して

ja:
  sessions:
    create:
      not activated:        'アカウントが有効化されていません。 '
      check email:          'アカウント有効化に関するメールを確認してください。'
      invalid combination:  'メールアドレスとパスワードの組み合わせが間違っています。'

ビューの日本語化

ビューもコントローラーと同じようにRails#tヘルパー を使って日本語化します。また、フォームの部分も書き換えておきます。

 【中略】


      <%= f.label :email , t('.email')%>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password , t('.password')%>
      <%= link_to t('.forgot password'), new_password_reset_path %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :remember_me, class: "checkbox inline" do %>
        <%= f.check_box :remember_me %>
        <span> <%= t('.remember me') %> </span>
      <% end %>

      <%= f.submit t('.log in'), class: "btn btn-primary" %>
    <% end %>

 【中略】
<% if object.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
        <%= t('.errors count', errors_count: object.errors.count) %>
    </div>

 【中略】

f.label :password_confirmation, "Confirmation", "Confirmation" を削除しておく。

      <%= f.label :password_confirmation %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit t('.Create my account'), class: "btn btn-primary" %>

モデルの日本語化

ついでにモデルも日本語化してフォームを日本語にしておきます。

ja:
  activerecord:
    models:
      # view側: User.model_name.human => "ユーザ" / t("activerecord.models.user")と同じ
      micropost:          '投稿'
    attributes:
        micropost:
          content:        '投稿内容'
          image:          '画像'
          invalid format: 'jpeg,gif,png以外のフォーマットは使用できません'
          invalid size:   '1MB以上の画像は投稿できません'

  errors:
    messages:
      less_than_xmb:      'は1MBより小さいファイルをアップロードしてください'
ja:
  activerecord:
    models:
      user:                       'ユーザー'
    attributes:
        user:
          name:                   '名前'
          email:                  'メールアドレス'
          password:               'パスワード'
          password_confirmation:  'パスワード(確認用)'

以上で実装が完了です。

お役に立てれば幸いです。