Chaining language with default route

There are several ways how to include language id in default route of Zend Framework. However, generally you end up with the solution not quite elegant and likely not totally trouble-free. I have seen people overwriting the default route by new one which mimics module route with additional language id. There is no need to throw the default module route away to do this. To get it right chain the plain language route with default route.

// application/Bootstrap.php

protected function _initRoutes() {
    $locale = $this->getResource('locale');

    // Create route with language id (lang)
    $routeLang = new Zend_Controller_Router_Route(
            'lang' => $locale->getLanguage()
        array('lang' => '[a-z]{2}')

    // Now get router from front controller
    $front  = $this->getResource('frontcontroller');
    $router = $front->getRouter();

    // Instantiate default module route
    $routeDefault = new Zend_Controller_Router_Route_Module(
    // Chain it with language route
    $routeLangDefault = $routeLang->chain($routeDefault);

    // Add both language route chained with default route and
    // plain language route
    $router->addRoute('default', $routeLangDefault);
    $router->addRoute('lang', $routeLang);

    // Register plugin to handle language changes
    $front->registerPlugin(new My_Controller_Plugin_Language());

I think that the code above has enough comments to clarify it, so lets forward to the controller plugin. If you are not familiar with chaining routes, refer to reference guide Zend_Controller_Router_Route_Chain.

In Language controller plugin we have to take care of actions, which have to be done if language has been changed. Setting the language id as a global router parameter is the most important. The another common task is to check, if there is available translation for the selected language.

// library/My/Controller/Plugin/Language.php

class My_Controller_Plugin_Language
    extends Zend_Controller_Plugin_Abstract
    public function routeShutdown(Zend_Controller_Request_Abstract $request)
        $lang = $request->getParam('lang', null);

        $translate = Zend_Registry::get('Zend_Translate');

        // Change language if available
        if ($translate->isAvailable($lang)) {
        } else {
            // Otherwise get default language
            $locale = $translate->getLocale();
            if ($locale instanceof Zend_Locale) {
                $lang = $locale->getLanguage();
            } else {
                $lang = $locale;

        // Set language to global param so that our language route can
        // fetch it nicely.
        $front = Zend_Controller_Front::getInstance();
        $router = $front->getRouter();
        $router->setGlobalParam('lang', $lang);

In our Language plugin, we check if user selected language is available. In spite of its availability, we have valid language id in $lang, which is set as a global param. To achieve a better user experience you may want to store the user selected language in to session to be retrieved on later visits.

Update (2011-03-08): Updated the plugin to change the method “routeStartup” to “routeShutdown”, as the intention is to update the route with the default language when unmatched.