Integrace Symfony translation do Nette – Kdyby/Translation

před 6 lety

Tomáš Votruba
Moderator | 1154
+
0
-

// edited by @Filip Procházka:


Zdravím,
Kdyby/Translation je doplněk pro Nette, který do něj integruje Symfony/Translation, nástroj který umí používat formáty slovníků od gettextového po/mo až po Neon (vlastní implementace)

Samozřejmostí je podpora stable i dev Nette a také nejnovější verze translatoru (tedy 2.3@stable).

Rozšíření je klasicky na Githubu k vašim službám včetně dokumentace, nejlépe ho instalujte pomocí composeru. Chyby nebo nápady na vylepšení hlaste prosím na githubu

Instalace

Pokud chcete používat vývojové Nette, tak do composer.json dáme následující

"require": {
    "nette/nette": "@dev",
    "kdyby/translation": "@dev"
}

Pokud stable Nette

"require": {
    "nette/nette": "~2.0",
    "kdyby/translation": "~0.10"
}

(Pozn.: řada 0.* je pro ~2.0@stable Nette, řada 1.* je pro ~2.1@dev Nette)

Na co se těšit:


Disclaimer: Kdyby/Translation je plnohodnotný nástroj na multijazyčnost do Nette, ovšem neobsahuje proklínaný panel na překlad (a nikdy nebude, protože je to prasárna) a není to ani fork starého gettext translatoru.

Je to úplně nový nástroj, „pouhá“ integrace Symfony/Translation do Nette s cukrlátky.

Pokud jste přišli z gettext translatoru, přechod by vám mohla usnadnit jednoduchá integrace pomocí traity.

Editoval Tomáš Votruba (14. 11. 2013 15:42)

před 6 lety

bazo
Člen | 625
+
0
-

skoda, ze extrahuje message len zo sablon

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

@Schmutzka: Díky za založení stránky :)

@bazo: Protože nemám rád překládání přes funkce _() (kvůli nezbytnosti globálního stavu) a navíc plánuju něco lepšího :)

před 6 lety

Tomáš Votruba
Moderator | 1154
+
0
-

@Filip Procházka: Rádo se stalo :). Byla by škoda tu něco takového nemít. Chtěl jsem napsat uprav dle potřeby, ale taky už se stalo. Paráda.

před 6 lety

bazo
Člen | 625
+
0
-

@Filip: neviem co myslis funkciou _(), extractor co ja pouzivam extrahuje message z formularov, datagridov, flash messagov atd. inak standardne pouzivam $translator->translate(), odkial to tiez vytiahne.

Cize spravis extractory antique tym, ze spravis novy extractor? :D

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

Bude to trochu mocnejší než jenom extrakce stringů :) Každopádně, co to popisuješ bude umět taky :)

před 6 lety

Ivorius
Člen | 111
+
0
-

Možná by nebylo špatné popsat přechod z getexttranslatoru? Respektive jak do toho zakomponovat již vytvořené překlady.

před 6 lety

akadlec
Člen | 1322
+
0
-

Jeden možná hloupý dotaz. Na začátku vývoje sem si nahodil překladač s gettextem co byl v addonech. Funguje je mi fajn, resp zatím neřeším překlady ale jen mi to extrahuje texty co to má přeložit a hází do laděnky. Celá app je napsána v EN a překlady řeším takto:

$this->translator->translate('Warning! Something went wrong.');

či v šabloně:

<p>{_'Hello world'}</p>

Takže jako překladovou frázi mám celý text v EN abych se vyhnul problému kdy by se nenačetl konkrétní překlad pro zvolený jazyk tak je dle mého lepší to zobrazit v EN než nějaký string typu: message.home.hello

Ale jak jsem zjistil tak gettext plugin už asi umřel a dále se nebude vyvíjet a tak přemýšlím zda u něj zůstat nebo přejít na Kdyby. Jenže po zkouknutí kdyby jsem zjistil že to překlady řeší těmi řetězci.

