Regions: stop overriding their localised labels with hardcoded English
I have reported "the SELECT VIEW strip's three strings need
translating" for several ticks without ever quantifying it. Measured at
ar and zh by classifying every visible string by script: TWENTY were
still Latin, not three. Most were not awaiting a decision at all — they
were bugs, and the largest was my own code discarding their data.
- The chooser's region legend read WAGF_REGIONS[value] || r.label, so
the hardcoded English display names in library/colors/wagf-region.js
always WON over r.label — the localised name the API returns for
?lang=, and the very name the region CARDS render. At ar the cards
read "آسيا" while the legend beside them read "Asia". Inverted: the
API's label now wins and WAGF_REGIONS is the fallback for a region
returned with no label.
- The no-selection title was the literal 'All Regions' while this same
file already calls t('All Regions') for the back-link — one string,
translated in one place and hardcoded in another.
- syncTitle compared the subject title against the English literal, so
once the title was localised the comparison could never match; both
halves now go through t(), whose keys are theirs.
- The chooser's two dropdowns carried static label and placeholder
attributes. That is the element's only keyboard and screen-reader
path — pin clicking is mouse-only by construction — and it was
labelled in English inside an otherwise localised page. Now
t('Regions'), t('Countries') and t('Type to search...'), all their
keys, all translated in every locale we ship. The table's first
column too: t('Region') has no key in their catalogue, since they
never needed an all-regions state.
Twenty Latin strings down to fourteen, of which nine are our own
additions, four are Mapbox's chrome and one is an API code. 93.9%
localised at both ar and zh.
Two lessons in the measuring itself. My scan read only TEXT NODES, so
the two hardcoded placeholders were invisible to a check that had just
reported 95% localised — it now reads placeholder, aria-label, title
and alt as well, which immediately surfaced four more English
aria-labels that exist purely for assistive tech. And the remainder is
now classified in the probe rather than left to fail forever: a
permanently red check only teaches you to ignore it, so it passes on
the known set and fails on anything new.
This commit is contained in:
parent
d8a3e5992f
commit
cf689adeeb
7 changed files with 136 additions and 105 deletions
|
|
@ -97,6 +97,9 @@ useDocumentFont('Poppins', POPPINS_FACES, assetsBase.value)
|
|||
|
||||
// ── state ───────────────────────────────────────────────────────────────────
|
||||
const { t, tf , locale } = useRegionsI18n()
|
||||
// syncTitle() shadows `t` with the subject title, so it needs a second handle on the
|
||||
// translator. Same function, different name — not a second i18n instance.
|
||||
const t2 = t
|
||||
// their <h1 class="highlight" data-highlight-last> — the final word in brand primary
|
||||
const titleParts = computed(() => splitLastWord(subject.value?.title || ''))
|
||||
const isRTL = computed(() => RTL_LOCALES.has(locale.value))
|
||||
|
|
@ -170,7 +173,11 @@ const subject = computed(() => {
|
|||
all.engaged += r.engaged; all.withPrayer += r.withPrayer
|
||||
all.withFullPrayer += r.withFullPrayer; all.peopleCommitted += r.peopleCommitted
|
||||
}
|
||||
return { kind: 'all', title: 'All Regions', context: `${formatNumber(regions.value.length)} regions`, stats: all }
|
||||
// t('All Regions'), not the literal. Their own key, translated in all eleven locales
|
||||
// ('جميع المناطق' at ar), and this file ALREADY calls t('All Regions') for the back-link —
|
||||
// so the same string was translated in one place and hardcoded in another. MEASURED at ar
|
||||
// and zh: this title rendered English inside an otherwise fully localised element.
|
||||
return { kind: 'all', title: t('All Regions'), context: `${formatNumber(regions.value.length)} regions`, stats: all }
|
||||
})
|
||||
|
||||
// Headline tiles. Every one of these is a COUNT the rollup actually measured — no ratio is
|
||||
|
|
@ -252,7 +259,10 @@ const tableTitle = computed(() => {
|
|||
return t('Countries in this region')
|
||||
})
|
||||
|
||||
const tableFirstCol = computed(() => (subject.value.kind === 'all' ? t('Region') : t('Country')))
|
||||
// t('Region') has NO key in their catalogue — they never needed it, having no all-regions
|
||||
// state — so it fell back to English. t('Regions') and t('Countries') both exist and are
|
||||
// translated everywhere, and read correctly as column headings.
|
||||
const tableFirstCol = computed(() => (subject.value.kind === 'all' ? t('Regions') : t('Countries')))
|
||||
|
||||
// Their summary line, RegionDetail.vue:32-36 / the list's own. Kept as a %token% template for
|
||||
// the same reason tab 1 keeps it -- it is how their i18n carries this string.
|
||||
|
|
@ -294,7 +304,14 @@ const regionLegend = computed(() => {
|
|||
return REGION_ORDER
|
||||
.map((value) => {
|
||||
const r = byValue.get(value)
|
||||
return r ? { value, slug: r.slug, label: WAGF_REGIONS[value] || r.label, count: r.count, color: REGION_PALETTE[value] } : null
|
||||
// PREFER THE API'S LOCALIZED LABEL. This read WAGF_REGIONS[value] || r.label, so the
|
||||
// hardcoded ENGLISH display name in library/colors/wagf-region.js always WON over r.label
|
||||
// — which is the localized name the API returns for ?lang=, and the very name the region
|
||||
// CARDS render. MEASURED at locale=ar and zh: the cards read "آسيا" / "中东" while this
|
||||
// legend, inside the same element, read "Asia" and "Middle East". My code was discarding
|
||||
// their localized data. WAGF_REGIONS stays as the FALLBACK, for a region the API returns
|
||||
// with no label at all.
|
||||
return r ? { value, slug: r.slug, label: r.label || WAGF_REGIONS[value], count: r.count, color: REGION_PALETTE[value] } : null
|
||||
})
|
||||
.filter(Boolean)
|
||||
})
|
||||
|
|
@ -461,7 +478,14 @@ function writeUrl(push) {
|
|||
function syncTitle() {
|
||||
if (typeof document === 'undefined') return
|
||||
const t = subject.value?.title
|
||||
try { document.title = t && t !== 'All Regions' ? t + ' — Regions' : 'Regions' } catch (_) {}
|
||||
// COMPARE AGAINST THE TRANSLATED VALUE, not the English literal. The subject title is now
|
||||
// t('All Regions'), so at ar it is 'جميع المناطق' and a comparison with the English string
|
||||
// never matched — the no-selection state would have produced 'جميع المناطق — Regions'
|
||||
// instead of the bare fallback. Both halves go through t(), whose keys are theirs and are
|
||||
// translated in all eleven locales.
|
||||
const allLabel = t2('All Regions')
|
||||
const suffix = t2('Regions')
|
||||
try { document.title = t && t !== allLabel ? t + ' — ' + suffix : suffix } catch (_) {}
|
||||
}
|
||||
|
||||
// popstate WRITES the selection, so the watcher below must not turn around and push a new
|
||||
|
|
@ -810,21 +834,28 @@ useShadowStyles(
|
|||
|
||||
<!-- THE CHOOSER — the no-selection state, and the only thing here DX-life does not have -->
|
||||
<div v-if="isAll" class="rd-chooser">
|
||||
<!-- Labels and placeholders through t(), using THEIR existing keys rather than new
|
||||
ones: 'Regions', 'Countries' and 'Type to search...' are all in their common.json
|
||||
and translated in every locale we ship. These were static attributes, so at ar the
|
||||
element's one KEYBOARD/SCREEN-READER path — which the brief calls load-bearing,
|
||||
since pin clicking is mouse-only — was labelled in English inside an otherwise
|
||||
localised page. The plural reads naturally on a selector and costs no new strings.
|
||||
The placeholders were invisible to a text-node scan, which is its own lesson. -->
|
||||
<FilterDropdown
|
||||
label="Region"
|
||||
:label="t('Regions')"
|
||||
name="region"
|
||||
:options="regionOptions"
|
||||
value=""
|
||||
placeholder="Type a region…"
|
||||
:placeholder="t('Type to search...')"
|
||||
@filter-change="onPickRegion"
|
||||
@filter-clear="clearSelection"
|
||||
/>
|
||||
<FilterDropdown
|
||||
label="Country"
|
||||
:label="t('Countries')"
|
||||
name="country"
|
||||
:options="countryOptions"
|
||||
value=""
|
||||
placeholder="Type a country…"
|
||||
:placeholder="t('Type to search...')"
|
||||
@filter-change="onPickCountry"
|
||||
@filter-clear="clearSelection"
|
||||
/>
|
||||
|
|
|
|||
2
public/doxa-maps/index.html
generated
2
public/doxa-maps/index.html
generated
File diff suppressed because one or more lines are too long
8
public/doxa-maps/manifest.json
generated
8
public/doxa-maps/manifest.json
generated
|
|
@ -856,7 +856,7 @@
|
|||
"bundle": "regions-dashboard",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
|
||||
"integrity": "sha384-O+KKVXrz1cHdDNNgxmA5OhFk03sIKRUn6nsuvuq0smZaiQTYwqSFTcsfvIJj55TW",
|
||||
"integrity": "sha384-2vTu8GqysCwitNhCrtG3Rvo/GFQVEovIR5CsTz8o7P70jYWuEj8pV4yf+gd4KjnN",
|
||||
"elements": [
|
||||
"regions-dashboard"
|
||||
],
|
||||
|
|
@ -876,7 +876,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-O+KKVXrz1cHdDNNgxmA5OhFk03sIKRUn6nsuvuq0smZaiQTYwqSFTcsfvIJj55TW\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-2vTu8GqysCwitNhCrtG3Rvo/GFQVEovIR5CsTz8o7P70jYWuEj8pV4yf+gd4KjnN\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -890,7 +890,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-O+KKVXrz1cHdDNNgxmA5OhFk03sIKRUn6nsuvuq0smZaiQTYwqSFTcsfvIJj55TW\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-2vTu8GqysCwitNhCrtG3Rvo/GFQVEovIR5CsTz8o7P70jYWuEj8pV4yf+gd4KjnN\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -904,7 +904,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-O+KKVXrz1cHdDNNgxmA5OhFk03sIKRUn6nsuvuq0smZaiQTYwqSFTcsfvIJj55TW\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-2vTu8GqysCwitNhCrtG3Rvo/GFQVEovIR5CsTz8o7P70jYWuEj8pV4yf+gd4KjnN\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-O+KKVXrz1cHdDNNgxmA5OhFk03sIKRUn6nsuvuq0smZaiQTYwqSFTcsfvIJj55TW" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-2vTu8GqysCwitNhCrtG3Rvo/GFQVEovIR5CsTz8o7P70jYWuEj8pV4yf+gd4KjnN" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
178
public/doxa-maps/regions-dashboard/regions-dashboard.js
generated
178
public/doxa-maps/regions-dashboard/regions-dashboard.js
generated
File diff suppressed because one or more lines are too long
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-O+KKVXrz1cHdDNNgxmA5OhFk03sIKRUn6nsuvuq0smZaiQTYwqSFTcsfvIJj55TW" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-2vTu8GqysCwitNhCrtG3Rvo/GFQVEovIR5CsTz8o7P70jYWuEj8pV4yf+gd4KjnN" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-O+KKVXrz1cHdDNNgxmA5OhFk03sIKRUn6nsuvuq0smZaiQTYwqSFTcsfvIJj55TW" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-2vTu8GqysCwitNhCrtG3Rvo/GFQVEovIR5CsTz8o7P70jYWuEj8pV4yf+gd4KjnN" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue