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 c130fe14f..2ef85852a 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` @@ -1194,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 6de1ce182..e3f078a5c 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-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 ee0a54a31..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" } } ]