SwiftUI + Localization

In SwiftUI you are led down the path of just using a string inline in something like Text and it automatically gets converted LocalizableStringKey. Something like Text(“Hello”) will cause a key “Hello” to be exported with a default value of “Hello” for English. Unfortunately this now means that every usage of “Hello” would get the same translation and that might not be intended for ALL usages of a word. We had been accounting for this using a private enum where every string in a file has a dedicated key:

Code Block swift
private enum Strings {
  static var hello: String {
   NSLocalizableString(“com.company.namespace.hello”, bundle: <some>.bundle, value: “Hello”, comment: “description of usage”)
 }
}


This had the benefit of allowing code to not be muddied with long NSLocalizedString blocks. See example.


Code Block swift
struct ContentView: View {
  var body: some View {
    VStack {
      Text(String.hello)
        .padding()
Spacer()
      Text(NSLocalizableString(“com.company.namespace.hello”, bundle: <some>.bundle, value: “Hello”, comment: “description of usage”))
        .padding()
    }
  }
}


This makes the actual code more readable.

We've recently started to bring this pattern to some SwiftUI views and were hoping to utilize Previews to visualize every localization we support, but this enum-based approach does not work with Previews, it always displays English.

NOTE: Although previews don't work, running on device or simulator correctly displays the translated text

If we switch to the string literal approach where the key is the default value, then it works as expected and localizations also show up in the preview canvas when given an overriding locale

Anyone have any ideas/thoughts on how to use the enum approach AND have previews work?

I've tried with both Xcode 11.7 and 12b6.

I'm going to file a Feedback as well in case it's just a bug in the preview handling code.

Thanks!


Here is my simple sample.

English output:

Hello
World
com.hello
com.world

Spanish output:

Hello
World
Hola
Mundo

Code Block swift
struct ContentView: View {
  var body: some View {
    VStack {
      Text(Strings.hello)
        .padding()
      Text(Strings.world)
        .padding()
      Text("com.hello")
        .padding()
      Text("com.world")
        .padding()
    }
  }
}
enum Strings {
  static var hello: String {
    NSLocalizedString("com.hello", value: "Hello", comment: "hello")
  }
  static var world: String {
    NSLocalizedString("com.world", value: "World", comment: "world")
  }
}
struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    Group {
      ForEach(["en", "es"], id: \.self) { identifier in
        ContentView()
          .environment(\.locale, .init(identifier: identifier))
          .previewLayout(.sizeThatFits)
      }
    }
  }
}



I also use this enum based approach for the same reason and noticed the same thing in SwiftUI previews. Sorry, but I haven't found a way to make it work.
Running Xcode 12.3 here and I get the expected output as long as a valid Localizable.strings file is present with the correct translations. That is:

Code Block
"com.hello" = "Hola";
"com.world" = "Mundo";

produces:

Hola
Mundo
Hola
Mundo

Maybe it was just a bug in Xcode? Since you are writing out all your strings anyway, it may be easier to use LocalizedStringKey() rather than NSLocalizedString() and add the strings to the Localizable.strings file instead of an enum.

Babble-on App Localization

SwiftUI + Localization
 
 
Q