write ahead log

ロールフォワード用

RailsのI18n色々

Rails5触ってみようとチュートリアル読んでからRailsの入門メモが増えてる...

とりあえずサンプルプロジェクトを用意する

scaffoldでタスクリストのページを用意します.

$ rails new i18n_sample
$ rails g scaffold Task title:string done:boolean
$ rails db:migrate

バリデーションメッセージも見たいので適当にチェックをモデルへ追加します.

# app/models/task.rb
class Task < ApplicationRecord
  validates :title, presence: true
end

rails-i18nのgemを導入する

バリデーションメッセージなどを国際化したいのでgemを導入する.

Gemfileに以下を追記.

gem 'rails-i18n'

忘れぬよう.

$ bundle install

I18nのデフォルトロケールを日本語にする

config/application.rbにデフォルトロケールを設定してやります.

# config/application.rb
...
module I18nSample
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.1

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    
    # locale setting                   #<== 追加
    config.i18n.default_locale = :ja   #<== 追加
  end
...
end

この時点でサーバを再起動してやると基本的なメッセージは日本語化されています.

素晴らしいですね.

日本語の言語ファイルを作成する

モデルの属性名などは当然まだ日本語化されていないので, 言語ファイルを作成します.

config/locale/ja.ymlを作成します.

$ touch config/locale/ja.yml

基本と言語ファイルの構成

言語ファイルの構成はざっくりこんな感じのようです.

規約的に以下のようになっているだけなので, 共通で使うものを言語名(jaとか)の下の階層に差し込んで色々な箇所から使ったりも普通にできます.

[言語名]:
    activerecord:
        models:
            [モデル名]:
            [モデル名]:
            ...
        attributes:
            [モデル名]:
                [属性名]: [対応リテラル]
                [属性名]: [対応リテラル]
            [モデル名]:
                [属性名]: [対応リテラル]
                [属性名]: [対応リテラル]
            ...
    [コントローラ名]:
        [アクション名]:
            [リテラル]: [対応リテラル]
            [リテラル]: [対応リテラル]
            ...

プログラムから利用する際にはtというメソッドを使います.

このメソッドの引数は上記yamlの構成をパスの様に指定して使う.

t('activerecord.models.[モデル名]')

みたいな感じ.

モデルの言語を登録する

scaffoldの内容に合わせて以下にしてみました.

ja:
    activerecord:
        models:
            task: タスク
        attributes:
            task:
                title: タスク
                done: 完了

これだけで, form_with+labelを使って作られている部分も変わってたりしてて良い感じです.

バリデーションメッセージの属性名もちゃんと日本語化されます.

モデルの名前と属性名を得る

上述の規約通りに言語ファイルを作成しておくと, ActiveRecordのメソッドで簡単に国際化されたリテラルを取得する事ができます.

例えばタスクのモデル名は以下の様に得られます.

   Task.model_name.human

属性名も以下の様に簡単に. 引数が属性名ですね.

Task.human_attribute_name(:title)

最初, humanって?と思いましたが, 人間が読めるという意味なんでしょうね.

ネストしたモデルの場合

この例ではないけれど, accept_nested_attributes_forを利用してネストしたモデルを受け入れる場合には一工夫必要になる.

例えば今回利用している例でサブタスクという概念があった場合, Modelの定義では以下の様になると思う.

class Task < ApplicationRecord
  has_many :sub_tasks
end

class SubTask < ApplicationRecord
  belongs_to :task
end

こういう場合の言語ファイルは以下になります.

ja:
    activerecord:
        models:
            task: タスク
        attributes:
            task:
                title: タスク
                done: 完了
            task/posts:
                title: サブタスク名
                done: 完了

親モデル/子モデル(複数形)です.

ビューのリテラルを多言語化する

例として * activerecordリテラルでビューを表示 * ビュー用に追加したリテラルを利用 をやってみる.

# config/locale/ja.yml
ja:
    activerecord:
        models:
            task: タスク
        attributes:
            task:
                title: タスク
                done: 完了
    tasks:
        index:
            are_you_sure: 本当に削除しますか?

activerecordに設定したリテラルを利用する部分.

...
    <tr>
      <th><%= t('activerecord.attributes.task.title') %></th>
      <th><%= t('activerecord.attributes.task.done') %></th>
      <th colspan="3"></th>
...

追加したビュー用のリテラルを利用する部分.

     <td><%= link_to 'Destroy', task, method: :delete, data: { confirm: t('.are_you_sure') } %></td>

規約に従っておけば

t('tasks.index.are_you_sure')

のように冗長な形ではなく

t('.are_you_sure')

でよくなるので便利.

日付と時刻の国際化

国際化で困るのは単に言語だけではなく日時フォーマットもだけど, ここら辺のサポートが行き届いているのは良いなぁと.

yamlに以下のフォーマットで書いておくと日付と時間がtメソッドでフォーマットされる.

# config/locale/ja.yml
ja:
    date:
        formats:
            default: "%Y/%m/%d"
            long: "%Y年%m月%d日(%a)"
            short: "%m/%d"
    time:
        formats:
            default: "%Y/%m/%d %H:%M:%S"
            long: "%Y年%m月%d日(%a) %H時%M分%S秒 %z"
            short: "%y/%m/%d %H:%M"

使う時はこんな感じ. よしなにしてくれてよい.

<%= l(task.created_at) %>
<%= l(task.created_at, format: :short) %>

ざっくり見てきたけど, 式展開など多様な機能があるようなので, これ以上は一度Railsガイドを読んだ方が良いのだろうなぁ.