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

NOT SO BADなブログ

Towards a not so bad world.

Rails4でi18nの多言語対応やったときのハマりどころとかまとめ。

技術系

絶賛運営中のトーナメント表作成サービス「THE TOURNAMENT(ザ・トーナメント)」。
今回はRails多言語対応のi18nをつかって英語版に切替れるようにしてみました。

i18n対応でいろいろハマったりしたところのまとめです。

まずは基本的なやり方とか

i18nの使い方については公式ドキュメントに詳しく載ってます。困ったときはちゃんと読めば書いてある。

RailsGuides「Rails Internationalization (I18n) API

あとはいつものようにmorizyunさんのブログも参考にさせてもらいました。

酒と泪とRubyとRailsと「Railsの多言語化対応 I18nのやり方を整理してみた!【国際化/英語化】

まずはこのあたりを読み込んでその通りにやっていけば基本的な設定はできるはず。
以下さらに詳細な設定やハマったところとかのまとめです。

shallow routingつかってるとlocaleのscope設定でハマる

まずはこれですね。そもそも他言語対応したときにURLをどうするかという問題があって、

とかって選択肢があります。

同一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の設定

タイトルとかキーワードとか翻訳するのはまぁ当然として、多言語対応時に必要になるのがalternateというやつ。
これは現在のページに他言語版があれば対応するURLを指定してあげるという感じです。

kpumuk/meta-tags「Multi-regional and multilingual URLs

いったん日本語・英語の2つなので、

# _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を他言語版で取得する

しかしここで指定したい「現在のページ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まわりの翻訳

日本語辞書ファイルを配置

ユーザー認証にdeviseつかってますが、登録時のflashメッセージ表示とか、
いろいろデフォルトの文言が英語で用意されてるのでこれも翻訳する必要があります。

量が多いので大変ですが、Gistで日本語辞書ファイルが提供されているのでありがたく使わせていただきます。

Gist「kawamoto / devise.ja.yml

このファイルを

config/locale/devise.ja.yml

として配置します(同じとこにdevise.en.ymlがもともとあるはず)。
これで各種文言が日本語化されました。便利!

deviseのviewファイルを翻訳

ただこのデフォルト辞書ファイルだけでは完璧じゃなくて、ログイン画面とかのviewファイルで
自分が作った部分は当然別途翻訳を用意する必要があります。

基本はふつうの画面の翻訳と同じなんですが、上記の階層化のときにdevise用の対応が必要だったのでちょっとハマる。

# config/locale/views/devise/ja.yml
ja:
  devise:
    sessions:
      new:
        hoge: ほげほげ

こんな感じでいつものcontroller, actionの前にdevise:を入れる必要があるみたいです。

Simple formのラベル表示も翻訳する

しかしさらにまだやることは残ってて、これだけだとログインなどの画面で表示される入力項目のラベルが
デフォルトの英語のままになってるはず。

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まわりの表現も全部多言語対応できたんじゃないですかね。。

辞書定義のtips

あとは知らなかった細かいTipsとか。

波打際のブログさん「Ruby on RailsのI18nで使用する名前空間に関してのまとめと、ベストプラクティスの検討。

ここを大変参考にさせていただきました。

変数を使う

翻訳定義するときに変数を使いたい時がある。 とくに日本語と英語で語順が変わるときとか。

これは辞書ファイルで

hoge: "ほげほげ%{keyword}ほげ"

って感じで変数を埋め込んで、view側で

= t('.hoge'), keyword: xxxx

って変数を渡す感じですね。

HTMLタグを使う

あとは定義した翻訳内容にHTMLタグを入れるというやつ。
あんまりよくないけど、ちょっと改行だけ入れたかったりとか…あるじゃないですか。

これは辞書側で

hoge_html: ほげ<br>ほげ

と定義名にxx_htmlをつけて、

= t('.hoge_html')

とふつうに呼び出してやればオッケー。ちゃんとHTMLタグとして出力されます。

まぁあんまり使わんにこしたことはないけど。。

というわけで意外と大変やったけどだいぶ満足いく感じで多言語対応できました。
せっかく作ったので英語でプレスリリース打ったりしてみますかな。。