From 391985cd422762d8f919231acba329059dd3055e Mon Sep 17 00:00:00 2001 From: Hermes Date: Thu, 4 Jun 2026 19:43:23 +0000 Subject: [PATCH] fix(flags): replace hand-coded inline SVGs with official flag-icons assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous inline flag SVGs were visually broken — most notably the 'en' Union Jack, which was reduced to a single X plus a cross and did not resemble the real flag at all. The 'de' and 'ru' stripes also had slight off-by-pixel rounding errors. Switched to lipis/flag-icons (CC-BY 4.0) shipped as static files under public/img/flags/. These are the canonical, professionally-designed flag icons with correct proportions and all the details of the real flags. Loaded via plain tags (no JS, no external CDN at runtime, no FOUC, no extra request after the page is cached). Locale code mapping: en -> gb (per ADR-002, en = en-GB). Unknown locales fall back to a 1x1 transparent gif so the layout stays intact. --- app/Controllers/LocaleSwitcher.php | 70 +++++++++++------------- public/css/haus-schleusingen.css | 6 +- public/img/flags/de.svg | 5 ++ public/img/flags/en.svg | 7 +++ public/img/flags/ru.svg | 5 ++ public/img/flags/uk.svg | 6 ++ tests/Controllers/LocaleSwitcherTest.php | 45 +++++++-------- 7 files changed, 82 insertions(+), 62 deletions(-) create mode 100644 public/img/flags/de.svg create mode 100644 public/img/flags/en.svg create mode 100644 public/img/flags/ru.svg create mode 100644 public/img/flags/uk.svg diff --git a/app/Controllers/LocaleSwitcher.php b/app/Controllers/LocaleSwitcher.php index 1f2a5e3..d168874 100644 --- a/app/Controllers/LocaleSwitcher.php +++ b/app/Controllers/LocaleSwitcher.php @@ -47,7 +47,7 @@ final class LocaleSwitcher 'UTF-8', ); $currentCode = htmlspecialchars($this->currentLocale, ENT_QUOTES, 'UTF-8'); - $currentFlag = self::flagSvg($this->currentLocale); + $currentFlag = self::flagImg($this->currentLocale); $html = '
'; $html .= ' + * pointing at the official flag-icons SVG asset shipped under + * public/img/flags/. 4:3 aspect (de/gb/ua/ru), crisp at any DPI, + * no external CDN dependency. * - * - 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. + * Decorative: `alt=""` (the visible locale-switcher label and + * the 's `hreflang`/`lang` carry the accessible name). */ - public static function flagSvg(string $locale): string + public static function flagImg(string $locale): string { - $svg = match ($locale) { - 'de' => '', - 'en' => '', - 'uk' => '', - 'ru' => '', - default => '', + $src = self::flagSource($locale); + return ''; + } + + /** + * Map our locale codes to flag-icons file names. Locale "en" + * is en-GB per ADR-002, so the asset is "gb.svg". Anything we + * do not know falls back to a transparent 1×1 gif so the layout + * stays intact and the alt text (from the surrounding ) is + * the only signal. + */ + private static function flagSource(string $locale): string + { + $file = match ($locale) { + 'de' => 'de', + 'en' => 'gb', + 'uk' => 'ua', + 'ru' => 'ru', + default => null, }; - return $svg; + if ($file === null) { + return 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAACAkQBADs='; + } + return '/img/flags/' . $file . '.svg'; } /** diff --git a/public/css/haus-schleusingen.css b/public/css/haus-schleusingen.css index e60f6b9..5607074 100755 --- a/public/css/haus-schleusingen.css +++ b/public/css/haus-schleusingen.css @@ -1568,20 +1568,22 @@ footer { .locale-switcher .flag { width: 24px; - height: 16px; + height: 18px; flex: 0 0 24px; border-radius: 2px; box-shadow: 0 0 0 1px rgb(0 0 0 / 15%); display: block; + object-fit: cover; } .locale-switcher__option .flag { width: 24px; - height: 16px; + height: 18px; flex: 0 0 24px; border-radius: 2px; box-shadow: 0 0 0 1px rgb(0 0 0 / 15%); display: block; + object-fit: cover; } .locale-switcher__label { diff --git a/public/img/flags/de.svg b/public/img/flags/de.svg new file mode 100644 index 0000000..71aa2d2 --- /dev/null +++ b/public/img/flags/de.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/img/flags/en.svg b/public/img/flags/en.svg new file mode 100644 index 0000000..7991383 --- /dev/null +++ b/public/img/flags/en.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/img/flags/ru.svg b/public/img/flags/ru.svg new file mode 100644 index 0000000..cf24301 --- /dev/null +++ b/public/img/flags/ru.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/img/flags/uk.svg b/public/img/flags/uk.svg new file mode 100644 index 0000000..a339eb1 --- /dev/null +++ b/public/img/flags/uk.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/Controllers/LocaleSwitcherTest.php b/tests/Controllers/LocaleSwitcherTest.php index 5dab100..95b9356 100644 --- a/tests/Controllers/LocaleSwitcherTest.php +++ b/tests/Controllers/LocaleSwitcherTest.php @@ -97,34 +97,35 @@ final class LocaleSwitcherTest extends TestCase public static function flagDataProvider(): array { return [ - 'DE Germany' => ['de', '#FFCC00'], - 'EN UnionJack' => ['en', '#C8102E'], - 'UK Ukraine' => ['uk', '#FFD500'], - 'RU Russia' => ['ru', '#D52B1E'], + 'DE Germany' => ['de', 'de.svg'], + 'EN en-GB' => ['en', 'gb.svg'], + 'UK Ukraine' => ['uk', 'ua.svg'], + 'RU Russia' => ['ru', 'ru.svg'], ]; } #[Test] - public function flagSvgReturnsValidSvgForEverySupportedLocale(): void + public function flagImgReturnsValidImgForEverySupportedLocale(): void { foreach (Locale::SUPPORTED as $code) { - $svg = LocaleSwitcher::flagSvg($code); - self::assertStringStartsWith('', $svg); + $img = LocaleSwitcher::flagImg($code); + self::assertStringStartsWith('', $img); } } #[Test] - public function flagSvgHasFallbackForUnknownLocale(): void + public function flagImgHasFallbackForUnknownLocale(): void { - $svg = LocaleSwitcher::flagSvg('xx'); - self::assertStringStartsWith('', $svg); + $img = LocaleSwitcher::flagImg('xx'); + self::assertStringStartsWith('render(); - // The first in the document is the trigger - self::assertStringContainsString(' in the document is the trigger and it + // must point at the German flag asset under /img/flags/. + $deFlag = LocaleSwitcher::flagImg('de'); $pos = strpos($html, $deFlag); - self::assertNotFalse($pos, 'expected German flag SVG in the trigger (first in document)'); + self::assertNotFalse($pos, 'expected German flag in the trigger (first in document)'); + self::assertStringContainsString('src="/img/flags/de.svg"', $deFlag); } }