Localizza la tua applicazione Web per qualsiasi paese con l'API di Google Traduttore

Cosa starai creando

Nel mio tutorial Localization With I18n per la serie Building Your Startup With PHP, ho creato un campione di codice spagnolo tagliando e incollando le stringhe di testo in Google Translate. Ho iniziato a chiedermi se potevo integrare l'API di Google Translate con lo script di estrazione delle risorse I18n di Yii Framework per automatizzare la traduzione per un certo numero di paesi. Ho postato una richiesta di funzionalità al forum Yii e poi ho deciso di vedere se potevo costruire la funzione da solo.

In questo tutorial, ti guiderò attraverso le mie estensioni allo script di estrazione Yii I18n che fa esattamente questo. E dimostrerò di tradurre la mia applicazione di avvio, Meeting Planner, in una manciata di lingue.

Tieni presente che Google Traduttore non è perfetto e non affronta problemi relativi a formati e valute di data e ora. Ma per un modo rapido e conveniente (gratuito) di creare traduzioni predefinite per la tua applicazione web in oltre 50 lingue, questa è la soluzione ideale.

Ad esempio, tuttavia, ecco un errore più evidente che ho incontrato durante i test, fortunatamente questi sono rari:

'nFormatted TB' => 'nFormattato tubercolosi', 

Se hai bisogno di un approccio più professionale, un amico mi ha indirizzato verso un servizio a pagamento per la gestione della localizzazione all'interno delle app, Transifex. Non l'ho verificato da solo ma sembra intrigante.

Lavorare con Google Translate

Quali lingue supporta?

Google Traduttore offre servizi di traduzione per 64 lingue, tra cui lo svedese, ma purtroppo non lo chef svedese:

Ecco un campione delle lingue supportate da Google: consulta l'elenco completo qui:

Parlando con l'API di Google Traduttore

Ho trovato due librerie Composer per lavorare con l'API di Google Translator in PHP:

  • La libreria di Google Translate di Levan Velijanashvili
  • Google Translation Client di Travis Tillotson

Ho trovato il primo Velijanashvili quindi è quello che ho usato in questo tutorial. Sfrutta Google Traduttore tramite la sua interfaccia web RESTful gratuita in modo da non aver bisogno di una chiave API. Tuttavia, se hai una vasta libreria di risorse o hai intenzione di tradurre molte lingue, probabilmente vorrai integrare Tillotson in quanto è completamente integrato con il servizio a pagamento di Google Translate tramite le chiavi..

Per questo tutorial, sto costruendo il codebase di Building Your Startup With PHP series. Per installare la libreria di Google Traduttore di Velijanashvili, digita semplicemente:

il compositore richiede stichoza / google-translate-php

Ecco alcuni esempi di codice per tradurre dall'inglese allo spagnolo:

usa Stichoza \ Google \ GoogleTranslate; echo GoogleTranslate :: staticTranslate ('hello world', "en", "es"). "\ N"; 

Dovrebbe produrre:

hola mundo

Estensione del messaggio I18n di Yii2 / script di estrazione

Come funziona il supporto I18n di Yii2 oggi

A questo punto, potresti voler rivedere il mio tutorial sulla localizzazione con I18n che spiega come estrarre le stringhe dei messaggi per le tue traduzioni linguistiche necessarie. 

È possibile utilizzare il generatore di codice Gii di Yii per generare modelli e codice CRUD che integra automaticamente il supporto I18n. Ogni stringa del codice è sostituita da una chiamata di funzione come Yii :: t ('category', 'text string to translate');.

Yii offre un messaggio / estratto di comando della console che trova tutte queste chiamate di funzione nell'applicazione e crea un albero di directory di file per lingua e categoria per le traduzioni di tutte queste stringhe.

Ecco un file di esempio per il tedesco:

 'Machen Sie sich mit Yii begonnen', 'Intestazione' => 'Überschrift', 'My Yii Application' => 'Meine Yii-Anwendung', 'Yii Documentation' => 'Yii Dokumentation', 'Yii Extensions' => ' Yü -Erweiterungen ',' Yii Forum '=>' Yii Forum ',' Sei sicuro di voler eliminare questo elemento? ' => 'Sind Sie sicher, Sie wollen diesen Inhalt löschen?', 'Congratulazioni!' => 'Herzlichen Glückwunsch!', 'Crea' => 'schaffen', 'Crea modelClass' => 'schaffen modelClass', 'Creato in' => 'Erstellt am', 'Elimina' => 'löschen ',' ID '=>' Identifikation ',

Ecco un esempio dei percorsi di directory:

Estendere il messaggio / Estrai per Google Translate

Ho scelto l'approccio per creare uno script sostitutivo chiamato messaggio / google_extract che chiamerebbe Google Translate ogni volta che era necessario per tradurre una stringa.

