From f03028f958c4b804d252c5a7601e2fbe5a93e202 Mon Sep 17 00:00:00 2001 From: Taner Kucukyuruk Date: Tue, 10 Jan 2023 15:17:01 +0100 Subject: [PATCH] add support for custom prefixes for localized routes (#20) --- config/localized-routes.php | 10 ++++++++ src/Controller/FallbackController.php | 4 +-- src/LocalizedUrlGenerator.php | 5 +++- src/Macros/LocalizedRoutesMacro.php | 7 +++-- src/UrlGenerator.php | 5 +++- tests/TestCase.php | 12 +++++++++ tests/Unit/Middleware/SetLocaleTest.php | 22 ++++++++++++++++ tests/Unit/RedirectToLocalizedTest.php | 29 +++++++++++++++++++++ tests/Unit/RouteModelBindingTest.php | 34 +++++++++++++++++++++++++ tests/Unit/UrlGeneratorTest.php | 17 +++++++++++++ 10 files changed, 139 insertions(+), 6 deletions(-) diff --git a/config/localized-routes.php b/config/localized-routes.php index b984c0b..87765e2 100644 --- a/config/localized-routes.php +++ b/config/localized-routes.php @@ -68,4 +68,14 @@ */ 'use_localizer' => false, + /** + * Map application locale to a custom route prefix. + * + * 'custom_prefixes' => [ + * 'en' => 'english', + * 'nl' => 'dutch', + * ] + */ + 'custom_prefixes' => [], + ]; diff --git a/src/Controller/FallbackController.php b/src/Controller/FallbackController.php index e933474..cecc641 100644 --- a/src/Controller/FallbackController.php +++ b/src/Controller/FallbackController.php @@ -32,7 +32,7 @@ public function __invoke() } } - return $this->NotFoundResponse(); + return $this->notFoundResponse(); } /** @@ -54,7 +54,7 @@ protected function findRouteByUrl($url) * * @return \Illuminate\Http\Response */ - protected function NotFoundResponse() + protected function notFoundResponse() { $view = Config::get('localized-routes.404_view'); diff --git a/src/LocalizedUrlGenerator.php b/src/LocalizedUrlGenerator.php index 7beb066..4ac02b9 100644 --- a/src/LocalizedUrlGenerator.php +++ b/src/LocalizedUrlGenerator.php @@ -69,10 +69,13 @@ public function generateFromRequest($locale = null, $parameters = null, $absolut $urlBuilder->setPath($this->replaceParameters($this->route->uri(), $slugs)); } + // Map the locale string to a prefix. + $prefix = data_get(Config::get('localized-routes.custom_prefixes'), $locale, $locale); + // If custom domains are not used and it is not a registered, // non localized route, update the locale slug in the path. if (!$this->hasCustomDomains() && ($this->is404() || $this->isLocalized())) { - $urlBuilder->setSlugs($this->updateLocaleInSlugs($urlBuilder->getSlugs(), $locale)); + $urlBuilder->setSlugs($this->updateLocaleInSlugs($urlBuilder->getSlugs(), $prefix)); } if ($domain = $this->getCustomDomain($locale)) { diff --git a/src/Macros/LocalizedRoutesMacro.php b/src/Macros/LocalizedRoutesMacro.php index 0bd0b5b..acb35b8 100644 --- a/src/Macros/LocalizedRoutesMacro.php +++ b/src/Macros/LocalizedRoutesMacro.php @@ -54,11 +54,14 @@ public static function register() // to register translated route URI's. App::setLocale($locale); + // Map the locale string to a prefix. + $prefix = data_get(Config::get('localized-routes.custom_prefixes'), $locale, $locale); + // Prepend the locale to the route name // and set a custom attribute so the middleware // can find it to set the correct app locale. $attributes = [ - 'as' => "{$locale}.", + 'as' => "{$prefix}.", 'localized-routes-locale' => $locale ]; @@ -71,7 +74,7 @@ public static function register() // Prefix the URL unless the locale // is configured to be omitted. if ($domain === null && $locale !== $omitPrefix) { - $attributes['prefix'] = $locale; + $attributes['prefix'] = $prefix; } if ($setMiddleware) { diff --git a/src/UrlGenerator.php b/src/UrlGenerator.php index 8d255f8..1368920 100644 --- a/src/UrlGenerator.php +++ b/src/UrlGenerator.php @@ -54,13 +54,16 @@ public function route($name, $parameters = [], $absolute = true, $locale = null) // as a prefix for the route name. $locale = $locale ?: $currentLocale; + // Map the locale string to a prefix. + $prefix = data_get(Config::get('localized-routes.custom_prefixes'), $locale, $locale); + // Normalize the route name by removing any locale prefix. // We will prepend the applicable locale manually. $baseName = $this->stripLocaleFromRouteName($name); // If the route has a name (not just the locale prefix) // add the requested locale prefix. - $newName = $baseName ? "{$locale}.{$baseName}" : ''; + $newName = $baseName ? "{$prefix}.{$baseName}" : ''; // If the new localized name does not exist, but the unprefixed route name does, // someone might be calling "route($name, [], true, $locale)" on a non localized route. diff --git a/tests/TestCase.php b/tests/TestCase.php index 0ddc87e..fdf0240 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -115,6 +115,18 @@ protected function setUseLocalizer($value) Config::set('localized-routes.use_localizer', $value); } + /** + * Set the custom prefixes config option. + * + * @param array $prefixes + * + * @return void + */ + protected function setCustomPrefixes($prefixes) + { + Config::set('localized-routes.custom_prefixes', $prefixes); + } + /** * Fake that we created a routes.php file in 'resources/lang/' * for each language with the given translations. diff --git a/tests/Unit/Middleware/SetLocaleTest.php b/tests/Unit/Middleware/SetLocaleTest.php index 26cc746..039132b 100644 --- a/tests/Unit/Middleware/SetLocaleTest.php +++ b/tests/Unit/Middleware/SetLocaleTest.php @@ -216,4 +216,26 @@ public function it_passes_the_supported_locales_to_localizer_in_the_correct_form $localizer->shouldHaveReceived('setSupportedLocales')->with(['en', 'nl']); } + + /** @test */ + public function it_sets_the_right_locale_with_custom_prefixes() + { + $this->setSupportedLocales(['en', 'nl']); + $this->setCustomPrefixes(['en' => 'english', 'nl' => 'dutch']); + $this->setUseLocaleMiddleware(true); + + Route::localized(function () { + Route::get('/', function () { + return App::getLocale(); + }); + }); + + $response = $this->call('GET', '/english'); + $response->assertOk(); + $this->assertEquals('en', $response->original); + + $response = $this->call('GET', '/dutch'); + $response->assertOk(); + $this->assertEquals('nl', $response->original); + } } diff --git a/tests/Unit/RedirectToLocalizedTest.php b/tests/Unit/RedirectToLocalizedTest.php index b302b1c..d1dc9fc 100644 --- a/tests/Unit/RedirectToLocalizedTest.php +++ b/tests/Unit/RedirectToLocalizedTest.php @@ -74,4 +74,33 @@ public function it_throws_404_and_does_not_redirect_if_no_localized_route_is_reg $this->setAppLocale('en'); $this->get('missing')->assertNotFound(); } + + /** @test */ + public function it_redirects_to_the_localized_url_with_custom_prefixes() + { + $this->withoutExceptionHandling(); + $this->setSupportedLocales(['en', 'nl']); + $this->setCustomPrefixes(['en' => 'english', 'nl' => 'dutch']); + $this->setUseLocaleMiddleware(false); + $this->setRedirectToLocalizedUrls(true); + + Route::localized(function () { + Route::get('/', function () {}); + Route::get('about', function () {}); + }); + + Route::fallback(\CodeZero\LocalizedRoutes\Controller\FallbackController::class); + + $this->setAppLocale('en'); + $this->get('/')->assertRedirect('english'); + $this->get('english')->assertOk(); + $this->get('about')->assertRedirect('english/about'); + $this->get('english/about')->assertOk(); + + $this->setAppLocale('nl'); + $this->get('/')->assertRedirect('dutch'); + $this->get('dutch')->assertOk(); + $this->get('about')->assertRedirect('dutch/about'); + $this->get('dutch/about')->assertOk(); + } } diff --git a/tests/Unit/RouteModelBindingTest.php b/tests/Unit/RouteModelBindingTest.php index b87d5bb..8b7140b 100644 --- a/tests/Unit/RouteModelBindingTest.php +++ b/tests/Unit/RouteModelBindingTest.php @@ -78,4 +78,38 @@ public function it_loads_a_route_with_a_custom_localized_route_key_based_on_the_ $this->get('en/test/en-slug')->assertOk(); $this->get('en/test/nl-slug')->assertNotFound(); } + + /** @test */ + public function it_loads_a_route_with_a_localized_route_key_with_custom_prefixes() + { + $this->setSupportedLocales(['en', 'nl']); + $this->setCustomPrefixes(['en' => 'english', 'nl' => 'dutch']); + $this->setUseLocaleMiddleware(true); + + $model = (new Model([ + 'slug' => [ + 'en' => 'en-slug', + 'nl' => 'nl-slug', + ], + ]))->setKeyName('slug'); + + App::instance(Model::class, $model); + + Route::middleware(['web'])->get('test/{model}', function (Model $model) {}); + + Route::localized(function () { + Route::middleware(['web'])->get('test/{model}', function (Model $model) {}); + }); + + $this->setAppLocale('nl'); + + $this->get('test/nl-slug')->assertOk(); + $this->get('test/en-slug')->assertNotFound(); + + $this->get('dutch/test/nl-slug')->assertOk(); + $this->get('dutch/test/en-slug')->assertNotFound(); + + $this->get('english/test/en-slug')->assertOk(); + $this->get('english/test/nl-slug')->assertNotFound(); + } } diff --git a/tests/Unit/UrlGeneratorTest.php b/tests/Unit/UrlGeneratorTest.php index 7807415..ac10513 100644 --- a/tests/Unit/UrlGeneratorTest.php +++ b/tests/Unit/UrlGeneratorTest.php @@ -270,6 +270,23 @@ public function it_allows_routes_to_be_cached() $this->get('en/route')->assertSuccessful(); } + /** @test */ + public function it_maps_locales_with_custom_prefixes() + { + $this->setSupportedLocales(['en', 'nl', 'fr']); + + // Set custom prefixes for all locales except "fr" + $this->setCustomPrefixes(['en' => 'english', 'nl' => 'dutch']); + + $this->registerRoute('english/route', 'english.route'); + $this->registerRoute('dutch/route', 'dutch.route'); + $this->registerRoute('fr/route', 'fr.route'); + + $this->assertEquals(url('english/route'), route('english.route')); + $this->assertEquals(url('dutch/route'), route('dutch.route')); + $this->assertEquals(url('fr/route'), route('fr.route')); + } + /** * Cache registered routes. *