I18n(0.9.3)のコードリーディングをしました。

まず簡単な使い方から。

Railsを使っていると特に何も考えなくてもconfig/locales/から設定ファイルをロードしたり色々やってくれて便利なんですが、I18n単体でもこんなにシンプルに書けます。Railsでやっている処理に関しては後半に紹介します。

まずI18n.load_pathの処理をみてみます。load_pathはconfigに処理を委譲しており、configはI18n::Configのインスタンスです。

load_pathはI18n::Configのクラス変数の@@load_pathの操作になります。

translate

#tは#translateのエイリアスです。config.backendはBackend::Simpleのインスタンスが入ります。

#enforce_available_locales!はconfig.enforce_available_localesがtrueの場合、ロケールが利用可能かどうかをチェックします。@@enforce_available_localesはデフォルトtrueなので未設定の場合は必ず#locale_available?が呼ばれます。

#locale_available?はさらにI18n::Config#available_locales_set経由でbackend.available_localesを呼び出します。

I18n::Backend::Simple#available_localesは初期化されていない場合は#init_translations経由で#load_translationsを呼び出します。

#load_translationsはI18n.load_pathに設定されたファイルパスを#load_fileの引数に渡します。

#load_fileは”load_#{ファイルの拡張子}”のメソッドを呼び出します。予め定義されているのはload_ymlとload_rbになります。YAMLの拡張子に.yamlが使えないのはこれが理由だったりします。普通にalias貼れば解決しそうな感じはしますが…。

読み込んだデータはstore_translationsによってメモリ上に保存されます。

Hash#deep_symbolize_keysによって全てのキーがシンボル化された上で#translations経由で翻訳データが保存されます。

#translateの後半が実際の翻訳処理になります(以下、再掲)

backend.translateを呼び出します。色々と処理しているのですが本質的なところだけ抜き出しました↓

I18n::Backend::Simple#lookupは以下のようになっており、

#normalize_keyはキーをセパレータで区切って配列にします

#resolveは値がシンボルやProcだった場合、追加処理をするものです。シンボルの場合は、その値をキーとしてI18n.tを再帰的に呼び出すリンク的な処理をしたり、Procの場合はcallして評価した上でさらにresolveします。ProcはYAMLで表現できないので、Rubyの翻訳ファイルを読み込んだときだけ利用可能な機能っぽいです(コード読んで初めて知った)

#lookup内のresolveと#translate内のresolveは役割が異なっており、キー評価の途中の解決は#lookup、キー評価後の最後の値に対して解決するのが#translateです。

例えば、キーがfoo.bar.bazでfooの値がシンボルの場合は、そのシンボルを解決するのが#lookup内のresolve、キーがfoo.bar.bazでbazの値がシンボルの場合は、そのシンボルを解決するのが#translate内のresolveとなります。

さらに、#translate内ではパラメータをバインドするinterpolationの処理も行います。

localize

#lは#localizeのエイリアスです。まず、backend.localizeを呼び出します。

objectにはDate、DateTime、Timeオブジェクトが入ります。formatは"#{type}.formats.#{key}"のキーで取得したデータを利用して、strftimeで文字列に置き換えます。

#translate_localization_formatではロケールごとの月や曜日などの名前をString#gsubで変換していきます。

Railsでやっていること

Railsの場合、I18n::RailtieというRailtieが定義されており、I18nの初期設定やconfigで設定したものを読み出します。

またRails::Engineではi18n.railties_load_pathにpaths[“config/locales”]を追加しています。これによってconfig/locales以下のファイルがI18n.load_pathに追加されます。

また、ビューファイル内ではI18n.lではなくlがエイリアスとして使えます。これはビューをレンダリングするコンテキストのクラスがActionView::Baseを継承していて、ActionView::BaseがActionView::Helpers(HelpersはさらにTranslationHelperをinclude)をincludeしているためです。