THE POPUP WAS A STUB. Click a pin on their region page and you get a card: the
people group's photo, the name, "country · primary ethnolinguistic group", a
definition list of Population / Religion / Intercessors-out-of-100, up to three
clamped lines of location description, and two buttons — Pray, which is the
point of the entire site, and Full Profile. Click one on ours and you got four
lines of hardcoded English with en-US number grouping in all eleven locales.
No diff here could ever have found it. Every vs-live probe reads the page at
rest, and a popup only exists after a click. So there is a new one, and the
popup now matches theirs on 37 of 37 measured properties with the same stat
labels, the same two actions, and their ?source=doxalife attribution on the
Pray link — which is invisible on screen and exactly what a re-implementation
drops silently.
Noticing it was not enough to fix it: the DATA was not on the page. Their
PIN_FIELDS asks for rop1, religion, location_description, image_url and
has_photo; ours asked for none of them, so a faithful popup would have rendered
empty rows. Five more columns on the same single request. Their row mapping
comes with it, including the has_photo gate on the image — a row can carry a
placeholder image_url and they only show it when the flag says there is a real
photo — as does their fuller escape, which also escapes both quote characters,
and that matters because these strings are interpolated into href and alt.
AND THAT FILE HAD NO t() AT ALL. Its prayer legend rendered four hardcoded
English strings on a page whose every other word is translated into eleven
languages — and three of those four were already in the catalogue, translated
for the Prayer Coverage card and hardcoded here anyway.
LOCALISATION, re-measured honestly. The old 97.4% was counting strings
identical to English as untranslated, which is wrong: es/fr/pt/ro share
"Total", and French shares "Population" and "Religion". Those are cognates. The
real question is coverage of their surfaces, so: every t() argument in
RegionDetail.vue, pages/regions/index.vue, CountryDetail.vue and CountryMap.vue
— 35 strings — checked against our catalogue. Two were missing, both
CountryMap's: "Outside this region" and "Pray". Added in all eleven, the eight
DX-life translates copied from their own common.json and it/ro/zh composed from
words already attested in those locales' own files rather than invented. Then
the reverse direction: a scan for capitalised text nodes outside {{ }} found
two of our own strings left in English — the dashboard's loading state and the
list's empty state, neither of which DX-life has an equivalent for because
their page is server-rendered. Both translated. The scan now returns zero in
all three components, and coverage of their surfaces is 35/35.
Verified alongside: all seven regions at 107 properties identical to live each
(not just Africa), mobile parity still 44/0 at 390 and 768 after the CSS
surgery, and first paint of the list measured at 3766ms with 1863KB of it a map
engine that leaf never calls — carded to the build machine, not worked around.
|
||
|---|---|---|
| .. | ||
| api | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| data | ||
| i18n | ||
| layouts | ||
| maps | ||
| packet | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| index.js | ||
| ProfileLoader.vue | ||
| README.md | ||
library/ (@map) — the shared map template
The other half of the architecture (see ../plugin-designs/README.md):
every map in plugin-designs/<your-map>/ composes the reusable, parameterized pieces that live here.
Fix something here and every map benefits on the next build; break something here and every map
breaks — read components/WARNING.md before touching shared pieces.
| Folder | What lives there |
|---|---|
ProfileLoader.vue |
The root of every bundle: parses profile-config, loads the profile, provides the stores + config |
components/ |
Shared UI — map, legends, controls, popup (PeopleGroupDetail), tables, charts, hints (README) |
composables/ |
Shared logic — map engine (useMapInstance), provider switch, shadow-DOM styles, theming |
stores/ |
Per-instance Pinia stores (ui / map / data) — provided by ProfileLoader, injected by children |
api/ |
Data-source registry (sources.json) + fetch layer — the JAMstack runtime wiring |
colors/ |
Color sets maps opt into via profile-config.colorSet |
i18n/ |
The shared string catalogs (all maps speak the same languages) |
data/ |
Small shared datasets baked at build time (big served data lives in ../data/) |
constants/ |
Shared constants |
scripts/ |
Maintainer pipelines (posters, research) — not part of any bundle |
The rule of thumb (same as WARNING.md): custom code in your app-profile, reusable code here — and things only move from the first to the second once they've earned it.