Gettext + Poedit + Nette – konečné řešení lokalizace

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Karel Klíma
Člen | 31
+
+1
-

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)

pmg
Člen | 372
+
0
-

Děkujem! Vypadá to moc pěkně + vzorová dokumentace. :-)

Ola
Člen | 385
+
0
-

Zdravím, je to super, nechceš ještě přidat podporu pro překládání formulářů?

Karel Klíma
Člen | 31
+
0
-

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
+
0
-

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í?

nAS
Člen | 277
+
0
-

Zkus prozkoumat, jak to mají udělané v Drupalu. Tam také používají Gettext a v administraci lze upravovat jednotlivé překlady. Ale jestli to mají udělané nějak hezky, to nevím, nikdy jsem to nezkoumal podrobně.

lopasovsky
Člen | 17
+
0
-

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)
David Grudl
Nette Core | 8215
+
0
-

U je ungreedy, pro UTF-8 je modifikátor malé u.

PaBi3
Bronze Partner | 62
+
0
-

Tí ktorí používajú Eclipse (alebo Zend Studio 6+) môžu skúsiť namiesto Poedit editor Gted. Je omnoho stabilnejší a pohodlnejšie sa v ňom pracuje.

danik
Člen | 56
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

Nestálo by za to tohle přidat do https://componette.org/search/?q=cs ?

rokerkony
Člen | 122
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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)

romansklenar
Člen | 655
+
0
-

Nepochopil jsem.

kravčo
Člen | 721
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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..

jasir
Člen | 746
+
0
-

danik napsal mnoho textu…

wtf?? ;-)

danik
Člen | 56
+
0
-

mno a taky jsem se mozna az trochu moc vyrazne stylizoval do doktora House, nechtel jsem nikoho urazit, takovej detinskej malej for :o) snad si to nikdo neberete moc osobne..

ic
Člen | 430
+
0
-

Zapište do záznamu že vůbec nevím o čem to tady mluvíte

toho textu je hodně a diakritika by výrazně vypelšila jeho čtivost

danik
Člen | 56
+
0
-

ok, napravím to, ale chvíli to potrvá..

Patrik Votoček
Člen | 2221
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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 danika (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)

Patrik Votoček
Člen | 2221
+
0
-

Neumí… :-(
Více zde: https://forum.nette.org/…tte-aplikaci

danik
Člen | 56
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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é.

mcjahudka
Člen | 1
+
0
-

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.

danik
Člen | 56
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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í:
    1. Je-li aktuálním jazykem ten výchozí a pluralCount není uvedeno, vrátí msgId.
    2. 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
    3. 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
+
0
-

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
+
0
-

vrtak-cz napsal(a):

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.

Jakou to má potom výhodu oproti přímému použití gettext funkcí?

Patrik Votoček
Člen | 2221
+
0
-

Nemusí to mít s gettextem nic společného…

wotaen
Člen | 82
+
0
-

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
+
0
-

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:47
msgid "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