Localizing an iOS App

Joyce Echessa

The App Store is a global market. Taking international users into consideration and building an app that adapts to them can greatly increase your market reach and/or your business growth.

Instead of forcing users to make do with the default language, you could instead give them an app that presents information in their language. With the availability of so many apps that could be used instead of yours, adapting for international audiences could go a long way in retaining users.

This is achieved through internationalization and localization, two terms that are often used interchangeably and whose meanings are usually confused.

Internationalization is structuring an app to support localized content by not hardcoding any country or culture specific information, e.g. dates, currency, numbers.

This is done by utilizing the system provided APIs whenever this kind of information is outputted, thus making the application adaptable to different locales without any additional code changes.

Localization is the process of providing appropriate data and resources in your application based on the user’s Language settings.

It involves not only translating the app’s text into the different supported languages, but also providing different resources like audio files, images and nib files to the different supported languages and cultures.

The two activities complement each other. Internationalization provides the base infrastructure needed to support and facilitate localization. We are going to look at how to implement both of these in an iOS app.

Internationalization

The first step in internationalizing an app is to identify all the culture-specific information it displays. This consists of currency, dates, numbers etc.

Apple provides classes that should be used instead of harcoding, so that if you decide to distribute your apps to other markets, you will not need to change the base code.

It is usually advisable to internationalize your app from the very beginning even if you don’t plan to localize it. It doesn’t require that much extra effort to build an internationalized app, but the payoffs for internationalizing can be great.

Should your company grow and require a localized app that targets more markets, you will have a harder time localizing an app that wasn’t internationalized in the first place.

A lot of code changes and testing will be necessary to make sure no culture-specific information was left hardcoded. If the application had been internationalized, there will be less code rewrites and additional testing.

To internationalize your app, you should use the following classes and functions when outputting dates, numbers, strings and text.

NSDateFormatter

The NSDateFormatter is used whenever a date is to be displayed. It has a locale property which is set to the device’s current locale. When NSDateFormatter is used to create a date, it checks its locale property and displays the date in the appropriate format.

Below is a code snippet showing how the NSDateFormatter can be used. Here we set the locale, but for your app this won’t be necessary as it will use the device’s locale setting.

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = NSDateFormatterMediumStyle;
dateFormatter.timeStyle = NSDateFormatterNoStyle;

NSDate *date = [NSDate date];

[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];
NSLog(@"Date for locale %@: %@", [[dateFormatter locale] localeIdentifier], [dateFormatter stringFromDate:date]);

