I'm writing an iOS app that shares content with buddies. So my app will run in one language but the shared content will use the locale configured for the buddy.
I found this Apple documentation which suggests that locale: (Locale(identifier:
is the solution.
Apple Documentation
But I can't get it to work. Here's sample code.
struct LocalizationDemoView: View {
@State var isEnglish = true
var body: some View {
var myLocale: String { isEnglish ? "en": "de" }
VStack {
Toggle("Switch language", isOn: $isEnglish).frame(maxWidth: 200)
HStack {
Text("\(myLocale): ")
Text(String(localized: "Hello world!", locale: (Locale(identifier: myLocale)), comment: "To share"))
}
}
}
}
And here's the excerpt from the string catalog:
{
"sourceLanguage" : "en",
"strings" : {
"Hello world!" : {
"comment" : "To share",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "🇩🇪 Moin Welt!"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "🇬🇧 Hello world!"
}
}
}
}
}
}
Has Apple reduced support for string catalogs or is my code wrong?
Xcode 16.4 compiled on MacOS 15.6.1, device iOS 18.6.2
I understand that the locale
parameter is confusing. Additionally there's confusion around which framework ultimately configures the locale to load the string from.
SwiftUI always uses the locale from the environment when localizable strings are resolved, i.e. in Text
and LocalizedStringResource
. This makes sure that values from the environment always take precedence.
You can either influence what's in the environment, or ask Foundation to resolve a LocalizedStringResource
into a localized String
yourself – bypassing SwiftUI's environment.
Here's the full code snippet that gives you two options how to load strings in a different language; one for SwiftUI and one for all the other strings.
Please don't try to load a bundle from a file path to an .lproj folder, you will miss out on Foundation's language matching when it comes to language variants and other edge-cases.
struct ContentView: View {
@State var isOtherLanguage = false
var myLocale: String { isOtherLanguage ? "de" : "en" }
var locale: Locale {
Locale(identifier: myLocale)
}
var body: some View {
VStack {
Toggle("Switch language", isOn: $isOtherLanguage).frame(maxWidth: 200)
HStack {
Text("✅\(myLocale): ")
Text(LocalizedStringResource(
"Hello world!",
table: "Localizable",
locale: locale
)).environment(\.locale, Locale(identifier: myLocale))
}
HStack {
Text("✅\(myLocale): ")
Text(localizedString("Hello world!", locale: locale))
}
}
}
// Foundation resolving the string:
func localizedString(_ resource: LocalizedStringResource, locale: Locale) -> String {
var resource = resource
resource.locale = locale
return String(localized: resource)
}
}