languages->all(); if ($languages->isEmpty()) { return $next($request); } $segment = (string) $request->segment(1); $language = $languages->firstWhere('code', $segment); if (! $language) { return $this->redirectToLocalizedUrl($request, $languages); } App::setLocale($language->code); $request->attributes->set('locale', $language->code); $request->attributes->set('language', $language); // Lets route() calls omit {locale} anywhere in the request lifecycle // (controllers, views) — without this, every route() call would need // locale passed explicitly every time. URL::defaults(['locale' => $language->code]); $this->shareLocaleViewData($request, $language, $languages); return $next($request); } /** * Shares the current locale and every OTHER available locale (each with its * own URL for the current page) with all views, so the header language * switcher and layout hreflang tags don't have to recompute it. * * `altLocales` is a collection, not a single value — firstWhere('code', '!=', * ...) would only ever surface one alternate, which happens to look correct * with exactly 2 configured languages (there's only one "other" to find) but * silently drops every locale past the first for a 3+ language store, with no * error, just fewer switcher options than actually configured. A view iterates * `$altLocales` to render as many links/dropdown entries as there are * alternates, whether that's 1 or 10. */ private function shareLocaleViewData(Request $request, Language $language, Collection $languages): void { $route = $request->route(); $routeName = $route?->getName(); $altLocales = $languages ->reject(fn (Language $other) => $other->code === $language->code) ->map(fn (Language $other) => [ 'code' => $other->code, 'name' => $other->name, 'url' => $routeName ? route($routeName, array_merge($route->parameters(), ['locale' => $other->code])) : url('/'.$other->code), ]) ->values(); View::share('currentLocale', $language->code); View::share('altLocales', $altLocales); } private function redirectToLocalizedUrl(Request $request, Collection $languages): Response { $locale = $this->negotiateLocale($request, $languages); $path = trim($request->getPathInfo(), '/'); $target = '/'.$locale.($path !== '' ? '/'.$path : ''); $query = $request->getQueryString(); if ($query) { $target .= '?'.$query; } return redirect($target); } private function negotiateLocale(Request $request, Collection $languages): string { $preferred = $request->getPreferredLanguage($languages->pluck('code')->all()); if ($preferred) { return $preferred; } return $languages->firstWhere('default', true)?->code ?? $languages->first()->code; } }