Commit 8aeea4ba authored by Fabien Potencier's avatar Fabien Potencier

feature #990 A solution to resolve #983 (bad comportment of Translator) (freepius44)

This PR was merged into the 2.0.x-dev branch.

Discussion
----------

A solution to resolve #983 (bad comportment of Translator)

Implementation of @henrikbjorn solution :

Use the Translator directly from Symfony and create a EventListener that sets the Locale on the translator based on the request (like LocaleListener does).

Commits
-------

36cfde2b A solution to resolve #983 (bad comportment of Translator)
parents 279c7733 36cfde2b
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex\Provider\Translation;
use Pimple\Container;
use Symfony\Component\Translation\Translator as BaseTranslator;
use Symfony\Component\Translation\MessageSelector;
/**
* Translator that gets the current locale from the container.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Translator extends BaseTranslator
{
protected $app;
public function __construct(Container $app, MessageSelector $selector, $cacheDir = null, $debug = false)
{
$this->app = $app;
parent::__construct(null, $selector, $cacheDir, $debug);
}
public function getLocale()
{
return $this->app['locale'];
}
public function setLocale($locale)
{
if (null === $locale) {
return;
}
$this->app['locale'] = $locale;
parent::setLocale($locale);
}
}
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex\Provider\Translation;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
* Initializes the Translator locale based on the Request locale.
*
* This listener works in 2 modes:
*
* * 2.3 compatibility mode where you must call setRequest whenever the Request changes.
* * 2.4+ mode where you must pass a RequestStack instance in the constructor.
*
* @author Mathieu Poisbeau <freepius44@gmail.com>
*/
class TranslatorListener implements EventSubscriberInterface
{
protected $translator;
protected $requestStack;
/**
* RequestStack will become required in 3.0.
*/
public function __construct(TranslatorInterface $translator, RequestStack $requestStack = null)
{
$this->translator = $translator;
$this->requestStack = $requestStack;
}
/**
* Sets the current Request.
*
* This method was used to synchronize the Request, but as the HttpKernel
* is doing that automatically now, you should never call it directly.
* It is kept public for BC with the 2.3 version.
*
* @param Request|null $request A Request instance
*
* @deprecated Deprecated since version 2.4, to be removed in 3.0.
*/
public function setRequest(Request $request = null)
{
if (null === $request) {
return;
}
$this->translator->setLocale($request->getLocale());
}
public function onKernelRequest(GetResponseEvent $event)
{
$this->translator->setLocale($event->getRequest()->getLocale());
}
public function onKernelFinishRequest(FinishRequestEvent $event)
{
if (null === $this->requestStack) {
return; // removed when requestStack is required
}
if (null !== $parentRequest = $this->requestStack->getParentRequest()) {
$this->translator->setLocale($parentRequest->getLocale());
}
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array(array('onKernelRequest', 0)),
KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
);
}
}
...@@ -13,33 +13,35 @@ namespace Silex\Provider; ...@@ -13,33 +13,35 @@ namespace Silex\Provider;
use Pimple\Container; use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
use Silex\Provider\Translation\Translator; use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector; use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Loader\XliffFileLoader; use Symfony\Component\Translation\Loader\XliffFileLoader;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Silex\Api\EventListenerProviderInterface;
use Silex\Provider\Translation\TranslatorListener;
/** /**
* Symfony Translation component Provider. * Symfony Translation component Provider.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */
class TranslationServiceProvider implements ServiceProviderInterface class TranslationServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface
{ {
public function register(Container $app) public function register(Container $app)
{ {
$app['translator'] = function ($app) { $app['translator'] = function ($app) {
if (!isset($app['locale'])) { if (!isset($app['locale'])) {
throw new \LogicException('You must register the LocaleServiceProvider to use the TranslationServiceProvider'); throw new \LogicException('You must define \'locale\' parameter or register the LocaleServiceProvider to use the TranslationServiceProvider');
} }
$translator = new Translator($app, $app['translator.message_selector'], $app['translator.cache_dir'], $app['debug']); $translator = new Translator($app['locale'], $app['translator.message_selector'], $app['translator.cache_dir'], $app['debug']);
// Handle deprecated 'locale_fallback' // Handle deprecated 'locale_fallback'
if (isset($app['locale_fallback'])) { if (isset($app['locale_fallback'])) {
$app['locale_fallbacks'] = (array) $app['locale_fallback']; $app['locale_fallbacks'] = (array) $app['locale_fallback'];
} }
$translator = new Translator($app, $app['translator.message_selector']);
$translator->setFallbackLocales($app['locale_fallbacks']); $translator->setFallbackLocales($app['locale_fallbacks']);
$translator->addLoader('array', new ArrayLoader()); $translator->addLoader('array', new ArrayLoader());
$translator->addLoader('xliff', new XliffFileLoader()); $translator->addLoader('xliff', new XliffFileLoader());
...@@ -53,6 +55,10 @@ class TranslationServiceProvider implements ServiceProviderInterface ...@@ -53,6 +55,10 @@ class TranslationServiceProvider implements ServiceProviderInterface
return $translator; return $translator;
}; };
$app['translator.listener'] = function ($app) {
return new TranslatorListener($app['translator'], $app['request_stack']);
};
$app['translator.message_selector'] = function () { $app['translator.message_selector'] = function () {
return new MessageSelector(); return new MessageSelector();
}; };
...@@ -61,4 +67,10 @@ class TranslationServiceProvider implements ServiceProviderInterface ...@@ -61,4 +67,10 @@ class TranslationServiceProvider implements ServiceProviderInterface
$app['locale_fallbacks'] = array('en'); $app['locale_fallbacks'] = array('en');
$app['translator.cache_dir'] = null; $app['translator.cache_dir'] = null;
} }
public function subscribe(Container $app, EventDispatcherInterface $dispatcher)
{
$dispatcher->addSubscriber($app['translator.listener']);
}
} }
...@@ -14,6 +14,8 @@ namespace Silex\Tests\Provider; ...@@ -14,6 +14,8 @@ namespace Silex\Tests\Provider;
use Silex\Application; use Silex\Application;
use Silex\Provider\TranslationServiceProvider; use Silex\Provider\TranslationServiceProvider;
use Silex\Provider\LocaleServiceProvider; use Silex\Provider\LocaleServiceProvider;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/** /**
* TranslationProvider test cases. * TranslationProvider test cases.
...@@ -118,26 +120,62 @@ class TranslationServiceProviderTest extends \PHPUnit_Framework_TestCase ...@@ -118,26 +120,62 @@ class TranslationServiceProviderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('The german translation', $result); $this->assertEquals('The german translation', $result);
} }
public function testChangingLocale() public function testLocale()
{ {
$app = $this->getPreparedApp(); $app = $this->getPreparedApp();
$app->get('/', function () use ($app) { return $app['translator']->getLocale(); });
$response = $app->handle(Request::create('/'));
$this->assertEquals('en', $response->getContent());
$this->assertEquals('The translation', $app['translator']->trans('key1')); $app = $this->getPreparedApp();
$app->get('/', function () use ($app) { return $app['translator']->getLocale(); });
$app['locale'] = 'de'; $request = Request::create('/');
$request->setLocale('fr');
$response = $app->handle($request);
$this->assertEquals('fr', $response->getContent());
$this->assertEquals('The german translation', $app['translator']->trans('key1')); $app = $this->getPreparedApp();
$app->get('/{_locale}', function () use ($app) { return $app['translator']->getLocale(); });
$response = $app->handle(Request::create('/es'));
$this->assertEquals('es', $response->getContent());
} }
public function testChangingLocaleViaTranslator() public function testLocaleInSubRequests()
{ {
$app = $this->getPreparedApp(); $app = $this->getPreparedApp();
$app->get('/embed/{_locale}', function () use ($app) { return $app['translator']->getLocale(); });
$app->get('/{_locale}', function () use ($app) {
return $app['translator']->getLocale().
$app->handle(Request::create('/embed/es'), HttpKernelInterface::SUB_REQUEST)->getContent().
$app['translator']->getLocale();
});
$response = $app->handle(Request::create('/fr'));
$this->assertEquals('fresfr', $response->getContent());
$this->assertEquals('The translation', $app['translator']->trans('key1')); $app = $this->getPreparedApp();
$app->get('/embed', function () use ($app) { return $app['translator']->getLocale(); });
$app['translator']->setLocale('de'); $app->get('/{_locale}', function () use ($app) {
return $app['translator']->getLocale().
$app->handle(Request::create('/embed'), HttpKernelInterface::SUB_REQUEST)->getContent().
$app['translator']->getLocale();
});
$response = $app->handle(Request::create('/fr'));
// locale in sub-request must be "en" as this is the value if the sub-request is converted to an ESI
$this->assertEquals('frenfr', $response->getContent());
}
$this->assertEquals('The german translation', $app['translator']->trans('key1')); public function testLocaleWithBefore()
$this->assertEquals('de', $app['locale']); {
$app = $this->getPreparedApp();
$app->before(function (Request $request) { $request->setLocale('fr'); });
$app->get('/embed', function () use ($app) { return $app['translator']->getLocale(); });
$app->get('/', function () use ($app) {
return $app['translator']->getLocale().
$app->handle(Request::create('/embed'), HttpKernelInterface::SUB_REQUEST)->getContent().
$app['translator']->getLocale();
});
$response = $app->handle(Request::create('/'));
// locale in sub-request is "en" as the before filter is only executed for the main request
$this->assertEquals('frenfr', $response->getContent());
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment