前回の投稿は、Int型に対してLanguageProtocolを適用しましたが、String型に適用しLocalizable.stringsから言語してして文字列を取得する機能を実装してみました。
// プロトコル
protocol LanguageProtocol
// プロトコル・エクステンション
extension LanguageProtocol
// 言語別のクラス
final class Language<Base>
は前回と同様です。(そもそもこの記述を様々なクラスに適用できるのがこの手法の素敵な点です)
以下のようにString型のextensionを記述します。
extension String: LanguageProtocol { }
// 関数の実装
extension Language where Base == String
{
// 多言語文字列
var localized: String
{
// // 無理やりだけど言語コードを取り出す。ja_JPのja部分。
// // identifierは、String.en.xxxx, String.ja.xxxxのen、jaで切り替わる
// let array = identifier.components(separatedBy: "_")
// guard array.count > 0 else { return self.base }
// // Localizable.stringsのパスを取得する。
// var path: String? = Bundle.main.path(forResource: "Localizable", ofType: "strings", inDirectory: nil, forLocalization: array[0])
let languageCode = Locale(identifier: identifier).languageCode ?? identifier
var path: String? = Bundle.main.path(forResource: "Localizable", ofType: "strings", inDirectory: nil, forLocalization: languageCode)
if path == nil
{
// en、jaで指定された言語がない場合は、Baseを採用する。
path = Bundle.main.path(forResource: "Localizable", ofType: "strings", inDirectory: nil, forLocalization: "Base")
}
guard let _path = path else { return self.base }
// ファイル名はいらないので削除してディレクトリー情報だけにする。
let path2: String = _path.deletingLastPathComponent
// Bundle作成に渡すのはディレクトリー情報だけ。
let bundle: Bundle? = Bundle(path: path2)
guard let _bundle = bundle else { return self.base }
// ファイル名を指定していないので、DefaultのLocalizable.stringsを読み込む。
return NSLocalizedString(self.base, bundle: _bundle, comment: self.base)
}
}
これで
XCTAssertEqual("多言語化".en.localized, "Multilingualization")
XCTAssertEqual("多言語化".ja.localized, "多言語化")
のような形で呼び出せるようになります。
iPhoneの設定に拠らないで言語を選択できるようになりました。
enの部分をjaに切り替えると結果は日本語に切り替わります。
ちなみに、
let path2: String = _path.deletingLastPathComponent
の部分は、下記のextensionを利用します。
extension String
{
private var url: URL?
{
return URL(string: self)
}
public var deletingLastPathComponent: String
{
guard let _url = url else { return self}
return _url.deletingLastPathComponent().path
}
}