Lepší podpora vícejazyčnosti

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
knyttl
Člen | 196
+
0
-

Ahoj,

jelikož mám databázi pro překládání normálně v MySQL, tak abych ji mohl snadno měnit přes datagrid, překládání samozřejmě není nijak extra rychlé. Normální makra {_"…"} jsou překládány až při zobrazení stránky a tudíž neustále dokola při každém zobrazení.

Vyřešil jsem to tak, že jsem podědil funkci createPresenter a v ní šablonám změnil adresář podle vybraného jazyka a stejně tak i změnil adresář cachovací service; potom jsem si napsal filtry, které překládají makra {_"…"} již při kompilaci šablon. Mám tedy teď zvlášť cache pro každý jazyk. Mám tedy adresáře:

temp/cache
temp/cache/_
temp/cache/_Nette.RobotLoader
temp/cs/_NetteFileTemplate
temp/cs/_NetteTemplate.Cache
temp/en/_NetteFileTemplate
temp/en/_NetteTemplate.Cache

Přijde mi to ale dost „zprasené“ a myslím, že by něco takového mohlo umět samotné Nette.

Př.: Kdyby šlo v configu povolit jazykové verze [cs, en], zaregistrovat překládací službu a definovat jak se má daný jazyk rozpoznat. Nette pak automaticky by cachovalo do adresářů podle jazyka.

Přijde mi prostě nesmyslné překládat šablony neustále dokola, když to může proběhnout jen jednou.

Editoval knyttr (2. 5. 2011 8:43)

Jur4
Člen | 51
+
0
-

Myslím, že jsi nepochopil jak přesně to funguje (nebo jsem já nepochopil o co se snažíš).

Jde o to, že šablony se v jakémkoliv jazyce nakešujou úplně stejně, protože makro {_'message'} se nahradí voláním funkce $template->translate('message') a ne výsledným přeloženym řetezcem. O překlad se stará až translátor při každém načtení stránky. Kešování překladů je pak spíš jeho záležitost.

Sám to tak mám v projektech. V translátoru si sestavím slovník pro aktuální jazyk a uložím do keše. Dokud se v překladech něco nezmění tak se slovník bere z keše, jinak se znovu sestaví a zase uloží do keše.

Editoval Jur4 (2. 5. 2011 0:45)

knyttl
Člen | 196
+
0
-

Tak jsem ten text výše upravil, snad to bude zřejmější.

Patrik Votoček
Člen | 2221
+
0
-

jsem na tom stejně jako Jur4… moc nerozumím proč se snažíš o to o co se snažíš…

knyttl
Člen | 196
+
0
-

Prostě mi jde o to, aby se šablona překládala při kompilaci a ne při běhu.

Pokud mám šablonu, ve které mám 100 překladů, tak se šablona přeloží jen jednou a pak při každém zobrazení stránek, kde je šablona použita, tak už překlad nemusí probíhat a generování stránky proběhne významně rychleji.

Ještě jinak: chtěl bych přesunout překládání z úrovně helperů na úroveň filtrů.

Filip Procházka
Moderator | 4668
+
0
-

To přece můžeš udělat, úpravou storage a napsáním vlastního filtru :) Popř. rozšířením Latte

Patrik Votoček
Člen | 2221
+
0
-

knyttr napsal(a):

Ještě jinak: chtěl bych přesunout překládání z úrovně helperů na úroveň filtrů.

To je nemožné. Protože pak by nefungovali pluráry!

knyttl
Člen | 196
+
0
-

Ne nutně. Stačilo by to tomu přizpůsobit. No vidím, že to nezaujalo, byl to jen nápad, mně se tímhle způsobem podařilo zrychlit běh aplikace o stovky ms.

pLuRál.

Ondřej Mirtes
Člen | 1536
+
0
-

Řešíš to na nesprávné úrovni. Proč zařizovat kešování překladů jen pro šablony? Tenhle mechanismus patří do samotného překládání – tedy kešovat si na úrovni ITranslator výstup pro dané kombinace parametrů.

knyttl
Člen | 196
+
0
-

Jenže to pořád nic nemění na tom, že musíš při každém zobrazení stránky celou stránku prozkoumat a pro každý {_""} najít jeho překlad a nahradit ho. Proč to dělat pořád dokola, když to stačí udělat jednou?

