iOSの国際化対応の2:ソースコード、リソースの国際化
アプリを国際化する際に変更を加えなくてはならない場所は大きく分けてソースコードとリソースの二通りある。
リソースレベルでの国際化対応はXcodeの機能を利用して実施するのに対し、ソースコードレベルでの国際化対応は文字通り(Swiftの)ソースコードに対応を書き込む必要がある。
二回目の今回はソースコードレベルでの国際化の主役、マクロNSLocalazedString()やLocale構造体、NSLocaleクラスなどについて書く。
Locale「構造体」、NSLocale「クラス」
Swiftでは「構造体」>「クラス」と言う原則がある。
Appleの開発者向け記事: Choosing Between Structures and ClassesによるとSwiftではクラス(参照型)より構造体(値型)を用いよとある。従って基本的にはLocale構造体を用いるべきである。
ところが例えばLocale構造体にはなくてNSLocaleクラスにしかない属性が欲しいことがしばしばある。そんな時にどうするか、と言うのが本項目で示したいことである。
具体例を挙げるのが一番だろう。
import UIKit
let locale = Locale.current
let currentLangID = Locale.preferredLanguages[0]
let displayLang = (locale as NSLocale).displayName(forKey: NSLocale.Key.languageCode, value: currentLangID)
この例ではdisplayNameがSwiftのLocale構造体にはないためNSLocaleで取得したものを利用するのである。一見すると無意味のようだが、これを付けないとXcodeはdisplayNameを候補に挙げてくれないのである。
マクロ:NSLocalazedString()
ソースコード内に現れるStringをLocale毎に変更できるようにするためのマクロがこれである。
具体例は次のようなものである。
class ViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel!
@IBOutlet weak var inputTextField: UITextField!
@IBOutlet weak var myButton: UIButton!
@IBOutlet weak var clearButton: UIButton!
@IBOutlet weak var outputLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
inputTextField.text = ""
outputLabel.text = ""
myLabel.text = NSLocalizedString("TITLE_TEXT", comment:"Title text")
inputTextField.placeholder = NSLocalizedString("PH_TEXT", comment: "Place holder text")
myButton.setTitle(NSLocalizedString("DO_IT", comment: "Used for do something"), for: UIControl.State.normal)
myButton.sizeToFit()
clearButton.setTitle(NSLocalizedString("CLEAR", comment: "Used for clear button"), for: UIControl.State.normal)
clearButton.sizeToFit()
}
@IBAction func doIt(_ sender: Any) {
outputLabel.text = inputTextField.text
}
@IBAction func clearText(_ sender: Any) {
inputTextField.text = ""
outputLabel.text = ""
}
}
このようにソースコードを入力しただけでは、ここにあるTITLE_TEXTのようなすべて大文字のラベルがビルド後のアプリでは表示される。ローカライズ前の本体プログラム作成中は差し当たってこのままで行く事もできる。それだと出来上がりがわかりにくい場合、Localizable.stringsを作成する。
ポイントはUIに出てくるテキストをすべてこのマクロで実装するようにすることである。ボタンのテキストなど個別に国際化対応できるが、一つのやり方に統一した方がバグが少ないのである。変更にも強い。
リソースの対応:Localizable.stringsの作成
プロジェクトにLocalizable.stringsと言うファイルを作成する。中身は以下のようなものだ。
"TITLE_TEXT" = "入力をコピーします";
"PH_TEXT" = "何か入力して下さい";
"DO_IT" = "実行";
"CLEAR" = "クリア";
これを作成するにはプロジェクトを右クリックしてNew File…を選びiOSのグループ内で下の方にあるStrings Fileを選び、Nextでプロジェクト内にLocalizableという名前で保存する。
対応言語の追加
最初に確認したLocalizationsに言語を追加する。追加すべきリソースの一覧が出てくるので、基本は全部追加で良いだろう。
例えば先ほどのLocalizable.stringsだとLocalizable.strings(Chineses(Simplified))というファイルが生成されるので、
"TITLE_TEXT" = "复制您键入的文本";
"PH_TEXT" = "输入的东西";
"DO_IT" = "运行";
"CLEAR" = "撤消";
のように書き換えるのである。イメージファイルなどの場合、参照パス文字列を「ローカライズ」することで対応できる。
テスト
実機やシミュレーターで言語を入れ替えて動作を確認する。問題がなければちゃんと切り替わってくれるはずだ。
まとめ
国際化対応には他にもやり方はあるが、ソースコード側はマクロNSLocalazedString()で統一するやり方が結局はメンテナンス性も良いと思っている。中身の変更はLocalizable.stringsに統一するのだ。
AndroidでTranslation Editorに文字列変換を集約させるのと同じ考え方だ。