Co je tedy lepší řešení?

před 6 lety

Tomáš Votruba
Moderator | 1154
+
0
-

@akadlec: Je to jen otázka zvyku – kvůli němu jsem zvažoval přechod pár týdnů. Výhodu v zástupném stringu vidím zejména pro překlady (překládající), nikoliv pro programovací návyky:

  • máš-li v šabloně string „Helo boys“, který přeložíš a pak opravíš na „Hello boys“, s gettextem můžeš překládat znovu
  • pokud potřebuješ někým přeložit aplikaci, vezmeš .neon (případně .txt), pošleš je překladateli a on ti vrátí přeloženou verzi × s gettextem musíš posílat .mo/.po soubory, které překladatel znát nemusí
  • potřebuješ-li 3 a více jazyků, je vhodnější mít různé jazykové verze k překladu (pl → hu, en → cs, de → ru) a ne žádnou základní × s gettextem je vždy jedna v šabloně a od té se odvíjí každá další (en → cs, en → pl, en → hu…)

Pokud máš aplikaci pouze v angličtině, nemá smysl přidávat překladač.

před 6 lety

akadlec
Člen | 1322
+
0
-

@Tomáš Votruba: Jasný je to zvyk.

  • Jo s tímhle jsem počítal, že pokud bych udělal změnu řetězce v šabloně tak bych to musel přegenerovat ve všech jazycích
  • Tohle omezení je mě jasné, tady jsem do budoucna počítal udělat překladateli rozhraní kde to uvidí v EN a bude dělat překlady přes rozhraní
  • No tady by asi byl základní překlad CS – zbytek, s tím že EN a CS zajištujě programátor s nutnou korekcí.

Appka je primárně EN a pak podle potřeby další jazyky CS, SK atd.

Jen mě teda příjde příjemnější číst šablony s reálným textem než s řetězci ;)

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

Translator by měl jít používat i přímo, bez domén (těch message ids s tečkami).

před 6 lety

Jan Tvrdík
Nette guru | 2559
+
0
-

@Filip Procházka: Dovolím si připomenout jeden zapomenutý feature request.

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

@Jan Tvrdík: díky za připomenutí, přidávám na hromádku :)

před 6 lety

Eda
Člen | 213
+
0
-

Zdarec.

Pořád se mi nějak nedaří tohle rozšíření rozjet tak, jak bych si představoval.

Co jsem tedy udělal.

  1. nainstalováno composerem, @dev verze
  2. do config.neon:
...
extensions:
    translation: Kdyby\Translation\DI\TranslationExtension

translation:
    default: sk
    fallback: [sk_SK, sk]
  1. V BasePresenteru persistentní $locale, v routě nastavena výchozí hodnota na sk.
  2. app/lang/messages.sk_SK.neon:
carRegisterTitleMaker:
    manufacturer: výrobca
  1. Ve službě, kde mám injectnutý translator pak zavolám:

$this->translator->translate('carRegisterTitleMaker.manufacturer')

Místo překladu se mi ale vypíše: „carRegisterTitleMaker.manufacturer“ a v panelu svítí chybějící překlad (jazyk ‚sk‘, to je rozpoznáno správně). Kde může být chyba?

Je zajímavé, že když změním v konfigu výchozí jazyk za češtinu:

translation:
    default: cs
    fallback: [cs_CZ, cs]

A přejmenuju soubor na messages.cs_CZ.neon, tak se najednou vše načte správně a skutečně se vypíše překlad (nezobrazí se ovšem v panelu chybějící překlad, což je taky trošku problém).


Ještě mám dotaz: od přečtení návodu jsem docela zmaten, jakých hodnot může nabývat locale/jazyk a z toho, jak se přesně chová fallback nastavovaný v konfigu.

