Advertisement
  1. Code
  2. APIs

Lokalisieren Sie Ihre Webanwendung für jedes Land mit der Google Translate-API

Scroll to top
Read Time: 13 min

German (Deutsch) translation by Wei Zhang (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

In meinem Tutorial Lokalisierung mit I18n für das Erstellen Ihres Startups mit PHP-Serie habe ich spanischen Beispielcode erstellt, indem Sie Textzeichenfolgen in Google Translate ausschneiden und einfügen. Ich begann mich zu fragen, ob ich die Google Translate API mit dem I18n-Ressourcenextraktionsskript des Yii Framework integrieren könnte, um die Übersetzung für eine Reihe von Ländern zu automatisieren. Ich habe im Yii-Forum eine Feature-Anfrage gestellt und dann entschieden, ob ich die Funktion selbst erstellen kann.

In diesem Tutorial werde ich Sie durch meine Erweiterungen des Yii I18n-Extraktskripts führen, die genau dies tun. Ich zeige Ihnen, wie Sie meine Startanwendung Meeting Planner in eine Handvoll Sprachen übersetzen.

Beachten Sie, dass Google Translate nicht perfekt ist und sich nicht mit Problemen im Zusammenhang mit Zeit- und Datumsformaten und Währungen befasst. Für eine schnelle und kostengünstige (kostenlose) Möglichkeit, Standardübersetzungen für Ihre Webanwendung in mehr als 50 Sprachen zu erstellen, ist dies eine ideale Lösung.

Zum Beispiel ist hier ein auffälligerer Fehler aufgefallen, auf den ich beim Testen gestoßen bin - zum Glück sind dies selten:

1
'{nFormatted} TB' => '{nFormatted} tuberculosis',

Wenn Sie einen professionelleren Ansatz benötigen, wies mich ein Freund auf einen kostenpflichtigen Dienst zur Verwaltung der Lokalisierung in Apps, Transifex, hin. Ich habe es selbst nicht überprüft, aber es sieht faszinierend aus.

Mit Google Translate arbeiten

Welche Sprachen unterstützt es?

Google Translate bietet Übersetzungsdienste für 64 Sprachen an, darunter Schwedisch, aber leider nicht Schwedisch.

Hier finden Sie eine Auswahl der von Google unterstützten Sprachen. Die vollständige Liste finden Sie hier:

Google Translate List of Languages SupportedGoogle Translate List of Languages SupportedGoogle Translate List of Languages Supported

Mit der Google Translate-API sprechen

Ich habe zwei Composer-Bibliotheken für die Arbeit mit der Google Translator-API in PHP gefunden:

Ich habe Velijanashvili zuerst gefunden, es ist also das, was ich in diesem Tutorial verwendet habe. Es nutzt Google Translate über seine kostenlose RESTful-Weboberfläche, sodass Sie keinen API-Schlüssel benötigen. Wenn Sie jedoch über eine große Ressourcenbibliothek verfügen oder viele Sprachen übersetzen möchten, sollten Sie Tillotson's integrieren, da es vollständig über Google Keys in den kostenpflichtigen Dienst von Google Translate integriert ist.

Für dieses Tutorial baue ich auf die Codebase der PHP-Serie zum Erstellen Ihres Startups auf. Um die Google Translate-Bibliothek von Velijanashvili zu installieren, geben Sie einfach Folgendes ein:

1
composer require stichoza/google-translate-php

Hier ist ein Beispielcode, um aus dem Englischen ins Spanische zu übersetzen:

1
use Stichoza\Google\GoogleTranslate;
2
echo GoogleTranslate::staticTranslate('hello world', "en", "es"). "\n";         

Es sollte ausgeben:

hola mundo

Extending Yii2's I18n Message/Extract Script

Wie der I18n-Support von Yii2 heute funktioniert

Zu diesem Zeitpunkt möchten Sie vielleicht das Lernprogramm Lokalisierung mit I18n überprüfen, in dem erläutert wird, wie Sie Nachrichtenzeichenfolgen für Ihre erforderlichen Sprachübersetzungen extrahieren.

Sie können den Gii-Code-Generator von Yii verwenden, um Modelle und CRUD-Code zu generieren, der automatisch die Unterstützung von I18n integriert. Jeder String im Code wird durch einen Funktionsaufruf wie ersetzt Yii::t('category','text string to translate');

Yii bietet einen Konsolenbefehl message / extract, der alle diese Funktionsaufrufe in Ihrer Anwendung findet und eine Verzeichnisstruktur der Dateien nach Sprache und Kategorie für die Übersetzung all dieser Zeichenfolgen erstellt.

Hier eine Beispiel-String-Datei für Deutsch:

1
<?php
2
/**

3
* Message translations.

4
*

5
* This file is automatically generated by 'yii translate' command.

6
* It contains the localizable messages extracted from source code.

7
* You may modify this file by translating the extracted messages.

8
*

9
* Each array element represents the translation (value) of a message (key).

10
* If the value is empty, the message is considered as not translated.

11
* Messages that no longer need translation will have their translations

12
* enclosed between a pair of '@@' marks.

13
*

14
* Message string can be used with plural forms format. Check i18n section

15
* of the guide for details.

16
*

17
* NOTE: this file must be saved in UTF-8 encoding.

18
*/
19
return [
20
    'Get started with Yii' => 'Machen Sie sich mit Yii begonnen',
21
    'Heading' => 'Überschrift',
22
    'My Yii Application' => 'Meine Yii-Anwendung',
23
    'Yii Documentation' => 'Yii Dokumentation',
24
    'Yii Extensions' => 'Yü -Erweiterungen',
25
    'Yii Forum' => 'Yii Forum',
26
    'Are you sure you want to delete this item?' => 'Sind Sie sicher, Sie wollen diesen Inhalt löschen ?',
27
    'Congratulations!' => 'Herzlichen Glückwunsch!',
28
    'Create' => 'schaffen',
29
    'Create {modelClass}' => 'schaffen {modelClass}',
30
    'Created At' => 'Erstellt am',
31
    'Delete' => 'löschen',
32
    'ID' => 'Identifikation',

Hier ein Beispiel für die Verzeichnispfade:

Yii I18n Directory Structure For Message FilesYii I18n Directory Structure For Message FilesYii I18n Directory Structure For Message Files

Nachricht erweitern / extrahieren für Google Translate

Ich entschied mich für das Erstellen eines Ersatzskripts mit dem Namen message/google_extract, mit dem Google Translate aufgerufen wird, wenn ein String übersetzt werden muss.

Verhindern, dass beschädigter Code Token übersetzt

Da I18n Parametermarken in geschwungenen geschweiften Klammern für Variablenwerte integriert, sind mir sofort einige Probleme aufgefallen. Hier sind zum Beispiel einige I18n-Zeichenfolgen, die Token und verschachtelte Token enthalten:

1
'Create {modelClass}'
2
'Registered at {0, date, MMMM dd, YYYY HH:mm} from {1}'
3
'{0, date, MMMM dd, YYYY HH:mm}'
4
'{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}'

Die Google Translate-API verfügt nicht über einen Parameter zum Ignorieren von Token wie diesen in diesem Formular. Wir können diese jedoch nicht übersetzen, da sie Variablennamen und Formatzeichenfolgen im Code entsprechen.

Es schien mir nicht, dass ein regulärer Ausdruck das lösen könnte, wenn übersetzbare Zeichenketten und Marken gemeinsam vorhanden waren. Es ist wahrscheinlich, dass die Leser eine effizientere Lösung haben, als ich für die Lösung dieses Problems gefunden habe. Wenn Ihnen eines klar ist, schreiben Sie es in die Kommentare.

Ich habe mich entschieden, die Saiten nach Zeichen zu scannen und die Verschachtelung von geschweiften Klammern zu verfolgen. Ich gebe als erster zu, dass es vielleicht einen besseren Weg gibt. Hier ist meine Funktion parse_safe_translate():

1
/*

2
     * parses a string into an array 

3
     * splitting by any curly bracket segments

4
     * including nested curly brackets

5
     */
6
     public function parse_safe_translate($s) {
7
       $debug = false;
8
       $result = array();       
9
       $start=0;
10
       $nest =0;
11
       $ptr_first_curly=0;
12
       $total_len = strlen($s);
13
       for($i=0; $i<$total_len; $i++) {
14
          if ($s[$i]=='{') {
15
            // found left curly

16
            if ($nest==0) {
17
              // it was the first one, nothing is nested yet

18
              $ptr_first_curly=$i;
19
            }
20
            // increment nesting

21
            $nest+=1;
22
          } elseif ($s[$i]=='}')  {
23
            // found right curly

24
            // reduce nesting

25
            $nest-=1;
26
            if ($nest==0) {
27
              // end of nesting

28
              if ($ptr_first_curly-$start>=0) {
29
                // push string leading up to first left curly

30
                $prefix = substr ( $s ,  $start , $ptr_first_curly-$start);
31
                if (strlen($prefix)>0) {
32
                  array_push($result,$prefix);                                  
33
                }
34
              }
35
              // push (possibly nested) curly string

36
              $suffix=substr ( $s ,  $ptr_first_curly , $i-$ptr_first_curly+1);
37
              if (strlen($suffix)>0) {
38
                array_push($result,$suffix);
39
              }
40
              if ($debug) {
41
                echo '|'.substr ( $s ,  $start , $ptr_first_curly-$start-1)."|\n";            
42
                echo '|'.substr ( $s ,  $ptr_first_curly , $i-$ptr_first_curly+1)."|\n";   
43
              }
44
              $start=$i+1;   
45
              $ptr_first_curly=0; 
46
              if ($debug) {
47
                echo 'next start: '.$start."\n";          
48
              }
49
            }              
50
          }          
51
       }
52
       $suffix = substr ( $s ,  $start , $total_len-$start);
53
       if ($debug) {
54
         echo 'Start:'.$start."\n";
55
         echo 'Pfc:'.$ptr_first_curly."\n";
56
         echo $suffix."\n";            
57
       }
58
       if (strlen($suffix)>0) {
59
         array_push($result,substr ( $s ,  $start , $total_len-$start));         
60
       }
61
       return $result;
62
     }    

Es konvertiert einen I18n-String in ein Array von Elementen, die in übersetzbare und nicht übersetzbare Elemente unterteilt sind. Zum Beispiel dieser Code:

1
$message='The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.';
2
print_r($this->parse_safe_translate($message));

Erzeugt diese Ausgabe:

1
Array
2
(
3
    [0] => The image "

4
    [1] => {file}

5
    [2] => " is too large. The height cannot be larger than 
6
    [3] => {limit, number}
7
    [4] =>  
8
    [5] => {limit, plural, one{pixel} other{pixels}}
9
    [6] => .
10
)

Immer wenn der Extraktionsprozess eine zu übersetzende neue Zeichenfolge identifiziert, teilt er die Zeichenfolge in diese Teile auf und ruft die Google Translate-API für jede übersetzbare Zeichenfolge auf, z. Eine, die nicht mit einer linken geschweiften Klammer beginnt. Anschließend werden diese Übersetzungen mit den tokenisierten Zeichenfolgen zu einer einzigen Zeichenfolge zusammengefasst.

Übersetzung eines Token-Strings mit Google Translate

Hier ist die Funktion getGoogleTranslation() für einen String und eine Zielsprache. Die Ausgangssprache wird durch Yii::$app->language festgelegt.

1
         public function getGoogleTranslation($message,$language) {
2
           $arr_parts=$this->parse_safe_translate($message);
3
           $translation='';
4
           foreach ($arr_parts as $str) {
5
             if (!stristr($str,'{')) {
6
               if (strlen($translation)>0 and substr($translation,-1)=='}') $translation.=' ';
7
               $translation.=GoogleTranslate::staticTranslate($str, Yii::$app->language, $language);               
8
             } else {
9
               // add space prefix unless it's first

10
               if (strlen($translation)>0)
11
                 $translation.=' '.$str;
12
                else
13
                  $translation.=$str;
14
             }
15
           }
16
           print_r($translation);
17
           return $translation;
18
         }

Ich fand, dass die Kombination dieser Ansätze in meinen Tests fast perfekt funktionierte.

Anpassen von Yiis Nachricht / Auszug

Die I18n-Implementierung von Yii unterstützt das Laden von Ressourcenzeichenfolgen aus .PO-Dateien, .PHP-Dateien (die ich verwende) und der Datenbank. Für dieses Tutorial habe ich Message / Extract für die PHP-Dateigeneration angepasst.

Ich habe die message/ extract in /console/controllers/TranslateController.php kopiert und erweitert. Aufgrund der strengen Regeln von PHP 5.6.x habe ich die Funktionsnamen für saveMessagesToPHP in saveMessagesToPHPEnhanced und saveMessagesCategoryToPHP in saveMessagesCategoryToPHPEnhanced geändert.

Hier ist die saveMessagesToPHPEnhanced() - Funktion:

1
/**

2
      * Writes messages into PHP files

3
      *

4
      * @param array $messages

5
      * @param string $dirName name of the directory to write to

6
      * @param boolean $overwrite if existing file should be overwritten without backup

7
      * @param boolean $removeUnused if obsolete translations should be removed

8
      * @param boolean $sort if translations should be sorted

9
      */
10
     protected function saveMessagesToPHPEnhanced($messages, $dirName, $overwrite, $removeUnused, $sort,$language)
11
     {       
12
         foreach ($messages as $category => $msgs) {           
13
             $file = str_replace("\\", '/', "$dirName/$category.php");
14
             $path = dirname($file);
15
             FileHelper::createDirectory($path);
16
             $msgs = array_values(array_unique($msgs));
17
             $coloredFileName = Console::ansiFormat($file, [Console::FG_CYAN]);
18
             $this->stdout("Saving messages to $coloredFileName...\n");
19
             $this->saveMessagesCategoryToPHPEnhanced($msgs, $file, $overwrite, $removeUnused, $sort, $category,$language);
20
         }
21
     }     

Es ruft die saveMessagesCategoryToPHP-Funktion auf:

1
/**

2
          * Writes category messages into PHP file

3
          *

4
          * @param array $messages

5
          * @param string $fileName name of the file to write to

6
          * @param boolean $overwrite if existing file should be overwritten without backup

7
          * @param boolean $removeUnused if obsolete translations should be removed

8
          * @param boolean $sort if translations should be sorted

9
          * @param boolean $language language to translate to

10
          * @param boolean $force google translate

11
          * @param string $category message category

12
          */
13
         protected function saveMessagesCategoryToPHPEnhanced($messages, $fileName, $overwrite, $removeUnused, $sort, $category,$language,$force=true)
14
         {
15
             if (is_file($fileName)) {
16
                 $existingMessages = require($fileName);
17
                 sort($messages);
18
                 ksort($existingMessages);
19
                 if (!$force) {
20
                   if (array_keys($existingMessages) == $messages) {
21
                       $this->stdout("Nothing new in \"$category\" category... Nothing to save.\n\n", Console::FG_GREEN);
22
                       return;
23
                   }                   
24
                 }
25
                 $merged = [];
26
                 $untranslated = [];
27
                 foreach ($messages as $message) {
28
                     if (array_key_exists($message, $existingMessages) && strlen($existingMessages[$message]) > 0) {
29
                         $merged[$message] = $existingMessages[$message];
30
                     } else {
31
                         $untranslated[] = $message;
32
                     }
33
                 }
34
                 ksort($merged);
35
                 sort($untranslated);
36
                 $todo = [];
37
                 foreach ($untranslated as $message) {
38
                     $todo[$message] = $this->getGoogleTranslation($message,$language);
39
                 }
40
                 ksort($existingMessages);
41
                 foreach ($existingMessages as $message => $translation) {
42
                     if (!isset($merged[$message]) && !isset($todo[$message]) && !$removeUnused) {
43
                         if (!empty($translation) && strncmp($translation, '@@', 2) === 0 && substr_compare($translation, '@@', -2, 2) === 0) {
44
                             $todo[$message] = $translation;
45
                         } else {
46
                             $todo[$message] = '@@' . $translation . '@@';
47
                         }
48
                     }
49
                 }
50
                 
51
                 $merged = array_merge($todo, $merged);
52
                 if ($sort) {
53
                     ksort($merged);
54
                 }
55
                 if (false === $overwrite) {
56
                     $fileName .= '.merged';
57
                 }
58
                 $this->stdout("Translation merged.\n");
59
             } else {
60
                 $merged = [];
61
                 foreach ($messages as $message) {
62
                     $merged[$message] = '';
63
                 }
64
                 ksort($merged);
65
             }
66
67
68
             $array = VarDumper::export($merged);
69
             $content = <<<EOD
70
<?php
71
/**

72
* Message translations.

73
*

74
* This file is automatically generated by 'yii {$this->id}' command.

75
* It contains the localizable messages extracted from source code.

76
* You may modify this file by translating the extracted messages.

77
*

78
* Each array element represents the translation (value) of a message (key).

79
* If the value is empty, the message is considered as not translated.

80
* Messages that no longer need translation will have their translations

81
* enclosed between a pair of '@@' marks.

82
*

83
* Message string can be used with plural forms format. Check i18n section

84
* of the guide for details.

85
*

86
* NOTE: this file must be saved in UTF-8 encoding.

87
*/
88
return $array;
89
EOD;
90
91
             file_put_contents($fileName, $content);
92
             $this->stdout("Translation saved.\n\n", Console::FG_GREEN);
93
         }

Leider ist der ursprüngliche Message / Extract-Code nicht kommentiert. Es gibt zwar einige weitere Verbesserungen, aber ich habe hier einfach einen Aufruf der Google Translate API hinzugefügt:

1
foreach ($untranslated as $message) {
2
    $todo[$message] = $this->getGoogleTranslation($message,$language);
3
}

Und ich habe einen Parameter ($force=true) hinzugefügt, um die Neuerstellung der Nachrichtendateien zu erzwingen:

1
if (!$force) {
2
    if (array_keys($existingMessages) == $messages) {
3
    $this->stdout("Nothing new in \"$category\" category... Nothing to save.\n\n", Console::FG_GREEN);
4
    return;
5
    }                   
6
}

Message Planner übersetzen

Die Tests wurden abgeschlossen und ich war begeistert, Message Planner in weitere Sprachen zu übersetzen. Zuerst fügen wir die neuen Sprachübersetzungen der Datei /console/config/i18n.php hinzu:

1
<?php
2
3
return [
4
    // string, required, root directory of all source files
5
    'sourcePath' => __DIR__. DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR,
6
    // Root directory containing message translations.
7
    'messagePath' => __DIR__ . DIRECTORY_SEPARATOR .'..'. DIRECTORY_SEPARATOR . 'messages',
8
    // array, required, list of language codes that the extracted messages
9
    // should be translated to. For example, ['zh-CN', 'de'].
10
     'languages' => ['ar','es','de','it','iw','ja','yi','zh-CN'],

Wenn Sie eine breitere Sprachunterstützung benötigen oder eine größere Anzahl von zu übersetzenden Zeichenfolgen benötigen, möchten Sie möglicherweise zu Travis Tillotsons Google Translation Client und einem kostenpflichtigen API-Zugriff wechseln.

Dann fügte ich die Übersetzungszeichenfolgen /frontend/views/layouts/main.php und /frontend/views/site/index.php hinzu, um die Übersetzung der Startseite zu demonstrieren. Da diese Seiten nicht von Yiis Gii-Code-Generator generiert werden, wurden die Textzeichenfolgen in reinem HTML-Code gelassen. Hier ist ein Beispiel, wie sie jetzt aussehen:

1
<div class="row">
2
    <div class="col-lg-4">
3
        <h2><?= Yii::t('frontend','Getting Started') ?></h2>
4
        <p><?= Yii::t('frontend','Follow along with our tutorial series at Tuts+ as we build Meeting Planner step by step. In this episode we talk about startups in general and the goals for our application.') ?></p>
5
        <p><a class="btn btn-default" href="http://code.tutsplus.com/tutorials/building-your-startup-with-php-getting-started--cms-21948"><?= Yii::t('frontend','Episode 1') ?> &raquo;</a></p>

So sieht die Homepage auf Englisch aus:

Meeting Planner English Home PageMeeting Planner English Home PageMeeting Planner English Home Page

Dann lief ich google_extract:

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

Hinweis: Stellen Sie dabei sicher, dass die Anwendungssprache auf Ihre Standardsprache eingestellt ist, z. Englisch. Diese Einstellung befindet sich in /common/config/main.php:

1
<?php
2
return [
3
    'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
4
    // available languages
5
    // 'ar','de','es','it','iw','ja','yi','zh-CN'
6
    'language' => 'en', // english
7
    'components' => [

Ich stellte fest, dass es notwendig war, google_extract einmal auszuführen, um die Vorlage für die ursprüngliche Nachrichtendatei zu erstellen, und ein zweites Mal, um die Aufrufe von Google Translate einzuleiten.

Dann kann ich die Spracheinstellung in /common/config/main.php ändern, um jede Übersetzung anzuzeigen. Die Ergebnisse sind ziemlich unglaublich für etwas, das so schnell generiert werden kann.

Hier ist die Startseite auf Chinesisch:

Meeting Planner Chinese Home PageMeeting Planner Chinese Home PageMeeting Planner Chinese Home Page

Hier ist die Homepage auf Arabisch:

Meeting Planner Arabic Home PageMeeting Planner Arabic Home PageMeeting Planner Arabic Home Page

Hier ist die Startseite auf Japanisch:

Meeting Planner Japanese Home PageMeeting Planner Japanese Home PageMeeting Planner Japanese Home Page

Hier ist die Homepage auf Jiddisch:

Meeting Planner Yiddish Home PageMeeting Planner Yiddish Home PageMeeting Planner Yiddish Home Page

Hier ist die Startseite in deutscher Sprache:

Meeting Planner German Home PageMeeting Planner German Home PageMeeting Planner German Home Page

Was kommt als nächstes?

Ich hoffe, dir hat dieses Tutorial gefallen. Es hat Spaß gemacht, etwas zu schreiben, das die Reichweite meiner Meeting Planner-Anwendung so stark beeinflusste. Wenn Sie mehr über Meeting Planner erfahren möchten, finden Sie in der Serie "Erstellen Sie Ihr Startup mit PHP" die nächsten Tutorials. Es gibt viele lustige Features.

Bitte zögern Sie nicht, Ihre Fragen und Kommentare unten hinzuzufügen. Ich nehme generell an den Diskussionen teil. Sie können mich auch auf Twitter @reifman erreichen oder mich direkt per E-Mail kontaktieren.

ähnliche Links

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.