知っておくと便利なローカライズ関連Tips

More than 3 years have passed since last update.

iOS Second Stage Advent Calendar 2015 - Qiita 6日目です。

アプリを開発するようになってから、最初にやってしまったローカライズ系の失敗は、数字のケタ数区切りを,(カンマ)固定でコードに埋め込んでしまっていたことでした。

ドイツでは、"ケタ数区切りはカンマではなくピリオドだ" と知って衝撃を受けたのですが、「そいういう常識を知らなかったので...」と言い訳するのはとても苦しい。

どの部分がローカライズを意識しないといけないのか? を最初に事前知識と知っておくのは大事。

そしてiOS SDKでは対応が楽になる機能をたくさん用意してくれているので、ちゃんと使わないともったいない。

ということで、ローカライズ関連のTips集をお送りしようと思います!

"お、これは知らなかったな" というものが一つでもあれば嬉しいな...


日付について


基本

日付表記に地域差があるのはだれもご存知ですよね!

年、月、日の並びがちがったり、カンマがあったりなかったりします。

日付はおなじみのNSDateFormatterを使います。

結果は、iPhone端末の言語設定 と 地域設定に依存します。

年の部分は、和暦カレンダー/西暦カレンダー など地域カレンダーの設定により変わります。

用意されているスタイルは Full, Medium, Shortの3つがありますが、Fullは長すぎで使うタイミングがほとんどないので、Medium, Short みるとこんな感じ。

地域
MediumStyle
ShortStyle

アメリカ
Dec 5, 2015
12/5/15

イギリス
5 Dec 2015
05/12/2015

日本
2015/12/05
2015/12/05

ドイツ
05.12.2015
05.12.15

フランス
5 déc. 2015
05/12/2015


曜日もいれた日付を表示したい

曜日を追加したい など、フォーマットを変えたい場合は、dateFormatプロパティに任意に指定が可能です。

地域によって正しい表示になるようにdateFormatFromTemplateを使って変換してから、dateFormatに設定します。


let formatter = NSDateFormatter()
formatter.dateFormat = NSDateFormatter.dateFormatFromTemplate("EE MMM d", options: 0, locale: NSLocale.currentLocale())!
print(formatter.stringFromDate(NSDate()))

上記のコードで、以下のようになります。

地域
EE MMM d

アメリカ
Sat, Dec 5

イギリス
Sat, 5 Dec

日本
12月5日(土)

ドイツ
Sa., 5. Dez.

フランス
sam. 5 déc.

曜日は、日本だと後ろにつくけど、他の言語はだいたい前につきます。

この、前につく、後ろにつく ということをiOSでは自動的に判断してフォーマットを用意してくれるので、とっても楽!


今日の日付だったら、"TODAY" と表示したい

NSDateFormatterのdoesRelativeDateFormattingをtrueにすると実現できます。


let formatter = NSDateFormatter()
formatter.dateStyle = .MediumStyle
formatter.timeStyle = .NoStyle
formatter.doesRelativeDateFormatting = true

地域
2日前
1日前
今日
1日後
2日後

アメリカ
Dec 3, 2015
Yesterday
Today
Tomorrow
Dec 7, 2015

イギリス
3 Dec 2015
Yesterday
Today
Tomorrow
7 Dec 2015

日本
一昨日
昨日
今日
明日
明後日

ドイツ
Vorgestern
Gestern
Heute
Morgen
Übermorgen

フランス
avant-hier
hier
aujourd’hui
demain
après-demain

ただし、dateFormatを使ったフォーマット任意指定する方法との併用はできません。


"2015/11/28~2015/12/05" という期間の表示

日本だと間に "~" を使いますが、他の言語だと "-" を使うのをよく見ます。

"-" は左右にスペース有が正しいのだろうか? 半角ハイフンでいいのか? と迷ったりしますが...

実は迷わなくて大丈夫で、専用にNSDateIntervalFormatterというのが用意されています。


let formatter = NSDateIntervalFormatter()
let fromDate:NSDate = ...
let toDate:NSDate = ...
print(formatter.stringFromDate(fromDate, toDate: toDate))

地域
期間が1日以内
1日以上

