Localization is a requirement, here’s a guide

Why read this post?

In the last year Growl has gone through a lot of changes, and translation is one of the big ones. We’ve gone from not actively translating Growl and our other applications at all, to making it a priority. This change has gotten us from under 10 languages that were starting to stagnate due to my stubbornness for doing it right, to 24 languages which are between 95% and 100% completed because we are closer to doing things right. This is a huge change for us, and it’s time to share how we did it so that others can follow similar steps.

What’s all this?

For anyone who is involved in the process of making software, making sure that the end user/customer is able to use your application is usually priority number 1. But what if the person using your software doesn’t speak the same language that you do? Luckily on most platforms you can translate (or have someone else translate) your software.

On OS X this translation process is called Localization. With it being 2012, if you aren’t translating your software you need to start now. The world has gotten much smaller than it was ten years ago, and the localization process is getting easier on at least OS X. However if you don’t know a thing about translating your software then this may seem like a mountain of work.

A large percentage of people will use your software who do not speak the same language that you speak. Let’s take a look at this fancy map:

Screen Shot 2012-09-17 at 10.51.46 PM.png

This map shows the sales for Growl for the last year. Darker colors indicate more sales. The United States is the clear winner here, but even within the States not everyone speaks the same language. There are a large amount of countries on this map, and very few have zero sales. Even the Republic of Maritius has some sales. Ignoring other languages than your own is a crucial mistake in this day and age.

Keep in mind that this post is focused on mac apps, but if you make iOS apps then the things discussed here should translate over in a meaningful way. If you work on another platform like Windows or Linux, it’s a good idea to skim through this post, and then find the specifics for your platform.

The obligatory cheat sheet

For this post, you will need to know the following terms and acronyms:

Also, these are the links throughout the post, listed here for easy reference:

Some History

Localization on OS X has traditionally been a real pain in the neck. You had to maintain a separate localized nib per localization. Imagine if you have 15 NIBs, and you wanted to add 20 languages. That adds up to 300ish nibs. You would need to modify the size of a string on a per language basis. If you added a new interface element you would need to add it to each language. If you redesigned your entire app interface, you redesigned it 20 times for 20 languages.

Some time in between 2009 and 2010 I halted the localization effort for The Growl Project. I found that the above process was really just not tenable with a development team in decline and no really automated system in place (and none i could find that was acceptable), it just didn’t seem like it was going to work.

Refocusing

Things all changed in 2011 for us. In 2011 we decided to try looking at how to do localization again. It was during this investigation that we found two methods that appeared to be acceptable on the maintainable side of things, but nothing on the automation side yet. The result of this shift now has Growl at 24 languages and growing at a rapid pace, so these steps were pretty effective.

The first was a blog post by Wil Shipley located here. The page starts out with some great statistics about usage in different regions, and just goes from there. The meat of the post talks about a method of localizing interfaces with strings files via bindings, which is pretty ingenious. Since Wil typed up his blog post about this, I’m going to try to avoid repeating what he says there. Go read his post, it’s definitely good stuff. Load it up, I’ll wait.

Done reading? Good, let’s move on.

The second thing that we looked at was the new autolayout. Autolayout is similar to the post by Wil in that it uses/can use strings files translation, rather than xib/nib translation in order to actually load the different languages. The difference is that auto layout also helps you define the layout based on math, with a bit of magic mixed in. Definitely check out auto layout on os x if you haven’t already, it’s the bees knees.

There are reasons to go with either method, and they have to do entirely on if you are targeting 10.6+ or 10.7+. 10.6 does not have support for AutoLayout so you would need to use the Wil Shipley bindings method. If you are using targeting 10.7+, you can use AutoLayout. Both are valid, the 10.6+ Wil Shipley method still works on 10.8, etc etc.

