I'm working on a multilingual Laravel 12 app. All my routes are grouped under a {locale} prefix, like this:
Route::prefix('{locale}')
->group(function () {
Route::get('/activities/{activity}', [ActivityController::class, 'show'])
->name('activities.show');
});
In my Localization middleware, I set the locale and define URL defaults:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\Response;
/**
* Middleware to set the application's locale based on URL parameter, user preference or session.
*/
class Localization
{
/**
* Handle an incoming request and set the locale.
*
* @param Request $request The incoming HTTP request instance.
* @param Closure(Request): Response $next The next middleware to call.
*/
public function handle(Request $request, Closure $next): Response
{
$locale = $request->route('locale')
?? optional(auth()->user())->language
?? session('locale', config('app.locale'));
if (! array_key_exists($locale, config('app.available_locales'))) {
$locale = config('app.locale');
}
App::setLocale($locale);
URL::defaults(['locale' => $locale]);
session()->put('locale', $locale);
return $next($request);
}
}
I am getting this error with my controllers:
TypeError: App\Http\Controllers\ActivityController::show(): Argument #1 ($activity) must be of type App\Models\Activity, string given
My controller looks like this:
/**
* Display the details for a given activity.
*/
public function show(Activity $activity): View
{
abort_unless($activity->isPublished(), 404);
return view('activities.show', [
'activity' => $activity,
'title' => $activity->title,
]);
}
If I manually add Request $request or string $locale before $activity, it works - but I'd really prefer not to have to include $locale or $request in every controller action.
Middleware priority is correct: Localization runs before SubstituteBindings.
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
// Add Localization middleware to web group
$middleware->web(append: [
Localization::class,
]);
// Prioritize Localization middleware before SubstituteBindings
$middleware->prependToPriorityList(
before: SubstituteBindings::class,
prepend: Localization::class,
);
})
Is there a clean way to keep my {locale} route prefix and make implicit model binding work without adding $locale or $request to every controller method?
Is this a known limitation or am I missing something subtle?
Here is my LanguageController for better insight:
/**
* Controller handling the application language switching.
*/
class LanguageController extends Controller
{
/**
* Switch the application language.
*
* Handles changing the application language for both authenticated
* and unauthenticated users. For authenticated users, it also updates their
* language preference in the database.
*
* @param Request $request The HTTP request containing the locale.
* @return RedirectResponse Redirects back to the previous page with the new locale.
*/
public function switch(Request $request): RedirectResponse
{
$validated = $request->validate([
'locale' => ['required','string', Rule::in(array_keys(config('app.available_locales')))],
]);
$newLocale = $validated['locale'];
session()->put('locale', $newLocale);
$prev = url()->previous();
$uri = Uri::of($prev);
$segments = collect(explode('/', ltrim($uri->path(), '/')));
$available = array_keys(config('app.available_locales'));
if ($segments->isNotEmpty() && in_array($segments->first(), $available, true)) {
$segments[0] = $newLocale;
} else {
$segments->prepend($newLocale);
}
$newPath = '/'.$segments->implode('/');
$target = (string) $uri->withPath($newPath);
return redirect()->to($target);
}
}
-
I removed the general "best way" part from your question because without it, your problem is much more focused, and it would be a pity for the post to get closed because of that. There's no such thing as "the best" or "recommended" way - it always depends on the project, goals, and many other factors. When you ask questions like that, you'll often get a wide range of scattered answers that don't necessarily address your original problem.rozsazoltan– rozsazoltan2025年10月25日 08:54:26 +00:00Commented yesterday