#338 Globalize3
- Download:
- source codeProject Files in Zip (93.4 KB)
- mp4Full Size H.264 Video (16.4 MB)
- m4vSmaller H.264 Video (7.84 MB)
- webmFull Size VP8 Video (8.46 MB)
- ogvFull Size Theora Video (17.9 MB)
下の画面は、複数のArticle
レコードを表示するブログアプリケーションです。このアプリケーションは国際化に対応しており、ユーザがページのトップのリンクで表示言語を選択できます。
“Wookieespeak”のリンクをクリックすると、ページ上部のタイトルが変わりますが、記事の内容は英語のままです。データベースからの内容を、ユーザが希望する言語で表示させるためには、どのように修正すればいいのでしょうか?
Railsアプリケーションの国際化は通常YAMLファイルを使っておこなわれます。/config/localesディレクトリの下に、アプリケーションがサポートする言語ごとの翻訳されたテキストを含んだ複数のファイルがあります。このアプローチについてはエピソード138で詳しく説明しました。静的なテキストであればこの方法でうまくいきますが、データベースからのテキストを別の言語で表示させる場合には対応していません。それをおこなうには、各翻訳文をデータベースのテーブルに格納する必要がありますが、Globalize3 gemを利用すればこれをとても簡単におこなうことができます。Globalize3は各モデルごとに翻訳を保存するために使用する別のテーブルを作成し、ユーザが選択したロケールにしたがって対応する翻訳に切り替えます。アプリケーションでこのgemを利用するにはgemfileにそれを追加してbundle
コマンドを実行してインストールをおこないます。
gem 'globalize3'
次に翻訳したいモデルに対して、translates
メソッドを使って多言語化したいカラムの名前を指定します。今回の場合はArticle
の中のnameとcontentの各カラムです。
class Article < ActiveRecord::Base translates :name, :content end
翻訳を保存するためのデータベーステーブルを作成します。gemのREADMEファイルを見ると、モデル用のテーブルを作成するためのmigrationの中で同時にcreate_translation_table!
を使用できるとありますが、ここではすでにarticles
テーブルが存在しデータが入っている状態なので、もう一つのアプローチとして別途migrationを作成して既存のデータを移行させます。まずcreate_article_translations
という名前のmigrationを作成します。
$ rails g migration create_article_translations
READMEファイルに従ってこのmigrationを修正します。翻訳を追加したいモデルに対してcreate_translations_table!
を呼び出し、翻訳できるようにするカラムの名前を渡します。
class CreateArticleTranslations < ActiveRecord::Migration def up Article.create_translation_table!({ name: :string, content: :text }, { migrate_data: true }) end def down Article.drop_translation_table! migrate_data: true end end
この2つのカラムのデータはarticles
テーブルには保存されず、新しいarticle_translations
テーブルに保存されます。これらの変更を反映させるためにデータベースのマイグレーションを実行します。
$ rake db:migrate
Railsコンソールで、このしくみが動作するところを確認できます。現在のロケールを確認すると、デフォルトの英語になっています。最初の記事の名称を取得しようとすると、新しいarticle_translations
テーブルから値を取り出します。
1.9.3-p125 :001 > I18n.locale => :en 1.9.3-p125 :002 > Article.first.name Article Load (0.2ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Superman"
ロケールをWookieespeakに変更し最初の記事の名称を取得しようすると、翻訳されたテキストが存在しないのでnilが返されます。
1.9.3-p125 :003 > I18n.locale = :wk => :wk 1.9.3-p125 :004 > Article.first.name Article Load (0.3ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 Article::Translation Load (0.3ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 AND "article_translations"."locale" = 'wk' LIMIT 1 => nil
Globalize3は、カラムに対するgetterとsetterの振る舞いをオーバーライドして、現在の言語にスコープを限定します。ロケールがwk
に設定されているときに最初の記事の名称を更新すると、article_translations
テーブルにそのロケール用のレコードが挿入され、再度名称を取得しようとすると入力した値が表示されます。
1.9.3-p125 :005 > Article.first.update_attribute(:name, "Ahhyya") 1.9.3-p125 :006 > Article.first.name Article Load (0.3ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Ahhyya"
ロケールを再度英語に変更すると、英語の名称が再度表示されます。
1.9.3-p125 :007 > I18n.locale = :en => :en 1.9.3-p125 :008 > Article.first.name Article Load (0.2ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Superman"
ではサイトで実際に動作する様子を見てみます。変更を反映するためにサーバを再起動すると、英語版ページは以前のデータがそのまま移行されたので見え方は変わりません。一方Wookieespeakでページを見てみると、ページはほとんど空白です。
この最初の記事にはコンソールから設定した名称がありますが、翻訳できるように設定した他のすべての属性は空欄です。しかし、これらの記事を編集して翻訳テキストを設定できるようになりました。
この記事は2言語で読めるようになり、言語を切り替えると選択した言語で表示されます。編集フォームに戻ると、テキストフィールドには選択した言語のテキストが表示され、リンクをクリックすることでそれらを入れ替えることで、いずれの言語も更新することが可能です。翻訳できるように設定していない属性(例えば著者名)を変更すると、それらはarticle_translations
テーブルではなくarticles
テーブルに保存されるため、全言語で更新されます。
フォールバック言語を使用する
データベースのすべてのレコードを2言語に翻訳するのは手間のかかる作業です。前に見たように、現在の言語に翻訳されていない属性にはnil
の値が返されますが、フォールバック言語を設定することで、現在の言語で翻訳が存在しない場合にそれを表示させることが可能です。これをおこなうにはアプリケーションの設定ファイルを修正して、i18n.fallbacks
オプションを追加します。
config.i18n.fallbacks = true
これによって、現在の言語で設定されていない翻訳はデフォルトのロケール(今回の場合は英語)にフォールバックします。この変更を反映するためにアプリケーションを再起動するしてページをリロードすると、Wookieespeakに翻訳テキストがない部分は代わりに英語で表示されます。