Localization with SwiftUI, how to preview your localized content

With SwiftUI being recently introduced, I was curious if we could take advantage of SwiftUI preview to speed up testing localization and make sure your app looks great for any language.

First, I’ll start with a very simple view, couple labels and a button to get something to work with.

localization-swiftui-starter

From this simple view, I need to extract hardcoded text to be able to localize it. To do so, I create a Localizable.strings file in my project to represent each language. So far, nothing new.

I’ve added French and Chinese Simplified to see how the app would behave.

localization-swiftui-starter

If I run the simulator foreach of those region, I can see the app reflecting the right translation, but it’s quite tedious if you have many languages to test and to adjust the UI.

That’s where SwiftUI becomes really great. Apple introduced with it environment variables, one is dedicated to region and language, something than we can play for our preview.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environment(\.locale, .init(identifier: "en"))
    }
}

By injecting new Locale identifier like fr or zh, the preview reload automatically with the latest translation. Pretty powerful!

localization-swiftui

That’s already really great.

Looking deeper, the Text and Button support specific initializer with LocalizableStringKey. This is how it automatically got the right translation.

init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil)

Although, our View is still hardcoded with those keys, meaning it’s quite easy for developers to make a typo mistake.

I’ve been using code generation tool to handle translation in the past, I’m personally fan of SwiftGen but even if it works with simulator, SwiftGen doesn’t work yet for preview.

But now we know how our View consume the LocalizableStringKey, so we can piggyback on code generating tool for the translation. We could also create our own enum as well to avoid those typo mistakes and allow autocompletion, whatever is easier for you.

After tweaking SwiftGen template, I’ve created one that could fit into SwiftUI just fine. This is part a part of it.

enum Translation {
    
    static var welcomeToMyApp: LocalizedStringKey {
        return "Welcome into my app"
    }
    
    static var startByTappingThisButton: LocalizedStringKey {
        return "Start by tapping this button"
    }
    
    static var startBrowsing: LocalizedStringKey {
        return "Start browsing"
    }
}

So we can replace our hardcoded values to the generated placeholders.

struct ContentView: View {
    
    var body: some View {
        
        VStack(spacing: 40) {
            VStack(alignment: .leading) {
                Text(Translation.welcomeToMyApp)
                    .font(.title)
                
                Text(Translation.startByTappingThisButton)
                    .font(.body)
            }
            
            Button(Translation.startBrowsing) { 
                // TODO
            }
            .foregroundColor(.white)
            .padding()
            .background(Color(#colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)))
            .cornerRadius(10)
            
        }
        .padding()
        .border(Color(#colorLiteral(red: 0.1215686277, green: 0.01176470611, blue: 0.4235294163, alpha: 1)), width: 1)
        
    }
}

I wonder if it’s something we could also apply to other localization tools in iOS like date, currency and number formatter but I believe those aren’t generated on the View side but on the ViewModel (or wherever is the computing logic for it), that’s why I didn’t represent it.

I believe we could go even further and try to preview other localizable elements like colors, images and anything static from your content.

Although, Xcode 11 and Preview mode is great, it’s not yet perfect for computed localization with parameters. At the best, here is what I came up with. LocalizedStringKey doesn’t support parameters so we have to work around.

enum Translation {
    // ...
    
    static func welcome(name: String) -> String {
        return String.localizedStringWithFormat(NSLocalizedString("Welcome %@, this is my app", comment: ""), name)
    }
}

// used as following
Text(Translation.welcome(name: "Ben"))

That being said, it will still work for your app, or if you set up a language on your scheme, the preview will reflect it, but the environment variable unfortunately won’t. I hope it’s something that will be fixed in Xcode 12.


That’s it, using generated code for our translation and SwiftUI, we can take advantage of its fast preview to test our UI foreach language and device in a very short amount of time. Something that we only could wish before.

Happy coding!

© 2023 Benoit Pasquier. All Rights Reserved
Author's picture

Benoit Pasquier

Software Engineer πŸ‡«πŸ‡·, writing about career development, mobile engineering and self-improvement

ShopBack πŸ’°

Singapore πŸ‡ΈπŸ‡¬