Как сделать локаль "липкой" во время сессии пользователя

Как сделать локаль "липкой" во время сессии пользователя

Symfony сохраняет настройку локали в запросе, что означает, что эта же настройка не сохраняется автоматически ("липко") для всех запросов. Но вы можете сохранять локаль в сессии так, чтобы она была использована в последующих запросах.

Создание LocaleSubscriber

Создайте нового подписчика событий. Обычно, _locale используется в качестве параметра маршрутизации, чтобы обозначить локаль, хотя вы можете определять правильную локаль так, как вам хочется:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// src/AppBundle/EventSubscriber/LocaleSubscriber.php
namespace AppBundle\EventSubscriber;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class LocaleSubscriber implements EventSubscriberInterface
{
    private $defaultLocale;

    public function __construct($defaultLocale = 'en')
    {
        $this->defaultLocale = $defaultLocale;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        if (!$request->hasPreviousSession()) {
            return;
        }

        // попробуйте увидеть, была ли локаль установлена как параметр маршрутизации _locale
        if ($locale = $request->attributes->get('_locale')) {
            $request->getSession()->set('_locale', $locale);
        } else {
            // если для этого запроса не было ясно установлено никакой локали, используйте её из сесии
            $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
        }
    }

    public static function getSubscribedEvents()
    {
        return array(
            // должен быть зарегистрирован после слушателя локали по умолчанию
            KernelEvents::REQUEST => array(array('onKernelRequest', 15)),
        );
    }
}

Если вы используете конфигурацию default services.yml, то вы закончили! Symfony автоматически узнает о подписчике событий и вызовет метод onKernelRequest для каждого запроса.

Чтобы увидеть, как это работает, либо установите ключ _locale в сессии вручную (например, через маршрут и контроллер "Изменить локаль"), или создайте маршрут с _locale по умолчанию.

Вы также можете сконфигурировать его ясно, чтобы передать в default_locale:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
services:
    # ...

    AppBundle\EventSubscriber\LocaleSubscriber:
        arguments: ['%kernel.default_locale%']
        # излишне, если вы используете автоконфигурацию
        tags: [kernel.event_subscriber]

Вот и всё! Теперь отпразднуйте, изменив локаль пользователя и увидев, что она "прилипает" во всём запросе.

Помните, чтобы получить локаль пользователя, всегда используйте метод Request::getLocale:

1
2
3
4
5
6
7
// from a controller...
use Symfony\Component\HttpFoundation\Request;

public function indexAction(Request $request)
{
    $locale = $request->getLocale();
}

Установка локали, основываясь на предпочтениях пользователя

Вы можете захотеть улучшить эту технику и обозначить локаль, основываясь на сущности пользователя, выполнившего вход. Однако, так как LocaleSubscriber вызывается перед FirewallListener, который отвечает за управление аутентификацией и установку метки пользователя в TokenStorage, у вас нет доступа к пользователю, выполнившему вход.

Представьте, что у вас есть свойство locale в вашей сущности User и вы хотите использовать это в качестве локали данного пользователя. Чтобы сделать это, вы можете подключиться к процессу выполнения входа и обновить сессию пользователя с этим значением локали до того, как он будет перенаправлен на свою первую страницу.

Чтобы сделать это, вам понадобится подписчик событий в событии security.interactive_login:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// src/AppBundle/EventSubscriber/UserLocaleSubscriber.php
namespace AppBundle\EventSubscriber;

use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Сохраняет локаль пользователя в сессии после выполнения входа.
 * Это может быть использовано LocaleSubscriber позднее.
 */
class UserLocaleSubscriber implements EventSubscriberInterface
{
    private $session;

    public function __construct(SessionInterface $session)
    {
        $this->session = $session;
    }

    /**
     * @param InteractiveLoginEvent $event
     */
    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();

        if (null !== $user->getLocale()) {
            $this->session->set('_locale', $user->getLocale());
        }
    }
}

Если вы используете конфигурацию services.yml по умолчанию, то вы закончили! Symfony автоматически узнает о подписчике событий и передаст его в ваше сервис session. Теперь, когда вы будете выполняь вход, локаль пользователя будет установлена в сессии.

Caution

Для того, чтобы обновить язык сразу же после того, как пользователь изменил языковые предпочтения, вам также понадобится обновить сессию, когда вы будете изменять сущность User.