Lav Din opstart med PHP: Lokalisering Med I18n
() translation by (you can also view the original English article)



Dette er en del 4, af Opbyg dit Startup med PHP-serien på Tuts+. I denne serie, jeg vejlede dig gennem lancering af en opstart fra ide til virkelighed ved hjælp af min Mødeplanlægger app som en virkelige liv eksempel. Hvert skridt på vejen, vil vi frigive Mødeplanlægger kode som open source eksempler, du kan lære af. Vi vil også tage fat startup-relaterede forretningsmæssige problemstillinger, som de opstår.
I denne tutorial ønskede jeg at træde et skridt tilbage og tilføje I18n internationalisering understøttelse til vores ansøgning, før vi opbygge mere og mere kode. Ifølge Wikipedia er I18n et numeronym:
18 står for antallet af breve mellem første i og sidste n internationalisering, en skik, opfundet på DEC i 1970 ' erne eller 80 'er.
Med I18n erstattes alle tekststrengene vises til brugeren fra programmet funktionskald, der dynamisk kan indlæse oversatte strenge for ethvert sprog, som brugeren vælger.
Alle kode for Mødeplanlægger er skrevet i Yii2 Framework for PHP, som har indbygget understøttelse af I18n. Hvis du vil vide mere om Yii2, tjek vores parallel serie Programmering med Yii2 på Tuts+.
Bare en påmindelse, jeg deltager i kommentar tråde nedenfor. Jeg er især interesseret, hvis du har forskellige tilgange eller yderligere ideer, eller ønsker at foreslå emner til fremtidige tutorials.
Målene for internationalisering
Når du bygger en start, det er nyttigt at tænke globalt fra begyndelsen – men ikke altid. Skiftevis, kan det være fornuftigt at kun fokusere på dit lokale marked. Skal din mindste levedygtig produkt at arbejde på andre sprog for brugere fra forskellige lande?
I vores tilfælde, Yii Framework indeholder indbygget understøttelse af I18n, så det er relativt let at indbygge understøttelse til I18n fra begyndelsen – og tidskrævende at tilføje det i senere.
Hvordan I18n virker
I18n fungerer ved at erstatte alle henvisninger til tekst, der vises til brugeren med funktionskald, der leverer oversættelse efter behov.
For eksempel, er her hvad attribut feltnavne i sted model look som før I18n:
1 |
public function attributeLabels() |
2 |
{
|
3 |
return [ |
4 |
'id' => 'ID', |
5 |
'name' => 'Name', |
6 |
'place_type' => 'Place Type', |
7 |
...
|
At yde oversatte versioner af koden ville blive meget kompliceret. Ikke-tekniske oversættere skulle oversætte koden i sted, sandsynligvis bryde syntaks.
Her er hvad den samme kode ser ud med I18n:
1 |
public function attributeLabels() |
2 |
{
|
3 |
return [ |
4 |
'id' => Yii::t('frontend', 'ID'), |
5 |
'name' => Yii::t('frontend', 'Name'), |
6 |
'place_type' => Yii::t('frontend', 'Place Type'), |
Yii:t()
er et funktionskald, der undersøger, hvilket sprog der aktuelt er valgt og viser den passende oversatte streng. 'frontend'
henviser til en del af vores program. Oversættelser kan eventuelt organiseret i forskellige kategorier. Men hvor synes disse oversatte strenge?
Standardsproget i dette tilfælde engelsk, er skrevet i kode, som vist ovenfor. Sprog ressourcefiler er lister over arrays af strenge hvis nøgle er den standard sprog text—e.g. 'Sted Type' — og hver fil indeholder oversatte tekstværdier for deres relevante sprog.
Her er et eksempel på vores færdige spansk oversættelses fil, sprogkoden "da". Funktionen Yii:t()
bruger denne fil til at finde en passende oversættelse til at vise:
1 |
<?php
|
2 |
/**
|
3 |
* Message translations.
|
4 |
*
|
5 |
* This file is automatically generated by 'yii message' 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 |
'Add Current Location' => 'Agregar ubicación actual', |
21 |
'Add a Google {modelClass}' => 'Añadir un Google {modelClass}', |
22 |
'Created By' => 'Creado por', |
23 |
'Full Address' => 'Dirección completa', |
24 |
'Google Place ID' => 'Google Place ID', |
25 |
'Name' => 'Nombre', |
26 |
'Notes' => 'Notas', |
27 |
'Place Type' => 'Place Tipo', |
28 |
'Places' => 'Lugares', |
Mens dette ser tidskrævende, giver Yii scripts til at automatisere generation og organisation af disse filer.
Ved at adskille tekst fra koden, vi gør det lettere for ikke-tekniske flersprogede eksperter til at oversætte vores programmer for os — uden at bryde koden.
I18n tilbyder også specialiserede funktioner til oversættelse af tid, valuta, flertalsformer mfl. Jeg vil ikke gå i detaljer med disse i denne tutorial.
Konfiguration af I18n understøttelse
Desværre, Yii2 dokumentationen for I18n endnu ikke er meget beskrivende — og det var svært at finde arbejde, trin for trin eksempler. Heldigvis for dig vil jeg lede dig gennem hvad jeg har lært fra vaskning docs og på nettet. Jeg fandt The Code Ninja I18n eksempel og Yii2 Definitive Guide på I18n nyttige, og Yii bidragyder Alexander Makarov tilbød mig noget hjælp.
Generering af filen I18n konfiguration
Vi bruger Yii2 avancerede skabelon til mødeplanlægning. Dette skaber to Yii programmer i vores codebase, frontend og backend. Og det skaber et fælles område for modeller delt mellem begge programmer. Yiis konfigurationsfiler er indlæst når sideanmodninger er lavet. Vi vil bruge Yii's I18n besked scripts til at opbygge en konfigurationsfil til I18n i stien common/config
.
Fra vores codebase rod, vil vi køre scriptet Yii message/config
:
1 |
./yii message/config @common/config/i18n.php |
Dette genererer Filskabelonen til følgende, som vi kan tilpasse:
1 |
<?php
|
2 |
|
3 |
return [ |
4 |
// string, required, root directory of all source files
|
5 |
'sourcePath' => __DIR__, |
6 |
// array, required, list of language codes that the extracted messages
|
7 |
// should be translated to. For example, ['zh-CN', 'de'].
|
8 |
'languages' => ['de'], |
9 |
// string, the name of the function for translating messages.
|
10 |
// Defaults to 'Yii::t'. This is used as a mark to find the messages to be
|
11 |
// translated. You may use a string for single function name or an array for
|
12 |
// multiple function names.
|
13 |
'translator' => 'Yii::t', |
14 |
// boolean, whether to sort messages by keys when merging new messages
|
15 |
// with the existing ones. Defaults to false, which means the new (untranslated)
|
16 |
// messages will be separated from the old (translated) ones.
|
17 |
'sort' => false, |
18 |
// boolean, whether to remove messages that no longer appear in the source code.
|
19 |
// Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks.
|
20 |
'removeUnused' => false, |
21 |
// array, list of patterns that specify which files/directories should NOT be processed.
|
22 |
// If empty or not set, all files/directories will be processed.
|
23 |
// A path matches a pattern if it contains the pattern string at its end. For example,
|
24 |
// '/a/b' will match all files and directories ending with '/a/b';
|
25 |
// the '*.svn' will match all files and directories whose name ends with '.svn'.
|
26 |
// and the '.svn' will match all files and directories named exactly '.svn'.
|
27 |
// Note, the '/' characters in a pattern matches both '/' and '\'.
|
28 |
// See helpers/FileHelper::findFiles() description for more details on pattern matching rules.
|
29 |
'only' => ['*.php'], |
30 |
// array, list of patterns that specify which files (not directories) should be processed.
|
31 |
// If empty or not set, all files will be processed.
|
32 |
// Please refer to "except" for details about the patterns.
|
33 |
// If a file/directory matches both a pattern in "only" and "except", it will NOT be processed.
|
34 |
'except' => [ |
35 |
'.svn', |
36 |
'.git', |
37 |
'.gitignore', |
38 |
'.gitkeep', |
39 |
'.hgignore', |
40 |
'.hgkeep', |
41 |
'/messages', |
42 |
],
|
43 |
|
44 |
// 'php' output format is for saving messages to php files.
|
45 |
'format' => 'php', |
46 |
// Root directory containing message translations.
|
47 |
'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages', |
48 |
// boolean, whether the message file should be overwritten with the merged messages
|
49 |
'overwrite' => true, |
50 |
|
51 |
|
52 |
/*
|
53 |
// 'db' output format is for saving messages to database.
|
54 |
'format' => 'db',
|
55 |
// Connection component to use. Optional.
|
56 |
'db' => 'db',
|
57 |
// Custom source message table. Optional.
|
58 |
// 'sourceMessageTable' => '{{%source_message}}',
|
59 |
// Custom name for translation message table. Optional.
|
60 |
// 'messageTable' => '{{%message}}',
|
61 |
*/
|
62 |
|
63 |
/*
|
64 |
// 'po' output format is for saving messages to gettext po files.
|
65 |
'format' => 'po',
|
66 |
// Root directory containing message translations.
|
67 |
'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
|
68 |
// Name of the file that will be used for translations.
|
69 |
'catalog' => 'messages',
|
70 |
// boolean, whether the message file should be overwritten with the merged messages
|
71 |
'overwrite' => true,
|
72 |
*/
|
73 |
];
|
Jeg tilpasse min fil. Jeg flytter messagePath
op til toppen og tilpasse sourcePath
og messagePath
. Jeg laver også angivelse af sprog Jeg vil have min application til understøtte udover engelsk-i dette tilfælde spansk og tysk, 'es' og 'de'. Her er en liste over alle I18n sprogkoder.
1 |
return [ |
2 |
// string, required, root directory of all source files
|
3 |
'sourcePath' => __DIR__. DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR, |
4 |
// Root directory containing message translations.
|
5 |
'messagePath' => __DIR__ . DIRECTORY_SEPARATOR .'..'. DIRECTORY_SEPARATOR . 'messages', |
6 |
// array, required, list of language codes that the extracted messages
|
7 |
// should be translated to. For example, ['zh-CN', 'de'].
|
8 |
'languages' => ['es','de'], |
I det næste trin, vil vi køre Yiis Ekstraktet script, som vil skanne alle kode i sourcePath
træet til at generere standard streng filer for alle de anvendte etiketter i vores kode. Jeg tilpasning af sourcePath
for at scanne hele kode træet. Jeg tilpasning af messagePath
for at generere den resulterende filer i common/message
.
1 |
./yii message/extract @common/config/i18n.php |
Du vil se Yii scanning alle din kodefiler:
1 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/Place.php... |
2 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/PlaceGPS.php... |
3 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/PlaceSearch.php... |
4 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/ResetPasswordForm.php... |
5 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/models/SignupForm.php... |
6 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/layouts/main.php... |
7 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/_form.php... |
8 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/_search.php... |
9 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/create.php... |
10 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/index.php... |
11 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/update.php... |
12 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/meeting/view.php... |
13 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_form.php... |
14 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_formGeolocate.php... |
15 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_formPlaceGoogle.php... |
16 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/_search.php... |
17 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/create.php... |
18 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/create_geo.php... |
19 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/create_place_google.php... |
20 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/index.php... |
21 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/locate.php... |
22 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/update.php... |
23 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/place/view.php... |
24 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/about.php... |
25 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/contact.php... |
26 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/error.php... |
27 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/index.php... |
28 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/login.php... |
29 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/requestPasswordResetToken.php... |
30 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/resetPassword.php... |
31 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/views/site/signup.php... |
32 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/web/index-test.php... |
33 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/web/index.php... |
34 |
Extracting messages from /Users/Jeff/Sites/mp/frontend/widgets/Alert.php... |
Når den er færdig, vil du se noget som dette i din kodebase:



Aktivering I18n og vælge et sprog
I den fælles konfigurationsfil, common/config/main.php
, vil vi fortælle Yii om vores nye sprogunderstøttelse. Jeg vil gøre spansk mit standardsprog:
1 |
<?php
|
2 |
return [ |
3 |
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', |
4 |
'language' => 'es', // spanish |
5 |
'components' => [ |
6 |
'cache' => [ |
7 |
'class' => 'yii\caching\FileCache', |
8 |
],
|
9 |
'i18n' => [ |
10 |
'translations' => [ |
11 |
'frontend*' => [ |
12 |
'class' => 'yii\i18n\PhpMessageSource', |
13 |
'basePath' => '@common/messages', |
14 |
],
|
15 |
'backend*' => [ |
16 |
'class' => 'yii\i18n\PhpMessageSource', |
17 |
'basePath' => '@common/messages', |
18 |
],
|
19 |
],
|
20 |
],
|
21 |
],
|
22 |
];
|
Men der er stadig mere at gøre. Vi skal gøre vores kode I18n opmærksomme.
Ved hjælp af Yii's Gii kode Generator med I18n
I del to af denne serie, bygning din opstart med PHP: funktionskrav og Database Design, vi brugte Yii's awesome kode generator, Gii, til at generere vores modeller, controllere og udsigt. Men vi gjorde ikke aktivere I18n, så alle vores kode indlejret tekststrenge. Lad os gentage dette.
Vi vende tilbage til Gii, sandsynligvis http://localhost:8888/mp/gii i din browser, og re-opstille model og controller generatorerne med I18n aktiveret.
Bemærk: Hvis du holdt op med del tre af vores serie, kan du nødt til at gøre opmærksom på forskellene med hver enkelt fil og flytte manuelt kode. Eller det kan være nemmest at erstatte din kode med Github opbevaringssted for denne tutorial, knyttet øverst til højre.
Her er et eksempel til at generere koden møde model med I18n aktiveret. Bemærk, at vi angiver "frontend" for vores besked kategori. Vi placerer alle vores frontend tekststrenge i én frontend kategori fil.



Lad os gøre det samme for CRUD generation controllere og visninger:



Hvis du gennemse den genererede kode i modeller, controllere og udsigt, vil du se tekststrenge erstattet med funktionen Yii:t('frontend',...)
:
1 |
<?php
|
2 |
|
3 |
use yii\helpers\Html; |
4 |
use yii\grid\GridView; |
5 |
|
6 |
/* @var $this yii\web\View */
|
7 |
/* @var $searchModel frontend\models\PlaceSearch */
|
8 |
/* @var $dataProvider yii\data\ActiveDataProvider */
|
9 |
|
10 |
$this->title = Yii::t('frontend', 'Places'); |
11 |
$this->params['breadcrumbs'][] = $this->title; |
12 |
?>
|
13 |
<div class="place-index"> |
14 |
|
15 |
<h1><?= Html::encode($this->title) ?></h1> |
16 |
<?php // echo $this->render('_search', ['model' => $searchModel]); ?> |
17 |
|
18 |
<p> |
19 |
<?= Html::a(Yii::t('frontend', 'Create {modelClass}', [ |
20 |
'modelClass' => 'Place', |
21 |
]), ['create'], ['class' => 'btn btn-success']) ?> |
22 |
|
23 |
<?= Html::a(Yii::t('frontend','Add Current Location'), ['create_geo'], ['class' => 'btn btn-success']) ?> |
24 |
<?= Html::a(Yii::t('frontend','Add a Google {modelClass}',[ |
25 |
'modelClass' => 'Place' |
26 |
]), ['create_place_google'], ['class' => 'btn btn-success']) ?> |
27 |
</p>
|
Oversæt Dine Meddelelses Filer
Tag et kig på vores spanske meddelelsesfil, /common/messages/es/frontend.php
. Det er en lang liste af Tom array værdier:
1 |
return [ |
2 |
'Add Current Location' => '', |
3 |
'Add a Google {modelClass}' => '', |
4 |
'Are you sure you want to delete this item?' => '', |
5 |
'Create' => '', |
6 |
'Create {modelClass}' => '', |
7 |
'Created At' => '', |
8 |
'Created By' => '', |
9 |
'Delete' => '', |
10 |
'Full Address' => '', |
11 |
'Google Place ID' => '', |
12 |
'ID' => '', |
13 |
'Meeting Type' => '', |
14 |
'Meetings' => '', |
15 |
'Message' => '', |
16 |
'Name' => '', |
17 |
'Notes' => '', |
18 |
'Owner ID' => '', |
19 |
'Place Type' => '', |
20 |
'Places' => '', |
21 |
'Reset' => '', |
22 |
'Search' => '', |
23 |
'Slug' => '', |
24 |
'Status' => '', |
25 |
'Update' => '', |
26 |
'Update {modelClass}: ' => '', |
27 |
'Updated At' => '', |
28 |
'Vicinity' => '', |
29 |
'Website' => '', |
30 |
];
|
For at udfylde vores spanske oversættelser for denne tutorial, vil jeg bruge Google Translate. Tricky, huh?



Derefter, vi vil nogle klippe og indsætte med disse oversættelser tilbage til meddelelsesfilen.
1 |
return [ |
2 |
'Add Current Location' => 'Agregar ubicación actual', |
3 |
'Add a Google {modelClass}' => 'Añadir un Google {modelClass}', |
4 |
'Created By' => 'Creado por', |
5 |
'Full Address' => 'Dirección completa', |
6 |
'Google Place ID' => 'Google Place ID', |
7 |
'Name' => 'Nombre', |
8 |
'Notes' => 'Notas', |
9 |
'Place Type' => 'Place Tipo', |
10 |
'Places' => 'Lugares', |
11 |
'Slug' => 'Slug', |
12 |
'Vicinity' => 'Alrededores', |
13 |
'Website' => 'Sitio Web', |
14 |
'Are you sure you want to delete this item?' => '¿Estás seguro que quieres borrar este elemento?', |
15 |
'Create' => 'Crear', |
16 |
'Create {modelClass}' => 'Crear Lugar', |
17 |
'Created At' => 'Creado El', |
18 |
'Delete' => 'Eliminar', |
19 |
'ID' => 'ID', |
20 |
'Meeting Type' => 'Tipo de Reunión', |
21 |
'Meetings' => 'Encuentros', |
22 |
'Message' => 'Mensaje', |
23 |
'Owner ID' => 'Propietario ID', |
24 |
'Reset' => 'Reset', |
25 |
'Search' => 'Buscar', |
26 |
'Status' => 'Estado', |
27 |
'Update' => 'Actualizar', |
28 |
'Update {modelClass}: ' => 'Actualizar Lugar', |
29 |
'Updated At' => 'Actualización A', |
30 |
];
|
Når vi besøger sted indeksside, du vil se den spanske version-nice, huh?



Bemærk, at navigationslinjen er stadig på engelsk-det er fordi scriptet besked/ekstrakt ikke afhente Bootstrap navigation array definitioner og konvertere dem til ved hjælp af Yii:t()
. Vi vil gøre det i hånden. Bemærk også, at hjem og sidefil tekst blev oversat automatisk — Yii's codebase indeholder oversættelser for disse standard strenge.
Her er formen Opret et sted:



Hvis jeg ønsker at skifte tilbage til engelsk, ændre jeg bare konfigurationsfilen, /common/main.php
, tilbage til engelsk:
1 |
<?php
|
2 |
return [ |
3 |
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', |
4 |
'language' => 'en', // english |
5 |
// 'language' => 'es', // spanish
|



Du vil også bemærke, når du går videre, at erstatte strenge i JavaScript har sin egen kompleksitet. Jeg har ikke undersøgt det selv, men Yii 1.x JsTrans forlængelse kan give en nyttig rettesnor for at støtte dette.
Går videre med I18n
I sidste ende kan vi ønsker at omsætte vores program i et antal sprog. Jeg har sendt Yii funktionen om at udvide besked/ekstrakt scriptet for at bruge Google Oversæt API til at automatisere denne proces. Jeg har også bedt Tuts+ at lade mig skrive en tutorial om det, så stay tuned. Selvfølgelig, giver det bare en base oversættelse. Kan du hyre professionelle oversættere til at tune filerne bagefter.
Nogle programmer kan brugere at vælge deres eget sprog, så når de logger på, brugergrænsefladen automatisk oversætter for dem. I Yii betyder indstilling af $app->language
variabel dette:
1 |
\Yii::$app->language = 'es'; |
Andre applikationer, som JScrambler.com nedenfor, udnytte URL-sti for at skifte sprog. Brugeren klikker bare sprog præfiks de ønsker, fx "FR", og app er automatisk oversat:



Bemærk: Watch mine Tuts+ instruktør side for en kommende tutorial om JScrambler — det er en smuk nyttig service. Det kan allerede har optrådt ved den tid, du læser dette.
Yii's URL Manager kan give denne type af functionaliaty så godt. Jeg vil formentlig gennemføre disse funktioner for Mødeplanlægger i en fremtidig tutorial.
Hvad næste?
Jeg håber, du har lært noget nyt med denne tutorial. Jeg havde brugt I18n med Rails før — men det var første gang jeg havde gennemført det med PHP. Hold øje med kommende tutorials i vores bygning din opstart med PHP serie – der er masser af sjove funktioner kommer.
Behage beføle omkostningsfrit tilføje dine spørgsmål og kommentarer nedenfor; Jeg normalt deltage i drøftelserne. Du kan også kontakte mig på Twitter @reifman eller email mig direkte.