アメリカ
12/5/15, 12:00 AM - 8:00 PM
12/5/15, 12:00 AM - 12/12/15, 12:00 AM

イギリス
05/12/2015, 00:00 - 20:00
05/12/2015, 00:00 - 12/12/2015, 00:00

日本
2015/12/05 0時00分~20時00分
2015/12/05 0:00~2015/12/12 0:00

ドイツ
05.12.15, 00:00–20:00
05.12.15, 00:00 – 12.12.15, 00:00

フランス
05/12/2015 00:00 – 20:00
05/12/2015 00:00 – 12/12/2015 00:00

期間が24時間以内だった場合は、2015/12/05 0時00分~20時05分のように、いい感じにフォーマットを整えてくれます。

NSDateFormatterと同じように、dateStyle, timeStyleなどスタイル指定も可能です。

dateStyle = .MediumStyle, timeStyle = .NoStyle にするとこんな感じに。

地域
日付のみの表記

アメリカ
Dec 5 - 12, 2015

イギリス
5 – 12 Dec 2015

日本
2015/12/05~2015/12/12

ドイツ
05.12.2015 – 12.12.2015

フランス
5–12 déc. 2015

地域によって、ほんと形式が違うものですね。


"1日2時間" という経過時間表示

こちらもFormatterが用意されていて、NSDateComponentsFormatterが使えます。


let formatter = NSDateComponentsFormatter()
formatter.unitsStyle = .Full
let components = NSDateComponents()
components.day = 1
components.hour = 2
print(formatter.stringFromDateComponents(components)!)

unitsStyleは以下の5種類用意されています。

地域
Positional
Abbreviated
Short
Full
SpellOut

アメリカ
1d 2
1d 2h
1 day, 2 hr
1 day, 2 hours
one day, two hours

イギリス
1d 2
1d 2h
1 day, 2 hr
1 day, 2 hours
one day, two hours

日本
1日 2
1日2時間
1日 2時間
1日 2時間
一日 二時間

ドイツ
1d 2
1d 2h
1 d, 2 Std.
1 Tag und 2 Stunden
eins Tag und zwei Stunden

フランス
1j 2
1j 2h
1 j et 2 h
1 jour et 2 heures
un jour et deux heures

さらに、allowedUnits, maximumUnitCountなどのパラメータで少し制御ができるようになっています。

formatter.allowedUnits = [NSCalendarUnit.Hour , NSCalendarUnit.Minute]

とすると、秒数は無視して切り捨てされた値になります。

formatter.allowedUnits = [NSCalendarUnit.Hour , NSCalendarUnit.Minute, NSCalendarUnit.Second]

とすると、秒数まで表記されるようになり、さらに

formatter.maximumUnitCount = 2

とを追加すると、最大単位2つまで表示するという設定にでき、丸めが入ります。

5:28:41 → 5h 29m

1:00:00 → 1h

0:05:10 → 5m 10s

1:00:05 → 1h 5s

0:00:10 → 10s


数字について


基本

冒頭にかきましたが、ケタ数区切り文字などが地域によって異なります。

NSNumberFormatter を使うのをお勧めします。面倒ですが(笑)


let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
print(formatter.stringFromNumber(12345.67)!)

地域
DecimalStyle
CurrencyStyle

アメリカ
12,345.67
$12,345.67

イギリス
12,345.67
£12,345.67

日本
12.345,67
¥12,346

ドイツ
12.345,67
12.345,67 €

フランス
12 345,67
12 345,67 €

ドイツは、カンマとピリオドの扱いが日本とは反対。フランスはケタ数区切りがスペースになります。


長さの表示

日本ではメートルですが、他の国だったらヤードだったり...いろいろある長さ。

長さの変換にはNSLengthFormatterが使えます。

func stringFromValue(value: Double, unit: NSLengthFormatterUnit) -> String

で、単位を任意に指定できますが、

func stringFromMeters(numberInMeters: Double) -> String

では、メートルから、端末で設定されている地域に合わせた単位に変換してくれます。


let formatter = NSLengthFormatter()
print(formatter.stringFromMeters(100))

地域
stringFromMeters(100)

