chinleung/laravel-locales
Add easy locale support to Laravel with simple config and helper functions. Set and get the current locale with locale(), and manage supported locales with locales(), prioritizing app.locales over package config. Supports Laravel 6–13 (versioned).
Publish the configuration file to define supported locales:
php artisan vendor:publish --provider="ChinLeung\LaravelLocales\LaravelLocalesServiceProvider" --tag="config"
Update config/app.php with your supported locales:
'locales' => ['en', 'fr', 'es', 'zh'],
Use the locale() helper to get/set the current locale:
// Get current locale
$currentLocale = locale(); // 'en'
// Set new locale
locale('fr'); // 'fr'
Use the locales() helper to manage supported locales:
// Get supported locales
$supportedLocales = locales(); // ['en', 'fr', 'es', 'zh']
// Update supported locales
locales(['en', 'fr', 'zh']);
Use the locale() helper to dynamically switch locales based on user input or session:
public function switchLocale(Request $request, $locale)
{
if (in_array($locale, locales())) {
locale($locale);
$request->session()->put('locale', $locale);
return back();
}
abort(400, 'Unsupported locale');
}
Combine with Laravel’s route groups and middleware for URL-based locale switching:
Route::middleware(['set-locale'])->group(function () {
Route::get('/blog', [BlogController::class, 'index']);
});
Create a middleware (app/Http/Middleware/SetLocale.php):
public function handle(Request $request, Closure $next)
{
$locale = $request->segment(1);
if (in_array($locale, locales())) {
locale($locale);
}
return $next($request);
}
Use the locales() helper to fetch supported locales for UI dropdowns:
public function getSupportedLocales()
{
return collect(locales())->mapWithKeys(function ($code) {
return [$code => trans("languages.$code")];
});
}
Implement fallback locales in middleware or controllers:
public function handle(Request $request, Closure $next)
{
$locale = $request->segment(1) ?? session('locale') ?? config('app.fallback_locale');
if (!in_array($locale, locales())) {
$locale = config('app.fallback_locale');
}
locale($locale);
return $next($request);
}
Leverage associative arrays for UI-friendly labels:
// config/locales.php
'locales' => [
'en' => ['code' => 'en', 'name' => 'English'],
'fr' => ['code' => 'fr', 'name' => 'Français'],
],
Access in Blade:
<select>
@foreach (locales() as $code => $locale)
<option value="{{ $code }}" {{ locale() === $code ? 'selected' : '' }}>
{{ $locale['name'] ?? $code }}
</option>
@endforeach
</select>
Combine with spatie/laravel-translatable for model translations:
use ChinLeung\LaravelLocales\Facades\Locale;
public function store(Request $request)
{
$post = new Post();
$post->translate()->title = $request->input('title_' . Locale::get());
$post->save();
}
Config Overrides:
locales() helper overrides app.locales in config/app.php. Ensure consistency between both.locales() sparingly; prefer config('app.locales') for static lists.Middleware Conflicts:
SetLocaleMiddleware.app/Http/Kernel.php if using custom logic:
protected $middleware = [
// Remove or comment out:
// \Illuminate\Foundation\Http\Middleware\SetLocale::class,
];
Route Caching:
php artisan route:cache if locales are dynamic.php artisan route:clear in deployment.Session Bloat:
Associative Array Quirks:
['en' => 'English']) require exact key matching in locales() calls.$newLocales = ['en' => 'English', 'fr' => 'Français'];
if (count(array_intersect(array_keys($newLocales), locales())) > 0) {
locales(array_keys($newLocales));
}
Check Current Locale: Use Tinker to verify the active locale:
php artisan tinker
>>> locale();
Validate Supported Locales:
Ensure locales() returns the expected array:
>>> locales();
['en', 'fr', 'es']
Middleware Debugging: Add logging to debug locale-setting middleware:
public function handle($request, Closure $next)
{
\Log::info('Setting locale to:', [$request->segment(1)]);
locale($request->segment(1));
return $next($request);
}
Route Debugging:
Use route:list to verify locale-based routes:
php artisan route:list | grep "/blog"
Dynamic Locale Providers:
Override the locales() helper in a service provider:
use ChinLeung\LaravelLocales\Facades\Locale;
public function boot()
{
Locale::macro('getDynamicLocales', function () {
return Cache::remember('supported_locales', 60, function () {
return DB::table('locales')->pluck('code')->toArray();
});
});
}
Custom Facade Macros:
Add locale-specific macros to the App facade:
use Illuminate\Support\Facades\Facade;
Facade::macro('getCurrentLocaleName', function () {
$code = app()->getLocale();
$locales = config('locales.locales', []);
return $locales[$code]['name'] ?? $code;
});
Locale Fallback Middleware: Create a reusable fallback middleware:
public function handle($request, Closure $next)
{
$locale = $request->segment(1) ?? session('locale') ?? config('app.fallback_locale');
if (!in_array($locale, locales())) {
session()->flash('error', 'Unsupported locale. Falling back to default.');
locale(config('app.fallback_locale'));
return redirect()->back();
}
locale($locale);
return $next($request);
}
Avoid locales() in Loops:
Cache the result of locales() if used frequently:
$supportedLocales = locales(); // Cache this value
foreach ($supportedLocales as $locale) { ... }
URL-Based Locales:
For high-traffic sites, URL-based locales (e.g., /fr/blog) may increase route resolution time.
Fix: Use route caching with locale-specific groups:
php artisan route:cache --locale=fr
Session Storage: If using session-based locales, consider:
locale → l).Locale Switching Tests: Test locale changes in controllers and middleware:
public function test_locale_switching()
{
$response = $this->post('/locale/fr');
$response->assertRedirect();
$this->assertEquals('fr', locale());
}
Fallback Tests: Verify fallback behavior for unsupported locales:
public function test_locale_fallback()
{
$this->app->setLocale('xx');
$this->assertEquals(config('app.fallback_locale'), locale());
}
Route Tests: Test locale-aware routes:
public function test_locale_routes()
{
$response = $this->get('/fr/blog');
$response->assertStatus(200);
$this->assertEquals('fr', locale());
}
Configuration Tests:
Ensure locales() respects `config/app.php
How can I help you explore Laravel packages today?