- 更新日: 2014年4月6日
- Rails
RailsでGeoIPのIPアドレスからロケール判定する際の注意点
Rails の I18n による多言語化でユーザーのロケール判定のために、GeoIP というライブラリを利用していたのですが、自分が書いたコードにバグを発見して修正しました。GeoIP の導入や I18n 多言語化についての詳細は以下のエントリーを参照。
Rails4 で i18n で英語化(多言語化) | EasyRamble
日本語と英語を利用できるようにする前提
まず、前提としてロケールのデフォルトは日本語、利用できる言語は日本語と英語としています。application.rb に以下のように設定している場合です。
config/application.rb
1 2 |
config.i18n.default_locale = :ja config.i18n.available_locales = [:ja, :en] |
GeoIP#country(ip_address_in_japan).country_code2 が “JP” を返す
IPアドレスから国コードを判定するコードとして、次のようなコードを書いていたのですが、結論から言うとここの locale_from_ip メソッドがバグを含んでいました。
app/controllers/application.rb
1 2 3 4 5 6 7 8 9 10 11 12 |
def set_locale locale = params_locale || user_locale || locale_from_accept_language || locale_from_ip I18n.locale = (I18n.available_locales.include? locale.to_s.to_sym) ? locale.to_s.to_sym : I18n.default_locale end ... def locale_from_ip geoip ||= GeoIP.new(Rails.root.join( "db/GeoIP.dat" )) country = geoip.country(request.remote_ip) country.country_code2.downcase end |
set_locale は before_action に設定して、全てのコントローラーのアクション前に実行され、ロケールがセットされるようにしています。
locale_from_ip メソッドに対して日本のIPアドレスを渡して “ja” が返されるべき、という内容のテストを書いて判明したのですが、日本の国コードを表す文字列として “jp” を返されます。以下、コンソールの pry でも試しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
pry(main)> geoip ||= GeoIP.new(Rails.root.join( "db/GeoIP.dat" )) => #<GeoIP:0x007f911195ad18 @database_segments=[16776960], @database_type=1, @file=#<File:/path/to/rails_root/db/GeoIP.dat>, @flags=0, @mutex=#<Mutex:0x007f911195acf0>, @record_length=3> pry(main)> country = geoip.country("123.456.789.123") => #<struct GeoIP::Country request="123.456.789.123", ip="123.456.789.123", country_code=111, country_code2="JP", country_code3="JPN", country_name="Japan", continent_code="AS"> [11] pry(main)> code = country.country_code2.downcase => "jp" |
geoip.country(“123.456.789.123″) のIPアドレス指定の部分の、”123.456.789.123″ は自分のプロバイダのIPアドレス(日本のもの)で試しました。
以上のように国コードは、”jp” という文字列が返されるので注意が必要です。I18n.locale では通常日本語のロケール用に :ja, “ja” を使うはずですので。翻訳用の yml ファイルも ja で作成します。
locale_from_ip メソッドを修正
これを踏まえて、以下のように locale_from_ip メソッドを修正しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def locale_from_ip locale_from_ip = nil geoip ||= GeoIP.new(Rails.root.join( "db/GeoIP.dat" )) country = geoip.country(request.remote_ip) code = country.country_code2.downcase if code == "jp" locale_from_ip = "ja" elsif code.present? && code != "--" locale_from_ip = "en" end locale_from_ip end |
取得した国コード( GeoIP#country(ip_address).country_code2.downcase )が、”jp” の場合は locale_from_ip が “ja” を返すように修正しました。
また色々調べたら、国コードが取得できなかった場合は “–” を返すことが分かったので、elsif 節を書いて、国コードが取得できてかつ “–” じゃなかった場合は、ロケールとして “en” を返すようにしました。国コードが “jp” じゃない場合に nil を返したければ、elsif 節を削除すれば良いです。
- Rails の関連記事
- RailsでMySQLパーティショニングのマイグレーション
- Rails ActiveRecordでdatetime型カラムのGROUP BY集計にタイムゾーンを考慮する
- RailsプラグインGemの作成方法、RSpecテストまで含めたrails pluginの作り方
- RailsでAMPに対応するgemをリリースしました
- Railsでrequest.urlとrequest.original_urlの違い
- Railsでwheneverによるcronバッチ処理
- Google AnalyticsのRails Turbolinks対応
- Railsアプリにソーシャル・シェアボタンを簡単設置
- Rails監視ツール用にErrbitをHerokuで運用
- Facebook APIバージョンのアップグレード手順(Rails OmniAuth)
Leave Your Message!