Pagine

2013-08-15

Il 'locale' personalizzato

Mac OS X (e ovviamente iOS) ha un'ottima gestione della internazionalizzazione delle applicazioni: basta usare le cartelle magiche per ogni lingua che vogliamo supportare, un paio di macro di compilazione e siamo a posto. Il risultato è un'applicazione che si presenta differente a seconda della lingua scelta nel sistema dall'utente; la documentazione Apple al proposito è sufficientemente chiara: per l'interfaccia usiamo degli xib differenti (ciascuno nella propria cartella della lingua) e per le stringhe nel codice usiamo sempre la macro NSLocalizedString, che andrà a pescare dal file localizableString della lingua giusta.

C'è però una cosa più sottile ed è rappresentata da tutte le varie convenzioni tipiche di ogni lingua; ciò che fa si che la data 5/4/2013 significhi '5 aprile 2013' in Italia, mentre negli Stati Uniti stia per '4 maggio 2013'. Oppure che il le cifre 1,234 indichino un numero appena più grande di 1 in Francia, ma per gli inglesi superi il migliaio.
Per vederle tutte basta aprire Preferenze di Sistema e cliccare su Lingua e Testo e poi scegliere il tab Regione.
Quindi l'utente è abituato (p.es. in Italia) a leggere e scrivere numeri con la virgola per indicare i decimali e così via; quindi la nostra app deve metterlo a suo agio, altrimenti dopo pochi minuti finirà nel cestino, seguita da alcune maledizioni al nostro riguardo...

Per fortuna è molto semplice tenerne conto: di solito è sufficiente impostare nello xib un formatter legato al campo che riceverà il numero e impostare il formatter come ci interessa: in automatico le impostazioni saranno quelle usate nel sistema.

Ci sono però dei momenti in cui è utile conoscere da codice p.es. cosa viene usato come separatore decimale in una certa lingua: nulla di più facile! Per questa operazione ci viene in aiuto l'oggetto NSLocale: basta prima ottenere la lingua impostata sul computer dell'utente usando currentLocale e poi utilizzare l'oggetto così ottenuto per ricavare il carattere usato e molte altre cose. Detto in 'codicese':
NSLocale *myPresentLocale = [NSLocale currentLocale];
//ricaviamo il separatore decimale della lingua dell'utente
NSString *punto = [myPresentLocale objectForKey:NSLocaleDecimalSeparator];
//oppure otteniamo il simbolo di valuta in uso
NSString *valuta = [myPresentLocale objectForKey:NSLocaleCurrencySymbol];
//o per esempio il nome della lingua dell'utente
NSString *lingua = [myPresentLocale objectForKey:NSLocaleIdentifier];
//se vogliamo strafare, questo è il codice della lingua
NSString *codice = [myPresentLocale objectForKey:NSLocaleLanguageCode];
//questa invece è il nome di una lingua... nella lingua dell'utente!
NSString *nome = [myPresentLocale displayNameForKey:NSLocaleIdentifier value:@"fr_FR"];
//se siamo in italiano, il risultato sarà 'Francese'
In questo modo possiamo ottenere tutte le informazioni che ci servono ed utilizzarle per mettere l'utente a proprio agio.
Tuttavia, il nostro caro utente ha molta più libertà che la sola scelta della lingua: p.es. qualcuno potrebbe preferire l'utilizzo del punto decimale al posto della virgola, ma continuare a tenersi il sistema in italiano per avere le date nel formato corretto. Se guardiamo quanto disponibile nelle Preferenze di Sistema, vediamo che l'utente può personalizzare tutto (e non chiediamoci perché dovrebbe farlo: dato che può, qualcuno lo farà certamente!) e questo sarebbe un bel guaio per il povero programmatore. Però... ci viene in aiuto di nuovo il NSLocale: possiamo infatti ottenere un oggetto NSLocale che contiene tutti i valori del locale attuale ma corretti con le eventuali personalizzazioni che il nostro caro utente ha ritenuto opportuno inserire:
NSLocale *actualLocale = [NSLocale autoupdatingCurrentLocale];
NSString *punto = [actualLocale objectForKey:NSLocaleDecimalSeparator];
//se l'utente è un italiano che usa il punto al posto della virgola, otterremo '.'
Da notare che la stessa funzione esiste anche nella Foundation (se dovessimo per qualche motivo lavorare in quel modo); il codice diventerebbe:
CFLocaleRef userLocale = CFLocaleCopyCurrent();
CFTypeRef decimalPoint = CFLocaleGetValue(userLocale, kCFLocaleDecimalSeparator);
//più difficile da leggere, ma con lo stesso risultato!
Quindi, da oggi i nostri utenti saranno felici e contenti di poter usare le loro impostazioni anche nelle nostre app!