MaCocoa 011

Capitolo 011 - Localizzazione icone ed altre storie

Il corredo di una applicazione.

Sorgenti: Manualistica Apple

Primo inserimento: 26 Novembre 2001

Ritocchi per aggiornamento XCode 2.1: 9 Agosto 2005

Stringhe in lingua

EuroConv è un programma che nel suo piccolo si presta naturalmente ad essere utilizzato in diversi paesi europei. Nel caso di apparecchiature, dispositivi, automobili, elettrodomestici, eccetera, è una legge dello stato fornire le istruzioni ed in generale l'interfaccia utente nella lingua dell'utilizzatore. È buona cosa farlo anche nel caso di programmi per calcolatore, ed è proprio il primo problema che mi pongo in questo capitolo.

figura 01

figura 01

Ho deciso che quando l'utente seleziona una delle voci del menu pop-up, è mostrata al centro della finestra, in una stringa di testo, il rapporto di conversione tra l'Euro e la valuta selezionata. Selezionando quindi Marchi Ger deve saltare fuori la stringa 'Euro 1 = 1.95583 Marchi tedeschi', e sia così per tutte le altre. Questa stringa pone un problema quando EuroConv sarà utilizzato da persone non italiane. Sarebbe quindi giusto che la stringa sia scritta nella lingua scelta nel pannello Preferenze di sistema. Dal punto di vista del programmatore, l'adeguamento alla lingua (operazione nota come localizzazione o internazionalizzazione) genera tipicamente un sovraccarico di lavoro piuttosto noioso e non focalizzato sull'applicazione vera e propria. Apple opera una sottile distinzione tra i due concetti. Si localizza quando l'intera applicazione è adattata ai vari mercati locali; sono comprese quindi anche le operazioni di traduzione del manuale, predisposizione delle strutture di supporto, eccetera.

L'internazionalizzazione invece riguarda più strettamente il supporto al programmatore per la stesura di applicazioni nelle diverse lingue. Ogni ambiente di sviluppo fornisce in varia misura un supporto per adeguare l'interfaccia utente nelle varie lingue. L'ambiente Cocoa non è da meno: ereditando alcuni concetti da Unix, unendone altri di originali (derivati in realtà da Next) fornisce una serie di strumenti per la localizzazione delle applicazioni.

Lingue dell'applicazione

figura 02

figura 02

Una parte consistente di localizzazione consiste nell'adeguare le voci dei menu, l'aspetto delle finestre, il testo dei pulsanti e di tutte le stringhe statiche dell'interfaccia. Nei precedenti sistemi operativi Macintosh (ma non è l'unico sistema operativo che soffre di questo problema) non c'era altra possibilità che rilasciare diverse applicazioni per le diverse lingue. Esisteva quindi l'applicazione in inglese e l'applicazione in italiano. Chi ha provato a cimentarsi con l'opera di localizzazione, sa che lo strumento principe era l'applicazione ResEdit unito ad una lunga pazienza da certosino. Con Mac Os X le cose sono molto diverse: esiste una unica applicazione che contiene al suo interno tutte le risorse per la localizzazione nelle varie lingue. Questo significa che lo stesso file (che so, iTunes) contiene nel suo interno tutte le informazioni per la localizzazione nelle varie lingue (o almeno, nelle lingue previste da chi ha rilasciato l'applicazione). Se infatti seleziono l'icona di iTunes e ne chiedo le Informazioni, tra i pannelli disponibili c'è anche quello relativo alle lingue supportate. Nel mio caso sono disponibili Italiano ed Inglese. Questo significa che, modificando la lingua principale attraverso le preferenze di sistema, è possibile avere l'interfaccia utente in italiano oppure in inglese.

