Konečně jsem předělal přepínaní jazyků na webu tak, aby každá jazyková verze měla jedinečná URL. Tím pádem odpadlo i používání cookies/sessions. Mořil jsem se s tím poměrně dlouho, ale nakonec jsem to zlomil. Protože mám celý web postaven na Zend Frameworku, hledal jsem řešení, jak to udělat co nejvíc tak, aby to zapadlo do konceptu Zend Frameworku, nakonec jsem ale musel použít trochu hack. K napsání článku mě přivedla i diskuze na fóru.
Možností, jak udělat přepínání jazyků je několik. Já jsem původně používal možnost s přepínáním jazyků prostřednictvím parametru v URL (entry?lang=en
) a ukládání do cookies. Problém byl, že obě jazykové verze mají stejné URL. Parametr slouží jen k přepnutí. To je dostatečné pro intranety, ale na veřejném internetu se to moc nehodí, protože vyhledávače pak indexují jen jednu variantu.
Při použití Zend Frameworku máme zhruba tyto možnosti:
- použít klasické řešení routy s definicí jazyka v URL při registraci
- napsat si vlastní router
- ve front controller pluginu si pohrát s URL
- to samé jako předešlý bod, ale udělat to v bootstrapu
Klasický způsob
Jako nejčistější se jeví první řešení. Přesně zapadá do filozofie frameworku a přes URL view helper lze snadno vytvářet url pro použití ve view. Stačí při definici rout v bootstrapu definovat alternativní cestu pro jazykovou verzi
//default module project controller route $this->_router->addRoute('projectsRoute', new Zend_Controller_Router_Route(':lang/projects/:action/:name', array('controller'=>'project','action'=>'index','name'=>null)) );
Toto bude fungovat pokud bude označení jazyka v URL vždy přítomné. Pokud nebude přítomné, tak to nezafunguje ani pokud je v poli s parametry přednastaven lang
parametr. Pokud tedy chceme používat implicitní jazyk v url, tak je potřeba takovou routu explicitně definovat.
//default module project controller route - with the language switcher $this->_router->addRoute('projectsRoute', new Zend_Controller_Router_Route(':lang/projects/:action/:name', array('controller'=>'project','action'=>'index','name'=>null)) ); //default module project controller route - default language is cs $this->_router->addRoute('projectsRouteDefLang', new Zend_Controller_Router_Route('projects/:action/:name', array('lang'=>'cs','controller'=>'project','action'=>'index','name'=>null)) );
Problém je ten, že to musíte udělat pro každou Vámi definovanou routu. Pokud máte rout více (já jich mám okolo deseti), tak se Vám jejich počet zdvojnásobí, tím se pak i proces hledání správné cesty zhruba dvakrát zpomalí. Proto jsem toto řešení nepoužil.
Hrátky s URL
Ostatní řešení mají jedno společné, je potřebné z URL vypreparovat jazyk a takto upravené url pak vrátit dál do procesu jeho zpracování v routeru. Proto se dají tato řešení označit za tak trochu hack. Řešení se liší jen v tom, kde se uvedená akce provádí. Já jsem se rozhodl pro front controller plugin, protože ho už stejně používám. Přetížíme tedy metodu routeStartup()
.
class MainAppPlugin extends Zend_Controller_Plugin_Abstract { //============================================================================== /** * Plugin hook before front controller begins evaluating the request. * * Sets language and locale if necessary parameters are presented in the url. * This is a hack, because directly modifies the $request->pathInfo, which * is the used for matching routes. */ public function routeStartup(Zend_Controller_Request_Abstract $request) { //set host part for the base application URL AppBaseUrl::setHost($request->getServer("SERVER_NAME")); $app=HomeWebApp::getInstance(); //pathinfo = requestUri - baseUrl //e.g.: //whole URL = http://server_name/homepage/blog/myitem //requestUri = /homepage/blog/myitem, baseUrl=/homepage, pathInfo=/blog/myitem $pathInfo=$request->getPathInfo(); //check if lang identfier is in the pathinfo if (preg_match('#^/([a-z]{2}/|[a-z]{2}$)#',$pathInfo,$matches)) { if (HomeWebApp::DEBUG) $app->getLog()->info("lang matched in pathInfo"); $urlLang=rtrim($matches[1],'/'); //remove lang identifiers from the pathInfo and sets as new pathinfo $pathInfo = strlen($pathInfo)==3 ? "/" : substr($pathInfo,3); $request->setPathInfo($pathInfo); } else { //language isn't in the url, default lang "without url specification" is cs $urlLang="cs"; } //set application language $app->setLang($urlLang); //set language in all urls if ($urlLang!="cs") AppUrlEnum::getInstance()->setLang($urlLang); //set language in viewrenderer views $viewRenderer=Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); //set language $viewRenderer->view->currentLang=$app->getLocale()->getLanguage(); }
Je nutné si uvědomit, že Zend Framework používá pro vyhodnocování URL pathInfo
proměnnou v request
objektu. Takže ta nejdůležitější část za preg_match
se pokusí najít v pathInfo
označení jazyka, pokud tam je, tak ho z pathInfo
odstraní a takto osekané pathInfo
pak vrátí zpět do requestu
, kde na něm proběhne standardní vyhodnocení routování a následný dispatch. Pokud v pathInfo
nastavení jazyka není, nic se nedělá, jen se nastaví implicitní jazyk.
Metoda dále volá další metody pro nastavení locale a překladů a nastavuje jazyk pro view ve viewRendereru.
Protože na práci s URL používám speciální objekt, není těžké do něj přidat podporu pro jazyk. To má zásadní výhodu, není třeba na nic ostatního v aplikaci sahat a ke změně URL v celé aplikaci dojde automaticky.
Vše si můžete prohlédnout v aktuálním subversion
Komentáře (9)
Pěkný začátek, ptal jsem se na v podstatě tu samou věc na Frameworks I , ale odpověď "nende" se mi nelíbila :) Nicméně weby na ZF nestavím (zatím) a vlastně weby vůbec nestavím :P a kdyžtak mám vlastní controller/dispatcher, který toto, resp. to, co přijde poskytuje, tak jsem po tom dál nepátral.
Co by se mi moc líbilo, kdyby česká verze byla na
http://mirin.cz/blog/cs/nastaveni-jazyku-v-url-pluginem
anglická na
http://mirin.cz/en/blog/url-language-selection-using-plugin
a pak ruzné cviky jako třeba
http://mirin.cz/cs/blog/url-language-selection-using-plugin
přesměruje na
http://mirin.cz/en/blog/url-language-selection-using-plugin
a obráceně, bude fungovat lang-nego a přesměrování, pokud jazyk nebude v URL uveden apod. Toto v ZF by bylo naprosto dokonalé.
Ano, taky jsem nad tím kdysi uvažoval, asi by to i nad ZF také šlo udělat, ale na můj vkus je to taková ne moc důležitá věc s velkým množstvím práce. Každopádně ZF na takové věci nějakým přednastaveným chováním nemyslí.
Zaujal me 1. zpusob pouziti, skusil sem to tedy zabudovat do bootstrapu, ale nejak to furt nefunguje, vlastne to nic nedela.
Potreboval bych nasledujici url: www.nakejroot.cz/en/novinky
Controller se samozrejme muze menit, action je defaultne index. Jak mohu dosahnout pozadovaneho chovani?
já bych to tipnul na tyhle dvě routy
ta první by měla zareagovat na /en/novinky, /cs/novinky atd a ta druha na /novinky
Já jsem se nakonec v jednom projektu, kde naštěstí není SEO na prvním místě, rozhodl použít variantu s cookies + prvotní nastavení jazyka, který je nastaven v prohlížeči a zasílá se v každém http requestu (čili standardní autodetekce v Zend_Translate). Tzn. když člověk příjde na web poprvé, nastaví se mu jazyk automaticky (jako třeba na Googlu) a cookie, která má přednost, se vytvoří až po tom, co jazyk explicitně změnil. Minimálně o Googlu jsem se dozvěděl, že prochází stránky tak, že v hlavičce requestu zkouší různá nastavení jazyka a pokud zjístí, že se přitom určitá stránka změnila, tak web indexuje pro různé jazyky zvlášť. Jak jsou na tom jiné world wide vyhledávače nevím, co se týče těch lokálních, tak tam bude předpokládám samozřejmostí, že v hlavičce posílají ten správný preferovaný jazyk. Každopádně jsem zvědavý, co to udělá :)
Ahoj, mozna to moc dobre nechapu, ale vpripade, ze mi nevadi stal pritomnost jazyka (tedy i defaultniho) v url, stejne budu potrebovat pro kazdou stranku zvlastni routu?
nejde udelat? Jde mi oto udelat obecnou routu, psat tam konkretni controller a action mi prijde dost nesikovne.Neco jako
Narazil jsem na problem, ktery byl zmineny vyse. A to totiz na preklad i samotnyho url do zvoleneho jazyka. V bootstrapu mam routu, ktera mi zajistuje routovani podle jazyka a lokalizace je plne funkcni. Jakym zpusobem by se dalo jednoduse resit to aby v ceske verzi bylo url v cestine a v anglicke anglicky? Dalo by se to resit jen v samotnem bootstrapu?
[6] - podle mě ne, jen se do rout přidá ten parametr lang
[7] - co si tak pamatuju, tak se to musí dělat přes dvě routy, jedna pro
/projekty a druhá pro /projects, obě budou ukazovat na jeden controller a view a bude se lišit jen lang. Někde jsem zahlédl, že se snad v ZF připravuje řešení, které by tyhle scénáře podporovalo.
Už je to dost dlouho, co jsem se tím vším zabýval, tak snad to ještě platí, nějaké podrobné rady už ze mě asi nevypadnou.
No ale to je dost tezko realizovatelny. To by musela bejt routa na kazdou url pro kazdej jazyk. Takze treba 100 a vic.
Vzdal sem se snu mit url lokalizovane, ale kazdopadne diky za odpoved.
Komentáře jsou uzavřeny.