assertSame('de', Locale::resolve()); } public function testQueryParamWinsOverCookieAndHeader(): void { $this->assertSame('en', Locale::resolve('en', 'ru', 'uk')); } public function testCookieWinsOverHeader(): void { $this->assertSame('ru', Locale::resolve(null, 'ru', 'en')); } public function testHeaderUsedWhenNoQueryOrCookie(): void { $this->assertSame('en', Locale::resolve(null, null, 'en-US,de;q=0.9')); } public function testFallsBackToDefaultWhenHeaderDoesNotMatch(): void { $this->assertSame('de', Locale::resolve(null, null, 'fr-FR,it-IT')); } public function testInvalidQueryParamIsSkippedAndCookieWins(): void { // Invalid query (e.g. 'fr' which is not supported) is treated as // "no signal" from that source — we fall through to the next source. $this->assertSame('en', Locale::resolve('fr', 'en', 'en')); } public function testEmptyStringsAreTreatedAsNoSignal(): void { $this->assertSame('en', Locale::resolve('', '', 'en')); } // ────────────────────────────────────────────── // isSupported() // ────────────────────────────────────────────── #[DataProvider('provideSupportedLocales')] public function testIsSupportedReturnsTrueForKnownLocales(string $code): void { $this->assertTrue(Locale::isSupported($code)); } #[DataProvider('provideUnsupportedLocales')] public function testIsSupportedReturnsFalseForUnknownLocales(string $code): void { $this->assertFalse(Locale::isSupported($code)); } public static function provideSupportedLocales(): array { return [ 'german' => ['de'], 'uk-english' => ['en'], 'ukrainian' => ['uk'], 'russian' => ['ru'], ]; } public static function provideUnsupportedLocales(): array { return [ 'french' => ['fr'], 'empty' => [''], 'upper' => ['DE'], 'region' => ['de-DE'], 'wildcard' => ['*'], 'garbage' => ['xx'], ]; } // ────────────────────────────────────────────── // parseAcceptLanguage() // ────────────────────────────────────────────── public function testParseAcceptLanguageReturnsEmptyForEmptyHeader(): void { $this->assertSame([], Locale::parseAcceptLanguage('')); } public function testParseAcceptLanguageHandlesSingleTag(): void { $this->assertSame(['en'], Locale::parseAcceptLanguage('en')); } public function testParseAcceptLanguageSortsByQValue(): void { $this->assertSame( ['ru', 'en', 'de'], Locale::parseAcceptLanguage('de;q=0.5,en;q=0.8,ru;q=0.9') ); } public function testParseAcceptLanguageDefaultsQTo1(): void { $this->assertSame(['de', 'en'], Locale::parseAcceptLanguage('de,en;q=0.5')); } public function testParseAcceptLanguageDropsQ0(): void { // 'en;q=0' is explicitly forbidden by the client → drop it. // 'de;q=1' is the only one left. $this->assertSame(['de'], Locale::parseAcceptLanguage('en;q=0,de;q=1')); } public function testParseAcceptLanguageStripsBcp47Region(): void { $this->assertSame(['en', 'de'], Locale::parseAcceptLanguage('en-US,de-DE')); } public function testParseAcceptLanguageDropsWildcard(): void { $this->assertSame([], Locale::parseAcceptLanguage('*')); // Wildcard plus q=0 → nothing to use $this->assertSame([], Locale::parseAcceptLanguage('*,en;q=0')); } public function testParseAcceptLanguageIsStableForEqualQValues(): void { $this->assertSame( ['en', 'de', 'uk'], Locale::parseAcceptLanguage('en;q=0.8,de;q=0.8,uk;q=0.8') ); } public function testParseAcceptLanguageHandlesWhitespace(): void { $this->assertSame(['en', 'de'], Locale::parseAcceptLanguage(' en , de ; q=0.5 ')); } // ────────────────────────────────────────────── // toOgLocale() // ────────────────────────────────────────────── public function testToOgLocaleMapsToBcp47(): void { $this->assertSame('de_DE', Locale::toOgLocale('de')); $this->assertSame('en_GB', Locale::toOgLocale('en')); $this->assertSame('uk_UA', Locale::toOgLocale('uk')); $this->assertSame('ru_RU', Locale::toOgLocale('ru')); } public function testToOgLocaleFallsBackToDeForUnknown(): void { $this->assertSame('de_DE', Locale::toOgLocale('fr')); } // ────────────────────────────────────────────── // hreflangAlternates() // ────────────────────────────────────────────── public function testHreflangAlternatesBuildsFullSet(): void { $alts = Locale::hreflangAlternates('/'); $this->assertCount(4, $alts); $locales = array_column($alts, 'locale'); $this->assertSame(['de', 'en', 'uk', 'ru'], $locales); $en = array_values(array_filter($alts, static fn ($a) => $a['locale'] === 'en'))[0]; $this->assertSame('en-GB', $en['hreflang']); $this->assertStringContainsString('?lang=en', $en['href']); } }