Development

Documentation/it_IT/book/1.1/13-I18n-and-L10n (diff)

You must first sign up to be able to contribute.

Changes from Version 1 of Documentation/it_IT/book/1.1/13-I18n-and-L10n

Show
Ignore:
Author:
garak (IP: 85.18.214.242)
Timestamp:
10/24/08 12:44:46 (9 years ago)
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Documentation/it_IT/book/1.1/13-I18n-and-L10n

    v0 v1  
     1{{{ 
     2#!WikiMarkdown  
     3 
     4Capitolo 13 - i18n e l10n 
     5========================= 
     6 
     7Qualora tu abbia mai sviluppato un'applicazione internazionale, sai che gestire ogni aspetto delle traduzioni dei testi, degli standard locali e dei contenuti localizzati è un incubo. Fortunatamente, symfony automatizza nativamente tutti gli aspetti dell'internazionalizzazione.  
     8 
     9Dato che si tratta di una parola lunga, spesso gli sviluppatori si riferiscono all'internazionalizzazione con i18n (conta le lettere della parola "internationalization" per capirne il motivo). Ci si riferisce alla localizzazione invece come l10n. Esse coprono due diversi aspetti delle applicazioni web multi-lingua. 
     10 
     11Un'applicazione internazionalizzata contiene diverse versioni dello stesso contenuto i diverse lingue e formati. Ad esempio, un'interfaccia webmail può offrire lo stesso servizio in diverse lingue; solo l'interfaccia stessa cambia. 
     12 
     13Un'applicazione localizzata invece contiene informazioni distinte a seconda del paese da cui la si sta visitando. Pensa al contenuto di un portale di notizie: se navigato dagli Stati Uniti fornisce notizie sugli Stati Uniti, mentre se lo navighi dall'Italia vedrai notizie sull'Italia. Per cui un'applicazione l10n non solo fornisce testo tradotto, ma i contenuti possono essere diversi da una versione all'altra. 
     14 
     15In conclusione, gestire i18n e l10n significa occuparsi dei seguenti punti: 
     16 
     17  * Traduzione dei testi (interfacce, risorse e contenuti) 
     18  * Standard e formati (date, quantità, numeri e così via) 
     19  * Contenuti localizzati (diverse versioni dello stesso oggetto a seconda del paese) 
     20  
     21Questo capitolo spiega il modo in cui symfony gestisce questi elementi, e come puoi sviluppare applicazioni localizzate ed internazionalizzate. 
     22  
     23  
     24User Culture 
     25------------ 
     26  
     27Tutte le funzionalità i18n incluse in symfony sono basate su di un parametro della sessione utente chiamato culture. Esso è una combinazione del paese e della lingua dell'utente, e determina il modo in cui vengono visualizzati testi ed informazioni dipendenti dalla propria cultura. Dato che viene serializzato in sessione, esso è persistente nelle pagine. 
     28  
     29  
     30### Impostare la culture di default 
     31  
     32Per default, la cultura di nuovi utenti è `default_culture`. Puoi cambiare questa impostazione nel file di configurazione `i18n.yml`, come mostrato nel Listato 13-1. 
     33  
     34Listato 13-1 - Impostare la cultura di default, in `frontend/config/i18n.yml` 
     35  
     36    [php] 
     37    all: 
     38      .settings: 
     39        default_culture:     it_IT 
     40 
     41>**NOTE** 
     42> Durante lo sviluppo, potresti essere sorpreso nel non vedere cambiamenti nel browser dopo aver modificato l'impostazione nel file `i18n.yml`. Questo è dovuto al fatto che la sessione possiede già la cultura dalle pagine precedenti; se vuoi vedere le modifiche devi eliminare i cookie del dominio o riavviare il browser. 
     43 
     44Mantenere sia il paese che la lingua nell'oggetto culture è importante in quanto potresti avere traduzioni francesi differenti per Francia, Belgio o Canada, oppure per Spagna e Messico. La lingua è codificata in due caratteri minuscoli, secondo lo standard ISO 639-1 (ad esempio, `en` per inglese). Il paese invece con due caratteri maiuscoli, secondo lo standard ISO 3166-1 (ad esempio `GB` per Gran Bretagna). 
     45 
     46### Cambiare la cultura di un utente 
     47 
     48La cultura di un utente può essere cambiata durante la navigazione; ad esempio, se l'utente decide di cambiare versione dell'applicazione, da francese a inglese, oppure quando un utente si autentica e viene visualizzata la lingua memorizzata nelle sue preferenze. Ecco perché la classe `sfUser` offre metodi getter e setter per la cultura. Il Listato 13-2 mostra tali metodi in azione. 
     49 
     50Listato 13-2 - Impostare e recuperare la cultura in un'azione 
     51 
     52    [php] 
     53    // Culture setter 
     54    $this->getUser()->setCulture('en_US'); 
     55     
     56    // Culture getter 
     57    $culture = $this->getUser()->getCulture(); 
     58     => en_US 
     59 
     60 
     61>**SIDEBAR** 
     62>Cultura nelle URL 
     63> 
     64>Utilizzando le funzionalità i18n e l10n di symfony, le pagine tendono ad avere diverse versioni per una URL singola; ognuna dipende dalla sessione utente. Ciò è di ostacolo al caching ed all'indicizzazione nei motori di ricerca. 
     65>Una soluzione potrebbe essere quella di mostrare la cultura ogni URL, in modo che le pagine tradotte vengano viste come URL diverse nel mondo esterno. Per fare ciò, aggiungi il token `:sf_culture` ad ogni regola del file `routing.yml` della tua applicazione: 
     66>     [php] 
     67>     page: 
     68>       url: /:sf_culture/:page 
     69>       requirements: { sf_culture: (?:fr|en|de) } 
     70>       params: ... 
     71>      
     72>     article: 
     73>       url: /:sf_culture/:year/:month/:day/:slug 
     74>       requirements: { sf_culture: (?:fr|en|de) } 
     75>       params: ... 
     76> 
     77>Per evitare di impostare manualmente il parametro di richiesta `sf_culture` in ogni `link_to()`, symfony aggiunge automaticamente la cultura dell'utente ai parametri di routing di default. Ciò funziona anche in ingresso, in quanto symfony cambierà automaticamente la cultura dell'utente se trova il parametro `sf_culture` nell'URL. 
     78 
     79 
     80### Determinare automaticamente la cultura 
     81 
     82In molte applicazioni la cultura viene definita durante la prima richiesta, basandosi sulle preferenze del browser. Gli utenti possono definire nel browser una lista di lingue accettate, e tale dato viene spedito al server ad ogni richiesta, nell'header HTTP `Accepted-Language`. Puoi vederlo in symfony tramite l'oggetto `sfRequest`. Ad esempio, per vedere in un'azione la lista delle lingue preferite di un utente scrivi: 
     83 
     84    [php] 
     85    $languages = $request->getLanguages(); 
     86 
     87L'header HTTP è una stringa, ma symfony ne fa il parsing automaticamente e la converte in un array. Per cui nell'esempio precedente la lingua preferita dall'utente è disponibile in `$languages[0]`. 
     88 
     89Può essere utile impostare automaticamente la cultura dell'utente nella sua lingua preferita per la home page od in un filtro per tutte le pagine. Ma poiché il tuo sito probabilmente supporterà solo un insieme limitato di lingue, è meglio usare il metodo `getPreferredCulture()`. Esso restituisce la lingua migliore confrontanto la lingua preferita e le lingue supportate: 
     90 
     91    $language = $request->getPreferredCulture(array('en', 'fr')); // the website is available in English and French 
     92 
     93Se non c'è corrispondenza, il metodo restiuisce la prima lingua supportata (nell'esempio precedente, `en`). 
     94 
     95>**CAUTION** 
     96>L'header HTTP `Accept-Language` non è in realtà un'informazione molto affidabile, in quanto raramente l'utente sa come modificarlo nelle proprie impostazioni del browser. La maggior parte delle volte, la lingua preferita nella navigazione è quella dell'interfaccia, ed i browser non sono disponibili in tutte le lingue. Se decidi di impostare la lingua di default secondo quella utilizzata nel browser, assicurati di fornire all'utente un modo per cambiarla. 
     97 
     98 
     99Standard e formati 
     100------------------ 
     101 
     102Gli interni di un'applicazione web di solito non si preoccupano molto delle particolarità culturali. I database, ad esempio, utilizzano standard internazionali per memorizzare i dati, quantità e così via. Ma quando un dato è spedito o ricevuto da un utente, è necessaria una conversione. Gli utenti non capiranno i timestamp, e preferiranno impostare la propria lingua come English invece di Inglese. Per cui c'è bisogno di aiuto nella conversione automatica di tali valori basati sulla cultura. 
     103 
     104 
     105### Visualizzare i dati nella cultura dell'utente 
     106 
     107Una volta che la cultura è definita, gli helper che dipendono da essa stamperanno automaticamente i dati nel formato corretto. Ad esempio, l'helper `format_number()` visualizza automaticamente nel formato familiare all'utente, secondo la propria cultura, come mostrato nel Listato 13-3. 
     108 
     109Listato 13-3 - Visualizzare numeri nella cultura dell'utente 
     110 
     111    [php] 
     112    <?php use_helper('Number') ?> 
     113      
     114    <?php $sf_user->setCulture('en_US') ?> 
     115    <?php echo format_number(12000.10) ?> 
     116     => '12,000.10' 
     117      
     118    <?php $sf_user->setCulture('fr_FR') ?> 
     119    <?php echo format_number(12000.10) ?> 
     120     => '12 000,10' 
     121 
     122Non hai bisogno di passare esplicitamente la cultura agli helper. Essi la cercheranno da soli nell'oggetto sessione corrente. Il Listato 13-4 mostra una lista degli helper che tengono conto della cultura nel loro output. 
     123 
     124Listato 13-4 - Helper dipendenti dalla cultura 
     125 
     126    [php] 
     127    <?php use_helper('Date') ?> 
     128      
     129    <?php echo format_date(time()) ?> 
     130     => '9/14/06' 
     131      
     132    <?php echo format_datetime(time()) ?> 
     133     => 'September 14, 2006 6:11:07 PM CEST' 
     134      
     135    <?php use_helper('Number') ?> 
     136      
     137    <?php echo format_number(12000.10) ?> 
     138     => '12,000.10' 
     139      
     140    <?php echo format_currency(1350, 'USD') ?> 
     141     => '$1,350.00' 
     142      
     143    <?php use_helper('I18N') ?> 
     144      
     145    <?php echo format_country('US') ?> 
     146     => 'United States' 
     147      
     148    <?php format_language('en') ?> 
     149     => 'English' 
     150      
     151    <?php use_helper('Form') ?> 
     152      
     153    <?php echo input_date_tag('birth_date', mktime(0, 0, 0, 9, 14, 2006)) ?> 
     154     => input type="text" name="birth_date" id="birth_date" value="9/14/06" size="11" /> 
     155      
     156    <?php echo select_country_tag('country', 'US') ?> 
     157     => <select name="country" id="country"><option value="AF">Afghanistan</option> 
     158          ... 
     159          <option value="GB">United Kingdom</option> 
     160          <option value="US" selected="selected">United States</option> 
     161          <option value="UM">United States Minor Outlying Islands</option> 
     162          <option value="UY">Uruguay</option> 
     163          ... 
     164        </select> 
     165 
     166Gli helper delle date accettano un parametro aggiuntivo di formattazione per forzare una visualizzazione indipendente dalla cultura, ma non dovresti usarlo se la tua applicazione è internazionalizzata. 
     167 
     168 
     169### Ricevere i dati da un input localizzato 
     170 
     171Se è necessario mostrare i dati nella cultura dell'utente, così come riceverli, dovresti per quanto possibile educare i tuoi utenti ad inserire dati già internazionalizzati. Questo approccio di eviterà di cercare di capire come convertire dati con diversi formati. Ad esempio, chi potrebbe inserire in un campo di testo un valore monetario con la virgola? 
     172 
     173Puoi limitare la formattazione dell'input inserito dall'utente sia nascondendo i dati effettivi (come nel caso di `select_country_tag()`) sia dividendo i diversi componenti di dati complessi in vari campi di testo. 
     174 
     175Per le date, comunque, spesso questo non è possibile. Gli utenti sono abituati ad inserire le date nel proprio formato culturale, e hai bisogno di saper convertire tali date in un formato interno (ed internazionale). Ecco dove ti viene in aiuto la classe `sfI18N`. Il Listato 13-5 mostra come usarla. 
     176 
     177Listato 13-5 - Ricevere una data in formato localizzato in un'azione 
     178 
     179    [php] 
     180    $date= $request->getParameter('birth_date'); 
     181    $user_culture = $this->getUser()->getCulture(); 
     182      
     183    // Ottenere un timestamp 
     184    $timestamp = $this->getContext()->getI18N()->getTimestampForCulture($date, $user_culture); 
     185      
     186    // Ottenere una data strutturata 
     187    list($d, $m, $y) = $this->getContext()->getI18N()->getDateForCulture($date, $user_culture); 
     188 
     189 
     190Testi nel database 
     191------------------ 
     192 
     193Un'applicazione localizzata fornisce diversi contenuti a seconda della cultura dell'utente. Ad esempio, un negozio online può fornire prodotti allo stesso prezzo in tutto il mondo, ma con descrizioni differenti a seconda del paese. Ciò significa che il database deve essere capace di memorizzare diverse versioni dello stesso dato, e per questo devi disegnare il tuo schema in un modo particolare; inoltre devi usare la cultura ogni volta che manipoli gli oggetti del modello. 
     194 
     195 
     196### Creare uno schema localizzato 
     197 
     198Devi dividere ogni tabella contenente dati localizzati in due parti: una tabella senza alcuna colonna i18n, e l'altra contenente esclusivamente colonne i18n. Le due tabelle saranno collegate tramite una relazione uno-a-molti. Questo setup ti permette di aggiungere nuove lingue qualora ce ne sia il bisogno, senza modificare il modello. Vediamo un esempio considerando la tabella `Product`. 
     199 
     200Prima di tutto crea lo schema delle tabelle come mostrato nel Listato 13-6. 
     201 
     202Listato 13-6 - Esempio di schema per dati i18n, in `config/schema.yml` 
     203 
     204    [php] 
     205    my_connection: 
     206      my_product: 
     207        _attributes: { phpName: Product, isI18N: true, i18nTable: my_product_i18n } 
     208        id:          { type: integer, required: true, primaryKey: true, autoincrement: true } 
     209        price:       { type: float } 
     210     
     211      my_product_i18n: 
     212        _attributes: { phpName: ProductI18n } 
     213        id:          { type: integer, required: true, primaryKey: true, foreignTable: my_product, foreignReference: id } 
     214        culture:     { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true } 
     215        name:        { type: varchar, size: 50 } 
     216 
     217Nota gli attributi `isI18N` e `i18nTable` della prima tabella, e la colonna speciale `culture` nella seconda. Sono entrambi miglioramenti specifici di symfony dovuti a Propel. 
     218 
     219Le automazioni di symfony rendono questo molto veloce da scrivere. Se la tabella che contiene i dati internazionalizzati ha lo stesso nome della tabella principale con il suffisso `_i18n`, ed esse sono collegate tramite `id`, puoi evitare di scrivere le colonne `id` e `culture` nella tabella `_i18n` e gli attributi i18n della tabella principale; symfony si occuperà di tutto. Con tali presupposti, symfony vedrà lo schema del Listato 13-7 esattamente come quello del Listato 13-6. 
     220 
     221Listato 13-7 - Esempio di schema per dati i18n, versione abbreviata, in `config/schema.yml` 
     222 
     223    [php] 
     224    my_connection: 
     225      my_product: 
     226        _attributes: { phpName: Product } 
     227        id: 
     228        price:       float 
     229      my_product_i18n: 
     230        _attributes: { phpName: ProductI18n } 
     231        name:        varchar(50) 
     232 
     233 
     234### Utilizzare gli oggetti i18n generati 
     235 
     236Una volta creato il corrispondente modello (non dimenticare di di chiamare `symfony propel:build-model` dopo aver modificato il file `schema.yml`), puoi usare la tabella `Product` con il supporto i18n come se fosse una tabella unica, come mostrato dal Listato 13-8. 
     237 
     238Listato 13-8 - Gestire oggetti i18n 
     239 
     240    [php] 
     241    $product = ProductPeer::retrieveByPk(1); 
     242    $product->setName('Nome del prodotto');  // Per default, la cultura è quella dell'utente 
     243    $product->save(); 
     244 
     245    echo $product->getName(); 
     246     => 'Nome prodotto' 
     247  
     248    $product->setName('Product name', 'en'); // cambia il valore per la cultura 'en' 
     249    $product->save(); 
     250  
     251    echo $product->getName('en'); 
     252     => 'Product name' 
     253 
     254Come per le query con gli oggetti peer, puoi restringere il risultato ad oggetti che abbiano una traduzione per la cultura corrente tramite il metodo `doSelectWithI18n`, invece di `doSelect`, come mostrato dal Listato 10-13. Inoltre, esso creerà l'oggetto i18n relativo nello stesso momento di quello di default, in modo che il numero di query per recuperare il contenuto sarà ridotto (consulta il Capitolo 18 per maggiori informazioni sull'impatto positivo di questo metodo sulle prestazioni). 
     255 
     256Listato 13-10 - Recuperare oggetti con un Criteria i18n 
     257 
     258    [php] 
     259    $c = new Criteria(); 
     260    $c->add(ProductPeer::PRICE, 100, Criteria::LESS_THAN); 
     261    $products = ProductPeer::doSelectWithI18n($c, $culture); 
     262    // Il parametro $culture è opzionale 
     263    // Se non viene passata alcuna cultura viene utilizzata quella di default dell'utente corrente 
     264 
     265Per cui, fondamentalmente, non dovresti mai gestire gli oggetti i18n direttamente, piuttosto passare la cultura al modello (o farglielo capire) ogni volta che esegui query. 
     266 
     267 
     268Traduzione delle interfacce 
     269--------------------------- 
     270 
     271Le interfacce utente hanno bisogno di essere adattate per applicazioni i18n. Le template devono saper presentare nello stesso formato, ma in lingue differenti, etichette, messaggi, e menu di navigazione. Symfony raccomanda di costruire le tue template nella lingua di default, per poi fornire le traduzioni in un dizionario. In tale maniera, non hai bisogno di cambiare le template ogni volta che modifichi, aggiungi o elimini una nuova lingua. 
     272 
     273 
     274### Configurare la traduzione 
     275 
     276Le template non vengono tradotte per default, ciò significa che devi prima di tutto attivare la gestione delle traduzioni nel file `settings.yml`, come mostrato nel Listato 13-11. 
     277 
     278Listato 13-11 - Attivazione della traduzione delle interfacce, in `frontend/config/settings.yml` 
     279 
     280    [php] 
     281    all: 
     282      .settings: 
     283        i18n: on 
     284 
     285 
     286### Utilizzare l'helper per la traduzione 
     287 
     288Diciamo che tu voglia costruire un sito in inglese e francese, con l'inglese come lingua di default. Ancora prima di pensare alle traduzioni, probabilmente hai scritto nelle tue template qualcosa come mostrato nell'esempio del Listato 13-12. 
     289 
     290Listato 13-12 - Template a lingua unica 
     291 
     292    [php] 
     293    Welcome to our website. Today's date is <?php echo format_date(date()) ?> 
     294 
     295Per poter fare in modo che symfony traduca i testi delle template, essi devono essere in qualche modo riconoscibili. Questo è lo scopo dell'helper `__()` (due underscore), un membro del gruppo di helper I18n. Per cui, in tutte le tue template, tutto ciò che deve essere tradotto deve essere racchiuso in tale helper. Il Listato 13-12, ad esempio, può essere modificato come nel Listato 13-13 (come vedrai nella sezione "Gestire traduzioni complesse" più avanti in questo capitolo, c'è anche un modo migliore per chiamare l'helper delle traduzioni di quello in questo esempio). 
     296 
     297Listato 13-13 - Template multi-lingua 
     298 
     299    [php] 
     300    <?php use_helper('I18N') ?> 
     301      
     302    <?php echo __('Welcome to our website.') ?> 
     303    <?php echo __("Today's date is ") ?> 
     304    <?php echo format_date(date()) ?> 
     305 
     306>**TIP** 
     307>Se la tua applicazione utilizza il gruppo di helper I18n per ogni pagina, probabilmente è una buona idea includerlo nell'impostazione `standard_helpers` del file `settings.yml`, in modo da ripetere in ogni template la chiamata `use_helper('I18N')`. 
     308 
     309 
     310### Utilizzare un dizionario 
     311 
     312Ogni volta che viene chiamata la funzione `__()` symfony cerca una traduzione del testo passatole come argomento nel dizionario della cultura dell'utente. Se trova una corrispondenza, viene visualizzata la traduzione, che quindi si basa su un file contenente il dizionario. 
     313 
     314I file dei dizionari con le traduzioni sono scritti in XML Localization Interchange File Format (XLIFF), nominati secondo il pattern `messages.[codice linguaggio].xml` e memorizzati nella cartella `i18n/` dell'applicazione. 
     315 
     316XLIFF è uno standard basato su XML. Dato che è molto conosciuto, puoi usare software di terze parti per trovare e gestire tutte le traduzioni. Le aziende che si occupano di traduzioni sanno come gestire tali file e come tradurre un intero sito semplicemente aggiungendone uno. 
     317 
     318>**NOTE** 
     319>In aggiunta allo standard XLIFF, symfony supporta anche diversi strumenti di backend per la traduzione, quali gettext, MySQL, SQLite, e Creole. Consulta la documentazione sulle API per la configurazione di tali backend. 
     320 
     321Il Listato 13-14 mostra un esempio di sintassi XLIFF con il file `messages.fr.xml` per la traduzione in francese del Listato 13-13. 
     322 
     323Listato 13-14 - Dizionario XLIFF, in `frontend/i18n/messages.fr.xml` 
     324 
     325    [php] 
     326    <?xml version="1.0" ?> 
     327    <xliff version="1.0"> 
     328      <file orginal="global" source-language="en_US" datatype="plaintext"> 
     329        <body> 
     330          <trans-unit id="1"> 
     331            <source>Welcome to our website.</source> 
     332            <target>Bienvenue sur notre site web.</target> 
     333          </trans-unit> 
     334          <trans-unit id="2"> 
     335            <source>Today's date is </source> 
     336            <target>La date d'aujourd'hui est </target> 
     337          </trans-unit> 
     338        </body> 
     339      </file> 
     340    </xliff> 
     341 
     342L'attributo `source-language` deve sempre contenere l'intero codice ISO della lingua della cultura di default, mentre ogni traduzione è racchiusa tra tag `trans-unit` ed ha un `id` unico. 
     343 
     344Nella lingua di default (in questo caso en_US) non avviene alcuna traduzione ed è visualizzata la stringa passata come argomento all'helper. Il risultato del Listato 13-13 è quindi simile a quello del Listato 13-12. Ma se la cultura viene cambiata in `fr_FR` o `fr_BE`, vengono invece mostrate le traduzioni del file `messages.fr.xml`, ed il risultato è il Listato 13-15. 
     345 
     346Listato 13-15 - Template tradotta 
     347 
     348    [php] 
     349    Bienvenue sur notre site web. La date d'aujourd'hui est 
     350    <?php echo format_date(date()) ?> 
     351 
     352Se hai bisogno di aggiungere lingue, non devi far altro che creare altri file `messages.XX.xml` nella stessa cartella. 
     353 
     354>**TIP** 
     355>Poiché la ricerca dei file dei dizionari, la loro analisi e la ricerca della traduzione corretta di una data stringa può portar via un po' di tempo, symfony usa una cache interna per accelerare il processo. Per default, questa cache usa il filesystem. Puoi configuare il funzionamento della cache i18N (per esempio, per condividere la cache tra server diversi) in `factories.yml` (vedi Capitolo 19). 
     356 
     357### Gestione dei dizionari 
     358 
     359Se il tuo file `messages.XX.xml` del dizionario diventa troppo lungo da leggere, lo puoi suddividere in più file, magari nominandoli per "tema". Ad esempio si potrebbe dividere il file `messages.fr.xml` nella cartella `i18n/` nei seguenti tre: 
     360 
     361  * `navigation.fr.xml` 
     362  * `terms_of_service.fr.xml` 
     363  * `search.fr.xml` 
     364     
     365Nota che ogni volta che non deve essere usato il file di default `messages.XX.xml`, occorre dichiarare esplicitamente quali file sono da utilizzare ogni volta che usi l'helper `__()`, specificandone il terzo argomento. Ad esempio, per stampare una stringa dal dizionario `navigation.fr.xml`: 
     366 
     367    [php] 
     368    <?php echo __('Welcome to our website', null, 'navigation') ?> 
     369 
     370Un altro modo per organizzare i dizionari è quello di suddividerli in moduli. Invece di scrivere un unico file `messages.XX.xml` per tutta l'applicazione, ne puoi scrivere uno in ogni cartella `modules/[module_name]/i18n/`. Questo rende i moduli più indipendenti dall'applicazione, cosa particolarmente utile quando hai la necessità di riutilizzarli (ad esempio come per i plugin, Capitolo 17). 
     371 
     372### Nuovo in symfony 1.1 
     373 
     374Poiché l'aggiornamento manuale dei dizionari i18n è spesso soggetto ad errori, symfony fornisce un task per automatizzare il processo. Il task `i18n:extract` analizza un'applicazione symfony ed estrae tutte le stringhe che necessitano di traduzione. Prende come argomenti un'applicazione ed una cultura: 
     375 
     376    > php symfony i18n:extract frontend en 
     377 
     378Per default, il task non modifica i tuoi dizionari, ma visualizza solamente il numero di stringhe i18n vecchie e nuove. Per aggiungere le nuove stringhe al tuo dizionario, puoi passare l'opzione `--auto-save`: 
     379 
     380    > php symfony i18n:extract --auto-save frontend it 
     381 
     382Puoi anche cancellare le vecchie stringhe automaticamente passando l'opzione `--auto-delete`: 
     383 
     384    > php symfony i18n:extract --auto-save --auto-delete frontend en 
     385 
     386Il task ha delle limitazioni note. Funziona solo con la cartella predefinita dei dizionari, e per gestioni basate su file (`XLIFF` o `gettext`), salva e cancella stringhe solo nel file principale `apps/frontend/i18n/messages.XX.xml`. 
     387 
     388### Gestione di altri elementi che necessitano di traduzione 
     389 
     390Altri elementi che potrebbero necessitare di una traduzione sono i seguenti: 
     391 
     392 * Immagini, documenti di testo, od ogni altro tipo di risorsa. Un esempio potrebbe essere un pezzo di testo con una tipografia particolare, visualizzata come immagine; per tutte queste risorse, puoi creare sotto cartelle con il la `culture` nel nome: 
     393     
     394    [php] 
     395    <? getCulture().'/myText.gif') ?> 
     396 
     397 * I messaggi di errore della validazione vengono automaticamente stampati in output con `__()`, per cui hanno solo bisogno di avere le traduzioni corrispondenti nei dizionari. 
     398 * Le pagine di default di symfony (pagina non trovata, errore interno del server, accesso riservato e così via) sono scritte in inglese e devono essere riscritte. Probabilmente dovresti scrivere il tuo modulo `default` personalizzato ed usare `__()` nelle template. Vedi il Capitolo 19 per capire come personalizzare tali pagine. 
     399 
     400 
     401Gestire traduzioni complesse 
     402---------------------------- 
     403 
     404Le traduzioni hanno senso solo quando l'argomento di `__()` è una frase completa. Tuttavia, potresti avere la necessità di inserire parametri in una frase, ed essere quindi tentato di suddividere la frase in diversi parti, cosicché l'argomento dell'helper perde di significato. Fortunatamente, l'helper `__()` offre una funzionalità basata su token che aiuta i traduttori ad avere un dizionario sensato. Il Listato 13-16 ne mostra qualche esempio. 
     405 
     406Listato 13-16 - Tradurre frasi che contengono codice 
     407 
     408    [php] 
     409    // Esempio di base 
     410    Welcome to all the <b>new</b> users.<br /> 
     411    There are <?php echo count_logged() ?> persons logged. 
     412      
     413    // Modo sbagliato di tradurre 
     414    <?php echo __('Welcome to all the') ?> 
     415    <b><?php echo __('new') ?></b> 
     416    <?php echo __('users') ?>.<br /> 
     417    <?php echo __('There are') ?> 
     418    <?php echo count_logged() ?> 
     419    <?php echo __('persons logged') ?> 
     420      
     421    // Modo migliore da utilizzare 
     422    <?php echo __('Welcome to all the <b>new</b> users') ?> <br /> 
     423    <?php echo __('There are %1% persons logged', array('%1%' => count_logged())) ?> 
     424 
     425In questo esempio il token è %1%, ma potrebbe essere qualsiasi cosa, dato che la funzione usata per la sostituzione è `strtr()`. 
     426 
     427Uno dei problemi più comuni nelle traduzioni è la forma plurale. A seconda della lingua e del numero di risultati il testo può cambiare. Per esempio nel Listato 13-16 la frase non è corretta se il risultato della funzione `count_logged()` fosse 0 o 1. Potresti certamente fare un test per verificare il valore e scegliere la frase di conseguenza, ma ciò implicherebbe scrivere più codice del necessario. Inoltre le lingue hanno regole differenti per il plurale, per cui la cosa può diventare complicata. Dato che questo problema è molto diffuso, symfony offre un helper per gestirlo, chiamato `format_number_choice()`. Il Listato 13-17 mostra come usarlo. 
     428 
     429Listato 13-17 - Frasi tradotte a seconda del valore di parametri 
     430 
     431    [php] 
     432    <?php echo format_number_choice( 
     433      '[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are %1% persons logged', array('%1%' => count_logged()), count_logged()) ?> 
     434 
     435 
     436Il primo parametro è una lista con le possibili scelte; il secondo, come per l'helper `__()` è il pattern di sostituzione, ed è opzionale. 
     437Il terzo è un numero sul quale si basa la scelta del testo da stampare. 
     438La sintassi di ogni singola frase (possibilità) da stampare è composta da pipe (|) come separatore, seguito da un array di valori accettabili: 
     439 
     440 * `[1,2]`: accetta valori tra 1 e 2, inclusi 
     441 * `(1,2)`: accetta valori tra 1 e 2, esclusi 
     442 * `{1,2,3,4`}: accetta solo i valori definiti tra le parentesi 
     443 * `[-Inf,0)`: accetta valori compresi tra infinito negativo e 0 (escluso) 
     444 * {n: n % 10 > 1 && n % 10 < 5} pliki: individua numeri come 2, 3, 4, 22, 23, 24 (utile per lingue come il polacco o il russo) '''Nuovo nella versione di sviluppo''' 
     445 
     446È accettabile anche una composizione non vuota di parentesi quadre e tonde. 
     447 
     448Il messaggio dovrà apparire esplicitamente anche nel file XLIFF per essere visualizzato nel modo corretto. Il Listato 13-18 mostra come. 
     449 
     450Listato 13-18 - Dizionario XLIFF per la funzione `format_number_choice()` 
     451 
     452    [php] 
     453    ... 
     454    <trans-unit id="3"> 
     455      <source>[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are%1% persons logged</source> 
     456      <target>[0]Personne n'est connecté|[1]Une personne est connectée|(1,+Inf]Il y a %1% personnes en ligne</target> 
     457    </trans-unit> 
     458    ... 
     459 
     460>**SIDEBAR** 
     461>Qualche parola a proposito dei charset 
     462>Gestire traduzioni porta spesso a problemi con i charset. Se usi un charset localizzato, dovrai cambiarlo ogni volta che l'utente cambia cultura. Inoltre, le template scritte in un charset non saranno visualizzate correttamente in un altro. 
     463>Per tale motivo, quando gestisci più di una cultura, tutte le template devono essere salvate in formato UTF-8, ed il layout deve dichiarare il contenuto di tale tipo. Se lavori sempre con questo formato ti risparmierai brutte sorprese e pesanti mal di testa. 
     464>Le applicazioni symfony si basano su un'impostazione centrale per il charset, nel file `settings.yml`. Cambiare tale parametro cambierà l'header `content-type` di tutte le risposte. 
     465> 
     466>    [php] 
     467>    all: 
     468>      .settings: 
     469>        charset: utf-8 
     470> 
     471 
     472 
     473### Chiamare l'helper per la traduzione fuori dalle template 
     474 
     475Non tutti i testi di un'applicazione provengono dalle template. Per cui spesso potresti aver bisogno di chiamare `__()` in altre parti dell'applicazione: azioni, filtri, classi del modello e così via. Il Listato 13-19 mostra come chiamare l'helper in un'azione recuperando l'istanza corrente dell'oggetto `I18n` attraverso il singleton. 
     476 
     477Listato 13-19 - Chiamare `__()` in un'azione 
     478 
     479    [php] 
     480    $this->getContext()->getI18N()->__($text, $args, 'messages'); 
     481 
     482 
     483Sommario 
     484-------- 
     485 
     486La gestione dell'internazionalizzazione e della localizzazione è più semplice se sai come gestire la cultura dell'utente. Gli helper si occupano automaticamente di stampare i dati formattati nel modo corretto, ed i contenuti localizzati nel database vengono visti come facenti parte di un'unica tabella. Per la traduzione delle interfacce l'helper `__()` ed i dizionari XLIFF assicurano che tu abbia la massima versatilità con il minimo sforzo. 
     487 
     488}}}