Gettext + Poedit + Nette – konečné řešení lokalizace
- Karel Klíma
- Člen | 31
Rád bych vás obeznámil s nápadem na lokalizaci aplikací napsaných v Nette za použití gettextu. Gettext má oproti jiným jazykovým řešením obrovskou výhodu – editor Poedit, který úpravy jazykových mutací značně urychluje. Zapněte si pásy, šou začíná.
Výhody řešení:
- maximální a zcela automatická podpora plurálů
- extrahování gettext jazykových klíčů ze skriptů a šablon
- kompatibilita s šablonami Nette a s filtrem CurlyBrackets
- konfigurovatelnost a rozšiřitelnost
Lokalizace aplikací už pro vás nikdy nebude otravnou činností.
Představuji vám tedy řešení i s tutoriálem: GettextExtractor | Gettext + Poedit + Nette
Na závěř malá ochutnávka, příkládeček:
<p>{!_"You have %d new messages", $number}</p>
Tři různé výsledky:
<p>Máte 1 novou zprávu</p>
<p>Máte 2 nové zprávy</p>
<p>Máte 5 nových zpráv</p>
Editoval Karel Klíma (19. 10. 2009 19:11)
- Karel Klíma
- Člen | 31
Ukázalo se, že navrhovaný Zend_Translate nefunguje, respektive nepodporuje plurál. Vydal jsem tedy další revizi mého řešení, již opravenou. Nový návrh využívá nativní PHP gettext extension.
Jediné, co se mi nepodařilo zprovoznit, jsou parametrizované překlady.
<p>{!_"Hello %s, you have %d new messages", "Karel", 1}</p>
<p>{!_"Hello %2$s, you have %1$d new messages", 10, "Karel"}</p>
První řádek se zpracuje perfektně, druhý ne. Chyba je v regulérním
výrazu určeném pro determinaci plurálu. Viz třída Translator
kvůli kontextu.
- Jakub Šulák
- Člen | 222
Nemáte náhodou někdo zkušenost s tím, jak řešit překlady v Nette tak, aby si uživatel mohl překlady dělat sám v administračním rozhraní? Tzn. bez použití Poedit.
Moje stávající řešení není právě dokonalé z hlediska výkonu a
využití paměti.
Jde o systém, který při konstrukci objektu Translator vytvoří asociativní
pole překladů v daném jazyce (toto pole se pak může cachovat; vytváří
se z databáze). Pokud se překlad nenajde (nebo je prázdný), ověří se zda
je překlad v DB, jinak se tam přidá.
Úklid starých překladů je zajištěn také celkem blbě – lze zapnout na pár dní „updater“, který při každém použití překladu nastaví timestamp danému záznamu. Poté se vyhodnocuje, které záznamy nebyly použity a nabídne se jejich smazání (archivace).
Takto to funguje celkem dobře, ale výkon není nic moc. Máte někdo lepší řešení?
- lopasovsky
- Člen | 17
Vďaka za GettextExtractor!
V NetteCurlyBracketsFilter.php – pre podporu UTF8 treba doplniť za regexp modifikátor „U“:
preg_match_all('#{(!_)([^}]*?)([a-z](?:[^\'"}\s|]+|\\|[a-z]|\'[^\']*\'|"[^"]*")*)?}#U', $contents, $matches);
…
preg_match('#\'[^\']*\'|"[^"]*"#U', $m, $x)
- danik
- Člen | 56
Nejak mi to nejede, nemuzu prijit na to cim to je, myslim ze to pise nejaky nesmysly do toho vygenerovanyho .po souboru, POEdit krici neco jako
01:24:15 AM: /tmp/poeditM87TgL:8:12: invalid control sequence
01:24:15 AM: /tmp/poeditM87TgL:9:13: invalid control sequence
01:24:15 AM: msgcat: found 2 fatal errors
01:24:15 AM: Failed command: msgcat --force-po -o "/tmp/poeditBuDxGe" "/tmp/poeditM87TgL" "/tmp/poeditMlv5TZ"
01:24:15 AM: Failed to merge gettext catalogs.
01:24:15 AM: Entries in the catalog are probably incorrect.
01:24:15 AM: Updating the catalog failed. Click on 'More>>' for details.
rad bych podotkl ze se mi nepodarilo odchytit nikde ty /tmp/poeditC0s1
soubory, maze je rovnou za chodu blbec cistotnej takze nevim nic, nevim kterej
soubor tu chybu dela, jestli to je necim v my template nebo cim to jako je…
ale fakt je, ze jsem zkousel ten testovaci skript GettextExtractoru pro
CurlyBracketsFilter (jde mi hlavne o templaty) na svym @layout souboru a
GTE v nem nasel jenom pulku (!) prekladanejch retezcu, takze uz ten prvni
regexp je asi spatnej (ale takovej regexp guru abych ho opravoval zas nejsem).
Ostatne proc tam vlastne musi bejt takova vec, nefungovalo by stejne dobre
napriklad /\{\!_([^\{]+)\}/u
? nevim presne co tak genialniho
tenhle regexp musi umet ale tohle by mi celkem stacilo myslim…
Zatim jdu kopat dal, uvidime jestli najdu nejakyho psa trebas u toho
poeditu…
- danik
- Člen | 56
ha, objevil jsem neco dost zajimavyho: gettextextractor (s tim mnou navrhovanym regexpem) si hrave poradi se vsim co mam v templatach ale neporadi si s normalnima PHP souborama, naprotitomu xgettext nezvlada ty templaty… takze v php parseru parsuju jenom soubory .php a vytvoril jsem novej parser na .phtml kterej parsuju gteckem. Zasadni je ze to funguje a kdyz uz to clovek rozchodi, tak to praci urychli asi o tisic procent… Super vec! Dik.
- jasir
- Člen | 746
pekelnik napsal(a):
Návod dobrej… vše jsem udělal, ale nemůžu to rozběhat. Poedit v XP hlásí že chybí knihovny. Po hodinovém zkoušení snad úplně všeho to nedopadlo. Asi jsem lamička :-) Nevíte jak na to?
Popiš problém konkrétněji, taky jsem to rozběhával nette-clean a nějak jsem to dokopal.
- David Grudl
- Nette Core | 8215
Nestálo by za to tohle přidat do https://componette.org/search/?q=cs ?
- rokerkony
- Člen | 122
ahoj
zkousim rozjet localizaci podle navodu, ale mam problem… vzdy pri vytvoreni
noveho katalogu (nebo i nasledne aktualizece) mi vyskoci hlaska „Program CLI
přestal pracovat“.
nevim jestli je to chyba apache, windows, nebo poeditu? nesetkal jste se
s tim nekdo?
Poedit potom zahlasi jenom „Entries in the catalog are probably incorrect.“ ? ale i tak nevim co bych tam delal spatne :-(
- apeiron
- Člen | 1
rokerkony napsal(a):
ahoj
zkousim rozjet localizaci podle navodu, ale mam problem… vzdy pri vytvoreni noveho katalogu (nebo i nasledne aktualizece) mi vyskoci hlaska „Program CLI přestal pracovat“.
nevim jestli je to chyba apache, windows, nebo poeditu? nesetkal jste se s tim nekdo?Poedit potom zahlasi jenom „Entries in the catalog are probably incorrect.“ ? ale i tak nevim co bych tam delal spatne :-(
Chyba by měla být v php, respektive v mysqli_php.dll. Zkusil bych
nainstalovat novější PHP (popřípadě XAMPP).
Stejné řešení je popsané např. tady
- rokerkony
- Člen | 122
apeiron napsal(a):
rokerkony napsal(a):
ahoj
zkousim rozjet localizaci podle navodu, ale mam problem… vzdy pri vytvoreni noveho katalogu (nebo i nasledne aktualizece) mi vyskoci hlaska „Program CLI přestal pracovat“.
nevim jestli je to chyba apache, windows, nebo poeditu? nesetkal jste se s tim nekdo?Poedit potom zahlasi jenom „Entries in the catalog are probably incorrect.“ ? ale i tak nevim co bych tam delal spatne :-(
Chyba by měla být v php, respektive v mysqli_php.dll. Zkusil bych nainstalovat novější PHP (popřípadě XAMPP).
Stejné řešení je popsané např. tady
super… znova musim rici ze tohle forum je nejlepsi ktere jsem kdy zazil :-) vzdy se vse vyresi :-) at je to kolem nette visty Xammpu nebo cehokoli jineho!!! super!! diky :-)
- norbe
- Backer | 405
Ahoj, chtěl bych upozornit na malou chybičku v třídě Translator. Pokud se překladá prázdný řetězec, vrátí se META data daného překladu. Chybu jsem objevil, když jsem Translator připojil k formuláři (pokud políčka nemají předvyplněnou žádnou hodnotu, zobrazí se META data o překladu, což není to co by člověk čekal).
Řešení je velmi jednoduché (jedna podmínka na začátku metody translate):
if($message == "")
return "";
P.S. Při použití tohoto translatoru jsem na lokále také narazil na problém, že jazyková mutace nemůže být pojmenována pouze „cs“, ale musí být uvedena jako „cs_CZ“(seznam možných hodnot se vypíše zadáním příkazu locale -a), je to údajně způsobeno op. systémem Ubuntu.
Editoval norbe (7. 7. 2009 19:06)
- danik
- Člen | 56
Troufám si tvrdit, že koncept ITranslatoru tak jak je v Nette je poněkud nahouby, odpusťte ten výraz. Totiž mám-li překládat nějakou zprávu z jazyka A do jazyka B a oba jazyky mají podporovat plurál, něco mi tu chybí. Jasně, asi by šlo dávat do runtime skriptu nějakej parser, kterej v anglickým řetězci v určitým formátu rozpozná slovo, kterýmu má nakonci přidat/ubrat S. Ale tak jednoduché to přeci není. Proč bych měl mít ve zdrojácích singuláry od všech překládaných řetězců (dejmetomu v AJ) a potom v locales mít cs/[textdomain].mo a en/[textdomain].mo? To mám mít singulárovou formu všech anglickejch řetězců na dvou místech? To je dost anti-nette, alespoň tak jak já jsem pochopil nette. Opravte mě, prosím, pokud se pletu. Navrhuji změnit definici metody translate() rozhraní Nette\ITranslator po vzoru Gettextu takto:
interface /*Nette\*/ITranslator {
public function translate ($msgId, $msgIdPlural, $pluralCnt);
}
Editoval danik (23. 8. 2009 18:47)
- kravčo
- Člen | 721
danik napsal(a):
Troufám si tvrdit, že koncept ITranslatoru tak jak je v Nette je poněkud nahouby, odpusťte ten výraz. Totiž mám-li překládat nějakou zprávu z jazyka A do jazyka B a oba jazyky mají podporovat plurál, něco mi tu chybí.
Čo presne ti tu chýba?
Proč bych měl mít ve zdrojácích singuláry od všech překládaných řetězců (dejmetomu v AJ) a potom v locales mít cs/[textdomain].mo a en/[textdomain].mo? To mám mít singulárovou formu všech anglickejch řetězců na dvou místech? To je dost anti-nette, alespoň tak jak já jsem pochopil nette. Opravte mě, prosím, pokud se pletu.
Myslím, že sa trochu pletieš.
Rozhranie ITranslator
nehovorí, aký význam má mať
string $message
v konštruktore, to je na implementácii.
Z môjho pohľadu je správny prístup použiť ako kľuč k prekladom texty
v jednom z jazykov.
switch ($model->whatsGoingOn()) {
case 1:
return $translator->translate(1089, $cnt);
case 2:
return $translator->translate(2172, $cnt);
default:
return $translator->translate(5156, $cnt);
}
Vyššie uvedený kód rozhodne nie je veľmi čitateľný (je jedno, či si to predstavíme v prezenteri alebo šablóne) a ja mám s podobným používaním prekladov svoje zlé skúsenosti.
Nette ti však nebráni implementovať translator, ktorý toto prelúska.
class MyTranslator implements ITranslator
{
public function getModel() { ... }
public function translate(string $message, $count = NULL)
{
if (!is_numeric($message)) {
throw new InvalidArgumentException(...);
}
return $this->getModel()->lookup($message, $count);
}
}
Navrhuji změnit definici metody translate() rozhraní Nette\ITranslator po vzoru Gettextu takto:
interface /*Nette\*/ITranslator { public function translate ($msgId, $msgIdPlural, $pluralCnt); }
Nie je mi jasné, prečo dve id – mám to chápať tak, že jedno je pre singulár a jedno pre plurál?
- danik
- Člen | 56
To me neprekvapuje. Zkusim to polopaticteji.
Mejme danu funkci t($message, $count = null), ktera nam nejakym mechanismem preklada retezec $message do jazyka ktery „nekde nastavime“. Je uplne jedno, jak funguje tahle cast mechanismu, jestli to je zalozene na gettextu, hard-codovanem asociativnim poli nebo na databazi. Podstatne je, ze _kdesi_ mame nejakym zpusobem ulozene pary klic ⇒ hodnota, ktere funkci t() slouzi jako prekladova tabulka. Dokud fungujeme pouze v singularu, mame kazdy klic dejmetomu* na dvou mistech: jednak v ulozisti definic paru a jednak v miste, kde se retezec vyskytuje a my ho chceme prelozit. Dejmetomu proto, ze samozrejme ten samy klic muze byt pouzit (odkazovan, prekladan) na vice mistech.
Rekneme, ze chceme, aby nas projekt podporoval dva jazyky: anglictinu a cestinu. V projektu potrebujeme vyuzivat pluralove formy (Mate 1 novou zpravu / 3 nove zpravy / 10 novych zprav).
Takze na prislusnem miste ve zdrojovem kodu zavolame t(„You have %d new messages“, $pocetZprav). Funkce t() se podiva do prekladove tabulky pro cesky jazyk a zjisti: Pokud je $pluralCnt rovno 1, vratim „Mate %d novou zpravu“. Pokud je $pluralCnt v uzavrenem intervalu <2, 4>, vratim „Mate %d nove zpravy“ a konecne pokud je $pluralCnt rovno 0 nebo vetsi rovno 5 vratim „Mate %d novych zprav“. Je to velmi jednoduchy model, pro vetsinu ceskych aplikaci s nim vystacime.
A ted prepneme do anglictiny.
A nabizi se dve moznosti, co se muze stat: budto mame i pro anglictinu prekladovou tabulku – pak preklad probehne podobne jako u cestiny – nebo pro anglictinu tabulku nemame. Co se stane? Budeme mit vystup budto „You have 0 new messages“ nebo „You have 1 new messages“ nebo „You have 5 new messages“. Obavam se, ze pokud je obecna uroven anglictiny mezi programatory takova, jako ve firme kde pracuji, nikdo v tom neodhali chybu. Chyba tam ovsem je a vsem, kdo maji anglictinu alespon trochu v ucte, bude „You have 1 new messages“ vadit.
Dejme tomu, ze mam anglictinu v ucte a tak mi tohle vadi. Co muzu jako programator delat? Napsat prekladovou tabulku, ktera bude z 80% zaplnena pary typu „Login“ ⇒ „Login“, „User name“ ⇒ „User name“, „Welcome back, %s“ ⇒ „Welcome back, %s“ a zbylych rekneme 20% budou moznosti typu „You have %d new messages“ ⇒ [„You have %d new message“, „You have %d new messages“] nebo „There were %d unsuccessful login attempts“ ⇒ [„There was %d unsuccessful login attempt“, „There were %d unsuccessful login attempts“]. Z toho zacinam mit dojem urcite redundance, to se mi zda zrejme.
Druha alternativa je upravit rozhrani Nette\ITranslator (.. respektive vytvorit si vlastni, pokud Nette tuhle koncepci neprijme za svou) a jednu tabulku prekladu (samozrejme tu anglickou) mit hardcodovanou primo ve zdrojovem kodu – tzn. pripravime si tabulku ceskych prekladu a kdyz si klient stranku vyzada v anglictine, funkce t() ma vsechno potrebne k zobrazeni spravneho prekladu v parametrech – jak retezec pro singular tak pro plural: t(„You have %d new message“, „You have %d new messages“, $pocetZprav).
Zkusme se statisticky zamyslet. Pristoupime-li na separovanou druhou tabulku prekladu, vznikaji nam redundantni data hned na nekolika mistech: jednak samotna zprava, kterou ve zdrojovych kodech predavame funkci t(), neni nikdy primo pouzita, takze kdybychom misto ni uvadeli ciselny identifikator, usetrime parseru nejakou praci s porovnavanim retezcu etc. (cemuz se kvuli prehlednosti kodu obloukem vyhneme, jak uz rekl kravco), a jednak budeme mit spoustu nesmyslnych paru typu Ahoj ⇒ Ahoj v tabulce s preklady.
Nekdo by mohl namitnout, ze kdyz bude muset pro kazdy plural volat funkci t() se tremi misto se dvema argumenty, zahlti se tim zbytecne kod. Namitku bych zamitnul s tim, ze pluralu na beznem webu zas tak moc neprekladame, takze nam to v porovnani s metodou druhe tabulky hodne prace usetri.
Snad jsem to vysvetlil dostatecne.
Editoval danik (23. 8. 2009 20:59)
- jasir
- Člen | 746
Já to chápu tak, že vstupy do translate() nejsou konečné texty v angličtině, programátor nemusí umět dost dobře anglicky (mnoho jych neumý ani česki), texty může na závěr upravit překladatel v mo/po souborech. Chápu je jako klíče do tabulky překladů, ne jako konečný překlad. Texty se pak mění jen v mo/po souborech a ne ve zdrojácích. Proto to nevidím jako duplicitu tak jako ty.
Ovšem jak psal kravčo, co se týče implementace Translatoru, fantazii se meze nekladou… ;-)
Editoval jasir (23. 8. 2009 21:07)
- kravčo
- Člen | 721
danik napsal(a):
Najprv sa mi to nechcelo čítať (dlhé a zliate dokopy)…
Dejme tomu, ze mam anglictinu v ucte a tak mi tohle vadi. Co muzu jako programator delat? Napsat prekladovou tabulku, ktera bude z 80% zaplnena pary typu „Login“ ⇒ „Login“, „User name“ ⇒ „User name“, „Welcome back, %s“ ⇒ „Welcome back, %s“ a zbylych rekneme 20% budou moznosti typu „You have %d new messages“ ⇒ [„You have %d new message“, „You have %d new messages“] nebo „There were %d unsuccessful login attempts“ ⇒ [„There was %d unsuccessful login attempt“, „There were %d unsuccessful login attempts“]. Z toho zacinam mit dojem urcite redundance, to se mi zda zrejme.
Urob z toho múdru tabuľku:
"You have %d new messages" => [
"You have %d new message",
TRUE,
],
A redundancia je fuč ;)
t(„You have %d new message“, „You have %d new messages“, $pocetZprav).
Môj názor: toto má dosť veľký wtf faktor. A navyše v prípade opakovania v kóde máš redundanciu približne takú, akej sa chceš zbaviť…
Zkusme se statisticky zamyslet. Pristoupime-li na separovanou druhou tabulku prekladu, vznikaji nam redundantni data hned na nekolika mistech: jednak samotna zprava, kterou ve zdrojovych kodech predavame funkci t(), neni nikdy primo pouzita, takze kdybychom misto ni uvadeli ciselny identifikator, usetrime parseru nejakou praci s porovnavanim retezcu etc., a jednak budeme mit spoustu nesmyslnych paru typu Ahoj ⇒ Ahoj v tabulce s preklady.
Zásadne šetrím čas sebe, nie parseru…
Nekdo by mohl namitnout, ze kdyz bude muset pro kazdy plural volat funkci t() se tremi misto se dvema argumenty, zahlti se tim zbytecne kod. Namitku bych zamitnul s tim, ze pluralu na beznem webu zas tak moc neprekladame, takze nam to v porovnani s metodou druhe tabulky hodne prace usetri.
Namietam, no nie zahltením kódu, ale wtf faktorom vyššie…
Snad jsem to vysvetlil dostatecne.
Myslím, že áno.
- danik
- Člen | 56
wtf faktor… velmi vystizna velicina.
jak uz jsem rekl, parametrizovane pluraly v prekladu jsou na webu alespon podle moji zkusenosti spis vyjimka, tzn. bezne budu volat napriklad t(„User name“), t(„Welcome back, %s!“) a podobne, tady se nic nemeni. Ale kdyz budu potrebovat prelozit pocet novych zprav, rekl bych, ze to budu mit v sablone jen jednou… a tim padem budu mit t(„%d new message“, „%d new messages“, $pocetZprav), coz ma dejme tomu wtf faktor rovny dvema – dvakrat tolik retezcu nez by bylo nezbytne nutne. Zajima me, jaky wtf faktor potom bude mit kazdy radek .PO souboru s pary typu „User name“ ⇒ „User name“? Vzhledem k tomu, ze tady by nebyl potreba ani jeden vyskyt retezce „User name“ a mame tu vyskyty hned dva, delime nulou. A kdybychom si nahodou chteli misto nuly predstavit extremne male kladne cislo, abychom se deleni nulou vyhnuli, dostavame se limitne k wtf faktoru rovnemu nekonecnu. Trocha matematiky.. Navic, kravco, tenhle pristup ma byt skutecne programatorsky privetivy, taky nejsem fanousek translate(5488, $msgCnt) a podobnych priser.
Nakonec je ale pravda, ze spousta programatoru anglicky temer neumi.. takze to asi nema smysl. Zapomente na to, asi to je zbytecna snaha. Obcas kdyz ctu zdrojove kody a narazim na nejakou vyjimku nebo neco kde autor musel zapojit vyraznejsi anglictinarske schopnosti nez if a while mam pocit ze nechci byt programator..
- Patrik Votoček
- Člen | 2221
Zrovna když se to chystám řešit tak to někdo vytáhne… Předem říkám že neumím česky na tož anglicky… Ale tohle mě vadí (zase tak špatně na tom snad nejsem)… :-)
danik díky za nakopnutí. Dělat něco jako
translate(string $message, string $messagePlural, int $count = NULL);
se mě ale nelíbí spíš bych uvítal variantu
translate(string $message, int $count = NULL, string $messagePlural = NULL);
nebo proste odstanit string z metody translate pak by se to
dalo řešit
translate(array("You have %d new message", "You have %d new messages"), 1);
s tím že translator si to už pořeší.
Když se na to podívám z druhého pohledu tak stejně budu (asi)
potřebovat anglickou tabulku překladů. Protože pak příjde potencionální
překladatel který bude chtít přeložit appsku třeba do „cikánštiny“
(nejsem rasista → podporuju menšiny :-D) a jednoduše pouze veme anglickou
tabulku a přepíše jí. Blbost můžu vzít jakýkoliv jiný překlad a
dělat to z něho… 2× Blbost co když je aplikace jenom anglicky a
potencionální překladatel si z ní původní texty neumí vytáhnout?
Editoval vrtak-cz (23. 8. 2009 22:15)
- romansklenar
- Člen | 655
danik napsal(a): … velmi mnoho textu
Myšlenka to možná není špatná, ale IMO jen pokud je implementace
vykonání překladu na úrovni ternálního operátoru „pokud je počet roven
1 vrať singulár jinak plurál“ (tady by možná bylo znatelné nějaké
zrychlení). Redundance bude pořád v šablonách (pokud teda upustíme od
nějakých pseudo konstant) a když budete primárně vyvíjet v jazyku, který
má tři formy jako čeština tak co potom? Budete psát
$translator->translate("%d kočka", "%d kočky", "%d koček", 3)
?
Jinak obecně není si problém v Nette něco přiohnout když mi to nevyhovuje, ale proč to rovnou tlačit do distribuce, když jsem zvyklý sám nějakou svou problematiku řešit jinak? Připadá mi to jako výmýšlení kola s potřebou dokázat ostatním, že to moje je kulaťejší, protože jsem na něj už zvyklý a vymyslel jsem ho sám :)
- Panda
- Člen | 569
danik napsal(a):
wtf faktor… velmi vystizna velicina.
jak uz jsem rekl, parametrizovane pluraly v prekladu jsou na webu alespon podle moji zkusenosti spis vyjimka, tzn. bezne budu volat napriklad t(„User name“), t(„Welcome back, %s!“) a podobne, tady se nic nemeni. Ale kdyz budu potrebovat prelozit pocet novych zprav, rekl bych, ze to budu mit v sablone jen jednou… a tim padem budu mit t(„%d new message“, „%d new messages“, $pocetZprav), coz ma dejme tomu wtf faktor rovny dvema – dvakrat tolik retezcu nez by bylo nezbytne nutne. Zajima me, jaky wtf faktor potom bude mit kazdy radek .PO souboru s pary typu „User name“ ⇒ „User name“? Vzhledem k tomu, ze tady by nebyl potreba ani jeden vyskyt retezce „User name“ a mame tu vyskyty hned dva, delime nulou. A kdybychom si nahodou chteli misto nuly predstavit extremne male kladne cislo, abychom se deleni nulou vyhnuli, dostavame se limitne k wtf faktoru rovnemu nekonecnu. Trocha matematiky.. Navic, kravco, tenhle pristup ma byt skutecne programatorsky privetivy, taky nejsem fanousek translate(5488, $msgCnt) a podobnych priser.
Nakonec je ale pravda, ze spousta programatoru anglicky temer neumi.. takze to asi nema smysl. Zapomente na to, asi to je zbytecna snaha. Obcas kdyz ctu zdrojove kody a narazim na nejakou vyjimku nebo neco kde autor musel zapojit vyraznejsi anglictinarske schopnosti nez if a while mam pocit ze nechci byt programator..
Nevím jak ty, ale já podobný problém řeším .po
souborem,
kde mám jen ty plurály. Pokud se pokusím přeložit singulár, tak můj
translator nenalezne žádný záznam a použije ten, co jsem mu předal ve
funkci. Sice to není úplně ideální, ale pracuje se s tím docela
fajnově.
Navíc s tím WTF faktorem můžeme jít ještě dál, pokud bychom chtěli něco takového:
t("No new messages", "%d new message", "%d new messages", count($messages));
A co kdybychom jako nativní jazyk nepoužili angličtinu, ale nějaký jiný jazyk, například proto, že máme překladatele, který anglickou lokalizaci napíše lépe a bude to rychlejší, než kdyby to po programátorech opravoval? To by počet těch volání mohl být ještě vyšší, protože ten jazyk může mít třeba singulár, duál, plurál, do toho se může zamíchat varianta pro 0 zpráv a ještě ten plurál může mít víc tvarů, třeba jako tvoří čeština pro intervaly 2 – 4 a 5+?
Osobně tedy zastávám názor, že konkrétní realizace překladů by měla
být specifická pro konkrétní projekt. Pokud chceme předat více řetězců,
můžeme jako parametr $message
předat již zmiňované pole.
Větší počet parametrů na úrovni rozhraní by dle mého soudu celý systém
značně komplikoval.
Editoval Panda (23. 8. 2009 22:30)
- DocX
- Člen | 154
AHA!
Já, od malička vychovávaný k gettext
a
ngettext
, jsem na ten ITranslator
čuměl jak puk :D
A ono se to má používat tak, že v kódu mám „pseudo-jazyk“ (dejme
tomu nějakou „englysch“ (podle toho jak programatoři umí anglicky)) a
potom mám pro každý skutečný jazyk překladovou tabulku, o kterou se
stará skutečný „textoplnič“ webu.
Správně, nejsem příznivce danik
a (i když jsem si ze
začátku myslel, že jsem)
Co jsem ale teď rychle koukal na ten gettext_extractor, nevím, jestli dokáže vytáhnout texty z Nette struktur, které pak používají k překladu ITransaltor. Například takové
<?php
$form->setTranslator(new MyTranslator());
$form->addText('id', 'User name:')->addRule(Form::FILLED, 'You are idiot!');
?>
Umí tohle taky?
Editoval DocX (23. 8. 2009 22:45)
- danik
- Člen | 56
Tady se nám toho ale sešlo.
Vrtáku, děkuji za podporu. Pole je ale v dané situaci podle mě spíš sedm zbytečných písmenek navíc.
Argument „anglickou tabulku udělá překladatel“ se zamítá. Naučte se anglicky a pak se vraťte k programování. Psát do zdrojového kódu programovacího jazyka, jehož sémantika je založena na angličtině, výrazy v jiných jazycích (ať už jako klíčové názvy nebo do řetězců) je pro slušného člověka tabu. To nám zároveň velmi zjednodušuje teoretickou úvahu nad počtem argumentů, které by musela překládací funkce mít a tleská teorii Romana Sklenáře o ternárních operátorech.
DocX, jděte se schovat někam do kouta, než někdo objeví, že vás někde ukradli. Pokud možno si s sebou vemte původní tutoriál Karla Klímy k jeho GettextExtractoru a o volných chvílích si čtěte.
Pane Sklenáři, nikdo nikoho nenutí tlačit věci do distribuce bez předchozí diskuse a konsensu všech zúčastněných. Jezděte dál na velocipédu.
- jasir
- Člen | 746
danik napsal(a):
Argument „anglickou tabulku udělá překladatel“ se zamítá. Naučte se anglicky a pak se vraťte k programování. Psát do zdrojového kódu programovacího jazyka, jehož sémantika je založena na angličtině, výrazy v jiných jazycích (ať už jako klíčové názvy nebo do řetězců) je pro slušného člověka tabu. To nám zároveň velmi zjednodušuje teoretickou úvahu nad počtem argumentů, které by musela překládací funkce mít a tleská teorii Romana Sklenáře o ternárních operátorech.
wtf?
DocX, jděte se schovat někam do kouta, než někdo objeví, že vás někde ukradli. Pokud možno si s sebou vemte původní tutoriál Karla Klímy k jeho GettextExtractoru a o volných chvílích si čtěte.
WTF??
Pane Sklenáři, nikdo nikoho nenutí tlačit věci do distribuce bez předchozí diskuse a konsensu všech zúčastněných. Jezděte dál na velocipédu.
WTF, tak tohle jsem přečetl asi 3× a stále nevím jestli už nespím…
- romansklenar
- Člen | 655
danik: Už ti někdo řekl, že arogantní voly nemá nikdo rád? ;)
EDIT: pro zachování kontinuity vesmíru raději vlákno zavřeme…
- Patrik Votoček
- Člen | 2221
danik napsal(a):
Tady se nám toho ale sešlo.
Teď na to koukám že zajímavě.
>
Vrtáku, děkuji za podporu. Pole je ale v dané situaci podle mě spíš sedm zbytečných písmenek navíc.
Nemyslím si snažil jsem se aby můj návrh co nejméně ovlivnil stávající implementaci. Jinak pokud jsi četl celý příspěvěk tak jsem tě zase tak moc nepodporoval.
Naučte se anglicky a pak se vraťte k programování. Psát do zdrojového kódu programovacího jazyka, jehož sémantika je založena na angličtině, výrazy v jiných jazycích (ať už jako klíčové názvy nebo do řetězců) je pro slušného člověka tabu. To nám zároveň velmi zjednodušuje teoretickou úvahu nad počtem argumentů, které by musela překládací funkce mít a tleská teorii Romana Sklenáře o ternárních operátorech.
WTF? Spousta lidí začínajících s programováním aglicky neumí (někteří vůbec!!!). Ale nevím proč by jim to mělo bránit v programování (co když vymyslejí další Google, FB, YouTube?) na to přece angličtinu umět nemusejí!!! Proč když to PHP dovoluje bych nemohl pojmenovat proměnou česky a klidně i s diakritikou? (kdyby to PHP zakazovalo tak to chápu ale takhle ne!)
DocX, jděte se schovat někam do kouta, než někdo objeví, že vás někde ukradli. Pokud možno si s sebou vemte původní tutoriál Karla Klímy k jeho GettextExtractoru a o volných chvílích si čtěte.
Ale no tak snad se tu nebudeme urážet! Pokud si chcete měřit (teď mi promiňte) pinďoury tak někde jinde prosím. Nerad bych zbytečně četl další nic neříkající příspěvky.
Pane Sklenáři, nikdo nikoho nenutí tlačit věci do distribuce bez předchozí diskuse a konsensu všech zúčastněných. Jezděte dál na velocipédu.
Však roman neříkal že někdo někoho nutí jenom vyjádřil svůj názor že se mu to nelíbí. A jelikož je to jeden z commitujících lidí a tím pádem v něj má david důvěru.
PS: Prosím urážení si nechte někam jinam!!! (máte na to své blogy a podobně) Pokud doopravdy musíte někomu říc že je to… Pošlete mu e-mail ať ostatní nemusejí číst zbytečné texty (a ztrácet tak čas).
- kravčo
- Člen | 721
vrtak-cz napsal(a):
Poprosil bych některého z moderátorů o odemknutí vlákna: https://forum.nette.org/…i-lokalizace důvod proč bylo zamknuto už není aktuální (vyříkaly jsme si to e-mailem) a mohly by jsme se tam vrátit k seriózní diskuzi.
Vlákno je opäť odomknuté.
- danik
- Člen | 56
vrtak-cz napsal(a):
mcjahudka napsal(a):
Děkuji a ještě tady veřejně se omlouvám všem za svoje stupidní příspěvky, byl to špatný pokus o špatný humor. Danik.
//OT: Mohu vědět proč jsi změnil přezdívku? – jak dostanu odpověď příspěvek mažu!!!
nezměnil, myslel jsem si, že jsem jenom dostal ban do tohohle vlákna (ani nevím, jestli to jde) a až když jsem si zaregistroval MC Jahůdku tak jsem zjistil že je zamčený globálně.. takže zkusím nějak zrušit MC Jahůdku, na co jsou mi dva profily.. :o)
By the bye, poslal jsem Romanu Sklenářovi, vrtaku-cz a DocX tuhle věc.. nechci tím tady floodovat, tak kdo má zájem přečíst si moje vysvětlení / omluvu v tom větším rozsahu, je to tady .
- DocX
- Člen | 154
Pokusím se o malé shrnutí:
Použití ITranslatoru
Výhody:
- některé třídy Nette mají zabudovanou jeho podporu → nemusí se pak
psát okolo každého textu věci jako
_()
. - co se týče (současné) definice rozhraní, nepřikazuje pužití jakékoli technologie (gettext, databáze, pole ID → text, apod.).
- může být použito tak, že finální texty nejsou nikde v kódu a řeší je až ten, kdo se stará o texty.
Nevýhody:
- Neumí plurál, což ústí k užití překladové tabulky i pro všeobecně plurálově asi nejjednodušší jazyk jako je AJ.
- Zda jde o množné nebo jednotné číslo se zjišťuje až v runtimu.
Upravit ITranslator o plurál
Výhody:
- Jelikož se všeobecně jako primární jazyk používá angličtina, může být ta celkem jednoduše součástí kódu.
- Tím by odpadlo použítí jedné překladové tabulky navíc.
- Dopředu by program věděl, zda se jedná o množný nebo jednotný tvar.
Nevýhody:
- Asi by se tím zamezilo možnosti zapouzřit překlad do tříd, jako jsou
teď
Forms
. - Rozhraní by se přiblížilo k překladové technologii na principech gettextu.
Obecné nevýhody ITranslatoru
- Při využití zmíněné abstrakce (což je podle mě celkem fajn věc) se prakticky zamezí automatické získávání textu.
- Pokud by jsme se tomu chtěli vyhnout a pužívali všude
$mujITranslator->translate(...)
, je nám to vlastně k ničemu.
Ačkoliv je tedy ITranslator vlastně „geniálně jednoduchý“,
představuje pro mě z pohledu implementace a údržby riziko. Proto pro mě
osobně stále zůstává jako dobrá volba přímo gettext a všude píši
_(...)
nebo ngettext(..., ..., x)
. I když jsem si
chvilku myslel, že je ITranslator výborný, nakonec by mi přinesl spíše
více starostí než ulehčení.
PS: je to můj názor, samozřejmě se mohu mílit nebo se špatně formulovat. Každou seriozní výtku výtám :)
Editoval DocX (25. 8. 2009 1:40)
- Patrik Votoček
- Člen | 2221
DocX napsal(a):
Upravit ITranslator o plurál
Nevýhody:
- Rozhraní by se přiblížilo k překladové technologii na principech gettextu.
Můžu vědět proč to považuješ za nevýhodu?
- DocX
- Člen | 154
vrtak-cz napsal(a):
DocX napsal(a):
Upravit ITranslator o plurál
Nevýhody:
- Rozhraní by se přiblížilo k překladové technologii na principech gettextu.
Můžu vědět proč to považuješ za nevýhodu?
Z tohoto vlákna jsem nabyl pocitu, že je právě výhoda to, jak je rozhraní jednoduché. Někdo tu zmiňoval pole s konkstantami, jiný texty v SQL databázi. Ať už jsou tyto varianty jakkoli dobré nebo špatné, rozhraní by podle mě nemělo nic přikazovat.
//EDIT: Jo jinak jsem se vlastně snažil schrnout argumenty pro a proti oboum verzím :) Takže je to jakoby nevýhoda oproti původnímu ITranslatoru.
Editoval DocX (25. 8. 2009 1:45)
- Patrik Votoček
- Člen | 2221
Pokusím se shnout k čemu jsem zatím dospěl já a jakou myšlenkou sí
pohrávám…
Jediný problém který vydím v plural-ovém ITranslatoru je spolupráce
s Formulářema a DataGridem… Ale jelikož stále neexistuje nástroj který
by automatizovaně získával řetězce i z Form-u a DataGrid-u viz toto
vlákno: https://forum.nette.org/…tte-aplikaci
Tak to asi budu řešit tak že si udělám ITranslatorPlural
s metodou translate(string $msgid1, string $msgid2, int $n)
v base presenteru rozšířím metodu setTranslator
o jeho
podporu. A napíšu si macro _n
nebo ngettext
nebo
_plural
. Editnu Gettext extractor… A hurá máme to tam. Možná
jenom rozšířím ITranslator
o metodu
translatePlural(string $msgid1, string $msgid2, int $n)
. Ještě
nevím musím si to nechat pořádně projít hlavou.
- danik
- Člen | 56
Já jsem nad tím taky trochu přemýšlel. Vím, že u template translatorů jde ze šablon předat do překládací funkce libovolný počet argumentů, takže ať budeme translate volat s jedním msgId a plurálem nebo dvěma a plurálem, není to problém. Ale (opravte mě, jestli se pletu) myslím, že formulářové volání translatoru předává jenom a pouze jeden argument – není tu prostor pro plurály ani parametrizaci zprávy. Řekl bych že změnit tohle by si vyžadovalo relativně rozsáhlé zásahy do API Nette a to by asi nebylo nijak jednoduché.
Tak či onak, to je problém na další diskuzi, protože ať už budeme chtít překládací funkci předat jakýkoliv počet msgId, pořád nám chybí to podstatné a to je pluralCount, podle kterého by náš překladač určil, kterou formu má použít.
No a co se týká msg id a wtf faktorů atp, napadlo mě jedno řešení, přivedl mě na něj Panda (a možná to myslel přímo takhle):
- Překladač dostane při inicializaci definováno, co je výchozím jazykem aplikace, v němž jsou v kódu psána msg id.
- ITranslator se neupraví, zůstane tak jak je. Tj. při překládání mu předáme buď msgId nebo msgId plus pluralCount.
- Budeme mít kompletní tabulky s překlady pro všechny jazyky s výjimkou jazyka výchozího. Ten bude mít tabulku speciální: budou v ní páry nikoliv JazykA ⇒ JazykB, ale Singulár ⇒ Plurál (-y jako pole, řekněme).
- Naše implementace ITranslatoru potom posoudí následující:
- Je-li aktuálním jazykem ten výchozí a pluralCount není uvedeno, vrátí msgId.
- Je-li aktuálním jazykem jazyk výchozí a zároveň je specifikováno pluralCount, „přeloží“ odpovídajícím způsobem singulár výchozího jazyka na plurálovou formu odpovídající pluralCount
- Není-li aktuálně zvolen výchozí jazyk, přeloží standardní cestou msgId do odpovídající formy aktuálního jazyka podle jeho překladové tabulky.
Takhle by vlastně všechna msgId byla zároveň singulárovou formou všech výrazů jednoho jazyka, plurály k němu bychom měli jinde a další jazyky bychom překládali tou běžnou cestou. Je to trochu směrem k gettextu, ale nemusí to tak nutně být. Koneckonců už relativně dlouho přemýšlím o tom, že bych zkusil napsat k Gettextu nějakou alternativu šitou PHP / Nette na míru, ale ještě nejsem dost guru :o)
Jinak já mám obecně na překládání pod Nette zaregistrovanou jako službu komponentu i18n, kterou taky pro každý překlad nevolám Environment::getService(„i18n“)->translate(..) :o) mám makro t(..), které mi to zprostředkuje s libovolným počtem argumentů, můžu poslat kód (ale tohle je zrovna trivka ;o) ).
- Ondřej Mirtes
- Člen | 1536
BTW: Má nějaký důvod, že GetTextExtractor bere ze šablon jen
escapované řetězce? ({!_'string'}
). XSS útok tam nehrozí, ale
stačí, aby překladatel hodil do překladu třeba &
a hned je
rozhozená validita…
Editoval LastHunter (25. 8. 2009 5:06)
- DocX
- Člen | 154
vrtak-cz napsal(a):
Tak to asi budu řešit tak že si udělám
ITranslatorPlural
s metodoutranslate(string $msgid1, string $msgid2, int $n)
v base presenteru rozšířím metodusetTranslator
o jeho podporu. A napíšu si macro_n
nebongettext
nebo_plural
. Editnu Gettext extractor… A hurá máme to tam. Možná jenom rozšířímITranslator
o metodutranslatePlural(string $msgid1, string $msgid2, int $n)
. Ještě nevím musím si to nechat pořádně projít hlavou.
Jakou to má potom výhodu oproti přímému použití gettext funkcí?
- wotaen
- Člen | 82
Ahoj, pravda tohle moc nesouvisí s Nette, ale třeba na to někdo narazil.
poedit používá utilitu msgcat na merge dvou .po souborů. Například když
se přidá nějaký nový překlad.
Gettextextractor volá addslashes při zápisu výstupního souboru (ten
slouží jako jako INPUTFILE pro msgcat), tj. escapují se např. apostrofy.
Imho to je správně, msgcat ale zařve při merge „invalid control
sequence“ na řádku a znaku kde je apostrof. Řešení ve stylu odstranění
volání addslashes v extractoru se mi moc nelíbí. Setkal jste se
s tím někdo?
Díky, Michal
- wotaen
- Člen | 82
Hmm, tak přecejen to může být tím extractorem…
msgcat nemá rád, když se escapuje něco, co se escapovat nemusí, třebas
# Links.php:47msgid "Your friend\'s email"
msgstr "Your friend\'s email"
není nutné escapovat. Jenomže GettextExtractor volá addslashes na všechno, tudíž zapisuje apostrofy kde nejsou třeba. Tj. v metodě formatData je třeba zavolat místo addslashes něco jiného, např. addcslashes.
Tož tak, třebas to pomůže.
Michal