Revert the popup number format to a fixed en-US: grouping is part of the held decision

NUMERALS ARE HELD covers the whole formatted number, not just the digit system.
-u-nu-latn keeps digits Latin but lets the SEPARATORS follow the locale, so
de reads 1.234.567 and ru 1 234 567 -- and choosing that is the held decision.
I read "numerals" as "digits", shipped it at d988c6f1, and then wrote a check
asserting that grouping SHOULD vary, which passed green while certifying the
breach. A check encoding the wrong intent turns a policy breach into evidence of
compliance, which is worse than having no check. It now asserts one format across
all eleven locales, with a negative control that the reverted form produced five.

Reverted on the shepherd's instruction; gospel shipped the same change and was
asked to revert its number part too, so no single map settles this.

The try/catch goes with it rather than being kept "just in case": it existed
because a locale-derived tag can throw RangeError, and a string literal cannot.

EVIDENCE FOR THE RULING, read from their master, because it cuts both ways:
  RegionDetail.vue:16   n.toLocaleString(`${locale.value}-u-nu-latn`)
                        their own comment: "Locale grouping separators with Latin
                        digits, matching the rest of the site."
  CountryMap.vue:128    toLocaleString(languageCode)   <- the popup, no subtag
So per-locale grouping is THEIR deliberate site-wide convention, and this
element's two other formatters clone that line verbatim. Those are left alone and
flagged, not reverted: changing them would override DX-life's documented choice
rather than stop a map from freelancing, and that is a bigger call than mine.
The popup is the one place theirs follows the locale for digits as well, so it is
also the one place the hold's own rationale actually bites.

And the rationale bites via the REGION, not the language: ar resolves
numberingSystem=latn (1,234,567) while ar-EG resolves arab. A host setting
profile-config.locale to 'ar-EG' is the real exposure, and a fixed en-US is immune.
This commit is contained in:
Doxa Maps 2026-09-26 01:47:39 +00:00
parent d988c6f1ad
commit 084e3ec5b7
7 changed files with 135 additions and 140 deletions

View file

@ -365,38 +365,33 @@ function esc(s) {
// which is the entire point of the site, and Full Profile.
// It could not have been ported before this pass because the DATA was not being fetched: rop1,
// religion, location_description and the photo were missing from our field list.
// Population, formatted the way the other two formatters in this element do it -- see the note at
// its call site for why the -u-nu-latn subtag is there and when it comes off.
function fmtPop(v) {
try { return v.toLocaleString(`${locale.value || 'en'}-u-nu-latn`) } catch (_) { return v.toLocaleString('en-US') }
}
function popupHtml(p) {
const name = esc(p.name || '')
const subtitle = [p.country, p.rop1].filter(Boolean).map(esc).join(' · ')
// Their formatPopulation: toLocaleString(languageCode), NOT a hardcoded 'en'. Ours printed
// 'en-US' grouping in every one of the eleven locales.
// ★ -u-nu-latn: NUMERALS ARE HELD (Driver via shepherd, 2026-09-26). The subtag is NOT belt and
// braces -- but the reason is narrower than it first looks, and MEASURED against full-ICU Node:
// ★ NUMBER FORMAT IS HELD, AND THAT MEANS THE GROUPING TOO -- NOT JUST THE DIGITS.
// 'NUMERALS ARE HELD' (COMMON.md; Driver via shepherd 2026-09-26) covers the whole formatted
// number, so per-locale grouping is itself the held decision. I misread it as a digits-only rule
// and shipped `${locale}-u-nu-latn`, which keeps digits Latin but lets the SEPARATORS follow the
// locale (de 1.234.567, ru 1 234 567). That is the held choice being made by one map, which is
// exactly what the hold exists to prevent -- gospel did the same and was asked to revert too.
// Reverted to a fixed 'en-US' on the shepherd's instruction so no map decides this alone.
//
// For the record, the state immediately before that was `toLocaleString(locale.value || 'en')`,
// which followed the locale for digits AND grouping -- so this element was already out of
// compliance; my change altered the form of the deviation rather than introducing it. 'en-US' is
// the older form, from 29eaf3a6.
//
// THEIRS DIFFERS, deliberately and temporarily: their formatPopulation is
// toLocaleString(languageCode). Exact parity is on hold, not abandoned -- when the Driver rules,
// this line and the two sibling formatters change together.
//
// EVIDENCE FOR THAT RULING, measured here and passed to the Driver's card: a REGIONED tag is what
// switches the digit system, not the bare language code --
// ar -> numberingSystem=latn -> 1,234,567
// ar-EG -> numberingSystem=arab -> ١٬٢٣٤٬٥٦٧
// So the BARE codes this element ships (SUPPORTED_LOCALES, and localeFromUrl() which strips the
// region at .split('-')[0]) were already Latin, and plain toLocaleString('ar') never violated the
// hold. What does violate it is a REGIONED tag arriving from outside -- a host setting
// profile-config.locale to 'ar-EG' reaches this line unstripped. The subtag makes the hold hold
// whatever arrives, instead of depending on every caller to strip a region first.
// (I first wrote "'ar' renders Arabic-Indic" here. It does not; that was asserted, then measured.)
// The subtag splits the two halves of this apart: grouping SEPARATORS still follow the locale,
// which is the part of theirs we can match ('1.234.567' in de, '1 234 567' in ru), while the
// digits stay Latin. So this is closer to their output than the hardcoded 'en-US' it replaced
// AND compliant with the hold. The other two number formatters in this element already do it
// this way (region-dashboards.vue:191, regions-list.vue:178); this one was the odd one out, and
// it is the only one a reader of an 'ar' map popup would have seen.
// When the hold lifts, dropping the subtag in all three restores exact parity.
// The try/catch is not decoration: a malformed tag throws RangeError here, and this runs while
// building popup HTML, so an uncaught throw takes out the popup rather than one number. The two
// sibling formatters guard it for that reason; this one must too.
const pop = Number(p.population) > 0 ? fmtPop(Number(p.population)) : ''
// so a host setting profile-config.locale to 'ar-EG' is the case that actually changes digits.
// A fixed 'en-US' is immune to it, which is a second reason the revert is safe.
const pop = Number(p.population) > 0 ? Number(p.population).toLocaleString('en-US') : ''
const profileUrl = `${props.researchUrl}${p.slug || ''}`
const prayUrl = `${String(props.prayBaseUrl).replace(/\/+$/, '')}/${p.slug || ''}?source=doxalife`

View file

@ -888,7 +888,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "dx-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j",
"integrity": "sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG",
"elements": [
"regions-dashboard"
],
@ -908,7 +908,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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j\" 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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG\" 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>"
}
},
{
@ -922,7 +922,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-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j\" 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-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG\" 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>"
}
},
{
@ -936,7 +936,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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j\" 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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG\" 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>"
}
}
]

View file

@ -888,7 +888,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "dx-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j",
"integrity": "sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG",
"elements": [
"regions-dashboard"
],
@ -908,7 +908,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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j\" 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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG\" 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>"
}
},
{
@ -922,7 +922,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-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j\" 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-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG\" 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>"
}
},
{
@ -936,7 +936,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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j\" 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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG\" 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>"
}
}
]

View file

@ -30,7 +30,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-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,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-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -41,7 +41,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-c7LJZWxXoNP1I7ULRtIRftxRS4Tph3x9YC0rdd04MTLP3GITJVeQvYrhbhZrNP2j" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: