Localization is a common feature that you should implement in your app if you want to improve user satisfaction. Support for multiple languages allows you to reach more customers, access a global market, and improve your return on investment.

This isn’t just another article about how to add localization to your iOS project. In this guide, we’d like to introduce our new tool for localization 一 SKLocalizable.

There are two ways to set the language of your app:

  1. Based on the device language
  2. Manually in the app

Both of these options use the same core mechanism: .strings files and NSLocalizedString methods.

Main steps of app localization

1. Define the list of supported languages in your project settings. You don't have to do this step when you first create your app, though, as you can add new languages at any moment.

SOurce: steelkiwi.com

2. Add a Localizable.strings file and fill it with the corresponding key-value pairs. We highly recommended that you plan the key structure. You can find our recommendations for this at the beginning of the Localizable.strings file in the demo project.

source: steelkiwi.com

3. Use defined localized keys in the code:

label1.text = NSLocalizedString("Key1", comment: "")

label2.text = NSLocalizedString("Key2", comment: "")

label3.text = NSLocalizedString("Key3", comment: "")

Seems easy, doesn’t it? But let’s imagine that you have more than 20 localizable components on each screen, each of them should have an outlet, you need to set localization manually in different places in your code, and localized values depends on UI state. Not very convenient, is it?

So how can we simplify the internationalization process? Further, we’ll provide some simple examples of common solutions.

Common ways to localize your app more easily

Let’s have a look at the most common ways to simplify app localization:

1. Shorten the localization code

One common way to simplify localization is by transforming the Objective-C style function into Swift. A common solution is to create a wrapper via a String extension:

extension String {

  var localized: String {

    return NSLocalizedString(self, comment: "")

  }

}
label1.text = "Key1".localized

label2.text = "Key2".localized

label3.text = "Key3".localized

Now the code will be shorter and easier to read, but it will still require outlets for all localizable components and we’re excluding the possibility of using parameters. Also, if we need to insert some values in a localized string, we should use the String.localizedStringWithFormat(format, arguments) method. It’s an absolutely different method, so we need to create another extension method with parameters:

func localized(arguments: CVarArg) -> String {

  return String.localizedStringWithFormat("KeyParams", arguments)

}

If you don’t like that the variable and function have the same name, you can combine them into one function with the parameter value set by default and implemented according to transmitted parameters.

But the main problem is still the same 一 the order of arguments. The localization function will put them in the localization value in the order they’re passed to the function. So if you need a different parameter order in the text for some languages, this will be a problem.

2. Put static values in the storyboard and localize the .storyboard file

Values that are static while the app is working and don’t require outlets can be set right in the storyboard components and localized directly in the .storyboard file.

source: steelkiwi.com

There are two ways to do this:

1. Interface Builder Storyboard. An additional storyboard file will be created under the corresponding .lproj folder in the file structure and will be linked to the original .storyboard file in the Project Navigator Editor.

source: steelkiwi.com

+ This method allows you to not only translate components using the Interface Builder properties but also adapt your layout for a specific language. You can even make some additional fixes or add UI features for specific localizations.

+ There’s no need to create outlets for all components; you can define static values right in the .storyboard file.

- If you need to make changes in your screen layout, you’ll need to do it in every .storyboard version.

- You can miss a component during localization, for example, if components are located one above the other, such as the tableView with its content and the empty state view.

2. Localizable.strings. The .strings file will be added and linked to your storyboard.

source: steelkiwi.com

+ There’s only one UI file, so it’s easier to make layout changes.

+ All texts from the storyboard are exported in the .stringsfile, so there’s no chance you’ll miss any component.

- You should be very careful while translating. The strings file contains a specific key format, which is intended to link values to components via the ID of an object defined in the storyboard. If you accidentally change this ID (no matter whether it’s in the .storyboardor .strings file), this connection will be broken.

- If you need to add, remove, or replace UI components, you need to manually make the corresponding changes in the .stringsfile, which usually isn’t convenient.

SKLocalizable

Here’s a short list of functions available in our localization tool and descriptions of how they work. You can find the full text in the Readme documentation.

1. Direct (manual) localization via code

There are two methods for manual in-code localization:

Plain text with parameters: localized(tableName: String? = nil, bundle: Bundle = .localization, arguments: Dictionary<String, Any>? = nil) -> String

Example:

label1.text = "Key1".localized()

label2.text = "Key2".localized(tableName: “SecondLocalizableFile”, bundle: .main, arguments: ["value_key" : valueVariable])

Text with plural values: localizedPlural(tableName: String? = nil, arguments: CVarArg...) -> String

Example:

label3.text = "Key3".localizedPlural(arguments: pluralValue)

We didn’t combine these in one function in order to allow developers to clearly indicate plural usages in the code. Using these methods, you’ll receive the localized value from the .strings or .stringsdict file, and you can use it anywhere you want, for example in the UI text, debug or log messages, or icon names.

This method will be suitable if you decide to use device-based localization because it doesn’t handle language change events. You can handle language changing manually by subscribing to the languageChangednotification in NotificationCenter.default.

This method also allows you to use named parameters. You can pass them in Dictionary<String, Any>form in the arguments parameter. Check out point 5 in the Demo App Localizable.strings file for the naming convention for parameters in strings. A parameter key should be inserted in the localization value and wrapped in $(dict_parameter_key).

Example:

Localizable.strings:

”KeyParameterized” = “Passed value is $(value_key)”;

Usage:

label.text = "KeyParameterized".localized(arguments: ["value_key": valueVariable])

2. Automated localization via code

At present, any NSObject class can set a value for its localizationKey property. This property is used for detecting language change notifications and assigning translated values to components. You can check out the list of components that implement this behavior out of the box in section 3.1 of the documentation.

Example:

label.localizationKey = "Key"

This method will automatically update the component content with the value corresponding to localizationKey, but it can’t be used for localization with parameters or plurals.

3. Localization in the Interface Builder (Storyboard or Xib file)

The localizationKey property mentioned in the previous paragraph is marked as @IBInspectable for components in section 3.1 of the documentation.

source: steelkiwi.com

This is the same mechanism used for automated localization via code, but using this method, you don’t need to create @IBOutlet for every component that you need to localize.

4. Changing the language

The default language is device-based. You can change it using the following:

Bundle.localization = Bundle.init(languageCode: languageCode)

where languageCode is a language code, such as en, es, or de. If you aren’t sure about the language code, you can take it from the name of the .lproj folder after the new project language has been added. After setting the new bundle, a languageChanged notification will be sent via NotificationCenter.default.

All components with a defined localizationKey will be subscribed to this notification, so they’ll update their localizable content automatically. However, you can also subscribe to this notification in your code and update your components manually.

Need help?

If you need to build a custom application with multi-language support, get in touch with our sales team and we’ll be more than happy to assist. You might also like to look through our portfolio of software solutions we’ve worked on.

Useful links