Patrik Votoček
Člen | 2221
+
0
-

Jak by jsi to řešil:

<table>
	<tr>
		<th>{_"Name"}</th>
		<th>{_"Participation"}</th>
	</tr>
	{foreach $actions as $action}
	<tr>
		<td>{$action->name}</td>
		<td>{_['%d participant', '%d participants'], $action->participants}</td>
	</tr>
	{/foreach}
</table>
knyttl
Člen | 196
+
0
-

Třeba takhle:

CS:

<?php
<table>
        <tr>
                <th>Jméno</th>
                <th>Účast</th>
        </tr>
        {foreach $actions as $action}
        <tr>
                <td>{$action->name}</td>
                <td>{__['%d účastník', '%d účastníků'], $action->participants}</td>
        </tr>
        {/foreach}
</table>
?>

S tím, že {__[…], $value} makro by jen rozhodlo, který překlad použít.

Editoval knyttr (2. 5. 2011 11:58)

Aurielle
Člen | 1281
+
0
-

Narval jsi do šablony natvrdo stringy. Budeš mít snad 2 helpery pro překlad a překládat při kompilaci jen jeden? To je příšerně neefektivní.

knyttl
Člen | 196
+
0
-

Nevím, jak moc je to neefektivní, ale každé zobrazení je asi o 300 ms rychlejší.

David Grudl
Nette Core | 8218
+
0
-

Teoreticky by to udělat šlo, úprava by šla nad rámec vlastního makra, protože je třeba ještě zajistit, aby se pro každý jazyk vytvářela samostatná cache šablony.

knyttl
Člen | 196
+
0
-

David Grudl napsal(a):

Teoreticky by to udělat šlo, úprava by šla nad rámec vlastního makra, protože je třeba ještě zajistit, aby se pro každý jazyk vytvářela samostatná cache šablony.

Právě proto jsem myslel, že by to mohlo být součástí Nette – v tuhle chvíli tu cache doslova znásilňuju a rozhodně to pěkné není.

Aurielle
Člen | 1281
+
0
-

Dle mého názoru je dávat toto do Nette overkill – jsi první člověk se kterým jsem se setkal, který má překlady uložené v databázi.

Ondřej Brejla
Člen | 746
+
0
-

To si se asi s moc lidmi nesetkal ;-) Tím samozřejmě neříkám, že to není overkill ;-)

Aurielle
Člen | 1281
+
0
-

Tak jsem zase o něco chytřejší no :)

knyttl
Člen | 196
+
0
-

No, tak můžeš díky tomu mít všechna data na jednom místě a nemusíš používat žádné nástroje navíc.

Btw, to makro {__['', ''], $var} by vlastně mohlo být prospěšné i pro jednojazyčné verze – dalo by se uplatnit kdekoliv je potřeba vypisovat plurály.

Ještě v těch filtrech takhle používám např. bloky {cs}…{/cs} a {en}…{/en}, přičemž {cs} bloky jsou z anglické verze odstraněny a vice versa.

Editoval knyttr (3. 5. 2011 18:53)

knyttl
Člen | 196
+
0
-

A ještě mě napadá – i kdybych byl jediný člověk, který ukládá překlady do databáze a i kdyby to byl „overkill“, tak proč by to mělo být špatně? Trochu mi tohle vlákno přijde, jakože se mi od počátku snažíte naznačit, že jsem úplnej debil :-)

Už tak jsem očividně jediný člověk, kdo si překládá šablony v compile-time. Potrestejte mě :-)

Editoval knyttr (3. 5. 2011 18:58)

Ondřej Mirtes
Člen | 1536
+
0
-

Viz můj příspěvek. Řešíš daný problém na úplně špatném místě a z toho vyplývají všechny ty problémy. Přesuň kešování do tvé implementace ITranslator, zrychlení pocítíš stejné a budeš mít kešované překlady v celé aplikaci, nejen v šablonách. A bez problémů.

knyttl
Člen | 196
+
0
-

A kde je definováno to, že je to „úplně špatné místo“?

Kešování do ITranslator se mi stejně nelíbí – místo toho, aby byly překlady hledány v databázi, tak je to bude hledat někde na disku. Pořád to bude stát čas.

