Eighteen keys across eleven locales for text that was written in English in the components: the rail and filter toggles, the selection count, the CSV export toast, the drawer's prayer and information links, the dashboard title, the map's failure message, and the four search placeholders that name the tiers of whichever tab is open. The export toast is interpolated rather than concatenated. Its counts keep going through fmt(), which groups digits the way each locale does, and only the words around them come from the catalogue — the order of "rows", "fields" and the outcome is not the same in every language, so a sentence built by joining fragments cannot be translated at all. It uses the shared fillParams, which leaves a placeholder visible when a value is missing instead of rendering the word "null". The map's failure message no longer names the token and the container: that is for the console line, which stays English for whoever is debugging. What the reader sees says what to do. Three more shapes the scan could not see, each of them correct code: tStatic called directly rather than through a wrapper; `config || t(key) || English`, where the translator is not the immediate left operand because || is left-associative; and the licence credits, media queries and product name that must not be translated at all. Own leaks 91 -> 1, the last being a tab label behind a key that resolves in all eleven locales. Fleet 249 -> 159. check:i18n-parity, never-translated, treeshake and stopped-scope all pass. |
||
|---|---|---|
| .. | ||
| api | ||
| charts/whiteboard | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| dashboards | ||
| data | ||
| elements | ||
| i18n | ||
| layouts | ||
| lists | ||
| maps | ||
| packet | ||
| packets | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| vendor/cldr | ||
| 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.