iOS9から変わったNSLocale.preferredLanguagesの対応を考える

# はじめに
起動時と、起動後に言語設定を変更するアプリをいじることがあり
iOS9以上の端末でのNSLocale.preferredLanguages()の対応をまとめた記事を
数少ない中いくつか目にして
`ja（日本語）`に限定していて多言語対応としては難しい内容や

 - `言語`にあたる頭の二文字で判断していいの？
 - `-`で分割すればいい（`言語`-`地域`でパラメータを取得するから）？

上記の疑問をもって調べてみました。

# 検証
## 方針
 - 新規でプロジェクトファイルを作成
    - iOSアプリ
 - AppDelegateに記述し実行
    - Alertからパラメータを確認
 - `設定`から言語設定を変更
    - シミュレーター使用
    - 実機使用

## 端末の設定

シミュレーターの初期設定は英語
<img width="309" alt="スクリーンショット 2016-03-19 17.00.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/12362/ad469209-4788-31c1-639c-7cc24411ec3a.png">

# Technical Note TN2418

https://developer.apple.com/library/ios/technotes/tn2418/_index.html

ここに書いてあることの解釈は次の通り

　- iOS9以降、NSLocale.preferredLanguages()は`言語`以外に`地域`を含めた値を返す
　- 設定しているアプリで対応していれば`地域`は異なるが`言語`が同じリソースを利用する
　- 設定されている言語から、アプリで対応しているリソースファイルをロードするNSBundleの仕組みを使ってみて


## iOS9以降、NSLocale.preferredLanguages()は地域も含めた値を返す

<img width="309" alt="スクリーンショット 2016-03-19 17.00.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/12362/85bb39d4-e868-0d57-c30e-07f6365df390.png">

言語タグでいうところの次のような値が渡されます。

`language` - `region`
<img width="310" alt="スクリーンショット 2016-03-19 18.13.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/12362/1507116c-de2e-7df3-8367-9062b6b953d2.png">

もしくは
`language` - `script` -  `region`
<img width="310" alt="スクリーンショット 2016-03-19 18.08.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/12362/28772d7d-c157-cd38-4022-4f2d31dfe756.png">

> `-`で分割すると決めたら、[-`region`]部分を除くとか？

## NSBundleからの取得パラメータ

### 多言語設定をしていない状態
`Base.ploj`に含まれているリソース（storyboardなど）がどの言語を設定されていても適用されます。

```swift
NSBundle.mainBundle().preferredLocalizations.first!
```

で取得する値は`en`になります。

> 多言語対応していないアプリではNSBundleは言語判定に使えない

### 多言語設定をしている状態

<img width="551" alt="スクリーンショット 2016-03-19 21.31.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/12362/aba80529-6e09-5878-8d7b-2c3389667959.png">

`Technical Note TN2418`の例から

地域をインドに設定
<img width="304" alt="スクリーンショット 2016-03-19 19.51.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/12362/810d592f-942e-4bbb-8572-23fdb1772665.png">

アプリのリソースで選択されるのは
<img width="308" alt="スクリーンショット 2016-03-19 19.53.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/12362/086bded1-cefe-6877-ae2b-3fb473fc0b9f.png">
en-GBである`English(United Kingdom)`

> `en`である`English - Development Language`ではない。

# 言語コード

言語を規定している`ISO 639-1`（二文字で表示）にない言語の場合`ISO 639-2`（３文字で表示）
https://developer.apple.com/library/prerelease/ios/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html

`ISO 639-1`がカラだと表示される`ISO 639-2`はこちらの方がわかりやすいかも
http://www.loc.gov/standards/iso639-2/php/English_list.php

>3文字の言語名コードが使用される可能性がある

# コード
```swift:AppDelegate.swift
//
//  AppDelegate.swift
//  LocalTest
//
//  Created by nofrmm on 2016/03/19.
//  Copyright © 2016年 All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    func applicationWillResignActive(application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(application: UIApplication) {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

        let langFirstFromLocale:String = NSLocale.preferredLanguages().first!
        let langFirstFromBundle:String = NSBundle.mainBundle().preferredLocalizations.first!
        
        let alertController = UIAlertController(title: "SettingCheck", message: String(format: "NSLocale:%@\n NSBundle:%@",langFirstFromLocale, langFirstFromBundle), preferredStyle: .Alert)
        
        let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
        alertController.addAction(defaultAction)
        
        let activeVc = UIApplication.sharedApplication().keyWindow?.rootViewController
        
        
        activeVc?.presentViewController(alertController, animated: true, completion: nil)
    }

    func applicationWillTerminate(application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}



```

# さいごに
曖昧なところは残っています。

## 国別の対処について
中国語まわりは簡体語、繁体語以外にもハマりどころはありそうな印象。
イギリス英語と、アメリカ英語があってどっちかに寄せたい場合はどうするんだろう、とか
info.plistの`CFBundleDevelopmentRegion`は具体的にどう動く、とか

## 実機とシミュレータ
シミュレーターの結果にばらつきがでています。
iOS8.4では想定通り日本語が選択されても、同じ設定のiOS8.1では英語が選ばれたり
最終的には実機での検証が必要。


# 参考
https://developer.apple.com/library/ios/technotes/tn2418/_index.html

https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html

## 規格
https://ja.wikipedia.org/wiki/IETF言語タグ
http://www.loc.gov/standards/iso639-2/php/English_list.php

## 不具合事例
http://togetter.com/li/874874