-based dropdown shown at every viewport. The * trigger is a flag-sized button (24×16 SVG + tiny caret) that * opens to a menu of all 4 supported locales. * * Each menu option gets: * - an inline 24×16 SVG flag, * - `hreflang` and `lang` for SEO and screen readers, * - `aria-current="true"` on the active option. * * The active option is rendered as a (not a link) so it * cannot be reactivated. The trigger and every menu option are * ≥44px touch targets via CSS. */ final class LocaleSwitcher { public function __construct( private readonly string $currentLocale, private readonly string $currentPath, ) { } public function render(): string { $path = $this->sanitisePath($this->currentPath); $ariaLabel = htmlspecialchars( I18n::t('locale.switcher.aria', [], $this->currentLocale), ENT_QUOTES, 'UTF-8', ); $currentName = htmlspecialchars( I18n::t('locale.' . $this->currentLocale, [], $this->currentLocale), ENT_QUOTES, 'UTF-8', ); $currentCode = htmlspecialchars($this->currentLocale, ENT_QUOTES, 'UTF-8'); $currentFlag = self::flagSvg($this->currentLocale); $html = '
'; $html .= ''; $html .= ''; $html .= $currentFlag; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= '
'; return $html; } /** * Inline 24×16 SVG for the four supported locales. * * - DE: black/red/gold horizontal stripes (Germany) * - EN: simplified Union Jack (en-GB per ADR-002) * - UK: blue/yellow horizontal stripes (Ukraine) * - RU: white/blue/red horizontal stripes (Russia) * * Decorative: marked `aria-hidden="true"` + `focusable="false"`. * The full accessible name is conveyed by the visible label and * the 's hreflang/lang. */ public static function flagSvg(string $locale): string { $svg = match ($locale) { 'de' => '', 'en' => '', 'uk' => '', 'ru' => '', default => '', }; return $svg; } /** * Make sure the path is safe to embed as a query string value and * a redirect target. Drops query/fragment, keeps only the path. */ private function sanitisePath(string $path): string { $path = parse_url($path, PHP_URL_PATH) ?: '/'; if ($path === '' || $path[0] !== '/') { return '/'; } return $path; } }