useDashboardData read `lang` straight out of sources.json, where it is frozen at "en", and nothing ever overrode it. So every row the dashboard fetched came back English regardless of the reader's locale. The result was not a uniformly English page, which would at least be honest. It was worse. DashboardChartsRail localizes its country labels through countryName(), so at ?lang=ar the top-countries chart read الصين while the table row directly beneath it read "People's Republic of China" — and a page that is half translated looks like a bug in the half that works. Three surfaces rendered the raw English label: the data table's country column (dashboard.config.js:299), the detail drawer (DashboardDetailDrawer.vue:171) and the search-result subtitle (DashboardTopBar.vue:327). Fixing it at the SOURCE rather than at the three render sites is what makes them agree: ask the API in the reader's language and the labels arrive correct, with no countryName() call and no third spelling of a country's name to reconcile. DataSourceManager.js:261 has done exactly this for its own callers all along — `if (this.locale) extraParams.lang = this.locale` — and doxa-research-map, doxa-simple-map and country-posters all pass a locale in. This composable simply never got the same treatment. BOTH LOCALE CHANNELS, because it genuinely arrives by either: `lang` is provided by ProfileLoader for an element configured through profile-config, and window[PAGE_LOCALE_KEY] is what a generated leaf sets from ?lang= — the URL a person actually shares. Reading only one left the regions dashboard's data labels English on the shared-link path while its chrome translated, which is the measurement this copies rather than rediscovers. Resolved at REQUEST time, not captured at setup, so a locale switch cannot pin the dashboard to whichever language happened to load first. AND A LOCALE CHANGE NOW REFETCHES. load() ran only onMounted, so switching language mid-session would have re-rendered chrome over data still in the old language — the same defect one layer up. Same shape as region-dashboards.vue:680, guarded on a real change so the mount fetch is not doubled. Caught while writing it: my first version of that watch called inject() inside the callback, where it does not work — it warns on every tick and returns null. The ref is captured in setup and only read later, which is what this file already does for profileConfig, for the same reason. Proved headless: locale resolution across all four channels (leaf ?lang=, injected profile-config, neither, and injected-beats-runtime), then that the chart, the table column, the drawer and the search subtitle produce the SAME string for CHN, LAO and COD at ar — the disagreement that started this. The API's ?lang= behaviour itself is not my assumption: it was measured live at ar and zh on region-dashboards (its comment at :366). |
||
|---|---|---|
| .. | ||
| api | ||
| charts | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| 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.