shureban/laravel-localization
Laravel middleware that auto-sets the application locale from the HTTP Accept-Language header. Install via Composer, register the service provider, and add the Localization middleware globally, per group, or per route.
Installation:
composer require shureban/laravel-localization
Add the provider to config/app.php:
Shureban\LaravelLocalization\LocalizationServiceProvider::class,
Publish Config (optional but recommended for customization):
php artisan vendor:publish --provider="Shureban\LaravelLocalization\LocalizationServiceProvider"
Register Middleware:
Add to app/Http/Kernel.php:
protected $middleware = [
\Shureban\LaravelLocalization\Localization::class,
// ...
];
or for route-specific use:
protected $routeMiddleware = [
'localization' => \Shureban\LaravelLocalization\Localization::class,
];
First Use Case:
Test locale detection by accessing a route with an Accept-Language header (e.g., en-US or fr-FR). Verify the app respects the header via:
dd(app()->getLocale()); // Should match the header's primary language
Automatic Locale Detection:
Accept-Language header and sets the app locale (e.g., en, fr).Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8, and the app defaults to fr.Fallback Logic:
config/localization.php (e.g., ['fr' => 'en']).'fallbacks' => [
'fr' => 'en',
'es' => 'en',
'de' => 'en',
],
Route-Based Localization:
Route::middleware(['localization'])->get('/fr', function () {
// Force 'fr' locale for this route
});
locale() helper for dynamic routes:
Route::get('/{locale}/posts', function ($locale) {
app()->setLocale($locale);
// ...
})->middleware('localization');
API Responses:
return response()->json([
'message' => trans('api.welcome'),
])->setEncodingOptions(JSON_UNESCAPED_UNICODE);
User-Specific Overrides:
$userLocale = session('locale') ?? app()->getLocale();
app()->setLocale($userLocale);
Blade Templates:
Use Laravel’s built-in {{ trans() }} or @lang() directives, which respect the set locale:
<h1>@lang('welcome.title')</h1>
Validation Messages: Localize validation errors by setting the locale before validation:
app()->setLocale('fr');
$validator = Validator::make($request->all(), [...]);
Database Localization:
For multilingual content (e.g., with spatie/laravel-translatable), ensure the package’s locale aligns with your model’s active locale:
$post->setLocale(app()->getLocale());
Testing:
Mock the Accept-Language header in tests:
$response = $this->withHeaders(['Accept-Language' => 'fr-FR'])
->get('/');
$this->assertEquals('fr', app()->getLocale());
Header Parsing Quirks:
Accept-Language headers gracefully. Test with edge cases like:
Accept-Language: en-US,en;q=0.5,fr-FR;q=0.3,*;q=0.1
'parser' => \Shureban\LaravelLocalization\Parsers\CustomParser::class,
Middleware Order:
Localization middleware before authentication or session middleware to ensure the locale is set early in the request lifecycle.Caching Conflicts:
trans()), ensure the package’s locale changes invalidate caches. Test with:
Cache::forget('translations.' . app()->getLocale());
Right-to-Left (RTL) Languages:
ar, he) may require additional CSS/HTML tags. The package doesn’t handle this automatically; add logic like:
<html dir="{{ app()->getLocale() === 'ar' ? 'rtl' : 'ltr' }}">
Fallback Loops:
['en' => 'fr']) can cause infinite loops. Validate your fallbacks array:
$this->assertFalse(array_intersect(['en'], array_keys(config('localization.fallbacks'))));
Log Locale Changes: Add a middleware to log locale switches:
public function handle($request, Closure $next) {
$oldLocale = app()->getLocale();
$response = $next($request);
if ($oldLocale !== app()->getLocale()) {
\Log::info("Locale changed from {$oldLocale} to " . app()->getLocale());
}
return $response;
}
Inspect Headers:
Use Laravel’s dd() to debug the raw Accept-Language header:
dd(request()->header('Accept-Language'));
Disable Middleware Temporarily:
Comment out the middleware in Kernel.php to isolate issues:
// protected $middleware = [
// // \Shureban\LaravelLocalization\Localization::class,
// ];
Custom Parsers: Extend the parser to support non-standard formats:
namespace App\Parsers;
use Shureban\LaravelLocalization\Contracts\Parser;
class CustomParser implements Parser {
public function parse($header) {
// Custom logic (e.g., prioritize 'user-locale' cookie)
return 'en';
}
}
Register in config:
'parser' => \App\Parsers\CustomParser::class,
Dynamic Fallbacks: Use a closure for dynamic fallbacks (e.g., based on user role):
'fallbacks' => [
'fr' => function () {
return auth()->user()->preferredLocale ?? 'en';
},
],
Locale-Specific Routes: Combine with Laravel’s route model binding for locale-aware resources:
Route::get('/{locale}/posts/{post}', [PostController::class, 'show'])
->middleware('localization')
->where('locale', config('localization.supported'));
API Locale Overrides: Allow API clients to override the locale via a header or query param:
if ($request->hasHeader('X-Locale')) {
app()->setLocale($request->header('X-Locale'));
}
Supported Locales:
Ensure config/localization.php includes all locales used in your app:
'supported' => ['en', 'fr', 'es', 'ar'],
en).Case Sensitivity:
The package may normalize locales to lowercase (e.g., FR → fr). Test with mixed-case inputs.
Default Locale:
Override the default in config/app.php:
'locale' => 'fr', // Default if no Accept-Language header
Performance: Avoid heavy operations in the middleware (e.g., database queries). Offload logic to:
LocaleService class.How can I help you explore Laravel packages today?