From fd2e3c36fbd424f7e9be884465e65166214d86a3 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sun, 15 Jul 2018 16:11:16 +0200 Subject: [PATCH] [Routing] Add fallback to cultureless locale for internationalized routes --- src/Symfony/Component/Routing/CHANGELOG.md | 5 ++ .../Generator/Dumper/PhpGeneratorDumper.php | 15 +++-- .../Routing/Generator/UrlGenerator.php | 14 ++++- .../Dumper/PhpGeneratorDumperTest.php | 62 +++++++++++++++++-- 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md index d088e2eddc..8e359a3722 100644 --- a/src/Symfony/Component/Routing/CHANGELOG.md +++ b/src/Symfony/Component/Routing/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.2.0 +----- + + * added fallback to cultureless locale for internationalized routes + 4.0.0 ----- diff --git a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php index 97e0335014..12dd3f28fa 100644 --- a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php +++ b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php @@ -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)); } diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php index 6bb8222266..1fd75cb796 100644 --- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php +++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php @@ -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)); } diff --git a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php index dfcac06b40..482da395cd 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php @@ -46,8 +46,8 @@ class PhpGeneratorDumperTest extends TestCase $this->routeCollection = new RouteCollection(); $this->generatorDumper = new PhpGeneratorDumper($this->routeCollection); - $this->testTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.php'; - $this->largeTestTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.large.php'; + $this->testTmpFilepath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.php'; + $this->largeTestTmpFilepath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.large.php'; @unlink($this->testTmpFilepath); @unlink($this->largeTestTmpFilepath); } @@ -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()