Takže:

  1. Proč se mají soubory s překlady pojmenovávat ve stylu messages.cs_CZ.neon a nikoliv messages.cs.neon, když perzistentní proměnná $locale má vždy jen dvoupísmennou hodnotu?
  2. Jak rozšíření zjistí, zda má použít messages.en_GB.neon, nebo messages.en_US.neon, když má $locale hodnotu „en“?
  3. „Fallback“ chápu intuitivně tak, že vyjmenuju pořadí jazykových mutací, jejichž překlad se má použít, pokud nebude k dispozici překlad z nastaveného jazyka. Tedy třeba v případě webu sk/cz/en bych mohl nastavit fallback: [cs, sk, en]. Proč se ale v příkladu nastavení objevuje fallback: [cs_CZ, cs]?

Díky za případné odpovědi :-)

Editoval Eda (17. 11. 2013 20:06)

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

@Eda: Prozatím ti řeknu, že musíš doplnit whitelist o sk a já dokumentaci aby to bylo na očích :)

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**Filip Procházka**: Dalo by se nějak udělat, že jazyk nebude v routě? Rád bych měl překlady, ale nechci mít v url /en nebo /cz…

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

@martinit jasně, napiš si vlastní resolver a do routy to vůbec nedávej :) Ale budeš to muset mít někde v session (takovej by mohl být přímo v mé extension abys ho jen použil, že?).

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**Filip Procházka**: Určitě by to bylo super :) A jak to vypadá s překladem fomulářů a jejich error messages? Pokud vím, tak aktuálně to zvládá jen šablony, nebo se pletu?

před 6 lety

sKopheK
Člen | 207
+
0
-

Můžu se informovat, zda se vyplatí pár dní počkat na dokončení toho kulerváče, nebo si mám vytahat všechny překládané texty sám?

před 6 lety

sKopheK
Člen | 207
+
0
-

Konečně jsem pospojoval všechny dílky puzzle a rozjel jsem ten současný extractor. Pokud jste také Nette/Kdyby konzolí nepolíbení, přidám pár hintů:

  1. informace o Kdyby/Console: https://forum.nette.org/…dyby-console
  2. stáhnout do knihoven k Nette / do souboru composer.json přidat + updatnout
"require": {
    ...
    "kdyby/translation": "@dev",
    "kdyby/console": "2.0.*@dev"
},
  1. ideálně do config.local.neon
services:
    extractCommand:
        class: Kdyby\Translation\Console\ExtractCommand
        tags: [kdyby.console.command]

extensions:
    console: Kdyby\Console\DI\ConsoleExtension

console:
    url: http://www.kdyby.org
  1. pustit PHP CLI z kořenového adresáře aplikace
php www/index.php kdyby:translation-extract -f neon

Namísto neon můžete dosadit libovolný formát a stejně tak můžete upřesnit další parametry.

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**sKopheK**: Bohužel to nefunguje. Napíše mi to Catalogue was written to …, ale nic tam není.

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

@martinit: formuláře i errory to překládat umí, ale protože nette to má blbě udělané, tak to neumí překládat dodatečné parametry. Ale to je problém Nette, nikoliv translatoru. Něco s tím ještě zkusím vymyslet :)


@sKopheK: když zaregistruješ kdyby/console před translation tak se ten command zaregistruje sám a bude ti stačit

"require": {
    "nette/nette": "@dev",
    "kdyby/translation": "@dev",
    "kdyby/console": "@dev",
    ...
},
extensions:
    console: Kdyby\Console\DI\ConsoleExtension
    translation: Kdyby\Translation\DI\TranslationExtension

Dyštak jsou tam i parametry na jazyk katalogu a složku kam má zapisovat

$ php www/index.php help kdyby:translation-extract

Ale bohužel, zatím umí jenom latte, php soubory teprve budou.

před 6 lety

sKopheK
Člen | 207
+
0
-

@Filip Procházka: Konzoli pro produkční prostředí nepotřebuji, tak ji mám jen v lokálním configu. Nějaký primitivní extractor z PHP souborů jsem si vyrobil, něco málo času to snad ušetří. Jsem zvědavý jak moc robustní bude oficiální extractor z kódu PHP.

