Symfony JMSI18nRoutingBundle能否使用HTTP接受语言数组?
我正在尝试创建一个国际化的网站,为我翻译的每种语言(例如Symfony JMSI18nRoutingBundle能否使用HTTP接受语言数组?,symfony,internationalization,Symfony,Internationalization,我正在尝试创建一个国际化的网站,为我翻译的每种语言(例如/fr/my/page或/it/my/page)提供一个URL前缀 我尝试了JMSI18nRoutingBundle,它工作得非常好,几乎没有额外的配置。但我真的想自动确定用户喜欢的语言。 用户最喜欢的语言被传输到Accept LanguageHTTP头中,我想选择我有翻译的第一种语言 以下是我的JMSI18N路由配置: jms_i18n_routing: default_locale: en locales: [fr, e
/fr/my/page
或/it/my/page
)提供一个URL前缀
我尝试了JMSI18nRoutingBundle,它工作得非常好,几乎没有额外的配置。但我真的想自动确定用户喜欢的语言。
用户最喜欢的语言被传输到Accept Language
HTTP头中,我想选择我有翻译的第一种语言
以下是我的JMSI18N路由配置:
jms_i18n_routing:
default_locale: en
locales: [fr, en]
strategy: prefix_except_default
我想要这种行为:
http://mywebsite.com/my/page
执行自动语言检测,然后重定向到/xx/..
(其中xx
是用户最喜欢的语言),因为URL中未指定语言-目前默认语言为EN
http://mywebsite.com/XX/my/page
以XX语言显示页面-目前运行良好
有什么办法吗?配置是否正常
哦,而且,如果有人有办法在纯Symfony(没有JMSI18nRoutingBundle)中做同样的事情,我的耳朵是敞开的
编辑/找到一种使用JMSI18nRoutingBundle进行智能重定向的方法,以尊重用户喜爱的语言或让用户强制显示语言。请参阅我的。这里有一个使用直接符号的方法。这可能会让人感觉有点不舒服,因为它要求每个动作指定2条路径,所以如果有人能想出更好的方法,我会全神贯注地听 首先,我将为所有可接受的地区定义某种配置参数,并将第一个列为默认值 parameters.yml.dist:
parameters:
accepted_locales: [en, es, fr]
然后确保控制器路由与设置和未设置的\u locale
相匹配。对这两种路由名称使用相同的路由名称,但在不带\u locale
的路由名称后面加上类似
的分隔符:
/**
* @Route("/{_locale}/test/{var}", name="test")
* @Route( "/test/{var}", name="test|")
*/
public function testAction(Request $request, $var, $_locale = null)
{
// whatever your controller action does
}
接下来定义一个服务,该服务将侦听控制器事件并将您接受的区域设置传递给它:
<service id="kernel.listener.locale" class="My\Bundle\EventListener\LocaleListener">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<argument>%accepted_locales%</argument>
</service>
有关在控制器上设置前置过滤器的详细说明,请查看。如果使用类似的方法,请非常小心,确保正确定义每个路由名称。最后,我回答我的问题 我开发了一个小“补丁程序”使用JMSI18nRoutingBundle并检测用户的首选语言,还允许用户强制使用一种语言 创建监听器
YourBundle/EventListener/LocaleListener.php
如果用户的首选区域设置与Symfony或JMSI18nRoutingBundle定义的区域设置不同,则此侦听器将更改URL。这样,您就有了两个URL,用于两种不同语言的两种不同内容:它对SEO友好
您还可以创建一个由指向?setlang=xx
的链接组成的语言选择器,其中xx
是用户想要显示的语言。侦听器将检测setlang
查询,并强制显示xx
lang,包括在下一个请求中
注意$this->translateable=[…
数组。它允许您定义站点的哪些部分是可翻译的。粒度可以从供应商定义到操作方法。
您还可以创建一个配置节点来定义可翻译的供应商/捆绑包/控制器,出于性能考虑,我没有这样做
<?php
namespace YourVendor\YourBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class LocaleListener implements EventSubscriberInterface
{
private $defaultLocale;
private $acceptedLocales;
private $translatable;
public function __construct($router, $defaultLocale, $acceptedLocales)
{
$this->router = $router;
$this->defaultLocale = $defaultLocale;
$this->acceptedLocales = $acceptedLocales;
$this->translatable = [
'Vendor1',
'Vendor2\Bundle1',
'Vendor2\Bundle2\Controller1',
'Vendor2\Bundle2\Controller2::myPageAction',
];
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$route = $request->get('_route');
if(!empty($newLocale = $request->query->get('setlang'))) {
if(in_array($newLocale, $this->acceptedLocales)) {
$cookie = new Cookie('force_lang', $newLocale, time() + 3600 * 24 * 7);
$url = $this->router->generate($route, ['_locale' => $newLocale] + $request->attributes->get('_route_params'));
$response = new RedirectResponse($url);
$response->headers->setCookie($cookie);
$event->setResponse($response);
}
} else if($this->translatable($request->attributes->get('_controller'))) {
$preferred = empty($force = $request->cookies->get('force_lang')) ? $request->getPreferredLanguage($this->acceptedLocales) : $force;
if($preferred && $request->attributes->get('_locale') != $preferred) {
$url = $this->router->generate($route, ['_locale' => $preferred] + $request->attributes->get('_route_params'));
$event->setResponse(new RedirectResponse($url));
}
}
}
private function translatable($str)
{
foreach($this->translatable as $t) {
if(strpos($str, $t) !== false) return true;
}
return false;
}
public static function getSubscribedEvents()
{
return [ KernelEvents::REQUEST => [['onKernelRequest', 200]] ];
}
}
JMSI18nRoutingBundle的配置
你没有什么可以改变的。
例如:
另一个更有用的解决方案
转到I18nRoutingBundle的供应商,编辑侦听器
/www/vendor/jms/i18n-routing-bundle/JMS/I18nRoutingBundle/EventListener
更换
$locale = $this->localeResolver->resolveLocale($request, $this->locales) ?: $this->defaultLocale;
由
$locale = $this->localeResolver->resolveLocale($request, $this->locales) ?: $request->getPreferredLanguage($this->locales);
(覆盖监听器比直接编辑供应商更为简洁)Hi Morgan,您是否考虑过使用事件监听器来确定用户的语言?您可以将其与每个路由提供2个选项(一个带区域设置,一个不带区域设置)结合起来,如果本地不存在,则使用您在侦听器中检测到的区域设置值重定向。感谢您的帮助!但是,我不想为每个方法定义twho路由。因此,根据您的建议,我编写了一个侦听器,设计用于JMSI18nRoutingBundle。我发布了一个带有解释和代码的答案。再次感谢您,很抱歉没有选择y我们的解决方案!不客气!我很高兴您找到了一个更简洁、更短的解决方案,每个控制器操作需要定义2条路径,因此感觉不那么麻烦。不要担心没有选择我的解决方案,我只是很高兴它激发了一种思维过程,让您到达了需要的地方。
/www/vendor/jms/i18n-routing-bundle/JMS/I18nRoutingBundle/EventListener
$locale = $this->localeResolver->resolveLocale($request, $this->locales) ?: $this->defaultLocale;
$locale = $this->localeResolver->resolveLocale($request, $this->locales) ?: $request->getPreferredLanguage($this->locales);