From 2ee1ddc0df2170f19ed932be2a96c949ea5a763f Mon Sep 17 00:00:00 2001 From: banghamid <62824084+banghamid@users.noreply.github.com> Date: Wed, 11 Nov 2026 17:05:52 +0700 Subject: [PATCH 1/2] fix(Router): handle {locale} placeholder in redirect routes --- system/Router/Router.php | 33 +++++++++++++++++------------- tests/system/Router/RouterTest.php | 3 +++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/system/Router/Router.php b/system/Router/Router.php index f0f9555ad1ac..02426fcc9106 100644 --- a/system/Router/Router.php +++ b/system/Router/Router.php @@ -457,20 +457,6 @@ protected function checkRoutes(string $uri): bool // Does the RegEx match? if (preg_match('#^' . $routeKey . '$#u', $uri, $matches)) { - // Is this route supposed to redirect to another? - if ($this->collection->isRedirect($routeKey)) { - // replacing matched route groups with references: post/([0-9]+) -> post/$1 - $redirectTo = preg_replace_callback('/(\([^\(]+\))/', static function (): string { - static $i = 1; - - return '$' . $i++; - }, is_array($handler) ? key($handler) : $handler); - - throw new RedirectException( - preg_replace('#\A' . $routeKey . '\z#u', $redirectTo, $uri), - $this->collection->getRedirectCode($routeKey), - ); - } // Store our locale so CodeIgniter object can // assign it to the Request. if (str_contains($matchedKey, '{locale}')) { @@ -491,6 +477,25 @@ protected function checkRoutes(string $uri): bool unset($matched); } + // Is this route supposed to redirect to another? + if ($this->collection->isRedirect($matchedKey)) { + // replacing matched route groups with references: post/([0-9]+) -> post/$1 + $redirectTo = preg_replace_callback('/(\([^\(]+\))/', static function (): string { + static $i = 1; + + return '$' . $i++; + }, is_array($handler) ? key($handler) : $handler); + + if (str_contains($matchedKey, '{locale}')) { + $redirectTo = str_replace('{locale}', $this->detectedLocale, $redirectTo); + } + + throw new RedirectException( + preg_replace('#\A' . $routeKey . '\z#u', $redirectTo, $uri), + $this->collection->getRedirectCode($matchedKey), + ); + } + // Are we using Closures? If so, then we need // to collect the params into an array // so it can be passed to the controller method later. diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index 6e22c95ed4f9..465143ebf125 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -976,6 +976,9 @@ public static function provideRedirectRoute(): iterable ['post/(:num)', 'article/(:num)', 'alias', '/article/1', 'post/1', 'alias'], ['post/(:num)/comment/(:any)', 'article/(:num)/(:any)', 'post/$1/comment/$2', '/article/1/2', 'post/1/comment/2', 'alias'], ['post/(:segment)/comment/(:any)', 'article/(:num)/(:any)', 'alias', '/article/1/2', 'post/1/comment/2', 'alias'], + ['posts', '{locale}/articles', 'posts', '/en/articles', 'posts', 'alias'], + ['{locale}/posts', '{locale}/articles', '{locale}/posts', '/en/articles', 'en/posts', 'alias'], + ['{locale}/posts', '{locale}/articles', 'alias', '/en/articles', 'en/posts', 'alias'], ]; } From 33ff38266eea075341b9498f9221ec0226df0432 Mon Sep 17 00:00:00 2001 From: banghamid <62824084+banghamid@users.noreply.github.com> Date: Thu, 12 Nov 2026 02:20:07 +0700 Subject: [PATCH 2/2] test(Router): cover redirect status code and locale edge cases --- tests/system/Router/RouterTest.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index 465143ebf125..88f10ca54140 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -958,6 +958,7 @@ public function testRedirectRoute( $this->expectException(RedirectException::class); $this->expectExceptionMessage($expectedPath); + $this->expectExceptionCode(302); $router = new Router($collection, $this->request); $router->handle($url); @@ -979,9 +980,29 @@ public static function provideRedirectRoute(): iterable ['posts', '{locale}/articles', 'posts', '/en/articles', 'posts', 'alias'], ['{locale}/posts', '{locale}/articles', '{locale}/posts', '/en/articles', 'en/posts', 'alias'], ['{locale}/posts', '{locale}/articles', 'alias', '/en/articles', 'en/posts', 'alias'], + ['{locale}/post/(:num)', '{locale}/article/(:num)', '{locale}/post/$1', '/en/article/1', 'en/post/1', 'alias'], + ['{locale}/post/(:num)', '{locale}/article/(:num)', 'alias', '/en/article/1', 'en/post/1', 'alias'], ]; } + public function testRedirectRouteWithUnsupportedLocale(): void + { + $config = config(App::class); + $config->supportedLocales = ['en']; + + $collection = clone $this->collection; + $collection->resetRoutes(); + $collection->useSupportedLocalesOnly(true); + + $collection->get('{locale}/posts', 'Controller::method'); + $collection->addRedirect('{locale}/articles', '{locale}/posts'); + + $this->expectException(PageNotFoundException::class); + + $router = new Router($collection, $this->request); + $router->handle('/fr/articles'); + } + public function testRoutePlaceholderAnyWithMultipleSegmentsParamFalse(): void { $this->createRouteCollection();