Rails4でi18nの多言語対応やったときのハマりどころとかまとめ。
絶賛運営中のトーナメント表作成サービス「THE TOURNAMENT(ザ・トーナメント)」。
今回はRails多言語対応のi18nをつかって英語版に切替れるようにしてみました。
i18n対応でいろいろハマったりしたところのまとめです。
i18nの使い方については公式ドキュメントに詳しく載ってます。困ったときはちゃんと読めば書いてある。
RailsGuides「Rails Internationalization (I18n) API」
あとはいつものようにmorizyunさんのブログも参考にさせてもらいました。
酒と泪とRubyとRailsと「Railsの多言語化対応 I18nのやり方を整理してみた!【国際化/英語化】」
まずはこのあたりを読み込んでその通りにやっていけば基本的な設定はできるはず。
以下さらに詳細な設定やハマったところとかのまとめです。
shallow routingつかってるとlocaleのscope設定でハマる
まずはこれですね。そもそも他言語対応したときにURLをどうするかという問題があって、
URLは同一でsessionとかで判別して表示言語を出し分け
言語ごとにドメインをわける e.g. http://the-tournament.us(英語版) と http://the-tournament.jp(日本語版)
全URLの後ろにパラメータくっつける e.g. http://the-tournament.jp/?locale=en
URLの一部に言語識別のパラメータを持たせる e.g. http://the-tournament.jp/en/
同一URLで違うコンテンツを出すのはRESTの観点からいけてないので一番上は非推奨。
残り3つを比べると、ドメインわける→いろいろ大変、後ろにパラメータ→カッコ悪い、
ということで必然的に最後のURLにパラメータ組み込む案を採用することに。
やり方は各サイトを参考にしてもらうとして、この案はroutingを少しいじる必要が出てきます。
scope "(:locale)", locale: /ja|en/ do resources :tournaments end
とかでできるはずなんやけど、shallow routingつかってるせいでハマりました。。
scope "(:locale)", locale: /ja|en/ do resources :tournaments, shallow: true do resources :players resources :games end end
みたいにshallow routingを使ってて、こうするとリンクに自動的にlocaleがセットされるときに
http://localhost:5000/hogehoge?locale=en
と後ろパラメータ方式にされてしまう。
しかも本来やりたかったhttp://localhost:5000/en/hogehogeでroutingエラーが起きてしまって全然だめ。
明らかにshallow: trueがあやしいなーと思ったらやっぱりそうでした。
Stackoverflow「Rails routes with scope “:locale” and shallow nested resources」
scope "(:locale)", shallow_path: "(:locale)", locale: /ja|en/ do
とshallow_pathオプションをつけると無事解決。
よかったよかった。。
タイトルとかキーワードとか翻訳するのはまぁ当然として、多言語対応時に必要になるのがalternateというやつ。
これは現在のページに他言語版があれば対応するURLを指定してあげるという感じです。
kpumuk/meta-tags「Multi-regional and multilingual URLs」
# _meta_tags.html.haml - if I18n.locale == :ja - set_meta_tags alternate: {en: "PATH_FOR_EN"} - else - set_meta_tags alternate: {ja: "PATH_FOR_JA"}
しかしここで指定したい「現在のページURLの他言語版」を取得するのに手間取った。
Railsで現在のURLを取得するのは request.url とかいくつかあるけど、localeを指定する方法がわからない。
root_path(locale: 'ja')とかやとlocaleの指定はできたけど今度は現在のURLを指定する方法がわからない。
で結局ここでurl_for使う方法見つけて無事解決しましたよー。
Stackoverflow「Rails link to current page and passing parameters to it」
url_forはcontrollerとか指定しないと現在のパスを返すんですね。
なのでシンプルにこんな感じ。
url_for(locale: 'en') #=> "/ja/tournaments/30"
ただこれやとホスト名以下の相対パスだけ返すので、オプションで絶対パスを返すように指定。
url_for(locale: 'en', only_path: false) #=> "http://localhost:5000/ja/tournaments/30"
- if I18n.locale == :ja - set_meta_tags alternate: {en: url_for(locale:'en', only_path: false)} - else - set_meta_tags alternate: {ja: url_for(locale:'ja', only_path: false)}
これで各ページで他言語版のURLをメタタグで指定してくれるようになりました。
これは途中まで気づかんかったんですが、辞書ファイルを定義していくときに
yamlの構造をうまくやればRailsが自動的に階層構造を読み取ってくれるんですね。これは便利。
Rails3事始め「[Rails3] 国際化 I18n のまとめ(その3:辞書ファイルの使い方)」
翻訳を一番よくつかうのはViewやと思うけど、views/tournaments/index.html.hamlで使う単語を定義するときは、
# config/locale/views/tournaments/ja.yml ja: tournaments: #=> controller index: #=> action hoge: ほげほげ
とかって感じで定義してやれば、viewファイル内で簡潔に参照できるとな。
# views/tournaments/index.html.haml ... = t('.hoge')
参照時に"."(ドット)をつけるのがポイントですね。
ユーザー認証にdeviseつかってますが、登録時のflashメッセージ表示とか、
いろいろデフォルトの文言が英語で用意されてるのでこれも翻訳する必要があります。
量が多いので大変ですが、Gistで日本語辞書ファイルが提供されているのでありがたく使わせていただきます。
Gist「kawamoto / devise.ja.yml」
config/locale/devise.ja.yml
として配置します(同じとこにdevise.en.ymlがもともとあるはず)。
これで各種文言が日本語化されました。便利!
ただこのデフォルト辞書ファイルだけでは完璧じゃなくて、ログイン画面とかのviewファイルで
自分が作った部分は当然別途翻訳を用意する必要があります。
基本はふつうの画面の翻訳と同じなんですが、上記の階層化のときにdevise用の対応が必要だったのでちょっとハマる。
# config/locale/views/devise/ja.yml ja: devise: sessions: new: hoge: ほげほげ
こんな感じでいつものcontroller, actionの前にdevise:を入れる必要があるみたいです。
しかしさらにまだやることは残ってて、これだけだとログインなどの画面で表示される入力項目のラベルが
デフォルトの英語のままになってるはず。
Simple_formを使ってる場合、これはconfig/locale/simple_form.ja.ymlに定義することで翻訳できます。
(これも同じ所にsimple_form.en.ymlがもともとあるはず)
plataformatec/simple_form「i18n」
simple_form.en.ymlに書き方のサンプルがあるのでそれを参考に、
# config/locale/simple_form.ja.yml ja: simple_form: labels: default: password: パスワード user: new: hoge: ほげほげ
newかeditかで表現変えれたりもするのがきめ細かい。
ここまでやればdeviseまわりの表現も全部多言語対応できたんじゃないですかね。。
波打際のブログさん「Ruby on RailsのI18nで使用する名前空間に関してのまとめと、ベストプラクティスの検討。」
翻訳定義するときに変数を使いたい時がある。 とくに日本語と英語で語順が変わるときとか。
= t('.hoge'), keyword: xxxx
あとは定義した翻訳内容にHTMLタグを入れるというやつ。
あんまりよくないけど、ちょっと改行だけ入れたかったりとか…あるじゃないですか。
とふつうに呼び出してやればオッケー。ちゃんとHTMLタグとして出力されます。
というわけで意外と大変やったけどだいぶ満足いく感じで多言語対応できました。
せっかく作ったので英語でプレスリリース打ったりしてみますかな。。