InstantShot! FastIcns CocoPad
DW Firefox extension JavaImg Ajax Calendar
Our projects
<
>
M T W T F S S
  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

Recent comments

(dALmO)
(kerubina704)
(reim)
(joeporrini)
(nicola)
(anonymous)

Usage and bugs of NibTool

Matteo - 11 Mar 2007

Updated on 22/02/08: The code from now on is to be considered released under a BSD license and the script (together with the others discussed in the followup From NibTool to IBTool) can be downloaded clicking here.

 

We have already written about a bug in AppleGlot. Nothing after what we've found in nibtool.

nibtool is the main tool in Apple's developer's tools to localize resources in Mac's applications: it extracts strings from a nib file and creates a localized version from a source nib file and the translated strings file. It also extracts strings incrementally: if you've already translated some parts of the nib file and it gets changed, you can get an automatially merged file containing the new strings and the already translated ones untouched.

To generate a strings file from a source nib file, the command would be:

nibtool -L MainMenu.nib > MainMenu.strings 

The generated file is a text file in UTF–16 format containing lines like these:

/* component (text) : <title:text> (oid:XXX) */
"text" = "text";

"component" is the Cocoa name of the component to which the string is connected, for example NSButton. "text" is the original text you put in Interface Builder. "XXX" is the numeric id of the string. In all that the only part you'd modify (translate) is the last "text".

To create a nib file from the original one, applying the adaptions you made to the strings file, the command would be:

nibtool -d MainMenu.strings MainMenu.nib -W MainMenu_trad.nib

Finally, if the MainMenu.nib has been changed and you want to create a new MainMenu.strings without losing the already made translations, the command to use would be:

nibtool -L -I MainMenu_trad.nib MainMenu.nib > MainMenu.strings 

Unfortunately the incremental generation seems to be affected by a bug: for some reason you'll lose all the translations made to the tooltips' strings, identified in the file by the NSIBHelpConnector component.

Also, nibtool can't be used for the automatic merge of the bundles' strings files, those that are used by functions in the family NSLocalizedString* to localize strings in the code: even if those strings can be easily created from the code with the genstrings tool, an integration usually has to be made by hand.

To solve both these problems, I've written a short Python script (my first actually! — sorry for any mistakes):

#!/usr/bin/python

import sys
import codecs
import re
import os.path

def exit_usage(err):
    sys.stderr.write('Usage: ' + sys.argv[0] + ' <target language> <strings file name>\n')
    sys.stderr.write(err)
    sys.exit()

try:
    if len(sys.argv) != 3:
        exit_usage('')
   
    tgtLang = sys.argv[1]
    if not os.path.isdir(tgtLang + '.lproj'):
        exit_usage('Target language\'s folder not found.\n')

    filename = sys.argv[2]
    tgtFileName = tgtLang + '.lproj/' + filename
    if not os.path.isfile(tgtFileName):
        exit_usage('Target file not found.\n')

    srcFileName = 'English.lproj/' + filename
    if not os.path.isfile(srcFileName):
        exit_usage('English source file not found\n')

    tgtFile = codecs.open(tgtFileName, "r", "utf-16")
    text = tgtFile.read()
    tgtFile.close()
    lines = text.split(u'\n')
    assign_preg = re.compile(r'^\"(.*)\" = \"(.*)\"\;')

    strings = {}
    for line in lines:
        m = assign_preg.match(line)
        if m:
            orig = m.expand(r'\1')
            trad = m.expand(r'\2')
            strings[orig] = trad

    srcFile = codecs.open(srcFileName, "r", "utf-16")
    text = srcFile.read()
    srcFile.close()
    lines = text.split(u'\n')

    txtOut = u''

    for line in lines:
        m = assign_preg.match(line)
        if m:
            orig = m.expand(r'\1')
            if orig in strings:
                txtOut += u'"' + orig + u'" = "' + strings[orig] + u'";\n'
                del strings[orig]
            else:
                txtOut += line + u'\n'
        else:
            txtOut += line + u'\n'
           
    if len(strings):
        txtOut += u'/* The following strings were not found '
        txtOut += u'in the English file: delete them if you don\'t '
        txtOut += u'need them anymore */\n\n'
        for k in strings.keys():
            txtOut += u'"' + k + u'" = "' + strings[k] + u'";\n'
           
    tgtFile = codecs.open(tgtFileName + '.new', 'w', 'utf-16')
    tgtFile.write(txtOut)
    tgtFile.close()

except IOError:
    print 'I/O error'
    sys.exit(0)

This script expects a quite common structure of the project's folder: the original strings files in the English.lproj folder, those of the other languages in the respective folders, e.g. Italian.lproj. I've named this script strings_update.py and I've set the execution flag on it: chmod +x strings_update.py. In this way you can call it from the Terminal as if it was a shell script.

This script merges changes to the English strings file with a previously translated strings file. For example to apply changes to the Italian MainMenu.strings file from the most recent English one (generated as told earlier with the -L option of nibtool), you'd issue from the command line:

./strings_update.py Italian MainMenu.strings

The script creates a MainMenu.strings.new file in the Italian.lproj folder. Any string found in the Italian file but not in the recent English one is put at the end of the file after a short note as they could be related to strings that were just slightly changed or simply corrected in the English resources.

Weblog Koan Projects Photos Contacts