こんにちは、デスクの上にiPhoneを意味もなく3台並べる男、faultierです。
僕の普段の仕事はlivedoor blogやlivedoor ニュースのモバイル版のシステム開発ですが、ときにはlivedoor クリップアプリのようなiPhoneアプリの開発もやっています。ということで、今日はPerlの話ではなく、iPhoneアプリとObjective-Cの話をしようと思います。
上の画像はlivedoor クリップアプリですが、実は言語設定が「日本語」以外に設定されていると日本語部分が英語に変わります。
MacOSXのアプリケーションを作成したことがある方はご存知かと思いますが、Cocoaアプリケーションは簡単に多言語化する仕組みが備わっています(できることは単順ですが)。
ローカライズされたリソースを用意する
まず、ローカライズされたリソースの置き場を用意します。英語と日本語に対応する場合は、en.lprojとja.lprojというディレクトリを作って、Xcode上でプロジェクトに取り込みます。他の言語にも対応する場合は、(ISO 639で定義されている2文字もしくは3文字の言語コード).lprojというディレクトリを追加していくだけです。
また、もっと細かく分類したいのであれば、en_USやen_GBなどのように、ISO 3166で定義されているコードを使用して国や地域に絞り込むことができます。
さらに、English、Japanese、French、Germanなど言語名での指定もサポートしていますが、こちらはレガシーなやり方のようなので、言語コードを使う方をお薦めします(後述するXcode上からローカライズドなリソースを作る方法では、何故かEnglishやJapaneseのようなディレクトリになりますが…)。
en.lprojとja.lprojをプロジェクトに取り込んだら、それぞれの下にInfoPlist.stringsとLocalizable.stringsというファイルを作ります。InfoPlist.stringsは、Info.plistで設定される内容(例えばアプリの表示名など)をローカライズするために使います。Localizable.stringsの方は好きに使えるローカライズドされた文字列を入れて置きます。
中身は以下のように、"key" = "localized value"の形式で記述します。あとは、プログラム側からこのキーを元に文字列を取得すれば、そのときのiPhoneの設定に応じてそれぞれの言語の文字列を使うことができます。
/* ja.lproj/InfoPlist.strings */ CFBundleDisplayName = "日本の心";
/* ja.lproj/Localizable.strings ここはコメント エンコーディングはUTF-16でなければならないことに注意 */ "Hoge" = "ほげ";
/* en.lproj/Localizable.strings */ "Hoge" = "foo";
また、先にlprojディレクトリを作るのではなく、先にInfoPlist.stringsやLocalizable.stringsをプロジェクトに追加しておき、「情報を見る」から「ローカライズ可能にする」こともできます。この場合、Xcodeのプロジェクト上にはlprojグループは表示されず、stringsファイルそのものが二つに分裂したように表示されますが、内部では(言語名).lprojが作られています。
プログラム側をLocalizedStringに対応させる
こうして用意したローカライズドされた文字列をプログラム側から使うコードは、次のようになります。
NSString *localizedString = [[NSBundle mainBundle] localizedStringForKey:@"Hoge" value:@"Hoge" table:nil];
これで、「Localizable.stringsからキーがHogeのローカライズドされた文字列を取得する。現在の設定の言語専用のリソースが無いか、あってもこのキーが定義されていなければ、"Hoge"という文字列を返す」という処理です。
ただ、毎回これを書くのは「文字列を取得する」という目的のためだけにはちょっと煩雑なので、次のようなマクロが用意されています。
NSString *localizedString = NSLocalizedString(@"Hoge", @"");
2個目の引数はただのコメントなので、特に何かに使われるわけではありません。これで最初のコードと同じ文字列が得られます。元のメソッドでは、どこのバンドルから取ってくるか、どのテーブル(ファイル)から文字列を探すか、指定されたキーが見つからなかった場合に返すデフォルトの値は何か、まで指定できますが、「バンドルがmainBundle、テーブルはLocalizable.strings、指定したキーが見つからない場合はキーと同じ文字列」で良いのであれば、このマクロの方が楽ですね。
ちなみに、テーブル名を指定した場合はLocalizable.strings以外のファイルから文字列を取得することもできます。例えば、Error.stringsというファイルを作り、そこにエラーメッセージをまとめておくと、プログラムからは次のように呼び出せます。
NSString *lcalizedErrorMessage = NSLocalizedStringFromTable(@"ConnectionError", @"Error", @""); // 次のコードでも同じ //NSString *lcalizedErrorMessage = [[NSBundle mainBundle] localizedStringForKey:@"ConnectionError" value:@"ConnectionError"];
このようにして、アプリ中で使われる文字列を(言語名).lproj以下のstringsファイルに抜き出しておけば、あとはそのファイルを言語毎に作ってやるだけで、表示する文言やメニューのラベルなどを変える程度ではありますが、簡単に多言語対応できるようになります。
xibファイルのローカライズ
stringsファイルでローカライズする他に、xibファイルも同じ方法で各言語ごとのローカライズ版を作ることができます。プログラム側を大幅に変更せずとも、言語に合わせてUIを変えることができるようになります。
ただ、機能やデザインに関わるxibファイルを各言語分用意して管理するのは、文字列しか含まれていないstringsファイルの管理よりは手間もかかり気を使う必要もありますし、サイズも大きくなります。これを使う場合はその必要があるかどうか、慎重に検討した方が良いでしょう。
ちなみに、livedoor クリップアプリのUIは大部分をxibファイルを使わずに実装しているため、特にUI自体のローカライズはしていません。
多言語対応のメリット
iPhoneアプリは、AppStoreのおかげで思った以上に海外のiPhoneユーザにも見てもらえます。
livedoor クリップの場合、元のサービスがそもそも日本語を理解できる人でなければ使えないサービスなので、日本語のみ対応にしてもよかったのですが、リリース後に国別のダウンロード数を見たところ予想以上に海外からのダウンロードがありました(もちろん、海外在住の日本人だったり、無料のアプリなので気軽にダウンロードしてしまっただけの可能性もありますが)。
よく分からないアプリケーションでも、まったく読めない言語で書いてあるより、英語化されているだけでも少し安心してもらえるようです。
みなさんも、是非、世界中で使ってもらえるiPhoneアプリを作ってみて下さい!