From a4622781a2299551b60654db57f6f38b1654fb66 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 12:45:22 +0000 Subject: [PATCH] Key Cities: all nine region headings read in the page's language, and the region stays the key This corrects my own "Other" fix (9d12e59b6), which could never fire. gospel's cities-internet analysis (87a168195) showed the same shape here: key-cities.json has no row with an empty region (1,646 rows), "Other" (2) and "N/A" (19) are region VALUES, and every section heading printed its raw English data string in every locale. The fallback I translated also put a label into the grouping key. Now, as in cities-internet: the raw region string stays the grouping key (sections, :key, collapse state), with 'Other' as the data guard; the heading is projected at render by regionLabel.js from the keys the data's strings already are the English values of (dashboard.wagfRegion.*, dashboard.other, wall.na), written out in full so the build ships those areas. The poster wall, whose PosterWall prints its section name as given, groups by that projected heading, as country-posters' wall does. keyCities.posters.otherRegion is no longer used. Headless (_qa-cycle/exp-shepherd/check-keycities-regions.mjs): every one of the nine region values maps to its catalogue heading in ar, zh, es and ru, and an unknown value passes through as data. key-cities is gated, so nothing in public/ changes. --- plugin-designs/key-cities/cities-list.vue | 9 +++- .../key-cities/city-poster-wall.vue | 5 ++- plugin-designs/key-cities/regionLabel.js | 43 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 plugin-designs/key-cities/regionLabel.js diff --git a/plugin-designs/key-cities/cities-list.vue b/plugin-designs/key-cities/cities-list.vue index 7c0084ea4..71fb7674c 100644 --- a/plugin-designs/key-cities/cities-list.vue +++ b/plugin-designs/key-cities/cities-list.vue @@ -29,6 +29,7 @@ import { computed, inject, ref, onMounted, h } from 'vue' import { useI18n } from 'vue-i18n' import { sentenceParts, keep, ltr } from '@map/composables/useSentence.js' +import { regionLabel } from './regionLabel.js' import { useShadowStyles } from '@map/composables/useShadowStyles.js' import { resolveAssetsBase } from '@map/utils/embedBase.js' @@ -70,7 +71,10 @@ const shown = computed(() => { const sections = computed(() => { const by = new Map() for (const r of shown.value) { - const k = r.region || t('keyCities.posters.otherRegion') // a row with no region: a translated heading, not 'Other' + // The RAW data value is the key (sections, :key, collapse state); only the heading is translated, + // at render (secLabel below). 'Other' is a data guard for a row with no region: the sentinel + // dashboard.other projects. No row has an empty region today; "Other" and "N/A" are region values. + const k = r.region || 'Other' if (!by.has(k)) by.set(k, []) by.get(k).push(r) } @@ -87,6 +91,7 @@ const collapsed = ref({}) const isShut = (n) => !!collapsed.value[n] const toggle = (n) => { collapsed.value = { ...collapsed.value, [n]: !collapsed.value[n] } } const fmt = (n) => (Number(n) || 0).toLocaleString() +const secLabel = (name) => regionLabel(t, name) // ONE key for the caveat's sentence: the count and the gazetteer's name go in as values, the name as // its own left-to-right island (it is a proper name, never translated), so each language orders it. const caveatRow = computed(() => h('span', sentenceParts( @@ -156,7 +161,7 @@ const band = (a) => a === 'yes' ? 'ok' : a === 'partial' ? 'mid' : a === 'no' ?