アメリカ
109.36 yd

イギリス
100 m

日本
100 m

ドイツ
100 m

フランス
100 m

他に、ジュール/カロリーなどの表記のための NSEnergyFormatter

kg/lbなどの重量表記のための NSMassFormatter もあります。

余談ですが、HealthKitフレームワークではHKUnitという単位オブジェクトを用いて、いろんな単位に変換できる仕組みがあります。

HealthKitを使用するためには、entitlementsに設定が必要で、PrivacyPolicyもしっかりしないといけないので、単位の変換のためだけに使用するものではありませんが、HKUnitのソースコードのコメントに

    // [Mass]

// oz (ounces) = 28.3495 g
// lb (pounds) = 453.592 g
// st (stones) = 6350.0 g
//
// [Length]
// in (inches) = 0.0254 m
// ft (feet) = 0.3048 m
// mi (miles) = 1609.34 m

..のように各単位での値が載っていますので、知りたくなったら覗いてみるといいかもです!


その他


地域に差がある項目を取得する

地域ごとの内容はNSLocaleに基本的に入っています。

objectForKeyを使って、以下の情報などを得ることができます。

NSLocaleGroupingSeparator : ケタ数区切り文字

NSLocaleDecimalSeparator : 小数点区切り文字

NSLocaleQuotationBeginDelimiterKey : Quotesの開始区切り文字

NSLocaleQuotationEndDelimiterKey : Quotesの終了区切り文字

使用できるKeyの一覧は、NSLocaleのComponentKeysを参照してください。

試しにQuoteを抜き出してみると...


let locale = NSLocale.currentLocale()
let startQuote = locale.objectForKey(NSLocaleQuotationBeginDelimiterKey)!
let endQuote = locale.objectForKey(NSLocaleQuotationEndDelimiterKey)!
print("\(startQuote)iOS\(endQuote)")

地域

アメリカ
“iOS”

イギリス
“iOS”

日本
「iOS」

ドイツ
„iOS“

フランス
«iOS»

ドイツ、フランス...そ、そうなんだ!という感じです(笑


localized.stringに設定がないものをチェックする

XCodeのScheme設定に、

Screen Shot 2015-12-05 at 9.55.05 PM.png

という設定があります。

localized.stringにないものをNSLocalizedStringで使った時に、コンソールに警告を表示出すことができます。

出力される警告メッセージ例


Localizable string "aaa" not found in strings table "Localizable" of bundle CFBundle 0x7f828bf019d0



Simulatorで言語、Localeを簡単に設定する

XCodeのScheme設定で、"Application Language", "Application Region"の設定ができます。

Screen Shot 2015-12-05 at 9.59.06 PM.png

チェックが必要な言語,地域を別々にScheme設定しておくと便利です。

"Application Language"で選択できる言語は、プロジェクトのLocalizationに設定している言語のみのようです。(これは少し残念)


Storyboard上で表示言語を切り替える

StorybardのPreviewの表示で言語を切り替えることができます。

XCodeのAssistantEditorからStoryboardのPreviewを表示します。

storyboard.png

右下に言語切替のメニューが表示されるので、この切替で各言語での配置を確認できます。

FitPortの画面を拝借


Localized.stringに設定されている値を言語指定で取得する

2016/05/01 追記

//英語

let enPath = NSBundle.mainBundle().pathForResource("en", ofType: "lproj")!
let enValue = NSLocalizedString(value, tableName: nil, bundle: NSBundle(path:enPath)! , value: "", comment: "")

//日本語
let jaPath = NSBundle.mainBundle().pathForResource("ja", ofType: "lproj")!
let jaValue = NSLocalizedString(value, tableName: nil, bundle: NSBundle(path:jaPath)! , value: "", comment: "")


最後に...

世界中のStoreにアプリを出すと、いろんな国の人からフィードバックがあって嬉しいです。

比較的ですが...日本人は厳しくてたまに辛くなるけど、他の国の人は優しいです。

あなたのアプリが大好き!ってだけのフィードバックもあったりしてほんと、嬉しいです。

うまくiOSの機能を使いつつ世界にアプリを発信していきましょう〜!