This post is the second part of an ongoing series on localization of iOS apps. Please read part one here, if you missed it.
While this post on the MacRumors Forum is a good tutorial for bringing Localization (L10n) to your iOS app, it’s a little sparse in detail, has some updates that change the process, and assumes a few steps. A couple of bugs have also been discovered that need to be worked around to successfully initiate localization in a new iOS app in Xcode.
Today I’ll attempt to clarify some of these steps, and dig into the minutiae that are important to a successful development and deployment cycle. What follows is an example of creating an app from scratch and adding localization features.
There are two shell scripts available at https://github.com/AppliedIS/iOSL10n: UpdateStoryboardStrings.sh and UpdateCodeStrings.sh.
UpdateStoryboardStrings.sh is used to pull localizable text from your storyboard files and put them in the appropriate .strings files. UpdateCodeStrings.sh is used to parse Objective-C *.m code files for all NSLocalizedStrings(key,comment) function calls and update the Localizable.strings files for each language for which the app is being localized. Details on how to use UpdateStoryboardStrings.sh will be provided below, while UpdateCodeStrings.sh will be discussed in Part 3 of this series.
I’m assuming you already know how to create a new iOS app in Xcode. This project is also available at https://github.com/AppliedIS/TestLocalization–Part-1-.git.
Setting up the App in Xcode
Follow the basic steps for creating an iOS app. In order to make full use of the latest L10n features available in Xcode, you should be using version 4.5 or later. This example was built using 4.6.1.
Establish Base Internationalization
1) Once you have created the app, you will need to (a) select the Project folder, (b) select the Project details and (c) click on the “Info” tab. You should see “Use Base Internationalization” in the bottom pane:
2) Click on the checkbox. You should see the following dialog box:
If you have more than one MainStoryboard in your project and they don’t all show up initially, simply cancel this dialog and click the checkbox again. It may take a couple of iterations before they all show up. Once they are all available, and you select the ones you want to use, click on “Finish.”
You won’t notice any significant changes, but “Base” should be added as a language in the Localizations panel:
3) You now need to create the .strings files for your storyboards. However, before you execute this step, you must populate your storyboard(s) with something. It can be a dummy label; it doesn’t matter. But there has to be something in the storyboard, or the .strings files that get created by the ibtool command (a command-line tool that comes bundled with Xcode, and which is run inside of the UpdateStoryboardStrings.sh shell script reference at the beginning of this article, and will be used below) will be flagged by Xcode as corrupt. If this happens, there is no way to recover the process. You will not be able to build the app. A bug has been opened with Apple (bug #13630796; see this thread on the Apple Developer Forums for further discussion).
4) Place a label in the center of one of the storyboards:
5) Next, add “Localizable.strings” to the project:
For now, keep it in the root group of the project, but make sure you save it to the /en.lproj folder:
Though not directly related to the localization of the storyboard files, Localizable.strings is an important file for the L10n process if you are creating text in your code that needs to be localized.
In Project Navigator, you should now see this:
Your actual file structures will resemble this:
The storyboards are in /Base.lproj and the .strings files are in /en.lproj.
6) If you haven’t already, download the two scripts referenced above, and execute UpdateStoryboardStrings.sh. The proper command should looks something like this:
…where the full path to the root directory of the current project is passed in as a command-line argument. If there are spaces in the path, then double-quotes will need to be placed around the full path. For example:
./ UpdateStoryboardStrings.sh “/My Code/TestLocalization”
The basic algorithm behind UpdateStoryboardStrings.sh is thus:
- Move to the project directory supplied at the command-line;
- Look for storyboards in the project sub-directories and loop through the available ones;
- If the associated .strings file doesn’t exist for each storyboard, create it;
- If the .strings file is new or the storyboard has been modified more recently, then use the ibtool command to extract any new text from the storyboard and add it to the appropriate .strings file. Make this addition for each localized language applicable for this app.
Once you execute this command, .strings files will be created for whichever storyboards you have in the project, in this case
MainStoryboard_iPad.strings and MainStoryboard_iPhone.strings. You will now have storyboard .strings files in both the /Base.lproj and /en.lproj folders:
7) Now, add the storyboard .strings files in the /en.lproj folder to the project:
Add New Languages
Okay! You are now ready to add new languages to your app.
8) Once again, select the project. In the Localizations panel, click on the “+” button. This will bring up a comprehensive list of languages you can add to the project:
Select a language (Spanish, for example). This will bring up the following dialog:
You are only interested in .strings files as references, so make sure those are selected, and the .storyboard files are de-selected. You may have multiple references to “InfoPList.strings.” If you elected to enable unit tests for your app during the initial creation of your app, then an additional InfoPList.strings file will be created. Make sure both are selected and click on “Finish”.
You will notice two immediate changes to the project:
First, the language you selected has been added to the list in the Localizations pane (A). Second, InfoPList.strings, Localizable.strings, and the *storyboard.strings files are now all “groups” (B). If you open each group, you will now see representative files for each language. So, for example, in the “MainStoryboard_iPad.strings” group, there are now two files:
These are the lookup files used by the app when displaying text formatted by MainStoryboard*.storyboard.
At this point, you have everything in place to make your iOS app work in multiple languages based off of one core storyboard. Now it is simply a function of determining which language(s) you want to localize for, and updating and managing the .strings files that act as the lookup sources for each language. We’ll look at that in the next section.
L10n in Action
Now, let’s see how localization actually works in Xcode with an iOS app. We’ve already added a label to the layout, and created the base .strings files that will be used as lookups for each language. If you look at MainStoryboard_iPad.strings you will see this:
Initially, MainStoryboard_iPad.strings (Spanish) will appear exactly the same. They look a little confusing, but the important thing to understand is that these are lookup files. The text on the left of the equals sign (“Ucl-cK-DfT.text” in this case) is simply the storyboard’s way to identify what object, and what field in that object, has text that needs to be assigned to it. The text on the right is the text being assigned. These are the “key/value” pairs.
8) If we want to have the Spanish translation of “My Spectacular Label!” appear when the app is running in Spanish, we just put the translation in the Spanish version of the .strings file. Let’s do that. According to Google Translate, the translation becomes “Mi Sello Espectacular!” Edit the Spanish .strings file with that change.
But how do we actually see the Spanish version of the app running? You do this by setting the language the device is using.
Changing Languages in the iPad Simulator
You change languages on an iOS device by going to Settings->General->International->Language, and selecting the language you want. This forces a general restart of all of the apps currently running on your device. Once an app restarts, it will reference the language of the device, and use that as the base language.
9) To do this in the iPad Simulator in Xcode, simply start the app in the Simulator. You will see this:
10) Next, press Shift-Command-H (simulates the Home button), and then change the language to Spanish (Español). The Simulator will quit the app, and you will get an error in Xcode, but you can ignore that. Go back to the Simulator, and scroll to the screen in the Simulator with your app’s icon, tap it, and then you should see:
Updating the Storyboard
Now that we’ve seen the basics of how Xcode and iOS manage localization, let’s add a couple of new objects to the iPad storyboard and update the .strings files.
11) First, add a button and a textfield to the storyboard:
Make sure you connect the textfield to an outlet and the button to an action in your code. We’ll use this textfield in the next article when we look at Localizable text in code.
12) Go back to the command-line and run UpdateStoryboardStrings.sh again. When you look at the English and Spanish versions of MainStoryboard_iPad.strings, you should see that the button text has been added, and the already-translated text for the original label hasn’t been touched. Good. Now, provide the translation for “Press Me” (“pulse mí” according to Google Translate), and run the app:
Every time you make a quantitative change to the storyboard (i.e., add or remove an object that contains displayable text), you will need to run UpdateStoryboardStrings.sh. You will still need to provide the translations, but the process of updating all localized storyboard .strings files with key/value pairs for new fields will be handled with this script. Later, we will look at having Xcode run the script every time you run a new build.
At this point, you have done everything you need to in order to develop and deploy a storyboard-based iOS app that is localized. Every time you make a change to the storyboard(s) you will need to:
a) Run the UpdateStoryboardStrings.sh script;
b) Edit the appropriate lookup .strings files to add the translated text for the new objects in the storyboards.
To localize the app for any additional languages, just follow the steps in the “Add New Languages” section above.
In the next article (Part 3), we will continue with a look at how to localize any text being generated in the code. We will also look at cleaning up the project’s organization a bit, and how to run the UpdateStoryboardStrings.sh script automatically with every build. In the final article (Part 4), we will look at how to establish what language is being used on the device and programmatically notify a web service in order to download data in the appropriate language.