Translating route segments with ZF's gettext adapter - zend-framework

I want to try out the route translations in Zend Framework, but I'm using the gettext adapter and the most tutorials have PHP translate adapter, so I'm having problems to make it work.
In the main Bootstrap.php I have the method in which I set the routes:
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
$translator = Zend_Registry::get('Zend_Translate');
Zend_Controller_Router_Route::setDefaultTranslator($translator);
$routerRoute = new Zend_Controller_Router_Route('#about',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'about'
)
);
$router->addRoute('about', $routerRoute);
This works for /about path.
I'll paste the code in which I set Zend_Translate, but it basically loads a *.mo file depending on current session language:
$langParam = $this->getSessionLang();
$localeString = $languages[$langParam];
$locale = new Zend_Locale($localeString);
$moFilePath = $langFolder . $langParam . '.mo';
if (!file_exists($moFilePath)) {
throw new Zend_Exception('File does not exist: ' . $moFilePath);
}
$translate = new Zend_Translate(
array(
'adapter' => 'gettext',
'content' => $moFilePath,
'locale' => $locale,
'ignore' => array('.'), // ignore SVN folders
'disableNotices' => ('production' === APPLICATION_ENV) // disable notices in Production
)
);
Zend_Registry::set('Zend_Translate', $translate);
Zend_Registry::set('Zend_Locale' , $locale);
This, ofc, it's called prior to routing.
My question: can gettext be used as a translation adapter for a route, because I can't figure out how can I catch the #about string with, let's say, poEdit? It can? Hooray! How?

Well, my mo's were all messed up. So there's no problem with the code.
Here's how you edit your mo files so that the route can translate it (I'm presume you have your ZF i18n working - Translate, Locale and the like):
1. Remember this?
$routerRoute = new Zend_Controller_Router_Route('#about',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'about'
)
);
2. See that '#about' string? That's the soon-to-be-translated path. So how do you translate a string so that poEdit will catch it? Well, you don't; not with poEdit anyway. You manually edit the .po file:
#ro.po
msgid "about"
msgstr "despre"
#en.po
msgid "about"
msgstr "about"
The msgid should match your path string (minus the '#' string, ofc).
3. Now go open your po file with poEdit. You'll see a new string (surprised, huh?). Compile this po to get a new shiny mo file that ZF can use.
4. Now my site.com/about or site.com/despre paths work.
More info here.

Related

Using Zend translate in Bootstrap.php

I have several routes defined in Zend's Bootstrap.php that I want to translate with Zend's translate function:
$trans = new Zend_View_Helper_Translate();
$router->addRoute(
'myroute',
new Zend_Controller_Router_Route(':lang/'.$trans->translate('mytitle').'/',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'statistics'
)
)
);
The helper itself seems to work (doesn't throw an exception or error) but always returns mytitle instead of the actual translation which is defined in the language file (I checked - the language files work in the view).
How can I get the translate function to work in the Bootstrap.php file?
Remember that you needs to load translations before you use it.
Load routing translations like:
$router->setDefaultTranslator($yourTranslator);
Best how to use routing translations is adding '#' before a word you want to translate f.e.:
$router->addRoute(
'myroute',
new Zend_Controller_Router_Route(':lang/#mytitle/',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'statistics'
)
)
);

Zend Controller Router : Define variables to point to a different action in one controller

