サーバーサイドエンジニアの趙(じゃお)です。
PlanBのフロントエンドとサーバーサイドを担当しています。

PlanBは「あなたの時間を最高にするCtoCのダイニングコンシェルジュサービス」です。

アレンジの価格帯は500円~で、街をよく知るコンシェルジュたちが飲食店選びから予約までを行い、ゲストのシチュエーションにあったアレンジをすることで唯一の体験を提供します。

このPlanBですが、リリース当初は日本語のみに対応していました。
しかし、海外市場やインバウンド市場への展開を視野に入れ、国際化対応版をリリースしました。

この記事では、PlanBの国際化にあたり行なった対応をまとめています。

言語ごとにURLを分ける

URLの分け方はplnb.jp/(:locale)/path/toにしました。

この分け方を選んだ理由は、

  • SEOフレンドリーである
  • ユーザーはURLからどの言語を使っているか判断できる
  • Routeコンフィグファイルだけを変更すれば簡単に実現できる

の3つです。

ユーザーのロケール設定

ユーザーに表示する言語を決定する順番は以下の通りです。

  1. URLの中のlocale変数
  2. Cookieで保存された言語
  3. ブラウザーのAccept-Language設定
  4. デフォルトロケール(日本語)

Railsのdefault_url_optionsメソッドで、サイト内すべてのリンクにロケール変数を入れました。

def default_url_options(options = {})
  {locale: I18n.locale}.merge(options)
end

そのため、locale変数の順位を最優先にします。

また、ユーザーが言語を変える時、その言語をCookieに保存します。
目的はユーザーがplnb.jp/path/toのようなURL(特にホームのplnb.jp)を訪問する時、もともと使っていた言語で表示させるためです。そのためCookieを第二順位にしました。

次のAccept-Languageによる設定は、ユーザーが初めてplnb.jpを訪問するための仕組みです。

もしAccept-Languageの検出が失敗したらデフォルトロケールである日本語で表示します。

データベースの翻訳

コンシェルジュの自己紹介、得意料理ジャンル名など、データベースに保存するデータの多言語化はGlobalizeというgemを使いました。

翻訳テーブルを作るマイグレーションファイルは以下の通りです。

class TranslateAreas < ActiveRecord::Migration
  def up
    Area.create_translation_table!({
      name: :string
    }, {
      migrate_data: true
    })
  end

  def down
    Area.drop_translation_table!(migrate_data: true)
  end
end

ActiveRecordクラスは以下の通りです。

class Area < ActiveRecord::Base
  translates :name
  default_scope { eager_load(:translations) }
  # ...
end

default_scope { eager_load(:translations) }により、翻訳テーブルのN+1ロードを回避することができます。

レビューの自動翻訳

レビューの自動翻訳にMicrosoftのTranslator APIを使いました。
このAPIは2,000,000文字/月までは無料で使うことが出来ます。

ユーザーがレビューを書くとき、ユーザーが使う言語以外の言語に自動翻訳を行います。

class Review < ActiveRecord::Base
  translates :content
  globalize_accessors
  default_scope { eager_load(:translations) }
  before_validation :translate_content
  #...
  def translate_content
    self.origin_language = I18n.locale
    (I18n.available_locales - [I18n.locale]).each do |trans_to|
      # APIにより自動翻訳
      self.send("content_#{trans_to}=", translation)
    end
  end
end

タイムゾーンに沿った時刻の表示

PlanBは、コンシェルジュがユーザーの要望によってレストランを提案し、予約してくれるサービスです。
ユーザーとコンシェルジュのやり取りはメッセージで行います。
各メッセージには送信時間が表示されるため、この時間はユーザーがいる場所のタイムゾーンに沿って表示する必要があります。

タイムゾーンを判断する方法はいくつかありますが、PlanBではユーザーのIPアドレスによりタイムゾーンを判断しています。
具体的にはgeoipというgemと、無料のGeoLiteデーターベースを使っています。

GEO_IP = GeoIP.new(Rails.root.join("db", "GeoLiteCity.dat"))
@timezone = GEO_IP.city(request.remote_ip).try(:timezone) || "Unknown"

現地通貨での価格表示

PlanBの決済は日本円ですが、参考価格としてユーザーロケールに沿った現地の通貨を表示しています。

実装は以下の通りです。

まず、前述のgeoipで国を判定します。

country = GEO_IP.country(request.remote_ip).country_code2

次に、geoipで判定した国により、通貨を判定します。

currency = Settings.country_currency[country]

最後に、Redisに保存する変換レートをOpen Exchange Ratesを使って取得しています。

rate = REDIS.hget("country_currency_rate", country).to_f

まとめ

PlanBの国際化対応にあたり、下記に挙げる実装を行ないました。

  • 言語ごとにURLを分ける
  • ユーザーのロケール設定
  • データベースの翻訳
  • レビューの自動翻訳
  • タイムゾーンに沿った時刻の表示
  • 現地通貨での価格表示

国際化対応には様々な方法があり、その時々で最適な方法は異なりますが、PlanBで行った対応方法が何かの参考になれば幸いです。

PlanBは、あなたの時間を最高にするダイニングコンシェルジュです。
記念日や会食はもちろん、普段とちょっと違う時間を過ごしたい時にご活用ください。
TwitterFacebookオフィシャルブログも要チェックです。