Jur4
Člen | 51
+
0
-

Já taky ukládám překlady v databázi. Jak už jsem ale psal, kešuju je na úrovni Translatoru (pokud je možnost tak do MemcachedStorage, ale to není všude). Mám tak překlady pro celou aplikaci na jednom místě a jednoduše je můžu invalidovat.

S tvým přístupem stejně budu muset mít Translator, který bude překládat všechny ostatní věci a při změně překladů budu muset řešit nejen invalidaci keše Translatoru ale i invalidaci šablon. A to pro pár milisekund.

Filip Procházka
Moderator | 4668
+
0
-

Ondřeji, tvůj přístup je pravděpodobně správný, ale když budeš mít šablonu ukládanou jako navrhuje knyttr, tak bude zrychlení ještě drastičtější. Nemám v plánu se to snažit implementovat, ale nápad na zrychlení je to opravdu dobrý a nezavrhoval bych ho.

Nebyla by paráda, mít dvou-stupňové cachování? Jednou by se přeložily „statické“ texty do šablony a podruhé by se cachovaly výsledky pro ostatní dynamické překlady?

redhead
Člen | 1313
+
0
-

Taky si myslím, že to není úplně špatný nápad. Proč by se měli pořád vyhledávat texty v nějaké kolekci, která ve velkých aplikací nebude malá, když by mohly být statické texty rovnou v šabloně a není tak nutná naprosto žádná režie.

Jediné, co se musí řešit je invalidace šablon, pokud se změní překlad.

A to pro pár milisekund.

Jistě.. Tak, prosím, smaž veškeré cache z velkých aplikací a pak si řeknem něco o pár milisekundách.

Edit: dodatek – jsem ale pro obě cache současné jak translatoru, tak přeložených šablon.

Editoval redhead (3. 5. 2011 21:18)

Tharos
Člen | 1030
+
0
-

IMHO to není chyba návrhu. Mně to připomíná klasickou výstupní cache (uchovávající kompletní vygenerované HTML či generující patřičné hlavičky) a ta se přece běžně používá. Každý přístup má své výhody i nevýhody a určitě bych něco neprezentoval jako „jediné správné řešení“. Zrovna tak je to IMHO s ukládáním překladů do databáze, to má přece taky svá pro i proti a určitě to není „zhmotnělé zlo“. :)

Editoval Tharos (3. 5. 2011 21:49)

knyttl
Člen | 196
+
0
-

Tharos napsal(a):

IMHO to není chyba návrhu. Mně to připomíná klasickou výstupní cache (uchovávající kompletní vygenerované HTML či generuje patřičné hlavičky) a ta se přece běžně používá. Každý přístup má své výhody i nevýhody a určitě bych něco neprezentoval jako „jediné správné řešení“. Zrovna tak je to IMHO s ukládáním překladů do databáze, to má přece taky svá pro i proti a určitě to není „zhmotnělé zlo“. :)

Ono jde o to, když z jedné šablony generuješ stovky stránek, tak u stránek, které se otevírají hodně a jsou už nakešované, není problém. Potom ale ty stránky, které se otevírají zřídka a nakešované nejsou, tak se pak poprvé generují zbytečně dlouho.

Jur4
Člen | 51
+
0
-

redhead napsal(a):
Jistě.. Tak, prosím, smaž veškeré cache z velkých aplikací a pak si řeknem něco o pár milisekundách.

Ja jsem myslel rozdíl mezi keší v Translatoru (ten se ve většině případů bude muset stejně načíst) a keší v šablonách, ne smazanou keš. Tam ten rozdíl nebude nijak velký.

Editoval Jur4 (3. 5. 2011 22:11)

Filip Procházka
Moderator | 4668
+
0
-

knyttr napsal(a):

Ono jde o to, když z jedné šablony generuješ stovky stránek, tak u stránek, které se otevírají hodně a jsou už nakešované, není problém. Potom ale ty stránky, které se otevírají zřídka a nakešované nejsou, tak se pak poprvé generují zbytečně dlouho.

To je ale normální princip invalidace, to přece jinak udělat nejde. Jeden z těch uživatelů prostě odnese generování stránky a o nepatrné cuknutí se mu zpomalí vykreslování.

