I'm working on a SwiftUI iOS app that uses a custom framework I have created to be able to share code with an app extension.
In that framework I've created a CustomError enum with a message parameter that needs to be translated so that users see the error in their own language. Often, I have to pass interpolated strings to the CustomError to clarify where the error is.
The translation of the message works well but for interpolated strings. With those the test is translated but the but the placeholders of the interpolated string are not replaced by its value.
To showcase the problem, I've created some demo code which I've separated in two parts: the part of the app and the part of the framework.
Below, is th part of the app:
struct ContentView: View {
@State private var message: String = "No messages"
@State private var otherMessage: String = "No messages"
var body: some View {
VStack {
Text("Super app")
Text(message)
Text(otherMessage)
}
.padding()
.onAppear {
let fwCode = FrameworkCode()
do {
try fwCode.doStuff()
}
catch {
self.message = error.localizedDescription
}
do {
try fwCode.doOtherStuff()
}
catch {
self.otherMessage = error.localizedDescription
}
}
}
}
///Localizable.xcstrings for the app:
{
"sourceLanguage" : "en",
"strings" : {
"Super app" : {
"localizations" : {
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Super aplicación"
}
}
}
}
},
"version" : "1.0"
}
and next the part of the framework (the framework is created from Xcode goint to File -> New -> Target -> Framework):
public enum CustomError : LocalizedError {
case myError(message: LocalizedStringResource)
public var errorDescription: String? {
switch self {
case let .myError(message):
return String(localizedFromFramework:message)
}
}
}
public class FrameworkCode {
public init() {}
public func doStuff() throws {
let param = "XBox"
throw CustomError.myError(message: "The best console is \(param)")
}
public func doOtherStuff() throws {
throw CustomError.myError(message: "The best console is PlayStation")
}
}
public extension String {
init(localizedFromFramework name: LocalizedStringResource, comment: StaticString? = nil) {
self.init(localized: String.LocalizationValue(name.key), table: "Localizable", bundle: Bundle(identifier:"eversoft.TestFramework"), comment: comment)
}
}
///Below is the code for the Localizable.xcstrings for the farmework:
{
"sourceLanguage" : "en",
"strings" : {
"The best console is %@" : {
"localizations" : {
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "La mejor consola es %@"
}
}
}
},
"The best console is PlayStation" : {
"localizations" : {
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "La mejor consola es la PlayStation"
}
}
}
}
},
"version" : "1.0"
}
When executing the app, the error thrown from the method doOtherStuff() is translated correctly, but the error thrown by the method doStuff() gets translated but the placeholders are not replaced by the value.
What is causing the placeholders not to be replaced?
I'm using Xcode 16.2 and I've tested this with iOS 17.4, 18.1 and 18.2 and all of them behave equally.