I am trying to implement URL based translation in Zend Framework so that my site is SEO friendly. This means that I want URLs like the below in addition to the default routes.
zend.local/en/module
zend.local/en/controller
zend.local/en/module/controller
zend.local/en/controller/action
The above are the ones I have problems with right now; the rest should be OK. I have added a controller plugin that fetches a lang parameter so that I can set the locale and translation object in the preDispatch method. Here are some of my routes (stored in a .ini file):
; Language + module
; Language + controller
resources.router.routes.lang1.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.lang1.route = "(^[a-zA-Z]{2})/(\w+$)"
resources.router.routes.lang1.defaults.controller = index
resources.router.routes.lang1.defaults.action = index
resources.router.routes.lang1.map.1 = "lang"
resources.router.routes.lang1.map.2 = "module"
; Language + module + controller
; Language + controller + action
resources.router.routes.lang2.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.lang2.route = "(^[a-zA-Z]{2})/(\w+)/(\w+$)"
resources.router.routes.lang2.defaults.module = default
resources.router.routes.lang2.defaults.action = index
resources.router.routes.lang2.map.1 = "lang"
resources.router.routes.lang2.map.2 = "controller"
resources.router.routes.lang2.map.3 = "action"
As the comments indicate, several URL structures will match the same route, which makes my application interpret the format incorrectly. For instance, the following two URLs will be matched by the lang1 route:
zend.local/en/mymodule
zend.local/en/mycontroller
In the first URL, “mymodule” is used as module name, which is correct. However, in the second URL, “mycontroller” is used as module name, which is not what I want. Here I want it to use the “default” module and “mycontroller” as controller. The same applies for the previous lang2 route. So I don’t know how to distinguish between if the URL is of the structure /en/module or /en/controller.
To fix this, I experimented with the code below in my controller plugin.
// Get module names as array
$dirs = Zend_Controller_Front::getInstance()->getControllerDirectory();
$modules = array_keys($dirs);
// Module variable contains a module that does not exist
if (!in_array($request->getModuleName(), $modules)) {
// Try to use it as controller name instead
$request->setControllerName($request->getModuleName());
$request->setModuleName('default');
}
This works fine in the scenarios I tested, but then I would have to do something similar to make the lang2 route work (which possibly involves scanning directories to get the list of controllers). This just seems like a poor solution, so if it is possible, I would love to accomplish all of this with routes only (or simple code that is not so “hacky”). I could also make routes for every time I want /en/controller, for instance, but that is a compromise that I would rather not go with. So, if anyone knows how to solve this, or know of another approach to accomplish the same thing, I am all ears!
I’ve reproduced your problem here and come out with the following (not using config files though):
Router
Plug-in
Basically, there’s the route chain for applying the language settings within the module default route. As a fix, we ensure that the URI will contain the language if the user left it out.
You’ll need to adapt it, as I’m using the
Zend_Registry::get('Zend_Locale')andZend_Registry::get('Zend_Translate'). Change it to the actual keys on your app.As for the lang route:
[a-z]{2}_?([a-z]{2})?it will allow languages like mine:pt_BRLet me know if it worked for you.