Prevenire il codice danneggiato dalla traduzione di token

Poiché I18n integra i token dei parametri nelle parentesi graffe per i valori delle variabili, mi sono imbattuto subito in alcuni problemi. Ad esempio, ecco alcune stringhe I18n che includono token e token nidificati:

'Crea modelClass "Registrato a 0, data, MMMM dd, YYYY HH: mm da 1" 0, data, MMMM dd, YYYY HH: mm "nFormattato n, plurale, = 1 gibibyte other gibibytes '

L'API di Google Traduttore non ha un parametro per ignorare i token come questi in questo modulo. Ma non possiamo tradurli perché corrispondono a nomi di variabili e stringhe di formato nel codice.

Non mi sembrava che un'espressione regolare potesse risolvere ciò in cui le stringhe e i token traducibili erano presenti insieme. È probabile che i lettori possano avere una soluzione più efficiente di quella che ho trovato per risolvere questo problema, se ti è chiaro, per favore pubblicalo nei commenti.

Ho scelto di scansionare le corde per carattere e tracciare l'annidamento di parentesi graffe. Sarò il primo ad ammettere che potrebbe esserci un modo migliore. Ecco la mia funzione parse_safe_translate ():

/ * * analizza una stringa in un array * suddividendolo in qualsiasi segmento di parentesi graffa * incluse parentesi graffe annidate * / public function parse_safe_translate ($ s) $ debug = false; $ result = array (); $ Start = 0; $ nest = 0; $ Ptr_first_curly = 0; $ total_len = strlen ($ s); per ($ i = 0; $ i<$total_len; $i++)  if ($s[$i]=='')  // found left curly if ($nest==0)  // it was the first one, nothing is nested yet $ptr_first_curly=$i;  // increment nesting $nest+=1;  elseif ($s[$i]=='')  // found right curly // reduce nesting $nest-=1; if ($nest==0)  // end of nesting if ($ptr_first_curly-$start>= 0) // spinga la stringa che porta al primo prefisso di sinistra a sinistra $ substr ($ s, $ start, $ ptr_first_curly- $ start); if (strlen ($ prefix)> 0) array_push ($ result, $ prefix);  // push (possibilmente annidato) stringa riccia $ suffix = substr ($ s, $ ptr_first_curly, $ i- $ ptr_first_curly + 1); if (strlen ($ suffix)> 0) array_push ($ result, $ suffix);  if ($ debug) echo '|' .substr ($ s, $ start, $ ptr_first_curly- $ start-1). "| \ n"; echo '|' .substr ($ s, $ ptr_first_curly, $ i- $ ptr_first_curly + 1). "| \ n";  $ start = $ i + 1; $ Ptr_first_curly = 0; if ($ debug) echo 'next start:'. $ start. "\ n";  $ suffix = substr ($ s, $ start, $ total_len- $ start); if ($ debug) echo 'Start:'. $ start. "\ n"; echo 'Pfc:'. $ ptr_first_curly. "\ n"; echo $ suffisso. "\ n";  if (strlen ($ suffix)> 0) array_push ($ result, substr ($ s, $ start, $ total_len- $ start));  return $ result;  

Converte una stringa I18n in una serie di elementi separati in elementi traducibili e intraducibili. Ad esempio, questo codice:

$ message = 'L'immagine "file" è troppo grande. L'altezza non può essere maggiore di limite, numero limite, plurale, un pixel altro pixel. '; print_r ($ this-> parse_safe_translate ($ messaggio)); 

Genera questo output:

Array ([0] => L'immagine "[1] => file [2] =>" è troppo grande. L'altezza non può essere maggiore di [3] => limite, numero [4] => [ 5] => limite, plurale, un pixel altro pixel [6] =>.) 

Ogni volta che il processo di estrazione identifica una nuova stringa da tradurre, interrompe la stringa in queste parti e chiama l'API di Google Traduttore per qualsiasi stringa traducibile, ad es. uno che non inizia con una parentesi graffa sinistra. Quindi concatena quelle traduzioni con le stringhe tokenizzate in un'unica stringa.

Traduzione di una stringa con codice con Google Traduttore

Ecco la funzione getGoogleTranslation () per una stringa e una lingua di destinazione. La lingua di partenza è determinata da Yii :: $ app-> lingua.

 funzione pubblica getGoogleTranslation ($ message, $ language) $ arr_parts = $ this-> parse_safe_translate ($ message); $ translation = "; foreach ($ arr_parts as $ str) if (! stristr ($ str, '')) if (strlen ($ translation) 0 e substr ($ translation, -1) == ' ') $ translation. = "; $ translation. = GoogleTranslate :: staticTranslate ($ str, Yii :: $ app-> lingua, $ lingua);  else // aggiungi prefisso spazio a meno che non sia il primo if (strlen ($ translation)> 0) $ translation. = ". $ str; else $ translation. = $ str; print_r ($ translation); return $ translation;  

