How to Internationalize iOS Apps – A Quick Guide
I find my myself routinely surprised about how little information is available on topics that would seem to me quite obvious. Today I decided to try to internationalize app I’ve built – create some locale specific date support and foreign language sets. I figured this would be pretty simple…d’oh. A quick peek on developer.apple.com yields a few docs however many of them are still at Xcode 3.0 and its quite obvious that the menus and commands are all different now. After quite a bit of piking around I finally found something useful:
Many of the docs linked form this one are out of date, but this is the best source of info on developer.apple.com. You can google around and find links to tutorials, however most of them are also way out of date and almost none of them bother to say what version of iOS and Xcode are being used. So for clarity I will say: I’m working with iOS 6.1 and Xcode 4.6. Less impt. – I’m running on OS-X 10.8.2 as well.
Where to Start?
I’ve got quite a bit of experience with this stuff from web programming. If you do not have a background in this area then the best place to start is the above linked library article. It provides a decent background on all of this. GNU GetText is another great place to look at for general information on i18n and l10n in software systems. As you read thru the Apple docs you’ll quickly see that they are written for Xcode 3.0 and all the menus are different now. I skimmed over all of this, but then I found it easier to “go to the source”. Grab the International Mountains sample app and open that up in Xcode.
Check out the directory structure of the project. This app has been setup for Chinese, English, and French – you can see the separate directory structures (ending in .lproj) and their contents. Seems pretty obvious. On a per language basis there are copies of XIB files, string files and then also plist files because there are strings used in things like the icon name for your app.Note that iOS apparently cannot deal with dialects of a language like French vs. Canadian French – OS-X can so I’m not sure what happens with that Chinese sample…maybe this has changed since the Apple docs were written.
It is a bit concerning that the xib files themselves are placed in here. Any changes to the app’s screens will have to be replicated across all the files I suppose. We’ll address some of that later in the helpers and tools section of this post. Open up Xcode and take a look at this project. Open up the resources group and notice that things are nicely organized for you. At least that is good! Click on a xib to open it in the IB tool and then click on the Identity tab to see the configuration. Notice that this app was built back in the stone age for iOS3. I updated all of these to match my config and confirmed I can still run the app in my simulator. You have to change each one individually which is a pain. You should also update all the other spots in build settings likewise.
Adding International Aspects to My App
So now that we have a model app, albeit a very ancient one, we can take a look at how to do this on one of my own projects. I have a project that uses both xib files and where I have hand coded a lot of modal screens and panels. I’ll need to use both IB and some command line tools to fully extract all my strings.
Open up a xib file and click the identity tab in the assistant. About 2/3 down you’ll see a “localize” button. Use that and Xcode will do some “stuff”. If you look in the file system you’ll see that Xcode has moved the file to the en.lproj file and created a plist file. Unfortunately you have to manually keep your project tidy in Xcode itself. If you want to make another language it does not seem like there is a way to do this from IB. What I did was copy the en.lproj folder and then went back to my project, selected the xib file, and added the new xib file to the project. After doing that – Xode restructured the project view and IB location menu to indicate that there is now an english and french localization. I did the same thing with the InfoPlist.strings files. Not exactly intuitive…maybe there is a better way that I’m missing.
Next you will need to create the Localizable.strings files and add them to your project. To add the first one, select your Resources group and then do a File -> New -> Resource ->Strings File. You should add this one to you en.lproj folder and make sure you name it Localizable.strings. To add the next one I had to do it manually because Xcode would crash. Go to finder and copy and paste the file you just created into the fr.lproj folder. Then add this to the Resource folder manually – voila. My project structure now looks like this.
So great – I have separate copies of all my xib files. Now the chore would be to go thru them updating the text with the translation. Sounds painful and it is. The approach does have one benefit – any slight changes for spacing and sizing can be accommodated. There are also scattered strategies for how to automate this and that part of it. The most obvious one to me would be to use placeholders strings in the XIB, but replace them all in the onViewLoad() or viewDidLoad() method, but this would mean I need to setup connections and properties for all the labels. Lots of work…
Once that is set the next step is to go thru all my .m files and replace all the hard coded strings with a call to NSLocalizedString(@”StringKey”, @”StringKey comment”); . That also sounds like a lot of work! One you have gone thru the app and done this, there is a tool that you can run against your .m files to extract the keys and comments to your .strings file. Then you can complete your strings files with the proper values using a format like this:
/* Title used for the Navigation Controller for the detail view */
“detailViewNavTitle” = “Mountain Detail”;
This is straight from the apple example. If you look in the RootViewController.m file you will see that they are in fact doing what I suggested – using the viewDidLoad() method to replace strings in the XIB file.
Well that is a lot of work. I’m going to now actually do this in one of my projects and see how all this theory works out. I’ll post back an update here with my results and any changes in approaches that I come up with.