From 0f18bafe9eaa4957d8d98f63ef7cd0a002dbeef0 Mon Sep 17 00:00:00 2001 From: Malek Date: Wed, 9 Sep 2026 16:35:06 +0100 Subject: [PATCH] fix(radio): resolve common country exonyms Intl.DisplayNames omits The radio country parser builds its name->code table from the Intl.DisplayNames primary English label, so common names and exonyms that are not that exact label fell through to fail-closed. Requests like "play radio in Turkey" (ICU renders TR as "Turkiye"), plus Myanmar/Burma, UAE, Holland, Swaziland, East Timor, Cabo Verde and Vatican, returned no stations -- and dataset stations whose country field uses those names were dropped from country filtering. Add each as an unambiguous alias to the existing bounded map. Ambiguous names (bare "Congo", "Korea") are deliberately left to fail closed. Adds unit coverage for the new resolutions and the preserved fail-closed behavior. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 5 +++++ docs/CURRENT-STATE.md | 8 +++++--- src/data/radioCountry.js | 16 ++++++++++++++++ src/data/radioCountry.test.mjs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21544fa..450a83d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md ### Fixed +- Radio country filtering and voice country requests now resolve common English + names and exonyms that `Intl.DisplayNames`' primary label omits, so requests + like "play radio in Turkey" no longer fail closed (Turkey → Türkiye, plus + Myanmar/Burma, UAE, Holland, Swaziland, East Timor, Cabo Verde, Vatican). + Ambiguous names such as a bare "Congo" or "Korea" still fail closed. - Mapped-site outages show their scheduled retry countdown and distinguish known Overpass rate limits, timeouts, and query failures. Search feedback no longer claims a refresh succeeded while the layer is unavailable or loading. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 1f4d861..100213e 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -1684,9 +1684,11 @@ preserves object identity for an idempotent repeat of the same generation, and degrades without replacement if a fresh response presents an older generation. Snapshot records mark community metadata as untrusted, and Radio tool results omit station names so directory text never becomes model instruction context. -One bounded country parser maps recognized ISO codes and English/common names -through proxy metadata and final station selection, while malformed, non-ISO, -control-containing, and oversized inputs fail closed. Literal or resolved +One bounded country parser maps recognized ISO codes and English/common names — +including widely used exonyms the Intl display label omits (Turkey, Holland, +Burma, and similar) — through proxy metadata and final station selection, while +malformed, non-ISO, control-containing, ambiguous, and oversized inputs fail +closed. Literal or resolved non-global IPv4/IPv6 targets are refused. Destroy fully releases the Radio audio session, voice ducking/restoration, request state, filter, selection, volume, accepted snapshot, and feed telemetry before re-initialization; monotonic diff --git a/src/data/radioCountry.js b/src/data/radioCountry.js index f72a50f..6b056ce 100644 --- a/src/data/radioCountry.js +++ b/src/data/radioCountry.js @@ -55,6 +55,22 @@ for (const [name, code] of Object.entries({ brunei: 'BN', 'cape verde': 'CV', 'the netherlands': 'NL', + // Common English names and exonyms whose code is not reachable through the + // Intl.DisplayNames primary label, so they would otherwise fail closed + // (verified against ICU 77): TR resolves only as 'Türkiye', MM only as + // 'Myanmar (Burma)', AE only as 'United Arab Emirates', SZ as 'Eswatini', + // TL as 'Timor-Leste'. Each maps to exactly one country; ambiguous names + // (e.g. bare "Congo") are deliberately left to fail closed. + turkey: 'TR', + turkiye: 'TR', + myanmar: 'MM', + burma: 'MM', + uae: 'AE', + holland: 'NL', + swaziland: 'SZ', + 'east timor': 'TL', + 'cabo verde': 'CV', + vatican: 'VA', })) COUNTRY_NAME_TO_CODE.set(countryKey(name), code); function canonicalCountryName(code) { diff --git a/src/data/radioCountry.test.mjs b/src/data/radioCountry.test.mjs index 70511de..3270b46 100644 --- a/src/data/radioCountry.test.mjs +++ b/src/data/radioCountry.test.mjs @@ -18,6 +18,37 @@ test('Radio country normalization maps ISO codes and bounded common names', () = } }); +test('Radio country normalization resolves common English names/exonyms ICU misses', () => { + // Each of these fails closed against the Intl.DisplayNames primary label + // alone (e.g. TR is "Türkiye", MM is "Myanmar (Burma)", AE is "United Arab + // Emirates"), so a request like "play radio in Turkey" would return nothing. + for (const [input, code] of [ + ['Turkey', 'TR'], + ['Turkiye', 'TR'], + ['Myanmar', 'MM'], + ['Burma', 'MM'], + ['UAE', 'AE'], + ['U.A.E.', 'AE'], + ['Holland', 'NL'], + ['Swaziland', 'SZ'], + ['East Timor', 'TL'], + ['Cabo Verde', 'CV'], + ['Vatican', 'VA'], + ]) { + const result = normalizeRadioCountryInput(input); + assert.equal(result.valid, true, input); + assert.equal(result.code, code, input); + } +}); + +test('Ambiguous country names still fail closed (no broadened selection)', () => { + // Two states share the name "Congo" (CD/CG), so a bare mention must not + // resolve to either — it stays low-confidence and fails closed. + for (const input of ['Congo', 'Korea']) { + assert.equal(normalizeRadioCountryInput(input).valid, false, input); + } +}); + test('Radio country normalization rejects malformed, non-ISO, and oversized values', () => { for (const input of [ 'ZZ',