symfony 2根据存储在DB中的用户首选项设置区域设置


symfony 2 set locale based on user preferences stored in DB

我试图根据存储在DB中的当前用户的首选项设置区域设置。

我们的User类因此有一个getPreferredLanguage,它返回一个地区标识('en', 'fr_FR'等)。

我考虑过以下方法:

  • 注册一个订阅KernelEvents::REQUEST事件的"locale"侦听器服务。
  • 这个服务可以访问安全上下文(通过它的构造函数)
  • 这个服务的onKernelRequest方法尝试从安全上下文中获取用户,获取用户的首选语言环境,并将其设置为请求的语言环境。

不幸的是,这不起作用。当调用"locale"侦听器服务的onRequestEvent方法时,安全上下文没有令牌。似乎上下文侦听器是在非常晚的阶段调用的(优先级为0),并且不可能告诉我的"locale"侦听器在安全上下文之前运行。

有谁知道如何修复这个方法,或建议另一个?

您可能对语言环境侦听器感兴趣,我在这个答案中发布了:Symfony2语言环境检测:不考虑会话中的_locale

编辑:如果用户在个人资料中更改了他的语言,没有问题。如果你正在使用FOSUserBundle (master),你可以钩入配置文件编辑成功事件。否则在你的配置文件控制器中,如果你使用的是自制系统。下面是FOSUserBundle的一个例子:
<?php
namespace Acme'UserBundle'EventListener;
use FOS'UserBundle'Event'FormEvent;
use FOS'UserBundle'FOSUserEvents;
use Symfony'Component'EventDispatcher'EventSubscriberInterface;
class ChangeLanguageListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
                FOSUserEvents::PROFILE_EDIT_SUCCESS => 'onProfileEditSuccess',
        );
    }
    public function onProfileEditSuccess(FormEvent $event)
    {
        $request = $event->getRequest();
        $session = $request->getSession();
        $form = $event->getForm();
        $user = $form->getData();
        $lang = $user->getLanguage();
        $session->set('_locale', $lang);
        $request->setLocale($lang);
    }
}

和services.yml

services:
    acme.change_language:
        class: Acme'UserBundle'EventListener'ChangeLanguageListener
        tags:
            - { name: kernel.event_subscriber }
对于多个浏览器中的多个会话来说,

是没有问题的,因为每个新的会话都需要一个新的登录。嗯,好的,在改变语言之后就不会了,因为只有当前会话才会被更新。但是你可以修改languagellistener来支持这个。
如果管理员更改了语言,这种情况应该是微不足道的。

如果你通过谷歌找到这个答案,我目前正在使用这个解决方案。

    <?php
namespace App'EventSubscriber;
use Symfony'Component'EventDispatcher'EventSubscriberInterface;
use Symfony'Component'HttpKernel'KernelEvents;
use Symfony'Component'HttpKernel'Event'ControllerEvent;
use Symfony'Component'Security'Core'Security;
use Symfony'Contracts'Translation'TranslatorInterface;
class SetLocaleEventSubscriber implements EventSubscriberInterface
{
    private Security $security;
    private TranslatorInterface $translator;
    public function __construct(Security $security, TranslatorInterface $translator)
    {
        $this->security = $security;
        $this->translator = $translator;
    }
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::CONTROLLER => [
                ['setLocale', 1]
            ]
        ];
    }
    public function setLocale(ControllerEvent $event)
    {
        if (!$event->isMasterRequest()) {
            return;
        }
        $request = $event->getRequest();
        /**
         * @var 'App'Entit'User
         */
        $user = $this->security->getUser();
        if ($user) {;
            $request->setLocale($user->getLocale());
            $this->translator->setLocale($user->getLocale());
        }
    }
}

为了实现这一点,需要在Kernel::REQUEST事件上设置一个事件订阅者,其优先级高于文档

中所示的默认Locale侦听器。

此时,不幸的是,您将无法访问当前登录的用户,因为这是在Locale侦听器之后触发的另一个Symfony事件中设置的。

但是,您可以访问会话。

解决方案是在成功登录后将用户的语言环境保存在会话中,然后在来自会话的请求中设置语言环境。

// src/EventSubscriber/UserLocaleSubscriber.php
namespace App'EventSubscriber;
use Symfony'Component'EventDispatcher'EventSubscriberInterface;
use Symfony'Component'HttpFoundation'RequestStack;
use Symfony'Component'Security'Http'Event'InteractiveLoginEvent;
use Symfony'Component'Security'Http'SecurityEvents;
/**
 * Stores the locale of the user in the session after the
 * login. This can be used by the LocaleSubscriber afterwards.
 */
class UserLocaleSubscriber implements EventSubscriberInterface
{
    private $requestStack;
    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }
    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();
        if (null !== $user->getLocale()) {
            $this->requestStack->getSession()->set('_locale', $user->getLocale());
        }
    }
    public static function getSubscribedEvents()
    {
        return [
            SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin',
        ];
    }
}
// src/EventSubscriber/LocaleSubscriber.php
namespace App'EventSubscriber;
use Symfony'Component'EventDispatcher'EventSubscriberInterface;
use Symfony'Component'HttpKernel'Event'RequestEvent;
use Symfony'Component'HttpKernel'KernelEvents;
class LocaleSubscriber implements EventSubscriberInterface
{
    public function onKernelRequest(RequestEvent $event)
    {
        $request = $event->getRequest();
        if ($request->getSession()) {
            // Set user's locale from session
            if ($locale = $request->getSession()->get('_locale')) {
                $request->setLocale($locale);
            }
        }
    }
    public static function getSubscribedEvents()
    {
        return [
            // must be registered before (i.e. with a higher priority than) the default Locale listener
            KernelEvents::REQUEST => [['onKernelRequest', 20]],
        ];
    }
}