こんにちは、バックエンドエンジニアのグッチです。
2019年にもなったことだし、今日はWebサービスを作った思い出を振り返りながら、多言語・多通貨・複数国対応のバッドプラクティスについて書いてみようと思います。
Webサービス・アプリなどを仕事や趣味で作って(作ろうとして)いる方の参考になれば嬉しいです。実際に自分のサービスが全世界で使われるサービスになるかはさておき、いつか来るかもしれないその日に向けて、考えてみる機会にしていただけたら……。
▼最近のご飯
目次
Webサービスの海外展開って結構大変
日ごろWebサイト/サービス/アプリを使っていて、こーゆーの見たことありますよね?
- 海外のサイトなはずなのになぜか初めから日本語で表示される
- 言語設定から様々な言語へ変更できる
何気なく使っているかもしれませんが、裏側では国際化(i18n: Internationalization)と呼ばれる仕組みを使って、エンジニアやデザイナーが頑張って書いているんです。
doorの場合
私は主にdoorというSNSマーケティングプラットフォームの開発を行なっています。SNS上でフォロワーを多く抱えるインフルエンサーと、宣伝して欲しい自社製品やイベントがある企業とをマッチングさせるサービスです。
スポンサー視点だと、
- 今度こんなイベントをやります。招待するので遊びにきて感想を投稿して欲しい
- こんな商品を作ったので、使った感想を投稿して欲しい
- LIGブログの企画に出てくれる方募集
など、いろいろな種類の依頼が可能です。
またインフルエンサー視点だと、
- 1いいねや1投稿を基準に報酬を貰えたり
- 限定イベントに参加できたり
- 商品が貰えたり
- 商品やご飯が安く手に入ったり
といったメリットがあります。そのほか詳細は下記の記事をご覧ください。
LIGが新サービス「door」をリリース! インフルエンサーへの拡散依頼が簡単に!
構成
構成はざっくりとこんな感じ。
開発言語
- Ruby on Rails
- ユーザー認証:devise
- admin:active_admin
- …
- coffee script/javascript
- jQuery
- …
- scss
- Bourbon
- Neat
- Bitters
開発環境
- docker-compose(開発開始時はvagrant)
インフラ
- GKE(Google Kubernetes Engine)(今はGAEへの乗り換えを検討中)
CI
- GCB(Google cloud build)(CircleCI、Jenkinsを経てひとまず落ち着いた感)
これまでの開発の流れ
開発開始
スケジュールもけっこうキツキツだったので、優先度の低いところはリリース後の機能拡張で対応するということにして「とりあえず早く」をモットーに開発。
日本版サービス開始
徐々にユーザー数も増えるのを横目でみつつ、UI/UX改善とほんとは欲しかった機能を並行して実装。このころはサービスの性質上、海外展開はしばらくないだろうと思っていました。
多言語化は突然に
ところが、ある日偉い人から突然こう言われました。
「T国でこのサービス展開したい。英語にして」
- ダメポイント
-
- 早さを優先するあまりHTMLに日本語を直接書いて作っていた
- 多言語化の準備なんてしてなかった
海外版リリース
そこで以下のような実装を行い、なんとかリリースしました。
- HTML上の全テキストの多言語化
- JSで表示するテキストの多言語化
- メールの多言語化
- 時差を考慮した時間表示へ修正
- Batch処理のタイミング修正
- 国に応じた決済サービスの出し分け
が、国際情勢的なタイミングの問題でプレスリリースやCMを打ったりすることが難しく、口コミなどで少しユーザーは増えたものの、結局はほとんど動きませんでした(かなしいね)。
サービス展開国の追加
そして、またある日、
「V国でもやりたい。V語追加して」
- ダメポイント
-
- そのころ日本側では超大型アップデートを進めていたため、新規言語追加に伴うデグレがとても怖かった
「運営もV国側でやってくれるって!」
- ダメポイント
-
- 社員しか触らないと思って管理画面を突貫で作っていたため、権限レベルや閲覧可能なデータの範囲なんかもふわっとしていた
- 日本人しか使わないと思って管理画面は多言語化していなかった
「V国側のパートナー企業のエンジニアも一緒に開発してくれるって!」
- ダメポイント
-
- 社外のメンバーが開発に参加することを想定していなかった(secretとか)
サービス国追加のリリース
なんやかんやあって実装も終わり、リリースはできたものの……いろいろと大変でした。
- 決済サービス追加
- 管理画面の多言語化
- V国用のカスタマイズ
- V国側を隔離するためにサーバー群構築
- 日本側で同時期に行なっていた超大型アップデートによるデグレ回避
リリース後に寄せられたけど重すぎて断念した意見もいろいろありました。
- ダメポイント
-
- 国・言語・通貨が一連になっているため、「日本版を英語やV国語で使いたい」ができない
- 「日本のアカウントから海外へ依頼を投げたい(またはその逆)」といった地域をまたぐ依頼は考慮していなかった
ここには書けないけど、その国の文化に根ざした意見の食い違いもありました。
結果
できたこと
- 日本・T国・V国の3ヶ国へサービス提供
- 日本語・英語・T国語の3ヶ国語サポート
- 居住国に応じた決済サービス切り替え
各フェーズでの要件定義は満たせてはいるものの、将来的な要望までは手が回らず、大改修して作り直したい気持ちでいっぱいです。
犠牲になったもの
- メンテナンス性
- テスト大変
なにを基準に、なにを変えるのかをもう少し考えよう
世界中の人が自分のWebサービスにアクセスすることを想定する場合、表示上の注意点はなんでしょうか?
言語・時間などはパッと思いつきますね。
多くのフレームワークでよく見る多言語化(i18n)は「表示する言語を変える」という目的は満たせますが、実際にはもう少し考える必要がありました。
使用するフレームワークによって時間の取り扱いもi18nに含まれたり、含まれなかったりすることがあるので注意しなければなりません。また場合によってはi18nとl10n(localization:地域化)を併用する必要なども出てくるので気をつけましょう。
コンテンツを出し分ける必要があるかどうか考える
これは、「Webサービスがユーザーになにを提供したいか」によっても変わってきます。
国も地域も時間も関係なく同じコンテンツを見せる
表示する言語が違うだけというパターンの場合、同じコンテンツを出せばいいので素直に多言語化するだけですが、実際にはそうはいかない方が多いでしょう。
まず考えるとすればこれくらい。
- 初めて来たユーザーに表示する言語をどう判断するか
- 言語の選択UI
- 言語設定の持ち方(DBなのかcookieなのか)
居住国・地域の情報をユーザーの言語で表示する
コンテンツを出し分ける場合には、追加でいろいろ考える必要があります。
- なにを基準にコンテンツを出し分けるのか(例, 国・地域
- その基準によってインフラを分けるのか
- その基準によってURLを変えるのか
- …
doorでは、
- 「V国では運営チームが違うこと」・「将来的にパートナー会社に渡すこと」を視野に入れ、インフラは全て隔離
- ドメイン別でインフラ(=運営会社)出し分け
- URLに国コードをいれて国を出し分け
という形に落ち着きました。
国によって決まるもの・言語によって決まるものを混同しない
多言語化(i18n)の際は言語を変えますが、Webサービスの多地域展開を考える上では言語ではなく国で整理した方がわかりやすくなります。
おそらくもっとたくさんあるんだとは思いますがdoorでハマったポイントを並べてみます。
言語によって決まるもの
- 氏名の順序
国によって決まるもの
- 電話番号の桁数
- 郵便番号の桁数
- 桁区切り・小数点の取り扱い(「1,234.56」なのか「1.234,56」なのかその他なのか
- 都道府県や州などの地域
- 外部サービスのシェア(決済サービスとか)
- SNSのシェア(Facebook優勢だったり、Instagram優勢だったり
国によって決まるとは限らないもの
- 言語:公用語が複数ある国もあるし、海外から来て働いている人や旅行者が使う場合もある
- 通貨:現地通貨と米ドルの両方が流通しているような場合がある
- 時差:同じ国でもTime Zoneが違う場合がある
言語によって決まるものって案外少なくて、実は国によって決まるものの方が多いんじゃないかなと思ってます。言語によらないデータまで言語に紐づけてしまうと、後から大変になるので注意しましょう。
doorを通して学んだこと
早すぎる最適化問題
最適化していい場所かどうか判断がつく前にシンプルに実装したことで後から泣きたくなる問題。
海外展開しないだろうと思ってHTMLに日本語書いたりするあれですね。
doorでは失敗したなぁと思うこともたくさんありますが、多言語化まわりで思うのは「国・言語・通貨を1:1:1という関係性にしてしまった」ことかもしれません。
これは当時の開発メンバーも社内の誰もが気づかずに進んだため、「英語圏の人が日本版で英語を使う」といったことができなくなりました。今からそこを変えるのは重いので、ずっと保留になっています。
ほかにも気になるところはあるので綺麗にしたいなぁ。
早すぎる拡張性問題
将来のためを考えてそのための拡張性を保って実装したせいで、日々の実装が大変になったりメンテナンス性が悪くなることがある問題。
当初、「同一アカウントでスポンサー側にもインフルエンサー側にもなれ、スポンサーには複数のユーザーが所属し、ひとりのユーザーが複数のスポンサーに所属できる」ような構造にするという設計でした。
しかし、ビジネスロジック側の理由から大きな機能変更が入り、その辺をだいぶ簡略化して実装することになりました。
「でも将来的には当初の構造にしたいよね」という声も多く、データ構造的には当初の実装が可能な形を維持しつつ、簡略化された方法で実装しました。ユーザー周りのアソシエーションが複雑になったり、deviseでのユーザーの管理が複雑になり普段の実装がほんの少しめんどくさくなりましたが、「将来のために」と思ってそのまま頑張りました。
時間がたち、当初の仕様へアップグレードするという夢は今さら手をつけるには負荷が大きすぎる状態になり、実現するにはかなりめんどくさ……難しいタスクと言わざるを得ない状況です。
拡張性を高くしようと思ったことで、かえって不幸になることもあります。何事も適度なバランスが大切ですね。
まとめ
結局のところ、どんなに綿密に練った要件で実装してもビジネス上の理由やその他なんやかんやの理由で「え、今それ言うの?? あのときこうしておけば。。。」となることは避けられないことかもしれません。日々それなりに頑張っていくしかないですね。
早すぎる最適化と早すぎる拡張性の確保の間で、バランスをとりながら生きていきましょう。グッチでした。
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。