I'm wondering how to fix problems when working with String Localization in Swift Packages in SwiftUI?
Currently facing problems:
Text("String Example", bundle: .module)
However, when strings are called from variables, they are not localized.To demonstrate the problems, I created a package named TestPackage
.
The TestPackage
code is as follows:
import PackageDescription
let package = Package(
name: "TestPackage",
defaultLocalization: "en",
platforms: [
.iOS(.v16)
],
products: [
.library(
name: "TestPackage",
targets: ["TestPackage"]
),
],
targets: [
.target(
name: "TestPackage",
resources: [.process("Localizable.xcstrings")] // also tried [.copy("Localizable.xcstrings")]
)
]
)
After that, in my Sources > TestPackage
folder, I created a SwiftUIViewForPreviews.swift
file to test the localized strings and a Strings
struct for holding an example string:
import SwiftUI
struct SwiftUIViewForPreviews: View {
let stringFromCurrentView = "String Example"
var body: some View {
VStack {
Text("String Example", bundle: .module) // Works in Preview
Text(stringFromCurrentView) // Localization doesn't work
Text(Strings.stringOneLocalized) // Localization doesn't work
}
}
}
#Preview {
SwiftUIViewForPreviews().environment(\.locale, .init(identifier: "de"))
}
public struct Strings {
public static let stringOneLocalized = String(localized: "String Example", bundle: .module)
}
When running the preview, I only see the Text("String Example", bundle: .module)
string localized. For Text(stringFromCurrentView)
and Text(Strings.stringOneLocalized)
, localization does not work.
I also tried to set up strings the old way using .lproj
instead of using a Localizable Strings Catalog, but I faced the same problems.
To test how our package will work inside of a project, I created a project named ProjectForLocalizationPackageTesting
and attached the TestPackage
to it. I created a ContentView
to test how the localized string will show up in German localization, but in the preview, the string Text("String Example")
was not localized. I also tested it in the simulator with App Language and App Region set to German/Germany, but it still displayed the English variant in a simulator.
import SwiftUI
import TestPackage
struct ContentView: View {
var body: some View {
VStack {
Text("String Example") // Localization doesn't work
Text(Strings.stringOneLocalized) // Localization doesn't work
Text("String Example", bundle: .module) // Call doesn't work due to internal protection level
}
}
}
#Preview {
ContentView()
.environment(\.locale, .init(identifier: "de"))
}
I tried the approach of adding a special extension and when adding it to a Package SwiftUIViewForPreviews View, in a Preview we start to see our strings localised, :
extension Text {
init(_ key: LocalizedStringKey, comment: String = "") {
self.init(key, bundle: .module)
}
}
But when we import our package to another project, our strings are shown again as not localised, even with the extension added to our Package.
Adding this code into our ContentView in our Project also didn't help
extension Text {
init(_ key: LocalizedStringKey, comment: String = "") {
#if SWIFT_PACKAGE
self.init(key, bundle: .module) // For Swift Package
#else
self.init(key, bundle: Bundle.main) // For regular Xcode project
#endif
}
}
The questions are:
The question is specific to working with swift packages, Dynamic Text LocalizedStringKey approach Text(LocalizedStringKey(stringFromCurrentView)) didn't help to solve the problem. Any help appreciated.
Xcode version: Version 16.0, Swift, SwiftUI
- How to correctly set up a Swift Package to support string localization to be able to see correct localisations for strings?
Your description of the package setup is the correct way to add localization to the package.
When the package is built, the resources from the packes will be collected into a Bundle that will be accessible within the package by using Bundle.module
.
Anywhere you want to use the string in the package, you need to pass Bundle.module
otherwise it will default to looking in Bundle.main
which is the bundle for app, not the package.
- How can we localize strings declared outside of a Text view in SwiftUI?
let stringFromCurrentView = "String Example"
Text(stringFromCurrentView) // Localization doesn't work
That code doesn't localize the string because stringFromCurrentView
is of type String
, so when passing to the Text
it'll call the init that takes a string and does no localization
That's opposed to creating Text
directly with a string literal, which will call the Text.init
that takes a LocalizedStringKey
which can be initialized with a string literal.
If the code above was update so the property was a LocalizedStringKey
, it should work.
let stringFromCurrentView: LocalizedStringKey = "String Example"
Text(stringFromCurrentView, bundle: .module)
String(localized:...)
should work when being run in an app. It doesn't work in the preview because the view modifier .environment(\.locale, ...)
is only used by other SwiftUI code. String(localized:...)
is defined in Foundation and won't use the environment value since it doesn't known about SwiftUI.
- How do I correctly configure a Swift Package in a project to display localized strings from the package where we have strings Localized?
Correct is a matter of personal preference. I've used the approach similar to the struct Strings
containing properties or functions if the string can take values. That's also what the code from tools like SwiftGen generate.
The important thing to remember for any solution is that it's the combination of the string key and bundle (also tableName but it's not applicable here) that allows a localized string to be looked up.