# Phase 15: Locale Detection & Routing - Research **Researched:** 2026-02-02 **Domain:** WinterCMS locale routing, URL prefix patterns, browser detection, preference persistence **Confidence:** HIGH ## Summary This phase extends the existing Golem15\Translate plugin infrastructure to implement URL-based locale routing with automatic browser detection and persistent preference storage. The core infrastructure already exists and is well-implemented - LocaleMiddleware handles the priority cascade (URL > User DB > Session > Browser > Default), Translator manages locale state, and LocalePicker/AlternateHrefLangElements components are available. The primary work involves: (1) Setting the `locale_manually_set` cookie when switching locales via URL prefix or LocalePicker, (2) Creating a language suggestion banner component for mismatched browser languages, (3) Adding a language preference section to the account settings page, and (4) Integrating AlternateHrefLangElements into all layouts for SEO. **Primary recommendation:** Leverage existing plugin infrastructure completely. Focus on UI integration (language switcher in header, suggestion banner, account settings), cookie management enhancement, and ensuring logged-in users' preferences sync to DB. ## Standard Stack The established libraries/tools for this domain: ### Core | Library | Version | Purpose | Why Standard | |---------|---------|---------|--------------| | Golem15\Translate | Current | Full translation infrastructure | Already in codebase, handles locales, middleware, components | | Golem15\User | Current | User model with `preferred_locale` field | Already has preference field, integrated with middleware | ### Supporting | Library | Version | Purpose | When to Use | |---------|---------|---------|-------------| | LocaleMiddleware | Current | Priority cascade for locale detection | Already registered in CmsController | | Translator | Current | Singleton for locale state management | Use for all locale operations | | LocalePicker | Current | Component for language switching | Embed in header | | AlternateHrefLangElements | Current | Generate hreflang tags | Add to all layouts | ### Already Configured The following is already configured in `/config/golem15/translate/config.php`: - `prefixDefaultLocale: false` - English (default) has no URL prefix - `disableLocalePrefixRoutes: false` - URL prefixes enabled - `browserDetection.enabled: true` - Browser detection active - `browserDetection.manualSelectionCookie: 'locale_manually_set'` - Cookie name defined **No additional package installation required.** ## Architecture Patterns ### Existing Infrastructure Overview ``` plugins/golem15/translate/ |-- classes/ | |-- LocaleMiddleware.php # Priority cascade: URL > User > Session > Browser > Default | |-- Translator.php # Singleton, setLocale/getLocale/loadLocaleFromRequest |-- components/ | |-- LocalePicker.php # onSwitchLocale handler, forceUrl property | |-- AlternateHrefLangElements.php # Generates hreflang tags |-- config/config.php # browserDetection settings |-- routes.php # Registers /{locale}/* route groups ``` ### Pattern 1: Locale Priority Cascade (Existing) **What:** The middleware implements a clear priority order for locale resolution. **When to use:** Already runs on every CMS request. **Implementation (from LocaleMiddleware.php):** ```php // Priority 1: URL prefix (explicit override) if (!$translator->loadLocaleFromRequest()) { // Priority 2: Authenticated user's preferred_locale from DB if (!$this->loadLocaleFromUser($translator)) { // Priority 3: Session (previous selection) $localeLoaded = $translator->loadLocaleFromSession(); // Priority 4: Browser Accept-Language (only without manual selection cookie) if (!$localeLoaded && !$this->hasManualLocaleSelection($request)) { $localeLoaded = $this->loadLocaleFromBrowser($translator, $request); } // Priority 5: Default locale fallback if (!$localeLoaded) { $translator->setLocale($translator->getDefaultLocale()); } } } ``` ### Pattern 2: URL Prefix Routing (Existing) **What:** Routes with locale prefix are registered dynamically. **When to use:** Automatically for any URL with `/pl/` or `/de/` prefix. **Implementation (from routes.php):** ```php $locale = $translator->getLocale(); Route::group(['prefix' => $locale, 'middleware' => 'web'], function () { Route::any('{slug?}', 'Cms\Classes\CmsController@run')->where('slug', '(.*)?'); }); ``` ### Pattern 3: LocalePicker Component Usage **What:** Provides locale switching with proper URL handling. **When to use:** In theme partials for language switcher. **Example:** ```twig {# In layout or header partial #} {% component 'localePicker' %} {# Custom implementation #}
{{ 'Choose your preferred language for the site.'|_ }}