From c2eb9bd66a2ce722c784218c0020f96615df63bc Mon Sep 17 00:00:00 2001 From: Konstantinos Arvanitakis Date: Wed, 26 Aug 2026 13:26:29 +0300 Subject: [PATCH] Hotfix: Updating LocaleMiddleware to handle more than 2 languages --- docs/localization.md | 27 +++++++++++++++++++++ src/Localization/LocaleMiddleware.php | 34 ++++++++++++++++++--------- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/docs/localization.md b/docs/localization.md index 18fe74d..9a3bff9 100644 --- a/docs/localization.md +++ b/docs/localization.md @@ -105,6 +105,33 @@ $language = $request->attributes->get('language'); // Lunar\Models\Language in Use `$language->id` when querying Lunar's translatable content (e.g. `Url::where('language_id', ...)`). +### Shared view data — language switcher and `hreflang` tags + +The middleware also shares two variables with every view, via `View::share()`, so a layout's +language switcher or `hreflang` tags don't have to recompute the language list themselves: + +```blade +{{-- current locale --}} +{{ $currentLocale }} {{-- e.g. "el" --}} + +{{-- every OTHER configured language, each with its own URL for the current page --}} +@foreach ($altLocales as $altLocale) + {{ $altLocale['name'] }} +@endforeach +``` + +`$altLocales` is a **collection**, not a single value — deliberately, so it scales to any number +of configured languages rather than assuming exactly two. Each entry is a plain array: + +| Key | Description | +|---|---| +| `code` | The language's `Lunar\Models\Language::code` (e.g. `en`) | +| `name` | The language's display name | +| `url` | The **current route**, re-generated with that language's code — via `route($routeName, [...])` when the current request matched a named route, or a bare `/{code}` fallback otherwise | + +A 3+ language store gets one `$altLocales` entry per additional language automatically — nothing +about this shape assumes or special-cases a two-language store. + --- ## Single-language shops diff --git a/src/Localization/LocaleMiddleware.php b/src/Localization/LocaleMiddleware.php index 5bd1f51..5cd694d 100644 --- a/src/Localization/LocaleMiddleware.php +++ b/src/Localization/LocaleMiddleware.php @@ -62,24 +62,36 @@ class LocaleMiddleware } /** - * Shares the current/alternate locale (and the alternate's URL) with all - * views, so the header language switcher and layout hreflang tags don't - * have to recompute it. + * 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 { - $altLanguage = $languages->firstWhere('code', '!=', $language->code); $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('altLocale', $altLanguage?->code); - View::share( - 'altLocaleUrl', - $altLanguage && $routeName - ? route($routeName, array_merge($route->parameters(), ['locale' => $altLanguage->code])) - : ($altLanguage ? url('/'.$altLanguage->code) : null), - ); + View::share('altLocales', $altLocales); } private function redirectToLocalizedUrl(Request $request, Collection $languages): Response