Search code examples
phplocalizationzend-framework2zend-translate

What's the best option to have language switcher in my ZF2 system?


I have developed small ZF2 doctrine system for one of my client. All is good so far, but they require the system in 2 languages.

I can set default language as english or another language from my module/Application/config/module.config.php

'translator' => array(
    'locale' => 'en_US',
    'translation_file_patterns' => array(
        array(
            'type'     => 'gettext',
            'base_dir' => __DIR__ . '/../language',
            'pattern'  => '%s.mo',
        ),
    ),
),

And I also can use this method to set default in module.php

public function onBootstrap(MvcEvent $e)
{
    $application = $e->getApplication();
    $serviceManager = $application->getServiceManager();
    // Just a call to the translator, nothing special!
    $serviceManager->get('translator');
    $this->initTranslator($e);

    // Etc, more of your bootstrap function.
}

protected function initTranslator(MvcEvent $event)
{
    $serviceManager = $event->getApplication()->getServiceManager();

    // Zend\Session\Container
    $session = New Container('language');

    $translator = $serviceManager->get('translator');
    $translator
        ->setLocale($session->language)
        ->setFallbackLocale('zh_CN')
        ;


}

This is good, now my system shows default language as Chinese. However, I would like to give option to users to choose from.

How do I do that?

I had found this link but I couldn't get it work.

When I add following function in Application/IndexController.php it doesn't do anything instead http://myurl.com/changeLocal throw 404 error. Do I need to add anything in module.config.php too?

public function changeLocaleAction() 
{
    // New Container will get he Language Session if the SessionManager already knows the language session.
    $session = new Container('language');
    $language = $request->getPost()->language;
    $config = $this->serviceLocator->get('config');
    if (isset($config['locale']['available'][$language])) {
        $session->language = $language;
        $this->serviceLocator->get('translator')->setLocale($session->language);
    }
}

Solution

  • After long battle, here is how I have achieved this. Special thanks to @Kwido and @Wilt for sending me to right direction (I have voted both of them up).

    Application/config/module.config.php

    return array(
        'controllers' => array(
            'invokables' => array(
                'Application\Controller\Index' => 'Application\Controller\IndexController'
            ),
        ),
        'router' => array(
            'routes' => array(
                'home' => array(
                    'type' => 'Zend\Mvc\Router\Http\Literal',
                    'options' => array(
                        'route'    => '/',
                        'defaults' => array(
                            'controller' => 'Application\Controller\Index',
                            'action'     => 'index',
                        ),
                    ),
                ),
    
                'language' => array(
                    'type' => 'Segment',
                    'options' => array(
                        //'route' => '/[:language]',
                        'route' => '/en',
                        'constraints' => array(
                            'language' => 'en',
                        ),
                        'defaults' => array(
                            'controller' => 'Application\Controller\Index',
                            'action' => 'changeLocaleEnglish'
                        )
                    )
                ),
                'languageChinese' => array(
                    'type' => 'Segment',
                    'options' => array(
                        //'route' => '/[:language]',
                        'route' => '/cn',
                        'constraints' => array(
                            'language' => 'cn',
                        ),
                        'defaults' => array(
                            'controller' => 'Application\Controller\Index',
                            'action' => 'changeLocaleChinese'
                        )
                    )
                ),
                ////
                // other stuff
                //////////// like child routes etc
            ),
        ),
        'service_manager' => array(
            'abstract_factories' => array(
                'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
                'Zend\Log\LoggerAbstractServiceFactory',
            ),
            'factories' => array(
                'translator' => 'Zend\Mvc\Service\TranslatorServiceFactory',
            ),
        ),
    
        'translator' => array(
            'locale' => 'zh_CN', //default is english which is set in module.php
            'translation_file_patterns' => array(
                array(
                    'type'     => 'gettext',
                    'base_dir' => __DIR__ . '/../language',
                    'pattern'  => '%s.mo',
                ),
            ),
        ),
    
        'view_helpers' => array(
            'invokables'=> array(
                'PaginationHelper' => 'Application\View\Helper\PaginationHelper'
            )
        ),
    
        'view_manager' => array(
            //....... view stuff
        ),
        // Placeholder for console routes
        'console' => array(
            'router' => array(
                'routes' => array(
                ),
            ),
        ),
    );
    

    Application/module.php

    public function onBootstrap(MvcEvent $e)
    {
        $application = $e->getApplication();
        $serviceManager = $application->getServiceManager();
        // Just a call to the translator, nothing special!
        $serviceManager->get('translator');
        $this->initTranslator($e);
    }
    
    protected function initTranslator(MvcEvent $event)
    {
        $serviceManager = $event->getApplication()->getServiceManager();
    
        // use Zend\Session\Container add this to top
        $session = New Container('language');
    
        $translator = $serviceManager->get('translator');
        if($session['language'] != 'zh_CN'){ //if session doesn't have zh_CN then set it as english
        $translator
            ->setLocale($session->language)
            ->setFallbackLocale('en_US')
            ;
        }
    }
    

    now in Application/src/Application/Controller/IndexController.php I have added two action to catch the session and set the language:

    public function changeLocaleChineseAction() 
        {
            $result = new ViewModel();
            $result->setTerminal(true);
            $response = $this->getResponse();
    
    
            // New Container will get he Language Session if the SessionManager already knows the language session.
            $session = new Container('language');
            $request = $this->getRequest();
            $config = $this->serviceLocator->get('config');
    
            $language = $config['translator']['locale']; //default locale from Application/config/module.config.php
            if (isset($config['translator']['locale'])) {
                $session->language = $language;
                $this->serviceLocator->get('translator')->setLocale('zh_CN')
                    ->setFallbackLocale('zh_CN')
                    ;
    
            }
    
            return $this->redirect()->toRoute('home'); 
    
        }
    
        public function changeLocaleEnglishAction() 
        {
            // New Container will get he Language Session if the SessionManager already knows the language session.
            $session = new Container('language');
    
            //just clear the language session
            $session->getManager()->getStorage()->clear('language');
            $language = 'en_US'; //set new language
            $request = $this->getRequest();
            $config = $this->serviceLocator->get('config');
            $session->language = $language;
            $this->serviceLocator->get('translator')->setLocale('en_US')
                ->setFallbackLocale('en_US')
                ;
            return $this->redirect()->toRoute('home');
    
        }
    

    Now just add the link in layout.phtml to have language switcher:

    <a href="<?php echo $this->url('home')."cn";?>"><?php echo $this->translate("Chinese");?></a>
    <a href="<?php echo $this->url('home')."en";?>"><?php echo $this->translate("English");?></a>
    

    Hope this helps others in future.