Localization of Xcode iOS Apps, Part 2

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:

Figure 1: New Xcode iOS Project

2) Click on the checkbox. You should see the following dialog box:

Figure 2: Adding Base Internationalization (Localization)

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:

Figure 3: 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:

Figure 4: Addng Label to iPad storyboard

5)   Next, add “Localizable.strings” to the project:

Figure 5: Adding Localizable.strings to Project

For now, keep it in the root group of the project, but make sure you save it to the /en.lproj folder:

Figure 6: Keeping in root group, and saving to /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:

Figure 7: Project Structure After L10n Enabled and Localizable.strings File Added

Your actual file structures will resemble this:

Figure 8: Folder & File Structure After L10n Enabled and Localizable.strings File Added

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:

./UpdateStoryboardStrings.sh ~/Code/TestLocalization

…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:

Figure 9: Directory/File Structure After UpdateStoryboardStrings.sh is Executed the First Time

7)   Now, add the storyboard .strings files in the /en.lproj folder to the project:

Figure 10: Adding Storyboard .strings Files to Project.

Figure 11: Select MainStoryboard*.strings Files from /en.lproj Folder.

Figure 12: MainStoryboard_iPad.strings & MainStoryboard_iPhone.strings Files in 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:

Figure 13: Adding New Language to Project.

Select a language (Spanish, for example). This will bring up the following dialog:

Figure 14: Reference .strings Files for Adding New Language.

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:

Figure 15: New L10n .strings Lookup Files Added to 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:

MainStoryboard_iPad.strings (English)

MainStoryboard_iPad.strings (Spanish)

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:

Figure 16: MainStoryboard_iPad.strings (English)

Figure 17: MainStoryboard_iPad.strings (Spanish)

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:

Figure 18: Running the iPad app in the Simulator.

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:

Figure 19: iPad app Displaying Spanish Text.

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:

Figure 20: Storyboard with Button and Textfield.

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:

Figure 21: Spanish Translations of Button in Simulator.

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.

Conclusion

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.

About Gregory Hill

Greg is a Senior Software Engineer with AIS. He is the new iOS development "subject matter expert". Prior to coming to AIS, he spent two-and-a-half years working independently, developing his own iOS apps and pitching product ideas to businesses in the Mid-Atlantic region. He has been involved in the software industry since the mid-80s, developing and supporting applications for such companies and institutions as Sony, Wadsworth Publishing and Johns Hopkins Hospital.

  • Pingback: Localization of an Xcode iOS App, Part 2 | iOS ...()

  • Jay

    Gotta say thank you. I used this tutorial (and used SmoothLocalize translation service) and my revenue doubled. My app is now available in 21 countries, and Im reaching 99% of the iOS audience instead of 35%.

  • Chris

    I would recommend to change the script a bit to this:

    $ diff UpdateCodeStrings.sh ~/script/UpdateCodeStrings.sh

    95c95

    > $stringsFile

    > echo “$finalKey = $finalKey;n” >> $stringsFile

    Then this makes more sense
    self.title = NSLocalizedString(@”Heading”, @”Heading of the first View (help for the translator)”);

    The English Translation is then directly available since the key is used as translations (that’s how genstrings from apple works as well)

  • Pingback: Translation Tragedy « Under The Bridge()

  • Thanks for this script and a great tutorial!

    Could you also make a script that goes throug the storyboard stringsfiles for iPad and iPhone and synchronizes the localization?

    I have the same 300 strings in storyboard.strings for iPhone and iPad and I’m done translating the iPhone strings, but field identifiers are different so I can’t copy-paste and I would love to not have to translate the same strings twice.

    I’m thinking the script could check which is newer or it could be two scripts with one copying from iPad to iPhone and one the other way.
    The script would need to search for matching base language text in “text = “Base language text”” and copy localized text in “.text” = “Localized text”;” on the next line from iPhone to iPad (or the other way)

    I was looking into making this script last night, but I’m afraid I’m not entirely understanding the awk procedure 🙂

  • tangbineml

    How to dynamically load in the program based on the story board in base.lproj international language

  • Holub

    There is another interesting service that provides sdk and online update for ios apps translation: http://localize.io

    You can manage you localisation files without the need to think about which version of the app you’re updating. You also don’t need to recompile the app each time you add a new language.