Symfony2本地化检测:不考虑会话中的_locale

6
我正在尝试实现一个LocaleListener,用于检测用户的首选语言(考虑Accept-Language头)并将其存储在会话中,以避免每次请求时都进行检查。我已经编写了下面的代码来完成这个任务:
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);
    }
}

代码正在工作,首选语言被存储在会话中,但Symfony没有考虑存储在会话中的区域设置来翻译字符串。在我的情况下,我的首选语言是“pt_BR”,当我转义时:

{{ app.request.locale }}

symfony正在转义 'en'。symfony不应该考虑存储在session('_locale')中的值来定义请求语言环境吗?这是正确的行为吗?我该怎么做才能实现这个功能?

2个回答

15
这里有一个能够工作的语言监听器。第二种方法是根据用户选择的偏好更改语言。如果用户无法定义其语言,则可以省略此方法。请注意保留 HTML 标签。
<?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);
        }
    }
}

在您的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 }

哦,你需要在config.yml中定义一个未定义的fallback_language才能使其正常工作。

framework:
    translator:      { fallback: "undefined" }
    default_locale:  "en"

谢谢,@Pazi。我有两个关于你的代码片段的问题:1)在每个请求中设置区域设置是否是一个好主意?我试图避免这种情况,将区域设置存储在会话中。2)你在哪里设置了会话中的区域设置?你的方法$user->getLanguage()返回什么? - Hugo Nogueira
在我的项目中没有问题。我也从另一个来源得到了这个片段,但我不再知道URL了。也许你可以修改它,只使用会话中的 _locale。过去当我引入它时,它没有起作用。$user->getLanguage() 只是我的用户实体中的语言字段,因为我的用户可以独立于浏览器区域设置决定使用什么语言来使用Web应用程序。 - Emii Khaos

7

自Symfony 2.1以来,区域设置不再存储在会话中,而是存储在请求中。要解决此问题,您可以采取以下步骤:

  • 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(...);
    }
    

官方文档(http://symfony.com/doc/2.1/book/translation.html#handling-the-user-s-locale)指出,也可以将区域设置存储在会话中。这不是自动的吗?还是应该报告一个错误?“也可以将区域设置存储在会话中,而不是每个请求基础上。如果这样做,每个后续请求都将具有此区域设置。” - Hugo Nogueira
@hugomn请注意这句话:“如果你这样做,每个后续的请求都会有这个语言环境。”我们不是在谈论子请求,对吗?所以它不起作用。 - Wouter J
我没明白。这是说Symfony不会在后续请求中考虑会话中的 _locale 键吗? - Hugo Nogueira
1
区域设置存储在请求中而不是会话中。如果您将其存储在会话中,则该区域设置将用于子请求而不是主请求。 - Wouter J
1
“subsequent requests”和“子请求”是有区别的。我认为Symfony或文档中存在问题,因为如果我在会话中设置了语言环境,我期望该会话中的每个请求都具有该语言环境(而不仅仅是子请求)。 - thomaskonrad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接