Name the legend's carets, and translate a hint that had never been translated

TWO SHARED-COMPONENT DEFECTS, both routed to me, both a step away from where
the routing pointed.

THE 14 NAMELESS BUTTONS ARE ONE COMPONENT. check:a11y-button-names reported 14
visible buttons with no accessible name across two country-posters leaves. The
routing named countries-map.vue, which contains exactly one <button> and it has
visible text — the gate prints the LEAF, so the file was an inference. Running
the gate and then a probe that prints what a developer can grep for — class,
innerHTML, box size, the custom-element host chain — named the real one:
library/components/legends/SemanticTreeLegend.vue:1228, seven
<button class="stl-caret">, 14x14, a 9x9 chevron and nothing else. The 14 is
those 7 counted twice, because country-posters-mfe composes countries-map;
worth saying, or the next reader hunts a second defect that does not exist.
"(no title either)" in the gate's output is the diagnostic half: it rules out
the title-only control this gate was built for, and it rules out Mapbox's own
controls, which ship aria-labels. These were ours and they were empty —
assistive technology said "button" once per expandable branch.

Fixed at the source, which is why it lives in library/: an aria-label naming
the branch the caret discloses, and aria-expanded, because a name tells a
screen-reader user what the control is for and still not whether the branch is
open. Three keys across eleven locales rather than a hardcoded label. And the
same defect one element over, latent: .stl-x-chip carried only a `title` —
precisely what this gate exists to reject — and hardcoded English besides. It
escaped the sweep because it renders only while a row is selected.

Seven surfaces render that legend, so one fix reaches all of them. The
artifacts are per-bundle though: country-posters is rebuilt here because its
cards are routed to me, and my own bundle with it. The other five stay stale
until their owners rebuild, and I have not touched them.

AND THE PIN HINT HAD NEVER BEEN TRANSLATED. The ask was to audit eleven locales
for a stale "red dot". There is nothing stale, because there is nothing: no
locale file contains "Tap a", "pinHint" or "coloured dot"; PinHint.vue imports
no useI18n; the sentence is a JavaScript string literal. So every map that
renders the hint has been showing an English sentence to readers in Arabic,
Russian and Chinese.

Which is worth noticing about the fix that came before it. The colour half was
found and fixed carefully — the comment above it records the Driver reporting it
five or more times, and why a shared component must not name a colour it cannot
know. All correct. Nobody asked whether the sentence was in the catalogue. Same
shape as the Russian headings next door: wrong only for readers who do not file
bug reports in English, and so invisible to everyone who could.

Eleven locales, every one saying coloured and never red. The pinHintLabel
override still comes first, so a profile that does want to name its colour can.
And the key falls back to the English sentence when a locale lacks it, so a
missing translation degrades to today's behaviour rather than rendering the
literal "messages.pinHint.tapDot" — the failure mode of a naive t() swap.

Flagged and not taken, because they are shared components with owners:
FilterDropdown renders a hardcoded English "Clear selection" and "No options
found" (the latter already exists translated in the regions catalogue, so the
component is ignoring a string that is right there), and PosterWall's export
modal renders its reassurance line untranslated.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 21:14:47 +00:00
parent bb589d584e
commit 9ba6ad7a7a
34 changed files with 346 additions and 225 deletions

View file