[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"]];
NSLog(@"Date for locale %@: %@", [[dateFormatter locale] localeIdentifier], [dateFormatter stringFromDate:date]);

[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_UK"]];
NSLog(@"Date for locale %@: %@", [[dateFormatter locale] localeIdentifier], [dateFormatter stringFromDate:date]);

[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"de_DE"]];
NSLog(@"Date for locale %@: %@", [[dateFormatter locale] localeIdentifier], [dateFormatter stringFromDate:date]);

Below is the output of the above code.

Date for locale en_US: May 15, 2014
Date for locale fr_FR: 15 mai 2014
Date for locale en_GB: 15 May 2014
Date for locale de_DE: 15.05.2014

In the example above, we manually set the locale that the NSDateFormatter uses. In a real application, you let the device’s setting determine this. In such cases, you can test the output by changing the Region Format in your phone’s settings. Go to Settings -> General -> International -> Region Format -> [Select Region]

NSNumberFormatter

NSNumberFormatter is used to internationalize currency values and number formats. Numbers and currencies are usually formatted differently in different countries. Instead of hardcoding the format of these figures, use NSNumberFormatter which will display the figures according to the device’s locale.

The example below uses NSNumberFormatter to output a value according to different locales.

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;

[numberFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];
NSLog(@"Number format for locale %@: %@", [[numberFormatter locale] localeIdentifier], [numberFormatter stringFromNumber:@123456.789]);

[numberFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"]];
NSLog(@"Number format for locale %@: %@", [[numberFormatter locale] localeIdentifier], [numberFormatter stringFromNumber:@123456.789]);

[numberFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_UK"]];
NSLog(@"Number format for locale %@: %@", [[numberFormatter locale] localeIdentifier], [numberFormatter stringFromNumber:@123456.789]);

[numberFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"de_DE"]];
NSLog(@"Number format for locale %@: %@", [[numberFormatter locale] localeIdentifier], [numberFormatter stringFromNumber:@123456.789]);

The following is the output from the above code.

Number format for locale en_US: 123,456.789
Number format for locale fr_FR: 123 456,789
Number format for locale en_GB: 123,456.789
Number format for locale de_DE: 123.456,789

Apart from formatting numbers, NSNumberFormatter also formats currency values. If its numberStyle property is set to NSNumberFormatterCurrencyStyle it will output currencies with the appropriate decimal separator (a dot or comma), thousands separator (a comma or a dot) and currency symbols which will be properly placed (in some countries the symbol comes before the value while in others it comes after).

The following code snippet uses NSNumberFormatter to output currency values for different locales.

NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;

[currencyFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];
NSLog(@"Currency format for locale %@: %@", [[currencyFormatter locale] localeIdentifier], [currencyFormatter stringFromNumber:@100000]);

[currencyFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"]];
NSLog(@"Currency format for locale %@: %@", [[currencyFormatter locale] localeIdentifier], [currencyFormatter stringFromNumber:@100000]);

[currencyFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_UK"]];
NSLog(@"Currency format for locale %@: %@", [[currencyFormatter locale] localeIdentifier], [currencyFormatter stringFromNumber:@100000]);

[currencyFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"de_DE"]];
NSLog(@"Currency format for locale %@: %@", [[currencyFormatter locale] localeIdentifier], [currencyFormatter stringFromNumber:@100000]);

The above will have the following output.

Currency format for locale en_US: $100,000.00
Currency format for locale fr_FR: 100 000,00 €
Currency format for locale en_GB: £100,000.00
Currency format for locale de_DE: 100.000,00 €

NSLocalizedString

Instead of hardcoding text in your application, you should put the text into a strings file (called a strings file because of its .strings extension) and update your code to load the data it needs from those files. If you decide later to localize the application, it would just be a matter of translating the strings without requiring a change in the interface (not always the case, as some languages are so different you would require to also localize your nib files).

To load the text from the strings file we use NSLocalizedString.

NSLocalizedString is a Foundation macro which is used to get the localized version of a string. It takes two arguments – key and comment. The key uniquely identifies the string to be localized and the comment indicates the context where the string is used. The comment can be set to nil but you might want to include a descriptive comment for your own reference and to aid a translator.

To internationalize strings in your code, replace literal strings with the NSLocalizedString function. For example instead of the following

NSString *string = @"Hello World";

you would instead use

NSString *string = NSLocalizedString(@"HELLO", nil);

Where you will have a strings file with the following

"HELLO" = "Hello World";

Localization

Once we have a properly internationalized application, it is easier to translate its resources and user interface into the different languages we want to target. This is what localization is. We provide appropriate resources (text, audio, images, nib files, e.t.c) for each language.

To see how his works, we will localize a simple application. Download the starter project which we’ll use in this tutorial.

The project folder downloaded contains the main project labeled Hello World and an image named image.jpg that we will use later. The application has a single view as shown below with an image, label and a button.

Final application preview

Any resource in a project can be localized, whether it is an image, audio file, xib file or a storyboard. When a resource is localized, the localized copy is added to the application bundle. The resources are placed in language-specific directories, known as lproj directories. The directories are given the name of the localization suffixed with lproj. For example en.lproj for English and es.lproj for Spanish.

The localization codes are a combination of a language code and an optional region code. For language codes, you can use either the ISO 639-1 or ISO 639-2 conventions. ISO 639-1 is the preferred way to identify languages. However, if an ISO 639-1 code is not available for a particular language, the three-letter codes defined by the ISO 639-2 specification may be used instead.

For regional codes, you can use the ISO 3166-1 conventions. This specification uses a two-letter, capitalized code to identify a specific country. This is optional. Check here for a full list of language codes.

We are going to first localize our interface, the Main.storyboard file. Xcode has a feature called Base internationalization, which simplifies the process of localizing XIB and storyboard files. Instead of creating storyboard or XIB files for every language you want to support, Xcode creates a Base.lproj directory which contains the main storyboard or XIB files.

With this in place, localization can be undertaken by creating the Localizable.strings files. For languages whose views cannot be changed by localizing only the strings, you can create the full XIB or storyboard files.

To begin, click on the Hello World project folder on the left panel, select the Hello World project on the next panel and click on the Info tab. Notice that Base international has been enabled by default.

Enable base internationalization

At the Localizations section, click the + and choose Spanish (es). Click the Finish button on the window that pops up with the selection shown below.

Choose files dialogue

In the project navigator, you will notice that Main.storyboard and InfoPlist.strings can now be expanded. Expand the storyboard file and you’ll find both the base storyboard and a strings file for Spanish.

The Main.strings (Spanish) file contains a list of strings in key/value pairs. Xcode generates these strings from the textual elements on the interface. The keys relate to the object IDs of the UI items and the values are the strings that should be translated.

Our Main.strings (Spanish) file contains the following:

/* Class = "IBUILabel"; text = "Hello"; ObjectID = "Fg0-t0-XFF"; */
"Fg0-t0-XFF.text" = "Hello";

/* Class = "IBUIButton"; normalTitle = "Say Hello"; ObjectID = "pr4-OE-zWY"; */
"pr4-OE-zWY.normalTitle" = "Say Hello";

Edit the file as shown below

/* Class = "IBUILabel"; text = "Hello"; ObjectID = "Fg0-t0-XFF"; */
"Fg0-t0-XFF.text" = "Hola";

/* Class = "IBUIButton"; normalTitle = "Say Hello"; ObjectID = "pr4-OE-zWY"; */
"pr4-OE-zWY.normalTitle" = "Saludar";

That is it for localizing our view’s text. To localize the image, select the image file in the project navigator Supporting Files -> Images -> image.jpg. In the File Inspector on the right, click on the Localize button.

Localize a file

In the dialog box that appears, select English. Upon confirmation, the Localization section on the File Inspector will now show English as selected. Check the Spanish checkbox.

Localize a file

You should now see both the English and Spanish versions on the project navigator. At the moment, the Spanish image is the exact same copy of the English image.

To change this, in Finder, locate the image file named image.jpg that was in the starter project you downloaded for this tutorial. Copy it into your project’s Hello World/es.lproj directory, replacing the image that is there.

You should note that your resources for the different lproj directories should have the same name as their counterparts.

Run the application and on changing your devices language to Español you will see the views language translated and the image changed. Path to change language settings: General -> International -> Language -> Espanol.

Different versions of app

To see the changes, you might need to reset the simulator and do a clean build. To reset the simulator, navigate to iOS Simulator -> Reset Content and Settings in the menu bar. To do a clean build, navigate to Product -> Clean in the Xcode menu bar.

We have localized the static resources in our application. We are now going to look at how to localize dynamic text. We will use the NSLocalizedString macro we looked at earlier to load text from a strings file.

Go to File -> New -> File. Choose Strings File under the Resource subsection.

Adding a strings file

Click Next, name the file Localizable.strings and then save it.

By default, NSLocalizedString looks for a file named Localizable.strings. So it is important to stick to this naming convention, otherwise you will have to type the name of your .strings file every time you reference a localized string.

Localizing this file will be similar to how we localized the image file. Select Localizable.strings in the project navigator and on the File Inspector click the Localize button.

Select English in the dialog box that appears.

With the strings file still selected, check the Spanish check box in the Localization section of the File Inspector.

You should now have two versions of Localizable.strings file.

Localized strings

Place the following in the English strings file.

"TITLE" = "Greeting";
"GREETING" = "Hello";
"OK" = "Ok";

And in the Spanish strings file, place in the following.

"TITLE" = "Saludo";
"GREETING" = "Hola";
"OK" = "Bueno";

We want the above message to appear when the Say Hello button is pressed. Create an action named sayHello for the button. (I am assuming that the reader knows how to do this. If not look at this guide). Edit the JKEViewController.m file as shown below.

- (IBAction)sayHello:(UIButton *)sender {

    [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"TITLE", nil)
                                message:NSLocalizedString(@"GREETING", nil)
                               delegate:nil
                      cancelButtonTitle:NSLocalizedString(@"OK", nil)
                      otherButtonTitles:nil] show];
}

Run the application and test it. You should see results similar to the below image. (If you don’t see expected changes when you run the project, reset the simulator and do a clean build as previously described).

Final applications

To localize the app name add the following string to the InfoPlist.string (Spanish) file:

"CFBundleDisplayName" = "Hola Mundo";

Auto Layout

Auto Layout was introduced in iOS 6 and is an important functionality to use when localizing your application. Before Auto Layout, developers used to set the app’s user interface using coordinates and bounds and the interface was static and unchangeable.

With Auto Layout, you set constraints on the UI views which describe the relationship that they have with each other. During runtime the Auto Layout system determines how to lay out each view using the set constraints. This creates flexible layouts which adapt to different screen sizes (e.g. 3.5′ or 4.0′ iPhone), orientations and text size (which is now changeable because of Dynamic Type).

This is important with internationalization because of the varying text lengths of different languages. If you lay out views that display text, they might expand greatly to accommodate text from other languages (or they might take up less space than they do with the base language). If Auto Layout is used, the other views will adapt to this change according to how you set the constraints.

We won’t go into much detail about Auto Layout here, but be sure to check the documentation.

Conclusion

We have looked at ways in which an app can be localized and thus make it usable and appealing to users in different locales. The app store provides developers access to a global market. Taking the international market into consideration when building your app can greatly increase your market reach. For more on localization, check the apple guides.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Christian Bonato

    Well written, well documented. This is a fundemental point in every app developement.

    • http://www.echessa.com/ Joyce Echessa

      Thanks Christian.