From 6104b10fc004aff489c66a8ae5f4a26ee25b66e7 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Sat, 26 Sep 2026 00:58:02 +0000 Subject: [PATCH 1/2] Gospel map: read the Mapbox token through the library's channel resources-map read profileConfig.tk || window.DOXA_TOKEN. DOXA_TOKEN is set by a few dev pages and nothing else, so in the studio and on every built page this map ignored the page's token entirely. It now takes profile-config tk, then ProfileLoader's injected mapboxToken, then window.MAP_TOKEN, as the universal-props contract says. The pattern is copied from the same fix on cities-internet and key-cities (1cf57f7a) rather than re-invented. Three raw maps each deciding for themselves how to find a token is how all three came to be wrong in the same way. Headless: the built bundle no longer contains DOXA_TOKEN at all, and carries the injected token and MAP_TOKEN instead. --- .../gospel-resources/resources-map.vue | 10 ++++- public/dx-maps/doxa-maps-spa/manifest.json | 12 +++--- .../gospel-resources/charts/index.html | 2 +- .../gospel-resources/data-tables/index.html | 2 +- .../gospel-dashboard/index.html | 2 +- .../gospel-resources/gospel-resources.js | 42 +++++++++---------- .../resources-list/index.html | 2 +- .../gospel-resources/resources-map/index.html | 2 +- public/dx-maps/manifest.json | 12 +++--- 9 files changed, 47 insertions(+), 39 deletions(-) diff --git a/plugin-designs/gospel-resources/resources-map.vue b/plugin-designs/gospel-resources/resources-map.vue index c130fe14f..bc6714d7e 100644 --- a/plugin-designs/gospel-resources/resources-map.vue +++ b/plugin-designs/gospel-resources/resources-map.vue @@ -93,7 +93,15 @@ import { resolveNamedStyle } from '@map/styles/registry.js' const profileConfig = inject('profileConfig', ref(null)) const isDark = computed(() => profileConfig.value?.isDark === true) -const mapboxToken = computed(() => profileConfig.value?.tk || window.DOXA_TOKEN || '') +// The token through the library's channel (documentation/reference-universal-props.md): the +// profile-config tk, then ProfileLoader's injected mapboxToken (which carries the page-wide shared +// token, fed by window.MAP_TOKEN), then window.MAP_TOKEN. This read window.DOXA_TOKEN, which only a +// few dev pages set, so in the studio and on every built page the map ignored the page's token. +// Same fix as cities-internet and key-cities (1cf57f7a) — the pattern is copied, not re-invented, +// because three raw maps drifting apart on how they find a token is how this happened. +const injectedTk = inject('mapboxToken', null) +const mapboxToken = computed(() => profileConfig.value?.tk || injectedTk?.value + || (typeof window !== 'undefined' && window.MAP_TOKEN) || '') // ── HONOUR THE PROVIDER SWITCH: this profile builds its map RAW ───────────── // Bundles that go through useMapInstance get the switch for free; this one calls `new gl.Map` diff --git a/public/dx-maps/doxa-maps-spa/manifest.json b/public/dx-maps/doxa-maps-spa/manifest.json index dd2f497be..fb843151d 100644 --- a/public/dx-maps/doxa-maps-spa/manifest.json +++ b/public/dx-maps/doxa-maps-spa/manifest.json @@ -579,7 +579,7 @@ "bundle": "gospel-resources", "scriptUrl": "./gospel-resources/gospel-resources.js", "output": "dx-maps/gospel-resources/gospel-resources.js", - "integrity": "sha384-eXLtFilz5hpBxBl0C9BjZHXXFUl6KWwvqcVbe+XkQwZYD13DZdUGmJZx4Gi7Y78z", + "integrity": "sha384-C/dJ9QFiGtS82fGK+MCeiZu3vm3+Z0WZQEL349RnOwaFnwa5ts1nFZQcWMVtzEmv", "elements": [ "gospel-resources-map" ], @@ -599,7 +599,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -613,7 +613,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -627,7 +627,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } }, { @@ -641,7 +641,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -655,7 +655,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } } ] diff --git a/public/dx-maps/gospel-resources/charts/index.html b/public/dx-maps/gospel-resources/charts/index.html index ee0a54a31..96c44f999 100644 --- a/public/dx-maps/gospel-resources/charts/index.html +++ b/public/dx-maps/gospel-resources/charts/index.html @@ -26,7 +26,7 @@ - + - + - + - + - + \n\n" + "script": "\n\n" } }, { @@ -613,7 +613,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -627,7 +627,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } }, { @@ -641,7 +641,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -655,7 +655,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } } ] From 2eac0fb3c1366e12fff501529f907273307798de Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Sat, 26 Sep 2026 01:29:52 +0000 Subject: [PATCH 2/2] Gospel leaves: numbers follow the reader's locale, not the browser's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit toLocaleString() with no argument formats for the JS runtime's locale, which has nothing to do with the locale the plugin was asked to render in. The Arabic build on an English browser got English grouping and the same page on a French browser got French grouping: one plugin, one locale, two different numbers. All four leaves now format with the locale i18n actually resolved. numberingSystem is pinned to latn, and the obvious justification for that is wrong, so here is the measured one. Against full ICU, the eleven tags this bundle ships all resolve to latn already — plain "ar" included — so for them the pin changes nothing. It earns its place only if a host passes a region tag: ar-EG resolves to arab and fa to arabext, and those would switch the digits. Numeral format is held for the Driver, so the pin keeps that his decision rather than a host's. What does change is grouping, and it is visible: Hindi groups in lakhs, so 1234567 reads 12,34,567, and French and Russian use a space. Those are correct for the locale and differ from what shipped before. The bare calls left in the built bundle are in library components this bundle does not render — LegendProgressBar and PosterWall — and belong to whoever owns library, not here. --- plugin-designs/gospel-resources/charts.vue | 27 ++++++++++++- .../gospel-resources/data-tables.vue | 29 +++++++++++++- .../gospel-resources/resources-list.vue | 29 +++++++++++++- .../gospel-resources/resources-map.vue | 27 ++++++++++++- public/dx-maps/doxa-maps-spa/manifest.json | 12 +++--- .../gospel-resources/charts/index.html | 2 +- .../gospel-resources/data-tables/index.html | 2 +- .../gospel-dashboard/index.html | 2 +- .../gospel-resources/gospel-resources.js | 38 +++++++++---------- .../resources-list/index.html | 2 +- .../gospel-resources/resources-map/index.html | 2 +- public/dx-maps/manifest.json | 12 +++--- 12 files changed, 142 insertions(+), 42 deletions(-) diff --git a/plugin-designs/gospel-resources/charts.vue b/plugin-designs/gospel-resources/charts.vue index 52b066b28..f07758e36 100644 --- a/plugin-designs/gospel-resources/charts.vue +++ b/plugin-designs/gospel-resources/charts.vue @@ -397,7 +397,32 @@ const active = ref('coverage') const picked = ref(null) const onPick = (row) => { picked.value = row?.meta ? row : null } -const fmt = (n) => (Number(n) || 0).toLocaleString() +/** + * ═══ NUMBERS FOLLOW THE READER'S LOCALE, DIGITS DO NOT CHANGE ════════════════════════════ + * + * toLocaleString() with no argument formats for the JS RUNTIME's locale — the browser's — which + * has nothing to do with the locale this plugin was asked to render in. A reader who opens the + * Arabic build on an English browser got English grouping, and the same page on a French browser + * got French grouping: the same plugin, the same locale, two different numbers. + * + * ⚠ numberingSystem IS PINNED TO latn, and it is worth being exact about what that does, because + * the obvious claim is wrong. MEASURED against full ICU: the eleven tags this bundle ships all + * resolve to latn ALREADY — plain "ar" included — so for them the pin changes nothing. It earns + * its place only if a host passes a REGION tag: ar-EG resolves to arab and fa to arabext, and + * those would switch the digits. The numeral format is held for the Driver, so the pin keeps that + * decision his rather than a host's. + * + * What DOES change today is which locale decides the separators: the reader's, not the browser's. + * Hindi groups in lakhs, so 1234567 reads 12,34,567 in hi, and fr and ru use a space. Those are + * correct for the locale and are a visible change from what shipped before. + */ +const localeTag = () => { + try { return String(useI18n().locale.value || 'en') } catch (_) { return 'en' } +} +const fmt = (n) => { + const v = Number(n) || 0 + try { return v.toLocaleString(localeTag(), { numberingSystem: 'latn' }) } catch (_) { return v.toLocaleString('en') } +} useShadowStyles( '.gc{position:absolute;inset:0;overflow-y:auto;padding:18px;background:#f6f7f9;color:#0f1419;' + diff --git a/plugin-designs/gospel-resources/data-tables.vue b/plugin-designs/gospel-resources/data-tables.vue index 0efb4e8fb..c128ea1b5 100644 --- a/plugin-designs/gospel-resources/data-tables.vue +++ b/plugin-designs/gospel-resources/data-tables.vue @@ -265,7 +265,32 @@ const gap = computed(() => { deathsDay: Math.round(g.reduce((n, x) => n + (x.est_deaths_per_day || 0), 0)), } }) -const fmt = (n) => (Number(n) || 0).toLocaleString() +/** + * ═══ NUMBERS FOLLOW THE READER'S LOCALE, DIGITS DO NOT CHANGE ════════════════════════════ + * + * toLocaleString() with no argument formats for the JS RUNTIME's locale — the browser's — which + * has nothing to do with the locale this plugin was asked to render in. A reader who opens the + * Arabic build on an English browser got English grouping, and the same page on a French browser + * got French grouping: the same plugin, the same locale, two different numbers. + * + * ⚠ numberingSystem IS PINNED TO latn, and it is worth being exact about what that does, because + * the obvious claim is wrong. MEASURED against full ICU: the eleven tags this bundle ships all + * resolve to latn ALREADY — plain "ar" included — so for them the pin changes nothing. It earns + * its place only if a host passes a REGION tag: ar-EG resolves to arab and fa to arabext, and + * those would switch the digits. The numeral format is held for the Driver, so the pin keeps that + * decision his rather than a host's. + * + * What DOES change today is which locale decides the separators: the reader's, not the browser's. + * Hindi groups in lakhs, so 1234567 reads 12,34,567 in hi, and fr and ru use a space. Those are + * correct for the locale and are a visible change from what shipped before. + */ +const localeTag = () => { + try { return String(useI18n().locale.value || 'en') } catch (_) { return 'en' } +} +const fmt = (n) => { + const v = Number(n) || 0 + try { return v.toLocaleString(localeTag(), { numberingSystem: 'latn' }) } catch (_) { return v.toLocaleString('en') } +} useShadowStyles( '.gt{position:absolute;inset:0;overflow-y:auto;padding:18px;background:#f6f7f9;color:#0f1419;' + @@ -334,7 +359,7 @@ useShadowStyles(
diff --git a/plugin-designs/gospel-resources/resources-list.vue b/plugin-designs/gospel-resources/resources-list.vue index bfd632b52..a11cfb957 100644 --- a/plugin-designs/gospel-resources/resources-list.vue +++ b/plugin-designs/gospel-resources/resources-list.vue @@ -202,10 +202,35 @@ const biggestUnfound = computed(() => .map((l) => `${l.language} (${fmtShort(l.population)})`).join(', ')) const fmtShort = (n) => { const v = Number(n) || 0 - return v >= 1e6 ? `${Math.round(v / 1e5) / 10}M` : v.toLocaleString() + return v >= 1e6 ? `${Math.round(v / 1e5) / 10}M` : fmt(v) } -const fmt = (n) => (Number(n) || 0).toLocaleString() +/** + * ═══ NUMBERS FOLLOW THE READER'S LOCALE, DIGITS DO NOT CHANGE ════════════════════════════ + * + * toLocaleString() with no argument formats for the JS RUNTIME's locale — the browser's — which + * has nothing to do with the locale this plugin was asked to render in. A reader who opens the + * Arabic build on an English browser got English grouping, and the same page on a French browser + * got French grouping: the same plugin, the same locale, two different numbers. + * + * ⚠ numberingSystem IS PINNED TO latn, and it is worth being exact about what that does, because + * the obvious claim is wrong. MEASURED against full ICU: the eleven tags this bundle ships all + * resolve to latn ALREADY — plain "ar" included — so for them the pin changes nothing. It earns + * its place only if a host passes a REGION tag: ar-EG resolves to arab and fa to arabext, and + * those would switch the digits. The numeral format is held for the Driver, so the pin keeps that + * decision his rather than a host's. + * + * What DOES change today is which locale decides the separators: the reader's, not the browser's. + * Hindi groups in lakhs, so 1234567 reads 12,34,567 in hi, and fr and ru use a space. Those are + * correct for the locale and are a visible change from what shipped before. + */ +const localeTag = () => { + try { return String(useI18n().locale.value || 'en') } catch (_) { return 'en' } +} +const fmt = (n) => { + const v = Number(n) || 0 + try { return v.toLocaleString(localeTag(), { numberingSystem: 'latn' }) } catch (_) { return v.toLocaleString('en') } +} // ── EVERY LINK THE ROW ACTUALLY HAS, not just the scripture ones ───────────────────────────── // THE DEFECT THIS FIXES (measured 2026-09-23 over all 862 languages in resources.json): diff --git a/plugin-designs/gospel-resources/resources-map.vue b/plugin-designs/gospel-resources/resources-map.vue index bc6714d7e..2ef85852a 100644 --- a/plugin-designs/gospel-resources/resources-map.vue +++ b/plugin-designs/gospel-resources/resources-map.vue @@ -1202,7 +1202,32 @@ function setMode(m) { if (mapRef.value && adm0Ref.value) paint(mapRef.value, adm0Ref.value) } -const fmt = (n) => (Number(n) || 0).toLocaleString() +/** + * ═══ NUMBERS FOLLOW THE READER'S LOCALE, DIGITS DO NOT CHANGE ════════════════════════════ + * + * toLocaleString() with no argument formats for the JS RUNTIME's locale — the browser's — which + * has nothing to do with the locale this plugin was asked to render in. A reader who opens the + * Arabic build on an English browser got English grouping, and the same page on a French browser + * got French grouping: the same plugin, the same locale, two different numbers. + * + * ⚠ numberingSystem IS PINNED TO latn, and it is worth being exact about what that does, because + * the obvious claim is wrong. MEASURED against full ICU: the eleven tags this bundle ships all + * resolve to latn ALREADY — plain "ar" included — so for them the pin changes nothing. It earns + * its place only if a host passes a REGION tag: ar-EG resolves to arab and fa to arabext, and + * those would switch the digits. The numeral format is held for the Driver, so the pin keeps that + * decision his rather than a host's. + * + * What DOES change today is which locale decides the separators: the reader's, not the browser's. + * Hindi groups in lakhs, so 1234567 reads 12,34,567 in hi, and fr and ru use a space. Those are + * correct for the locale and are a visible change from what shipped before. + */ +const localeTag = () => { + try { return String(useI18n().locale.value || 'en') } catch (_) { return 'en' } +} +const fmt = (n) => { + const v = Number(n) || 0 + try { return v.toLocaleString(localeTag(), { numberingSystem: 'latn' }) } catch (_) { return v.toLocaleString('en') } +} // The table lives on a SIBLING page, so "see all of these" is a URL. Built here rather // than inline in the template so the group link and the country link cannot drift apart. diff --git a/public/dx-maps/doxa-maps-spa/manifest.json b/public/dx-maps/doxa-maps-spa/manifest.json index fb843151d..f074f3761 100644 --- a/public/dx-maps/doxa-maps-spa/manifest.json +++ b/public/dx-maps/doxa-maps-spa/manifest.json @@ -579,7 +579,7 @@ "bundle": "gospel-resources", "scriptUrl": "./gospel-resources/gospel-resources.js", "output": "dx-maps/gospel-resources/gospel-resources.js", - "integrity": "sha384-C/dJ9QFiGtS82fGK+MCeiZu3vm3+Z0WZQEL349RnOwaFnwa5ts1nFZQcWMVtzEmv", + "integrity": "sha384-w99J54Kg5/CBs37TxGIqgtErXVD9o1qIdlOZ6psyxFW+GPkTSclQMuoe8XusNIMD", "elements": [ "gospel-resources-map" ], @@ -599,7 +599,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -613,7 +613,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -627,7 +627,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } }, { @@ -641,7 +641,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -655,7 +655,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } } ] diff --git a/public/dx-maps/gospel-resources/charts/index.html b/public/dx-maps/gospel-resources/charts/index.html index 96c44f999..4e9c760d1 100644 --- a/public/dx-maps/gospel-resources/charts/index.html +++ b/public/dx-maps/gospel-resources/charts/index.html @@ -26,7 +26,7 @@ - + - + - + - + - + \n\n" + "script": "\n\n" } }, { @@ -613,7 +613,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -627,7 +627,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } }, { @@ -641,7 +641,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -655,7 +655,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } } ]