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

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

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
    1
    2
    3
    4
    5
    6
    7
    services:
        # ...
    
        AppBundle\EventSubscriber\LocaleSubscriber:
            arguments: ['%kernel.default_locale%']
            # излишне, если вы используете автоконфигурацию
            tags: [kernel.event_subscriber]
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="AppBundle\EventSubscriber\LocaleSubscriber">
                <argument>%kernel.default_locale%</argument>
    
                <tag name="kernel.event_subscriber" />
            </service>
        </services>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    use AppBundle\EventSubscriber\LocaleSubscriber;
    
    $container->register(LocaleSubscriber::class)
        ->addArgument('%kernel.default_locale%')
        ->addTag('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.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.