feature #27957 [Routing] Add fallback to cultureless locale for internationalized routes (fancyweb)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[Routing] Add fallback to cultureless locale for internationalized routes

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | https://github.com/symfony/symfony/issues/27938
| License       | MIT
| Doc PR        | https://github.com/symfony/symfony-docs/pull/10108

Please check the related issue for the context.

TODO :
- [x] Update CHANGELOG
- [x] Update documentation

Commits
-------

fd2e3c36fb [Routing] Add fallback to cultureless locale for internationalized routes
This commit is contained in:
Nicolas Grekas 2018-08-10 09:25:39 +02:00
commit f96753b9ab
4 changed files with 82 additions and 10 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
4.2.0
-----
* added fallback to cultureless locale for internationalized routes
4.0.0
-----

View File

@ -113,10 +113,17 @@ EOF;
?? $this->context->getParameter('_locale')
?: $this->defaultLocale;
if (null !== $locale && (self::$declaredRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) {
unset($parameters['_locale']);
$name .= '.'.$locale;
} elseif (!isset(self::$declaredRoutes[$name])) {
if (null !== $locale) {
do {
if ((self::$declaredRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) {
unset($parameters['_locale']);
$name .= '.'.$locale;
break;
}
} while (false !== $locale = strstr($locale, '_', true));
}
if (!isset(self::$declaredRoutes[$name])) {
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
}

View File

@ -112,13 +112,21 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
*/
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
{
$route = null;
$locale = $parameters['_locale']
?? $this->context->getParameter('_locale')
?: $this->defaultLocale;
if (null !== $locale && null !== ($route = $this->routes->get($name.'.'.$locale)) && $route->getDefault('_canonical_route') === $name) {
unset($parameters['_locale']);
} elseif (null === $route = $this->routes->get($name)) {
if (null !== $locale) {
do {
if (null !== ($route = $this->routes->get($name.'.'.$locale)) && $route->getDefault('_canonical_route') === $name) {
unset($parameters['_locale']);
break;
}
} while (false !== $locale = strstr($locale, '_', true));
}
if (null === $route = $route ?? $this->routes->get($name)) {
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
}

View File

@ -84,19 +84,20 @@ class PhpGeneratorDumperTest extends TestCase
$this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter);
}
public function testDumpWithLocalizedRoutes()
public function testDumpWithSimpleLocalizedRoutes()
{
$this->routeCollection->add('test', (new Route('/foo')));
$this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test'));
$this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_locale', 'nl')->setDefault('_canonical_route', 'test'));
$code = $this->generatorDumper->dump(array(
'class' => 'LocalizedProjectUrlGenerator',
'class' => 'SimpleLocalizedProjectUrlGenerator',
));
file_put_contents($this->testTmpFilepath, $code);
include $this->testTmpFilepath;
$context = new RequestContext('/app.php');
$projectUrlGenerator = new \LocalizedProjectUrlGenerator($context, null, 'en');
$projectUrlGenerator = new \SimpleLocalizedProjectUrlGenerator($context, null, 'en');
$urlWithDefaultLocale = $projectUrlGenerator->generate('test');
$urlWithSpecifiedLocale = $projectUrlGenerator->generate('test', array('_locale' => 'nl'));
@ -109,6 +110,57 @@ class PhpGeneratorDumperTest extends TestCase
$this->assertEquals('/app.php/testen/is/leuk', $urlWithSpecifiedLocale);
$this->assertEquals('/app.php/testing/is/fun', $urlWithEnglishContext);
$this->assertEquals('/app.php/testen/is/leuk', $urlWithDutchContext);
// test with full route name
$this->assertEquals('/app.php/testing/is/fun', $projectUrlGenerator->generate('test.en'));
$context->setParameter('_locale', 'de_DE');
// test that it fall backs to another route when there is no matching localized route
$this->assertEquals('/app.php/foo', $projectUrlGenerator->generate('test'));
}
/**
* @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
* @expectedExceptionMessage Unable to generate a URL for the named route "test" as such route does not exist.
*/
public function testDumpWithRouteNotFoundLocalizedRoutes()
{
$this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test'));
$code = $this->generatorDumper->dump(array(
'class' => 'RouteNotFoundLocalizedProjectUrlGenerator',
));
file_put_contents($this->testTmpFilepath, $code);
include $this->testTmpFilepath;
$projectUrlGenerator = new \RouteNotFoundLocalizedProjectUrlGenerator(new RequestContext('/app.php'), null, 'pl_PL');
$projectUrlGenerator->generate('test');
}
public function testDumpWithFallbackLocaleLocalizedRoutes()
{
$this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_canonical_route', 'test'));
$this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_canonical_route', 'test'));
$this->routeCollection->add('test.fr', (new Route('/tester/est/amusant'))->setDefault('_canonical_route', 'test'));
$code = $this->generatorDumper->dump(array(
'class' => 'FallbackLocaleLocalizedProjectUrlGenerator',
));
file_put_contents($this->testTmpFilepath, $code);
include $this->testTmpFilepath;
$context = new RequestContext('/app.php');
$context->setParameter('_locale', 'en_GB');
$projectUrlGenerator = new \FallbackLocaleLocalizedProjectUrlGenerator($context, null, null);
// test with context _locale
$this->assertEquals('/app.php/testing/is/fun', $projectUrlGenerator->generate('test'));
// test with parameters _locale
$this->assertEquals('/app.php/testen/is/leuk', $projectUrlGenerator->generate('test', array('_locale' => 'nl_BE')));
$projectUrlGenerator = new \FallbackLocaleLocalizedProjectUrlGenerator(new RequestContext('/app.php'), null, 'fr_CA');
// test with default locale
$this->assertEquals('/app.php/tester/est/amusant', $projectUrlGenerator->generate('test'));
}
public function testDumpWithTooManyRoutes()