Tale disponibilità di lingua si riflette su come è costruita l'applicazione. Attraverso il Terminale, è possibile esaminare il contenuto dell'applicazione (ecco, questa è una cosa che occorrerà approfondire prima o poi, ovvero, come sono conservate le applicazioni; per ora basti dire che un'applicazione in Mac Os X non è altro che una cartella, di tipo speciale. All'interno di questa cartella trovano posto il vero e proprio codice eseguibile, assieme a tutte le altre risorse necessarie all'applicazione stesse, suoni, immagini e quant'altro...).

PowerDjZero:/Applications/iTunes.app/Contents djzero$ ls -la
total 48
drwxrwxr-x    9 root    admin    306 Aug  9 20:45 .
drwxrwxr-x    3 root    admin    102 Jul  2 09:35 ..
drwxrwxr-x    3 root    admin    102 Mar 28 06:27 Frameworks
-rw-rw-r--    1 root    admin  14938 Jun 23 00:10 Info.plist
drwxrwxr-x    3 root    admin    102 Jul  2 09:34 MacOS
-rw-rw-r--    1 root    admin      8 Jun 16 22:33 PkgInfo
drwxrwxr-x   44 root    admin   1496 Jul  2 09:35 Resources
drwxr-xr-x    2 djzero  admin     68 Aug  9 20:45 Resources Disabled
-rw-rw-r--    1 root    admin    510 Jun 28 23:31 version.plist
PowerDjZero:/Applications/iTunes.app/Contents djzero$ ls -la Resources
total 8808
drwxrwxr-x   44 root  admin     1496 Jul  2 09:35 .
drwxrwxr-x    9 root  admin      306 Aug  9 20:45 ..
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 Dutch.lproj
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 English.lproj
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 French.lproj
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 German.lproj
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 Italian.lproj
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 Japanese.lproj
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 Spanish.lproj
-rw-rw-r--    1 root  admin    36570 Jun 16 22:18 complete.aif
drwxrwxr-x    6 root  admin      204 Jul  2 09:34 da.lproj
        __ altri file che non ci interessano __

Il contenuto della cartella /Application/iTunes.app/Contents/ presenta alcuni file e cartelle; in particolare, scendendo di livello, nella cartella Resources sono infine presenti altre due cartelle, English.lproj e Italian.lproj. Ebbene, queste due cartelle contengono appunto tutte le risorse localizzate per la lingua indicata nel nome della cartella. Se iTunes fosse localizzato anche per la lingua aramaica, ci sarebbe stata una cartella Aramaic.lproj (o qualcosa del genere), e via di questo passo per le altre lingue.

Supporto all'internazionalizzazione

figura 03

figura 03

figura 04

figura 04

Per realizzare una struttura similare nella applicazione EuroConv, torno nel XCode e seleziono le risorse che voglio far comparire in versione localizzata. Al momento, ho solo il file nib che contiene finestra e menu. Seleziono il file e scelgo di vedere le informazioni relative (menu contestuale e poi Get Info). C'è un pulsante che permette l'inserimento una variante locale: Add Localization; nel pannello che appare scrivo Italian. Di colpo, il file nib si duplica, e ne compaiono due, uno che rimane MainMenu.nib, versione di default in inglese, ed un altro che si chiama Italian. Su questo file nib posso adesso lavorare con IB modificando tutti gli elementi dell'interfaccia che ritengo opportuno modificare per adeguati alla lingua (in realtà, essendo baldanzosamente partito con l'italiano, devo internazionalizzare il tutto).

E per quanto riguarda l'interfaccia utente, non devo fare altro. È compito dell'ambiente Cocoa e del sistema operativo utilizzare l'uno o l'altro file nib al lancio dell'applicazione per visualizzare l'interfaccia in lingua.

figura 05

figura 05

Infatti, compilando il progetto, si genera alla fine l'applicazione. L'esame del contenuto del prodotto si può fare direttamente dal Finder: seleziono da Finder il prodotto della compilazione e, mediante menu contestuale, scelgo Mostra Contenuto Pacchetto. Si apre una finestra del Finder con dentro la cartella Contents, e da qui, aprendola, si scopre la struttura già descritta, con due cartelle English ed Italian dense di file di risorse (la figura si riferisce all'applicazione in uno stadio più avanzato di quanto finora ho descritto, ma ci arriviamo presto). Al momento voglio notare il file mc010 nella cartella Contents/MacOS, che contiene il codice eseguibile vero e proprio, e i due file MainMenu.nib nelle due cartelle English.lproj e Italian.lproj, che corrispondono ai due file nib che ho duplicato e manipolato per metterli in lingua. Degli altri file, di qualcuno ne parlo dopo, di altri ignoro totalmente cosa siano e a cosa servono (ma non credano di resistermi a lungo).

Stringhe Localizzate

Torno adesso al problema lasciato in sospeso il capitolo precedente, ovvero recuperare le stringhe di informazione da mostrare nella finestra quando l'utente cambia attraverso il menu pop-up la valuta di riferimento. Il problema era appunto di inserire al stringa corretta in dipendenza dalla lingua dell'utente. Ciò si realizza facilmente utilizzando il meccanismo sopra descritto di file localizzato, e la funzione NSLocalizedString (la documentazione afferma in realtà trattarsi di una macro, ma la cosa al momento non mi interessa).

In primo luogo, occorre costruire un file di stringhe, ovvero un file dall'estensione string, costruito da una serie di righe siffatte:

<<stringa chiave>> = <<stringa risultante>> ;

La stringa chiave è il nome della stringa, il cui contenuto è proprio la stringa risultante. Il file delle stringhe può avere un nome qualsiasi; il nome più comune è Localizable.string, utilizzato come default. Questo file di stringhe va poi replicato per tutte le lingue che ci interessa supportare, utilizzando appunto il meccanismo sopra visto di creazione di una versione localizzata del file. Vale a dire, costruisco un nuovo file dove sono contenute le stesse stringhe chiave, ma la stringa risultante è nella lingua desiderata. Nel caso di EuroConv, i miei due file sono così costruiti. Il file corrispondente all'italiano è:

/* info conversione da visualizzare */
"it" = "\U20ac 1 = 1936.27 Lire italiane";
"au" = "\U20ac 1 = 13.7603 Scellini austriaci";
"be" = "\U20ac 1 = 40.3399 Franchi belgi";
"de" = "\U20ac 1 = 1.95583 Marchi tedeschi";
"es" = "\U20ac 1 = 166.386 Peseta spagnole";
"fi" = "\U20ac 1 = 5.94573 Marchi finlandesi";
"fr" = "\U20ac 1 = 6.55957 Franchi francesi";
"gr" = "\U20ac 1 = 340.75 Dracme greche";
"ir" = "\U20ac 1 = 0.78756 Lire irlandesi";
"nl" = "\U20ac 1 = 2.200371 Fiorini olandesi";
"por" = "\U20ac 1 = 200.482 Escudo portoghesi";

mentre quello per l'inglese è il seguente (e perdonate la mia ignoranza sui nomi dei paesi e delle monete in inglese... anzi, se ne sapete più di me, correggetemi):

/* info conversione da visualizzare in inglese */
"it" = "\U20ac 1 = 1936.27 Italian Liras";
"au" = "\U20ac 1 = 13.7603 Austrian shellings";
"be" = "\U20ac 1 = 40.3399 Belgian francs";
"de" = "\U20ac 1 = 1.95583 German marks";
"es" = "\U20ac 1 = 166.386 Spanish pesetas";
"fi" = "\U20ac 1 = 5.94573 Finnish marks";
"fr" = "\U20ac 1 = 6.55957 French francs ";
"gr" = "\U20ac 1 = 340.75 Grecian dracmas";
"ir" = "\U20ac 1 = 0.78756 Irish liras";
"nl" = "\U20ac 1 = 2.200371 Dutch ???";
"por" = "\U20ac 1 = 200.482 Portuguese Escudos";

Non vi sarà sfuggita la bizzarra sequenza iniziale di caratteri e numeri. Ebbene, \U20ac non è altro che la rappresentazione del simbolo dell'Euro (€) nella rappresentazione Unicode dei caratteri. La codifica Unicode ha sostituito completamente la vecchia codifica ASCII, utilizzata praticamente fin dalla nascita dei calcolatori come equivalente numerico dei caratteri. Unicode è la rappresentazione nativa dei caratteri all'interno del Mac OS X.

Adesso entra in gioco la macro NSLocalizedString. Supponiamo ad un certo punto di aver bisogno all'interno del nostro codice di una certa stringa. Uso la macro per recuperarne la versione nella lingua:

<<stringa risultante>> = NSLocalizedString( <<stringa chiave>>, <<commento>>);

La macro sorella NSLocalizedSringFromTable dovrebbe funzionare nei casi in cui il file dalle stringhe non si chiama Localizable.string, ma con qualche altro nome (ad esempio, perché ho costruito diverse file di stringhe per comprensibilità nella costruzione del programma).

EuroConv europeo

A questo punto, diventa molto semplice localizzare la stringa di informazioni dell'applicazione EuroConv.

In primo luogo, definisco nel file EutoCtrl.h le chiavi delle stringhe:

#define                ITALY_STRING        @"it"
#define                GERMANY_STRING      @"de"
#define                FRANCE_STRING       @"fr"
#define                BELGIO_STRING       @"be"
#define                FINLAND_STRING      @"fi"
#define                OLANDA_STRING       @"nl"
#define                SPAGNA_STRING       @"es"
#define                PORTOGAL_STRING     @"por"
#define                IRLANDA_STRING      @"ir"
#define                AUSTRIA_STRING      @"au"
#define                GRECIA_STRING       @"gr"

Aggiungo una variabile d'istanza in grado di conservarle (in realtà, un vettore), utilizzando la stessa idea dei rapporti di conversione tra Euro e valuta locale:

NSString        * cvtMsg[ NUM_EURO_NATIONS ];

Il vettore va inizializzato all'interno del metodo awakeFromNib:

cvtMsg [ EURO_ITALY ]          = ITALY_STRING ;
cvtMsg [ EURO_FRANCE ]         = FRANCE_STRING ;
cvtMsg [ EURO_GERMANY ]        = GERMANY_STRING ;
cvtMsg [ EURO_BELGIO ]         = BELGIO_STRING ;
cvtMsg [ EURO_FINLAND ]        = FINLAND_STRING ;
cvtMsg [ EURO_SPAGNA ]         = SPAGNA_STRING ;
cvtMsg [ EURO_OLANDA ]         = OLANDA_STRING ;
cvtMsg [ EURO_PORTOGAL ]       = PORTOGAL_STRING ;
cvtMsg [ EURO_IRLANDA ]        = IRLANDA_STRING ;
cvtMsg [ EURO_AUSTRIA ]        = AUSTRIA_STRING ;
cvtMsg [ EURO_GRECIA ]         = GRECIA_STRING ;

Finalmente all'interno del metodo cvtMsg siamo arrivati alla stesura finale:

- (IBAction)cvtMsg:(id)sender
{
        currSel = [ sender indexOfSelectedItem ];
        [ setMsg setStringValue: NSLocalizedString( cvtMsg[ currSel ], @"cheneso" ) ];    
        [ setLocal setFloatValue: [ setEuro floatValue ] * cvtRate[ currSel ] ] ;
}

La prima istruzione, come detto, recupera l'identificatore della valuta selezionata; utilizzo la chiave corrispondente cvtMsg[currSel] per recuperare la stringa dal file della lingua scelto per me dal sistema operativo e dall'ambiente Cocoa:

NSLocalizedString( cvtMsg[ currSel ], @"cheneso" )

La stringa risultante la uso come argomento del messaggio setStringValue che modifica il contenuto del campo informazioni nell'interfaccia. Finito. L'istruzione successiva, come già detto, effettua il cambiamento del valore in Euro nella nuova valuta locale.

Rimane da chiarire l'uso del commento all'interno della macro NSLocalizedString. In effetti, non ha qui alcuna funzione. È però utilizzata come supporto all'internazionalizzazione. Accade che il programmatore non conosca tutte le lingue del mondo, ma solo la sua. Allora, quando ha bisogno di una stringa localizzata, non si preoccupa minimamente della sua rappresentazione, ma usa NSLocalizedString con la stringa chiave. Aggiunge poi un commento sul significato effettivo della stringa (la cosa più praticata è di mettere la stringa effettiva nella lingua principale, in inglese) di modo che qualcun altro, linguisticamente più dotato, scriva le stringhe corrette. Questo meccanismo non è proprio di Cocoa, ma lo ricordo adottato (con nomi differenti, ovviamente) all'interno di Unix e sviluppo di interfacce in Xwindow. Lì esisteva un programma (a linea di comando) che esaminava tutti i file sorgenti dell'applicazione e produceva direttamente tutti i file di stringhe necessari, raccogliendo chiavi e stringhe risultanti. In Mac Os X, esiste una funzione simile, ma non riesco più a trovare la pagina del manuale dove ne avevo appreso l'esistenza.

Daniele Baracchi mi suggerisce che il programma si chiama genstrings. Trovate le informazioni relative attraverso il terminale e digitando man genstrings oppure nella sezione 'Internationalizing Your Software' nella documentazione dell'ambiente di sviluppo.

Icone

figura 07

figura 07

figura 06

figura 06

Aggiungere l'icona all'applicazione è estremamente semplice. Ovviamente, la prima cosa da fare è disegnare l'icona. Occorre una immagine di partenza costituita da un quadrato di 128 pixel di lato; io ho prodotto una cosa orribile a vedersi. Apro l'applicazione IconComposer nei Developer Tools; con la tecnica del drag and drop trascino il file sulle quattro dimensioni previste di icona, e lascio all'applicazione il compito di ridimensionarla e di calcolarne la maschera ad un bit. Salvo il risultato in un file che chiamo fantasiosamente Euroconv.icns; ripasso in XCode e lo aggiungo al progetto, nella sezione risorse. Il passo successivo è associare questo file alle icone del programma; basta andare nel pannello delle informazioni del Target sotto esame e scrivere il nome del file nell'apposito spazio.

Già che sono ho riempito un altro paio di campi, utili per cambiare il nome del file eseguibile (altrimenti XCode usa di default il nome del progetto), informazioni da mostrare nella finestra del Finder, eccetera.

About

figura 08

figura 08

Per chiudere, personalizzo la finestra di About dell'applicazione. In primo luogo utilizzo il file InfoPlist.string, costruito da XCode nel momento di creazione del progetto. Lo divido subito in due versioni locali (una per l'inglese e l'altra per l'italiano), e modifico le stringhe proposte da XCode con le informazioni che mi piacciono.

figura 09

figura 09

figura 10

figura 10

Poi, costruisco, ad esempio con TextEdit, un file in formato RTF (che per altro è il formato nativo di TextEdit) dal nome Credits.rft. Ebbene, questo file, una volta inserito nel progetto, è mostrato dalla finestra di About tale e quale (anche fosse piuttosto corposo...). Ciò permette di personalizzare velocemente e senza fatica la finestra di About. Per avere qualcosa di completamente diverso (che so, credits scorrevoli, animazioni e quant'altro), credo che si debba lavorare un bel po'. Non è questo il momento.

Per ora mi accontento di aver costruito una applicazione funzionante, bilingue, magari di non troppa utilità pratica, ma abbastanza completa. Fra un po', potrei anche cominciare a spacciarne in giro come freeware...

Licenza Creative Commons
Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto Licenza Creative Commons.
Pagina a cura di Livio Sandel (macocoa2012@gmail.com).