Passare da NibTool a IBTool

Matteo - 27 Novembre 2007

Aggiornamento 22/02/08: sono stato avvertito di un bug e di un problema con la licenza del codice sottostante. Il codice da questo momento è da considerarsi sotto licenza BSD e i tre script (corretti) possono essere scaricati cliccando qui.

 

Abbiamo avuto più volte modo di parlare di nibtool, quello che fino a ieri era lo strumento principale fornito da Apple (tra quelli a basso livello) per eseguire la localizzazione delle proprie applicazioni su OS X.

Un paio di settimane fa installo Leopard e XCode 3.0. Uno spettacolo e tutto, ma dov'è nibtool??? Non c'è, sostituito da ibtool.

Un veloce man ibtool mostra una discreta compatibilità di comandi tra i due, cambiano un po' i nomi dei parametri ma, almeno a leggere lì, ci si possono fare le stesse cose e, naturalmente, qualcosina di più.

Faccio qualche prova e scopro che, ahimé, non solo è cambiato il file di stringhe, ma è anche decuplicato in dimensione: mi sono subito immaginato la contentezza dei nostri gentilissimi traduttori volontari nel ricevere, invece del solito file da 300 righe compresi spazi bianchi e cose già tradotte, 3000 righe in un formato assolutamente poco adatto alla fruizione umana.

Confrontiamo una tipica stringa "vecchio stile":

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

con una "nuovo stile":

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

Fin qui non ci sarebbe molta differenza (anche se già mi sembra molto meno chiaro), ma il problema più grosso è che vengono aggiunte tutte le stringhe, anche quelle non definite. Ad esempio, per un campo di testo a cui nel file nib non avevo associato alcuna stringa, il file generato da ibtool contiene tutte queste righe:

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

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

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

Un traduttore dovrebbe veramente affrontare 3000 righe così?

Inoltre eventuali doppioni vengono messi tutte le volte: se la stessa stringa appare in due controlli diversi, va tradotta due volte. Ok questo potrebbe essere utile per differenziare la traduzione in base al contesto, ma personalmente non mi è mai servito: la differenza può essere resa anche in Inglese.

Una piccola nota / bug / avvertenza: eventuali testi nei controlli NSTextView non vengono inseriti nei file di stringhe da ibtool, come invece faceva nibtool. In realtà la traduzione di quei testi con nibtool non ha mai funzionato, perché in realtà sono testi con attributi RTF e venivano tradotti lasciando inalterata la posizione dei cambi di stile… insomma un macello :) però da tenere a mente nel caso si usassero, magari senza stili nei testi. La cosa migliore è sempre creare dei file RTF nelle risorse e caricare quello giusto per la lingua corrente a runtime, è molto semplice. 

L'alternativa rimane quella di far modificare ai traduttori direttamente i file nib, la cui gestione diretta mi sembra molto migliorata in ibtool. Questa soluzione è però tutt'altro che ottimale: non solo tutti i traduttori devono aver installato XCode per poter utilizzare InterfaceBuilder (e non parliamo per ora dei plugin, altra nota molto dolente), ma nella traduzione diretta alcune stringhe più nascoste (ad esempio i tooltip o i sottomenu) potrebbero essere dimenticate: questo significa che, nel ricevere ogni traduzione, dovrei controllare personalmente tutto quanto… Come si capisce non è cosa…

Insomma alla fine ho deciso di mantenere un'interfaccia con i traduttori identica a quella precedente: stessi file su cui lavorare, stesso formato. Il resto è impiccio mio.

E l'impiccio è una doppia trasformazione: la prima che trasforma i file di stringhe generati da ibtool in quelli "vecchio stile", simili a quelli generati da nibtool; la seconda che genera file di stringhe tradotti che ibtool sia in grado di leggere, a partire dai file vecchio stile tradotti.

Schematicamente i passaggi sono:

  1. generazione con ibtool delle stringhe, a partire dai file nib;
  2. conversione (automatica) in stringhe "vecchio stile";
  3. traduzione delle stringhe "vecchio stile";
  4. traduzione (automatica) delle stringhe generate da ibtool sulla base di quelle "vecchio stile" tradotte;
  5. generazione con ibtool dei file nib tradotti.

Il passo 1 è semplice, utilizzando ibtool al posto di nibtool, con una linea di comando tipo:

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

Il passo 2 lo eseguo con uno script Python, chiamato gen_nib_strings.py, che prende in ingresso un file generato da ibtool e ne crea uno "vecchio stile":

#!/usr/bin/python
## generate a strings file compatible with nibtool from a strings file generated
## with ibtool --generate-stringsfile

<ved. file allegato>

Dopo la conversione utilizzo lo script discusso nel precedente articolo per fare l'integrazione dei file di stringhe. 

Il passo 3 è meglio lasciarlo alle manine dei madrelingua :)

Per il passo 4 impiego un altro script Python, translate_ib_strings.py, che prende il file di prima generato con ibtool, quello tradotto nel passo 3 e crea un nuovo file nel formato riconosciuto da ibtool ma con le stringhe tradotte:

#!/usr/bin/python
## 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";

<ved. file allegato>

Il passo 5 richiede l'uso di ibtool, con una riga di comando del tipo:

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

Il gioco è fatto. 

Weblog Koan Progetti Foto Contatti