海外事業向けのiOSアプリケーション開発を担当している西山(@yuseinishiyama)です。クックパッドは現在、海外複数カ国に向けてサービスを展開しています。海外事業向けのiOSアプリケーションは、英語、スペイン語、インドネシア語、タイ語、ベトナム語、アラビア語をサポートしています。今後、サポートする言語は更に増えていく予定です。これまで、複数の言語に対応するための国際化(internationalization)と地域化(localization)を行ってきました。ここでは、その中で得た知見を以下の4つのパートに分けて共有したいと思います。翻訳に関するTipsちなみに、当該プロジェクトがサポートしているiOSバージョンはiOS8以上です。そのため、iOS9以降でしかサポートされない機能については触れません。我々のサービスの性質上、精度の高いローカライゼーションを実現する必要があり、標準的な方法に背いた強引な方法を取っている箇所がいくつかあります。全てのサービスで同様の対応が必要とは思いません。1つの事例として参考にしてください。以下、「Cookpadアプリ」は海外事業向けのアプリケーションのことを指します。本節では、どのような思想に基いて、CookpadアプリがコンテンツとUIの言語を決定しているかについて説明します。Cookpadアプリおける「言語」の意味Cookpadアプリにおいて、「言語」は以下の2つの意味を持ちます。コンテンツの言語UIの言語UIの言語は基本的には翻訳に関わる問題です。一方で、コンテンツの言語はそうではありません。それぞれのレシピは、特定の言語圏のユーザーがその言語で投稿したレシピであるため、言語によってコンテンツそのものが異なります。スペイン語圏のユーザーがインドネシア語のコンテンツを見たとしても、ほとんど意味を成しません。理解できないだけでなく、地域特有の材料などが手に入らないため、内容も役に立たないからです。次の画像は、あるインドネシア語のレシピをアプリで表示している様子です。本節では、Cookpadアプリがどのようにコンテンツの言語とUIの言語を決定しているかについて説明します。コンテンツの言語を決定する現在のCookpadアプリでは1つのアカウントは1つのコンテンツの言語に結びつく仕様となっています*1。これは、前述の理由から、1人のユーザーが複数の言語を跨いでコンテンツを閲覧する可能性は低いと考えているためです。そのため、適切なコンテンツの言語が最初に決定される必要があります。本項では、いかにしてコンテンツの言語を決定するかについて説明します。デバイスの言語やリージョンの設定は信頼できない適切なコンテンツの言語を選択する上で、まず参考になりそうなのが言語やリージョンの設定です。Regionからアクセスできる項目のことを指します。アプリで使用される言語は以下の手順で決定されます。Languagesの先頭の言語にアプリが対応しているか調べる。対応していれば、その言語を使用する。Languages内の次の言語にアプリが対応しているか調べる。対応していれば、その言語を利用する。これを、対応している言語が見つかるまで繰り返す。Languages内にアプリがサポートしている言語が見つからなければ、アプリのデフォルトの言語が使用される。Languagesの先頭の言語を取得しています。ちなみに、NSLocale.preferredLanguages()が返す値は、iOS9から変更されました。iOS8までは、["en"]のような値を返していましたが、iOS9からは、["en-GB"]というように言語とリージョンの組み合わせを返却するようになりました。NSLocale.preferredLanguages().first?.componentsSeparatedByString("-").firstところで、言語やリージョンの設定の値は信用できるものなのでしょうか。全てのユーザーが自分にとって適切な値に設定しているのでしょうか。各地のメンバーと調査した結果、言語やリージョンの設定があまりあてにならないということが分かりました。インドネシアでは多くのユーザーが言語設定を英語のまま使っていることが判明しました*2。端末の流通経路によっては、デフォルトの言語やリージョンの設定が適切とは限らず、また、それらの変更方法を知らないユーザーも多くいるためでしょう。そのため、コンテンツの言語の選択にデバイスの言語やリージョンの設定をそのまま適用することはせず、ユーザーがアプリ内でコンテンツの言語を明示的に選択する仕様となりました。次のgifアニメはアプリ内で言語を選択している様子です。選択した言語にあわせて、UIの言語やレイアウトも動的に変更されています。アラビア語選択時は戻るボタンの位置が右側になっています。これらの挙動の実現方法については後述します。コンテンツの言語を予測するユーザーが任意で言語を選択できるとはいえ、誤った言語が選択されないように配慮するべきです。そこで、適切なコンテンツの言語を予測し、それがデフォルトで選択されるようにしました。先述の通り、デバイスの言語設定はあまり信頼できるものではありません。一方で、SIMから取得できる国情報はより信頼できるものであるはずです。Code)から得られる国コードを、言語設定や地域設定より優先して使用するようにしました。次の例では、SIMから国コードを取得しています。3166-1で表現された文字列です。}}この値が取得できなかった時に、初めて言語設定や地域設定を参照します。次のCountryCodePredictor型のinferredISOCountryCode()メソッドは、予測された国コードを返却しますが、まずSIMから得た国コードを参照し、次に地域設定から取得できる国コードを参照しています。}}地域に応じてコンテンツを最適化するちなみに、取得した国コードはヘッダを通じてAPIにも送っています。これは地域によって、検索結果などを最適化するためです。同じスペイン語圏であっても、スペインとメキシコではもちろん料理が異なります。アクセス先の地域の料理が優先的に表示されるようにしています。次の画像のうち、1つ目はスペインからの、2つ目はメキシコからの検索結果の例です。UIの言語を決定する既に述べたとおり、コンテンツの言語とUIの言語は別の概念です。そのため、コンテンツとUIで別の言語を使用することも可能です。Cookpadアプリではユーザーが選んだコンテンツの言語にUIの言語も合わせるという方針を取ることにしました。本項では、そのような方針に至るまでの経緯と、その実現方法について説明します。UIの言語をコンテンツの言語に一致させるUIの言語を決定する上で、Appleの思想に則った最も適切な方針はデバイスの言語設定を尊重することでしょう。先ほどの、言語とリージョンの設定画面にも次のような説明文があります。support.しかし、既に述べた通り、デバイスの言語設定は適切とは限りません。母国語でない言語を理解できるユーザーや、言語が理解できなくとも操作方法を推察することができるユーザーも多数存在するでしょう。「世界中の人々に向けて毎日の料理を楽しみにするサービスを提供していく」という我々のミッションの性質上、このようなユーザー以外の人々の課題も解決しなければいけません。UIの言語もユーザーが「確実に」理解できるものであるべきです。ここまでで説明したように、コンテンツの言語はユーザーが明示的に選択しているため、ユーザーが理解可能な言語であることはほぼ間違いありません。そこで、UIの言語をコンテンツの言語と一致させることにしました。UIの言語を動的に切り替えるローカライズされた文字列を取得するには、NSLocalizedString(_:comment:)関数を使用します。第1引数に指定したキーに対応する、ローカライズされた文字列を取得することができます。"comment")しかし、NSLocalizedString(_:comment:)関数はデバイスの言語設定を参照して、どの言語のリソースファイルを使用するかを決定するため、アプリ内で言語スイッチを持つ場合に、これをそのまま使用することはできません。あり得る手段の1つとしては、実行時にアプリの言語を強制的に上書きするというものです。"AppleLanguages")NSUserDefaults.standardUserDefaults().synchronize()しかし、この方法にはアプリの再起動が必要であるという致命的な欠点があります。Internationalization"でも次のように説明されています。apps利用開始後、早々にアプリから再起動を促されるという体験はユーザーに与える印象をかなり悪くすると思われます。そこで、NSLocalizedString(_:comment:)関数をラップした、任意のリソースファイルから文字列を取得する関数を実装しました。ここで、FoundationのNSLocalizedString(_:comment:)関数と重複するにも関わらず、アプリ本体の名前空間に同名の関数を定義している理由は後述します。comment)}ところで、このようにリソースファイルへのパスをアプリ内で組み立てることをAppleは推奨していません。Internationalization"では次のように説明されています。yourself同様のことを行う場合は、非推奨の方法であるということに留意しましょう。本節では、CookpadアプリのRTL対応について説明します。Left)といいます。RTLの言語を母語とするユーザーは5億人ほどいると言われています。そのためアプリケーションをRTLに対応させることは、ユーザーの獲得という観点から見て非常に重要です。Cookpadアプリはアラビア語に対応しているので、RTLの対応は必須でした。AutoLayoutによるRTL対応AutoLayoutを利用していれば、RTLに対応することは難しくありません。制約を設定する際のNSLayoutAttribute型の値として、.Left、.Rightではなく、.Leading、.Trailingを指定するだけで大部分がRTLに対応済みとなります。LTR環境において、.Leadingは左を、.Trailingは右を指しますが、RTL環境では、.Leadingが右を、.Trailingが左を指すようになります。directionという項目にチェックをいれます。手動でRTLに対応するAutoLayoutはデバイスの言語設定を参照してレイアウトの方向を決定します。AutoLayoutによるRTL対応は、デバイスの言語がRTLでなければ機能しません。先述の通り、Cookpadアプリは言語スイッチをアプリ内に持っているため、デバイスの言語はRTLではないが、ユーザーが選択した言語はRTLであるという可能性があります。結果として、AutoLayoutに頼らない手動のRTLを実装することになりました。LTRデバイスの言語を明示的にRTLにしているユーザーが、LTRの言語を選択するのはレアケースと考えています。そのため、今のところ、RTLを強制的にLTRにすることはしていません。要素を反転させる手動でRTL対応を行うため、UIViewクラスのtransformプロパティを使用して、ビューを反転させることにしました。以下のようにすると、ビューは左右反転します。UIViewControllerクラスのviewプロパティの値に対して同様の操作を行えば、画面上の要素の左右の順序が入れ替わります。1)次の画像のうち、1つ目はオリジナルのもの、2つ目はルートビューを反転させたものです。レイアウトの方向がRTLになっていることが分かります。画像を見ても明らかな通り、テキストや画像も左右に反転してしまいます。これらの要素は、個別に、もう一度反転させることで適切な状態になります。機能追加毎に6つの言語への翻訳を行うコストは決して少なくはありません。適切な翻訳フローを設けなければ、翻訳のためのタスクがブロックとなり、スムーズな開発を妨げる可能性があります。本節では、Cookpadアプリの翻訳がどのようなフローに則って行われているかについて説明します。翻訳は各地のメンバーが行っています。PR毎に全ての翻訳を完了させるということも可能ですが、時差の都合上、翻訳待ちの状態のPRが多くなりがちです。そこで、各トピックブランチでは英語の文言を設定するだけにしました。developブランチには英語の文言だけが存在する変更がマージされ、リリースブランチ上で残りの言語の翻訳を行います。このようなフローを取るためにブランチモデルは、git-flowを採用しました。本項では、リリースブランチでの翻訳作業について説明します。Format)ファイルをエクスポートします。6からサポートされるようになりました。</file></xliff>このファイルはGUI上からもエクスポートすることができますが、xcodebuildを利用すれば、コマンドラインからもエクスポート可能です。自動化のタスクに組み込むなどすると良いでしょう。<プロジェクト名>ちなみに、エクスポートする際に、Xcodeはソースコードを解析して、存在するNSLocalizedString(_:comment:)のキーを確認します。この解析時に、XcodeはNSLocalizedStringという文字列を探して、その後に続く文字列リテラルからキーを取得しているようです。先ほど、NSLocalizedString(_:comment:)のラッパーの関数名に、そのままNSLocalizedStringを使っていたのはこのためです。次のように別名の関数を用意していた場合、エクスポートは上手く機能しません。"comment")OneSkyを用いた翻訳このプロジェクトでは、全プラットフォームで翻訳作業にOneSkyというサービスを利用しています。OneSkyはXLIFFフォーマットをサポートしているため、OneSkyに上記の手順で生成したベースとなる言語ののXLIFFファイルをアップロードすることができます。アップロード作業も公式のクライアントを用いれば、コマンドライン上から行うことができます。アップロードすると、文言が翻訳可能な状態となるので、後は各地のメンバーに翻訳作業を依頼します。OneSky内で翻訳について翻訳者と議論したり、別のプラットフォームで使用している文言を参照したり、翻訳の進捗状況を視覚的に確認することもできます。OneSky内で翻訳を外注することもできますが、我々はあくまでツールとして利用し、翻訳自体は各地のメンバーが行っています。単なる翻訳ではなく、サービスの思想を汲んだ上で適切な意訳を行うことができるということが、翻訳を外注しないことの最大のメリットでしょう。インポート翻訳が完了したら、OneSkyから全ての言語用のXLIFFファイルをダウンロードしてプロジェクトにインポートします。Cookpadアプリの場合は、6つの言語をサポートしているので、6つのXLIFFファイルをインポートすることになります。エクスポートと同じく、インポートもGUI、コマンドラインの両方から行えます。<プロジェクト名>翻訳に関するTips本節では翻訳に関するTipsについて説明します。適切なNSLocalizedStringのキーを設定するNSLocalizedString(_:comment:)のキーとして設定する値は非常に重要です。適切なキーを設定しなければ、あっという間に管理不能な状態になります。次のような、キーと値の組み合わせは良くありません。"latest"のような似通った表現が出てきた場合に、どこでどちらが使われているのか分からなくなる可能性があります。英語において、全て"new"と表現することができる箇所が、他の言語でも1つの用語で表現できるとは限りません。"new";キーは常にコンテキストが分かるような値にしましょう。同じ単語であっても、コンテキストが異なれば、別のものとして定義するべきです。"new";変数の順序に気をつける書式に変数を埋め込む形の翻訳対象の文字列があったとします。%@";このように複数の変数がある場合、必ずしも他の言語でも上手く機能するとは限りません。これをそのままドイツ語にすると次のようになりますが、これは誤りです。ドイツ語では1番目の変数と2番目の変数の順序が逆である必要があります。kopieren";この問題を回避するために、書式中の変数に対して、順序を指定することができます。kopieren";デバッグ本項ではデバッグに関するTipsを紹介します。Pseudolanguage言語が増えれば増えるほど、文字列の長さを予測することは難しくなります。ある言語では1行で収まるような文言でも、他の言語では複数行となるといったようなことは頻繁に起こります。常に想定より長い文字列を表示できるようなレイアウトを組むべきです。Pseudolanguage」というデバッグオプションを使用すれば、全てのローカライズされた文字列が2回繰り返される状態を再現できます。これによって、想定より長い文字列が入った場合にレイアウトが崩れるケースを検出できるかもしれません。Recipe"などの翻訳対象の文字列が全て2回繰り返されており、ここから想定より長い文字列を表示すると一部のレイアウトが崩れることが検出できます。Pseudolanguage」というデバッグオプションを使用すれば、擬似的なRTLの状態を再現することが可能です。RTLの言語をサポートしていないアプリでも、RTLになった際にどのようなレイアウトになるか確認することができます。次の例では、言語は英語ですが、レイアウトはRTLになっています。NSShowNonLocalizedStringsNSShowNonLocalizedStringsを起動時のオプションに指定すれば、未翻訳の文字列のキーが大文字で表示されます。次の例では、検索バー内のプレースホルダーが未翻訳のため、そのキーが大文字で表示されています。この結果、デバッグ時に翻訳忘れに気付きやすくなります。おわりに本記事では、CookpadアプリにおけるiOSアプリケーションの国際化と地域化について説明しました。言語毎にコンテンツやアカウントが異なるサービスというのは珍しく無いはずです。これをきっかけに、iOSアプリにおける言語の扱いに関する議論が盛んになれば、と思います。普遍的に役立つトピックもあれば、Cookpadアプリ固有のトピックもあったかと思います。最初に補足したように、いくつかの点で標準的な方法から逸脱した手段を取っています。我々のサービスでは、要求を満たすためにこうした手段を取りましたが、その代償として多少のメンテナンスコストの増加は避けられませんでした。同様のことを行うのであれば、それが本当に必要かどうか、そしてそのコストがどれくらいかを検討するのにこの記事が参考になれば幸いです。このようにサービスの海外展開では、海外の文化、ユーザーのモバイル利用環境など様々な事柄を考慮しなければなりません。Analyticsのデータからインドネシアからの多くのアクセスが、言語設定が英語のデバイスからであることが明らかになった。*2:2016年5月10日時点の仕様。今後、変更される可能性がある。vol.2...こんにちは。〜料理動画・広告のBtoB領域の開発事...こんにちは!人事部の冨永です。Tec...2018-04-12クッキングLIVEアプリcookpadTVのコメント配信技術こんにちは。メディアプロダクト開発部の長田です。クックパッドでは、昨...2016-08-12海外のユーザーを向いたプロダクト開発の工夫こんにちは、検索事業部の岡根谷です。使ったセキュリティログ検索基盤の構築サービス特性にあった検索システムの設計戦略クックパッド採用説明会「クックパッドはサービスの作り手を採用したいんです。/*}*/スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。