Internationalise your iPhone apps with Xcode
There are only a few simple steps to follow in order to make an iPhone app easily adapt itself to the language and cultural context of the device, and with Xcode you can quickly add the features that make your code internationalised. But let me set the scene first by briefly clarifying what is meant by internationalisation and localisation.
What is internationalisation?
Internationalisation (often referred to as I18N, because of the numbers of letters between the I and the final N) is the refactoring or writing of software in such a way that it does not contain any hard wired strings/images that might need to be rendered in different languages or any hard wired assumptions as to the presentation of number/dates/currencies and the like. It is important to stress at this point that internationalising code is not merely about the translation of text: numbers are rendered using different conventions in different countries (think of . or , for thousand separator and floating point, or the grouping of digits, done by four instead of three in Japan) and similar concerns apply also to dates and currencies. Generally, anything subject to variation based on language or culture should be externalised from the source code and made available as a resource file, so that various language- or context-specific versions of it can be provided and the appropriate one can be dynamically loaded at run-time based on the language/country setting of the system where the application is running.
What is localisation?
As for localisation (aka L10N), it stands for the process of adding resource files for a new cultural context and language into the internationalised application. It is the job of translators, not so much of developers, to produce the material necessary for localising a piece of software. And generally you internationalise code once, and then localise it as many times as you need.
The Xcode approach
So how do we internationalise an iPhone app with Xcode? I would say we face three main concerns:
- the hard-wired strings that we need to externalise
- values (numbers, dates, times, etc) that are rendered to the user and are subject to localisation
- images and any other resource files that might need converting to other languages and regional contexts
Working with strings
As you write your code, identify all of the text that is part of the UI and needs localising. Instead of typing the text as a literal string in your program (for example,
@"Welcome"), use the I18N macros of Cocoa. What the macros do is invoke the
localizedStringForKey:value:table: method of
NSBundle. In the following diagram you’ll find the four available macros and what they expand to
You will have noted that comments are never used in expanding the macro. But they are used by the
genstrings command, which will parse your source code looking for the aforementioned macros and create string tables for them. The comment will then appear in the corresponding entry in the table file. If you haven’t specified a table file name, then the name Localizable.strings will be assumed. Here’s a simple example.
Let’s say this is a snippet from some MyAppController.m
[button setTitle:NSLocalizedStringFromTable(@"Click here", @"Buttons", @"Label of a button") forState:UIControlStateNormal];
[label setText:NSLocalizedString(@"Your name", @"Text of a label") forState:UIControlStateNormal];
In the Terminal, run this command in the same folder as the above source file
genstrings -o en.lproj MyAppController.m
You will now find two files in the en.lproj folder (provided the folder already exists), one called Localizable.strings
/* Text of a label */
"Your name" = "Your name";
and the other Buttons.strings
/* Label of a button */
"Click here" = "Click here";
If you’re not lucky enough to be starting your internationalised application from scratch, you will then have to go through your source code and change all of the occurrences of UI strings into calls to the localised macros. You then run genstrings on the
.m files as shown.
At this points, in order to localise your application, you just need to create subfolders for each of the languages you want to support (for example it.lproj, fr.lproj, de.lproj, and so on) and place copies of the generated strings file in each folder, translating the second part of each line in the language of the containing folder, like so for Localizable.strings in it.lproj
/* Text of a label */
"Your name" = "Il tuo nome";
In order to make sure that numeric and date values appear in a consistent format to the locale of the iPhone device, you should use the formatter class of Cocoa whenever you want to render them into text. These classes offer a series of options as to how the value should be presented and within your settings will use locale-dependent information to display it. The locale that will be used is that of the device (chosen in the Settings app) or the one passed into the formatter instance with the
There are two formatters available in Cocoa: the
NSDateFormatter (for dates, times, or both) and
NSNumberFormatter (for numbers, currencies, percentages).
Finally you might want to localise other types of resources, for example image files that contain fancy text or symbols that depend on the user’s country or language. In this case, we turn to
NSBundle again, like we did for strings (albeit indirectly). Therefore, let’s take a closer look at how resources are loaded from a bundle (we will assume the
[NSBundle mainBundle] instance, as it’s the most common scenario in iPhone development).
When you request a resource from a bundle, the system will look for it in the following locations:
- Global (nonlocalized) resources
- Region-specific resources (based on the user’s region preferences)
- Language-specific resources (based on the user’s language preferences)
- Development language of the bundle (as specified by the CFBundleDevelopmentRegion in the bundle’s Info.plist file.)
So, unlike Java for example, if you want to internationalise your app, do NOT create a top-level resource, but only localised resources in the appropriate
pathForResource:ofType: and similar on
NSBundle will all use the search pattern above – on iOS you can also target certain resources to specific devices by adding an
~iphone suffix, which is not considered part of the name, but only use to load files selectively. You can also add a
@2x suffix to an image name when you want to provide an iPhone4-specific version (based on a 640×960 screen). If you use
UIImage, that will also behave in the same way.
This is on top of the option of placing the files in locale-specific folders.
What about XIBs
UPDATE JUN 2012: Also check out this post for a convenient way of internationalizing your XIB files…
All of your XIB files will be loaded in locale-specific folders, if provided. Xcode makes it easy to create the appropriate directory structure: you just right-click on the XIB file in the Groups & Files, select Get Info from the context menu, and you’ll find a Make File Localizable button in the Info panel. Clicking on that button will create a localised version of your XIB for English, and you can add more languages (you have to translate the files yourself though ;-))
Finally, any resource you reference within the XIB itself will automatically use localised resources if available…