Edited at

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

More than 3 years have passed since last update.


はじめに

起動時と、起動後に言語設定を変更するアプリをいじることがあり

iOS9以上の端末でのNSLocale.preferredLanguages()の対応をまとめた記事を

数少ない中いくつか目にして

ja(日本語)に限定していて多言語対応としては難しい内容や



  • 言語にあたる頭の二文字で判断していいの?


  • -で分割すればいい(言語-地域でパラメータを取得するから)?

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


検証


方針


  • 新規でプロジェクトファイルを作成


    • iOSアプリ



  • AppDelegateに記述し実行


    • Alertからパラメータを確認




  • 設定から言語設定を変更


    • シミュレーター使用

    • 実機使用




端末の設定

シミュレーターの初期設定は英語

スクリーンショット 2016-03-19 17.00.36.png


Technical Note TN2418

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

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

 - iOS9以降、NSLocale.preferredLanguages()は言語以外に地域を含めた値を返す

 - 設定しているアプリで対応していれば地域は異なるが言語が同じリソースを利用する

 - 設定されている言語から、アプリで対応しているリソースファイルをロードするNSBundleの仕組みを使ってみて


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

スクリーンショット 2016-03-19 17.00.36.png

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

language - region

スクリーンショット 2016-03-19 18.13.15.png

もしくは

language - script - region

スクリーンショット 2016-03-19 18.08.28.png


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



NSBundleからの取得パラメータ


多言語設定をしていない状態

Base.plojに含まれているリソース(storyboardなど)がどの言語を設定されていても適用されます。

NSBundle.mainBundle().preferredLocalizations.first!

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


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



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

スクリーンショット 2016-03-19 21.31.42.png

Technical Note TN2418の例から

地域をインドに設定

スクリーンショット 2016-03-19 19.51.56.png

アプリのリソースで選択されるのは

スクリーンショット 2016-03-19 19.53.51.png

en-GBであるEnglish(United Kingdom)


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



言語コード

言語を規定しているISO 639-1(二文字で表示)にない言語の場合ISO 639-2(3文字で表示)

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文字の言語名コードが使用される可能性がある



コード


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