Repository: codeigniter4/CodeIgniter4 (PHP)

Issue:
Bug: Using placeholder (:any) before {locale} in routes cause incorrect $localeSegment value.
### PHP Version

7.4

### CodeIgniter4 Version

4.1.9

### CodeIgniter4 Installation Method

Composer (using `codeigniter4/appstarter`)

### Which operating systems have you tested for this bug?

macOS

### Which server did you use?

apache

### Database

 MySQL 5.6

### What happened?

Incorrect `$localeSegment` when route is set as: `$routes->get('(:any)/lang/{locale}', 'Language::index');`

Using placeholder in routes cause problem in getting:
```php
$localeSegment = array_search('{locale}', preg_split('/[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+/m', $key), true);
```
at line 375.
The `$localeSegment` suppose to be 3 but returned value is 2

### Steps to Reproduce

```php
$routes->get('(:any)/lang/{locale}', 'Language::index');
```

URL: https://domain.name/test/lang/bg

App Config:
```php
public $defaultLocale = 'en';
public $supportedLocales = ['en', 'bg',];
```

### Expected Output

expect to set locale as 'bg'


### Anything else?

_No response_


Discussion:
Can't reproduce
```php
public $defaultLocale = 'en';
public $supportedLocales = ['en', 'uk', 'bg'];

$routes->get('(:any)/lang/{locale}', fn () => service('request')->getLocale());
```
test/lang/uk -> uk
test/lang/bg -> bg
test/lang/aaa -> en

```   
(:any)/lang/{locale}
  0     1      2  
```   
@iRedds Did you test on 4.1.9? 
@MGatner yep
Let me post my specific case and some debugging results:
Routes.php
```php
$routes->group('emenu', ['namespace' => 'Emenu\Controllers'], static function ($routes) {
    $routes->get('/', 'Home::index');
    $routes->get('(:any)/lang/{locale}', 'Language::index');
});
```

App.php
```php
    /**
     * --------------------------------------------------------------------------
     * Default Locale
     * --------------------------------------------------------------------------
     *
     * The Locale roughly represents the language and location that your visitor
     * is viewing the site from. It affects the language strings and other
     * strings (like currency markers, numbers, etc), that your program
     * should run under for this request.
     *
     * @var string
     */
    public $defaultLocale = 'bg';

    /**
     * --------------------------------------------------------------------------
     * Negotiate Locale
     * --------------------------------------------------------------------------
     *
     * If true, the current Request object will automatically determine the
     * language to use based on the value of the Accept-Language header.
     *
     * If false, no automatic detection will be performed.
     *
     * @var bool
     */
    public $negotiateLocale = false;

    /**
     * --------------------------------------------------------------------------
     * Supported Locales
     * --------------------------------------------------------------------------
     *
     * If $negotiateLocale is true, this array lists the locales supported
     * by the application in descending order of priority. If no match is
     * found, the first locale will be used.
     *
     * @var string[]
     */
    public $supportedLocales = ['bg', 'en', 'es', 'ru', 'fr', 'de', 'gr', 'ro'];
```

Call url: https://domain.dev/emenu/pod-vishnata/lang/en

Debug result:

Router.php:
```php
 // Reset localeSegment
            $localeSegment = null;

            $key = $key === '/'
                ? $key
                : ltrim($key, '/ ');

            $matchedKey = $key; **"emenu/(.*)/lang/{locale}"**

            // Are we dealing with a locale?
            if (strpos($key, '{locale}') !== false) {
                $localeSegment = array_search('{locale}', preg_split('/[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+/m', $key), true); **2**

                // Replace it with a regex so it
                // will actually match.
                $key = str_replace('/', '\/', $key);
                $key = str_replace('{locale}', '[^\/]+', $key); **"emenu\/(.*)\/lang\/{locale}"**
            }
```

