102
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOS Second StageAdvent Calendar 2015

Day 6

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

Last updated at Posted at 2015-12-05

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の機能を使いつつ世界にアプリを発信していきましょう〜!

102
89
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
102
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?