@martinit: Zkontroloval jsem, zda jsem na nějaký krok nezapomněl, ale dělal jsem to přesně tak, jak jsem popsal výše. Používám Nette 2.1dev; v dřívější verzi je postup registrace služeb trošku jinačí – zmiňuje to manuál k Kdyby/Console. Při přidání svého extractoru jsem musel samozřejmě smazat cache od celé Nette aplikace, možná to pomůže i tobě.

před 6 lety

sKopheK
Člen | 207
+
0
-

martinit napsal(a):

@**Filip Procházka**: Dalo by se nějak udělat, že jazyk nebude v routě? Rád bych měl překlady, ale nechci mít v url /en nebo /cz…

Když si před routy dáš např. [<locale=cs [a-z]{2}>/] (v kombinaci s @persistent public $locale v presenteru), tak budeš mít pro translator nastavenou češtinu a v URL se to neobjeví.

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**sKopheK**: To vše jsem zkoušel, ale nepreparuje to :/
To ohledně url vím, ale funguje to jen pro defaultní jazyk.

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

sKopheK napsal(a):

@Filip Procházka: Konzoli pro produkční prostředí nepotřebuji, tak ji mám jen v lokálním configu.

To že máš registrovaný extension (a ještě přes config, což to ještě více zefektivňuje) nemáš šanci na produkci naměřit.

před 6 lety

sKopheK
Člen | 207
+
0
-

Narazil jsem na drobnou chybku:

setTranslator(NULL) u formulářového prvku se nezohlední a snaží se text chyby u pravidla addRule() přeložit – v Nette panelu se pak zobrazuje jako nepřeložený text. U textu labelu problém není.

$form->addSelect('id_game', $this->translator->translate("event.phase.form.game"), $games)
    ->setTranslator(NULL)
    ->addRule(Form::RANGE, $this->translator->translate("event.phase.form.gameError"), array(1, NULL))
    ->getControlPrototype()
        ->class('form-control');

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

S tím asi moc neudělám, ale můžeš zkusit poslat pull request do Nette :)

před 6 lety

Felix
Nette Core | 937
+
0
-

Upozornuju, ze jsem jeste nepouzil, jenom premyslim jak bych to pouzival.

Jak se vyhnout dopredu tomu, ze nektere vety budou na vice strankach a ja na to narazim az potom co si budu projizdet neon, treba hlaska „Odeslani se nezdarilo ..“ apod.

To potom, abych si dopredu zalozil namespace common.messages.error napriklad – ale nevim jestli budu dopredu znat vsechny.

Kdyz si predstavim, ze mam velkou aplikaci s hodne textama a mailama. Tak si nejsem jistej, jestli obsahnu vsechny zastupny texty. Napriklad mails.newuser.paragraph1 .. paragraph2.. paragraph15.

Takhle mi zatim v sablone staci jenom texty hloupe obalovat do {_".."} a vysledek uvidim az po projeti sablon.

Urcite to ma sve pro i proti, spis si rikam kdy dostanu ten impuls abych presel :-)

před 6 lety

sKopheK
Člen | 207
+
0
-

@Felix: Když použiješ nějaký extractor, tak dostaneš řetězce, které překládáš, a jak je po jednom nahrazuješ v kódu, tak hledáš, ve kterých souborech je ten řetězec použitý. Podle toho získáš představu, k čemu všemu se ten řetězec váže a můžeš si ho zařadit.

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

Naopak bych řekl, že duplicity vůbec nevadí, dokonce jsou dobře. Občas chceš použít stejný výraz ale v různých kontextech a v jiném jazyku proto třeba na různých místech bude vhodnější výraz, nebo fráze.

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**Filip Procházka**: Koukal jsem, že už jsi přidal session, ale jak to funguje? Zkoušel jsem to dát podle testů dohromady a nefunguje to:

