doxa-maps/library
Doxa Maps 5ab4af2e44 Five plugins were silently monolingual, because the bootstrap was copied sixteen times
Goal (c), and it turned into a goal (d) fix. Every profile element is
defineCustomElement(ProfileLoader, { configureApp }), and the configureApp has four
steps. The fourth, app.provide('appI18n', i18n), is the one that gets dropped.

ProfileLoader injects that instance (ProfileLoader.vue:176) to drive its locale from
profile-config. Without the provide the watcher holds null and the drive NEVER RUNS --
while setRuntimeLocale() on the next line still fires, so tStatic strings DO follow the
host. The element renders a convincing MIXTURE of the reader's language and English, and
nothing throws, because the t() shim falls back to its key. It reads as "a few strings
not translated yet".

SHIPPING THAT TODAY: country-posters, key-cities, gospel-resources, cities-internet and
my-upg-100-list. regions-plugin censused all 19 entry files independently and found the
same five and no sixth. The measurement is regions-dashboard's, from when they hit it:
a Spanish catalogue present, es set, and every t() string still English.

All sixteen entries now call library/elements/defineMapElement.js, so the five are fixed
by construction rather than by five separate edits. Behaviour is otherwise identical --
same options, same element names, same per-plugin extras. wall-of-people-groups needed
the helper to hand its pinia back, because it takes a store off its own instance and a
helper that hid pinia would have given it a second one: two stores and a silent state
split.

★ AND THE CENSUS THAT RECOMMENDED THIS SAW FOUR OF SIXTEEN. My detector hashed function
BODIES; eleven sites are not `function buildXElement()` at all, they are
`const El = defineCustomElement(...)`. I wrote "every number here is a lower bound" in
the census as a caveat and it was the headline within the hour -- a 4x undercount of the
item it recommended. So this gate greps for the SHAPE, not for a known list: a check
written around the four that matched would have passed the twelve that did not.

check-element-bootstrap has two arms. No entry may build a profile element by hand, and
the helper itself must provide appI18n -- PLANTED as a negative case, so the regression
cannot return by editing library instead of a plugin. It also fails if it finds fewer
than ten entries, because a detection that stops matching reports the same green as a
tree with no problems.

THE PLANT CAUGHT TWO BUGS IN THIS GATE BEFORE IT CAUGHT ANYTHING ELSE. The predicate
matched the helper's DOCSTRING, so it would have passed a helper that only mentions the
provide in prose -- comments are stripped now, because a check that reads comments is
reading the author's intentions rather than the program. And the plant itself used /m
instead of /gm, so it deleted the documentation and left the code, then reported that it
could not detect an absence that was not there.
2026-09-28 17:40:34 +00:00
..
api The people-group list is a universal prop: the same maps can show any project's list 2026-09-26 05:16:02 +00:00
colors The legends mirror now, and their labels never became their values 2026-09-26 22:06:58 +00:00
components Record the agreed ring vocabulary, without making it a default 2026-09-28 16:49:01 +00:00
composables The packet's list becomes the profile TABLE: picture, description, three boxes 2026-09-28 16:49:01 +00:00
constants Every runtime CDN script and stylesheet is an exact version with an integrity hash 2026-09-26 05:50:24 +00:00
data Country names in the reader's language, for all 242 2026-09-27 04:14:31 +00:00
elements Five plugins were silently monolingual, because the bootstrap was copied sixteen times 2026-09-28 17:40:34 +00:00
i18n The packet-variant control is translated: three button keys in the other ten locales 2026-09-28 16:49:31 +00:00
layouts DataTable + StackLayout: physical left/right -> logical, where the rule mirrors 2026-09-28 09:48:45 +00:00
lists List names speak the reader's language: listLabel and listDefinition 2026-09-26 08:39:52 +00:00
maps Two readers of one private field, and nothing was holding them together 2026-09-28 15:52:46 +00:00
packet The table row is a border-box, and the offline packet gets its description 2026-09-28 16:49:01 +00:00
packets Number({value:"1"}) is NaN, so the whole reachedness sentence vanished 2026-09-28 15:52:47 +00:00
scripts The table row is a border-box, and the offline packet gets its description 2026-09-28 16:49:01 +00:00
stores Make the map's clustering picker real: four options, four different maps 2026-09-25 16:16:46 +00:00
styles Fiord: the MapLibre dark that actually looks like the map it replaces 2026-09-24 02:26:14 +00:00
utils The packet's generated description, and the language name it prints was index-ordered 2026-09-28 13:56:06 +00:00
vendor/cldr Country names in the reader's language, for all 242 2026-09-27 04:14:31 +00:00
index.js Move internal files out of the repo; fix five dead links; a fuller delegation diagram 2026-09-25 05:14:10 +00:00
ProfileLoader.vue ?lang= on the page turns every embedded map's direction and data language, not only its text 2026-09-27 06:42:54 +00:00
README.md Complete app-profiles->plugin-designs sweep across comments/docs; build green; layouts kept at root (load-bearing) 2026-09-17 18:18:19 +00:00

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.