InstantShot! FastIcns CocoPad
DW Firefox extension JavaImg Ajax Calendar
Our projects
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

Recent comments


From NibTool to IBTool

Matteo - 27 Nov 2007

Updated on 22/02/08: I was notified of a bug and of a problem with the license of the underlaying code. The code from now on is to be considered released under a BSD license and the three (corrected) scripts can be downloaded clicking here.


We already talked about nibtool in the past. Until yesterday it was the main low level tool released by Apple to localize your applications on OS X.

Some two weeks ago I upgraded to Leopard and XCode 3.0. Wonderful and all, but where is nibtool??? Bye bye nibtool, welcome ibtool.

A quick look at man ibtool shows quite a compatibility with the old commands, it seems like you just have to change names to some switches and you have the same old functionalities and, obviously, some new ones.

I run some test and I realize that, oh gracious!, not only has the format of the strings file changed, but it has become bigger tenfold too: I immediately imagined the happiness of our very kind voluntary translators in receiving, instead of the usual 300 lines full of blanks and already translated strings, 3000 lines, in a format so unsuitable for human consumption!

Let's confront a typical "old–style" string:

/* NSMenuItem : <title:To clipboard> (oid:494) */
"To clipboard" = "Sugli appunti";

with a "new–style" one:

/* Class = "NSMenuItem"; title = "To clipboard"; ObjectID = "494"; */
"494.title" = "Sugli appunti";

Even if I'd find it quite less clear, that's not the worst thing. The worst thing is that all strings are added to the file, even the empty ones.

For example, even if I've not associated any string to a text field in a nib file, I still get from ibtool a file containing all these lines:

/* Class = "NSTextField"; designAccessibilityDescription = ""; ObjectID = "398"; */
"398.designAccessibilityDescription" = "";

/* Class = "NSTextField"; designAccessibilityHelp = ""; ObjectID = "398"; */
"398.designAccessibilityHelp" = "";

/* Class = "NSTextField"; gToolTip = ""; ObjectID = "398"; */
"398.gToolTip" = "";

Should a translator really be exposed to 3000 lines like these?

Also, duplicates are not considered: if the same string appears in more than one place, it has to be translated more than once. Ok, this can be helpful to translate something differently from the context, but is it really useful? If the context is different, it should be clear in English text too.

A little note / bug / warning: if there are texts defined inside NSTextView controls, they won't be put in the strings files created by ibtool, as they were when using nibtool. Actually the translation of those texts has never worked well with nibtool, as they're actually texts with RTF attributes associated and in the translation the attributes change positions were assumed to be the same of the original, with disastrous results. But keep this difference in mind, maybe you've used them with no styles and they used to work correctly… The best thing has always been to put individual RTF files in the resources and load the correct one at runtime, as it's so easy to do.

The alternative is to let translators work directly on the nib files, as their direct access seems greatly improved in ibtool. This solution seems all but optimal: not only have all the translators got to install XCode to use InterfaceBuilder (and let's forget about plugins for now !), but in the direct translation process some more hidden strings (ie. tooltips and submenus) are often missed: this means that, when I receive every single translation, I have to check them fully by hand… nonsense…

So at the end I decided to keep the interface with the translators untouched: same files, same formats. The rest is my business.

And the business means two transformations: the first one transforms the strings files generated by ibtool in "old–style" files, like the ones created by nibtool; the second one creates translated strings files that ibtool can understand, starting from the translated old–style files.

In a nutshell the passages are:

  1. use of ibtool to generate strings files from nib files;
  2. (automatic) conversion to "old–style" strings;
  3. translation of the "old–style" strings;
  4. (automatic) translation of the strings generated by ibtool, using the translated old–style strings;
  5. use of ibtool to generate the translated nib files.

Step 1 is simple, we just use ibtool in place of nibtool, with a command line like:

ibtool --generate-stringsfile English.lproj/MainMenu_ib.strings English.lproj/MainMenu.nib

I execute step 2 with a Python script, called It takes a file generated by ibtool as input and creates an old–style file:

## generate a strings file compatible with nibtool from a strings file generated
## with ibtool --generate-stringsfile

<see attached file>

After the conversion, I use the script described in the previous article to merge new and old strings files. 

Step 3 is better left to mother tongue speakers :)

For step 4 I use another Python script,, that takes the file created with ibtool in step 1 and the file translated in step 3 as input and creates a new file for ibtool with the translated strings:

## translate a file generated with ibtool --generate-stringsfile :
##         /* Class = "NSMenu"; title = "Menu"; ObjectID = "305"; */
##         "305.title" = "Menu";
## with a strings file in the style of nibtool:
##         /* NSMenu : <title:Menu> (oid:305) */
##         "Menu" = "Menu";

<see attached file>

For step 5 we use ibtool again, with a command line like:

ibtool --strings-file Italian.lproj/MainMenu_ib.strings English.lproj/MainMenu.nib --write Italian.lproj/MainMenu.nib


Weblog Koan Projects Photos Contacts