bug #35101 [Routing] Fix i18n routing when the url contains the locale (fancyweb)

This PR was merged into the 4.3 branch.

Discussion
----------

[Routing] Fix i18n routing when the url contains the locale

| Q             | A
| ------------- | ---
| Branch?       | 4.3
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | https://github.com/symfony/symfony/issues/34469
| License       | MIT
| Doc PR        | -

This PR fixes different scenarios with i18n routing.

Commits
-------

cd40bb8604 [Routing] Fix i18n routing when the url contains the locale
This commit is contained in:
Nicolas Grekas 2020-01-04 13:43:28 +01:00
commit 423d3ddf05
6 changed files with 110 additions and 10 deletions

View File

@ -40,7 +40,6 @@ class CompiledUrlGenerator extends UrlGenerator
if (null !== $locale) {
do {
if (($this->compiledRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) {
unset($parameters['_locale']);
$name .= '.'.$locale;
break;
}
@ -53,6 +52,14 @@ class CompiledUrlGenerator extends UrlGenerator
list($variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes) = $this->compiledRoutes[$name];
if (isset($defaults['_canonical_route']) && isset($defaults['_locale'])) {
if (!\in_array('_locale', $variables, true)) {
unset($parameters['_locale']);
} elseif (!isset($parameters['_locale'])) {
$parameters['_locale'] = $defaults['_locale'];
}
}
return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes);
}
}

View File