IncomingRequest.php:
```php
 /**
     * Sets the locale string for this request.
     *
     * @return IncomingRequest
     */
    public function setLocale(string $locale) **$locale: "lang"**
    {
        // If it's not a valid locale, set it
        // to the default locale for the site.
        if (! in_array($locale, $this->validLocales, true)) {
            $locale = $this->defaultLocale; **sets default locale back to bg**
        }

        $this->locale = $locale;
        Locale::setDefault($locale);

        return $this;
    }
```

Now I confirm that such a bug exists.
A possible solution is to use a named group to get the locale.

@kpeu3u Have you tested this against the `develop` branch? There have been so many unreleased changes that 4.1.9 is hard to judge anymore. 
@MGatner I haven't test against develop branch, i just checked line `$localeSegment = array_search('{locale}', preg_split('/[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+/m', $key), true);` for regular expression changes. Changes in method that determine locale segment refers to variable name changes only.



Patch:
diff --git a/system/Router/Router.php b/system/Router/Router.php
index ea5eb79b8d70..c5985f95af30 100644
--- a/system/Router/Router.php
+++ b/system/Router/Router.php
@@ -403,9 +403,6 @@ protected function checkRoutes(string $uri): bool
 
         // Loop through the route array looking for wildcards
         foreach ($routes as $routeKey => $handler) {
-            // Reset localeSegment
-            $localeSegment = null;
-
             $routeKey = $routeKey === '/'
                 ? $routeKey
                 : ltrim($routeKey, '/ ');
@@ -414,12 +411,7 @@ protected function checkRoutes(string $uri): bool
 
             // Are we dealing with a locale?
             if (strpos($routeKey, '{locale}') !== false) {
-                $localeSegment = array_search('{locale}', preg_split('/[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+/m', $routeKey), true);
-
-                // Replace it with a regex so it
-                // will actually match.
-                $routeKey = str_replace('/', '\/', $routeKey);
-                $routeKey = str_replace('{locale}', '[^\/]+', $routeKey);
+                $routeKey = str_replace('{locale}', '[^/]+', $routeKey);
             }
 
             // Does the RegEx match?
@@ -440,10 +432,15 @@ protected function checkRoutes(string $uri): bool
                 }
                 // Store our locale so CodeIgniter object can
                 // assign it to the Request.
-                if (isset($localeSegment)) {
-                    // The following may be inefficient, but doesn't upset NetBeans :-/
-                    $temp                 = (explode('/', $uri));
-                    $this->detectedLocale = $temp[$localeSegment];
+                if (strpos($matchedKey, '{locale}') !== false) {
+                    preg_match(
+                        '#^' . str_replace('{locale}', '(?<locale>[^/]+)', $matchedKey) . '$#u',
+                        $uri,
+                        $matched
+                    );
+
+                    $this->detectedLocale = $matched['locale'];
+                    unset($matched);
                 }
 
                 // Are we using Closures? If so, then we need


Test patch:
diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php
index d2e48bff992d..db9cd9c5e7e9 100644
--- a/tests/system/Router/RouterTest.php
+++ b/tests/system/Router/RouterTest.php
@@ -47,6 +47,7 @@ protected function setUp(): void
             'books/(:num)/(:alpha)/(:num)'                    => 'Blog::show/$3/$1',
             'closure/(:num)/(:alpha)'                         => static fn ($num, $str) => $num . '-' . $str,
             '{locale}/pages'                                  => 'App\Pages::list_all',
+            'test/(:any)/lang/{locale}'                       => 'App\Pages::list_all',
             'admin/admins'                                    => 'App\Admin\Admins::list_all',
             'admin/admins/edit/(:any)'                        => 'App/Admin/Admins::edit_show/$1',
             '/some/slash'                                     => 'App\Slash::index',
@@ -402,6 +403,11 @@ public function testDetectsLocales()
 
         $this->assertTrue($router->hasLocale());
         $this->assertSame('fr', $router->getLocale());
+
+        $router->handle('test/123/lang/bg');
+
+        $this->assertTrue($router->hasLocale());
+        $this->assertSame('bg', $router->getLocale());
     }
 
     public function testRouteResource()