Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 locale detection: not considering _locale in session

I'm trying to implement a LocaleListener that detects user's preferred language (considering Accept-Language header) and stores it in session to avoid checking it every request. I've developed the code below to accomplish this:

public function onKernelRequest(GetResponseEvent $event) {
    $request = $event->getRequest();

    if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
        return;
    }

    $preferredLocale = $request->getPreferredLanguage($this->availableLocales);

    if ($this->container->has('session')) {
        $session = $this->container->get('session');
        if (!$session->has('_locale')) {
            $session->set('_locale', $preferredLocale);
        }
    } else {
        $request->setLocale($preferredLocale);
    }
}

The code is working, the preferred language is being stored in session, but symfony isn't considering the locale stored in session to translate strings. In my case, my preferred language was 'pt_BR' and when I escape:

{{ app.request.locale }}

symfony is escaping 'en'. Shouldn't symfony be considering the value stored in session('_locale') to define request locale? Is this a correct behavior? How can I accomplish that?

like image 851
Hugo Nogueira Avatar asked May 03 '13 02:05

Hugo Nogueira


2 Answers

Here is a working language listener. the second method is to change the language to the users preferences, which the user chooses. You can omit this method, if your user haven't the facility to define their language.

<?php

namespace Acme\UserBundle\EventListener;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class LanguageListener
{
    private $session;

    public function setSession(Session $session)
    {
        $this->session = $session;
    }

    /**
     * kernel.request event. If a guest user doesn't have an opened session, locale is equal to
     * "undefined" as configured by default in parameters.ini. If so, set as a locale the user's
     * preferred language.
     *
     * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
     */
    public function setLocaleForUnauthenticatedUser(GetResponseEvent $event)
    {
        if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
            return;
        }
        $request = $event->getRequest();
        if ('undefined' == $request->getLocale()) {
            if ($locale = $request->getSession()->get('_locale')) {
                $request->setLocale($locale);
            } else {
                $request->setLocale($request->getPreferredLanguage());
            }
        }
    }

    /**
     * security.interactive_login event. If a user chose a language in preferences, it would be set,
     * if not, a locale that was set by setLocaleForUnauthenticatedUser remains.
     *
     * @param \Symfony\Component\Security\Http\Event\InteractiveLoginEvent $event
     */
    public function setLocaleForAuthenticatedUser(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();

        if ($lang = $user->getLanguage()) {
            $this->session->set('_locale', $lang);
        }
    }
}

in your services.yml:

services:
    acme.language.interactive_login_listener:
        class: Acme\UserBundle\EventListener\LanguageListener
        calls:
            - [ setSession, [@session] ]
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: setLocaleForAuthenticatedUser }

    acme.language.kernel_request_listener:
        class: Acme\UserBundle\EventListener\LanguageListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: setLocaleForUnauthenticatedUser }

Oh, and you have to define an undefined fallback_language in config.yml to get it work.

framework:
    translator:      { fallback: "undefined" }
    default_locale:  "en"
like image 181
Emii Khaos Avatar answered Oct 25 '22 01:10

Emii Khaos


As of symfony 2.1, the locale is not stored in the session, but in the request. What you can do to solve it:

  • restore the old way of saving the locale. You can read how to do this in the upgrade file
  • edit the LocaleListener to store the locale in the request:

    if (!$request->attributes->has('locale')) {
        $request->setLocale(...);
    }
    
like image 43
Wouter J Avatar answered Oct 25 '22 01:10

Wouter J