trubi
Člen | 25
+
0
-

Mě přijde naprosto přirozené mít překlady v databázi. Zákazník (příp. někdo od SEO) si může v klidu kdykoliv změnit jakýkoliv text přímo v administraci aplikace. A jakmile už je to jednou přeložené, ty změny se zas tak často nedělají, takže vygenerovat znovu šablony přece není problém a návštěvník to téměř nepozná.

Překlad při kompilaci mám implementovaný už chvíli, rozšířil jsem si TemplateCacheStorage, jehož instanci vkládám do šablony v BasePresenteru. Je to hodně ohackované, ale funguje to docela dobře.

Problém je v tom, že to není univerzální řešení. Plno věcí při kompilaci překládat nejde, rozhodně se tím ale nějaký čas ušetří a toho textu, který se překládá až při běhu zas tak moc většinou není.

David Grudl
Nette Core | 8218
+
0
-

Už bych nerozpitvával, jestli požadavek je oprávněný nebo ne, protože oprávněný je, howgh :-)

Na straně přidání Latte makra by žádný zádrhel být neměl, prostě bude to obdoba implementace makra {_}, které se předá translator. Aby k tomu bylo možné použít FileTemplate, je třeba změnit klíč ze současného $this->file na vlastní zahrnující jazyk. Taktéž by se asi hodila možnost specifikovat vlastní podmínky při ukládání cache.

Ale – totéž platí i pro třídu Template, byť tady by se změna klíče dala simulovat přidáním nějakého {* lang:cs *} na konec řetězce. Napadá někoho šikovný refaktoring, jak kešování upravit v Template i FileTemplate?

knyttl
Člen | 196
+
0
-

Já se obávám, že ten refactoring nenavrhnu.

Každopádně s tím, jak to mám ohnuté docela válčím – jedna z nevýhod je třeba ta, že laděnka mi nevypisuje trace z šablon ale jen například z „../libs/Nette.php:5225 eval(…“ což moc k ničemu není.

Co jsem si ale teď díky tomu zvykl dělat je používat makra {cs}, {en}:

{cs}Tohle se vygeneruje v české šabloně{/cs} {en}This will appear in english template{/en}

Výhodou je, že člověk ty překlady nemá nikde zašité mimo a snadno se mění.

mancze
Člen | 58
+
0
-

knyttl napsal(a):

Co jsem si ale teď díky tomu zvykl dělat je používat makra {cs}, {en}:

{cs}Tohle se vygeneruje v české šabloně{/cs} {en}This will appear in english template{/en}

Výhodou je, že člověk ty překlady nemá nikde zašité mimo a snadno se mění.

Nevýhodou je, že překladateli nestačí poslat jeden soubor s klíči, ale musí se vrtat v šablonách. A zvyšuje se tím riziko, že něco rozvrtá.

Nicméně ale souhlasím s tím, že překlad při compile-time je dobrý nápad. Co se tím ušetří?

  • Soubor s překlady se nemusí vůbec ani parsovat, případně stahovat z databáze
  • Neprobíhá X vyhledávání v kolekci (kde X je počet překladů na stránce)
  • Neparametrické překlady ani nemusí vést k zavolání nějaké funkce, jsou do šablony prostě vepsané
  • Parametrické mají za následek pouze vložení parametrů, v nejhorším rozhodnutí formy plurálu
knyttl
Člen | 196
+
0
-

mancze napsal(a):

knyttl napsal(a):

Co jsem si ale teď díky tomu zvykl dělat je používat makra {cs}, {en}:

{cs}Tohle se vygeneruje v české šabloně{/cs} {en}This will appear in english template{/en}

Výhodou je, že člověk ty překlady nemá nikde zašité mimo a snadno se mění.

Nevýhodou je, že překladateli nestačí poslat jeden soubor s klíči, ale musí se vrtat v šablonách. A zvyšuje se tím riziko, že něco rozvrtá.

V našem případě si šablony překládáme sami, to znamená máme snadno pod kontrolou jejich veškerý obsah. Obrovskou výhodou je to, že člověk nemusí řešit, jestli daný klíč ještě je, nebo není používaný a může se z databáze překladů odstranit.