@ -38,8 +38,10 @@
* dropped from the custom element's shadow DOM (see WARNING.md). * dropped from the custom element's shadow DOM (see WARNING.md).
*/ */
import { ref, inject, computed, onMounted, onBeforeUnmount, getCurrentInstance, nextTick } from 'vue' import { ref, inject, computed, onMounted, onBeforeUnmount, getCurrentInstance, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import { useShadowStyles } from '../../composables/useShadowStyles.js' import { useShadowStyles } from '../../composables/useShadowStyles.js'
const { t, te } = useI18n()
const uiStore = inject('uiStore', null) const uiStore = inject('uiStore', null)
const profileConfig = inject('profileConfig', null) const profileConfig = inject('profileConfig', null)
const isDark = computed(() => uiStore?.theme === 'dark') const isDark = computed(() => uiStore?.theme === 'dark')
@ -54,8 +56,21 @@ const isDark = computed(() => uiStore?.theme === 'dark')
// The fix is the DEFAULT, not per-profile config, because the defect came from a default that // The fix is the DEFAULT, not per-profile config, because the defect came from a default that
// asserted more than it could know. A shared component cannot know what colour a given map's dots // asserted more than it could know. A shared component cannot know what colour a given map's dots
// are, so it must not claim one; profiles that DO want to name a colour still can, via pinHintLabel. // are, so it must not claim one; profiles that DO want to name a colour still can, via pinHintLabel.
const label = computed(() => // ★ AND IT WAS NEVER TRANSLATED — on ANY map, in ANY locale. The colour half of this defect was
profileConfig?.value?.pinHintLabel || 'Tap a coloured dot — someone still needs prayer') // found and fixed (the default no longer says "red"), and the note above records that carefully.
// What nobody checked is that the sentence is a JAVASCRIPT STRING LITERAL: this component has no
// useI18n, and no locale file anywhere carries the text. A reader in Arabic, Russian or Chinese
// has been shown an English sentence on every map that renders the hint. That is the same shape as
// the Russian-heading bug next door — wrong only for readers who do not file bug reports in
// English, and therefore invisible to everyone who could.
// The key falls back to the English sentence when a locale lacks it, so a missing translation
// degrades to today's behaviour rather than to "messages.pinHint.tapDot".
const label = computed(() => {
const override = profileConfig?.value?.pinHintLabel
if (override) return override
const EN = 'Tap a coloured dot — someone still needs prayer'
try { return te('messages.pinHint.tapDot') ? t('messages.pinHint.tapDot') : EN } catch (_) { return EN }
})
const visible = ref(false) const visible = ref(false)
const hintEl = ref(null) const hintEl = ref(null)

View file

@ -1225,8 +1225,19 @@ useShadowStyles(`
@click="selectNode(row.node)"> @click="selectNode(row.node)">
<span v-for="(seg, i) in row.trunks" :key="`tr${i}`" <span v-for="(seg, i) in row.trunks" :key="`tr${i}`"
class="stl-trunk" :class="`stl-trunk-${seg}`"></span> class="stl-trunk" :class="`stl-trunk-${seg}`"></span>
<!-- ★ AN ICON-ONLY DISCLOSURE NEEDS A NAME AND A STATE. This button held a 9x9 <svg> and
nothing else — no text, no aria-label, not even a title — so assistive technology
announced "button" seven times per legend with no indication of what any of them
did. check:a11y-button-names found 14 visible nameless buttons across two leaves and
these are all of them: country-posters-mfe COMPOSES countries-map, so it is the same
seven counted twice. Naming them here fixes every bundle that renders this legend,
which is the point of it living in library/.
aria-expanded comes with it, because a name alone tells a screen-reader user what
the control is for and still not whether the branch is open. -->
<button v-if="row.node.children?.length" <button v-if="row.node.children?.length"
class="stl-caret" :class="{ open: expandedIds.has(row.node.id) }" class="stl-caret" :class="{ open: expandedIds.has(row.node.id) }"
:aria-expanded="expandedIds.has(row.node.id) ? 'true' : 'false'"
:aria-label="t(expandedIds.has(row.node.id) ? 'legend.a11y.collapseRow' : 'legend.a11y.expandRow', { label: row.node.label })"
@click.stop="toggleExpand(row.node.id, $event)"> @click.stop="toggleExpand(row.node.id, $event)">
<svg width="9" height="9" viewBox="0 0 12 12" fill="none"> <svg width="9" height="9" viewBox="0 0 12 12" fill="none">
<path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.6" <path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.6"
@ -1240,8 +1251,15 @@ useShadowStyles(`
<span v-if="hasCount" class="stl-num">{{ fmtCount(row.node.count) }}</span> <span v-if="hasCount" class="stl-num">{{ fmtCount(row.node.count) }}</span>
<span v-if="hasPop" class="stl-num">{{ fmtPop(row.node.pop) }}</span> <span v-if="hasPop" class="stl-num">{{ fmtPop(row.node.pop) }}</span>
<span class="stl-x-slot"> <span class="stl-x-slot">
<!-- The same defect, latent: this one carries a `title`, and a title is a TOOLTIP —
unreliably announced and never shown to touch users — so its accessible name was
also empty. It escaped the gate only because it renders while a row is SELECTED
and the sweep measured an unselected legend. Hardcoded English besides, on a
component whose every other string is translated. -->
<button v-if="selectedId === row.node.id" <button v-if="selectedId === row.node.id"
class="stl-x-chip" title="Clear selection" class="stl-x-chip"
:title="t('legend.a11y.clearSelection')"
:aria-label="t('legend.a11y.clearSelection')"
@click.stop="deselect($event)"> @click.stop="deselect($event)">
<svg width="8" height="8" viewBox="0 0 12 12" fill="none"> <svg width="8" height="8" viewBox="0 0 12 12" fill="none">
<path d="M2 2L10 10M10 2L2 10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> <path d="M2 2L10 10M10 2L2 10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>

View file

@ -59,5 +59,10 @@
"upg100Region": "المنطقة — ترتّب قوائم UPG-100 حسب منطقة العالم (أفريقيا، الهند، الشرق الأوسط، الصمّ، …). الأعداد هي مجموع UUPG والسكان لكل قائمة تحتها.", "upg100Region": "المنطقة — ترتّب قوائم UPG-100 حسب منطقة العالم (أفريقيا، الهند، الشرق الأوسط، الصمّ، …). الأعداد هي مجموع UUPG والسكان لكل قائمة تحتها.",
"upg100Zone": "قوائم UPG-100 — كل واحدة هي قائمة صلاة للشعوب غير الموصول إليها في منطقة ما، وهي ما ترسمه الخريطة وتلوّنه. تُطبع كل قائمة ككُتيّب خاص بها.", "upg100Zone": "قوائم UPG-100 — كل واحدة هي قائمة صلاة للشعوب غير الموصول إليها في منطقة ما، وهي ما ترسمه الخريطة وتلوّنه. تُطبع كل قائمة ككُتيّب خاص بها.",
"upg100Group": "مجموعة شعبية — المجموعات غير الموصول إليها داخل القائمة، الأكثر حاجة للصلاة أولاً. كل نقطة على الخريطة هي واحدة منها." "upg100Group": "مجموعة شعبية — المجموعات غير الموصول إليها داخل القائمة، الأكثر حاجة للصلاة أولاً. كل نقطة على الخريطة هي واحدة منها."
},
"a11y": {
"expandRow": "توسيع {label}",
"collapseRow": "طي {label}",
"clearSelection": "إلغاء التحديد"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "القائمة قادمة قريباً" "menuComingSoon": "القائمة قادمة قريباً",
"pinHint": {
"tapDot": "اضغط على نقطة ملوّنة — لا يزال هناك من يحتاج إلى الصلاة"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Region — sortiert die UPG-100-Listen nach Weltgegend (Afrika, Indien, Naher Osten, Gehörlose, …). Die Zahlen sind die UUPGs und die Bevölkerung aller darunterliegenden Listen zusammen.", "upg100Region": "Region — sortiert die UPG-100-Listen nach Weltgegend (Afrika, Indien, Naher Osten, Gehörlose, …). Die Zahlen sind die UUPGs und die Bevölkerung aller darunterliegenden Listen zusammen.",
"upg100Zone": "UPG-100-Listen — jede ist eine Gebetsliste der unerreichten Volksgruppen eines Gebiets und das, was die Karte zeichnet und einfärbt. Jede Liste wird als eigenes Heft gedruckt.", "upg100Zone": "UPG-100-Listen — jede ist eine Gebetsliste der unerreichten Volksgruppen eines Gebiets und das, was die Karte zeichnet und einfärbt. Jede Liste wird als eigenes Heft gedruckt.",
"upg100Group": "Volksgruppe — die einzelnen unerreichten Gruppen innerhalb einer Liste, größter Gebetsbedarf zuerst. Jeder Kartenpunkt ist eine davon." "upg100Group": "Volksgruppe — die einzelnen unerreichten Gruppen innerhalb einer Liste, größter Gebetsbedarf zuerst. Jeder Kartenpunkt ist eine davon."
},
"a11y": {
"expandRow": "{label} ausklappen",
"collapseRow": "{label} einklappen",
"clearSelection": "Auswahl aufheben"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Menü folgt in Kürze" "menuComingSoon": "Menü folgt in Kürze",
"pinHint": {
"tapDot": "Tippe auf einen farbigen Punkt — jemand braucht noch Gebet"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Region — sort the UPG-100 lists by world area (Africa, India, Middle East, Deaf, …). Counts are the UUPGs and population summed across every list beneath it.", "upg100Region": "Region — sort the UPG-100 lists by world area (Africa, India, Middle East, Deaf, …). Counts are the UUPGs and population summed across every list beneath it.",
"upg100Zone": "UPG-100 Lists — each one is a prayer list of the unreached people groups in an area, and is what the map paints and colours. Every list prints as its own packet.", "upg100Zone": "UPG-100 Lists — each one is a prayer list of the unreached people groups in an area, and is what the map paints and colours. Every list prints as its own packet.",
"upg100Group": "People Group — the individual unreached groups inside a list, largest prayer need first. Each map dot is one of these." "upg100Group": "People Group — the individual unreached groups inside a list, largest prayer need first. Each map dot is one of these."
},
"a11y": {
"expandRow": "Expand {label}",
"collapseRow": "Collapse {label}",
"clearSelection": "Clear selection"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Menu coming soon" "menuComingSoon": "Menu coming soon",
"pinHint": {
"tapDot": "Tap a coloured dot — someone still needs prayer"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Región — ordena las listas UPG-100 por área mundial (África, India, Oriente Medio, Sordos, …). Los totales son los UUPG y la población sumados de todas las listas que contiene.", "upg100Region": "Región — ordena las listas UPG-100 por área mundial (África, India, Oriente Medio, Sordos, …). Los totales son los UUPG y la población sumados de todas las listas que contiene.",
"upg100Zone": "Listas UPG-100 — cada una es una lista de oración de los grupos étnicos no alcanzados de un área, y es lo que el mapa pinta y colorea. Cada lista se imprime como su propio paquete.", "upg100Zone": "Listas UPG-100 — cada una es una lista de oración de los grupos étnicos no alcanzados de un área, y es lo que el mapa pinta y colorea. Cada lista se imprime como su propio paquete.",
"upg100Group": "Grupo étnico — los grupos no alcanzados dentro de una lista, primero los de mayor necesidad de oración. Cada punto del mapa es uno de ellos." "upg100Group": "Grupo étnico — los grupos no alcanzados dentro de una lista, primero los de mayor necesidad de oración. Cada punto del mapa es uno de ellos."
},
"a11y": {
"expandRow": "Expandir {label}",
"collapseRow": "Contraer {label}",
"clearSelection": "Borrar la selección"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Menú próximamente" "menuComingSoon": "Menú próximamente",
"pinHint": {
"tapDot": "Toca un punto de color: alguien todavía necesita oración"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Région — trie les listes UPG-100 par zone du monde (Afrique, Inde, Moyen-Orient, Sourds, …). Les totaux sont les UUPG et la population additionnés sur toutes les listes qu’elle contient.", "upg100Region": "Région — trie les listes UPG-100 par zone du monde (Afrique, Inde, Moyen-Orient, Sourds, …). Les totaux sont les UUPG et la population additionnés sur toutes les listes qu’elle contient.",
"upg100Zone": "Listes UPG-100 — chacune est une liste de prière des groupes de peuples non atteints d’une zone, et c’est ce que la carte dessine et colore. Chaque liste s’imprime comme son propre livret.", "upg100Zone": "Listes UPG-100 — chacune est une liste de prière des groupes de peuples non atteints d’une zone, et c’est ce que la carte dessine et colore. Chaque liste s’imprime comme son propre livret.",
"upg100Group": "Groupe de peuples — les groupes non atteints au sein d’une liste, les plus grands besoins de prière en premier. Chaque point de la carte en est un." "upg100Group": "Groupe de peuples — les groupes non atteints au sein d’une liste, les plus grands besoins de prière en premier. Chaque point de la carte en est un."
},
"a11y": {
"expandRow": "Développer {label}",
"collapseRow": "Réduire {label}",
"clearSelection": "Effacer la sélection"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Menu bientôt disponible" "menuComingSoon": "Menu bientôt disponible",
"pinHint": {
"tapDot": "Touchez un point coloré — quelqu’un a encore besoin de prière"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "क्षेत्र — UPG-100 सूचियों को विश्व क्षेत्र के अनुसार क्रमित करता है (अफ़्रीका, भारत, मध्य पूर्व, बधिर, …)। संख्याएँ उसके नीचे की हर सूची के UUPG और जनसंख्या का योग हैं।", "upg100Region": "क्षेत्र — UPG-100 सूचियों को विश्व क्षेत्र के अनुसार क्रमित करता है (अफ़्रीका, भारत, मध्य पूर्व, बधिर, …)। संख्याएँ उसके नीचे की हर सूची के UUPG और जनसंख्या का योग हैं।",
"upg100Zone": "UPG-100 सूचियाँ — हर एक किसी क्षेत्र के अनपहुँचे जनसमूहों की प्रार्थना सूची है, और यही मानचित्र बनाता और रंगता है। हर सूची अपने अलग पैकेट के रूप में छपती है।", "upg100Zone": "UPG-100 सूचियाँ — हर एक किसी क्षेत्र के अनपहुँचे जनसमूहों की प्रार्थना सूची है, और यही मानचित्र बनाता और रंगता है। हर सूची अपने अलग पैकेट के रूप में छपती है।",
"upg100Group": "जनसमूह — किसी सूची के भीतर के अलग-अलग अनपहुँचे समूह, सबसे बड़ी प्रार्थना आवश्यकता पहले। मानचित्र का हर बिंदु इनमें से एक है।" "upg100Group": "जनसमूह — किसी सूची के भीतर के अलग-अलग अनपहुँचे समूह, सबसे बड़ी प्रार्थना आवश्यकता पहले। मानचित्र का हर बिंदु इनमें से एक है।"
},
"a11y": {
"expandRow": "{label} का विस्तार करें",
"collapseRow": "{label} को छोटा करें",
"clearSelection": "चयन हटाएँ"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "मेनू शीघ्र आ रहा है" "menuComingSoon": "मेनू शीघ्र आ रहा है",
"pinHint": {
"tapDot": "किसी रंगीन बिंदु पर टैप करें — किसी को अभी भी प्रार्थना की आवश्यकता है"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Regione — ordina le liste UPG-100 per area del mondo (Africa, India, Medio Oriente, Sordi, …). I totali sono gli UUPG e la popolazione sommati di ogni lista sottostante.", "upg100Region": "Regione — ordina le liste UPG-100 per area del mondo (Africa, India, Medio Oriente, Sordi, …). I totali sono gli UUPG e la popolazione sommati di ogni lista sottostante.",
"upg100Zone": "Liste UPG-100 — ognuna è una lista di preghiera dei gruppi etnici non raggiunti di un’area, ed è ciò che la mappa disegna e colora. Ogni lista si stampa come il proprio fascicolo.", "upg100Zone": "Liste UPG-100 — ognuna è una lista di preghiera dei gruppi etnici non raggiunti di un’area, ed è ciò che la mappa disegna e colora. Ogni lista si stampa come il proprio fascicolo.",
"upg100Group": "Gruppo etnico — i singoli gruppi non raggiunti dentro una lista, prima quelli con maggiore bisogno di preghiera. Ogni punto della mappa è uno di questi." "upg100Group": "Gruppo etnico — i singoli gruppi non raggiunti dentro una lista, prima quelli con maggiore bisogno di preghiera. Ogni punto della mappa è uno di questi."
},
"a11y": {
"expandRow": "Espandi {label}",
"collapseRow": "Comprimi {label}",
"clearSelection": "Cancella la selezione"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Menu in arrivo" "menuComingSoon": "Menu in arrivo",
"pinHint": {
"tapDot": "Tocca un punto colorato — qualcuno ha ancora bisogno di preghiera"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Região — ordena as listas UPG-100 por área do mundo (África, Índia, Oriente Médio, Surdos, …). Os totais são os UUPG e a população somados de todas as listas abaixo dela.", "upg100Region": "Região — ordena as listas UPG-100 por área do mundo (África, Índia, Oriente Médio, Surdos, …). Os totais são os UUPG e a população somados de todas as listas abaixo dela.",
"upg100Zone": "Listas UPG-100 — cada uma é uma lista de oração dos grupos étnicos não alcançados de uma área, e é o que o mapa pinta e colore. Cada lista é impressa como seu próprio pacote.", "upg100Zone": "Listas UPG-100 — cada uma é uma lista de oração dos grupos étnicos não alcançados de uma área, e é o que o mapa pinta e colore. Cada lista é impressa como seu próprio pacote.",
"upg100Group": "Grupo étnico — os grupos não alcançados dentro de uma lista, primeiro os de maior necessidade de oração. Cada ponto do mapa é um deles." "upg100Group": "Grupo étnico — os grupos não alcançados dentro de uma lista, primeiro os de maior necessidade de oração. Cada ponto do mapa é um deles."
},
"a11y": {
"expandRow": "Expandir {label}",
"collapseRow": "Recolher {label}",
"clearSelection": "Limpar a seleção"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Menu em breve" "menuComingSoon": "Menu em breve",
"pinHint": {
"tapDot": "Toque num ponto colorido — alguém ainda precisa de oração"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Regiune — sortează listele UPG-100 după zona lumii (Africa, India, Orientul Mijlociu, Surzi, …). Totalurile sunt UUPG-urile și populația însumate din fiecare listă de dedesubt.", "upg100Region": "Regiune — sortează listele UPG-100 după zona lumii (Africa, India, Orientul Mijlociu, Surzi, …). Totalurile sunt UUPG-urile și populația însumate din fiecare listă de dedesubt.",
"upg100Zone": "Liste UPG-100 — fiecare este o listă de rugăciune a grupurilor etnice neatinse dintr-o zonă și este ceea ce harta desenează și colorează. Fiecare listă se tipărește ca propriul pachet.", "upg100Zone": "Liste UPG-100 — fiecare este o listă de rugăciune a grupurilor etnice neatinse dintr-o zonă și este ceea ce harta desenează și colorează. Fiecare listă se tipărește ca propriul pachet.",
"upg100Group": "Grup etnic — grupurile neatinse dintr-o listă, mai întâi cele cu cea mai mare nevoie de rugăciune. Fiecare punct de pe hartă este unul dintre ele." "upg100Group": "Grup etnic — grupurile neatinse dintr-o listă, mai întâi cele cu cea mai mare nevoie de rugăciune. Fiecare punct de pe hartă este unul dintre ele."
},
"a11y": {
"expandRow": "Extinde {label}",
"collapseRow": "Restrânge {label}",
"clearSelection": "Șterge selecția"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Meniu în curând" "menuComingSoon": "Meniu în curând",
"pinHint": {
"tapDot": "Atinge un punct colorat — cineva are încă nevoie de rugăciune"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "Регион — сортирует списки UPG-100 по частям света (Африка, Индия, Ближний Восток, Глухие, …). Числа — это UUPG и население, просуммированные по всем спискам внутри.", "upg100Region": "Регион — сортирует списки UPG-100 по частям света (Африка, Индия, Ближний Восток, Глухие, …). Числа — это UUPG и население, просуммированные по всем спискам внутри.",
"upg100Zone": "Списки UPG-100 — каждый представляет собой молитвенный список непросвещённых народов области и именно он отображается и раскрашивается на карте. Каждый список печатается отдельным буклетом.", "upg100Zone": "Списки UPG-100 — каждый представляет собой молитвенный список непросвещённых народов области и именно он отображается и раскрашивается на карте. Каждый список печатается отдельным буклетом.",
"upg100Group": "Народ — отдельные непросвещённые народы внутри списка, сначала с наибольшей нуждой в молитве. Каждая точка на карте — один из них." "upg100Group": "Народ — отдельные непросвещённые народы внутри списка, сначала с наибольшей нуждой в молитве. Каждая точка на карте — один из них."
},
"a11y": {
"expandRow": "Развернуть {label}",
"collapseRow": "Свернуть {label}",
"clearSelection": "Снять выделение"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "Меню скоро появится" "menuComingSoon": "Меню скоро появится",
"pinHint": {
"tapDot": "Нажмите на цветную точку — кому-то всё ещё нужна молитва"
}
} }

View file

@ -59,5 +59,10 @@
"upg100Region": "区域 — 按世界区域排列 UPG-100 名单(非洲、印度、中东、聋人……)。数字是其下每份名单的 UUPG 与人口总和。", "upg100Region": "区域 — 按世界区域排列 UPG-100 名单(非洲、印度、中东、聋人……)。数字是其下每份名单的 UUPG 与人口总和。",
"upg100Zone": "UPG-100 名单 — 每一份都是某地区未得之民的祷告名单,也是地图所绘制和上色的内容。每份名单印成独立的祷告册。", "upg100Zone": "UPG-100 名单 — 每一份都是某地区未得之民的祷告名单,也是地图所绘制和上色的内容。每份名单印成独立的祷告册。",
"upg100Group": "族群 — 名单内各个未得之民群体,祷告需要最大的排在前面。地图上每个点就是其中之一。" "upg100Group": "族群 — 名单内各个未得之民群体,祷告需要最大的排在前面。地图上每个点就是其中之一。"
},
"a11y": {
"expandRow": "展开{label}",
"collapseRow": "折叠{label}",
"clearSelection": "清除选择"
} }
} }

View file

@ -1,3 +1,6 @@
{ {
"menuComingSoon": "菜单即将推出" "menuComingSoon": "菜单即将推出",
"pinHint": {
"tapDot": "点按一个彩色圆点——仍有人需要祷告"
}
} }

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script> <script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. --> <!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-S0O8LKY8FDeykxEoMkbMbI7dHp8DZQlNAxW+xj+9rJ/XDvlGUvY4t9zEnuRym7hd" crossorigin="anonymous"></script> <script src="../country-posters.js" integrity="sha384-zA/pbofsasr4aEgMczbqePae9kmb2Xh2wp3V4UIp0T8ZRgAH3CSUx6dA/8IfwkFH" crossorigin="anonymous"></script>
<script> <script>
// Mapbox token resolution — the A3 ladder, in precedence order: // Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script> <script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. --> <!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-S0O8LKY8FDeykxEoMkbMbI7dHp8DZQlNAxW+xj+9rJ/XDvlGUvY4t9zEnuRym7hd" crossorigin="anonymous"></script> <script src="../country-posters.js" integrity="sha384-zA/pbofsasr4aEgMczbqePae9kmb2Xh2wp3V4UIp0T8ZRgAH3CSUx6dA/8IfwkFH" crossorigin="anonymous"></script>
<script> <script>
// Mapbox token resolution — the A3 ladder, in precedence order: // Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script> <script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. --> <!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-S0O8LKY8FDeykxEoMkbMbI7dHp8DZQlNAxW+xj+9rJ/XDvlGUvY4t9zEnuRym7hd" crossorigin="anonymous"></script> <script src="../country-posters.js" integrity="sha384-zA/pbofsasr4aEgMczbqePae9kmb2Xh2wp3V4UIp0T8ZRgAH3CSUx6dA/8IfwkFH" crossorigin="anonymous"></script>
<script> <script>
// Mapbox token resolution — the A3 ladder, in precedence order: // Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -158,7 +158,7 @@
"bundle": "country-posters", "bundle": "country-posters",
"scriptUrl": "./country-posters/country-posters.js", "scriptUrl": "./country-posters/country-posters.js",
"output": "doxa-maps/country-posters/country-posters.js", "output": "doxa-maps/country-posters/country-posters.js",
"integrity": "sha384-S0O8LKY8FDeykxEoMkbMbI7dHp8DZQlNAxW+xj+9rJ/XDvlGUvY4t9zEnuRym7hd", "integrity": "sha384-zA/pbofsasr4aEgMczbqePae9kmb2Xh2wp3V4UIp0T8ZRgAH3CSUx6dA/8IfwkFH",
"elements": [ "elements": [
"country-posters-map" "country-posters-map"
], ],
@ -178,7 +178,7 @@
"scriptUrl": "./country-posters/country-posters.js", "scriptUrl": "./country-posters/country-posters.js",
"embed": { "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>", "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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-S0O8LKY8FDeykxEoMkbMbI7dHp8DZQlNAxW+xj+9rJ/XDvlGUvY4t9zEnuRym7hd\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"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-zA/pbofsasr4aEgMczbqePae9kmb2Xh2wp3V4UIp0T8ZRgAH3CSUx6dA/8IfwkFH\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
} }
}, },
{ {
@ -192,7 +192,7 @@
"scriptUrl": "./country-posters/country-posters.js", "scriptUrl": "./country-posters/country-posters.js",
"embed": { "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>", "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-S0O8LKY8FDeykxEoMkbMbI7dHp8DZQlNAxW+xj+9rJ/XDvlGUvY4t9zEnuRym7hd\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"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-zA/pbofsasr4aEgMczbqePae9kmb2Xh2wp3V4UIp0T8ZRgAH3CSUx6dA/8IfwkFH\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
} }
}, },
{ {
@ -206,7 +206,7 @@
"scriptUrl": "./country-posters/country-posters.js", "scriptUrl": "./country-posters/country-posters.js",
"embed": { "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>", "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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-S0O8LKY8FDeykxEoMkbMbI7dHp8DZQlNAxW+xj+9rJ/XDvlGUvY4t9zEnuRym7hd\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"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-zA/pbofsasr4aEgMczbqePae9kmb2Xh2wp3V4UIp0T8ZRgAH3CSUx6dA/8IfwkFH\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
} }
} }
] ]
@ -879,7 +879,7 @@
"bundle": "regions-dashboard", "bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js", "scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js", "output": "doxa-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-m6qbvnKE6N5Ij+4ly5xXudaoAZ25bFr0qdMp5tnr/dpu0wwzmPQsKqPAgcNu1u5S", "integrity": "sha384-OuHpxUXT2tYGGqM5UVwr/D6X/2XgPYyk8ru8uVrEN7xaj3S6n+RxElpkrR0hgTmc",
"elements": [ "elements": [
"regions-dashboard" "regions-dashboard"
], ],
@ -899,7 +899,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js", "scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": { "embed": {
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>", "iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-m6qbvnKE6N5Ij+4ly5xXudaoAZ25bFr0qdMp5tnr/dpu0wwzmPQsKqPAgcNu1u5S\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>" "script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-OuHpxUXT2tYGGqM5UVwr/D6X/2XgPYyk8ru8uVrEN7xaj3S6n+RxElpkrR0hgTmc\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
} }
}, },
{ {
@ -913,7 +913,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js", "scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": { "embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>", "iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-m6qbvnKE6N5Ij+4ly5xXudaoAZ25bFr0qdMp5tnr/dpu0wwzmPQsKqPAgcNu1u5S\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>" "script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-OuHpxUXT2tYGGqM5UVwr/D6X/2XgPYyk8ru8uVrEN7xaj3S6n+RxElpkrR0hgTmc\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
} }
}, },
{ {
@ -927,7 +927,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js", "scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": { "embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>", "iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-m6qbvnKE6N5Ij+4ly5xXudaoAZ25bFr0qdMp5tnr/dpu0wwzmPQsKqPAgcNu1u5S\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>" "script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-OuHpxUXT2tYGGqM5UVwr/D6X/2XgPYyk8ru8uVrEN7xaj3S6n+RxElpkrR0hgTmc\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
} }
} }
] ]

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script> <script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. --> <!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-m6qbvnKE6N5Ij+4ly5xXudaoAZ25bFr0qdMp5tnr/dpu0wwzmPQsKqPAgcNu1u5S" crossorigin="anonymous"></script> <script src="../regions-dashboard.js" integrity="sha384-OuHpxUXT2tYGGqM5UVwr/D6X/2XgPYyk8ru8uVrEN7xaj3S6n+RxElpkrR0hgTmc" crossorigin="anonymous"></script>
<script> <script>
// Mapbox token resolution — the A3 ladder, in precedence order: // Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script> <script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. --> <!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-m6qbvnKE6N5Ij+4ly5xXudaoAZ25bFr0qdMp5tnr/dpu0wwzmPQsKqPAgcNu1u5S" crossorigin="anonymous"></script> <script src="../regions-dashboard.js" integrity="sha384-OuHpxUXT2tYGGqM5UVwr/D6X/2XgPYyk8ru8uVrEN7xaj3S6n+RxElpkrR0hgTmc" crossorigin="anonymous"></script>
<script> <script>
// Mapbox token resolution — the A3 ladder, in precedence order: // Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script> <script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. --> <!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-m6qbvnKE6N5Ij+4ly5xXudaoAZ25bFr0qdMp5tnr/dpu0wwzmPQsKqPAgcNu1u5S" crossorigin="anonymous"></script> <script src="../regions-dashboard.js" integrity="sha384-OuHpxUXT2tYGGqM5UVwr/D6X/2XgPYyk8ru8uVrEN7xaj3S6n+RxElpkrR0hgTmc" crossorigin="anonymous"></script>
<script> <script>
// Mapbox token resolution — the A3 ladder, in precedence order: // Mapbox token resolution — the A3 ladder, in precedence order: