🍁

Swift: 指定した言語のStringを取得する

2024/04/01に公開

まず、SwiftUIのPreviewで多言語対応の動作確認をするのは簡単です。

#Preview {
    ForEach(["en_US", "ja_JP"], id: \.self) { id in
        SomeView().environment(\.locale, Locale(identifier: id))
    }
}

のように書けば言語ごとのプレビューを確認できます。

ただし、Previewではなくアプリ実行中にプログラムによって動的に言語を指定して文言を取得したい場合には、場面によって仕様をよく理解して取得方法を変える必要があります。

文言をアプリTarget内(Bundle.main)に設置してある場合

SwiftUIのViewで直接指定する場合

Text("文言のキー")
    .environment(\.locale, Locale(identifier: "ja_JP"))

のように.environment()でLocaleを指定するだけで意図した言語の文言が表示されます。

Viewでないロジック層で指定する場合

Stringlocaleが指定できそうなAPIがあるのでそれを使ってみると

String(localized: "文言のキー", locale: Locale(identifier: "ja_JP"))

プライマリー言語の文言しか取得できません。この件についてはDeveloper Forumsで議論されています。
https://forums.developer.apple.com/forums/thread/691532

このAPIはlocalizedに指定されたフォーマット込みの文字列に埋め込まれる変数にDateなどローカライズ要素のあるものが含まれていた際に指定したLocaleが活用される仕様らしいです。

では、強制的に指定した言語の文言を直接取得するにはどうすればいいでしょうか。

Stack Overflowに答えがありました。
https://stackoverflow.com/a/31744226

Bundleは言語によって異なるパスを示すため、指定言語のBundleを取得してその中にある文言を拾ってくるようにすればいいようです。

import Foundation

extension String {
    init(localized: LocalizationValue, lang: String) {
        let path = Bundle.main.path(forResource: lang, ofType: "lproj")
        self.init(localized: localized, bundle: Bundle(path: path!))
    }
}

String(localized: "文言のキー", lang: "ja")

のようにすれば、直接意図した言語の文言を取得できます。

文言をローカルPackage内(Bundle.module)に設置してある場合

まず、異なるモジュールのリソースを扱う場合、現状かなりクセがつよいので仕様を理解する必要があります。

https://zenn.dev/kyome/articles/46e4f3641a4181

SwiftUIのViewで直接指定する場合

結論から言うと、モジュール側にその文言を利用するためのインタフェースを実装し、利用側でenvironment(\.locale)指定するのが一番安定します。

モジュール内
public enum SomeModule {
    public static let mongonText = Text("文言のキー", bundle: .module)
}
アプリ側
import SomeModule

SomeModule.mongonText
    .environment(\.locale, Locale(identifier: "ja_JP"))

Viewでないロジック層で指定する場合

基本的にはアプリTarget内に設置してある場合と同じです。Bundleを指定してStringを拾うextensionをモジュール側に置けばよいです。

モジュール内
import Foundation

extension String {
    init(localized: LocalizationValue, lang: String) {
+       let path = Bundle.module.path(forResource: lang, ofType: "lproj")
        self.init(localized: localized, bundle: Bundle(path: path!))
    }
}

public enum SomeModule {
    public static let mongon = String(localized: "文言のキー", lang: "ja")
}
アプリ側
import SomeModule

let mongon = SomeModule.mongon

Discussion