SwiftUI: translations with LocalizedStringKey

April 3, 2021

It took me a while before i figured out the different options for using translation in SwiftUI, such as NSLocalizedString and LocalizedStringKey. So i wanted to jot down a quick post outlining the easiest way to translate, or localize, a SwiftUI based app.

Given this translation key "ui.label.hello" = "Hello World"; in your Localizable.strings. When i first started out making translated text labels i used something like the following code.

struct HelloWorld: View {
    let label = NSLocalizedString("ui.label.hello", comment: "Hello World!")
    
    var body: some View {
        Text(label)
    }
}

While this works like a charm it’s a lot of code for a small thing and SwiftUI has something simpler too.

struct HelloWorld: View {
    let label = LocalizedStringKey("ui.label.hello")
    
    var body: some View {
        Text(label)
    }
}

This does the exact same thing, does not require a comment, and gets rid of that old-skool NS prefix. But.. this is still a lot of code for a simple thing. For some reason it took me a while to figure out that the following also works fine.

struct HelloWorld: View {
    var body: some View {
        Text("ui.label.hello")
    }
}

Nice! It’s just a string now! An added benefit here is that you become a bit more flexible in creating these localization keys. You can just pass around Strings now.

struct ParentView: View {
    var body: some View {
        HelloWorld(label: "ui.label.hello")
    }
}

struct HelloWorld: View {
    let label: String

    var body: some View {
        Text(label)
    }
}

So now that we have nice short keys for our translations, can we still do string interpolation? Yes we can! Let’s look at an example. If my view looks like this

struct Countdown: View {
    var body: some View {
        Text("ui.label.countdown \(100)")
    }
}

In my translation file i can do the following to use that variable number in my translated text.

"ui.label.countdown %@" = "Just %@ days left to reaching your goal.";

Which would display Just 100 days left to reaching your goal.

In fact you’re not even stuck with just strings and numbers, you could even display Images inline. Do note you need to build for iOS 14 and up to use this, or have an alternative for iOS 13 like in this example.

struct Gift: View {
    var body: some View {
        if #available(iOS 14.0, *) {
            Text("ui.label.foryou \(Image(systemName: "gift"))")
        } else {
            HStack {
                Text("ui.label.foryou \("")")
                Image(systemName: "gift")
            }
        }
    }
}

Which will allow you to inline a nice little SFSymbol gift icon in your text.

"ui.label.foryou %@" = "Good work, you've earned a %@";

You can even format dates right in the Text label!

struct Goal: View {
    
    // Don't put this code in your view in a real app though
    var formatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        return formatter
    }
    
    var body: some View {
        Text("You reached your goal at \(Date(), formatter: formatter)")
    }
}

Which, depending on the current locale, would display something like You reached your goal at Apr 3, 2021. Pretty neat right!