ラベル Unicode の投稿を表示しています。 すべての投稿を表示
ラベル Unicode の投稿を表示しています。 すべての投稿を表示

2010年5月3日月曜日

UTF-8 の文字列の長さを正確に求めるためには Normalize しましょう

Twitterにメッセージを送信する際に、クライアント側でメッセージの長さを判定して画面に表示したいというような要件があると思います。当然、このような場合には入力されたメッセージを取ってきてその長さを求めてやれば良いのですが、どうやら UTF-8 などのマルチバイトな文字コードを使っていると文字列の長さを正確に求めるのが大変なようです。


■何がどう大変なのか

図を書いてみました。「文」が文字数、「b」がバイト数を表しています。 Objective-C の場合は、「文」が ``[NSString length]`` を、「b」が [NSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding] を表します。ほかの言語の場合もそれぞれ相当するメソッドまたは関数が用意されていると思います。



上のケースが一般的な UTF-8 文字列です。この場合、バイト数はマルチバイトになってしまうため実際の文字数(我々人間が見て自然な文字数)とは一致しませんが、その代わり UTF-8 文字数が実際の文字数と一致するため問題なく長さを求めることができます。

面倒なのが下のケース。ウムラウト記号や濁点・半濁点が一文字として入力されてしまっている場合です。通常滅多にこのような文字列に遭遇することはないのですが、まれにこのような文字列を入力させる処理系があるようです。で、この場合はウムラウト記号や濁点・半濁点も一文字として認識されてしまうため、実際の文字数と文字列長判定メソッドが返す文字数が異なってきます。そこまで厳密にしなくてもよいのでは・・・と思ったのですが、なんと Twitter はきちんとこのような場合も自然な文字列の長さを測定して140文字かどうかを判断しているらしいです。


■対策:Normalizeする

このように実際の文字数と UTF-8 上の文字数が異なっている場合には、 Normalize と呼ばれる処理を行って文字を圧縮する必要があります。 Objective-C の場合には以下のようなメソッドが標準で用意されています。
// NFD
– (NSString *)decomposedStringWithCanonicalMapping

// NFKD
– (NSString *)decomposedStringWithCompatibilityMapping

// NFC
– (NSString *)precomposedStringWithCanonicalMapping

// NFKC
– (NSString *)precomposedStringWithCompatibilityMapping
これらのメソッドは、 Unicode 文字列をそれぞれ NFD, NFC, NFKD, NFKC と呼ばれるフォーマットの文字列に変換してくれます。それぞれの文字列がどのような表現を表しているかは、以下のページを参考にしてみてください。英語ですが図説がついてるので非常にわかりやすいです。
http://unicode.org/reports/tr15/
http://homepage1.nifty.com/nomenclator/unicode/normalization.htm

ほかの言語、たとえばPythonやRubyなどでこの機能が提供されているかどうかはわかりません。あしからず・・・><

2008年12月21日日曜日

iPhoneアプリをローカライズ(国際化対応)してみた

  • まずはNSLocalizedString()を利用してアプリを作る
  • 次にgenstringsコマンドを利用してLocalizable.stringファイルを自動生成する
  • Xcodeに取り込む
  • Xcodeからローカライズの設定を行う
  • 最後にかっこよく翻訳を行う
  • info.plistファイルの中身をローカライズしたい(たとえばアプリ名など)ときは、
  • InfoPlist.stringsファイルを手で作って、ローカライズ設定をして、翻訳すると良い
  • Localizable.stringは基本UTF-16に統一しておくこと
  • InfoPlist.stringsはUTF-16でないと動かない。Localizable.stringsはUTF-8でいいらしい(未確認)

iPhoneアプリを作ったならば、
「一人でも多くの人に使ってもらいたい」
「1本でも多く売りたい」
というのが心情というものです。
日本ではヒットしないアプリが海外では大ブレイクということも考えられますし、
iPhoneアプリの市場は日本国内よりも海外のほうが圧倒的に大きいです。
従って、iPhoneでアプリを作るなら、国際化対応は必須であるといえましょう!

ということで、今日はそんな大事な大事な国際化のやり方を勉強してみました。
Cocoa Frameworkは元々非常に国際化がしやすい作りになっていますので、
iPhoneアプリでもその仕組みの恩恵を受けることが出来ます!




国際化の第1歩はアプリ作成からです。まずはアプリを作らないと始まりません。
このとき、普段は画面に表示する文字列を
self.navigationItem.title = @"Title";

こんな感じで指定していると思いますが、
これをNSLocalizedString()関数を利用して
self.navigationItem.title = NSLocalizedString(@"Title",
@"Title message for the main view");

こんな風に書き直します。
第1引数はメッセージキー(普通は英語の文字列をそのまま使えばいいと思います)、
第2引数はコメントです。コメントは面倒でしたらnilでも大丈夫ですが、あると翻訳時に大変便利です。