bd($this->translator->getLocale()); // cs
bd($this->translatorSession->resolve($this->translator)); // NULL
$this->translatorSession->setLocale('en');
bd($this->translator->getLocale()); // cs
bd($this->translatorSession->resolve($this->translator)); // NULL

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

Tak tohle nevím kde jsi našel, ale rozhodně to nedáváš dohromady podle testů ;) Metodu resolve si totiž volá translator sám (na to v testech nikde nesahám a ty bys taky neměl).

// začne request, vezme se locale z hlaviček prohlížeče
bd($this->translator->getLocale()); // cs

// v signálu presenteru (protože uživatel klik na změnu jazyka) zavoláš
$this->translatorSession->setLocale('en');
// a tím se locale uloží, ovšem nezmění se locale translatoru

// pokud chceš to nové locale použít ještě v tomto requestu tak musíš to současné resetovat
$this->translator->setLocale(NULL);

// teď už to vrátí locale z SessionResolveru
bd($this->translator->getLocale()); // en

// další request už bude automaticky obsahovat to locale které jsi nastavil

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**Filip Procházka**: Díky za pomoc, ale stejnak je to divné. Nikdy se to nezmění na angličtinu.

public function handleLag()
{
    $this->translatorSession->setLocale('en');
    //$this->translator->setLocale(NULL);
    $this->redirect('this');
}

public function handleLag2()
{
    $this->translatorSession->setLocale('cs');
    //$this->translator->setLocale(NULL);
    $this->redirect('this');
}

Vždy je jazyk v cs. Ve fallback (které mi přijde jako zbytečne) mám fallback: [cs_CZ, cs, en, en_US, en_GB]

před 6 lety

Filip Procházka
Moderator | 4693
+
0
-

A co máš v translatorSession, předpokládám správně že SessionResolver? Možná bych to spíš udělal jako

public function handleChangeLocale($locale)
{
    $this->translatorSession->setLocale($locale);
    $this->redirect('this');
}
<a n:href="changeLocale!, cs">cs</a>
<a n:href="changeLocale!, en">en</a>

Každopádně, přesně tohle by mělo fungovat. Pořádně to otestuju a dám vědět :)

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**Filip Procházka**: Přesně tak to mám, použil jsem i tvůj příklad a nepomůže to. Budu rád, když na to koukneš.

před 6 lety

Filip Procházka
Moderator | 4693
+
+1
-

Vytvořím projekt

$ composer create-project hosiplan/project translator-sessioned
$ cd translator-sessioned/
$ composer require kdyby/translation:@dev

přepíšu app/templates/Homepage/default.latte

{_front.homepage.hello}

<a n:href="changeLocale!, cs">cs</a>
<a n:href="changeLocale!, en">en</a>

přepíšu app/presenters/HomepagePresenter.php

class HomepagePresenter extends BasePresenter
{
    /**
     * @var \Kdyby\Translation\Translator
     * @autowire
     */
    protected $translator;

    /**
     * @var \Kdyby\Translation\LocaleResolver\SessionResolver
     * @autowire
     */
    protected $translatorSession;



    public function handleChangeLocale($locale)
    {
        $this->translatorSession->setLocale($locale);
        $this->redirect('this');
    }



    protected function createTemplate($class = NULL)
    {
        /** @var Nette\Templating\FileTemplate|\stdClass $template */
        $template = parent::createTemplate($class);
        $template->registerHelperLoader([$this->translator->createTemplateHelpers(), 'loader']);
        return $template;
    }

}

A funguju…

První načtení:

Kliknu na en:

Kliknu na cs a zase vidím to první.

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**Filip Procházka**: Hmmm díky. Jdu hledat kde je problém, ale … Když kliknu na en, tak debugbar previous ukazuje správně en, ale ten akutuální zase ukazuje cs.

před 6 lety

enumag
Člen | 2128
+
0
-

