calamandrei-lorenzo / laravel-browser-lang
Auto detect browser language with middleware.
Package info
github.com/CalamandreiLorenzo/laravel-browser-lang
pkg:composer/calamandrei-lorenzo/laravel-browser-lang
Requires
- php: ^8.2
- laravel/framework: ^12.0|^13.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.5|^12.0|^13.0
- roave/security-advisories: dev-latest
This package is auto-updated.
Last update: 2026-08-20 08:44:08 UTC
README
Auto detect browser default language
This package makes available a middleware that auto-detects the language accepted by the browser and sets it as the current locale.
The Accept-Language header is parsed following RFC 9110: quality factors drive the priority, q=0 marks a refused
language, and the * wildcard is ignored. The first language the application can actually serve wins.
Compatibility
| Package | PHP | Laravel |
|---|---|---|
| 3.x | 8.2 – 8.4 | 12.x, 13.x |
| 2.x | 8.1 – 8.4 | 10.x – 13.x, see below |
| 1.x | 7.4 | 6.x, 7.x |
Install 3.x. Version 2.0.0 declares support for Laravel 10 and 11, which turned out to be a claim Composer cannot honour: both branches are past end of life and carry an unpatched CRLF injection advisory covering every release in them (PKSA-mdq4-51ck-6kdq, fixed in 12.60.0 and 13.10.0 only). Composer refuses to install any 10.x or 11.x, so on those versions 2.0.0 does not resolve at all, and on Laravel 12 and 13 it behaves exactly like 3.x. Dropping the two dead branches is the only difference between them, which is why 3.x is a major: the supported range narrowed.
Installing
composer require calamandrei-lorenzo/laravel-browser-lang
Optionally, you can publish the config file:
php artisan vendor:publish --provider="CalamandreiLorenzo\\LaravelBrowserLang\\ServiceProvider" --tag=config
Configuration
return [ // Request header inspected by the middleware. 'language_header' => 'Accept-Language', // Class turning the header into a locale, resolved through the container. 'header_conversion' => CalamandreiLorenzo\LaravelBrowserLang\HeaderConversion::class, // Locales the application can serve. The value listed here is the one // handed to App::setLocale(), so "pt_BR" and "pt-BR" are both fine. 'available_locales' => ['en'], // Used when the browser asks for nothing the application can serve. // null keeps the current application locale. 'fallback_locale' => null, // Alias registered for the middleware. Set to null to register it yourself. 'middleware_alias' => 'detect-language', ];
Locale matching is done in three steps, in order: exact match (en-GB against en-GB), primary subtag
(en-GB against en), and finally any available locale sharing the primary subtag (en-US against en-GB).
Usage
The detect-language alias is registered by the package, so the middleware is usable right away:
Route::middleware('detect-language')->group(static function () { // routes });
Registering it globally
In bootstrap/app.php:
use CalamandreiLorenzo\LaravelBrowserLang\Http\Middleware\BrowserLang; return Application::configure(basePath: dirname(__DIR__)) ->withMiddleware(function (Middleware $middleware) { $middleware->append(BrowserLang::class); }) ->create();
Custom header conversion
Implement HeaderConversionInterface and point the config at it:
use CalamandreiLorenzo\LaravelBrowserLang\HeaderConversionInterface; class MyHeaderConversion implements HeaderConversionInterface { public function convert(string $header): string { // ... } }
// config/browser-lang.php 'header_conversion' => MyHeaderConversion::class,
The class is resolved through the container, so constructor injection is available. The interface is bound too,
which means HeaderConversionInterface can be type-hinted anywhere in the application.
Upgrading to 3.x
- PHP 8.2 and Laravel 12 are now the minimum supported versions. Coming from 2.0.0, this is the only change: no code, config or behaviour differs between the two.
- Everything below applies when coming from 1.x.
- The middleware now reads the
header_conversionconfig key. Releases before 2.0 documentedheader_conversionbut actually read an undocumentedresolverkey; both are accepted,header_conversiontakes precedence. - When no requested language is available the middleware no longer fails; it falls back to
fallback_locale, or to the current application locale when that is null. - Quality factors are now honoured, so the browser's preferred language wins instead of the leftmost one.
- A
detect-languagemiddleware alias is registered automatically. Setmiddleware_aliastonullto opt out.
Testing
composer update
composer test
composer check-style
composer.lock is not committed, so composer update is what resolves the dependencies. Both pipelines run
composer test against every supported PHP and Laravel combination, and composer check-style on the newest.
Contributing
The repository is mirrored on both hosts, and either issue tracker works: GitHub or GitLab.
You can contribute in one of three ways:
- File bug reports using one of the issue trackers.
- Answer questions or fix bugs reported there.
- Contribute new features.
The code contribution process is not very formal. You just need to make sure that you follow the PSR-1, PSR-4 and PSR-12 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable.
License
MIT