Creating a great iOS app is no small feat, yet there is much more to it than great code, gorgeous design and intuitive interaction. Climbing the App Store rankings requires well-timed product marketing, the ability to scale up along with the user base, and utilizing tools and techniques to reach as wide of an audience as possible.
International markets are an afterthought for a lot of devs, but thanks to the painless global distribution provided by the App Store, any iOS dev can release their app in over 150 countries with a single click. Asia and Europe alone represent a continually growing pool of potential customers, many of whom are not native English speakers, but in order to capitalize on the global market potential of your app, you’ll need to at least be conversational in the language of app internationalization.
This tutorial will guide you through the basics concepts of internationalization by taking a simple app called iLikeIt and adding internationalization support. This simple app has a label and a You Like? button. Whenever the user taps You Like?, some optimistic sales data and accompanying image fades in below the button.
But currently, the app is English only – os vamos a traducir!
Note: Another important aspect of internationalization is using Auto Layout, due to changing text sizes. However, to keep this tutorial simple we will not be focusing on Auto Layout, as we have other tutorials for that.
Internationalization vs Localization
Before you start working your way through the tutorial, it is important to know the difference between internationalization and localization, as these concepts are often confused.
Simply put, internationalization is the process of designing your app for international compatibility. For example:
- Handle text input, output processing in the user’s native language.
- Handle different date, time and number formats.
- Utilize the appropriate calendar and time zone for processing data.
Internationalization is an activity that you, the developer, perform by utilizing the system provided APIs and making additions and modifications to your code to make your app as good in Chinese or Arabic as it is in English.
By contrast, localization is merely translating the app’s user interface and resources into different languages, which is something you can and should offload to someone else, unless you happen to be fluent in every language your app will support :)
The first step is to download the iLikeIt starter project you will use throughout this tutorial.
Open the project in Xcode 5 and run the app on the simulator. You should see the following appear after you tap ‘You like?':
As you can see from the screenshot, you will need to localize 4 items:
- UI Element: ‘Hello’ label
- UI Element: ‘You Like?’ button
- Sales Data Text: ‘Yesterday you sold 1000000 apps’
- Image Text: ‘I LIKE IT’
Take a moment to browse the files and folders to familiarize yourself with the project structure. Main.storyboard contains a single screen which is an instance of the
Separating text from code
Currently, all of the text displayed by the app exists as hard-coded strings within Main.storyboard and ViewController.m. In order to localize these strings, you need to put them into a separate file. Then, rather than hard-coding them within your methods, you will simply reference the strings using the file in your bundle.
Xcode uses files with the “.strings” file extension to store and retrieve all of the strings used within the app, for each supported language. A simple method call in your code will lookup and return the requested string based on the current language in use on the iOS device.
Let’s try this out. Go to File > New > File. Choose Strings File unders the Resource subsection as shown below:
Click Next, name the file Localizable.strings, then click Save.
Now that you’ve created the Localizable.strings file, you need to add all of the text that is currently hardcoded into the app. You need to follow a specific, but fairly simple, format like this:
"KEY" = "CONTENT";
These key/content pairs function just like an NSDictionary, and convention is to use the default language translation of the content as the key: e.g. for “You Like?” you would write:
"You like?" = "You like?";
Key/content pairs can also contain format strings:
"Yesterday you sold %@ apps" = "Yesterday you sold %@ apps";
Now switch to ViewController.m, and find the viewDidLoad method. Currently, the app sets the text for the likeButton and salesCountLabel as shown below:
_salesCountLabel.text = [NSString stringWithFormat:@"Yesterday you sold %@ apps", @(1000000)]; [_likeButton setTitle:@"You like?" forState:UIControlStateNormal];
Instead, you will need to read in the strings from the Localizable.strings file you created earlier. Change both lines to use a macro called
NSLocalizedString as shown below:
_salesCountLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Yesterday you sold %@ apps", nil), @(1000000)]; [_likeButton setTitle:NSLocalizedString(@"You like?", nil) forState:UIControlStateNormal];
Macros wrap up a longer snippet of code into a more manageable size, and are created using the #define directive.
If you’re curious what the
NSLocalizedString macro does, control-click on
NSLocalizedString where it is defined as follows:
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
The NSLocalizedString macro uses the localizedStringForKey method to look up the string for the given key, in the current language. It passes nil for the table name, so it uses the default strings filename (Localizable.strings). For full details, check out Apple’s NSBundle Class Reference.
If you use this method, you can put a comment for each string that will appear next to the default strings as an aid for the translator. For example, you could add a comment indicating the context where the string is used.
Enough background info – let’s try it out!
Build and run your project, and it should display the same text on the main screen as before, but where’s the Spanish? Now that your app is set up for localization, adding translations is a cinch.
Adding a Spanish Localization
To add support for another language, click on the blue iLikeIt project folder on the left pane, select the Project in the next pane (NOT the Target), and under the Info tab you’ll see a section for Localizations. Click the + and choose Spanish (es).
The next screen asks you which files you want to localize. Keep them all selected and click Finish. Note: Localizable.strings will not show up in this list, so don’t panic!
At this point, Xcode has set up some directories, behind the scenes, containing separate versions of InfoPlist.strings and Main.storyboard for each language you selected. To see this for yourself, open your project folder using Finder, and you should see the following:
See en.lproj and es.lproj? They contain the language-specific versions of your files.
‘en’ is the localization code for English, and ‘es’ is the localization code for Spanish. For other languages, see the full list of language codes.
From now on, when your app wants to get the English version of a file, it will look in en.lproj, and when it wants the Spanish version of a file it will look in es.lproj.
It’s that simple! Put your resources in the appropriate folder and iOS will do the rest.
But wait, what about Localizable.strings? To let Xcode know you want it localized, select the file using the left pane, and open the File Inspector in the right pane. There you will see a button labeled Localize, click it, choose English (because it’s currently in English), and finally click Localize.
Now the File Inspector panel will show which languages this file belongs to. Currently, as you can see, the file is only localized for English. Add Spanish localization by checking that box to the left of Spanish.
Go back to the left panel and click on the arrow next to Localizable.strings, so it shows the sub-elements. You now have two versions of this file: one for English and the other for Spanish:
To change the text for Spanish, select Localizable.strings (Spanish) and replace its contents with the following:
"Yesterday you sold %@ apps" = "Ayer le vendió %@ aplicaciones"; "You like?" = "~Es bueno?~";
Congratulations, your app is now bilingual!
To test it out and verify everything worked, change the display language on your simulator/device to Spanish by launching the Settings app and choosing:
General -> International -> Language -> Espanol.
If you are still running the Xcode debugger, click Stop in Xcode, then click Build & Run and you should see:
Locale vs Language
1 million is a pretty impressive sales number; let’s make it look even better by adding some formatting.
Open ViewController.m and replace the line that sets the text for
_salesCountLabel with the following:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; NSString *numberString = [numberFormatter stringFromNumber:@(1000000)]; _salesCountLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Yesterday you sold %@ apps", nil), numberString];
Build and Run the app and the number should now be a lot easier to read.
This looks great to an American, but in Spain 1 million is written as “1.000.000” not “1,000,000”. Run the app in Espanol and you’ll see commas used to separate the zeroes. In iOS, number formatting is based on the region/locale, not the language, so in order to see how someone in Spain will view the sales number, open Setting.app and change the locale by navigating to:
General -> International -> Region Format -> Spanish -> Spain
Build and Run the app again and you should now see the properly formatted number like this:
For a little extra work up-front, NSNumberFormatter automatically formats your numbers for the appropriate region. Whenever possible, resist the urge to re-invent the wheel, because on iOS, it usually pays to do things the Apple way.
UI elements in your storyboard such as labels, buttons and images can be set in your code or directly in the storyboard. You have already learned how to support multiple languages when setting text programmatically, but the “Hello” label at the top of the screen has no IBOutlet and only has its text set within Main.storyboard.
You could add an IBOutlet, connect it to the label in Main.storyboard, then set its text property using NSLocalizedString as with the likeButton and the salesCountLabel, but there is a much easier way to localize storyboard elements, without the need for additional code.
Open the disclosure triangle to the left of Main.storyboard and you should see Main.storyboard (Base) and Main.storyboard (Spanish). Clicking on Main.storyboard (Spanish) opens the editor with the localizable text in your storyboard. You should already have an entry for the Hello label which will look something like this:
/* Class = "IBUILabel"; text = "Hello"; ObjectID = "pUp-yc-27W"; */ "pUp-yc-27W.text" = "Hello";
Replace the two occurrences of “Hello” with the Spanish translation, “Hola” like this:
/* Class = "IBUILabel"; text = "Hola"; ObjectID = "pUp-yc-27W"; */ "pUp-yc-27W.text" = "Hola";
Note: Never directly change the auto-generated ObjectID. Also, do not copy and paste the lines above, as the ObjectID for your label may be different from the one shown above.
Since the app uses an image that contains english text, you will need to localize the image itself, as having bits and pieces of English in a mostly Spanish app not only makes your app look amateur, but also detracts from the overall usability and market potential.
To localize the image, first download this Spanish version of the image (right-click -> Save Image As… on most browsers):
Open Images.xcassets and add the image to the asset catalog by dragging and dropping the newly downloaded megusta.png into the list of images on the left. Asset catalogs cannot be internationalized, so you wil need to use a simple work-around to localize the image.
Open Localizable.strings (English) and add the following to it:
"imageName" = "ilike";
Similarly add the following to the Localizable.strings (Spanish) file:
"imageName" = "megusta";
From now on, you will use the
imageName key to retrieve the name of the localized version of the image. Open ViewController.m and add the following line of code to the
[_imageView setImage:[UIImage imageNamed:NSLocalizedString(@"imageName", nil)]];
If needed, switch your simulator/device to Espanol, then Build & Run and you will see the localized version of the image displayed:
Congrats! You now have all the tools required to localize your apps for multiple different languages.
Note: This is just one way to do things, useful if you have different filenames per language. A perhaps better way of doing this is to localize a resources folder, as described in this article.
For a final bonus, let’s localize the name of the app itself. Your Info.plist has a special file (InfoPlist.strings) in which you can put string overrides for other languages. To give the app a different name in Spanish, open Supporting Files > InfoPlist.strings (Spanish) and insert the following:
"CFBundleDisplayName" = "Me Gusta";
This changes the name of the app as it appears on the Springboard.
Exercise: Internationalizing Audio files
If you’ve got this far, you should be comfortable with the basics of internationalization. This is a simple exercise where you’ll test out your newly acquired knowledge by taking two different audio files, one in english and the other on Spanish, and playing the appropriate file based on the user’s selected language.
Here is a brief description of the necessary steps:
- Download the sample audio files
- Copy box-en.wav first audio file to the project.
- Open the file inspector for the audio file and select the localize button, make sure you select english and spanish as the supported languages
- Rename the second audio file (box-es.wav) to be the same as the first one (box-en.wav) and copy it to the es.lproj folder.
- Make sure you select the “Replace File” option in the Finder prompt.
Where To Go From Here?
Here is the Final Project with all of the code you’ve written in the above tutorial.
Now that you know the basic techniques for internationalizing an iPhone app, add a foreign language to one of your existing apps or when designing your next app. As you have seen, it takes almost no time to implement, you open up your apps to a wider, more diverse audience, and your non-English speaking audience will thank you for it!
For the actual translation, you may be able to get away with using Google’s free translation service at http://www.google.com/translate, but the results are very hit or miss. If you can spare a few bucks, there are several third party vendors listed at the bottom of Apple’s Internationalization and Localization page. Pricing varies from vendor to vendor, but is typically less than 10 cents per word.
If you have any questions, or advice for others, regarding internationalization, please join in on the forum discussion below!