Dan Wood is co-owner of Karelia Software, creating programs for the Macintosh computer. He is the father of two kids, lives in the Bay Area of California USA, and prefers bicycles to cars. This site is his older weblog, which mostly covers geeky topics like Macs and Mac Programming. Go visit the current blog here.
Useful Tidbits and Egotistical Musings from Dan Wood
I've been spending the last few days, on and off, dealing with localizations of the new Sandvox version we are trying to get ready. Each time we have a new version coming out, I have to go through a lot of tedium and hassle to successfully get Sandvox updated and fully functional in several languages. It really could be a lot easier than it is now.
The current version of Sandvox has eight localizations. Besides English, it's in Danish, French, German, Italian, Japanese, Traditional Chinese (Taiwan) and Simplified Chinese (China). The localizations are done by some wonderful, amazing volunteers. (We may have to drop a couple of supported localizations for our forthcoming 1.5 version; we've lost a couple of our volunteers unfortunately.) The volunteers all use iLocalize, which is a great (but by no means the only) localization tool out there; if they don't have the program we get them a license for them to use it.
The ideal workflow goes something like this: When we are getting near a release, we send a private build of Sandvox out to these localizers. They each open the application bundle with iLocalize, and it figures out what strings need to be translated, keeping track of the strings that have already been translated. When they are done, they export the resources (.strings files, .nib files, and a few .html files) and email them back to us. Using a shell script, I copy the resources into a new directory structure that parallels our subversion repositories (since the topology of an application package is not the same as our source code). Another script copies the files into the corresponding places in our subversion repository, careful not to stomp on the .svn directories.
In an ideal world, that would be pretty easy. The problem is, a release is a moving target. Even though we declared we were "frozen" for strings about a month ago, little things crop up -- a bugfix may change a string or require a new error message. Or last-minute changes, like the debut of MobileMe, mean that we have to go through another round of localization. And frustratingly, we find that even after we have accepted a localization, merged it into our source repository, and built a new version, that new version isn't quite fully localized. Somehow, some strings got missed.
The worst problem in all this is nib files. Once we have localizations in play, a change to a nib (adding or repositioning a label, hooking up a forgotten outlet or action or binding, etc.) has to be propagated to all the localizations, or there will be a bug in your program when it's running under other languages. This means that the programmer has to go through and make the same change n times, or rely on the translator to know that they have to rebuild the translated nib from the English (source) version. (I'll assume that English is your source language for the rest of this article.)
It's very difficult to make sure that all our languages are really working when maintaining multiple nib files is so fragile.
".strings" files, though they are a lot simpler and just text files, are not without their problems as well. If you fix a typo in your English text, you have to make sure to fix that source string in all of your translated .strings files, or your source will not be found and show up as English in your otherwise translated program. You are not likely to know that there is a problem unless you can test every message your program will ever encounter in all its translations. (Peter Hosey's Localization Helper is a utility that can help with this a bit, though.)
The problems of dealing with translations are so great that several developers have abandoned localized nibs completely — CocoaTech has done this for Path Finder, as has Delicious Monster for Delicious Library. I'm sure there are others. The alternate approach is to have a single nib, putting in translated strings on-the-fly, and possibly resizing the views programatically to make the elements fit. (Languages such as French and German take up quite a bit more space than English!)
On-the-fly localization is a decent idea — we're considering it for our next major version of Sandvox to alleviate some of these hassles — but it's not without its drawbacks. You really have to prepare your nibs for the "worst case" scenario of elements fitting. There are some scenarios in Sandvox that I'm not sure how we could work around: for instance, some labels are so long in their translation that we have to split them into two or three lines to fit. This affects the horizontal positioning of everything else. If we left room for the eventual translation, then the English version would look strange. When you are aligning elements with each other (for instance, right-justifying several labels to the left of some checkboxes), you would need to move all the items that were aligned. Or, you just leave a lot of blank space in the English version — a feat that sounds like a good idea but is hard to accomplish in small elements such as inspector windows.
It seems that Apple needs to take Internationalization ("i18n" for those who don't want to type a twenty-letter word) a lot more seriously and integrate this directly into Interface Builder and Xcode.
Xcode is slightly aware of localized files, and Interface Builder and Xcode are slightly aware of each other, so I don't see why there couldn't be more direct i18n support built in.
Imagine if your localized files were just always synced with their original counterparts. If I adjusted a string in my Objective C file, the English .strings file could be automatically updated, and so would the translated versions (with a warning marking to indicate that somebody will need to check the translations.) If I deleted an English string, the corresponding localizations would be deleted too, since they wouldn't be needed. If I added a new string, the translated files would have entries for those new strings, marked as needing to be translated.
If I modified a nib, I'd only have to deal with the English nib. I could add a connection or binding, fix the spelling of an action's method name, etc. and not have to worry about the translated nibs getting out of sync. If I added or changed the contents of a textual label, then the translated nib files could be marked as needing to be translated or checked. If I deleted something, they would be gone in the translated nib. If I moved or resized something, the translated nibs might indicate a warning if the corresponding string is conflicting with something.
(I'm always finding that it's very difficult to see when a string has not been given enough room in a translation, and we're forever getting complaints that buttons in German are truncated. Not surprising, considering I don't speak the language and thus I can't tell at a glance if the text has been truncated! There needs to be big bold warnings that text is getting truncated in your nibs!)
Perhaps what is needed is an overhaul of the way that nibs are stored. Yes, i know that the .xib format in Leopard is new, but couldn't nib files be split into connections (one per nib, period), strings (one per localization), and layout (one per localization if needed), so that it's impossible to get the kinds of inconsistencies that we developers are always fighting?
Maybe we need more ways to lay out items in a nib so that automatic repositioning is easier, so you can avoid a lot of the headaches of multiple layouts to check. In the simple cases where a button is the only element on a "line" and you make the text wider, that's a no-brainer. But when you start dealing with groups of items side by side that need to align with each other, I don't think that there is enough information in the struts-and-springs model we use now to automatically "stretch" things appropriately to fit different languages automatically.
What do you think? Are there any tricks to localization that I haven't covered here?