Text.init<S>(_ content: S) where S : StringProtocol localizes its argument. SwiftUI documents that overload as displaying "a stored string without localization" — it is the verbatim path, which is what a String variable binds to. Text(verbatim:) and Text(_ key: LocalizedStringKey) both behave correctly; only this overload diverges.
Sources/SkipSwiftUI/Text/Text.swift:22:
@_disfavoredOverload public init<S>(_ content: S) where S : StringProtocol {
self.init(spec: TextSpec(key: LocalizedStringKey(String(content))))
}
LocalizedStringKey(_:) forwards to init(stringLiteral:), so the string becomes a lookup key and SkipUI._Text resolves it against EnvironmentValues.shared.locale.
Reproduction
Localizable.xcstrings with base language en, key Welcome, fr translation Bienvenue:
let resolved: String = "Welcome" // a String variable, not a literal
var body: some View {
VStack {
Text(resolved) // StringProtocol overload
Text(verbatim: resolved)
Text("Welcome") // literal -> LocalizedStringKey
}
.environment(\.locale, Locale(identifier: "fr"))
}
|
Skip Fuse / Android |
SwiftUI |
Text(resolved) |
Bienvenue |
Welcome |
Text(verbatim: resolved) |
Welcome |
Welcome |
Text("Welcome") |
Bienvenue |
Bienvenue |
Measured on an Android emulator (API 36).
Why it matters
An app that resolves its own strings — for an in-app language setting independent of the device language, for instance — hands Text a finished string. If that string is also a key in the catalog (which it is whenever the catalog's source language is the app's own, since then the keys are the source strings), it gets translated a second time, into whatever \.locale happens to be. The result is a screen rendered in two languages, and it is invisible on iOS.
Text(verbatim:) is the workaround, but it only works where the author knew to reach for it: on iOS the plain call is already verbatim, so nothing signals that the Android build differs.
Note that Skip Lite is a separate case and not what this is about: SkipUI.Text has a non-generic init(_ key: String) that intentionally treats a String as a key, because after transpilation to Kotlin a literal and a variable are indistinguishable at the call site (CHANGELOG, 2023-12-22). Skip Fuse compiles with real Swift overload resolution, so the distinction is available here and the SwiftUI contract can be honored.
Text.init<S>(_ content: S) where S : StringProtocollocalizes its argument. SwiftUI documents that overload as displaying "a stored string without localization" — it is the verbatim path, which is what aStringvariable binds to.Text(verbatim:)andText(_ key: LocalizedStringKey)both behave correctly; only this overload diverges.Sources/SkipSwiftUI/Text/Text.swift:22:LocalizedStringKey(_:)forwards toinit(stringLiteral:), so the string becomes a lookup key andSkipUI._Textresolves it againstEnvironmentValues.shared.locale.Reproduction
Localizable.xcstringswith base languageen, keyWelcome,frtranslationBienvenue:Text(resolved)Text(verbatim: resolved)Text("Welcome")Measured on an Android emulator (API 36).
Why it matters
An app that resolves its own strings — for an in-app language setting independent of the device language, for instance — hands
Texta finished string. If that string is also a key in the catalog (which it is whenever the catalog's source language is the app's own, since then the keys are the source strings), it gets translated a second time, into whatever\.localehappens to be. The result is a screen rendered in two languages, and it is invisible on iOS.Text(verbatim:)is the workaround, but it only works where the author knew to reach for it: on iOS the plain call is already verbatim, so nothing signals that the Android build differs.Note that Skip Lite is a separate case and not what this is about:
SkipUI.Texthas a non-genericinit(_ key: String)that intentionally treats aStringas a key, because after transpilation to Kotlin a literal and a variable are indistinguishable at the call site (CHANGELOG, 2023-12-22). Skip Fuse compiles with real Swift overload resolution, so the distinction is available here and the SwiftUI contract can be honored.