post-photo

Localization on iOS: how Wanari deals with it.

Localization, why?

If you want to reach the most possible users with your app, you will probably need it to support multiple languages. Luckily we have a solution: localization. In this article I will mostly talk about how we started localization, what difficulties we faced, how we solved them, and what’s our current (not final) form.

text

How does it work?

As a charm, all you need is a Localizable.strings file and some lines of code. Easy, right? :) Well, almost :D

This, or better these .strings files are collections of key value pairs. I wrote “these files”, because you need one for every supported language. The syntax of the files is the following:

"key" = "value";

where the key, as always, is a uniqe identifier for the value.

If we have a simple login screen with username and password labels and input fields, and a login button, our .strings files can contain these pairs (English and Hungarian language used):

"TXT_USERNAME" = "Username";
"TXT_PASSWORD" = "Password";
"BTN_LOGIN" = "Login";
"TXT_USERNAME" = "Felhasználónév";
"TXT_PASSWORD" = "Jelszó";
"BTN_LOGIN" = "Bejelentkezés";

If we want to use a localized string programmatically, we should use the function NSLocalizedString:

func NSLocalizedString(_ key: String, tableName: String? = default, bundle: Bundle = default, value: String = default, comment: String) -> String

This will return the value of the key using the proper localization. Let’s say we have usernameLabel as an outlet, we can easily add localized text:

usernameLabel.text = NSLocalizedString("TXT_USERNAME", comment: "")

All we need to care about is to never mistype the key. What happens when you don’t use the right key? The mistyped string will be displayed. Not a huge problem, but annoying, and time consuming. The other downside is that you have to remember every key letter by letter, which is the hardest part.

How to avoid that? Enums, for example. You can create a simple enum, where you have to type the correct key only once:

enum LoginStrings: String {
    case username = "TXT_USERNAME"
    case password = "TXT_PASSWORD"
    case login = "BTN_LOGIN"
}

We can even improve it to return the localized value, not just the key:

enum LoginStrings: String {
    case username = "TXT_USERNAME"
    case password = "TXT_PASSWORD"
    case login = "BTN_LOGIN"
 
    func localized() -> String {
        return NSLocalizedString(self.rawValue, comment: "")
    }
}

With this enum it’s easy to get the desired texts:

usernameLabel.text = LoginStrings.username.localized()

This is a good start, we have no string to mistype, but we still need to update the structure every time a new value is added. Luckily, there are some libs that automatically generate these enumerations from resources like R.Swift.

When installed and the proper script is added (it can be found on the GitHub page), R.Swift automatically generates a struct called “R” from the resources in the project.

The usage is easy, similiar to the enum:

usernameLabel.text = R.string.localizable.txt_USERNAME()

There it is, we have an easy to use autogenerated struct. Now, only the .strings files need to be generated and managed one by one for every language. This can lead to some issues, even with a few languages. Personally I think, that having a document, which contains every key and text for all languages at once, is the best approach. Unfortunately I couldn’t find an ultimate converter for that, however with some text editing any document can be converted pretty easily. It can be even better, if you can write a script that does the job :).

Strings in UIKit Components

Sometimes you define texts in the Interface Builder, in a label for example, where you can’t localize them :( The easiest workaround for this problem is to create a subclass from UILabel, let’s call it UILocalizableLabel.

@IBDesignable final class UILocalizableLabel: UILabel {
    @IBInspectable var localizable: String? {
        didSet {
            guard let localizable = localizable else { return }
            text = NSLocalizedString(localizable, comment: "")
        }
    }
}

We wanted a property accessibe from the IB, that can define the localizable keys, and set the corresponding text for the label. To do so, we created an IBInspectable variable, called localizable. For this to be editable in the IB, you need to make your class IBDesignable. After these few lines we can add localizable keys to our label. In a simple UILabel we define the text we want to show:

text

and after we change the type of the UI component to UILocalizableLabel, we can add the key of the localizable string:

text

text

Finally, when we run our app, we can see, that the text is represented in Hungarian:

text

This approach can be used for textfields, buttons, or any UI object that represents text.

Final thoughts

These are the approaches we use for localization at Wanari. The autogenerated .strings file makes our work easier, and faster, and the R.Swift makes it typesafe. Feel free to share your ideas in the comments.

Wanari is an 18-year-old custom software development company. We take our job seriously and love knowledge sharing! If you want to learn more about us and our projects, follow us on Facebook, LinkedIn or Twitter.

member photo

He is a junior iOS developer at Wanari and has been with us since January. His coding style and favorite language are the same: Swift.

Latest post by Róbert Klacso

A Beginner’s Guide to XML Parsing in Swift