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',