@ -120,7 +120,6 @@ EOF;
if (null !== $locale && null !== $name) {
do {
if ((self::$declaredRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) {
unset($parameters['_locale']);
$name .= '.'.$locale;
break;
}
@ -133,6 +132,14 @@ EOF;
list($variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes) = self::$declaredRoutes[$name];
if (isset($defaults['_canonical_route']) && isset($defaults['_locale'])) {
if (!\in_array('_locale', $variables, true)) {
unset($parameters['_locale']);
} elseif (!isset($parameters['_locale'])) {
$parameters['_locale'] = $defaults['_locale'];
}
}
return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes);
}
EOF;

View File

@ -134,7 +134,6 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
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));
@ -147,7 +146,18 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
// the Route has a cache of its own and is not recompiled as long as it does not get modified
$compiledRoute = $route->compile();
return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes());
$defaults = $route->getDefaults();
$variables = $compiledRoute->getVariables();
if (isset($defaults['_canonical_route']) && isset($defaults['_locale'])) {
if (!\in_array('_locale', $variables, true)) {
unset($parameters['_locale']);
} elseif (!isset($parameters['_locale'])) {
$parameters['_locale'] = $defaults['_locale'];
}
}
return $this->doGenerate($variables, $defaults, $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes());
}
/**

View File

@ -131,9 +131,9 @@ class CompiledUrlGeneratorDumperTest extends TestCase
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'));
$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'));
$this->routeCollection->add('test.fr', (new Route('/tester/est/amusant'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'test'));
$code = $this->generatorDumper->dump();
file_put_contents($this->testTmpFilepath, $code);
@ -231,4 +231,29 @@ class CompiledUrlGeneratorDumperTest extends TestCase
$this->assertEquals('https://localhost/app.php/testing', $absoluteUrl);
$this->assertEquals('/app.php/testing', $relativeUrl);
}
public function testDumpWithLocalizedRoutesPreserveTheGoodLocaleInTheUrl()
{
$this->routeCollection->add('foo.en', (new Route('/{_locale}/foo'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'foo'));
$this->routeCollection->add('foo.fr', (new Route('/{_locale}/foo'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'foo'));
$this->routeCollection->add('fun.en', (new Route('/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'fun'));
$this->routeCollection->add('fun.fr', (new Route('/amusant'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'fun'));
file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
$requestContext = new RequestContext();
$requestContext->setParameter('_locale', 'fr');
$compiledUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, $requestContext, null, null);
$this->assertSame('/fr/foo', $compiledUrlGenerator->generate('foo'));
$this->assertSame('/en/foo', $compiledUrlGenerator->generate('foo.en'));
$this->assertSame('/en/foo', $compiledUrlGenerator->generate('foo', ['_locale' => 'en']));
$this->assertSame('/en/foo', $compiledUrlGenerator->generate('foo.fr', ['_locale' => 'en']));
$this->assertSame('/amusant', $compiledUrlGenerator->generate('fun'));
$this->assertSame('/fun', $compiledUrlGenerator->generate('fun.en'));
$this->assertSame('/fun', $compiledUrlGenerator->generate('fun', ['_locale' => 'en']));
$this->assertSame('/amusant', $compiledUrlGenerator->generate('fun.fr', ['_locale' => 'en']));
}
}

View File

@ -140,9 +140,9 @@ class PhpGeneratorDumperTest extends TestCase
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'));
$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'));
$this->routeCollection->add('test.fr', (new Route('/tester/est/amusant'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'test'));
$code = $this->generatorDumper->dump([
'class' => 'FallbackLocaleLocalizedProjectUrlGenerator',
@ -250,4 +250,32 @@ class PhpGeneratorDumperTest extends TestCase
$this->assertEquals('https://localhost/app.php/testing', $absoluteUrl);
$this->assertEquals('/app.php/testing', $relativeUrl);
}
public function testDumpWithLocalizedRoutesPreserveTheGoodLocaleInTheUrl()
{
$this->routeCollection->add('foo.en', (new Route('/{_locale}/foo'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'foo'));
$this->routeCollection->add('foo.fr', (new Route('/{_locale}/foo'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'foo'));
$this->routeCollection->add('fun.en', (new Route('/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'fun'));
$this->routeCollection->add('fun.fr', (new Route('/amusant'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'fun'));
file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump([
'class' => 'PreserveTheGoodLocaleInTheUrlGenerator',
]));
include $this->testTmpFilepath;
$requestContext = new RequestContext();
$requestContext->setParameter('_locale', 'fr');
$phpGenerator = new \PreserveTheGoodLocaleInTheUrlGenerator($requestContext);
$this->assertSame('/fr/foo', $phpGenerator->generate('foo'));
$this->assertSame('/en/foo', $phpGenerator->generate('foo.en'));
$this->assertSame('/en/foo', $phpGenerator->generate('foo', ['_locale' => 'en']));
$this->assertSame('/en/foo', $phpGenerator->generate('foo.fr', ['_locale' => 'en']));
$this->assertSame('/amusant', $phpGenerator->generate('fun'));
$this->assertSame('/fun', $phpGenerator->generate('fun.en'));
$this->assertSame('/fun', $phpGenerator->generate('fun', ['_locale' => 'en']));
$this->assertSame('/amusant', $phpGenerator->generate('fun.fr', ['_locale' => 'en']));
}
}

View File

@ -236,6 +236,29 @@ class UrlGeneratorTest extends TestCase
);
}
public function testDumpWithLocalizedRoutesPreserveTheGoodLocaleInTheUrl()
{
$routeCollection = new RouteCollection();
$routeCollection->add('foo.en', (new Route('/{_locale}/foo'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'foo'));
$routeCollection->add('foo.fr', (new Route('/{_locale}/foo'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'foo'));
$routeCollection->add('fun.en', (new Route('/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'fun'));
$routeCollection->add('fun.fr', (new Route('/amusant'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'fun'));
$urlGenerator = $this->getGenerator($routeCollection);
$urlGenerator->getContext()->setParameter('_locale', 'fr');
$this->assertSame('/app.php/fr/foo', $urlGenerator->generate('foo'));
$this->assertSame('/app.php/en/foo', $urlGenerator->generate('foo.en'));
$this->assertSame('/app.php/en/foo', $urlGenerator->generate('foo', ['_locale' => 'en']));
$this->assertSame('/app.php/en/foo', $urlGenerator->generate('foo.fr', ['_locale' => 'en']));
$this->assertSame('/app.php/amusant', $urlGenerator->generate('fun'));
$this->assertSame('/app.php/fun', $urlGenerator->generate('fun.en'));
$this->assertSame('/app.php/fun', $urlGenerator->generate('fun', ['_locale' => 'en']));
$this->assertSame('/app.php/amusant', $urlGenerator->generate('fun.fr', ['_locale' => 'en']));
}
public function testGenerateWithoutRoutes()
{
$this->expectException('Symfony\Component\Routing\Exception\RouteNotFoundException');