(not a link) so it cannot
* be reactivated. All options 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',
);
$html = '
';
foreach (Locale::SUPPORTED as $code) {
$isCurrent = $code === $this->currentLocale;
$name = htmlspecialchars(
I18n::t('locale.' . $code, [], $this->currentLocale),
ENT_QUOTES,
'UTF-8',
);
$codeAttr = htmlspecialchars($code, ENT_QUOTES, 'UTF-8');
$flag = self::flagSvg($code);
$classes = 'locale-switcher__option';
if ($isCurrent) {
$classes .= ' is-current';
}
$html .= '';
if ($isCurrent) {
$html .= ''
. $flag
. '' . $name . ' '
. ' ';
} else {
$url = '/locale?set=' . rawurlencode($code) . '&return=' . rawurlencode($path);
$html .= ''
. $flag
. '' . $name . ' '
. ' ';
}
$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;
}
}