diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9e9fd1f..4554c07 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,28 +8,17 @@ jobs: strategy: fail-fast: true matrix: - php: [ 8.0, 8.1 ] - laravel: [ 8.*, 9.*, 10.* ] + php: [ 8.1, 8.2, 8.3 ] + laravel: [ 10.*, 11.* ] dependency-version: [ prefer-stable ] exclude: - - laravel: 10.* - php: 8.0 + - laravel: 11.* + php: 8.1 include: - - laravel: 7.* - php: 7.2 - testbench: 5.* - - laravel: 7.* - php: 8.0 - testbench: 5.* - - laravel: 8.* - php: 7.3 - testbench: 6.* - - laravel: 8.* - testbench: 6.* - - laravel: 9.* - testbench: 7.* - laravel: 10.* testbench: 8.* + - laravel: 11.* + testbench: 9.* name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} diff --git a/.gitignore b/.gitignore index fe1052a..667fe37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /vendor /phpunit.xml composer.lock +/.phpunit.cache /.phpunit.result.cache diff --git a/README.md b/README.md index 4be467e..8ce4846 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Laravel Localizer [![GitHub release](https://img.shields.io/github/release/codezero-be/laravel-localizer.svg?style=flat-square)](https://github.com/codezero-be/laravel-localizer/releases) -[![Laravel](https://img.shields.io/badge/laravel-10-red?style=flat-square&logo=laravel&logoColor=white)](https://laravel.com) +[![Laravel](https://img.shields.io/badge/laravel-11-red?style=flat-square&logo=laravel&logoColor=white)](https://laravel.com) [![License](https://img.shields.io/packagist/l/codezero/laravel-localizer.svg?style=flat-square)](LICENSE.md) [![Build Status](https://img.shields.io/github/actions/workflow/status/codezero-be/laravel-localizer/run-tests.yml?style=flat-square&logo=github&logoColor=white&label=tests)](https://github.com/codezero-be/laravel-localizer/actions) [![Code Coverage](https://img.shields.io/codacy/coverage/ad6fcea152b449d380a187a375d0f7d7/master?style=flat-square)](https://app.codacy.com/gh/codezero-be/laravel-localizer) @@ -19,8 +19,8 @@ Automatically detect and set an app locale that matches your visitor's preferenc ## ✅ Requirements -- PHP >= 7.2.5 -- Laravel >= 7.0 +- PHP >= 8.1 +- Laravel >= 10.0 ## ⬆ Upgrade @@ -39,13 +39,36 @@ Laravel will automatically register the ServiceProvider. ## 🧩 Add Middleware -Add the middleware to the `web` middleware group in `app/Http/Kernel.php`. +By default, the app locale will always be what you configured in `config/app.php`. +To automatically update the app locale, you need to register the middleware in the `web` middleware group. Make sure to add it after `StartSession` and before `SubstituteBindings`. The order of the middleware is important if you are using localized route keys (translated slugs)! The session needs to be active when setting the locale, and the locale needs to be set when substituting the route bindings. +### Laravel 11 and newer: + +Add the middleware to the `web` middleware group in `bootstrap/app.php`. + +```php +// bootstrap/app.php +->withMiddleware(function (Middleware $middleware) { + $middleware->web(remove: [ + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ]); + $middleware->web(append: [ + \CodeZero\Localizer\Middleware\SetLocale::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ]); +}) +``` + +### Laravel 10: + +Add the middleware to the `web` middleware group in `app/Http/Kernel.php`. + ```php +// app/Http/Kernel.php protected $middlewareGroups = [ 'web' => [ //... diff --git a/UPGRADE.md b/UPGRADE.md index 8850e90..87bf804 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,25 @@ # Upgrade Guide +## Upgrading To 3.0 From 2.x + +### ➡ Minimum Requirements Updated + +Due to PHP and PHPUnit version constraints with Laravel 11, we dropped support for Laravel 7.x, 8.x and 9.x. + +- The minimum PHP version required is now 8.1 +- The minimum Laravel version required is now 10.0 + +--- + +### ➡ Re-register Middleware + +Laravel 11 no longer has a `app/Http/Kernel.php` to register middleware. +This is now handled in `bootstrap/app.php`. + +🔸 **Actions Required** + +If you use Laravel 11, register the middleware in `bootstrap/app.php` as described in the README. + ## Upgrading To 2.0 From 1.x ### ➡ Minimum Requirements Updated diff --git a/composer.json b/composer.json index daf66fa..6444ae8 100644 --- a/composer.json +++ b/composer.json @@ -21,14 +21,14 @@ } ], "require": { - "php": "^7.2.5|^8.0", + "php": "^8.1", "codezero/browser-locale": "^3.0", - "illuminate/support": "^7.0|^8.0|^9.0|^10.0" + "illuminate/support": "^10.0|^11.0" }, "require-dev": { "mockery/mockery": "^1.3.3", - "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0", - "phpunit/phpunit": "^8.0|^9.0" + "orchestra/testbench": "^8.0|^9.0", + "phpunit/phpunit": "^10.5" }, "scripts": { "test": "phpunit" @@ -53,10 +53,7 @@ "config": { "preferred-install": "dist", "sort-packages": true, - "optimize-autoloader": true, - "allow-plugins": { - "kylekatarnls/update-helper": true - } + "optimize-autoloader": true }, "minimum-stability": "dev", "prefer-stable": true diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e1988a6..dc4366a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,13 +1,13 @@ - + stopOnFailure="false" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Unit @@ -16,15 +16,15 @@ ./tests/Feature - - - ./src - - + + + ./src + + diff --git a/tests/Feature/SetLocaleTest.php b/tests/Feature/SetLocaleTest.php index 8b8a099..fcd3656 100644 --- a/tests/Feature/SetLocaleTest.php +++ b/tests/Feature/SetLocaleTest.php @@ -2,6 +2,7 @@ namespace CodeZero\Localizer\Tests\Feature; +use PHPUnit\Framework\Attributes\Test; use CodeZero\BrowserLocale\BrowserLocale; use CodeZero\Localizer\Middleware\SetLocale; use CodeZero\Localizer\Tests\TestCase; @@ -12,7 +13,7 @@ use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Session; -class SetLocaleTest extends TestCase +final class SetLocaleTest extends TestCase { protected $sessionKey; protected $cookieName; @@ -33,8 +34,8 @@ protected function setUp(): void $this->cookieName = Config::get('localizer.cookie_name'); } - /** @test */ - public function it_looks_for_a_locale_in_a_custom_route_action() + #[Test] + public function it_looks_for_a_locale_in_a_custom_route_action(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); @@ -44,7 +45,7 @@ public function it_looks_for_a_locale_in_a_custom_route_action() Route::group($routeAction, function () { Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); }); $response = $this->get('some/route'); @@ -54,15 +55,15 @@ public function it_looks_for_a_locale_in_a_custom_route_action() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_looks_for_a_locale_in_the_url() + #[Test] + public function it_looks_for_a_locale_in_the_url(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); Route::get('nl/some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('nl/some/route'); @@ -71,8 +72,8 @@ public function it_looks_for_a_locale_in_the_url() $this->assertEquals('nl', $response->original); } - /** @test */ - public function you_can_configure_which_segment_to_use_as_locale() + #[Test] + public function you_can_configure_which_segment_to_use_as_locale(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); @@ -81,7 +82,7 @@ public function you_can_configure_which_segment_to_use_as_locale() Route::get('some/nl/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('some/nl/route'); @@ -90,8 +91,8 @@ public function you_can_configure_which_segment_to_use_as_locale() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_looks_for_custom_slugs() + #[Test] + public function it_looks_for_custom_slugs(): void { $this->setSupportedLocales([ 'en' => 'english', @@ -101,7 +102,7 @@ public function it_looks_for_custom_slugs() Route::get('dutch/some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('dutch/some/route'); @@ -110,8 +111,8 @@ public function it_looks_for_custom_slugs() $this->assertEquals('nl', $response->original); } - /** @test */ - public function you_can_use_multiple_slugs_for_a_locale() + #[Test] + public function you_can_use_multiple_slugs_for_a_locale(): void { $this->setSupportedLocales([ 'en' => 'english', @@ -121,11 +122,11 @@ public function you_can_use_multiple_slugs_for_a_locale() Route::get('dutch/some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); Route::get('nederlands/some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('dutch/some/route'); @@ -140,8 +141,8 @@ public function you_can_use_multiple_slugs_for_a_locale() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_looks_for_custom_domains() + #[Test] + public function it_looks_for_custom_domains(): void { $this->setSupportedLocales([ 'en' => 'english.test', @@ -152,7 +153,7 @@ public function it_looks_for_custom_domains() Route::group(['domain' => 'dutch.test'], function () { Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); }); $response = $this->get('http://dutch.test/some/route'); @@ -162,8 +163,8 @@ public function it_looks_for_custom_domains() $this->assertEquals('nl', $response->original); } - /** @test */ - public function you_can_use_multiple_domains_for_a_locale() + #[Test] + public function you_can_use_multiple_domains_for_a_locale(): void { $this->setSupportedLocales([ 'en' => 'english.test', @@ -174,13 +175,13 @@ public function you_can_use_multiple_domains_for_a_locale() Route::group(['domain' => 'dutch.test'], function () { Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); }); Route::group(['domain' => 'nederlands.test'], function () { Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); }); $response = $this->get('http://dutch.test/some/route'); @@ -196,8 +197,8 @@ public function you_can_use_multiple_domains_for_a_locale() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_checks_for_a_configured_omitted_locale() + #[Test] + public function it_checks_for_a_configured_omitted_locale(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); @@ -206,7 +207,7 @@ public function it_checks_for_a_configured_omitted_locale() Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('some/route'); @@ -215,8 +216,8 @@ public function it_checks_for_a_configured_omitted_locale() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_looks_for_a_locale_on_the_authenticated_user() + #[Test] + public function it_looks_for_a_locale_on_the_authenticated_user(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); @@ -227,7 +228,7 @@ public function it_looks_for_a_locale_on_the_authenticated_user() Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->actingAs($user)->get('some/route'); @@ -236,8 +237,8 @@ public function it_looks_for_a_locale_on_the_authenticated_user() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_will_bypass_missing_attribute_exception_if_the_locale_attribute_is_missing_on_the_user_model() + #[Test] + public function it_will_bypass_missing_attribute_exception_if_the_locale_attribute_is_missing_on_the_user_model(): void { if (version_compare(App::version(), '9.35.0') === -1) { $this->markTestSkipped('This test only applies to Laravel 9.35.0 and higher.'); @@ -252,7 +253,7 @@ public function it_will_bypass_missing_attribute_exception_if_the_locale_attribu Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->actingAs($user)->get('some/route'); @@ -261,8 +262,8 @@ public function it_will_bypass_missing_attribute_exception_if_the_locale_attribu $this->assertEquals('en', $response->original); } - /** @test */ - public function it_looks_for_a_locale_in_the_session() + #[Test] + public function it_looks_for_a_locale_in_the_session(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); @@ -271,7 +272,7 @@ public function it_looks_for_a_locale_in_the_session() Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('some/route'); @@ -280,8 +281,8 @@ public function it_looks_for_a_locale_in_the_session() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_looks_for_a_locale_in_a_cookie() + #[Test] + public function it_looks_for_a_locale_in_a_cookie(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); @@ -290,7 +291,7 @@ public function it_looks_for_a_locale_in_a_cookie() Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->withCookie($this->cookieName, $cookie) ->get('some/route'); @@ -300,8 +301,8 @@ public function it_looks_for_a_locale_in_a_cookie() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_looks_for_a_locale_in_the_browser() + #[Test] + public function it_looks_for_a_locale_in_the_browser(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('en'); @@ -310,7 +311,7 @@ public function it_looks_for_a_locale_in_the_browser() Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('some/route'); @@ -319,8 +320,8 @@ public function it_looks_for_a_locale_in_the_browser() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_returns_the_best_match_when_a_browser_locale_is_used() + #[Test] + public function it_returns_the_best_match_when_a_browser_locale_is_used(): void { $this->setSupportedLocales(['en', 'nl', 'fr']); $this->setAppLocale('en'); @@ -329,7 +330,7 @@ public function it_returns_the_best_match_when_a_browser_locale_is_used() Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('some/route'); @@ -338,15 +339,15 @@ public function it_returns_the_best_match_when_a_browser_locale_is_used() $this->assertEquals('nl', $response->original); } - /** @test */ - public function it_looks_for_the_current_app_locale() + #[Test] + public function it_looks_for_the_current_app_locale(): void { $this->setSupportedLocales(['en', 'nl']); $this->setAppLocale('nl'); Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); $response = $this->get('some/route'); @@ -355,8 +356,8 @@ public function it_looks_for_the_current_app_locale() $this->assertEquals('nl', $response->original); } - /** @test */ - public function trusted_detectors_ignore_supported_locales_and_may_set_any_locale() + #[Test] + public function trusted_detectors_ignore_supported_locales_and_may_set_any_locale(): void { $this->setSupportedLocales(['en']); $this->setAppLocale('en'); @@ -370,7 +371,7 @@ public function trusted_detectors_ignore_supported_locales_and_may_set_any_local Route::group($routeAction, function () { Route::get('some/route', function () { return App::getLocale(); - })->middleware(['web']); + })->middleware(['web', \CodeZero\Localizer\Middleware\SetLocale::class]); }); $response = $this->get('some/route'); diff --git a/tests/Stubs/Kernel.php b/tests/Stubs/Kernel.php deleted file mode 100644 index 691f8f8..0000000 --- a/tests/Stubs/Kernel.php +++ /dev/null @@ -1,25 +0,0 @@ - [ - \Illuminate\Cookie\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - // \Illuminate\Session\Middleware\AuthenticateSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \CodeZero\Localizer\Middleware\SetLocale::class, // <== Added Middleware Here - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - ]; -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 25892ec..5f29dc2 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -21,24 +21,6 @@ protected function setUp(): void Config::set('app.key', Str::random(32)); } - /** - * Resolve application Console Kernel implementation. - * - * @param \Illuminate\Foundation\Application $app - * - * @return void - */ - protected function resolveApplicationHttpKernel($app): void - { - // In Laravel 6+, we need to add the middleware to - // $middlewarePriority in Kernel.php for route - // model binding to work properly. - $app->singleton( - 'Illuminate\Contracts\Http\Kernel', - 'CodeZero\Localizer\Tests\Stubs\Kernel' - ); - } - /** * Get the packages service providers. * diff --git a/tests/Unit/LocalizerTest.php b/tests/Unit/LocalizerTest.php index fb85c2b..ef45338 100644 --- a/tests/Unit/LocalizerTest.php +++ b/tests/Unit/LocalizerTest.php @@ -2,6 +2,7 @@ namespace CodeZero\Localizer\Tests\Unit; +use PHPUnit\Framework\Attributes\Test; use CodeZero\Localizer\Detectors\Detector; use CodeZero\Localizer\Localizer; use CodeZero\Localizer\Stores\Store; @@ -9,10 +10,10 @@ use Illuminate\Support\Facades\App; use Mockery; -class LocalizerTest extends TestCase +final class LocalizerTest extends TestCase { - /** @test */ - public function it_loops_through_the_detectors_and_returns_the_first_supported_locale() + #[Test] + public function it_loops_through_the_detectors_and_returns_the_first_supported_locale(): void { $supportedLocales = ['en', 'nl']; $detectors = [ @@ -26,8 +27,8 @@ public function it_loops_through_the_detectors_and_returns_the_first_supported_l $this->assertEquals('nl', $localizer->detect()); } - /** @test */ - public function it_returns_the_first_match_if_an_array_of_locales_is_detected() + #[Test] + public function it_returns_the_first_match_if_an_array_of_locales_is_detected(): void { $supportedLocales = ['en', 'nl']; $detectors = [ @@ -39,8 +40,8 @@ public function it_returns_the_first_match_if_an_array_of_locales_is_detected() $this->assertEquals('nl', $localizer->detect()); } - /** @test */ - public function trusted_detectors_ignore_supported_locales_and_may_set_any_locale() + #[Test] + public function trusted_detectors_ignore_supported_locales_and_may_set_any_locale(): void { $supportedLocales = ['en']; $detectors = [ @@ -55,8 +56,8 @@ public function trusted_detectors_ignore_supported_locales_and_may_set_any_local $this->assertEquals('nl', $localizer->detect()); } - /** @test */ - public function it_skips_null_and_false_and_empty_values() + #[Test] + public function it_skips_null_and_false_and_empty_values(): void { App::instance(Detector::class, Mockery::mock(Detector::class)->allows()->detect()->andReturns('')->getMock()); @@ -75,8 +76,8 @@ public function it_skips_null_and_false_and_empty_values() $this->assertEquals('nl', $localizer->detect()); } - /** @test */ - public function it_skips_null_and_false_and_empty_values_from_trusted_detectors() + #[Test] + public function it_skips_null_and_false_and_empty_values_from_trusted_detectors(): void { App::instance(Detector::class, Mockery::mock(Detector::class)->allows()->detect()->andReturns('')->getMock()); @@ -98,8 +99,8 @@ public function it_skips_null_and_false_and_empty_values_from_trusted_detectors( $this->assertEquals('nl', $localizer->detect()); } - /** @test */ - public function it_returns_false_if_no_supported_locale_could_be_detected() + #[Test] + public function it_returns_false_if_no_supported_locale_could_be_detected(): void { $supportedLocales = ['en']; $detectors = [ @@ -113,8 +114,8 @@ public function it_returns_false_if_no_supported_locale_could_be_detected() $this->assertFalse($localizer->detect()); } - /** @test */ - public function it_loops_through_the_stores_and_calls_the_store_method_with_the_given_locale() + #[Test] + public function it_loops_through_the_stores_and_calls_the_store_method_with_the_given_locale(): void { $stores = [ Mockery::mock(Store::class)->expects()->store('nl')->once()->getMock(), @@ -127,8 +128,8 @@ public function it_loops_through_the_stores_and_calls_the_store_method_with_the_ $localizer->store('nl'); } - /** @test */ - public function it_accepts_class_names_instead_of_instances_in_the_constructor() + #[Test] + public function it_accepts_class_names_instead_of_instances_in_the_constructor(): void { App::instance(Store::class, Mockery::mock(Store::class)->expects()->store('nl')->once()->getMock()); App::instance(Detector::class, Mockery::mock(Detector::class)->expects()->detect()->once()->getMock()); @@ -142,8 +143,8 @@ public function it_accepts_class_names_instead_of_instances_in_the_constructor() $localizer->store('nl'); } - /** @test */ - public function you_can_set_the_supported_locales_at_runtime() + #[Test] + public function you_can_set_the_supported_locales_at_runtime(): void { $supportedLocales = ['en']; $detectors = [