アプリ側の準備が出来たら、次は翻訳文字列ファイル(Localizable.strings)を生成します。
手で作っても良いのですが、genstringsという大変便利なコマンドが最初から用意されているので、
こちらを利用するのが良いと思います。
使い方はこんな感じです。
genstrings [オプション] ファイル名

たとえば
genstrings *.m

コレを実行すると、カレントディレクトリにあるすべての拡張子がmのファイルから
自動的にLocalizable.stringsをつくってカレントディレクトリに保存してくれます。
生成されたLocalizable.stringsはUTF-16でエンコードされています。
(BOMのありなし、およびエンディアンはリトルかビッグかについては不明ですが、
とにかくUTF-16としてMac, Xcode, およびiPhoneが正しく認識してくれるのは間違いないです)

これだけだとプロジェクト内のすべてのソースをまとめてファイルにしてくれないので、
実際の開発時にはfindコマンドを併用して、以下のように実行するのがおすすめです。
genstrings -a $(find . -name "*.m") 

このコマンド入力によって!
カレントディレクトリ以下のすべてのの拡張子がmのファイルを対象にしてLocalizable.stringsを生成してくれる
ソースコードが更新されていた場合、以前に作成したLocalizable.stringsの内容に新しく追加された文字列が追加される
(-aオプションがないとファイルは上書き保存されてしまいます)


Localizable.stringsが生成されたら、Xcodeに取り込みます。
単純にファイルをXcodeプロジェクトに対してドラッグ&ドロップすれば大丈夫です。
このとき、必ず文字コードを「UTF-16」にしてください。
取り込み時にUTF-16にするのを忘れていたら、あとからファイルのエンコードをUTF-16にする必要があります。


取り込んだら、ローカライズの設定を行います。


まずは対象のファイルを選択して右クリック、「情報を見る」ウィンドウを開きます。
「ファイルをローカライズ可能にする」をクリック。


再度、「情報を見る」ウィンドウを開き直し、
「ローカリゼーションの追加」をクリック。プルダウンからJapaneseを選択。
ドイツ語とかフランス語もプルダウンから簡単に選択できます。
その他の言語の場合は自分で調べる必要がありそうです。


成功するとこんな感じになります。

解除するときは「情報を見る」ウィンドウの一般タブから、
「すべてのローカライズを取り除く」をクリックするだけです。

以上、とても簡単です。
ただしこのローカライズの設定時に文字コードの指定がUTF-16ではなくなることがあるので、
もう一度設定したファイルの文字コードをチェックしておくと安心です。


これで準備は出来たので、あとはLocalizable.stringsを翻訳するだけです。
翻訳したらビルドしてiPhoneにインストールすれば完成!


Localizable.stringsでは翻訳できない箇所、たとえばアプリ名を翻訳するときなど、
Info.plistの内容を翻訳したいときは、InfoPlist.stringsと言うファイルをXcode上で作って、
ローカライズ設定を行い、翻訳してビルドすればうまくいくと思います。


Xibファイルについてもローカライズ設定が可能なようなのですが、
Xib自体を翻訳するより、ソースコード中からラベルやタイトル名を変更するようにして、
ソースコード自体をLocalizable.stringsで翻訳するようにしたほうが変更に強くて楽かと思います。




トラブルシューティング
以上の内容に従っても翻訳されない!というときは、
まずはLocalizable.stringsが本当にUTF-16になっているか確認してみましょう。
InfoPlist.stringsはUTF-16でないと動きません。
Localizable.stringsについてはUTF-8でも動くそうなのですが、私の場合駄目だったので、
やはりUTF-16をオススメしておきます。
ただし、いずれの場合でも、UTF-8とUTF-16が混在すると確実に動きません。

それでも駄目な場合は、Finderでプロジェクトのディレクトリを開き、
buildディレクトリの中身を丸ごと消してから再度ビルドするとうまくいくと思います。
(以前のビルド結果が残って居るとうまくいかないみたいです)


それでは素敵な翻訳ライフを!

2008年10月24日金曜日

よくわかるユニコード

  • Unicodeは文字コードセットのこと。UTFとは別。
  • UTF-何々というのはUnicodeをのエンコード方式のこと。
  • BOM(Byte Order Mark)は基本的にUTF-16にしかつかない。UTF-8では不要。
近頃はWeb上のリソースから、iPhoneの文字列までみんなUnicodeになりましたね。
でもよく考えたらUnicodeについて真面目に学んだことが一度もなかったので、BOMのあるなしで悩んだのをきっかけに、Unicodeについて勉強してみました。

勉強しただけではすぐ忘れちゃうので、こんな図にしてみました。




これで少しは覚えられるかも。