NetteTranslator (GettextTranslator & Nette Translation Panel)
- Patrik Votoček
- Člen | 2221
NetteTranslator
Jedná se v zásadě o sloučení Nette Translation Panel s GettextTranslator .
Nette Translation Panelu bylo vychytáno pár mušek a nastavení. A GettextTranslator byl kompletně přepsán (kvůli potřebě ukládání). Inspirací mě bylo: https://gist.github.com/432842
Co to umí?
Jak už jsem psal víš jedná se o sloučení dvou věcí v jednu. Tou prví a důležitější je Translator*. V tomto případě se jedná o Gettext translátor, který ovšem nevyužívá nativní implementaci přímo v PHP. A to z několika důvodů:
- V nativní implementaci jsou překlady udržovány v cache a pokud
aktualizujete
.mo
soubor musíte restartovat server - Nativní implementace používá nehezkou strukturu složek
- Nativní implementace neumožňuje ukládání
Pokud tento translátor nenalezne překlad řetězce, který chcete
přeložit. Uloží si tuto informaci. Zde na řadu přichází Panel,
který si od Translatoru načte přeložené a nepřeložené řetězce
a umožní vám jejich překlad (živě přímo v aplikaci).
Doplníte překlady případně opravíte vadné. Kliknete na uložit a
translator AJAXově uloží změny (vygeneruje .mo
a
.po
soubory), kterými přepíše stávající.
(do .po
souboru uloží i nepřeložené řetězce)
Použití
Registrace Translatoru se provádí v config.ini
a to
pomocí:
variable.langDir = %appDir%/lang ; složka s překlady
variable.lang = cs_CZ ; výchozí jazyk
; a
service.Nette-ITranslator.factory = "NetteTranslator\\Gettext::getTranslator" ; nastavení
service.Nette-ITranslator.run = TRUE ; samotné zapnutí
Použití v PHP
$translator = Nette\Environment::getService('Nette\ITranslator');
$translator->translate("This is translation text"); //jednoduchý překlad textu
$translator->translate("This %s translation %s", 'is', 'text'); //překlad s nahrazením
$translator->translate("This is translation text", array("This is translation texts", 6)); //překlad pluralu (první je singularova forma, pluralova forma, a cislo ze ktereho se vychází)
$translator->translate("This is %s text", array("This is %s texts", 6), 'translation'); //překlad pluralu s nahrazením
//Nicméně doporučuji používat zkratky
__("This is translation text"); //jednoduchý překlad textu
_x("This %s translation %s", array('is', 'text')); //překlad s nahrazením
_n("This is translation text", "This is translation texts", 6); //překlad pluralu (první je singularova forma, pluralova forma, a cislo ze ktereho se vychází)
_nx("This is %s text", "This is %s texts", 6, array('translation')); //překlad pluralu s nahrazením
Použití v šablonách
Nejdříve musíme Translator zaregistrovat k šablonám.
class BasePresenter extends \Nette\Application\Presenter
{
protected function createTemplate()
{
$template = parent::createTemplate();
$template->setTranslator(\Nette\Environment::getService('Nette\ITranslator'));
return $template;
}
}
A pak už stačí používat.
{* jednoduchý překlad textu *}
{_"This is translation text"}
{* překlad s nahrazením *}
{_"This %s translation %s", 'is', 'text'}
{* překlad pluralu *}
{_"This is translation text", array("This is translation texts", 6)}
{* překlad pluralu s nahrazením *}
{_"This is %s text", array("This is %s texts", 6), 'translation'}
Použití s formuláři
U formulářů nehledejte žádné složitosti stačí zaregistrovat.
use Nette\Forms\Form;
protected function createComponentLoginForm($name)
{
$form = new \Nette\Application\AppForm($this, $name);
$form->setTranslator(\Nette\Environment::getService('Nette\ITranslator'));
$form->addText('username', 'Username:')
->addRule(Form::FILLED, 'Please provide a username.');
$form->addPassword('password', 'Password:')
->addRule(Form::FILLED, 'Please provide a password.');
$form->addCheckbox('remember', 'Remember me on this computer');
$form->addSubmit('login', 'Login');
$form->onSubmit[] = callback($this, 'loginFormSubmitted');
}
Změna jazyka
class FooPresenter extends BasePresenter
{
/**
* @var string
* @persistent string
*/
public $lang = "en";
protected function startUp()
{
\Nette\Environment::getService('Nette\ITranslator')->lang = $this->lang;
}
}
Pár informací na závěr
- Dávejte si pozor na nativní
_()
funkci gettextu (jedno podtržítková) - Změna jazyka se aplikuje pouze pokud pře změnou nebylo voláno
->translate()
,__()
,_n()
,_x()
nebo_nx()
- Nehledá to nepřeložené řetězce (pouze zaznamenává neúspěšné pokusy o překlad)
- Do addons jsem to nedal protože
- to zatím není otestované
- to vychází z addonů dalších 2(3) lídí se kterými jsem zatím neměl možnost o tom podiskutovat
- najde te to tady: https://github.com/…teTranslator
- u základních řetězců (ty v kódu) je podporován pouze jazyk který má jenom 2 formy plurálů (singular, plural – třeba angličtina)
- při programování jsem zjistil že navrhnuté rozhraní
Nette\ITranslator
je strašně špatně vymyšlené (narážím na podporu plurálů)
- norbe
- Backer | 405
Možná by nebylo na škodu to propojit ještě s GettextExtractorem, případně nějak jinak umožnit dolpnění nepřeložených textů, jelikož se některé situace dost špatně vyvolávají (např. informace o úspěšném odeslání emailu na localhostu) a nemuselo by tedy dojít k překladu všech řetězců.
- iguana007
- Člen | 970
norbe napsal(a):
Možná by nebylo na škodu to propojit ještě s GettextExtractorem, případně nějak jinak umožnit dolpnění nepřeložených textů, jelikož se některé situace dost špatně vyvolávají (např. informace o úspěšném odeslání emailu na localhostu) a nemuselo by tedy dojít k překladu všech řetězců.
+1
- norbe
- Backer | 405
Ještě bych měl pár poznámek/dotazů k nativní podpoře gettextu
vrtak-cz napsal(a):
- V nativní implementaci jsou překlady udržovány v cache a pokud aktualizujete
.mo
soubor musíte restartovat server
Tohle by šlo vyřešit právě díky tomu, že soubor je generován „za chodu“ (název by mohl obsahoval čas poslední změny PO souboru)
- Nativní implementace používá nehezkou strukturu složek
Tohle mi nepřijde jako nevýhoda, obzvlášť když s tím díky panelu nepřijdu vůbec do styku…
- Nativní implementace neumožňuje ukládání
Co si mám pod tímhle představit? Mo soubor vygenerováný pomocí panelu není GNU Gettext schopen zpracovat?
- despiq
- Člen | 320
myslim ze kdyz gettextu podstrcis jednou soubor .mo tak si ho proste ulozi a i kdyz ho zmenis tak tim nic do restartu gettextu nezmenis, gettext taky myslim urcuje i nazev souboru s prekladem na messages.mo, mozna to jde zmenit ale bude to podle me pokazde nazev stejny do restartu
ukladanim mel vrtak myslim ukladani neprelozenych retezcu a to je podle meho prave killer feature protoze uz vubec neni potreba prave gettextextractor, staci aplikaci/web projet a smele prekladat
zajimala me ale jedna vec, kdyz je translator reglej jako service, cachuje se nebo si ho musim cachnout sam?
a s tema pluralama, me s tim lehce upravenym gettexttranslatorem cesky
pluraly fungujou, osobne sem mel s tema pluralama problem jiny, vsude bylo
napsano ze nastaveni pluralu ma byt
nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4 ? 1 : 2);
to mi ale proste nefungovalo a to nasledujici pak ano
nplurals=3; plural=(n==1) ? 0 : ((n>=2 && n<=4) ? 1 : 2);
Editoval despiq (15. 7. 2010 0:37)
- Patrik Votoček
- Člen | 2221
norbe napsal(a):
- V nativní implementaci jsou překlady udržovány v cache a pokud aktualizujete
.mo
soubor musíte restartovat serverTohle by šlo vyřešit právě díky tomu, že soubor je generován „za chodu“ (název by mohl obsahoval čas poslední změny PO souboru)
Tím by jsi ale taky mohl zahltit server (měl by v cache uloženy i ty starší soubory. Navíc by jsi musel dozjišťovávat jak se vlatně ten tvůj soubor aktuálně jmenuje. (celkově mě tento návrh nepřipadá vůbec čistý.
- Nativní implementace používá nehezkou strukturu složek
Tohle mi nepřijde jako nevýhoda, obzvlášť když s tím díky panelu nepřijdu vůbec do styku…
Do styku s tím příjdeš musíš vygenerovat „prázdný“
.MO
soubor (kvuli hlavičkám – forma plurálů, název
překladu, překladatel…). Navíc do takové složky musíš nastavit práva
zápisu. Nevím jak ty ale já mám ve složkách aplikace kterou tvořím rád
pořádek. A ./app/lang/cs/LC_MESSAGES/default.mo
mě připadá
jako zhůvěřilost.
- Nativní implementace neumožňuje ukládání
Co si mám pod tímhle představit? Mo soubor vygenerováný pomocí panelu není GNU Gettext schopen zpracovat?
.mo
i .po
soubor vygenerovaný pomocí panelu je
GNU Gettext schopen zpracovat. Nicméně nativní implementace Gettextu v PHP
ti neumožní jakkoli manipulovat s .mo
souborem umí ho pouze
číst. Takže pokud by jsi pomocí panelu změnil/přidal tak už to prostě
neuložíš. Ano mohl bych použít to mnou udělané ukládání a na zbytek
použít nativní gettext. Ale nativní implementace neumožňuje získat seznam
všech překladů které se aktuálně v .mo
soubouru
nacházejí.
despiq napsal(a):
ukladanim mel vrtak myslim ukladani neprelozenych retezcu a to je podle meho prave killer feature protoze uz vubec neni potreba prave gettextextractor, staci aplikaci/web projet a smele prekladat
Tohle není tak úplně pravda protože všude se nedoklikáš (různé chybové stavy aplikace)
zajimala me ale jedna vec, kdyz je translator reglej jako service, cachuje se nebo si ho musim cachnout sam?
Cache se zapíná tak to:
NetteTranslator\Gettext::$cache = NetteTranslator\Gettext::CACHE_ENABLE
nicméně se zapnutou cache je to pomalejší neš s vypnutou (zatím jsem
nezkoumal proč)
a s tema pluralama, me s tim lehce upravenym gettexttranslatorem cesky pluraly fungujou, osobne sem mel s tema pluralama problem jiny, vsude bylo napsano ze nastaveni pluralu ma byt
nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4 ? 1 : 2);
to mi ale proste nefungovalo a to nasledujici pak ano
nplurals=3; plural=(n==1) ? 0 : ((n>=2 && n<=4) ? 1 : 2);
mě ta první funguje OK…
- iguana007
- Člen | 970
despiq napsal(a):
myslim ze kdyz gettextu podstrcis jednou soubor .mo tak si ho proste ulozi a i kdyz ho zmenis tak tim nic do restartu gettextu nezmenis, gettext taky myslim urcuje i nazev souboru s prekladem na messages.mo, mozna to jde zmenit ale bude to podle me pokazde nazev stejny do restartu
S tímto musím bouhžel nesouhlasit, nedávno jsem dělal web, který gettext využívá a jakmile nahraju nový .mo soubor na server, tak se překlady objeví okamžitě. Netuším co je „jinak“ na daném serveru nastaveno, ale už jsem o tomto jevu dříve četl. Takže to imho nějak pořešit jde. Hosting výše zmíněného webu je u společnosti Media Factory.
- despiq
- Člen | 320
S tímto musím bouhžel nesouhlasit, nedávno jsem dělal web, který gettext využívá a jakmile nahraju nový .mo soubor na server, tak se překlady objeví okamžitě. Netuším co je „jinak“ na daném serveru nastaveno, ale už jsem o tomto jevu dříve četl. Takže to imho nějak pořešit jde. Hosting výše zmíněného webu je u společnosti Media Factory.
je to mozny, moje predpoklady jsou zalozeny jen na tom co pisi ostatni, musim rict ze sem to nezkousel
- iguana007
- Člen | 970
norbe napsal(a):
Možná by nebylo na škodu to propojit ještě s GettextExtractorem, případně nějak jinak umožnit dolpnění nepřeložených textů, jelikož se některé situace dost špatně vyvolávají (např. informace o úspěšném odeslání emailu na localhostu) a nemuselo by tedy dojít k překladu všech řetězců.
Imho ideální workflow by bylo, že by se web přeložil přes panel. Jakmile by byly překlady 100% hotovy (kromě zmíněných emailových skriptů apod.), tak by se to projelo Extraktorem via. např. POEdit a tím by se .po soubor doplnil o chybějící překlady a to už by se dopřeložilo právě v tom POEditu.
- ic
- Člen | 430
teď jsem zkoušel poslední verzi z github a zjistil jsem že mi tam
nefunguje vytváření nových souborů překladů…
abych to upřesnil… když mám prázdnou složku na překlady a přeložím
NetteTranslator-em něco vygenerované soubory se potom při zobrazení
vyhazují 4 chyby:
PHP Notice: Undefined offset: 1 in ...\libs\NetteTranslator\Gettext.php:198
PHP Notice: Undefined index: Plural-Forms in ...\libs\NetteTranslator\Gettext.php:224
PHP Notice: Undefined variable: plural in ...\libs\NetteTranslator\Gettext.php:230
PHP Warning: htmlspecialchars() expects parameter 1 to be string, array given in ...\libs\Nette\Templates\Filters\TemplateHelpers.php:68
když se podívám do .po tak tam nejsou vyplněny všechny hlavičky… a tím myslím hlavně Plural-Forms (možná ještě nějaké další)
.
.
.
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
msgid "It works!"
msgstr "workuje to!"
.
.
.
když je doplním ručně a vytvořím .mo v poeditu můžu už další překlady dělat pohodlně v NetteTranslator-u jak nové překlady, tak měnit ty staré, ale založit soubor musím vždy v poeditu… divné
- Patrik Votoček
- Člen | 2221
Musíš si vygenerovat „prázdný“ .MO soubor s hlavičkami. Kouzlit to doopravdy neumí a zjistit jakou formu pluralu chces pouzit si to z prstu taky nevicuca… :-)
- iguana007
- Člen | 970
kubiq napsal(a):
$result[] = "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4 ? 1 : 2);";
ad. zkazení nevím, zatím jsem Vrtákův panel nepoužil, protože nepoužívám namespaces, ale k tomu zápisu: Vrtak doporučoval na Jabber chatu tento formát:
nplurals=3; plural=(n==1) ? 0 : ((n>=2 && n<=4) ? 1 : 2);
Editoval iguana007 (23. 7. 2010 11:02)
- Patrik Votoček
- Člen | 2221
skazil jsi akorat to ze pri aktualizaci na novou verzi tam tahle zmena nebude… a to ze kdyz zmenis jazyk tak tam porad budes mit pevne danou plural forumu cestiny.
- Vyki
- Člen | 388
Překlady fungujou. Debugoval jsem to a i $strings v šabloně trans. panelu se daty naplní, ale do toho panelu se nevypíší.
Edit.: Zdá se že vůbec nefunguje ten JS kód vkládaný do stránky.
Editoval Vyki (10. 9. 2010 13:21)
- Vyki
- Člen | 388
Překlady fungujou, ale panel ne. Zasadil jsem to do skeletonu posledního Nette 1.0dev, ale pořád nic. Ke stažení je to zde, už si s tím fakt nevím rady. Díky za případné nápady.
- zarubik
- Člen | 31
Asi stejný problém. Když jsem aktualizoval Nette z
VERSION: 1.0-dev
REVISION: 913fbaf released on 2010–06–08
Na poslední ke stažení:
VERSION: 1.0-dev
REVISION: c2aa2e7 released on 2010–09–11
Přestal fungovat Translation Panel. Neobjeví se žádné překlady a pouze text „Initializing…“. Když verzi vrátím zpět, funguje vše jak má.
GettextTranslator funguje.
- Patrik Votoček
- Člen | 2221
Hodte to jako issue na github… Hned jak budu mit trochu casu tak na to mrknu…
- Aurielle
- Člen | 1281
Sice už pár dní starý update, ale přece:
- Jazyky teď jdou přepínat i po přeložení jakéhokoliv řetězce (používám na generování odkazů do jiných jazyků)
- Pokud si přepnete jazyk na jiný než defaultní, tak TranslationPanel bude nyní ukládat do správného jazyka (předtím ukládal do výchozího i přes přepnutí).
- 2bfree
- Člen | 248
Nějak jsem to asi nepochopil.
Všechno jsem to nastavil tak jak je zde psáno. Vytvořil jsem si adresář
/app/lang (mimochodem, ten by to mohlo vytvářet automaticky a nebo to
ošetřit výjimkou)
Spustil jsem si aplikaci a v debug panelu se mi objevil odkaz translations
s krásným ukazatelem počtu nepřeložených stringů. Když na to kliknu,
tak se mi otevře okno Translations ovšem pouze s jedním polem pro překlad a
to „Singular:“
Když v něm vyplním překlad a refreshnu aplikaci, tak se dostanu na výjimku undefined index 1. Jak tady ve fóru bylo psáno, že to neumí kouzlit, tak jsem si našel vygenerovaný soubor cs_CZ.po a doplnil hodnotu do „Plural-Forms:“ tak jak to tu bylo psáno. Smazal jsem .mo soubor a refreshnul. Znova jsem vyplnil překlad a zase Undefined index 1.
Mohl by mi někdo říct, jak to tedy nastavit?
- 2bfree
- Člen | 248
Tak jsem pokročil. Podařilo se mi vygenerovat soubor .po tak, aby jej NetteTranslator zkousnul a zobrazil mi i pole pro plurál.
Vzal jsem jedinný řetězec, přeložil pouze singulár a uložil.
Po refreshi na mě problikne výjimka „Undefined variable $plural“
v souboru Gettext.php na řádce 230 a následně to skončí na výjimce
„syntax error, unexpected $end“
- Patrik Votoček
- Člen | 2221
Pozor! Nette translátor s *.po souborem vůbec nepracuje!!! Všechno si parsuje z *.mo souboru!
- 2bfree
- Člen | 248
2bfree napsal(a):
Tak jsem pokročil. Podařilo se mi vygenerovat soubor .po tak, aby jej NetteTranslator zkousnul a zobrazil mi i pole pro plurál.
Vzal jsem jedinný řetězec, přeložil pouze singulár a uložil.
Po refreshi na mě problikne výjimka „Undefined variable $plural“ v souboru Gettext.php na řádce 230 a následně to skončí na výjimce „syntax error, unexpected $end“
Tak chyba byla způsobena tím, že jsem do konfigurace „Plural-Forms“ ve zdrojovém .po souboru zapomněl dát středník na konec. ;)
Kvůli takovým blbcům jako jsem já doporučuji změnu z
<?php
$tmp = preg_replace('/([a-z]+)/', '$$1', "n=$form;".$this->metadata['Plural-Forms']);
eval($tmp);
?>
na
<?php
eval(preg_replace('/([a-z]+)/', '$$1', "n=$form;".$this->metadata['Plural-Forms'].";"));
?>
- Patrik Votoček
- Člen | 2221
Po týdnech / měsících slibování konečně můžu říct že jsem začal pracovat na nové aktualizované odladěnější verzi a bude brzo.
Ad podpora PHP 5.2 já ji rozhodně dělat nebudu ať už je to z důvodu
že v 5.2 nedělám už více jak rok nebo z důvodu že samotnému PHP
5.2 skončila oficiální podpora od tvůrců PHP ( „[09-Dec-2010]
The PHP development team would like to announce the immediate availability of
PHP 5.2.15. This release marks the end of support for PHP 5.2. All users of
PHP 5.2 are encouraged to upgrade to PHP 5.3.“:http://www.php.net/…ive/2010.php#… )
- JakubJarabica
- Gold Partner | 184
Vitaj, ako vidíš novšiu verziu? na GITe vidím poslednú aktivitu 3. novembra.
V akom štádiu to je s GettextExtractorom? Panel mám v poslednej verzii pekne rozbehaný, ale pri dopĺňaní stringov preklikávať celý web sranda nie je :)
- voda
- Člen | 561
JAM3SoN napsal(a):
preklikávať celý web sranda nie je :)
Pokud nechceš klikat, zkus upravený NetteGettextExtractor
- despiq
- Člen | 320
zalezi na situaci, ja treba mam mnohojazycnou aplikaci a vim ze i kdyz jsou jednotlive instance v mnohem stejne, jejich emaily nemuseji byt vubec totozne, ve chvili kdy bych si byl jist ze budou totozne tak bych s prekladanim nevahal ani chvilku, lepsi mit jednu sablonu kterou kdyz zmenim tak se to projevi vsude
- 2bfree
- Člen | 248
despiq napsal(a):
zalezi na situaci, ja treba mam mnohojazycnou aplikaci a vim ze i kdyz jsou jednotlive instance v mnohem stejne, jejich emaily nemuseji byt vubec totozne, ve chvili kdy bych si byl jist ze budou totozne tak bych s prekladanim nevahal ani chvilku, lepsi mit jednu sablonu kterou kdyz zmenim tak se to projevi vsude
Takže pokud se jedná o email, který bude pro každý jazyk formálně totožný a bude se lišit jen jazykem, tak bys zachoval jednu .phtml šablonu a překlad dělal výhradně NetteTranslatorem?
- 2bfree
- Člen | 248
gmvasek napsal(a):
Zapracoval jsem a naimplementoval podporu více souborů, ze kterých se překlad bere a do kterých lze ukládat. Práce s NetteTranslatorem se příliš nezměnila, pouze je teď nutné manuálně registrovat soubory přímo v kódu.
Je to na GitHubu.
Paráda, to se opravdu hodí. Dal bys prosím ještě někam ukázku, jak se to má použít?
Třeba bych chtěl jeden soubor pro každý blok (model, presenter, template) a ideálně si ten jazykový soubor načítal až v presenteru…