The first thing you’re going to have to do is learn how to use NSLocalizedStrings. Basically this will help your app with the string in your code pointing to the string elsewhere in your app, either in a XIB or a strings file. Your code will end up looking something like this:

    NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Are you sure you want to quit?", nil)
                                     defaultButton:NSLocalizedString(@"Yes", nil)
                                   alternateButton:NSLocalizedString(@"No", nil)
                                       otherButton:nil
                         informativeTextWithFormat:NSLocalizedString(@"If you quit Growl you will no longer receive notifications.", nil)];
    [alert setShowsSuppressionButton:YES];

    NSInteger result = [alert runModal];
    if(result == NSOKButton)
    {
        [[NSUserDefaults standardUserDefaults] setBool:[[alert suppressionButton] state] forKey:@"HideQuitWarning"];
        [NSApp terminate:self];

Which turns into this window in English.

Screen Shot 2012-09-17 at 11.18.18 PM.png

The next thing you would want is to run genstrings. Now for Growl we run genstrings automatically as a build phase that runs this:

genstrings -o ./Core/Resources/en.lproj ./Core/Source/*.h Core/Source/*.m

Which takes input looking for *.h and *.m, and outputs the strings into a Localizable.strings file.

Once you have the localizable strings though, there are still other issues. String files can have duplicates, they can be formatted incorrectly by translators (or you). They can even be processed into the wrong localization. I’ve accidentally processed Romanian translations into a Norwegian update in the past.

The solution is to automate this whole process, along with adding some validation in. That’s where Transifex comes in.

Transifex

Note: I’m going to talk specifically about Transifex, but there are probably other solutions that do the same thing online. Transifex is what we use and that’s that.

Anyhow, so to solve these problems:

We started looking for a better solution, and we ended up with Transifex. Now, transifex did have one or two issues when we started using their service a while ago. For instance, we use RTF files in some sections of Growl and Transifex does not support that type of file. However, the majority of all of our apps use strings files either through AutoLayout or the Wil Shipley method as previously mentioned. For strings files transifex worked out perfectly.

The way it works is this.

  1. Generate the localizable string you want to use.
  2. Upload it to transifex.
  3. Translators notice the updated file, and translate it. Either through the website or by downloading the file. They can also use a tool like iLocalize and upload the translations.
  4. Once the translation is completed, you can download the file.
  5. Put the file into place.

The trick to this is that Transifex also includes a utility called tx. The tx utility has a plethora of options, but we’ve found that this works best:

tx pull -a –minimum-perc=100

What this does is pull down every translation that is 100% done. Once it’s downloaded you can process the translation into place, but now the translated resource lands on your disk in a very predictable place. Which means that you can script updating your resources. Which is what we did.

For updating the resources we made a script that overwrites what exists in the clone with what was pulled down from transifex. It’s a pretty straight forward process.

Effectively here is how we update Growl translations now. Keep in mind that I used to have to set aside 3-10 hours a week to handle this, which is now down to about 2 minutes.

  1. Perform an hg pull -u to update my local clone.
  2. Get into my localizations directory.
  3. Execute tx pull -a –minimum-perc=100 to pull down translations from our project on transifex.
  4. Run rake update_growl, rake update_extras or some other rake update_ command to update the appropriate applications.
  5. Add any new resources to the appropriate xcodeproj.
  6. Check it all in and push the changes.

This process takes about a minute to two minutes if there are no Xcode changes, and then a tad bit longer if there are. We opted to not automate adding items to Xcode since Xcode isn’t really to be trusted in all scenarios. Do keep in mind that when you add a localizable resource to Xcode, if you add it to the bottom of the list for that resource Xcode will likely crash due to an off-by-one error that we found.

Overall it took a while to get this whole process right, but once we nailed it down it’s served us well. Localization is a worthwhile en devour to work through and I really recommend it to anyone.

One more thing

This is only the beginning for us with regards to localization. I’d ideally like to see something like twice or three times as many languages. For that I think we need to go a few steps further from where we’re at.

One very interesting idea would be to have a transifex translation client. I could imagine using the very awesome Greenwich to facilitate this. The translator loads up Greenwich, inputs their transifex login information and then they can just go to town translating different resources. The same would hold true of iLocalize if it could submit information to transifex directly as well.

I really look forward to where translating applications is going to go now that it’s very easy to at least set up the infrastructure. Getting people to actually translate applications is the other half of this equation, but overall people are generally willing to help so that they can see their favorite applications in their preferred language(s).

Fin

Get with the program if you haven’t and get your app ready to translate and then get it translated. Nothing but good things will come of it.

 
111
Kudos
 
111
Kudos

Now read this

The place is on my screen

I read a post by some guy talking about how the compuhyperglobalmeganet is “still the place”. Luckily he’s wrong. Using “still” implies that eventually the web won’t be the place, but then he goes on to sell the web dev platform. I’m... Continue →