Symfony2 错误的区域设置检测?

11

根据Symfony2有关翻译的指南,我发现从HTTP头中(存储在$this->get('session')->getLocale())推断出的语言环境是错误的(发送了it,推断为en):

主机名 localhost 用户代理 Mozilla/5.0(Windows NT 6.1;WOW64;rv:7.0.1)Gecko/20100101 Firefox/7.0.1 接受 text/html、application/xhtml+xml、application/xml;q=0.9、/;q=0.8 接受语言 it-it、it;q=0.8、en-us;q=0.5、en;q=0.3

这是正常行为吗?还是应该设置一些东西以使本地化工作正常?


1
我发现了一个名为 Symfony\Component\HttpFoundation\Request::getPreferredLanguage() 的方法,如果这个方法可用的话应该被调用,但是我在代码中没有找到任何对这个函数的调用。我认为你应该提交一个错误报告。 - greg0ire
@greg0ire 如果我不确定这是否是错误的行为,我就无法报告错误。 - gremo
2
你永远无法百分之百确定,而且由于这没有记录,报告错误似乎并不那么糟糕。此外,你肯定会得到一个(好的)答案来解决你的问题。不要害羞!+1 鼓励你提问。 - greg0ire
这是正确的行为。默认语言环境是在您的config.yml中指定的,而不是浏览器发送的语言环境。 - wdev
4个回答

7
我今天更仔细地查看了代码,因为我也遇到了和你一样的问题。看来语言是从Session::getLocale()获取的。但是Symfony2从不调用Session::setLocale(),而是设置了Session对象的locale成员变量。在Google上搜索"symfony2 session setlocale"会导向文档的这个章节
所以我最终在我正在处理的控制器顶部放置了这行代码,并且它起作用了:
$this->getRequest()->getSession()->setLocale(
    $this->getRequest()->getPreferredLanguage());

我猜你不会接受这种方法,因为你不会在每个控制器的顶部都添加代码。而且这种方法也不需要在每次请求时都执行,只有在用户还没有会话的情况下才需要执行一次。如果有人知道如何实现,请随意编辑此答案。


4
根据HTTP标准,每个翻译版本的页面应使用不同的URL。剩下的是一个简单的操作,它将从请求中推断出最适合使用的语言环境,并重定向到相应的页面。
/**
 * @Route("/")
 */
public function localeRedirectAction() {
    /* @var $request \Symfony\Component\HttpFoundation\Request */
    /* @var $session \Symfony\Component\HttpFoundation\Session */
    $req = $this->getRequest();
    $session = $this->get('session');

    $session->setLocale($req->getPreferredLanguage(array('de', 'en')));

    return $this->redirect($this->generateUrl('home'));
}

如果您需要为任何页面执行此操作,您基本上需要做相同的事情,但是在kernel.request事件的侦听器内进行。为了可靠地在路由匹配程序完成其工作后调用它,您应将侦听器的priority设置为一个小于0的值:
# services.yml
services:
  my_locale_listener:
    class: Namespace\LocaleListener
    tags: [{ name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -255 }]
    arguments: [ @service_container, [ 'en', 'fr', 'de', 'es', 'it' ] ]

监听器将如下所示:
class LocaleListener {
    public function __construct($container, $availableLocales) {
        $this->container = $container;
        $this->availableLocales = $availableLocales;
    }

    public function onKernelRequest(GetResponseEvent $e) {
        $req = $e->getRequest();
        $locale = $req->getPreferredLanguage($this->availableLocales);

        // generate a new URL from the current route-name and -params, overwriting
        // the current locale
        $routeName = $req->attributes->get('_route');
        $routeParams = array_merge($req->attributes->all(), array('_locale' => $locale));
        $localizedUrl = $this->container->get('router')->generateUrl($routeName, $routeParams);

        $e->setResponse(new RedirectResponse($localizedUrl));
    }
}

注:我并不完全确定这段代码是否能够正常运行,但它应该可以提供关于如何完成此操作的基本思路。


1
你可以像下面这样注册监听器:
use Symfony\Component\DependencyInjection\ContainerInterface;

use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class LocaleListener
{
    private $container;
    private $defaultLocale;

    public function __construct(ContainerInterface $container, $defaultLocale = 'nl')
    {
        $this->container = $container;
        $this->defaultLocale = $defaultLocale;
    }

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

        if (!$this->container->has('session')) {
            return;
        }

        $session = $this->container->get('session');
        $session->setLocale($this->defaultLocale);
    }
}

(gist)

在框架会话设置阶段之后:

<service id="my.event_listener.locale_listener" class="MyBundle\MyEventListener\LocaleListener">
    <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="100" />
    <argument type="service" id="service_container" />
</service>

(代码片段)


你实际上可以修改它以获取请求对象并使用getPreferredLocale方法,但我喜欢这个想法。 - wdev

0
我写了一个LocaleListener,它不会将您重定向到特定于语言环境的URL,但它会为您设置语言环境设置 ;)
在services.yml中的代码:
services:
    acme.demobundle.listener.request:
        class: Namespace\LocaleListener
        tags: [{ name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -255 }]
        arguments: [ @service_container, [ 'en', 'fr', 'de', 'it', 'es' ] ]

以及实际的监听器:

class LocaleListener
{
    protected $container;
    protected $availableLocales;

    public function __construct(\Symfony\Component\DependencyInjection\Container $container, $availableLocales) {
        $this->container = $container;
        $this->availableLocales = $availableLocales;
    }

    public function onKernelRequest(GetResponseEvent $e) {
        $req = $e->getRequest();
        $locale = $req->getPreferredLanguage($this->availableLocales);
        $session = $this->container->get('session');
        $session->setLocale($locale);
    }
}

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