I'm just new with Zend and I have a little trouble with Zend Routers. I've searched about it, but nothing found...
I want to be able to define a router for each defined variable at uri level to point to a different action in one controller.
I'm working with lang and modules so I defined at bootstrap application the next initRoutes function:
protected function _initRoutes()
{
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
$defaultRoute = new Zend_Controller_Router_Route(
':lang/:module/:controller/:action',
array(
'lang' => 'es',
'module' => 'default',
'controller' => 'index',
'action' => 'index'
),
array(
'lang' => '^(en|es)$',
'module' => '^(default|admin)$'
)
);
$router->addRoute('defaultRoute', $defaultRoute);
return $router;
}
I want to be able to access forum sections and forum topics by their defined action.
Something like :
mydomain/forum -> forum/index
mydomain/forum/section -> forum/sectionAction
mydomain/forum/section/topic -> forum/topicAction
and also with the lang and module defined at uri level like :
mydomain/lang/module/forum
mydomain/lang/module/forum/section
mydomain/lang/module/forum/section/topic
So I have this :
class ForumController extends Zend_Controller_Action
{
public function indexAction()
{
}
public function sectionAction()
{
}
public function topicAction()
{
}
Then I created the next routes inside the Default_Bootstrap :
$forumRoutes = new Zend_Controller_Router_Route(
':lang/:module/forum',
array(
'lang' => 'es',
'module' => 'default',
'controller' => 'forum',
'action' => 'index'
)
);
$sectionRoutes = new Zend_Controller_Router_Route(
':lang/:module/forum/:section',
array(
'lang' => 'es',
'module' => 'default',
'controller' => 'forum',
'action' => 'section',
'section' => ''
)
);
$topic = new Zend_Controller_Router_Route(
':lang/:module/forum/:section/:topic',
array(
'lang' => 'es',
'module' => 'default',
'controller' => 'forum',
'action' => 'topic',
'section' => '',
'topic' => ''
)
);
$router->addRoute('forumTopics', $topic);
$router->addRoute('forumSections', $section);
$router->addRoute('forum', $forumRoutes);
Now, this only works if I define the lang and module at uri level, but doesn't work if I defined like => mydomain/forum/section | section/topic. This also brings me another problem with my navigation->menu. If I define "forum" as a static variable at router definition, when I hover over at any label defined at navigatoin.xml, the uri level have the same value for every one of them.
I've tried to make a chain like this:
$forumRoutes = new Zend_Controller_Router_Route(
':lang/:module/forum',
array(
'lang' => 'es',
'module' => 'default',
'controller' => 'forum',
'action' => 'index'
)
);
$section = new Zend_Controller_Router_Route(
':section',
array(
'action' => 'section',
'section' => ''
)
)
$topic = new Zend_Controller_Router_Route(
':topic',
array(
'action' => 'topic',
'topic' => ''
)
)
$chainedRoute = new Zend_Controller_Router_Route_Chain();
$chainedRoute->chain($topic)
->chain($section)
->chain($forumRoutes);
$router->addRoute($chainedRoute);
But this doesn't work as I expected.
Any help would be appreciated, thanks.
You are new to Zend. You said it. So here are some explanations:
Ideally the URL on Zend application is:
example.com/controller/action/param-name/:param-value
So in which case, if there is an action called Edit under UsersController, it will be:
example.com/users/edit
if action is add, it will be :
example.com/users/add
when you specify first parameter as a variable, it will collide with controller requests. Example: if you say controller is User but first parameter accepts a value and puts it in emplyees then a request as example.com/employees and example.com/user will both point towards employees controller even if the usercontroller exists! which again is a theory!
What you might want to do is, leave the routes to only accept dynamic values rather routing! You do not want users to route your application but, route user to different sections of the application.
About language then you need to use Zend_Locale which will check for HTML language that is
<html lang="nl"> or <html lang = "en">
Hope things are clear! :)
Here's a quick example which should help you work with routes like that in ZF.
If you are using a default project structure like the one you get when starting a new project using Zend Tool go into the Application folder.
In your Bootstrap.php set up something like this:
protected function _initRouteBind() {
// get the front controller and get the router
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
// add each custom route like this giving them a descriptive name
$router->addRoute(
'addTheDescriptiveRouteNameHere',
new Zend_Controller_Router_Route(
'/:controller/:id/:action/:somevar',
array(
'module' => 'default',
'controller' => ':controller',
'action' => ':action',
'id' => ':id',
'somevar' => ':somevar'
)
)
);
}
My example above is just to illustrate how you would use a route where the controller, the action and a couple of parameters are set in the url.
The controller and action should be resolved without any additional work however to get the 'id' or 'somevar' value you can do this in your controller:
public function yourAction()
{
// How you get the parameters to pass in to a function
$id = $this->getRequest()->getParam('id');
$somevar = $this->getRequest()->getParam('somevar');
// Using the parameters
$dataYouWant = $this->yourAmazingMethod($id);
$somethingElse = $this->yourOtherAmazingMethod($somevar);
// Assign results to the view
$this->view->data = $dataYouWant;
$this->view->something = $somethingElse;
}
So while you will want to make sure your methods handle the parameters being passed in with care (after all it is user supplied info) this is the principle behind making use of the route parameter binding. You can of course do things like '/site' as the route and have it direct to a CMS module or controller, then another for '/site/:id' where 'id' is a page identifier.
Also just to say a nice alternative to:
$id = $this->getRequest()->getParam('id');
$somevar = $this->getRequest()->getParam('somevar');
which wasn't that nice itself anyway since it was assuming a parameter was going to be passed, using shorthand conditional statement in conjunction with action helpers is:
$id = ($this->_hasParam('id')) ? $this->_getParam('id') : null;
$somevar = ($this->_hasParam('somevar')) ? $this->_getParam('somevar') : null;
If you are not familiar with this, the
($this->_hasParam('id'))
is the conditional test which if true assigns the value of whatever is on the left of the ':' and which if false assigns the value of whatever is on the right of the ':'.
So if true the value assigned would be
$this->_getParam('id')
and null if false.
Does that help? :-D

