aitor24/localizer
Localizer is a Laravel package for adding multilingual support to your app. It helps detect and set the user’s locale, manage language switching, and keep translations organized, making it easier to build localized Laravel applications with minimal configuration.
aitor24/localizer is a small, single-purpose package that hooks Laravel 5.5's locale resolution into the browser's Accept-Language header. At ~21 stars and one release from 2017, treat the source as the documentation: before installing, open src/ in the repo (the whole package is a handful of files) and read the service provider, the middleware, and config/localizer.php. That five-minute read will save you guessing at config keys.
Install and wire it up:
composer require aitor24/localizer
php artisan vendor:publish --provider="Aitor24\Localizer\LocalizerServiceProvider"
Laravel 5.5 package auto-discovery should register the provider, but this package predates widespread adoption of that convention — if composer require doesn't expose a Localizer binding, add Aitor24\Localizer\LocalizerServiceProvider::class to config/app.php.
Then:
config/localizer.php and set the locales your app actually ships with (e.g. ['en', 'es', 'fr']) plus the fallback.app/Http/Kernel.php. Put it in the web group after StartSession and before anything that formats dates/translations.config/app.php has a sane 'fallback_locale'; the package will fall through to it when the browser's language isn't in your list.Your first use case should be a stateless marketing/dashboard site with no per-user language preference stored in the database. Point a browser configured to es-ES at any page and confirm app()->getLocale() returns es (dump it in a Blade view or dd(app()->getLocale()) in a route) while the English browser still sees en.
Middleware-group placement. The canonical integration is adding it to the web group so every browser request gets a locale. If you have API routes that return localized payloads, add it to api explicitly — the package's detection relies on the request header, which is present on API calls too, but you don't want it clobbering a locale set per-request in a service.
Override precedence. The useful pattern is to treat the package as the default and layer explicit choices on top:
// app/Http/Middleware/SetLocale.php — register AFTER the Localizer middleware
public function handle($request, Closure $next)
{
if ($locale = $request->session()->get('locale')) {
app()->setLocale($locale);
}
return $next($request);
}
This gives you: explicit user choice (session) → browser detection (package) → config('app.fallback_locale'). Route-model-bound users who have a language column should be applied in a later middleware again, so the order is session → user preference → browser → fallback.
Language switcher. A POST /locale/{locale} route that validates against your configured list, writes session(['locale' => $locale]), and back()s is all you need; the next request is caught by the middleware above. Alternatively expose a query param (?lang=es) if the package supports it in its config — check config/localizer.php for a query_param / url_segment key before building one yourself.
Blade and validation. Once the locale is set, everything Laravel already does works: @lang, __(), trans_choice(), and resources/lang/{locale}/*.php. Add lang/{locale}/validation.php for each configured locale or browser-detected users will see English validation messages on a Spanish page.
Locale-aware URLs (optional). If you want /es/...-prefixed routes, keep the package purely for the initial redirect into a prefix and let a route-group middleware set the locale thereafter. Don't try to make it do URL segmentation — it's a header-reading middleware, not a routing layer.
Caching. For pages rendered under different locales, key your response cache by app()->getLocale(); otherwise the first browser to hit a route poisons the cache for everyone.
Accept-Language is a ranked list, not a string. Browsers send things like es-ES,es;q=0.9,en;q=0.8. Confirm the package parses and respects q weights and does region-to-language fallback (es-MX → es). If it does a naive substr() match, you'll get empty locales or mismatches on 3-letter codes.SubstituteBindings (so {locale} isn't a factor) and before any middleware that formats money/dates. If translations look one request behind, the middleware is running after the view has been constructed — move it earlier.config/localizer.php key you add manually will be lost on the next (hypothetical) publish; more importantly, verify the config keys match the code in the release you installed, because README/example drift is common on stale packages.Accept-Language header API and Kernel structure changed in 5.7+/6.x (middleware handle signature, RouteServiceProvider namespace changes). If you're on Laravel 8+, prefer a maintained alternative (e.g. mcamara/laravel-localization) or a ~30-line middleware of your own — the package's total surface area is small enough to vendor.app()->getLocale() in an artisan command, queued job, or PHPUnit test won't benefit from this package. Always set locale explicitly in those contexts, and set 'locale' => 'en' in phpunit.xml so tests are deterministic.php artisan route:cache doesn't affect middleware, but config:cache does — after publishing config, always run php artisan config:clear (and route:clear) if detection suddenly ignores your locale list.request()->header('Accept-Language'), config('localizer'), and app()->getLocale() inside the middleware to see exactly where the choice is made. Since the package is tiny, adding a Log::debug() line in its handle() method is faster than guessing.app/ so the dependency stays swappable.composer.json and keep a note of what you changed.How can I help you explore Laravel packages today?