Ho scoperto che la combinazione di questi approcci ha funzionato quasi perfettamente nei miei test.

Personalizzazione del messaggio / estratto di Yii

L'implementazione I18n di Yii supporta il caricamento di stringhe di risorse da file .PO, file .PHP (che utilizzo) e database. Per questo tutorial, ho personalizzato Message / Extract per la generazione di file PHP.

Ho copiato ed esteso messaggio / estratto nel /console/controllers/TranslateController.php. A causa delle rigide regole di PHP 5.6.x, ho cambiato i nomi delle funzioni per saveMessagesToPHPsaveMessagesToPHPEnhancedsaveMessagesCategoryToPHPsaveMessagesCategoryToPHPEnhanced.

Ecco il saveMessagesToPHPEnhanced () funzione:

/ ** * Scrive i messaggi in file PHP * * @param array $ messaggi * @param stringa $ dirNome il nome della directory da scrivere su * @param booleano $ sovrascrive se il file esistente deve essere sovrascritto senza backup * @param booleano $ removeUnused se le traduzioni obsolete dovrebbero essere rimosse * @param booleano $ ordina se le traduzioni devono essere ordinate * / funzione protetta saveMessagesToPHPEnhanced ($ messages, $ dirName, $ overwrite, $ removeUnused, $ sort, $ language) foreach ($ messages as $ category => $ msgs) $ file = str_replace ("\\", '/', "$ dirName / $ category.php"); $ percorso = dirname ($ file); FileHelper :: createDirectory ($ path); $ msgs = array_values ​​(array_unique ($ msgs)); $ coloredFileName = Console :: ansiFormat ($ file, [Console :: FG_CYAN]); $ this-> stdout ("Salvataggio di messaggi in $ coloredFileName ... \ n"); $ this-> saveMessagesCategoryToPHPEnhanced ($ msgs, $ file, $ overwrite, $ removeUnused, $ sort, $ category, $ language);  

Chiama il saveMessagesCategoryToPHP funzione:

/ ** * Scrive i messaggi di categoria nel file PHP * * @param array $ messaggi * @param stringa $ fileNome nome del file da scrivere in * @param booleano $ sovrascrivi se il file esistente deve essere sovrascritto senza backup * @param booleano $ removeUnused se le traduzioni obsolete devono essere rimosse * @param booleano $ ordina se le traduzioni devono essere ordinate * @param booleano $ lingua per tradurre in * @param booleano $ force google translate * @param stringa $ categoria messaggio categoria * / funzione protetta saveMessagesCategoryToPHPEnhanced ($ messaggi, $ fileName, $ overwrite, $ removeUnused, $ sort, $ category, $ language, $ force = true) if (is_file ($ fileName)) $ existingMessages = require ($ fileName); sort ($ i messaggi); ksort ($ existingMessages); if (! $ force) if (array_keys ($ existingMessages) == $ messages) $ this-> stdout ("Nessuna novità in \" categoria $ categoria \ "... Niente da salvare. \ n \ n", Console: : FG_GREEN); ritorno;  $ merged = []; $ untranslated = []; foreach ($ messages as $ message) if (array_key_exists ($ message, $ existingMessages) && strlen ($ existingMessages [$ message])> 0) $ unito [$ message] = $ existingMessages [$ message];  else $ non tradotto [] = $ messaggio;  ksort ($ unito); sort ($ non tradotto); $ todo = []; foreach ($ non tradotto come $ messaggio) $ todo [$ message] = $ this-> getGoogleTranslation ($ message, $ language);  ksort ($ existingMessages); foreach ($ existingMessages come $ message => $ translation) if (! isset ($ unito [$ message]) &&! isset ($ todo [$ message]) &&! $ removeUnused) if (! empty ($ translation) && strncmp ($ translation, '@@', 2) === 0 && substr_compare ($ translation, '@@', -2, 2) === 0) $ todo [$ message] = $ translation;  else $ todo [$ message] = '@@'. $ traduzione. '@@';  $ merged = array_merge ($ todo, $ merged); if ($ sort) ksort ($ merged);  if (false === $ sovrascrive) $ fileName. = '.merged';  $ this-> stdout ("Traduzione unita. \ n");  else $ merged = []; foreach ($ messages as $ message) $ merged [$ message] = "; ksort ($ merged); $ array = VarDumper :: export ($ merged); $ content = <<id 'comando. * Contiene i messaggi localizzabili estratti dal codice sorgente. * È possibile modificare questo file traducendo i messaggi estratti. * * Ogni elemento dell'array rappresenta la traduzione (valore) di un messaggio (chiave). * Se il valore è vuoto, il messaggio è considerato come non tradotto. * I messaggi che non hanno più bisogno di traduzione avranno le loro traduzioni * racchiuse tra una coppia di segni '@@'. * * La stringa del messaggio può essere utilizzata con il formato di forme plurali. Controllare la sezione i18n * della guida per i dettagli. * * NOTA: questo file deve essere salvato nella codifica UTF-8. * / return $ array; EOD; file_put_contents ($ fileName, $ content); $ this-> stdout ("Traduzione salvata. \ n \ n", Console :: FG_GREEN); 

Sfortunatamente, il codice originale Message / Extract non è commentato. Sebbene possano essere apportati alcuni ulteriori miglioramenti, ho semplicemente aggiunto una chiamata all'API di Google Traduttore qui:

foreach ($ non tradotto come $ messaggio) $ todo [$ message] = $ this-> getGoogleTranslation ($ message, $ language); 

E ho aggiunto un parametro ($ Forza = true) forzare la ricreazione dei file di messaggi:

if (! $ force) if (array_keys ($ existingMessages) == $ messages) $ this-> stdout ("Nessuna novità in \" categoria $ categoria \ "... Niente da salvare. \ n \ n", Console: : FG_GREEN); ritorno; 

Traduzione di Message Planner

Completato il test, ero entusiasta di tradurre Message Planner in più lingue. Innanzitutto, aggiungiamo le nuove traduzioni della lingua al /console/config/i18n.php file:

 __DIR__. DIRECTORY_SEPARATOR. '...'. DIRECTORY_SEPARATOR. '...'. DIRECTORY_SEPARATOR, // Directory principale contenente le traduzioni dei messaggi. 'messagePath' => __DIR__. DIRECTORY_SEPARATOR. '...'. DIRECTORY_SEPARATOR. 'messaggi', // array, richiesto, elenco dei codici lingua a cui devono essere convertiti i messaggi // estratti. Ad esempio, ['zh-CN', 'de']. 'languages' => ['ar', 'es', 'de', 'it', 'iw', 'ja', 'yi', 'zh-CN'], 

Di nuovo, se hai bisogno di un supporto linguistico più ampio o se hai una maggiore quantità di stringhe da tradurre, ti consigliamo di passare al client di traduzione di Travis Tillotson e all'account API pagato.

Quindi, ho aggiunto le stringhe di traduzione /frontend/views/layouts/main.php e /frontend/views/site/index.php per dimostrare la traduzione della home page. Poiché queste pagine non sono generate dal generatore di codice Gii di Yii, le stringhe di testo sono state lasciate in formato HTML semplice. Ecco un esempio di come sono ora:

»

Ecco come appare la pagina iniziale in inglese:

Quindi, ho corso google_extract:

./ yii translate / google_extract /Users/Jeff/sites/mp/common/config/i18n.php 

Nota: assicurati di fare in modo che la lingua dell'applicazione sia impostata sulla lingua predefinita, ad es. Inglese. Questa impostazione è in /common/config/main.php:

 dirname (dirname (__ DIR__)). '/ venditore', // lingue disponibili // 'ar', 'de', 'es', 'it', 'iw', 'ja', 'yi', 'zh-CN' language '=>' it ', // english' components '=> [ 

Ho scoperto che era necessario correre google_extract una volta per creare il modello del file del messaggio iniziale e una seconda volta per avviare le chiamate a Google Translate.

Quindi posso modificare l'impostazione della lingua in /common/config/main.php vedere ogni traduzione I risultati sono piuttosto incredibili per qualcosa che può essere generato così rapidamente.

Ecco la pagina iniziale in cinese:

Ecco la home page in arabo:

Ecco la pagina iniziale in giapponese:

Ecco la home page in Yiddish:

Ecco la home page in tedesco:

Qual'è il prossimo?

Spero che questo tutorial ti sia piaciuto. È stato divertente scrivere qualcosa che avesse un impatto così ampio sulla portata potenziale della mia applicazione Meeting Planner. Se desideri saperne di più su Meeting Planner, guarda le prossime esercitazioni nella nostra serie "Costruire la tua partenza con PHP". Ci sono un sacco di funzioni divertenti in arrivo.

Non esitate ad aggiungere le vostre domande e commenti qui sotto; In genere partecipo alle discussioni. Puoi anche raggiungermi su Twitter @reifman o mandarmi un'email direttamente.

Link correlati

  • Costruire la tua startup con PHP: localizzazione con I18n (Tuts +)
  • Programmazione con Yii2: Guida introduttiva (Tuts +)
  • Introduzione al framework Yii (Tuts +)
  • La Guida Definitiva Yii2: Internazionalizzazione