June 27 2009
ibtool Caveats
I've previously covered how Apple's command-line utility ibtool can be used to automate localization tasks: generating a .strings file, localizing an English XIB, and incrementally updating a localized XIB. It's a fantastic tool, included in the Xcode development installation, and works well for both Mac OS X applications and iPhone apps. I use this automation extensively on Mac OS X (10.5): 14 XIBs across 11 languages means many, many files to keep in sync. However, the heavy use has exposed an annoying bug in ibtool: silent errors.
"Could not be parsed"
When ibtool encounters an expected bad character, like an unescaped double-quote, it verbally fails, supplying the message below.
This error is helpful, but it doesn't identify the offending line. To do that, we can use another Apple command-line tool: plutil. (Thanks to Cocoa Musings for pointing this out.) plutil can parse a .strings file and provide the line the bad character was on. It's a very useful program when the .strings file that failed has 3K lines, and the problem is a single unescaped double-quote. ibtool generates these verbal parse errors on all alphanumeric characters and a number of others (" ' : . / - _ $).
Silent Errors
Unfortunately, ibtool (and plutil) fails silently on these other characters:
{ } [ ] ; , < > ? \ | = + ! ` ~ @ # % ^ & * ( )
Let me say that again: ibtool fails silently. The program stops incorporating localized strings when it encounters one of these characters, but it does not produce an error. The bug becomes particularly nasty when scaling up to 150 localized XIBs. I only discovered the bug when I found a single XIB incorrectly localized and tracked it back to a set of brackets randomly inserted into a .strings file.
So, I wrote a ruby script, validate_strings_files.rb, to validate .strings files more completely. The script parses through a directory of .strings files and produces verbal errors, with line numbers, for any out-of-place characters. It works well on the 11 languages I deal with. An error that would silently fail in ibtool would produce the following result with the script:
VERIFYING FILE MainMenu.ES.strings MainMenu.ES.strings (328): @
Below is the code for validate_strings_files.rb.
One important sidenote is Unicode support. The script works with Unicode characters but not universally. I have two lines to allow complete Unicode support. The first works on most languages, and the second is used for UTF-16 languages, like Japanese (JA).
Keep in mind that Ruby 1.9 is required because of its Unicode support; refer to the Hivelogic article for compilation instructions. The r:UTF-16LE:UTF-8 code is from this article on Ruby encodings.
"Class mismatch"
One other error I've encountered is "class mismatch". ibtool produces this error when the class of a UI element associated with a specific ObjectID changes. For instance, imagine two languages were modified independently, and both had a UI element added to them. The first might be an NSTextField whereas the second might be an NSMenuItem; however, the IDs might be the same. The two entries in the .strings files would look like so:
/* Class = "NSTextField"; title = "First Text"; ObjectID = "419"; */ /* Class = "NSMenuItem"; title = "Second Text"; ObjectID = "419"; */
Using the first as the base for localization, incoporating the strings into the second wouldn't make any sense for this UI element. Luckily, ibtool produces the following error when trying to localize those XIBs:
08/10/2009 Update: I updated the validation script to account for escaped backslashes, such as "\\"," = "";.
11/17/2009 Update: I finally submitted ibtool's "silent error" bug to Apple's Radar as rdar://7384153 and added it to OpenRadar. Also, Wil Shipley posted his experiences with Cocoa localization, touching on ibtool's failures.