注意:RubyのI18nフレームワークは、Railsアプリケーションの国際化/ローカリゼーションに必要なすべての手段を提供します。さらに機能や機能を追加するために使用できるさまざまなジェムも使用できます。詳細については、rails-i18n gemを参照してください。
1 Ruby on RailsでのI18nの動作方法
国際化は複雑な問題です。自然言語はさまざまな方法で異なる(複数形のルールなど)ため、すべての問題を一度に解決するためのツールを提供することは難しいです。そのため、Rails I18n APIでは次に焦点を当てています。
- ボックスから英語や類似の言語のサポートを提供する
- 他の言語に対してすべてをカスタマイズおよび拡張することを容易にする
この解決策の一環として、Railsフレームワークのすべての静的文字列(Active Recordのバリデーションメッセージ、時刻と日付の形式など)は国際化されています。Railsアプリケーションのローカライズは、これらの文字列の翻訳された値を所望の言語で定義することを意味します。
アプリケーション内のコンテンツ(ブログ記事の翻訳など)をローカライズ、保存、更新するには、モデルのコンテンツの翻訳セクションを参照してください。
1.1 ライブラリの全体的なアーキテクチャ
したがって、Ruby I18nジェムは2つのパートに分かれています。
- I18nフレームワークのパブリックAPI - ライブラリの動作を定義するパブリックメソッドを持つRubyモジュール
- これらのメソッドを実装するデフォルトのバックエンド(意図的にSimpleバックエンドと呼ばれています)
ユーザーとしては、常にI18nモジュールのパブリックメソッドにアクセスする必要がありますが、バックエンドの機能についても知っておくと便利です。
注意:出荷されたSimpleバックエンドをより強力なバックエンドと交換することができます。これにより、翻訳データをリレーショナルデータベース、GetText辞書などに保存することができます。以下の異なるバックエンドの使用セクションを参照してください。
1.2 パブリックI18n API
I18n APIの最も重要なメソッドは次のとおりです。
translate # テキストの翻訳を検索します
localize # 日付と時刻オブジェクトをローカルの形式にローカライズします
これらには#tと#lのエイリアスがありますので、次のように使用できます。
I18n.t 'store.title'
I18n.l Time.now
次の属性に対する属性リーダーと属性ライターもあります。
load_path # カスタムの翻訳ファイルを指定します
locale # 現在のロケールを取得および設定します
default_locale # デフォルトのロケールを取得および設定します
available_locales # アプリケーションで使用できる許可されたロケール
enforce_available_locales # ロケールの許可を強制する(trueまたはfalse)
exception_handler # 別の例外ハンドラーを使用する
backend # 別のバックエンドを使用する
では、次の章でシンプルなRailsアプリケーションを国際化してみましょう!
2 国際化のためにRailsアプリケーションをセットアップする
RailsアプリケーションでI18nサポートを利用するためには、いくつかの手順が必要です。
2.1 I18nモジュールの設定
「設定より規約」という哲学に従い、Rails I18nは合理的なデフォルトの翻訳文字列を提供します。異なる翻訳文字列が必要な場合は、上書きすることができます。
Railsはconfig/locales
ディレクトリ内のすべての.rb
および.yml
ファイルを翻訳のロードパスに自動的に追加します。
このディレクトリ内のデフォルトのen.yml
ロケールには、サンプルの翻訳文字列のペアが含まれています。
en:
hello: "Hello world"
これは、:en
ロケールでは、キー「hello」が「Hello world」という文字列にマップされることを意味します。Rails内のすべての文字列は、この方法で国際化されます。たとえば、activemodel/lib/active_model/locale/en.yml
ファイルのActive Modelのバリデーションメッセージや、activesupport/lib/active_support/locale/en.yml
ファイルの日付と時刻の書式などを参照してください。デフォルト(シンプル)バックエンドでは、翻訳を保存するためにYAMLまたは標準のRubyハッシュを使用できます。
I18nライブラリは、翻訳の検索にデフォルトのロケールとして英語を使用します。つまり、異なるロケールが設定されていない場合、翻訳の検索には:en
が使用されます。
注意:i18nライブラリは、ロケールキーに対して実用的なアプローチを取ります(一部の議論の後)。これには、通常「言語」と「地域設定」または「方言」を区別するために使用される、地域部分(:"en-US"
や:"en-GB"
など)ではなく、ロケール(「言語」)部分のみを含めるというものです。多くの国際アプリケーションでは、チェコ語、タイ語、スペイン語などのように、ロケールの「言語」要素のみを使用します。ただし、重要な地域ごとの違いもあります。たとえば、:"en-US"
ロケールでは通貨記号として$が使用されますが、:"en-GB"
では£が使用されます。これによって、このように地域やその他の設定を分離することはできますが、:"en-GB"
の辞書には完全な「英語 - イギリス」ロケールを提供する必要があります。
翻訳のロードパス(I18n.load_path
)は、自動的にロードされるファイルへのパスの配列です。このパスを設定することで、翻訳ディレクトリの構造やファイルの命名スキームをカスタマイズすることができます。
注意:このバックエンドは、翻訳が初めて参照されるときに遅延ロードされます。このバックエンドは、翻訳が既に公開されている後でも他のものと交換することができます。
デフォルトのロケールを変更し、翻訳のロードパスをconfig/application.rb
で次のように設定できます。
config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
config.i18n.default_locale = :de
ロードパスは、翻訳が参照される前に指定する必要があります。config/application.rb
ではなく、初期化子からデフォルトのロケールを変更する場合は、次のようにします。
# config/initializers/locale.rb
# I18nライブラリが翻訳ファイルを検索する場所
I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')]
# アプリケーションで使用可能な許可されたロケール
I18n.available_locales = [:en, :pt]
# デフォルトのロケールを:en以外の何かに設定する
I18n.default_locale = :pt
外部のgemからの翻訳を上書きするために、アプリケーションの設定されたI18nではなく、直接I18n.load_path
に追加することに注意してください。
2.2 リクエスト間でのロケールの管理
ローカライズされたアプリケーションでは、複数のロケールをサポートする必要があります。これを実現するために、ロケールは各リクエストの開始時に設定される必要があります。これにより、そのリクエストの寿命中にすべての文字列が所望のロケールで翻訳されます。
I18n.locale=
またはI18n.with_locale
が使用されない限り、デフォルトのロケールがすべての翻訳に使用されます。
I18n.locale
は、同じスレッド/プロセスで提供される後続のリクエストに漏れる可能性があります。たとえば、POSTリクエストでI18n.locale = :es
を実行すると、ロケールが設定されていないコントローラへの後続のリクエストに影響を与えますが、それは特定のスレッド/プロセスのみです。そのため、I18n.locale =
の代わりに、このリークの問題を持たないI18n.with_locale
を使用できます。
ロケールは、ApplicationController
のaround_action
で設定できます。
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
end
この例では、URLクエリパラメータを使用してロケールを設定しています(たとえば、http://example.com/books?locale=pt
)。このアプローチでは、http://localhost:3000?locale=pt
はポルトガル語のローカライゼーションを表示し、http://localhost:3000?locale=de
はドイツ語のローカライゼーションを読み込みます。
ロケールはさまざまな方法で設定できます。
2.2.1 ドメイン名からのロケールの設定
アプリケーションが実行されるドメイン名からロケールを設定するオプションがあります。たとえば、www.example.com
は英語(またはデフォルト)のロケールを読み込み、www.example.es
はスペイン語のロケールを読み込むようにしたい場合、トップレベルドメイン名をロケール設定に使用します。これにはいくつかの利点があります:
* ロケールはURLの一部であることは明らかです。
* 人々はどの言語でコンテンツが表示されるかを直感的に理解します。
* Railsで非常に簡単に実装できます。
* 検索エンジンは、異なる言語のコンテンツが異なる相互リンクされたドメインに存在することを好むようです。
ApplicationController
で次のように実装できます:
around_action :switch_locale
def switch_locale(&action)
locale = extract_locale_from_tld || I18n.default_locale
I18n.with_locale(locale, &action)
end
# ローカルドメインからロケールを取得するか、利用可能なロケールがない場合は+nil+を返します
# ローカルで試すために、/etc/hostsファイルに次のような内容を追加する必要があります:
# 127.0.0.1 application.com
# 127.0.0.1 application.it
# 127.0.0.1 application.pl
def extract_locale_from_tld
parsed_locale = request.host.split('.').last
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
非常に似た方法で、サブドメインからロケールを設定することもできます:
# リクエストのサブドメインからロケールコードを取得する(http://it.application.local:3000のような)
# ローカルで試すために、/etc/hostsファイルに次のような内容を追加する必要があります:
# 127.0.0.1 gr.application.local
def extract_locale_from_subdomain
parsed_locale = request.subdomains.first
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
アプリケーションにロケール切り替えメニューが含まれている場合、次のようになります:
link_to("Deutsch", "#{APP_CONFIG[:deutsch_website_url]}#{request.env['PATH_INFO']}")
APP_CONFIG[:deutsch_website_url]
をhttp://www.application.de
のような値に設定することを想定しています。
この解決策には前述の利点がありますが、異なるドメインで異なるローカライゼーション(「言語バージョン」)を提供できない場合もあります。最も明らかな解決策は、URLパラメータ(またはリクエストパス)にロケールコードを含めることです。
2.2.2 URLパラメータからロケールを設定する
ロケールを設定(および渡す)最も一般的な方法は、最初の例のI18n.with_locale(params[:locale], &action)
のようにURLパラメータに含めることです。この場合、www.example.com/books?locale=ja
やwww.example.com/ja/books
のようなURLを持ちたいと思います。
このアプローチは、ドメイン名からロケールを設定する方法とほぼ同じ利点を持っています。つまり、RESTfulであり、World Wide Webの他の部分と一致しています。ただし、実装には少しの作業が必要です。
params
からロケールを取得し、それに応じて設定することは難しくありません。ただし、リクエストを介してそれを渡すためには、少しの作業が必要です。明示的なオプションをすべてのURLに含めることは、たとえばlink_to(books_url(locale: I18n.locale))
のように、手間がかかり、おそらく不可能です。
Railsには、ApplicationController#default_url_options
で「URLに関する動的な決定を集中化する」インフラストラクチャが含まれており、このシナリオでは非常に便利です。これにより、url_for
とそれに依存するヘルパーメソッド(default_url_options
を実装/オーバーライドすることによって)の「デフォルト」を設定できます。
次のようにApplicationController
に含めることができます:
# app/controllers/application_controller.rb
def default_url_options
{ locale: I18n.locale }
end
url_for
に依存するすべてのヘルパーメソッド(root_path
やroot_url
などの名前付きルートのヘルパー、books_path
やbooks_url
などのリソースルートなど)は、これによって自動的にクエリ文字列にロケールを含めるようになります。例えば、http://localhost:3001/?locale=ja
のようになります。
これで満足できるかもしれません。ただし、アプリケーションのURLの可読性に影響を与えます。また、アーキテクチャ的な観点からは、ロケールは通常、アプリケーションドメインの他の部分よりも階層的に上位にあります。URLはこれを反映すべきです。
おそらく、URLは次のようになることを望んでいるでしょう:http://www.example.com/en/books
(英語のロケールを読み込む)およびhttp://www.example.com/nl/books
(オランダ語のロケールを読み込む)。これは、上記の「default_url_options
をオーバーライドする」戦略で実現できます。scope
を使用してルートを設定する必要があります:
# config/routes.rb
scope "/:locale" do
resources :books
end
これでbooks_path
メソッドを呼び出すと、デフォルトのロケールに対して"/en/books"
が返されるはずです。http://localhost:3001/nl/books
のようなURLはオランダ語のロケールを読み込み、その後のbooks_path
の呼び出しは"/nl/books"
を返すはずです(ロケールが変更されたため)。
警告:default_url_options
の返り値はリクエストごとにキャッシュされるため、ロケールセレクタのURLを生成する際に、各イテレーションで対応するI18n.locale
を設定するループ内でヘルパーを呼び出すことはできません。代わりに、I18n.locale
を変更せずに、ヘルパーに明示的な:locale
オプションを渡すか、request.original_fullpath
を編集します。
ルートでロケールの使用を強制したくない場合は、次のようにオプションのパススコープ(括弧で囲まれたもの)を使用できます:
# config/routes.rb
scope "(:locale)", locale: /en|nl/ do
resources :books
end
このアプローチでは、ロケールが指定されていない場合に、http://localhost:3001/books
のようなリソースにアクセスする際に Routing Error
を受け取ることはありません。これは、デフォルトのロケールを使用したい場合に便利です。
もちろん、アプリケーションのルートURL(通常は「ホームページ」または「ダッシュボード」)には特別な注意が必要です。routes.rb
の root to: "dashboard#index"
の宣言は、ロケールを考慮に入れていませんので、http://localhost:3001/nl
のようなURLは自動的に機能しません。(そして正当にそうです:「ルート」URLは1つしかありません。)
おそらく、次のようなURLをマッピングする必要があります。
# config/routes.rb
get '/:locale' => 'dashboard#index'
このルート宣言が他のルートを「食べない」ように、ルートの順序に特別な注意を払ってください。(root :to
の宣言の直前に追加することを検討するかもしれません。)
注意:ルートを簡素化するさまざまなジェムを見てみてください:routing_filter、route_translator。
2.2.3 ユーザーの設定からロケールを設定する
認証されたユーザーを持つアプリケーションでは、ユーザーがアプリケーションのインターフェースを介してロケールの設定を行えるようにすることがあります。このアプローチでは、ユーザーが選択したロケールの設定がデータベースに保存され、そのユーザーの認証済みリクエストのロケール設定に使用されます。
around_action :switch_locale
def switch_locale(&action)
locale = current_user.try(:locale) || I18n.default_locale
I18n.with_locale(locale, &action)
end
2.2.4 暗黙のロケールの選択
リクエストに明示的なロケールが設定されていない場合(上記のいずれかの方法で設定されていない場合)、アプリケーションは希望するロケールを推測する必要があります。
2.2.4.1 言語ヘッダーからのロケールの推測
Accept-Language
HTTPヘッダーは、リクエストのレスポンスに対する優先言語を示します。ブラウザは、ユーザーの言語設定に基づいてこのヘッダーの値を設定しますので、ロケールを推測する際には最初の選択肢となります。
Accept-Language
ヘッダーを使用する単純な実装は次のとおりです。
def switch_locale(&action)
logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
locale = extract_locale_from_accept_language_header
logger.debug "* Locale set to '#{locale}'"
I18n.with_locale(locale, &action)
end
private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
実際には、これを信頼性の高い方法で行うためには、より堅牢なコードが必要です。Iain Heckerの http_accept_language ライブラリやRyan Tomaykoの locale Rackミドルウェアは、この問題に対する解決策を提供しています。
2.2.4.2 IPジオロケーションからのロケールの推測
リクエストを行っているクライアントのIPアドレスは、クライアントの地域、つまりロケールを推測するために使用することができます。GeoLite2 Countryなどのサービスや、geocoderなどのジェムを使用して、このアプローチを実装することができます。
一般的に、このアプローチは言語ヘッダーを使用する方法よりも信頼性が低く、ほとんどのWebアプリケーションでは推奨されません。
2.2.5 セッションまたはクッキーからのロケールの保存
警告:選択したロケールを「セッション」または「クッキー」に保存しようとするかもしれませんが、これは行わないでください。ロケールは透過的でURLの一部であるべきです。これにより、URLを友達に送信した場合でも、同じページとコンテンツが表示されるべきです。これは、あなたが「RESTful」であると言われる洒落た言葉です。RESTfulアプローチについては、Stefan Tilkovの記事で詳しく説明されています。このルールには例外がある場合があり、それについては以下で説明します。
3 国際化とローカライゼーション
OK!これで、Ruby on RailsアプリケーションでI18nサポートを初期化し、どのロケールを使用するか、リクエスト間でそれを保持する方法を指定しました。
次に、アプリケーションを 国際化 するために、すべてのロケール固有の要素を抽象化する必要があります。最後に、これらの抽象化に必要な翻訳を提供することで、アプリケーションを ローカライズ する必要があります。
次の例を考えてみましょう。
# config/routes.rb
Rails.application.routes.draw do
root to: "home#index"
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
end
end
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = "Hello Flash"
end
end
<!-- app/views/home/index.html.erb -->
<h1>Hello World</h1>
<p><%= flash[:notice] %></p>
3.1 ローカライズされたコードの抽象化
コードの中には、レスポンスで表示される英語の2つの文字列があります("Hello Flash"と"Hello World")。このコードを国際化するには、これらの文字列をそれぞれ適切なキーを持つRailsの #t
ヘルパーの呼び出しに置き換える必要があります。
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = t(:hello_flash)
end
end
<!-- app/views/home/index.html.erb -->
<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
このビューがレンダリングされると、キー :hello_world
と :hello_flash
の翻訳が見つからないことを示すエラーメッセージが表示されます。
注意: Railsはビューに t
(translate
) ヘルパーメソッドを追加しているため、常に I18n.t
を明示する必要はありません。さらに、このヘルパーは翻訳が見つからない場合にエラーメッセージをキャッチし、結果のエラーメッセージを <span class="translation_missing">
で囲みます。
3.2 国際化された文字列の翻訳の提供
翻訳辞書ファイルに不足している翻訳を追加します。
# config/locales/en.yml
en:
hello_world: Hello world!
hello_flash: Hello flash!
# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash
default_locale
が変更されていないため、翻訳は :en
ロケールを使用し、レスポンスは英語の文字列を表示します。
ロケールが URL で海賊のロケール (http://localhost:3000?locale=pirate
) に設定されている場合、レスポンスは海賊の文字列を表示します。
注意: 新しいロケールファイルを追加する場合は、サーバーを再起動する必要があります。
SimpleStore で翻訳を保存するために、YAML (.yml
) またはプレーンな Ruby (.rb
) ファイルを使用することができます。YAML は Rails 開発者の間で好まれるオプションですが、1つの大きな欠点があります。YAML は空白と特殊文字に非常に敏感であり、アプリケーションが辞書を正しく読み込めない場合があります。Ruby ファイルは最初のリクエストでアプリケーションがクラッシュするため、問題の特定が容易です。(YAML 辞書で「奇妙な問題」に遭遇した場合は、辞書の関連部分を Ruby ファイルに入れてみてください。)
YAML ファイルに翻訳が保存されている場合、特定のキーをエスケープする必要があります。それらは次のとおりです。
- true, on, yes
- false, off, no
例:
# config/locales/en.yml
en:
success:
'true': 'True!'
'on': 'On!'
'false': 'False!'
failure:
true: 'True!'
off: 'Off!'
false: 'False!'
I18n.t 'success.true' # => 'True!'
I18n.t 'success.on' # => 'On!'
I18n.t 'success.false' # => 'False!'
I18n.t 'failure.false' # => Translation Missing
I18n.t 'failure.off' # => Translation Missing
I18n.t 'failure.true' # => Translation Missing
3.3 翻訳に変数を渡す
アプリケーションを正常に国際化するための重要な考慮事項の1つは、ローカライズされたコードを抽象化する際に文法ルールについて誤った仮定をしないことです。あるロケールで基本的な文法ルールに思えるものでも、別のロケールでは当てはまらない場合があります。
次の例では、異なる部分の順序についての仮定が行われ、不適切な抽象化が示されています。なお、Rails にはこのケースを処理するための number_to_currency
ヘルパーが用意されています。
<!-- app/views/products/show.html.erb -->
<%= "#{t('currency')}#{@product.price}" %>
# config/locales/en.yml
en:
currency: "$"
# config/locales/es.yml
es:
currency: "€"
製品の価格が10の場合、スペイン語の適切な翻訳は「10 €」であり、「€10」ではありませんが、抽象化ではそれを実現することができません。
適切な抽象化を作成するために、I18n ジェムには変数補間という機能が付属しており、翻訳定義で変数を使用し、これらの変数の値を翻訳メソッドに渡すことができます。
適切な抽象化の例:
<!-- app/views/products/show.html.erb -->
<%= t('product_price', price: @product.price) %>
# config/locales/en.yml
en:
product_price: "$%{price}"
# config/locales/es.yml
es:
product_price: "%{price} €"
文法的な決定や句読点の決定は定義自体で行われるため、抽象化は適切な翻訳を提供できます。
注意: default
キーワードと scope
キーワードは予約されており、変数名として使用することはできません。使用すると、I18n::ReservedInterpolationKey
例外が発生します。翻訳が補間変数を期待しているが、これが #translate
に渡されていない場合、I18n::MissingInterpolationArgument
例外が発生します。
3.4 日付/時刻のフォーマットの追加
OK! では、ビューにタイムスタンプを追加して、日付/時刻のローカライゼーション 機能もデモしてみましょう。時間のフォーマットをローカライズするには、Time オブジェクトを I18n.l
に渡すか(できれば)Rails の #l
ヘルパーを使用します。フォーマットを選択するには、format
オプションを渡します。デフォルトでは :default
フォーマットが使用されます。
<!-- app/views/home/index.html.erb -->
<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
<p><%= l Time.now, format: :short %></p>
そして、海賊の翻訳ファイルに時間のフォーマットを追加しましょう(これは Rails のデフォルトの英語の設定にすでに存在しています)。
# config/locales/pirate.yml
pirate:
time:
formats:
short: "arrrround %H'ish"
すると、次のようになります。
ヒント: 現時点では、I18n バックエンドが正しく機能するために(少なくとも 'pirate' ロケールに対して)、いくつかの追加の日付/時刻のフォーマットを追加する必要があるかもしれません。もちろん、既に誰かがあなたのロケールのために Rails のデフォルトを翻訳した作業をすべて行っている可能性があります。GitHub の rails-i18n リポジトリを参照して、さまざまなロケールファイルのアーカイブを確認してください。そのようなファイルを config/locales/
ディレクトリに配置すると、自動的に使用できるようになります。
3.5 他のロケールに対する曲法ルール
Railsでは、英語以外のロケールに対しても単数形と複数形のような曲法ルールを定義することができます。config/initializers/inflections.rb
では、複数のロケールに対してこれらのルールを定義することができます。イニシャライザには、英語以外の追加ルールを指定するためのデフォルトの例が含まれています。他のロケールについても同様の形式でフォローしてください。
3.6 ローカライズされたビュー
例えば、アプリケーションにBooksControllerがあるとします。indexアクションはapp/views/books/index.html.erb
テンプレートでコンテンツをレンダリングします。同じディレクトリにローカライズされたバリアントのテンプレートであるindex.es.html.erb
を配置すると、Railsはロケールが:es
に設定されている場合にこのテンプレートでコンテンツをレンダリングします。デフォルトのロケールに設定されている場合は、一般的なindex.html.erb
ビューが使用されます。(将来のRailsのバージョンでは、public
などのアセットにもこの自動的なローカライゼーションがもたらされるかもしれません。)
この機能は、YAMLやRubyの辞書に含めるのが難しい大量の静的コンテンツを扱う場合に使用することができます。ただし、後でテンプレートに対して行いたい変更はすべてのテンプレートに伝播する必要があることに注意してください。
3.7 ロケールファイルの組織化
i18nライブラリに付属しているデフォルトのSimpleStoreを使用している場合、辞書はディスク上のプレーンテキストファイルに格納されます。アプリケーションのすべての部分の翻訳を1つのロケールごとのファイルに格納することは管理が難しい場合があります。これらのファイルを意味のある階層に格納することができます。
例えば、config/locales
ディレクトリは次のようになるかもしれません。
|-defaults
|---es.yml
|---en.yml
|-models
|---book
|-----es.yml
|-----en.yml
|-views
|---defaults
|-----es.yml
|-----en.yml
|---books
|-----es.yml
|-----en.yml
|---users
|-----es.yml
|-----en.yml
|---navigation
|-----es.yml
|-----en.yml
これにより、ビュー内のテキストからモデルやモデル属性の名前を分離することができます。また、これらすべてを「デフォルト」(例:日付と時刻の形式)から分離することもできます。i18nライブラリの他のストアでは、このような分離の方法が異なる場合があります。
注意:Railsのデフォルトのロケールの読み込みメカニズムは、ここで説明したようなネストされた辞書のロケールファイルを読み込みません。したがって、これが機能するようにするには、明示的にRailsにさらに探すように指示する必要があります。
# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
4 I18n APIの機能の概要
i18nライブラリの使用方法について理解が深まり、基本的なRailsアプリケーションの国際化方法を知っているはずです。次の章では、その機能について詳しく説明します。
これらの章では、I18n.translate
メソッドとtranslate
ビューヘルパーメソッドの両方を使用した例を示します(ビューヘルパーメソッドが提供する追加機能に注目してください)。
以下のような機能がカバーされています。
- 翻訳の検索
- データの補間
- 翻訳の複数形化
- 安全なHTML翻訳の使用(ビューヘルパーメソッドのみ)
- 日付、数値、通貨などのローカライズ
4.1 翻訳の検索
4.1.1 基本的な検索、スコープ、およびネストされたキー
翻訳はキーで検索されます。キーはシンボルまたは文字列のどちらでも指定できるため、次の呼び出しは同等です。
I18n.t :message
I18n.t 'message'
translate
メソッドは、:scope
オプションも受け取ることができます。このオプションには、翻訳キーの「名前空間」またはスコープを指定するために使用される1つ以上の追加キーを含めることができます。
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
これにより、Active Recordエラーメッセージのrecord_invalid
メッセージが検索されます。
さらに、キーとスコープはドットで区切られたキーとして指定することもできます。
I18n.translate "activerecord.errors.messages.record_invalid"
したがって、次の呼び出しは同等です。
I18n.t 'activerecord.errors.messages.record_invalid'
I18n.t 'errors.messages.record_invalid', scope: :activerecord
I18n.t :record_invalid, scope: 'activerecord.errors.messages'
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
4.1.2 デフォルト値
:default
オプションが指定されている場合、その値が翻訳が見つからない場合に返されます。
I18n.t :missing, default: 'Not here'
# => 'Not here'
:default
の値がシンボルの場合、キーとして使用されて翻訳されます。複数の値をデフォルトとして指定することもできます。値が結果として返される最初の値が返されます。
例えば、次の例では、まずキー:missing
を翻訳し、次にキー:also_missing
を翻訳します。どちらも結果が得られないため、文字列"Not here"が返されます。
I18n.t :missing, default: [:also_missing, 'Not here']
# => 'Not here'
4.1.3 一括および名前空間の検索
複数の翻訳を一度に検索するには、キーの配列を渡すことができます。 ```ruby I18n.t [:odd, :even], scope: 'errors.messages'
=> ["奇数でなければなりません", "偶数でなければなりません"]
また、キーは(ネストされた可能性のある)グループ化された翻訳のハッシュに変換することもできます。例えば、次のようにして_Active Record_のすべてのエラーメッセージをハッシュとして受け取ることができます。
```ruby
I18n.t 'errors.messages'
# => {:inclusion=>"リストに含まれていません", :exclusion=> ... }
複数の翻訳のハッシュに対して補間を行いたい場合は、deep_interpolation: true
をパラメータとして渡す必要があります。次の辞書がある場合、
en:
welcome:
title: "ようこそ!"
content: "%{app_name}へようこそ"
ネストされた補間は、設定なしでは無視されます。
I18n.t 'welcome', app_name: '書店'
# => {:title=>"ようこそ!", :content=>"%{app_name}へようこそ"}
I18n.t 'welcome', deep_interpolation: true, app_name: '書店'
# => {:title=>"ようこそ!", :content=>"書店へようこそ"}
4.1.4 "遅延"ルックアップ
Railsでは、ビュー内でロケールを簡単に参照する方法が実装されています。次の辞書がある場合、
es:
books:
index:
title: "タイトル"
app/views/books/index.html.erb
テンプレート内で、books.index.title
の値を内部で次のように参照できます(ドットに注意してください)。
<%= t '.title' %>
注意:部分テンプレートによる自動翻訳スコープは、translate
ビューヘルパーメソッドからのみ利用できます。
"遅延"ルックアップは、コントローラでも使用できます。
en:
books:
create:
success: 本が作成されました!
これは、フラッシュメッセージを設定するために便利です。
class BooksController < ApplicationController
def create
# ...
redirect_to books_url, notice: t('.success')
end
end
4.2 複数形
英語を含む多くの言語では、特定の文字列には単数形と複数形の2つの形式しかありません。例えば、「1つのメッセージ」と「2つのメッセージ」です。他の言語(アラビア語、日本語、ロシア語など)では、追加または減少する複数形の形式が異なる文法があります。そのため、I18n APIは柔軟な複数形化機能を提供しています。
:count
補間変数は、翻訳に補間されるだけでなく、複数形化バックエンドで定義された複数形化ルールに基づいて翻訳から複数形を選択するためにも使用されます。デフォルトでは、英語の複数形化ルールのみが適用されます。
I18n.backend.store_translations :en, inbox: {
zero: 'メッセージなし', # オプション
one: '1つのメッセージ',
other: '%{count}個のメッセージ'
}
I18n.translate :inbox, count: 2
# => '2個のメッセージ'
I18n.translate :inbox, count: 1
# => '1つのメッセージ'
I18n.translate :inbox, count: 0
# => 'メッセージなし'
:en
の複数形化のアルゴリズムは次のようになります。
lookup_key = :zero if count == 0 && entry.has_key?(:zero)
lookup_key ||= count == 1 ? :one : :other
entry[lookup_key]
:one
として指定された翻訳は単数形と見なされ、:other
は複数形として使用されます。カウントがゼロであり、:zero
エントリが存在する場合は、:other
の代わりに使用されます。
キーのルックアップが複数形化に適したハッシュを返さない場合、I18n::InvalidPluralizationData
例外が発生します。
4.2.1 ロケール固有のルール
I18n gemは、ロケール固有のルールを有効にするために使用できる複数形化バックエンドを提供しています。これをSimpleバックエンドに含め、i18n.plural.rule
として翻訳ストアにローカライズされた複数形化アルゴリズムを追加します。
I18n::Backend::Simple.include(I18n::Backend::Pluralization)
I18n.backend.store_translations :pt, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } }
I18n.backend.store_translations :pt, apples: { one: '1つまたは0個', other: '1個以上' }
I18n.t :apples, count: 0, locale: :pt
# => '1つまたは0個'
別の方法として、rails-i18nという別のgemを使用して、より完全なロケール固有の複数形化ルールを提供することもできます。
4.3 ロケールの設定と渡し方
ロケールは、I18n.locale
(Time.zone
などと同様にThread.current
を使用する)に疑似的にグローバルに設定するか、#translate
と#localize
のオプションとして渡すことができます。
ロケールが渡されない場合、I18n.locale
が使用されます。
I18n.locale = :de
I18n.t :foo
I18n.l Time.now
明示的にロケールを渡す場合:
I18n.t :foo, locale: :de
I18n.l Time.now, locale: :de
I18n.locale
は、I18n.default_locale
(デフォルトは:en)をデフォルトとして使用します。デフォルトのロケールは次のように設定できます。
I18n.default_locale = :de
4.4 安全なHTML翻訳の使用
'_html'接尾辞を持つキーと、'html'という名前のキーは、HTMLセーフとしてマークされます。ビューで使用する場合、HTMLはエスケープされません。
# config/locales/en.yml
en:
welcome: <b>ようこそ!</b>
hello_html: <b>こんにちは!</b>
title:
html: <b>タイトル!</b>
<!-- app/views/home/index.html.erb -->
<div><%= t('welcome') %></div>
<div><%= raw t('welcome') %></div>
<div><%= t('hello_html') %></div>
<div><%= t('title.html') %></div>
必要に応じて補間がエスケープされます。例えば、次のように与えられた場合:
en:
welcome_html: "<b>Welcome %{username}!</b>"
ユーザーが設定したユーザー名を安全に渡すことができます:
<%# これは安全です。必要に応じてエスケープされます。 %>
<%= t('welcome_html', username: @current_user.username) %>
安全な文字列は、そのまま補間されます。
注意:HTMLセーフな翻訳テキストへの自動変換は、translate
(またはt
)ヘルパーメソッドからのみ利用できます。これはビューとコントローラーで動作します。
4.5 Active Recordモデルの翻訳
Model.model_name.human
とModel.human_attribute_name(attribute)
メソッドを使用して、モデルと属性名の翻訳を透過的に参照することができます。
例えば、次の翻訳を追加する場合:
en:
activerecord:
models:
user: Customer
attributes:
user:
login: "Handle"
# Userの属性"login"を"Handle"として翻訳します
その後、User.model_name.human
は"Customer"を返し、User.human_attribute_name("login")
は"Handle"を返します。
モデル名に複数形を設定することもできます。次のように追加します:
en:
activerecord:
models:
user:
one: Customer
other: Customers
その後、User.model_name.human(count: 2)
は"Customers"を返します。count: 1
またはパラメータなしの場合は"Customer"を返します。
特定のモデル内のネストされた属性にアクセスする必要がある場合は、モデルレベルの翻訳ファイルのmodel/attribute
の下にこれらをネストする必要があります:
en:
activerecord:
attributes:
user/role:
admin: "Admin"
contributor: "Contributor"
その後、User.human_attribute_name("role.admin")
は"Admin"を返します。
注意:ActiveRecord::Base
から継承しないActiveModel
を含むクラスを使用している場合は、上記のキーパスでactiverecord
をactivemodel
に置き換えてください。
4.5.1 エラーメッセージのスコープ
Active Recordのバリデーションエラーメッセージも簡単に翻訳することができます。Active Recordには、モデル、属性、および/またはバリデーションに対して異なるメッセージと翻訳を提供するためのいくつかの名前空間があります。また、単一テーブル継承も透過的に考慮されます。
これにより、アプリケーションのニーズに柔軟にメッセージを調整することができます。
次のような名前属性のバリデーションを持つUserモデルを考えてみましょう:
class User < ApplicationRecord
validates :name, presence: true
end
この場合、エラーメッセージのキーは:blank
です。Active Recordは、次の名前空間でこのキーを検索します:
activerecord.errors.models.[model_name].attributes.[attribute_name]
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages
したがって、この例では次の順序で次のキーを試し、最初の結果を返します:
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
モデルがさらに継承を使用している場合、メッセージは継承チェーンで検索されます。
例えば、Userを継承したAdminモデルがあるかもしれません:
class Admin < User
validates :name, presence: true
end
その場合、Active Recordは次の順序でメッセージを検索します:
activerecord.errors.models.admin.attributes.name.blank
activerecord.errors.models.admin.blank
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank
これにより、モデルの継承チェーンと属性、モデル、またはデフォルトのスコープでさまざまなエラーメッセージの特別な翻訳を提供することができます。
4.5.2 エラーメッセージの補間
翻訳されたモデル名、翻訳された属性名、および値は常にmodel
、attribute
、およびvalue
として補間で使用できます。
例えば、デフォルトのエラーメッセージ"cannot be blank"
の代わりに、次のように属性名を使用することができます:"Please fill in your %{attribute}"
。
count
は、利用可能な場合は複数形に使用できます:
バリデーション | オプション付き | メッセージ | 補間 |
---|---|---|---|
confirmation | - | :confirmation | attribute |
acceptance | - | :accepted | - |
presence | - | :blank | - |
absence | - | :present | - |
length | :within, :in | :too_short | count |
length | :within, :in | :too_long | count |
length | :is | :wrong_length | count |
length | :minimum | :too_short | count |
length | :maximum | :too_long | count |
uniqueness | - | :taken | - |
format | - | :invalid | - |
inclusion | - | :inclusion | - |
exclusion | - | :exclusion | - |
associated | - | :invalid | - |
non-optional association | - | :required | - |
numericality | - | :not_a_number | - |
numericality | :greater_than | :greater_than | count |
numericality | :greater_than_or_equal_to | :greater_than_or_equal_to | count |
numericality | :equal_to | :equal_to | count |
numericality | :less_than | :less_than | count |
numericality | :less_than_or_equal_to | :less_than_or_equal_to | count |
numericality | :other_than | :other_than | count |
numericality | :only_integer | :not_an_integer | - |
numericality | :in | :in | count |
numericality | :odd | :odd | - |
numericality | :even | :even | - |
comparison | :greater_than | :greater_than | count |
comparison | :greater_than_or_equal_to | :greater_than_or_equal_to | count |
comparison | :equal_to | :equal_to | count |
comparison | :less_than | :less_than | count |
comparison | :less_than_or_equal_to | :less_than_or_equal_to | count |
comparison | :other_than | :other_than | count |
4.6 Action Mailerのメール件名の翻訳
mail
メソッドに件名を渡さない場合、Action Mailerは翻訳で件名を見つけようとします。実行されるルックアップは、キーを構築するために<mailer_scope>.<action_name>.subject
パターンを使用します。
# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
#...
end
end
en:
user_mailer:
welcome:
subject: "Railsガイドへようこそ!"
メーラーで補間にパラメータを送信するには、default_i18n_subject
メソッドを使用します。
# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
mail(to: user.email, subject: default_i18n_subject(user: user.name))
end
end
en:
user_mailer:
welcome:
subject: "%{user}さん、Railsガイドへようこそ!"
4.7 I18nサポートを提供する他の組み込みメソッドの概要
Railsは、ヘルパーのいくつかで固定文字列やフォーマット文字列などの他のローカライズを使用しています。以下に簡単な概要を示します。
4.7.1 Action Viewヘルパーメソッド
distance_of_time_in_words
は、結果を翻訳し、複数形にし、秒数、分数、時間などを補間します。datetime.distance_in_wordsの翻訳を参照してください。datetime_select
とselect_month
は、選択したタグを作成するために翻訳された月名を使用します。翻訳にはdate.month_namesを参照してください。datetime_select
は、date.orderからオプションの順序を検索します(オプションを明示的に渡さない限り)。すべての日付選択ヘルパーは、該当する場合はdatetime.promptsスコープの翻訳を使用してプロンプトを翻訳します。number_to_currency
、number_with_precision
、number_to_percentage
、number_with_delimiter
、number_to_human_size
ヘルパーは、numberスコープの数値フォーマット設定を使用します。
4.7.2 Active Modelメソッド
model_name.human
とhuman_attribute_name
は、モデル名と属性名の翻訳を使用します。activerecord.modelsスコープで利用可能な場合にも、継承されたクラス名の翻訳もサポートしています。詳細は「エラーメッセージのスコープ」で説明されています。ActiveModel::Errors#generate_message
(Active Modelのバリデーションで使用されるが、手動で使用することもできる)は、model_name.human
とhuman_attribute_name
を使用します(上記参照)。エラーメッセージも翻訳し、継承されたクラス名の翻訳もサポートしています(「エラーメッセージのスコープ」で説明されているように)。ActiveModel::Error#full_message
とActiveModel::Errors#full_messages
は、errors.format
からフォーマットを検索してエラーメッセージの前に属性名を追加します(デフォルト:"%{attribute} %{message}"
)。デフォルトのフォーマットをカスタマイズするには、アプリのロケールファイルで上書きします。モデルごとまたは属性ごとにフォーマットをカスタマイズするには、config.active_model.i18n_customize_full_message
を参照してください。
4.7.3 Active Supportメソッド
Array#to_sentence
は、support.arrayスコープで指定されたフォーマット設定を使用します。
5 カスタム翻訳の保存方法
Active Supportに同梱されているSimpleバックエンドを使用すると、翻訳をプレーンなRuby形式とYAML形式の両方で保存できます。2
たとえば、翻訳を提供するRubyのハッシュは次のようになります。
{
pt: {
foo: {
bar: "baz"
}
}
}
同等のYAMLファイルは次のようになります。
pt:
foo:
bar: baz
上記の例では、最上位のキーがロケールです。:foo
は名前空間キーであり、:bar
は翻訳「baz」のキーです。
Active Supportのen.yml
翻訳YAMLファイルからの「実際の」例を次に示します。
en:
date:
formats:
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
したがって、次のいずれの等価なルックアップも:short
の日付フォーマット"%b %d"
を返します。
I18n.t 'date.formats.short'
I18n.t 'formats.short', scope: :date
I18n.t :short, scope: 'date.formats'
I18n.t :short, scope: [:date, :formats]
通常、翻訳を保存するためにYAML形式を使用することをお勧めします。ただし、特別な日付フォーマットなどのロケールデータの一部としてRubyのラムダを保存する必要がある場合もあります。
6 I18nのセットアップのカスタマイズ
6.1 異なるバックエンドの使用
いくつかの理由から、Active Supportに同梱されているSimpleバックエンドは、Ruby on Railsに対して「最も単純な方法」でのみ動作することが保証されています3。つまり、英語と非常に似ている言語に対してのみ動作することが保証されています。また、シンプルバックエンドは翻訳を読み取ることしかできず、動的に保存することはできません。
ただし、これによって制限されるわけではありません。Ruby I18n gemを使用すると、I18n.backend=
セッターにバックエンドインスタンスを渡すことで、Simpleバックエンドの実装を他のバックエンドに簡単に置き換えることができます。
たとえば、SimpleバックエンドをChainバックエンドに置き換えて、複数のバックエンドを連結することができます。これは、Simpleバックエンドで標準の翻訳を使用し、カスタムアプリケーションの翻訳をデータベースや他のバックエンドに保存したい場合に便利です。 Chainバックエンドを使用すると、Active Recordバックエンドを使用して(デフォルトの)Simpleバックエンドにフォールバックできます。
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
6.2 異なる例外ハンドラの使用
I18n APIでは、バックエンドが対応する予期しない条件が発生した場合に発生する例外が次のように定義されています。
例外 | 理由 |
---|---|
I18n::MissingTranslationData |
要求されたキーの翻訳が見つかりませんでした |
I18n::InvalidLocale |
I18n.locale に設定されたロケールが無効です(例:nil ) |
I18n::InvalidPluralizationData |
カウントオプションが渡されましたが、翻訳データが複数形に適していません |
I18n::MissingInterpolationArgument |
翻訳には渡されていない補間引数が必要です |
I18n::ReservedInterpolationKey |
翻訳に予約された補間変数名が含まれています(つまり、scope 、default のいずれか) |
I18n::UnknownFileType |
バックエンドがI18n.load_path に追加されたファイルタイプの処理方法を知りません |
6.2.1 I18n::MissingTranslationData
の処理方法のカスタマイズ
config.i18n.raise_on_missing_translations
がtrue
の場合、I18n::MissingTranslationData
エラーが発生します。欠落している翻訳が要求された場所を見つけるために、テスト環境でこれをオンにすると良いでしょう。
config.i18n.raise_on_missing_translations
がfalse
(すべての環境のデフォルト)の場合、例外のエラーメッセージが表示されます。これには、欠落しているキー/スコープが含まれているため、コードを修正できます。
さらにこの動作をカスタマイズする場合は、config.i18n.raise_on_missing_translations = false
を設定し、I18n.exception_handler
を実装する必要があります。カスタム例外ハンドラは、procまたはcall
メソッドを持つクラスであることができます。
# config/initializers/i18n.rb
module I18n
class RaiseExceptForSpecificKeyExceptionHandler
def call(exception, locale, key, options)
if key == "special.key"
"translation missing!" # return this, don't raise it
elsif exception.is_a?(MissingTranslation)
raise exception.to_exception
else
raise exception
end
end
end
end
I18n.exception_handler = I18n::RaiseExceptForSpecificKeyExceptionHandler.new
これにより、I18n.t("special.key")
の場合を除いて、デフォルトのハンドラと同じようにすべての例外が発生します。
7 モデルのコンテンツの翻訳
このガイドで説明されているI18n APIは、主にインターフェース文字列の翻訳に使用することを意図しています。モデルのコンテンツ(例:ブログ記事)を翻訳する場合は、これに対応する別のソリューションが必要です。
いくつかのGemがこれをサポートしています。
8 結論
この時点で、Ruby on RailsでのI18nサポートの概要を把握し、プロジェクトの翻訳を開始する準備が整いました。
9 Rails I18nへの貢献
Ruby on RailsでのI18nサポートは、リリース2.2で導入され、現在も進化しています。このプロジェクトは、最初にジェムや実際のアプリケーションで解決策を進化させ、最も広く有用な機能をコアに組み込むために、良いRuby on Railsの開発の伝統に従っています。
したがって、新しいアイデアや機能をジェムや他のライブラリで実験し、コミュニティで利用できるようにすることを推奨します(メーリングリストでの作業を発表するのを忘れないでください!)
Ruby on Railsの例の翻訳データリポジトリに自分のロケール(言語)がない場合は、リポジトリをforkし、データを追加してプルリクエストを送信してください。
10 リソース
- Googleグループ:rails-i18n - プロジェクトのメーリングリスト。
- GitHub:rails-i18n - rails-i18nプロジェクトのコードリポジトリと問題追跡システム。最も重要なのは、Railsのための多くの例の翻訳を見つけることができます。
- GitHub:i18n - i18n gemのコードリポジトリと問題追跡システム。
11 著者
- Sven Fuchs(初期の著者)
- Karel Minařík
12 脚注
フィードバック
このガイドの品質向上にご協力ください。
タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。 開始するには、ドキュメントへの貢献セクションを読んでください。
不完全なコンテンツや最新でない情報も見つかるかもしれません。 メインのドキュメントに不足しているドキュメントを追加してください。 修正済みかどうかは、まずEdge Guidesを確認してください。 スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。
修正すべき点を見つけたが、自分で修正できない場合は、 問題を報告してください。
そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。