The iOS App Localization for Multiple Targets

We love object oriented programming. And let’s admit it, we try to fit it in everywhere. So, when it came to localization of an ios application that had multiple targets for same codebase, I wanted a hierarchal structure for the strings where I could generalize common ones in one base file and put specific ones somewhere else.

Before dwelling in the solution, let’s recreate the scenario for a better understanding.

the application

The application under consideration is quite interesting. It uses same codebase to generate multiple applications with different preferences and behaviours. Most of the things are common with slight variations as per client requirements.

the localization

Having a lot of common behavior between targets, many of the strings that need to be localized are common as well. And there are a few strings that are common to all client server applications like network error messages.

So I wanted a system where I would generalize common strings in one file [per language] and then be able to override and even add additional strings specific to the target. [or specific language of a particular target] in some other files.

What I was intending was more like how CSS works. You define all your styling in one .css file to be shared amongst all the html files. But you are also able to override the styling for individual files.

how iOS localization works

As you would already know, iOS provides a nice API for getting localized string as following.

let stringValue = Bundle.main.localizedString(forKey: key, value: nil, table:nil];

And it also provides a macro for quick access as

NSLocalizedString(key, comment: "")

iOS, by default, looks for strings in Localizable.strings file. However, we can also provide a custom file for iOS to look for strings into. And this is where things get interesting.

To provide a custom file, we can use the API as mentioned above in following manner.

var localizedString: String = Bundle.main.localizedString(forKey: key, value: nil, table: "AnotherLocalizableStringsFile")

The table parameter takes a string argument AnotherLocalizableStringsFile which is the file containing the strings.

Another interesting parameter is the value parameter that takes in a string that should be returned in case no string is found matching the given key.

So following piece of code would return Invalid Key assuming the provided key does not exist in the file.

let stringValue = Bundle.main.localizedString(forKey: "Wrong_key_passed", value: "Invalid Key", table: "TargetLocalizable"];

the solution

By using the combination of these two interesting parameters, we can devise a method to suit our requirements. We will ask iOS to look for strings specific to target in target specific strings file and fall back to Localizablestrings file when it comes to loading generic strings common to all targets.

Here’s how the piece of code looks like.

let stringValue = Bundle.main.localizedString(forKey: "Key", value: NSLocalizedString("Key", comment: ""), table: "TargetLocalizable"];

So, iOS looks for the string first in the given TargetLocalizable.strings file. If it doesn’t find it there, it would search in the base Localizable.strings file.

So all I had to do was to place all the strings common to all targets in Localizable.strings file and put the additional and overridden strings specific to the target in TargetLocalizable.strings file.

Bamm… All Done