読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Railsのi18nの基本的な使い方まとめ

Rails初級 i18n まとめ

f:id:nipe880324:20150226164508j:plain:w480
Photo by Michael Kappel | Flickr - Photo Sharing!

Railsでの国際化(i18n: Internationalization)の方法をまとめています。
デフォルトのlocale設定、基本的な翻訳の流れ、翻訳ファイルの作成、localeを動的に変更する方法を説明します。

動作確認

目次

4. URLによるlocaleの設定方法

4.1. サブドメイン名からlocaleを設定
4.2. URLのサブディレクトリからlocaleを取得
4.3. URLパラメータからlocaleを取得




1. デフォルトのlocaleの設定と翻訳ファイルの読み込み

デフォルトのlocaleは暗黙的に:enです。他のlocaleを設定したい場合は、明示的に指定する必要があります。
また、翻訳ファイルはconfig/locales/*.rb,ymlに配置し、自動的にロードされるようになっています。


デフォルトのlocaleの設定や翻訳ファイルのディレクトリの追加をしたい場合、application.rbを修正します。

# config/application.rb

# デフォルトのlocaleを日本語(:ja)にする
config.i18n.default_locale = :ja

# 翻訳ファイルのディレクトを追加する場合は、次の行のコメントを外しパスを追加してください。
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]


設定できるlocaleは次のメソッドで確認できます。

I18n.available_locales
#=> [:en, :"de-AT", :"de-CH", :de, :"en-au-ocker", :"en-AU", :"en-BORK", :"en-CA", :"en-GB", ...]


I18n.load_pathメソッドで現在ロードしている翻訳ファイルの一覧を表示することができます。

I18n.load_path
#=>
#[
#  "/.../gems/2.2.0/gems/activesupport-4.2.0/lib/active_support/locale/en.yml",
#  "/.../gems/activemodel-4.2.0/lib/active_model/locale/en.yml",
#  "/.../gems/activerecord-4.2.0/lib/active_record/locale/en.yml",
#  "/.../gems/actionview-4.2.0/lib/action_view/locale/en.yml",
#  "/.../gems/faker-1.4.3/lib/locales/de-AT.yml",
#  "/.../gems/faker-1.4.3/lib/locales/de-CH.yml",
#  "/.../gems/faker-1.4.3/lib/locales/de.yml",
#  "/.../gems/faker-1.4.3/lib/locales/en-au-ocker.yml",
#  "/.../gems/faker-1.4.3/lib/locales/en-AU.yml",
#  "/.../gems/faker-1.4.3/lib/locales/en-BORK.yml",
#  ...
#]


2. i18nの翻訳の基本

i18nは、現在のlocaleに応じて、読み込んだ翻訳ファイルから適切な翻訳文を取得し、表示します。

翻訳ファイルは次のようになっています。

# config/locales/ja.yml
ja:
  hello: "こんにちは"
  time:
    formats:
      default: ! '%Y/%m/%d %H:%M:%S'


# config/locales/es.yml
es:
  hello: "hola mundo"
  time:
    formats:
      default: ! '%A, %d de %B de %Y %H:%M:%S %z'


変換には「翻訳(translate)を行うtメソッド」と「DateやTimeをlocalizeするlメソッド」を使います。

$ rails c

# localeを日本語(:ja)に設定し、翻訳した文字を表示する
I18n.locale = :ja
I18n.t :hello   # => "こんにちは"
I18n.l Time.now # => "2015/02/24 14:53:03"


# localeをスペイン語(:es)に設定し、翻訳した文字を表示する
I18n.locale = :es
I18n.t :hello   # => "hola mundo"
I18n.l Time.now # => "martes, 24 de febrero de 2015 14:57:17 +0900"


# localeを指定して、設定されていない文字を表示できる
I18n.locale  # => :es
I18n.t :hello, locale: :ja # => "こんにちは"


3. 翻訳ファイルの作成

3.1. 翻訳ファイルのディレクトリ構成

1つの翻訳に管理するのが難しくなってしまうので、プロジェクト規模に応じて、次のようにconfig/locales/配下のディレクトリ構成を行うとよいです。

config/locales/
├─ defaults
│  ├─ ja.rb
│  └─ en.rb
├── models
│   └─ product
│      ├─ ja.rb
│      └─ en.rb
└─ views
   ├─ defaults
   │  ├─ ja.rb
   │  └─ en.rb
   ├─ products
   │  ├─ ja.rb
   │  └─ en.rb
   └─ navigation
      ├─ ja.rb
      └─ en.rb

3.2. 各言語の基本的な翻訳テンプレートファイル

各言語の基本的な翻訳ファイルは「https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale」から取得できます。
日付、時刻、ActiveRecordのバリデーションエラー文言、数字などの基本的な翻訳が記載されていまので、上記でいう、config/locales/defaults/配下に配置するようなファイルを取得できます。


3.3. 遅延参照(lazy lookup)

Railsのビューファイルのディレクトリ構造と翻訳ファイルの階層構造を合わせることで、ビューに長ったらしくを書かなくてもよくなります。

ja:
  projects: # app/views/projectsを表す
    index:  # app/views/projects/index.html.erb
      title: "プロジェクト一覧"
    deadlines:  # app/views/_deadlines.html.erb
      due_date: "期限"
<!-- app/views/projects/index.html.erb -->
<!-- t 'projects.index.title' を t '.title' と短くかける -->
<%= t '.title' %>  # => "プロジェクト一覧"

<!-- app/views/projects/_deadlines.html.erb -->
<!-- t 'projects.deadlines.due_date' を t '.due_date' と短くかける -->
<%= t '.due_date' %>  # => "期限"


3.4. 式展開

翻訳文内で式展開を行うことができます。

翻訳ファイル内で%{}で変数名を定義します。

ja:
  hello: 'こんにちは %{name}'

そして、tメソッドでKey-Valueで変数に値を設定します。

<%= t :hello, name: '太郎' %> # => "こんにちは 太郎"

3.5. 翻訳ファイル内でのHTMLの使用

翻訳ファイル内でHTMLを使用するには、キーに「_htmlで終わる」か「htmlというキー名」の場合、HTMLはエスケープされません。そのため、記載したHTMLが解釈されて表示されます。

翻訳ファイル内で_htmlで終わるキー名にします。

ja:
  hello_html: '<b>こんにちは %{name}</b>'

そして、tメソッドで表示します。

<%= t :hello_html, name: '太郎' %> #=> 太字になった「こんにち 太郎」が表示される


次のようにhello_htmlの値にHTMLセーフでない文字を入力するとそれが表示されてしまうので注意してください。

ja:
  hello_html: 'こんにちは %{name} <script>alert("alertが表示される");</script>'


逆に、式展開の%{}のHTMLはエスケープされます。

<%= t :hello_html, name: '<script>alert("太郎");</script>' %> #=> "こんにちは <script>alert("太郎");</script>"と表示される

3.6. ActiveRecord/ActiveModelの翻訳

activerecord/activemodel.modelsactiverecord/activemodel.attributesを使うことでActiveRecordの翻訳を行えます。

次の翻訳ファイルは、Productモデルの翻訳例です。

ja:
  activerecord: &activerecord
    models:
      product: "商品"
      search_form: "検索フォーム"
    attributes:
      product:
        name:  "商品名"
        price: "値段"
        released_on: "発売日"
      search_form:
        q: "検索フィールド"

  # activemodelの翻訳もactiverecordに記載している
  activemodel:
    <<: activerecord

YAMLを開いて適切な設定になっているか確認できます。

YAML.load_file(Rails.root.join("config/locales/ja.yml"))


モジュールなどで階層構造の翻訳ファイルを書くときには、`/`で区切って階層構造を表します。
次のようなモジュールが入れ子の構造の場合、

module Forms
  class ProductForm
    attr_accessor :name
  end
end

attributesの下位階層でモジュールを/で区切って表す(`forms/product_form`)

ja:
  activemodel:
    attributes:
      forms/product_form:
        name:       '商品名'

次のようにして、ビューファイル内で利用します。

<!-- tメソッドでアクセスできます -->
<%= t 'activerecord.models.product' %>          #=> "商品"
<%= t 'activerecord.attributes.product.name' %> #=> "商品名"

<!-- 次のようにしてもアクセスできます -->
<%= Product.model_name.human %>                 #=> "商品"
<%= Product.human_attribute_name('name') %>     #=> "商品名"
<%= Forms::ProductForm.human_attribute_name('name') %>     #=> "商品名"


4. URLによるlocaleの設定方法

他言語のサイト(複数のlocaleのサイト)を作成するには、「URLからlocaleを取得する方法」が推奨です。
「URLからlocaleを取得する方法」は次のとおりです。
f:id:nipe880324:20150226164143j:plain:w560

詳細は、多地域、多言語のサイト - Search Console ヘルプを参照してください。
他地域、他言語のサイトを作成するときに、SEO的にどうすれば良いかといったヒントが書かれています。



4.1. サブドメイン名からlocaleを設定

次のようにサブドメイン名からlocaleを設定するようにしてみます。
Googleの他地域、他言語のサイトでは「gTLD を使用したサブドメイン」に対応します。

example.com      # => デフォルトのlocale
ja.example.com   # => 日本のlocale
en.example.com   # => 英語のlocale


まず、applicaiton_controller.rbにlocaleを設定するメソッドを追記します。

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

  before_action :set_locale

  def set_locale
    I18n.locale = extract_locale_from_tld || I18n.default_locale
  end

  # サブドメインからlocaleを取得する
  # 有効なlocaleが見つからない場合は、nilを返す
  def extract_locale_from_tld
    parsed_locale = request.subdomains.first
    I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
  end
end


そして、application.html.erbなどのビューファイルで次のように言語を変更するリンクを追加することで、言語を変更できます。

<%= link_to_if request.subdomains.first != 'ja', '日本語',   "http://ja.example.com:3000#{request.path}"%> |
<%= link_to_if request.subdomains.first != 'en', 'English', "http://en.example.com:3000#{request.path}" %>

URL部分は簡易的に直書きをしていますが、設定ファイルからの値を取得することをお勧めします。


http://ja.example.com:3000/...にアクセスすると、Railsのlocaleは、ja(日本語)になるので、日本語で画面に表示されています。
f:id:nipe880324:20150226163943j:plain:w320

http://en.example.com:3000/...にアクセスすると、Railsのlocaleは、en(英語)になるので、英語で画面に表示されています。
f:id:nipe880324:20150226164013j:plain:w320




4.2. URLのサブディレクトリからlocaleを取得

Googleの他地域、他言語のサイトでは「gTLD を使用したサブディレクトリ」に対応します。


次のようにサブディレクトリ名からlocaleを設定するようにしてみます。

example.com/     # => デフォルトのlocale
example.com/ja/  # => 日本のlocale
example.com/en/  # => 英語のlocale


まず、ルートにscoope:localeを追加します。かっこがあることで、存在しない場合は、デフォルトのlocaleが使われます。

# config/routes.rb
scope "(:locale)" do
  resources :products
end


URLのlocaleは、parmas[:locale]で取得できるので次のようにします。
また、default_url_optionsをオーバーライドすることで、すべてのリンクに現在のlocaleを設定することができます。

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

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

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


そして、application.html.erbなどのビューファイルで次のように言語を変更するリンクを追加することで、言語を変更できます。

<%= link_to_if params[:locale].present?, '日本語',  url_for(controller: controller.controller_name, action: controller.action_name, locale: '') %> |
<%= link_to_if params[:locale] != 'en',  'English',  url_for(controller: controller.controller_name, action: controller.action_name, locale: 'en') %>


URLにlocaleがない場合は、Railsのlocaleは、デフォルトのlocaleの日本語なので、日本語で表示されています。
f:id:nipe880324:20150226164033j:plain:w320

「English」リンクを押すことで、URLにenが追加され、Railsのlocaleが英語になるので、英語表示になります。
f:id:nipe880324:20150226164041j:plain:w320




4.3. URLパラメータからlocaleを取得

次のようにURLパタメータにlocaleを設定することでlocaleを設定するようにします。

example.com           #=> デフォルトのlocale
example.com?locale=ja #=> 日本語(ja)のlocale
example.com?locale=es #=> スペイン語(es)のlocale

Googleの他地域、他言語のサイトによると、非推奨の方法です。


まず、applicaiton_controller.rbにlocaleを設定するメソッドを追記します。

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

  before_action :set_locale

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

  # link_toなどのすべてのURLにlocaleパラメータを設定するようにする
  def default_url_options(options = {})
    { locale: I18n.locale }.merge options
  end
end


そして、application.html.erbなどのビューファイルで次のように言語を変更するリンクを追加することで、言語を変更できます。

<%= link_to_if params[:locale].present?, '日本語', "#{request.path}" %> |
<%= link_to_if params[:locale] != 'en', 'English', "#{request.path}?locale=en" %>


URLにlocaleがない場合は、Railsのlocaleは、デフォルトのlocaleの日本語なので、日本語で表示されています。
f:id:nipe880324:20150226164100j:plain:w320

「English」リンクを押すことで、locale=enがURLに追加され、Railsのlocaleが英語になるので、英語表示になります。
f:id:nipe880324:20150226164106j:plain:w320