Zend Routing problems

I've read all posts about routing and Zend Documentation but I still can't solve this issue.
I have a multi-language application with two modules: default and admin. The language selection is working fine (in a Controller routeShutdown Plugin), but I have some problems configuring the router:
I want to have these URL working:
/
/controller
/controller/action
/action (default controller)
/controller/param (default action)
/admin
/admin/admin-controller
/admin/admin-controller/action
and using the language selector it would be:
/en
/en/controller
/en/controller/action
/en/action (default controller)
/en/controller/param (default action)
/en/admin/admin-controller
/en/admin/admin-controller/action
I added this to my bootstap file (index.php):
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$router->removeDefaultRoutes();
$router->addRoute('langmodcontrolleraction',
new Zend_Controller_Router_Route('/:lang/:module/:controller/:action',
array('lang' => ':lang'))
);
$router->addRoute('langmodcontroller',
new Zend_Controller_Router_Route('/:lang/:module/:controller',
array('lang' => ':lang',
'action' => 'index'))
);
$router->addRoute('langmod',
new Zend_Controller_Router_Route('/:lang/:module',
array('lang' => ':lang',
'action' => 'index',
'controller' => 'index'))
);
$router->addRoute('lang',
new Zend_Controller_Router_Route('/:lang',
array('lang' => ':lang',
'action' => 'index',
'controller' => 'index',
'module' => 'default'))
);
$frontController->setControllerDirectory(array(
'default'=>BASE_PATH.'app/modules/default/controllers',
'admin'=>BASE_PATH.'app/modules/admin/controllers'));
In order to check how the router is parsing the URL, I added a var_dump to the routeShutdown plugin:
Entering to /en, I get:
array
'lang' => string 'en' (length=2)
'action' => string 'index' (length=5)
'controller' => string 'index' (length=5)
'module' => string 'default' (length=7)
which is OK. But when I enter to /en/controller1 I get:
array
'lang' => string 'en' (length=2)
'module' => string 'controller1' (length=8)
'action' => string 'index' (length=5)
'controller' => string 'index' (length=5)
It is setting module to "controller1". How can I tell the router to set the default value to the module? And for an URL like /en/controller/param? (setting module and action to default)
I'm afraid you're going to need to rethink your URL scheme a little, or change the way your routes are setup, as you've hit two limitations of the way ZF's routing works.
The first is that the router has no knowledge of what is or isn't a valid module, controller or action; all it does is match the strings in the URL to variables in the route. It does this by checking each route in succession, in reverse order, until it finds a match. When you hit /en/controller, it first checks your /:lang route, which won't match. It then checks /:lang/:module, which will match, because /:lang/:module will match /anything/anything unless you tell it otherwise.
With that in mind you won't be able to have both:
/en/controller
/en/action
unless you set some restrictions, as a URL like /en/foo will always be matched by whichever of the two you define last.
If you have a fairly small number of actions/controllers that don't often change, the simplest way around this is to hardcode in some possible values for the 2nd of the two routes, e.g.:
$router->addRoute('langmod', new Zend_Controller_Router_Route(
'/:lang/:module',
array(
'lang' => ':lang',
'action' => 'index',
'controller' => 'index'
),
array(
'module' => '(foo|bar|something)'
)
));
replace foo, bar etc. with valid module names. Now when you hit /en/controller1 it won't match this route because controller1 doesn't match the regexp pattern defined for the :module variable. You would then need a separate /:lang/:controller route (or possibly /:lang/:controller/:action) for it to match instead.
You asked how you set a default value for some of the variables. You are actually already doing this with the action in a few of your routes, but for controller/module won't quite work in the way you are hoping. If we take your langmodcontroller route and change it to this:
$router->addRoute('langmodcontroller',new Zend_Controller_Router_Route(
'/:lang/:module/:controller',
array(
'lang' => ':lang',
'controller' => 'index'
'action' => 'index'
)
));
there's now a default value for the controller variable. If we pretend for a second that this was the only route, a request for /en/blog would now get matched by this and set the request params to lang = en, module = blog, controller = index, action = index. /en/blog/index/foo would also match this route, and would give you module = blog, controller = index, action = foo. But note that even though controller = index you still need that in the URL. So limitation number two is that you always need the variable in the URL (even if it is set to your default) as long as you have something after it that isn't the default.
With these limitations in mind I'd suggest you go with something like this (defined in this order):
/:lang/:controller/:action/ (with 'index' defaults for controller and action)
/:lang/:action (with 'action' restricted to some predefined values)
/:lang/admin/:controller/:action (with 'admin' as a string in the URL, and :module set to 'admin' as the default)
This would give you URLs like this:
/en
/en/controller
/en/controller/action
/en/action
/en/controller/param
/en/admin/controller
/en/admin/controller/action
which is pretty much what you are after.
The routing in ZF is very powerful, you just need to know its quirks.

