🦓

[Rails]国際化(i18n)について7/20

2023/06/23に公開

はじめに

rails-i18ngemを使ってCRUDアプリを日本語化にしていきます。

環境:

Rails 6.1.7.3
ruby 3.0.0

tl;dr

  1. rails-i18nをインストールする
  2. ローカライズファルを読み込む
  3. ローカライズファイルを作成する
  4. モデルのローカライズを行う
  5. ビューのローカライズを行う
  6. ヘルパー変数を使う
  7. 訳文に変数を渡す

i18nとは

Railsのi18nは、異なる言語や地域のフォーマットに簡単に対応できるようにするための機能です。アプリケーションのテキストコンテンツをコードから分離し、翻訳やメンテナンスを容易にします。

国際化に向けの設定

利用するロケールに対応する翻訳データのymlファイルをconfig/initializers/localesの下に配置します。
デフォルトではen.ymlのファイルが用意されています、日本語の場合だったら、ja.ymlを作成しクラスや属性に対応する日本語の翻訳を記述していきます。

rails-i18nとは

rails-i18nのgemは、Railsアプリケーション内で国際化を行うための機能とリソースを提供するための特定のgemです。rails-i18n gemには、一般的なRailsのメッセージやラベルに対する事前定義された翻訳が含まれています。ActiveRecordモデル、日付や時刻のフォーマット、エラーメッセージなどの翻訳をカバーしています。これらの翻訳は、すぐに使用できる状態で提供されるため、手動で定義する手間や時間を節約できます。
https://github.com/svenfuchs/rails-i18n
https://railsguides.jp/i18n.html#日付・時刻フォーマットを追加する
https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml

インストール

Gemfile
gem 'rails-i18n'
bundle install

初期設定

config/application.rbでは次のように、デフォルトのロケールを変更し、訳文読み込みパスを設定していきます。

config/application.rb
# I18nライブラリに訳文の探索場所を指示する
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
# ロケールを:en以外に変更する
config.i18n.default_locale = :ja

# ロケールのリストを渡す
I18n.available_locales = [:en, :pt]

この設定の変更を反映させるには、サーバーを再起動させる必要があります。

ユーザごとに言語を切り替える

仮に、ユーザごとに利用言語を設定してDBに保存するとしますと、before_actionによって、リクエストごとにI18n.localeの値を切り替えることができます。
これにより。日本語を使うと設定されたユーザには日本語、英語を使うと設定されたユーザには英語で画面を表示することができます。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
    before_action :set_locale

    private

    def set_locale
        I18n.locale = Current.user&.locale || :ja # デフォルトがなければもしくはログインしていなければ日本語
    end
end

ローカライズファイルを作成する

アプリケーションが大きくなると単一ファイルでの管理が難しくなるので、モデルとビューで分けて作成していきます。

config/locales/views/ja.yml
config/locales/activerecord/ja.yml

config/localesディレクトリ以下のファイル構造は以下のリンクからをご参考ください。
https://railsguides.jp/i18n.html#ロケールファイルの編成

モデルのローカライズを行う

ドキュメントを参考しながらユーザモデルとarticleモデルの翻訳を入れていきます。
https://railsguides.jp/i18n.html#active-recordモデルで翻訳を行なう

config/locales/activerecord/ja.yml
ja:
  activerecord:
    models:
      user: 'ユーザー'
      article: '投稿'
    attributes:
      user:
        email: 'メールアドレス'
        password: 'パスワード'
        password_confirmation: 'パスワード確認'
        user_name: 'ユーザー'
      article:
        title: 'タイトル'
        body: '本文'

ビューのローカライズを行う

config/locales/views/ja.yml
# views/ja.yml
ja:
  defaults:
    login: 'ログイン'
    register: '登録'
    logout: 'ログアウト'
  sessions:
    new:
      title: 'ログイン'
      to_register_page: '登録ページへ'
      password_forget: 'パスワードをお忘れの方はこちら'
  articles:
    index:
      title: '投稿一覧'
    new:
      title: '投稿作成'
    edit:
      title: '投稿編集'
  signup:
    new:
      title: '新規登録'

tヘルパーを使ってビューにキーを渡す

tヘルパーは、Railsに組み込まれた国際化(i18n)機能を使用してビューで翻訳を行うための便利なメソッドです。
tヘルパーは、ビューファイル内で翻訳キーを指定し、対応する翻訳メッセージを取得するために使用されます。具体的には、I18n.tメソッドのショートカットとして機能します。
tヘルパーには他にも便利なオプションがあります。たとえば、変数を埋め込んだり、デフォルトの翻訳メッセージを指定したり、複数のオプションを渡したりすることができます。

ユーザ登録フォーム

Modelに紐付くform_withのlabelでは、Railsが定義を読み込んで適用しますので自動的に反映されます。タイトルだけ記述していきます。

config/locales/views/ja.yml
ja:
...
  signup:
    new:
      title: '新規登録'
app/views/signup/new.html.erb
# lazy lookup
<h1><%= t('.title') %></h1>
<h1><%= t '.title' %></h1>
# もしくは
<h1><%= t('signup.new.title') %></h1>

登録フォームを日本語に翻訳されたことを確認します。

エラーメッセージも同じく日本語に翻訳されました。

ログインフォーム

ログインフォームはセッションの概念を用いてモデルを渡していないform_withのlabelなので、i18nの記載を追加する必要があります。
Model.model_name.humanメソッドとModel.human_attribute_name(attribute)メソッドを使うことで、モデル名と属性名を透過的に参照できるようになります。

Model.model_name.humanメソッドとModel.human_attribute_name(attribute)メソッド

Model.model_name.humanメソッドとModel.human_attribute_name(attribute)メソッドは、Active Recordモデルで国際化(i18n)をサポートするためのメソッドです。これらのメソッドを使用すると、モデル名や属性名の翻訳を取得することができます。

  1. Model.model_name.humanメソッド:
    このメソッドは、Active Recordモデルのモデル名(クラス名)の翻訳を取得します。例えば、モデル名がUserの場合、User.model_name.humanはモデル名のデフォルトの翻訳を返します。

    User.model_name.human
    # => "ユーザー"
    

    上記の例では、"ユーザー"がモデル名Userの日本語の翻訳です。これにより、モデル名を表示する際に国際化をサポートすることができます。

  2. Model.human_attribute_name(attribute)メソッド:
    このメソッドは、Active Recordモデルの属性名の翻訳を取得します。属性名の翻訳は、属性名をキーとした翻訳ファイルで定義する必要があります。例えば、Userモデルのname属性の翻訳を取得する場合は次のようになります。

    User.human_attribute_name(:name)
    # => "名前"
    

    上記の例では、"名前"Userモデルのname属性の日本語の翻訳です。これにより、属性名を表示する際にも国際化をサポートすることができます。

これらのメソッドを使用すると、モデル名や属性名の翻訳を手動で定義する必要がなくなります。代わりに、翻訳ファイルに対応するキーと翻訳メッセージを定義し、これらのメソッドを使用して翻訳を取得します。また、ロケールを変更することで、異なる言語に対応する翻訳を表示することもできます。

config/locales/views/ja.yml
ja:
...
  sessions:
    new:
      title: 'ログイン'
      to_register_page: '登録ページへ'
      password_forget: 'パスワードをお忘れの方はこちら'
app/views/sessions/new.html.erb
<h1><%= t '.title' %></h1>
<%= form_with url:login_path do |form| %>
  <div class="form-group">
    <%= form.label :email, User.human_attribute_name(:email) %>
    <%= form.text_field :email, placeholder: "user@example.com", class: "form-control mb-3"  %>
  </div>
  <div class="form-group">
    <%= form.label :password, User.human_attribute_name(:password) %>
    <%= form.password_field :password, placeholder: "Please enter your password", class: "form-control mb-3"  %>
  </div>
  <%= form.submit t('defaults.login'), class:'btn btn-primary' %>
  <%= link_to t('.to_register_page'), signup_path, class: 'link-secondary d-inline-block ms-3' %>
  <%= link_to t('.password_forget'), password_reset_path, class: 'link-secondary d-inline-block ms-3' %>
<% end  %>

日本語の翻訳を反映されたことを確認します。

バリデーションのローカライズを行う

ログイン失敗の時のバリデーションを翻訳していきます。

config/locales/views/ja.yml
ja:
...
  sessions:
...
    create:
      success: 'ログインしました。'
      danger: 'ログインに失敗しました。もう一度試してください。'
    destroy:
      success: 'ログアウトしました。'
app/contollers/sessions_controller.rb
class SessionsController < ApplicationController
    def create
        user = User.find_by(email: params[:email])
        if user.present? && user.authenticate(params[:password])
            session[:user_id] = user.id
            flash[:success] = t('.success')
            redirect_to root_path
        else
            flash[:danger] = t('.danger')
            render :new
        end
    end
end

他のモデルとビューファイルも同じ手順でやっていきます。
- パスワード更新ページ
- パスワードリセットページ
- 投稿ページ
- 投稿更新ページ

訳文に変数を渡す

rails-I18ngemには「変数の式展開」機能が含まれており、訳文定義で変数を使えるようにし、翻訳メソッドでそれらに値を渡せるようにします。

ユーザーと投稿のバリデーションメッセージを変数に変えてもっと簡潔にまとめていきます。
モデルを増やしたら翻訳文も増やされるため、共通化して管理できると便利です。
アクション名に沿ってネーミングすると分かりやすいでしょう。

config/locales/views/ja.yml
ja:
  defaults:
    message:
     require_login: 'ログインしてください'
     created: '%{item}を作成しました'
     not_created: '%{item}の作成に失敗しました。'
     updated: '%{item}更新しました'
     not_updated: '%{item}の更新に失敗しました'
     destroyed: '%{item}を削除しました'
     delete_confirm: '%{item}を削除してもよろしいでしょうか?'
     not_authorized: '権限がありません'

コントローラーのバリデーションも変数に変えていきます。

app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
...
  def create
    @article = Current.user.articles.build(article_params)
    if @article.save
     # before
      flash[:success] = "記事が作成できました。"
      # after
      flash[:success] = t('defaults.message.created', item: Article.model_name.human)
      redirect_to @article
    else
     # before
      flash[:danger] = "記事の作成が失敗しました。もう一度試してください。"
      # after
      flash.now[:danger] = t('defaults.message.not_created', item: Article.model_name.human)
      render :new
    end
  end
...
end

タイムゾーン対応

投稿の作成時間を確認してみると日本時間ではないのでタイムゾーンを日本時間に変えます。

config/application.rb
config.time_zone = 'Tokyo'

ちょっと分かりにくいので、日付と時間のフォーマットも追記していきます。

config/locales/views/ja.yml
ja:
  time:
    formats:
        long: "%Y年%m月%d日(%a) %H時%M分%"

ビューファイルを編集して日付と時間を反映させます

app/views/articles/index.html.erb
<%= l(article.created_at, format: :long) %>

lヘルパーメソッド

lヘルパーメソッドは、Railsのビューファイル内で使われる国際化(I18n)に関連したメソッドです。主に、日付や時刻などのオブジェクトをローカライズされた形式で表示するために使用されます。

lヘルパーメソッドの一般的な構文は次のとおりです:

l(object, options = {})
  • objectはローカライズされた形式に変換したいオブジェクトです。例えば、TimeオブジェクトやDateオブジェクトなどです。
  • optionsはオプションのハッシュであり、lメソッドに渡される追加の設定を指定します。主なオプションは以下の通りです:
    • :format:ローカライズされた形式を指定します。例えば、:short:longなどのフォーマットキーを使用できます。
    • その他のオプション:他にもフォーマットオプションを指定することができます。これには、日時のフォーマットに関連する指示子(例:%d%m%Yなど)や、タイムゾーンの指定などが含まれます。

以下はいくつかの使用例です:

l(Time.now) # 現在の時刻をデフォルトのローカライズ形式で表示する
l(Date.today, format: :short) # 今日の日付をショートフォーマットで表示する
l(@event.start_time, format: :long) # @eventの開始時刻をロングフォーマットで表示する

lヘルパーメソッドは、ビューファイル内で日付や時刻のローカライズを容易にするための便利なメソッドです。設定されたフォーマットに基づいて、オブジェクトを適切なローカライズ形式で表示することができます。

$ bin/rails console
irb(main):001:0> Time.zone
=> #<ActiveSupport::TimeZone:0x00007fb4e7116568 @name="Tokyo", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Asia/Tokyo>>
irb(main):002:0> Time.zone.now
=> Fri, 23 Jun 2023 15:23:47.960015000 JST +09:00
irb(main):003:0> Time.zone.now.class
=> ActiveSupport::TimeWithZone
irb(main):004:0> Time.current
=> Fri, 23 Jun 2023 15:24:54.964547000 JST +09:00
irb(main):005:0> Time.zone.today
=> Fri, 23 Jun 2023
irb(main):006:0> Date.current
=> Fri, 23 Jun 2023

time.zone.nowTime.currentはどちらもRailsで現在の日時を表すメソッドですが、微妙な違いがあります。

time.zone.nowは、アプリケーションのタイムゾーンを考慮して現在の日時を返します。Railsアプリケーションはデフォルトでタイムゾーンが設定されており、time.zone.nowを使用することでそのタイムゾーンでの現在の日時が得られます。タイムゾーンの設定はconfig/application.rbconfig/time_zone.rbで行うことができます。

一方、Time.currentはRailsのTimeWithZoneクラスのインスタンスを返します。これは、time.zone.nowと同様にアプリケーションのタイムゾーンを考慮して現在の日時を表しますが、より直感的なAPIを提供します。Time.currentはTimeWithZoneオブジェクトを返すため、そのオブジェクトにはタイムゾーンに関連するメソッド(例:in_time_zoneto_sなど)が利用できます。

終わりに

いかがでしょうか。
i18nでアプリを日本語化することができました。
i18nの仕組みをよく理解していきましょう。

https://pikawaka.com/rails/i18n

Discussion