Merge remote-tracking branch 'origin/development' into upg100/phone-bigjob

This commit is contained in:
Doxa Maps 2026-10-01 12:48:41 +00:00
commit eeda3f168c
23 changed files with 870 additions and 220 deletions

View file

@ -26,7 +26,7 @@
*
* Usage: node internal/build-packet-fixtures.mjs
*/
import { existsSync, renameSync, readdirSync } from 'node:fs'
import { existsSync, renameSync, readdirSync, mkdirSync, rmSync } from 'node:fs'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { spawnSync } from 'node:child_process'
@ -38,6 +38,11 @@ const OUT = resolve(REPO, 'library/scripts/posters/out/packets')
// Every fixture is built against this zone: the records come from the fixture, and the zone only
// supplies page 1's map. Keeping it constant means a shape difference is the ONLY variable.
const ZONE = 'asia-china-tibet-autonomous-region'
// ★ BUILT IN THEIR OWN DIRECTORY. packet.mjs writes <zone>.html into its output dir, and every fixture is built
// against the REAL Tibet zone; built straight into out/packets, each run overwrote the real Tibet packet and the
// rename then removed it, so check:packets-all reported a generated zone as missing. PACKET_OUT sends each build
// to a private directory; only the renamed fixture-<name>.html reaches out/packets.
const BUILD = join(OUT, '.fixture-build')
if (!existsSync(FIXTURES)) {
console.log(`PACKET-FIXTURES: SKIP — no fixtures directory at ${FIXTURES}`)
@ -54,14 +59,16 @@ console.log(`PACKET-FIXTURES: building ${fixtures.length} fixture(s)`)
const built = []
const failed = []
mkdirSync(OUT, { recursive: true })
mkdirSync(BUILD, { recursive: true })
for (const fx of fixtures) {
const name = fx.replace(/^records-/, '').replace(/\.json$/, '')
const res = spawnSync(process.execPath, [join(REPO, 'library/scripts/posters/packet.mjs')], {
cwd: REPO,
encoding: 'utf8',
env: { ...process.env, PACKET_RECORDS: join(FIXTURES, fx), ONLY: ZONE },
env: { ...process.env, PACKET_RECORDS: join(FIXTURES, fx), ONLY: ZONE, PACKET_OUT: BUILD },
})
const produced = join(OUT, `${ZONE}.html`)
const produced = join(BUILD, `${ZONE}.html`)
if (res.status !== 0 || !existsSync(produced)) {
// The ERROR line, not the last line: Node ends a crash with its version banner, so the last line
// of a TDZ ReferenceError read "Node.js v20.20.2" and named nothing.
@ -77,6 +84,8 @@ for (const fx of fixtures) {
console.log(` ✓ ${fx} -> fixture-${name}.html`)
}
try { rmSync(BUILD, { recursive: true, force: true }) } catch { /* best effort */ }
if (failed.length) {
console.error(`\nPACKET-FIXTURES: FAIL — ${failed.length} fixture(s) did not build`)
for (const f of failed) console.error(' - ' + f)

View file

@ -36,7 +36,7 @@
process.stdout.write(`LANDING-PRECONDITIONS: start — pid ${process.pid}, node ${process.version}\n`)
import { spawnSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { TREE_NAME } from './_tree-name.mjs'
@ -181,7 +181,14 @@ if (process.env.DOXA_SKIP_FRESHNESS === '1') {
const libSet = new Set(libChanged)
const areas = new Set(libChanged.map((f) => (f.match(/^library\/i18n\/locales\/[^/]+\/([^/]+)\.json$/) || [])[1]).filter(Boolean))
const manifest = JSON.parse(readFileSync(resolve(ROOT, 'public', TREE_NAME, 'manifest.json'), 'utf8'))
for (const { bundle } of manifest.bundles) {
// ★ EVERY SHIPPED BUNDLE, NOT ONLY THE MANIFEST'S. A bundle the tree ships but does not list (template-bundle)
// was never in scope, so a library change it imports left it stale with no landing to rebuild it; a final
// all-bundle freshness run found it weeks of library changes behind. Shipped = a plugin with an index.js
// whose built <bundle>/<bundle>.js is in the tree.
const shipped = readdirSync(resolve(ROOT, 'plugin-designs')).filter((d) => existsSync(resolve(ROOT, 'plugin-designs', d, 'index.js'))
&& existsSync(resolve(ROOT, 'public', TREE_NAME, d, d + '.js')))
const candidates = [...new Set([...manifest.bundles.map((b) => b.bundle), ...shipped])]
for (const bundle of candidates) {
if (!existsSync(resolve(ROOT, 'plugin-designs', bundle, 'index.js'))) continue
const graph = bundleGraph(ROOT, bundle).map((f) => f.slice(ROOT.length + 1))
if (graph.some((f) => libSet.has(f)) || (areas.size && areasForBundle(ROOT, bundle).areas.some((a) => areas.has(a)))) own.add(bundle)

View file

@ -1,5 +1,43 @@
{
"indiaNote": "للهند قسمها الخاص في المقدّمة: عدد الشعوب غير المُبشَّرة فيها {count}، وهو أكثر بكثير من أي بلد آخر، لذا لكل ولاية وإقليم اتحادي فيها ملصقه الخاص.",
"indiaStateNoGroups": "لا توجد هنا شعوب غير مُبشَّرة مُدرَجة.",
"indiaItemCount": "لا ولايات ولا أقاليم | ولاية أو إقليم اتحادي واحد | ولايتان أو إقليمان | {n} ولايات وأقاليم اتحادية | {n} ولاية وإقليمًا اتحاديًا | {n} ولاية وإقليم اتحادي"
"indiaItemCount": "لا ولايات ولا أقاليم | ولاية أو إقليم اتحادي واحد | ولايتان أو إقليمان | {n} ولايات وأقاليم اتحادية | {n} ولاية وإقليمًا اتحاديًا | {n} ولاية وإقليم اتحادي",
"stateNames": {
"IN-AN": "جزر أندمان وَ نِكُبَار",
"IN-AP": "أندرا برديش",
"IN-AR": "أروناجل برديش",
"IN-AS": "آسام",
"IN-BR": "بهار",
"IN-CH": "شَنديغار",
"IN-CT": "تشاتيسغار",
"IN-DH": "دادرا وناغار هافيلي ودامان وديو",
"IN-DL": "دلهي",
"IN-GA": "غوا",
"IN-GJ": "كجرات",
"IN-HP": "هماجل برديش",
"IN-HR": "هريانة",
"IN-JH": "جاركند",
"IN-JK": "إقليم جمون وكشمير",
"IN-KA": "كرناتك",
"IN-KL": "كيرلا",
"IN-LA": "لداخ",
"IN-LD": "لكشديب",
"IN-MH": "مهاراشترة",
"IN-ML": "ميكالية",
"IN-MN": "منيبور",
"IN-MP": "مدية برديش",
"IN-MZ": "مِزُورَم",
"IN-NL": "ناغالند",
"IN-OR": "أديشة",
"IN-PB": "بنجاب",
"IN-PY": "بُدُجيري",
"IN-RJ": "راجستان",
"IN-SK": "سِكِم",
"IN-TG": "تلنكانة",
"IN-TN": "تاميل نادو",
"IN-TR": "تريبورة",
"IN-UP": "أتر برديش",
"IN-UT": "أوتاراخند",
"IN-WB": "بنغال الغربية"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "Indien hat einen eigenen Abschnitt, ganz oben: Mit {count} unerreichten Volksgruppen, weit mehr als jedes andere Land, hat jeder seiner Bundesstaaten und Unionsterritorien ein eigenes Plakat.",
"indiaStateNoGroups": "Hier sind keine unerreichten Volksgruppen verzeichnet.",
"indiaItemCount": "{n} Bundesstaat oder Unionsterritorium | {n} Bundesstaaten und Unionsterritorien"
"indiaItemCount": "{n} Bundesstaat oder Unionsterritorium | {n} Bundesstaaten und Unionsterritorien",
"stateNames": {
"IN-AN": "Andamanen und Nikobaren",
"IN-AP": "Andhra Pradesh",
"IN-AR": "Arunachal Pradesh",
"IN-AS": "Assam",
"IN-BR": "Bihar",
"IN-CH": "Chandigarh",
"IN-CT": "Chhattisgarh",
"IN-DH": "Dadra und Nagar Haveli und Daman und Diu",
"IN-DL": "Delhi",
"IN-GA": "Goa",
"IN-GJ": "Gujarat",
"IN-HP": "Himachal Pradesh",
"IN-HR": "Haryana",
"IN-JH": "Jharkhand",
"IN-JK": "Jammu und Kashmir",
"IN-KA": "Karnataka",
"IN-KL": "Kerala",
"IN-LA": "Ladakh",
"IN-LD": "Lakshadweep",
"IN-MH": "Maharashtra",
"IN-ML": "Meghalaya",
"IN-MN": "Manipur",
"IN-MP": "Madhya Pradesh",
"IN-MZ": "Mizoram",
"IN-NL": "Nagaland",
"IN-OR": "Odisha",
"IN-PB": "Punjab",
"IN-PY": "Puducherry",
"IN-RJ": "Rajasthan",
"IN-SK": "Sikkim",
"IN-TG": "Telangana",
"IN-TN": "Tamil Nadu",
"IN-TR": "Tripura",
"IN-UP": "Uttar Pradesh",
"IN-UT": "Uttarakhand",
"IN-WB": "Westbengalen"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "India has its own section, first: it has {count} unreached people groups, far more than any other country, so each of its states and union territories has its own poster.",
"indiaStateNoGroups": "No unreached people groups are listed here.",
"indiaItemCount": "{n} states and union territories | {n} state or union territory | {n} states and union territories"
"indiaItemCount": "{n} states and union territories | {n} state or union territory | {n} states and union territories",
"stateNames": {
"IN-AN": "Andaman and Nicobar Islands",
"IN-AP": "Andhra Pradesh",
"IN-AR": "Arunachal Pradesh",
"IN-AS": "Assam",
"IN-BR": "Bihar",
"IN-CH": "Chandigarh",
"IN-CT": "Chhattisgarh",
"IN-DH": "Dadra and Nagar Haveli and Daman and Diu",
"IN-DL": "Delhi",
"IN-GA": "Goa",
"IN-GJ": "Gujarat",
"IN-HP": "Himachal Pradesh",
"IN-HR": "Haryana",
"IN-JH": "Jharkhand",
"IN-JK": "Jammu and Kashmir",
"IN-KA": "Karnataka",
"IN-KL": "Kerala",
"IN-LA": "Ladakh",
"IN-LD": "Lakshadweep",
"IN-MH": "Maharashtra",
"IN-ML": "Meghalaya",
"IN-MN": "Manipur",
"IN-MP": "Madhya Pradesh",
"IN-MZ": "Mizoram",
"IN-NL": "Nagaland",
"IN-OR": "Odisha",
"IN-PB": "Punjab",
"IN-PY": "Puducherry",
"IN-RJ": "Rajasthan",
"IN-SK": "Sikkim",
"IN-TG": "Telangana",
"IN-TN": "Tamil Nadu",
"IN-TR": "Tripura",
"IN-UP": "Uttar Pradesh",
"IN-UT": "Uttarakhand",
"IN-WB": "West Bengal"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "India tiene su propia sección, la primera: tiene {count} pueblos no alcanzados, muchos más que cualquier otro país, así que cada uno de sus estados y territorios de la unión tiene su propio cartel.",
"indiaStateNoGroups": "Aquí no figura ningún pueblo no alcanzado.",
"indiaItemCount": "{n} estado o territorio de la unión | {n} de estados y territorios de la unión | {n} estados y territorios de la unión"
"indiaItemCount": "{n} estado o territorio de la unión | {n} de estados y territorios de la unión | {n} estados y territorios de la unión",
"stateNames": {
"IN-AN": "Islas Andamán y Nicobar",
"IN-AP": "Andhra Pradesh",
"IN-AR": "Arunachal Pradesh",
"IN-AS": "Assam",
"IN-BR": "Bihar",
"IN-CH": "Chandigarh",
"IN-CT": "Chhattisgarh",
"IN-DH": "Dadra y Nagar Haveli y Damán y Diu",
"IN-DL": "Delhi",
"IN-GA": "Goa",
"IN-GJ": "Guyarat",
"IN-HP": "Himachal Pradesh",
"IN-HR": "Haryana",
"IN-JH": "Jharkhand",
"IN-JK": "Jammu y Cachemira",
"IN-KA": "Karnataka",
"IN-KL": "Kerala",
"IN-LA": "Ladakh",
"IN-LD": "Laquedivas",
"IN-MH": "Maharashtra",
"IN-ML": "Meghalaya",
"IN-MN": "Manipur",
"IN-MP": "Madhya Pradesh",
"IN-MZ": "Mizoram",
"IN-NL": "Nagaland",
"IN-OR": "Orissa",
"IN-PB": "Punyab",
"IN-PY": "Puducherry",
"IN-RJ": "Rajastán",
"IN-SK": "Sikkim",
"IN-TG": "Telangana",
"IN-TN": "Tamil Nadu",
"IN-TR": "Tripura",
"IN-UP": "Uttar Pradesh",
"IN-UT": "Uttarakhand",
"IN-WB": "Bengala Occidental"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "L’Inde a sa propre section, en premier : elle compte {count} peuples non atteints, bien plus que tout autre pays ; chacun de ses États et territoires de l’Union a donc sa propre affiche.",
"indiaStateNoGroups": "Aucun peuple non atteint n’est répertorié ici.",
"indiaItemCount": "{n} État ou territoire de l’Union | {n} d’États et territoires de l’Union | {n} États et territoires de l’Union"
"indiaItemCount": "{n} État ou territoire de l’Union | {n} d’États et territoires de l’Union | {n} États et territoires de l’Union",
"stateNames": {
"IN-AN": "Îles Andaman-et-Nicobar",
"IN-AP": "Andhra Pradesh",
"IN-AR": "Arunachal Pradesh",
"IN-AS": "Assam",
"IN-BR": "Bihar",
"IN-CH": "Chandigarh",
"IN-CT": "Chhattisgarh",
"IN-DH": "Dadra et Nagar Haveli et Daman et Diu",
"IN-DL": "Delhi",
"IN-GA": "Goa",
"IN-GJ": "Gujarat",
"IN-HP": "Himachal Pradesh",
"IN-HR": "Haryana",
"IN-JH": "Jharkhand",
"IN-JK": "Jammu-et-Cachemire",
"IN-KA": "Karnataka",
"IN-KL": "Kerala",
"IN-LA": "Ladakh",
"IN-LD": "Lakshadweep",
"IN-MH": "Maharashtra",
"IN-ML": "Meghalaya",
"IN-MN": "Manipur",
"IN-MP": "Madhya Pradesh",
"IN-MZ": "Mizoram",
"IN-NL": "Nagaland",
"IN-OR": "Odisha",
"IN-PB": "Pendjab",
"IN-PY": "Territoire de Pondichéry",
"IN-RJ": "Rajasthan",
"IN-SK": "Sikkim",
"IN-TG": "Telangana",
"IN-TN": "Tamil Nadu",
"IN-TR": "Tripura",
"IN-UP": "Uttar Pradesh",
"IN-UT": "Uttarakhand",
"IN-WB": "Bengale-Occidental"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "भारत का अपना अलग खंड है, सबसे पहले: इसमें {count} अपहुँचे जनसमूह हैं, किसी भी अन्य देश से कहीं अधिक, इसलिए इसके हर राज्य और केंद्र शासित प्रदेश का अपना पोस्टर है।",
"indiaStateNoGroups": "यहाँ कोई अपहुँचा जनसमूह सूचीबद्ध नहीं है।",
"indiaItemCount": "{n} राज्य या केंद्र शासित प्रदेश | {n} राज्य और केंद्र शासित प्रदेश"
"indiaItemCount": "{n} राज्य या केंद्र शासित प्रदेश | {n} राज्य और केंद्र शासित प्रदेश",
"stateNames": {
"IN-AN": "अण्डमान और निकोबार द्वीपसमूह",
"IN-AP": "आन्ध्र प्रदेश",
"IN-AR": "अरुणाचल प्रदेश",
"IN-AS": "असम",
"IN-BR": "बिहार",
"IN-CH": "चण्डीगढ़",
"IN-CT": "छत्तीसगढ़",
"IN-DH": "दादरा और नगर हवेली और दमन और दीव",
"IN-DL": "दिल्ली",
"IN-GA": "गोवा",
"IN-GJ": "गुजरात",
"IN-HP": "हिमाचल प्रदेश",
"IN-HR": "हरियाणा",
"IN-JH": "झारखण्ड",
"IN-JK": "जम्मू और कश्मीर",
"IN-KA": "कर्नाटक",
"IN-KL": "केरल",
"IN-LA": "लद्दाख़",
"IN-LD": "लक्षद्वीप",
"IN-MH": "महाराष्ट्र",
"IN-ML": "मेघालय",
"IN-MN": "मणिपुर",
"IN-MP": "मध्य प्रदेश",
"IN-MZ": "मिज़ोरम",
"IN-NL": "नागालैण्ड",
"IN-OR": "ओडिशा",
"IN-PB": "पंजाब",
"IN-PY": "पुदुचेरी",
"IN-RJ": "राजस्थान",
"IN-SK": "सिक्किम",
"IN-TG": "तेलंगाना",
"IN-TN": "तमिल नाडु",
"IN-TR": "त्रिपुरा",
"IN-UP": "उत्तर प्रदेश",
"IN-UT": "उत्तराखण्ड",
"IN-WB": "पश्चिम बंगाल"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "L’India ha una sezione tutta sua, la prima: conta {count} popoli non raggiunti, molti più di qualsiasi altro paese, quindi ognuno dei suoi stati e territori dell’Unione ha il proprio manifesto.",
"indiaStateNoGroups": "Qui non è elencato alcun popolo non raggiunto.",
"indiaItemCount": "{n} stato o territorio dell’Unione | {n} di stati e territori dell’Unione | {n} stati e territori dell’Unione"
"indiaItemCount": "{n} stato o territorio dell’Unione | {n} di stati e territori dell’Unione | {n} stati e territori dell’Unione",
"stateNames": {
"IN-AN": "isole Andamane e Nicobare",
"IN-AP": "Andhra Pradesh",
"IN-AR": "Arunachal Pradesh",
"IN-AS": "Assam",
"IN-BR": "Bihar",
"IN-CH": "Chandigarh",
"IN-CT": "Chhattisgarh",
"IN-DH": "Dadra e Nagar Haveli e Daman e Diu",
"IN-DL": "Delhi",
"IN-GA": "Goa",
"IN-GJ": "Gujarat",
"IN-HP": "Himachal Pradesh",
"IN-HR": "Haryana",
"IN-JH": "Jharkhand",
"IN-JK": "Jammu e Kashmir",
"IN-KA": "Karnataka",
"IN-KL": "Kerala",
"IN-LA": "Ladakh",
"IN-LD": "Laccadive",
"IN-MH": "Maharashtra",
"IN-ML": "Meghalaya",
"IN-MN": "Manipur",
"IN-MP": "Madhya Pradesh",
"IN-MZ": "Mizoram",
"IN-NL": "Nagaland",
"IN-OR": "Orissa",
"IN-PB": "Punjab",
"IN-PY": "Pondicherry",
"IN-RJ": "Rajasthan",
"IN-SK": "Sikkim",
"IN-TG": "Telangana",
"IN-TN": "Tamil Nadu",
"IN-TR": "Tripura",
"IN-UP": "Uttar Pradesh",
"IN-UT": "Uttarakhand",
"IN-WB": "Bengala Occidentale"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "A Índia tem a sua própria seção, a primeira: tem {count} povos não alcançados, muito mais do que qualquer outro país, por isso cada um dos seus estados e territórios da união tem o seu próprio cartaz.",
"indiaStateNoGroups": "Nenhum povo não alcançado está listado aqui.",
"indiaItemCount": "{n} estado ou território da união | {n} de estados e territórios da união | {n} estados e territórios da união"
"indiaItemCount": "{n} estado ou território da união | {n} de estados e territórios da união | {n} estados e territórios da união",
"stateNames": {
"IN-AN": "Andamão e Nicobar",
"IN-AP": "Andra Pradexe",
"IN-AR": "Arunachal Pradesh",
"IN-AS": "Assam",
"IN-BR": "Bihar",
"IN-CH": "Chandigarh",
"IN-CT": "Chhattisgarh",
"IN-DH": "Dadrá e Nagar Aveli e Damão e Diu",
"IN-DL": "Delhi",
"IN-GA": "Goa",
"IN-GJ": "Gujarate",
"IN-HP": "Himachal Pradesh",
"IN-HR": "Haryana",
"IN-JH": "Jharkhand",
"IN-JK": "Jammu e Caxemira",
"IN-KA": "Carnataca",
"IN-KL": "Kerala",
"IN-LA": "Ladaque",
"IN-LD": "Laquedivas",
"IN-MH": "Maarastra",
"IN-ML": "Meghalaya",
"IN-MN": "Manipur",
"IN-MP": "Madhya Pradesh",
"IN-MZ": "Mizoram",
"IN-NL": "Nagaland",
"IN-OR": "Orissa",
"IN-PB": "Punjab",
"IN-PY": "Puducherry",
"IN-RJ": "Rajastão",
"IN-SK": "Siquim",
"IN-TG": "Telangana",
"IN-TN": "Tamil Nadu",
"IN-TR": "Tripura",
"IN-UP": "Utar Pradexe",
"IN-UT": "Utaracanda",
"IN-WB": "Bengala Ocidental"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "India are propria secțiune, prima: are {count} popoare neatinse, mult mai multe decât orice altă țară, așa că fiecare dintre statele și teritoriile sale ale Uniunii are propriul afiș.",
"indiaStateNoGroups": "Aici nu este listat niciun popor neatins.",
"indiaItemCount": "un stat sau teritoriu al Uniunii | {n} state și teritorii ale Uniunii | {n} de state și teritorii ale Uniunii"
"indiaItemCount": "un stat sau teritoriu al Uniunii | {n} state și teritorii ale Uniunii | {n} de state și teritorii ale Uniunii",
"stateNames": {
"IN-AN": "Insulele Andaman și Nicobar",
"IN-AP": "Andhra Pradesh",
"IN-AR": "Arunachal Pradesh",
"IN-AS": "Assam",
"IN-BR": "Bihar",
"IN-CH": "Teritoriul Unional Chandigarh",
"IN-CT": "Chhattisgarh",
"IN-DH": "Dadra și Nagar Haveli și Daman și Diu",
"IN-DL": "Delhi",
"IN-GA": "Goa",
"IN-GJ": "Gujarat",
"IN-HP": "Himachal Pradesh",
"IN-HR": "Haryana",
"IN-JH": "Jharkhand",
"IN-JK": "Jammu și Cașmir",
"IN-KA": "Karnataka",
"IN-KL": "Kerala",
"IN-LA": "Ladakh",
"IN-LD": "Lakshadweep",
"IN-MH": "Maharashtra",
"IN-ML": "Meghalaya",
"IN-MN": "Manipur",
"IN-MP": "Madhya Pradesh",
"IN-MZ": "Mizoram",
"IN-NL": "Nagaland",
"IN-OR": "Odisha",
"IN-PB": "Punjab",
"IN-PY": "Puducherry",
"IN-RJ": "Rajasthan",
"IN-SK": "Sikkim",
"IN-TG": "Telangana",
"IN-TN": "Tamil Nadu",
"IN-TR": "Tripura",
"IN-UP": "Uttar Pradesh",
"IN-UT": "Uttarakhand",
"IN-WB": "Bengalul de Vest"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "У Индии свой раздел, первый: число недостигнутых народов в ней — {count}, намного больше, чем в любой другой стране, поэтому у каждого её штата и союзной территории есть свой плакат.",
"indiaStateNoGroups": "Здесь не указано ни одного недостигнутого народа.",
"indiaItemCount": "{n} штат или союзная территория | {n} штата и союзные территории | {n} штатов и союзных территорий | {n} штата и союзной территории"
"indiaItemCount": "{n} штат или союзная территория | {n} штата и союзные территории | {n} штатов и союзных территорий | {n} штата и союзной территории",
"stateNames": {
"IN-AN": "Андаманские и Никобарские острова",
"IN-AP": "Андхра-Прадеш",
"IN-AR": "Аруначал-Прадеш",
"IN-AS": "Ассам",
"IN-BR": "Бихар",
"IN-CH": "Чандигарх",
"IN-CT": "Чхаттисгарх",
"IN-DH": "Дадра и Нагар-Хавели и Даман и Диу",
"IN-DL": "Дели",
"IN-GA": "Гоа",
"IN-GJ": "Гуджарат",
"IN-HP": "Химачал-Прадеш",
"IN-HR": "Харьяна",
"IN-JH": "Джаркханд",
"IN-JK": "Джамму и Кашмир",
"IN-KA": "Карнатака",
"IN-KL": "Керала",
"IN-LA": "Ладакх",
"IN-LD": "Лакшадвип",
"IN-MH": "Махараштра",
"IN-ML": "Мегхалая",
"IN-MN": "Манипур",
"IN-MP": "Мадхья-Прадеш",
"IN-MZ": "Мизорам",
"IN-NL": "Нагаленд",
"IN-OR": "Одиша",
"IN-PB": "Пенджаб",
"IN-PY": "Пондичерри",
"IN-RJ": "Раджастхан",
"IN-SK": "Сикким",
"IN-TG": "Телингана",
"IN-TN": "Тамилнад",
"IN-TR": "Трипура",
"IN-UP": "Уттар-Прадеш",
"IN-UT": "Уттаракханд",
"IN-WB": "Западная Бенгалия"
}
}

View file

@ -1,5 +1,43 @@
{
"indiaNote": "印度有单独的一栏,排在最前:这里有 {count} 个未得之民族群,远多于任何其他国家,因此它的每个邦和中央直辖区都有自己的海报。",
"indiaStateNoGroups": "此处未列出任何未得之民族群。",
"indiaItemCount": "{n} 个邦和中央直辖区"
"indiaItemCount": "{n} 个邦和中央直辖区",
"stateNames": {
"IN-AN": "安达曼-尼科巴群岛",
"IN-AP": "安得拉邦",
"IN-AR": "阿鲁纳恰尔邦",
"IN-AS": "阿萨姆邦",
"IN-BR": "比哈尔邦",
"IN-CH": "昌迪加尔",
"IN-CT": "切蒂斯格尔邦",
"IN-DH": "达德拉-纳加尔哈维利和达曼-第乌",
"IN-DL": "德里",
"IN-GA": "果阿邦",
"IN-GJ": "古吉拉特邦",
"IN-HP": "喜马偕尔邦",
"IN-HR": "哈里亚纳邦",
"IN-JH": "贾坎德邦",
"IN-JK": "查谟和克什米尔",
"IN-KA": "卡纳塔克邦",
"IN-KL": "喀拉拉邦",
"IN-LA": "拉达克",
"IN-LD": "拉克沙群岛",
"IN-MH": "马哈拉施特拉邦",
"IN-ML": "梅加拉亚邦",
"IN-MN": "曼尼普尔邦",
"IN-MP": "中央邦",
"IN-MZ": "米佐拉姆邦",
"IN-NL": "那加兰邦",
"IN-OR": "奥迪沙邦",
"IN-PB": "旁遮普邦",
"IN-PY": "本地治里",
"IN-RJ": "拉贾斯坦邦",
"IN-SK": "锡金邦",
"IN-TG": "特伦甘纳邦",
"IN-TN": "泰米尔纳德邦",
"IN-TR": "特里普拉邦",
"IN-UP": "北方邦",
"IN-UT": "北阿坎德邦",
"IN-WB": "西孟加拉邦"
}
}

View file

@ -31,14 +31,14 @@
* The shared wall always sorts sections AND cards by descending count, so the
* dense countries lead and the long tail sinks. There is no floor.
*/
import { computed, inject, ref, watch } from 'vue'
import { computed, inject, onBeforeUnmount, ref, watch } from 'vue'
import PosterWall from '@map/components/poster/PosterWall.vue'
import { useOptionalT, useOptionalI18n } from '@map/composables/useOptionalT.js'
// A country's name in the reader's language (Intl, joined from our ISO-3 codes).
import { countryName } from '@map/utils/countryName.js'
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
import { getApiBaseUrl } from '@map/api/apiBaseUrl.js'
import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
import { getRuntimeLocale, detectLocale, tStatic } from '@map/i18n/index.js'
// MOVED to the shared library 2026-09-24: the regions-dashboard poster dropdown is a second
// consumer of this same dataset, and RULE 2 puts a dataset with a named second consumer in
// library/. Generated by library/scripts/posters/emit-countries.mjs, which now writes there.
@ -108,21 +108,30 @@ try { tRegions = useRegionsI18n().t } catch (_) { /* a host with no i18n reads t
// ★ INDIA'S STATES AND UNION TERRITORIES, one poster each, like a country one level down. The table is
// fetched (and sha-checked) only when the unfiltered wall is shown; a host's filtered wall never asks for it.
// A failure hides the section rather than showing cards that cannot print. States with no people groups
// listed (Nagaland, Mizoram) are NAMED with a card that says so, never silently left out.
// A failure hides the section rather than showing cards that cannot print, and is retried a bounded number of
// times (the loader drops a failed fetch from its cache, so a retry really fetches again). States with no people
// groups listed (Nagaland, Mizoram) are NAMED with a card that says so, never silently left out.
const indiaStates = ref(null)
const STATE_RETRY_MS = [5000, 20000, 60000]
let stateRetry = null
function loadStates(attempt = 0) {
loadIndiaStates(assetsBase.value).then((table) => { indiaStates.value = table.states }).catch(() => {
if (attempt < STATE_RETRY_MS.length && !filtered.value) stateRetry = setTimeout(() => loadStates(attempt + 1), STATE_RETRY_MS[attempt])
})
}
watch(filtered, (f) => {
if (f || indiaStates.value) return
loadIndiaStates(assetsBase.value).then((table) => { indiaStates.value = table.states }).catch(() => { indiaStates.value = null })
if (f || indiaStates.value || stateRetry) return
loadStates()
}, { immediate: true })
onBeforeUnmount(() => { clearTimeout(stateRetry) })
const INDIA_REGION = (byIso3.get('IND') || {}).region || 'Asia'
const stateItems = computed(() => {
const all = indiaStates.value
if (filtered.value || !all) return []
const withGroups = statesWithPosters(all).map((st) => ({
id: st.id, label: st.name, count: st.count, sheet: '3x3', region: INDIA_REGION, indiaState: true, slugs: st.slugs,
id: st.id, iso: st.iso, label: st.name, count: st.count, sheet: '3x3', region: INDIA_REGION, indiaState: true, slugs: st.slugs,
}))
const none = all.filter((st) => !st.count).map((st) => ({ id: st.id, label: st.name, count: 0, indiaState: true, noGroups: true, slugs: [] }))
const none = all.filter((st) => !st.count).map((st) => ({ id: st.id, iso: st.iso, label: st.name, count: 0, indiaState: true, noGroups: true, slugs: [] }))
return [...withGroups, ...none]
})
@ -130,12 +139,15 @@ const items = computed(() => {
const l = wallLocale && (wallLocale.value ?? wallLocale)
const locale = (typeof l === 'string' && l) || getRuntimeLocale() || detectLocale() || 'en'
const countries = selected.value
// State names stay as the table has them (place names from the data); only country names are localized.
if (/^en\b/i.test(locale)) return [...countries, ...stateItems.value]
// A state's card is named in the reader's language from the catalogue (countryPosters.stateNames, generated from
// Wikidata by scripts/emit-india-state-names.mjs); its printed sheet stays English, like a country's.
const states = /^en\b/i.test(locale) ? stateItems.value
: stateItems.value.map((st) => ({ ...st, label: tStatic(`countryPosters.stateNames.${st.iso}`, locale) || st.label }))
if (/^en\b/i.test(locale)) return [...countries, ...states]
return [...countries.map((c) => {
const name = countryName(c.iso3, locale)
return name && name !== c.iso3 ? { ...c, label: name } : c
}), ...stateItems.value]
}), ...states]
})
// ★ EVERY COUNTRY PRINTS ITS PRAYER RESOURCES — the list, the card deck and the packet — through the

View file

@ -0,0 +1,44 @@
{
"what": "India's 36 states and union territories, named in the 11 UI locales, for the Country Posters wall's card labels.",
"source": "Wikidata (https://www.wikidata.org): labels of the items whose ISO 3166-2 code (P300) matches data/people-groups/india-states.json; dissolved items (P576) excluded.",
"licence": "CC0 1.0: Wikidata structured data is released under CC0 (https://creativecommons.org/publicdomain/zero/1.0/).",
"english": "the state table's own name (the English wall is unchanged); Wikidata's English is only a cross-check",
"retrieved": "2026-09-30",
"regenerate": "node plugin-designs/country-posters/scripts/emit-india-state-names.mjs",
"query": "SELECT ?code ?lang ?label WHERE {\n VALUES ?code { \"IN-AN\" \"IN-AP\" \"IN-AR\" \"IN-AS\" \"IN-BR\" \"IN-CG\" \"IN-CH\" \"IN-DH\" \"IN-DL\" \"IN-GA\" \"IN-GJ\" \"IN-HP\" \"IN-HR\" \"IN-JH\" \"IN-JK\" \"IN-KA\" \"IN-KL\" \"IN-LA\" \"IN-LD\" \"IN-MH\" \"IN-ML\" \"IN-MN\" \"IN-MP\" \"IN-MZ\" \"IN-NL\" \"IN-OD\" \"IN-PB\" \"IN-PY\" \"IN-RJ\" \"IN-SK\" \"IN-TN\" \"IN-TR\" \"IN-TS\" \"IN-UK\" \"IN-UP\" \"IN-WB\" }\n ?item wdt:P300 ?code .\n FILTER NOT EXISTS { ?item wdt:P576 ?dissolved }\n ?item rdfs:label ?label . BIND(LANG(?label) AS ?lang)\n FILTER(?lang IN (\"en\", \"es\", \"fr\", \"pt\", \"de\", \"it\", \"ro\", \"ru\", \"ar\", \"hi\", \"zh-hans\", \"zh-cn\", \"zh\"))\n}",
"codeAliases": {
"IN-CT": "IN-CG",
"IN-OR": "IN-OD",
"IN-TG": "IN-TS",
"IN-UT": "IN-UK"
},
"reviewedOverrides": [
"IN-DH zh: \"达德拉-纳加尔哈维利和达曼-第乌\" (Wikidata has only a Traditional-script zh label; converted character by character to the Simplified catalogue script)",
"IN-OR ar: \"أديشة\" (the label is the pre-2011 name Orissa (أريسة); ar.wikipedia's title for the item is أديشة (Odisha))"
],
"gaps": [],
"ambiguous": [],
"fallbacks": [
"IN-CH es: \"Chandigarh\" from the same-named item (1 of 1)",
"IN-CH fr: \"Chandigarh\" from the same-named item (1 of 1)",
"IN-CH pt: \"Chandigarh\" from the same-named item (1 of 1)",
"IN-CH de: \"Chandigarh\" from the same-named item (1 of 1)",
"IN-CH it: \"Chandigarh\" from the same-named item (1 of 1)",
"IN-CH ar: \"شَنديغار\" from the same-named item (1 of 1)",
"IN-CH hi: \"चण्डीगढ़\" from the same-named item (1 of 1)",
"IN-CH zh: \"昌迪加尔\" from the same-named item (1 of 1)",
"IN-DL es: \"Delhi\" from the same-named item (preferred)",
"IN-DL fr: \"Delhi\" from the same-named item (preferred)",
"IN-DL pt: \"Delhi\" from the same-named item (preferred)",
"IN-DL de: \"Delhi\" from the same-named item (preferred)",
"IN-DL it: \"Delhi\" from the same-named item (preferred)",
"IN-DL ro: \"Delhi\" from the same-named item (preferred)",
"IN-DL ru: \"Дели\" from the same-named item (preferred)",
"IN-DL ar: \"دلهي\" from the same-named item (preferred)",
"IN-DL hi: \"दिल्ली\" from the same-named item (preferred)",
"IN-DL zh: \"德里\" from the same-named item (preferred)"
],
"englishMismatches": [
"IN-DL: table \"Delhi\" vs Wikidata \"National Capital Territory of Delhi\"; same-named labels preferred"
]
}

View file

@ -141,9 +141,27 @@ export function armC(H, states, adm1Ind) {
return fails
}
// ARM E: India is its OWN section, FIRST, counted ONCE. The India country card, the state cards and the none-listed
// cards group together; the section's total is India's own count (not the card sum, which counts 855 twice); on that
// true total it outranks every regional section. PosterWall's default (no sectionTotal) must still be the card sum.
// ARM D: the state cards' localized names cover EXACTLY the table's divisions in every locale (a runtime-built key,
// `countryPosters.stateNames.${iso}`, that the i18n call-site gates cannot see), English is the table's own name, and
// the generated names carry their source licence.
const NAME_LOCALES = ['en', 'es', 'fr', 'pt', 'de', 'it', 'ro', 'ru', 'ar', 'hi', 'zh']
export function armD(states, areas, meta) {
const fails = []
const want = new Set((states || []).map((st) => st.iso))
for (const l of NAME_LOCALES) {
const names = (areas[l] && areas[l].stateNames) || {}
const have = new Set(Object.keys(names))
const missing = [...want].filter((x) => !have.has(x)), extra = [...have].filter((x) => !want.has(x))
const empty = Object.entries(names).filter(([, v]) => typeof v !== 'string' || !v.trim()).map(([k]) => k)
if (missing.length || extra.length || empty.length) fails.push(`${l} countryPosters.stateNames: missing [${missing}] extra [${extra}] empty [${empty}]`)
}
const en = (areas.en && areas.en.stateNames) || {}
const drift = (states || []).filter((st) => en[st.iso] !== st.name).map((st) => st.iso)
if (drift.length) fails.push(`English state names differ from the table's: ${drift.join(', ')} (re-run emit-india-state-names.mjs)`)
if (!meta || !/CC0/.test(String(meta.licence || ''))) fails.push('india-state-names.meta.json must record the source licence (CC0)')
return fails
}
// ARM F — A DESKTOP-ONLY CARD ON A PHONE (India): in EACH caller (the wall, the Regions list) its rows REJECT with
// poster.phoneLimitAll before any record is fetched, never resolve [] (the print path reads [] as "that list has no
// people groups to print", on a card that has 855), and the card shows the same full-block notice.
@ -158,6 +176,9 @@ export function armF(srcs) {
return fails
}
// ARM E: India is its OWN section, FIRST, counted ONCE. The India country card, the state cards and the none-listed
// cards group together; the section's total is India's own count (not the card sum, which counts 855 twice); on that
// true total it outranks every regional section. PosterWall's default (no sectionTotal) must still be the card sum.
export function armE(countries, states, wallSrc, posterWallSrc, H) {
const fails = []
if (!/:section-total=/.test(wallSrc)) fails.push('country-poster-wall.vue does not pass :section-total, so India is counted twice')
@ -189,6 +210,9 @@ async function main() {
const adm1Ind = JSON.parse(readFileSync(join(ROOT, 'public/dx-maps/assets/boundaries/gb-IND-ADM1.geojson'), 'utf8'))
if (states.length < 30 || (adm1Ind.features || []).length < 30) { console.log(`NOT MEASURED: state table ${states.length} / ADM1 ${(adm1Ind.features || []).length} features (floor 30)`); process.exitCode = 75; return }
const areas = Object.fromEntries(NAME_LOCALES.map((l) => [l, JSON.parse(readFileSync(join(ROOT, `library/i18n/locales/${l}/countryPosters.json`), 'utf8'))]))
const meta = JSON.parse(readFileSync(join(ROOT, 'plugin-designs/country-posters/data/india-state-names.meta.json'), 'utf8'))
const files = {}
for (const p of listSources(PD)) files[relative(ROOT, p)] = readFileSync(p, 'utf8')
@ -210,6 +234,8 @@ async function main() {
['a packet page becomes a large sheet', () => armB(seriesFor, countries, (it, f) => (it?.iso3 === 'IND' ? '60x96' : f))],
['the wall resolves [] for a phone-blocked card', () => armF({ wall: files[wall].replace("const desktopOnly = () => Promise.reject(keyedError('poster.phoneLimitAll'", "const desktopOnly = () => Promise.resolve([]) || (keyedError('poster.phoneLimitAll'"), regions: REG })],
['the Regions list resolves [] for a phone-blocked card', () => armF({ wall: files[wall], regions: REG.replace("phoneBlocked(item) ? Promise.reject(keyedError('poster.phoneLimitAll'", "phoneBlocked(item) ? Promise.resolve([]) || (keyedError('poster.phoneLimitAll'") })],
['a locale loses a state name', () => armD(states, { ...areas, ro: { ...areas.ro, stateNames: Object.fromEntries(Object.entries(areas.ro.stateNames).filter(([k]) => k !== 'IN-GJ')) } }, meta)],
['the names lose their licence line', () => armD(states, areas, { ...meta, licence: '' })],
['a helper that mutates the shared ADM1', () => armC({ ...H, stateGeometry: (z, a, n) => { (a.features || []).forEach((f) => { if (f.properties.shapeName === n) f.properties.listId = z.id }); return H.stateGeometry(z, a, n) } }, states, JSON.parse(JSON.stringify(adm1Ind)))],
]
let bad = 0
@ -228,12 +254,12 @@ async function main() {
const callers = []
const E = armE(countries, states, files['plugin-designs/country-posters/country-poster-wall.vue'], readFileSync(join(ROOT, 'library/components/poster/PosterWall.vue'), 'utf8'), H)
const fails = [...armA(files, callers), ...armB(seriesFor, countries, sheetFormatFor), ...armC(H, states, adm1Ind), ...E.fails, ...armF({ wall: files['plugin-designs/country-posters/country-poster-wall.vue'], regions: REG })]
const fails = [...armA(files, callers), ...armB(seriesFor, countries, sheetFormatFor), ...armC(H, states, adm1Ind), ...E.fails, ...armD(states, areas, meta), ...armF({ wall: files['plugin-designs/country-posters/country-poster-wall.vue'], regions: REG })]
const missing = [...KNOWN_CALLERS, `${STATE_CALLER}#state`].filter((k) => !callers.includes(k))
if (missing.length) fails.push(`known callers not found calling makeCountrySheet: ${missing.join(', ')}`)
if (fails.length) { fails.forEach((f) => console.log(`FAIL ${f}`)); process.exitCode = 1; return }
const ind = seriesFor(countries, countries.find((c) => c.iso3 === 'IND'))
console.log(`PASS check:country-sheet-one-path — ${Object.keys(files).length} plugin sources parsed-or-skipped, makeCountrySheet callers: ${callers.join(', ')}; seriesFor over ${countries.length} countries (India ${ind.index} of ${ind.totalLists}); ${H.statesWithPosters(states).length} state posters, series 1..${H.statesWithPosters(states).length}, every outline found; sections ${E.order.slice(0, 3).map(([g, t]) => `${g} ${t}`).join(' > ')}`)
console.log(`PASS check:country-sheet-one-path — ${Object.keys(files).length} plugin sources parsed-or-skipped, makeCountrySheet callers: ${callers.join(', ')}; seriesFor over ${countries.length} countries (India ${ind.index} of ${ind.totalLists}); ${H.statesWithPosters(states).length} state posters, series 1..${H.statesWithPosters(states).length}, every outline found; sections ${E.order.slice(0, 3).map(([g, t]) => `${g} ${t}`).join(' > ')}; state names in ${NAME_LOCALES.length} locales x ${states.length}`)
}
main().catch((e) => { console.error(`FAIL ${e.stack || e}`); process.exitCode = 1 })

View file

@ -0,0 +1,134 @@
#!/usr/bin/env node
// emit-india-state-names.mjs — India's 36 states and union territories named in the 11 UI locales, for the Country
// Posters wall's India section: the CARD labels, the print HUD and the packet/list titles. The printed sheet stays
// English by recorded decision (series.json english_only_note), like every country poster.
//
// SOURCE: Wikidata labels, matched on ISO 3166-2 (P300) against data/people-groups/india-states.json. Wikidata's
// structured data is CC0. The labels are human-edited, not machine translation. Dissolved items (P576) are excluded,
// so a retired code such as the pre-2020 Dadra and Nagar Haveli cannot win.
// English is the TABLE's own name, so the English wall does not change; Wikidata's English is only a cross-check.
// A locale Wikidata has no label for falls back to the English name AND is recorded in the meta file, never silently.
//
// Writes: library/i18n/locales/<locale>/countryPosters.json -> "stateNames": { "IN-GJ": "…", … } (other keys kept)
// plugin-designs/country-posters/data/india-state-names.meta.json (source, licence, query, date, gaps)
// Regenerate: node plugin-designs/country-posters/scripts/emit-india-state-names.mjs
import { readFileSync, writeFileSync } from 'node:fs'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..')
const LOCALES = ['en', 'es', 'fr', 'pt', 'de', 'it', 'ro', 'ru', 'ar', 'hi', 'zh']
const WD_LANG = { zh: ['zh-hans', 'zh-cn', 'zh'] } // the zh catalogue is Simplified
const wdLangs = (l) => WD_LANG[l] || [l]
// The state table keeps geoBoundaries' ISO codes; ISO 3166-2:IN has since renamed four, and Wikidata's P300 holds
// the CURRENT code. Queried under the current code, keyed back to the table's.
const CODE_ALIAS = { 'IN-CT': 'IN-CG', 'IN-OR': 'IN-OD', 'IN-TG': 'IN-TS', 'IN-UT': 'IN-UK' }
const wdCode = (iso) => CODE_ALIAS[iso] || iso
// REVIEWED OVERRIDES (hi, ar, zh read by eye, 2026-09-30), each with its evidence. Kept small and explicit: neither
// Wikidata's labels nor its Wikipedia titles are uniformly current (zh's Odisha TITLE is the old Orissa while its
// label is current; ar's Odisha LABEL is the old Orissa while its title is current), so no blanket rule is safe.
const OVERRIDES = {
'IN-OR': { ar: ['أديشة', "the label is the pre-2011 name Orissa (أريسة); ar.wikipedia's title for the item is أديشة (Odisha)"] },
'IN-DH': { zh: ['达德拉-纳加尔哈维利和达曼-第乌', 'Wikidata has only a Traditional-script zh label; converted character by character to the Simplified catalogue script'] },
}
const states = JSON.parse(readFileSync(join(ROOT, 'data/people-groups/india-states.json'), 'utf8')).states
const isos = states.map((s) => wdCode(s.iso)).sort()
const langs = [...new Set(LOCALES.flatMap(wdLangs))]
const query = `SELECT ?code ?lang ?label WHERE {
VALUES ?code { ${isos.map((c) => `"${c}"`).join(' ')} }
?item wdt:P300 ?code .
FILTER NOT EXISTS { ?item wdt:P576 ?dissolved }
?item rdfs:label ?label . BIND(LANG(?label) AS ?lang)
FILTER(?lang IN (${langs.map((l) => `"${l}"`).join(', ')}))
}`
const res = await fetch('https://query.wikidata.org/sparql?format=json&query=' + encodeURIComponent(query), {
headers: { accept: 'application/sparql-results+json', 'user-agent': 'dx-maps country-posters emit-india-state-names' },
signal: AbortSignal.timeout(60000),
})
if (!res.ok) { console.error(`Wikidata ${res.status}`); process.exit(1) }
const rows = (await res.json()).results.bindings
const got = {} // iso -> Wikidata language -> Set(labels)
for (const r of rows) ((got[r.code.value] ||= {})[r.lang.value] ||= new Set()).add(r.label.value)
// FALLBACK for a language the matched item has no label in (e.g. the "National Capital Territory of Delhi" item):
// the labels of items in India (P17 = Q668) whose English label IS the table's name (the city of Delhi,
// Chandigarh). Used only for the missing languages, and recorded.
async function sameNameLabels(name) {
const q = `SELECT ?lang ?label WHERE { ?item rdfs:label ${JSON.stringify(name)}@en ; wdt:P17 wd:Q668 .
?item rdfs:label ?label . BIND(LANG(?label) AS ?lang) FILTER(?lang IN (${langs.map((l) => `"${l}"`).join(', ')})) }`
const r = await fetch('https://query.wikidata.org/sparql?format=json&query=' + encodeURIComponent(q), {
headers: { accept: 'application/sparql-results+json', 'user-agent': 'dx-maps country-posters emit-india-state-names' },
signal: AbortSignal.timeout(60000),
})
if (!r.ok) throw new Error(`Wikidata ${r.status}`)
const by = {}
for (const b of (await r.json()).results.bindings) {
const m = (by[b.lang.value] ||= new Map()); m.set(b.label.value, (m.get(b.label.value) || 0) + 1)
}
return by // lang -> Map(label -> how many same-named items carry it)
}
const fallbacks = [], overridden = [], unreviewedZh = []
const gaps = [], ambiguous = [], englishMismatches = []
const names = Object.fromEntries(LOCALES.map((l) => [l, {}]))
for (const st of [...states].sort((a, b) => a.iso.localeCompare(b.iso))) {
const wd = got[wdCode(st.iso)] || {}
if (!Object.keys(wd).length) gaps.push(`${st.iso}: no Wikidata item (queried as ${wdCode(st.iso)})`)
let same = null
const wdEn = wd.en && [...wd.en][0]
// The table names the division more briefly than its item ("Delhi" vs "National Capital Territory of Delhi"): then
// the SAME-NAMED item's labels (the city of Delhi) are preferred in every language, so hi and ru match the English.
const preferSame = Boolean(wdEn && wdEn !== st.name)
if (preferSame) englishMismatches.push(`${st.iso}: table "${st.name}" vs Wikidata "${wdEn}"; same-named labels preferred`)
for (const l of LOCALES) {
if (l === 'en') { names.en[st.iso] = st.name; continue }
const ov = OVERRIDES[st.iso] && OVERRIDES[st.iso][l]
if (ov) { names[l][st.iso] = ov[0]; overridden.push(`${st.iso} ${l}: "${ov[0]}" (${ov[1]})`); continue }
if (preferSame) {
same ||= await sameNameLabels(st.name)
const m = wdLangs(l).map((w) => same[w]).find((x) => x && x.size)
if (m) { const [label] = [...m].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]; names[l][st.iso] = label; fallbacks.push(`${st.iso} ${l}: "${label}" from the same-named item (preferred)`); continue }
}
const src = wdLangs(l).find((w) => wd[w] && wd[w].size)
if (l === 'zh' && src === 'zh') unreviewedZh.push(`${st.iso}: only a plain "zh" label (script unknown: may be Traditional); add a reviewed OVERRIDE`)
const hit = src && wd[src]
if (!hit) {
same ||= await sameNameLabels(st.name)
const m = wdLangs(l).map((w) => same[w]).find((x) => x && x.size)
if (m) {
const [label, n] = [...m].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]
names[l][st.iso] = label; fallbacks.push(`${st.iso} ${l}: "${label}" from the same-named item (${n} of ${[...m.values()].reduce((a, x) => a + x, 0)})`)
continue
}
names[l][st.iso] = st.name; gaps.push(`${st.iso} ${l}: no label, English used`); continue
}
if (hit.size > 1) ambiguous.push(`${st.iso} ${l}: ${[...hit].join(' | ')}`)
names[l][st.iso] = [...hit].sort()[0]
}
}
for (const l of LOCALES) {
const p = join(ROOT, `library/i18n/locales/${l}/countryPosters.json`)
const area = JSON.parse(readFileSync(p, 'utf8'))
area.stateNames = names[l]
writeFileSync(p, JSON.stringify(area, null, 2) + '\n')
}
const meta = {
what: "India's 36 states and union territories, named in the 11 UI locales, for the Country Posters wall's card labels.",
source: 'Wikidata (https://www.wikidata.org): labels of the items whose ISO 3166-2 code (P300) matches data/people-groups/india-states.json; dissolved items (P576) excluded.',
licence: 'CC0 1.0: Wikidata structured data is released under CC0 (https://creativecommons.org/publicdomain/zero/1.0/).',
english: "the state table's own name (the English wall is unchanged); Wikidata's English is only a cross-check",
retrieved: new Date().toISOString().slice(0, 10),
regenerate: 'node plugin-designs/country-posters/scripts/emit-india-state-names.mjs',
query,
codeAliases: CODE_ALIAS,
reviewedOverrides: overridden,
gaps, ambiguous, fallbacks, englishMismatches,
}
writeFileSync(join(ROOT, 'plugin-designs/country-posters/data/india-state-names.meta.json'), JSON.stringify(meta, null, 2) + '\n')
console.log(`states ${states.length}, Wikidata rows ${rows.length}; gaps ${gaps.length}, ambiguous ${ambiguous.length}, fallbacks ${fallbacks.length}, English mismatches ${englishMismatches.length}`)
for (const x of [...gaps, ...ambiguous, ...fallbacks, ...overridden, ...englishMismatches]) console.log(' ' + x)
// A zh name from a plain "zh" label may be Traditional script in a Simplified catalogue: refuse rather than ship it.
if (unreviewedZh.length) { unreviewedZh.forEach((x) => console.error('FAIL ' + x)); process.exitCode = 1 }

View file

@ -76,7 +76,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -67,7 +67,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -81,7 +81,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2" 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

@ -232,7 +232,7 @@
"bundle": "country-posters",
"scriptUrl": "./country-posters/country-posters.js",
"output": "dx-maps/country-posters/country-posters.js",
"integrity": "sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm",
"integrity": "sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2",
"elements": [
"country-posters-map"
],
@ -255,8 +255,8 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -272,7 +272,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-poster-wall/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=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -288,8 +288,8 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-posters-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
}
]

View file

@ -232,7 +232,7 @@
"bundle": "country-posters",
"scriptUrl": "./country-posters/country-posters.js",
"output": "dx-maps/country-posters/country-posters.js",
"integrity": "sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm",
"integrity": "sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2",
"elements": [
"country-posters-map"
],
@ -255,8 +255,8 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -272,7 +272,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-poster-wall/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=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -288,8 +288,8 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-posters-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-hBvhBzd8aj06KvGJ4sAAawHJ6EDyJvyLaeJn8xNj70L20Go9QUpZihm9gKHWJDGm\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-h2oLxe0tebx5EhTvdA3ImH7HWCsdsuzslh73eeatGLLpUuHN8cUIvairSNaXk9x2\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
}
]