Symfony po krůčkách – Translation – překlady jednoduše

V tomto díle si ukážeme, jak použít pro překlady textů Symfony komponentu Translation. Ta umožňuje pracovat s překlady uloženými v různých formátech. To může usnadnit výměnu textů s překladatelem nebo externím API. Hodí se ale i k zobrazení různých textů v závislosti na počtu (1 jablko, 5 jablek).
Seriál: Symfony po krůčkách (18 dílů)
- Symfony po krůčkách – Event Dispatcher 30. 11. 2015
- Symfony Console jako první rande se Symfony 7. 12. 2015
- Symfony po krůčkách – Filesystem a Finder 14. 12. 2015
- Symfony po krůčkách – Paralýza možností? OptionsResolver tě zachrání 21. 12. 2015
- Symfony po krůčkách – spouštíme procesy 4. 1. 2016
- Symfony po krůčkách – Translation – překlady jednoduše 11. 1. 2016
- Symfony po krůčkách – Validator (1) 18. 1. 2016
- Symfony po krůčkách – Validator (2) 25. 1. 2016
- Symfony po krůčkách – Routing 1. 2. 2016
- Symfony po krůčkách – MicroKernel 9. 2. 2016
- Konfigurujeme Symfony pomocí YAMLu 16. 2. 2016
- Symfony po krůčkách – oblékáme MicroKernel 23. 2. 2016
- Symfony po krůčkách – ClassLoader 29. 2. 2016
- Symfony po krůčkách – Twig 8. 3. 2016
- Symfony po krůčkách – Twig II. 15. 3. 2016
- Symfony po krůčkách – DomCrawler a CssSelector 23. 3. 2016
- Symfony po krůčkách – HTTP fundamentalista 12. 4. 2016
- Symfony po krůčkách – ušli jsme pořádný kus 19. 4. 2016
Příklad na začátek
Celý princip práce s překladačem je velmi jednoduchý. Pro každý jazyk, se kterým chceme pracovat, naplníme Translator našimi překlady – to je jen seznam klíčů a jejich hodnot. Klíč určuje řetězec, ke kterému hledáme překlad, hodnota jeho překlad. Odpovídající překlady pro různé jazyky jsou uloženy pod stejným klíčem. Pak už se jen Translatoru ptáme na překlady pro konkrétní klíč a jazyk.
Komponentu nainstalujeme jednoduše přes Composer:
composer require symfony/translation
A použijeme v jednoduchém skriptu:
<?php
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Translator;
require_once __DIR__. '/vendor/autoload.php';
$translator = new Translator('cs_CZ');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [
'Hello world!' => 'Ahoj světe!',
], 'cs_CZ');
echo $translator->trans('Hello world!');
Po spuštění se vypíše Ahoj světe!
. Rozebereme si, co se v ukázce děje:
Translatoru určíme jazyk
Nejdříve vytváříme objekt třídy Translator
. Tomu musíme předat v parametru locale určující výchozí jazyk, do kterého chceme překládat.
Doporučený formát locale je dvoupísmený kod jazyka podle ISO 639-1 následovaný podtržítkem a dvoupísmenným kódem státu podle ISO 3166-1.
Nastavíme loader
V dalším kroku se přidává instance třídy Loaderu. Loader umí načítat zdroje překladů v určitém formátu, tady pro "array"
.
Přidáme zdroje
Metoda addResource()
přidává zdroj překladů. V tomto případě je to pole s jedním prvkem. Posledním argumentem říkáme, že texty jsou v češtině – v jednom zdroji jsou překlady vždy jen pro jeden jazyk.
Překládáme
Teď už můžeme na překladači zavolat metodu trans()
, které předáme překládaný řetězec a dostaneme zpět jeho překlad. Pokud by se stalo, že by překlad neexistoval, vrátí se řetězec předávaný v argumentu.
Locale můžeme předat i metodě trans()
jako čtvrtý argument a vynutit si tak překlad do jiného jazyka:
$translator->trans('Hello world!', [], null, 'en_EN');
Překládat původní text nebo zástupné klíče?
V úvodním příkladu vytváříme překladový slovník, kde klíčem překladu je přímo původní text v angličtině ('Hello world!'
) a ten se pak předává i metodě trans()
.
Druhá možnost je místo původního textu použít zástupný klíč, který by měl vyjadřovat obsah překladu. Zdrojový jazyk, v tomto případě angličtinu, je potřeba pak přidat jako další jazyk.
$translator->addResource('array', [
'hello.world' => 'Ahoj světe!',
], 'cs_CZ');
$translator->addResource('array', [
'hello.world' => 'Hello world!',
], 'en_EN');
echo $translator->trans('hello.world');
// “Ahoj světe!”
Použití klíčů má několik výhod:
- Když se mírně změní překládaný zdrojový text, jeho klíč zůstane stejný i ve všech dalších překladech.
- U dlouhých textů může být klíč také kratší.
- Pokud budou překlady uložené v YAMLu, je možné je zanořovat. (Ukážeme si dále.)
Častěji bývá doporučován a používán právě způsob se zástupnými klíči. Volba je ale jen na vás.
Proměnné v překladech
Někdy je do překládaných textů potřeba vložit hodnotu dynamicky. K tomu účelu se do překládaného řetězce vkládají placeholdery a metodě trans()
se předává pole náhrad:
$name = 'Zdroják';
echo $translator->trans(
'Hello %name%',
['%name%' => $name]
);
// "Hello Zdroják"
Doporučuje se placeholder ohraničit znaky procenta, může mít ale libovolný formát.
Další způsoby načítání překladů
V příkladu jsme předali texty přímo v poli a k jejich zpracování použili ArrayLoader
. Často jsou ale překlady připravovány profesionálními překladateli, kterým chceme dát seznam překladů v jiném formátu. Přímo s komponentou máte na výběr z dalších loaderů – CSV, Xliff, Php, Yaml, Po… Celý seznam naleznete v dokumentaci.
Oblíbeným formátem pro uchovávání překladů bývá YAML. Ten aktivujeme pomocí:
composer require symfony/yaml
YAML umožňuje zanořovat klíče:
#messages.cs.yml
greetings:
hello: 'Ahoj'
good:
morning: 'Dobré ráno'
afternoon: 'Dobré odpoledne'
evening: 'Dobrý večer'
Použít překlady z YAML souboru pak můžeme takto:
use \Symfony\Component\Translation\Loader\YamlFileLoader;
$translator->addLoader('yaml', new YamlFileLoader());
$translator->addResource('yaml', 'messages.cs.yml', 'cs_CZ')
echo $translator->trans('greetings.good.morning')
// "Dobré ráno"
Domény – dělení do skupin
Překladové soubory jsou organizovány podle jednotlivých locales, které překládají. Další členění může být na “domény”. Můžeme tak oddělit překlady validačních hlášek, chyb nebo třeba administračního rozhraní do samostatných souborů.
Doména se předává jako 4. argument metodě addResource()
. Výchozí hodnota je 'messages'
.
$translator->addResource('yaml', 'messages.cs.yml', 'cs_CZ')
$translator->addResource('yaml', 'admin.cs.yml', 'cs_CZ', 'admin')
Překlad s použitím domény pak vypadá takto:
$translator->trans('Přidat nový záznam', [], 'admin');
Fallback překlady
Pokud není klíč překladu nalezen v seznamu překladů pro daný jazyk, lze určit pořadí dalších jazyků, které se mají brát jako náhrada.
Pokud chceme překlad pro 'cs_CZ'
locale, hledá se v tomto pořadí:
- Překlad pro
'cs_CZ'
locale - Pokud není nalezen, hledá se v
'cs'
locale - Pokud není stále nalezen, hledá se ve fallback locale, které musí být explicitně nastaveno a může jich být více:
$translator->setFallbackLocales(['en']);
1 jablko, 5 jablek
V češtině jsou různé tvary podstatného jména pro “jeden”, “dva až čtyři” a “žádný nebo pět a více”. V angličtině jsou různé tvary jen dva – pro “jeden” a “žádný nebo více než jeden”. V jiných jazycích mohou být pravidla i složitější. Translation komponenta má podporu pro výběr vhodného tvaru ze zadaných možností.
V překladech je možné zadat více tvarů, oddělených znakem |
:
‘You have 1 mail|You have %count% mails’
Místo metody trans()
se pak používá metoda transChoice()
:
echo $translator->transChoice(
‘You have 1 mail|You have %count% mails’,
10,
['%count%' => 10]
);
// “You have 10 mails”
Druhý argument v příkladu výše je počet, podle kterého se zvolí vhodný překlad. Počet se v překladu nahrazuje přes placeholder "%count%"
.
Každé locale má vlastní pravidla, podle kterých se vybírá vhodná forma. Pro češtinu může překladový řetězec vypadat takto:
echo $translator->transChoice(
'Máte 1 zprávu|Máte %count% zprávy|Máte %count% zpráv',
10,
['%count%' => 10],
null,
'cs'
);
// “Máte 10 zpráv”
Profi intervaly
Někdy můžeme chtít vybrat různý text pro konkrétní počet, například pro “0”. Pro takové případy je možné uvést přímo intervaly čísel, pro které se má zvolit která zpráva:
'{0} Nemáte žádnou zprávu|{1} Máte jednu zprávu|]1,19] Máte několik zpráv|[20,Inf[ Máte moc zpráv'
Přístupy lze i kombinovat. V případě, že pro počet nelze vybrat konkrétní interval, berou se standardní pravidla po odstranění intervalových:
'{0} Nemáte žádnou zprávu|[1000,Inf[ Máte hodně moc zpráv|Máte 1 zprávu|Máte %count% zprávy|Máte %count% zpráv'
Interval může být konečný seznam čísel:
{1,2,3,4}
Nebo interval mezi dvěma čísly:
[1, +Inf[
]-1,2[
Jako v matematice může být interval zleva uzavřený [
nebo otevřený ]
, a zprava uzavřený ]
nebo otevřený [
. Kromě čísel jde použít i -Inf
a +Inf
pro nekonečno.
Zase o krok dále
Dnes jsme si ukázali komponetu, která nám pomůže:
- překládát pro různé jazyky
- načítat různé typy překladových souborů
- počítá s množnými čísly
- jak využít intervaly pro hlášku na míru
Kam jít dál?
Víc si o Translation komponentě můžete přečist v oficiální dokumentaci.
Do Nette existuje integrace komponenty v Kdyby/Translation od Filipa Procházky. Ve full-stack Symfony frameworku jsou překlady integrované a rozšířené ještě o další nástroje. To si ukážeme v některém z dalších dílů.
Už v Symfony děláš léta a chceš posdílet zkušenosti?
Nebo tě Symfony zatím jen láká a rád by se o něm dozvěděl více? Přijd si pokecat na pravidelné měsíční srazy v Praze, Brně a Ostravě.
Pěkný článek. Jen bych pro fallbacks doplnil:
V kontrukci
$translator = new Translator('cs_CZ');
vidíme hodnotucs_CZ
, kterou můžeme získávat např. z requestu. V takovém případě bude translátoru často namísto hodnotycs_CZ
předána hodnotacs
. A teď důležitá věc! Postupné hledání jazykůcs_CZ
a následněcs
platí pouze pro překládáný jazyk, nikoliv pro jazyk registrovaných překladů. Jinak řečeno, registrované překlady pro jazykcs_CZ
jsou pouze pro jazykcs_CZ
a nikoliv pro jazykcs
. Nejlépe to bude vidět na příkladě.Jak je na příkladě vidět, pro jazyk
en
překlad neexistuje.Proto je vhodné si pro každý registrovaný jazyk určit výchozí „mutaci“.
Ukážeme si to opět nejlépe na příkladě.
Pěkný článek, díky za něj. Jen mi tam trošku chybí dvě věci:
app/console translation:update
) a při té příležitosti zmínit jms/translation-bundle, který umí extrahovat nejen ze šablon, ale i z controllerů a formulářů.Např.
„Dnes je %date% a svátek má %name%“ => today.is.DATE.nameday.has.NAME
případně pokud jsou klíče založené na struktuře webu:
welcome.nameday.info.DATE-NAME (na welcome stránce přelož informaci o svátku; v překladu můžete použít proměnné %date% a %name%)