iOS11で変わったロケール

これはiOSアドベントカレンダーの2日目の記事です。

毎年アドベントカレンダーでは、カレンダーに関係あることを書くことにしています。 今回はiOS11で仕様変更があったロケールについて。

ロケールとは

iPhoneの設定に、「言語と地域」という項目があります。 ここでは、表示に使われる言語を設定することができますが、

この「地域」で設定される項目がロケールです。

このようにすると、現在設定されている言語とロケールを取得することができます。

let language = NSLocale.preferredLanguages
print("languages:\(language)")
let locale = NSLocale.current
print("locale:\(locale)")

ロケールを変えるとなにが変わるのか

さて、このロケールを変えると、ユーザーから見てなにが変わるのでしょうか。

まずは、日時の表示、そして小数点の記号などが変わります。 一部の国では、小数点の記号は「.」でなく「,」です。

例えば、言語を英語に固定してロケールだけ変えてみましょう。

ロケールがアメリカの場合にはこちら。日付は「月、日、年」の順番で表示されます。  

ロケールがイギリスの場合にはこちら。 日付は「日、月、年」の順番で表示されていますね。 時刻の表示も違いますし、通貨記号も違います。

そして、ロケールをドイツにすると、小数の表示が変わります。

カレンダーの開始曜日もロケールによって変わります。 日本やアメリカでは、カレンダーは日曜始まりですが、イギリスでは月曜に始まります。

また、最近のiOSでは、国によって機能が制限される場合があり、主にこのロケール情報をもとに制限されます。

ロケールをApple Payがまだ使えない国にすると、Walletアプリでクレジット登録をすることができなくなります。 (iPhone7でのSuica使用可・不可の場合のように、他の制限方法の場合もあります。)

あと、文字列のソートなども影響されます。 (主なOSでは、文字列のソートはロケールを考慮してできるようになっています。)

iOS11での変更点

さて、このロケールですが、実はiOS11からシステムの仕様が変わりました。

例えば、言語を日本語、ロケールをドイツに設定した場合、iOS10ではシステムの言語とロケールを取得するとこのような値がとれます。

languages:["ja-DE"]
locale:ja_DE

 

ところが、iOS11で同じ設定にすると、このような値がとれてしまいます。

languages:["ja-DE"]
locale:en_DE

言語は日本語でロケールはドイツ語なのに、なぜ関係ない「en」(英語)が含まれるのでしょうか。

実は、この「en」はアプリのローカライズ情報からでてきたものでした。

上記のコードは英語のローカライズ情報のみを持つアプリで実行した場合なので、日本語のローカライズ情報をもつアプリで実行した場合には下記のようになります。

languages:["ja-DE"]
locale:ja_DE

ユーザーが全く同じ設定をしていても、iOS10とiOS11では、取得できるロケールの値は違う場合があるのです。

なぜロケールの仕様が変わったのか

iOS10までは、ロケールと言語は全く別々の設定項目になっていたのに、iOS11では相互に影響するかたちとなりました。

いろいろと理由はあると思いますが、iOS9から導入されたローカライズ用のAPIも一因かと思います。

さきほど説明した通り、ロケールが変わると小数点が違います。 アプリで、数字を数字を表示する部分がある場合、いちいちロケールをチェックしてStringをフォーマットするのはめんどうくさいですよね。

その手間をはぶくために、iOS9からは下記のようなAPIが提供されています。

このように書くと、ロケールがアメリカの場合には「4567.89」、ロケールがドイツの場合には、「4567,89」と表示されます。

String.localizedStringWithFormat("%.2f", 4567.89)

また、同時にNumberFormatterの種類も増えました。 科学計算で使うようなフォーマットに加え、「1st, 2nd」などの序数にも対応しています。(下記は日本ロケールで実行した場合)

let formatter1 = NumberFormatter()
formatter1.numberStyle = .scientific

let formatter2 = NumberFormatter()
formatter2.numberStyle = .ordinal

let pi1 = formatter1.string(for: Float.pi)
//「"3.14159250259399E0"」と表示されます。
let pi2 = formatter2.string(for: Float.pi)
//「"第3"」と表示されます。

ここでわかると思いますが、序数のNumberFormatterを日本語ロケールで使うと、「第3」とフォーマットされるようになっており、「言語」を含んだ情報になっています。

例えば、端末の言語が日本語、ロケールがドイツ語のアプリで上記のフォーマッターを使うと、iOS10の仕様では、フォーマットされた文字列はドイツ語になってしまいます。 しかし、iOS11の仕様では、アプリの言語が英語なら英語でフォーマットされ、アプリの言語が日本語なら日本語でフォーマットされて表示されます。

iOS11の仕様変更で、特に何も考えずに、NumberFormatterを使えるようになっています。

まとめ

iOSのロケールも、だんだんと複雑化してきている感じがしますが、国際化をすすめてきたiOSの進化の影響ではあるんだろうな、と思います。

ただ、この仕様変更は影響範囲はそれなりにあると思うんですが、公式なアナウンスはほとんどないんですよね。 来年のWWDCにいけたら、このあたりちょっと聞いてみたいなと思います。