Zend Router chaining problem with subdomain

I have a $siteRoute for subdomains:
$siteRoute = new Zend_Controller_Router_Route_Hostname(
':siteSlug.test.com',
array(
'module' => 'site',
),
array('siteSlug' => '^(programming|photography|gaming)$')
);
$router->addRoute('site', $siteRoute);
and i have $questionRoute for questions' fancy
$questionRoute = new Zend_Controller_Router_Route(
'questions/:questionId/:questionSlug',
array(
'controller' => 'question',
'action' => 'view',
'questionSlug' => ''
)
);
$router->addRoute('question', $siteRoute->chain($questionRoute));
all these two routes are matching without any problems. for example:
programming.test.com matches and dispatches for site route and programming.test.com/questions/132/test-headline matches for question route.
But when i assemble a new url with Zend_View's url helper or Zend_Router's assemble function for question route, it returns just the path, not domain like:
echo $questionRoute->assemble(array('questionId' => 1, 'questionSlug' => 'testing-testing', 'siteSlug' => 'programming'));
echoes questions/1/testing-testing not programming.test.com/questions/1/testing-testing.
How can i do this?
Try this piece of code, works fine for me (if u need more example tell me)
$router = Zend_Controller_Front::getInstance()->getRouter();
echo $router->assemble(array('questionId' => 1, 'questionSlug' => 'testing-testing', 'siteSlug' => 'programming'), 'question');
The router is only concerned about routing the path, i.e. take the path and match it to your modules, controllers and actions. It has basically no awareness of the domain. Assemble only creates the route (path) based on your arguments.
echo $_SERVER['HTTP_HOST'] . '/' . $questionRoute->assemble(...);

No translation for the language - Works on development server but not in production

While translation works fine on the development server we get the following notice on the production server: No translation for the language 'fr' available.
Here is the translation configuration in the bootstrap (forcing the locale for the test) :
$locale = "fr_CA.utf8";
$translate = new Zend_Translate(
array(
'adapter'=>'gettext',
'content' => APPLICATION_PATH . '/lang',
'locale' => $locale,
'scan' => Zend_Translate::LOCALE_DIRECTORY,
'disableNotices' => false,
'clear' =>true,
'reload'=>true,
)
);
The .mo file is in APPLICATION_PATH/lang/fr_CA.utf8/LC_MESSAGES/messages.mo
There are translated strings in the .mo file and the locale exists on both servers, according to "locale -a".
Any clue as to why such a setup could work on one server and not the other?
EDIT :
I got it to work with the following configuration :
$translate = new Zend_Translate(
array(
'adapter'=>'gettext',
'content' => APPLICATION_PATH.'/lang/'.$locale.'/LC_MESSAGES/messages.mo',
'locale' => $locale,
'disableNotices' => true,
'clear' =>true,
'reload'=>true,
)
);
It seems like the scanning was not working.
I had a similar problem (using the array adapter)
Reason: production site webroot path contains hidden directory /home/.sites/path/to/my/webroot/
// Settings:
$locale = new Zend_Locale('browser');
$language = $locale->getLanguage();
// Solution: added option 'ignore' => '===' to override
// default $_options settings in Zend_Translate_Adapter
$translate = new Zend_Translate(array(
'adapter' => 'array',
'content' => APPLICATION_PATH . '/languages/' . $language,
'scan' => Zend_Translate::LOCALE_DIRECTORY,
'locale' => $locale,
'ignore' => '===', // override default '.'
));
I had a similar problem but using application.ini to configure translation.
These were the Zend_Translate related lines:
resources.translate.adapter = "gettext"
resources.translate.content = APPLICATION_PATH "/languages"
resources.translate.options.scan = 'directory'
This worked fine on our development server but not on our staging server. We had to remove the quotes from the scan option:
resources.translate.options.scan = directory
Without quotes it worked. But I have no idea why this particular config line can't handle quotes on our staging server.