@martinit: Zřejmě nastal nějaký problém při ukládání session. To se stává typicky v případě kdy do session dáváš objekty, které nelze serializovat.

před 6 lety

NiNu
Člen | 31
+
0
-

Zdravím,
skúšam túto komponentu a podľa návodu sa mi to podarilo bez problémov rozbehať, aspoň čo sa týka prekladu formulárov a template-u. Chcel by som sa ale spýtať či sa cez tento translator dajú prekladať aj texty ťahané z DB, napr. položky v menu a ich stránky, alebo články z DB či novinky, alebo sa to rieši nejak inak?
Ďakujem

před 6 lety

akadlec
Člen | 1322
+
0
-

@NiNu: tak můžeš když si to tím tranlsatorem proženeš ale IMHO je to blbost. Když už to máš v DB tak se předpokládá že se jedná o dynamický text takže bych to do db ukladat v jazykovych verzich jaké potřebuješ.

před 6 lety

NiNu
Člen | 31
+
0
-

@akadlec: ok, ďakujem a aký je teda najlepší spôsob ukladať tú jazykovú verziu do db? Ako sa to bežne robí? Vytvoriť novú tabuľku s jazykovou mutáciou? Alebo nejak inak?

před 6 lety

akadlec
Člen | 1322
+
0
-

@NiNu: ano třeba takto. Vytvořís si lokalizační tabulku kde to budeš vázat na konkrétní záznamy a jazyk.

před 6 lety

craz
Člen | 39
+
0
-

@NiNu: odporúčam sa pozrieť tu, sú tam pekne rozpísané najpoužívanejšie prístupy k prekladom v databáze.

před 6 lety

NiNu
Člen | 31
+
0
-

@craz: Diky moc, presne takéto niečo som hľadal. Bez toho aby som toto videl ma intuitívne napadla práve tá 4 možnosť (4. Additional Translation Table Approach), len som si nevedel celkom predstaviť ako na to. Teraz mi je to už celkom jasné a pustím sa do toho.

Ešte by ma zaujímal extractor, videl som, že kdyby/translation má nejaký, len by som chcel vidieť nejaký príklad ako to použiť.

před 6 lety

akadlec
Člen | 1322
+
0
-

Tak v okamžiku kdy ukládáš překlady do DB snad extraktor a vubec translation extension nepotřebuješ ne? Překlady si řídíš sám nějakým edit formem konkrétní položky

před 6 lety

NiNu
Člen | 31
+
0
-

akadlec napsal(a):

Tak v okamžiku kdy ukládáš překlady do DB snad extraktor a vubec translation extension nepotřebuješ ne? Překlady si řídíš sám nějakým edit formem konkrétní položky

ale hej, máš pravdu, použiť sa to nechystám, len ma to zaujíma, že ako to funguje po praktickej stránke, nejaký príklad keby som to do budúcna potreboval …

před 6 lety

akadlec
Člen | 1322
+
0
-

Tak jednoduše třeba v editačním formu budeš mít 3 políčka pro „name“ v jednotlivých jazycích co chceš používat, třeba EN, CZ, SK a při ukládání si to do těch tabulek rozhodíš.

před 6 lety

NiNu
Člen | 31
+
0
-

akadlec napsal(a):

Tak jednoduše třeba v editačním formu budeš mít 3 políčka pro „name“ v jednotlivých jazycích co chceš používat, třeba EN, CZ, SK a při ukládání si to do těch tabulek rozhodíš.

Ale nee, je mi jasné, že si rozšírim formuláre o príslušné preklady, ja som mal na mysli skôr ten extraktor, že ako funguje (nejaký malý príklad) …

před 6 lety

MartinitCZ
Člen | 590
+
0
-

@**enumag**: Po dleší době bych se k tomu rád vrátil. Jak toto zjistim? Do session totiž v tomto projektu nikde nic neukládám.

Editoval martinit (10. 12. 2013 20:38)

Stránky: 1 2 3 Next RSS tématu