diff --git a/plugin-designs/big-picture/big-picture.vue b/plugin-designs/big-picture/big-picture.vue index ef0c694a7..a5f68dfa7 100644 --- a/plugin-designs/big-picture/big-picture.vue +++ b/plugin-designs/big-picture/big-picture.vue @@ -70,7 +70,7 @@ const accent = computed(() => config.value.accent || '#8b5cf6') // `title` still wins — that is what the config key is for — but an absent one now resolves through // the catalogue rather than through a literal. The English wording is unchanged: this is the // shepherd's standing rule that translating EXISTING wording is not a product decision. -const heading = computed(() => config.value.title || tStatic('dashboard.bp_title') || 'Big Picture — Unified Research Dashboard') + // ★ THE LOCALE THE HOST ASKED FOR, AND THE ONE ELEMENT ON THE PAGE THAT CAN ACT ON IT. // MEASURED with check:host-locale-contract: this leaf drew 0 Arabic characters for // profile-config {"locale":"ar"} against 273 for the same page reached with ?lang=ar. Its siblings @@ -86,6 +86,17 @@ const hostLang = computed(() => { }) watchEffect(() => { if (hostLang.value) setRuntimeLocale(hostLang.value) }) +// ★ THE LOCALE IS PASSED, NOT ASSUMED, AND THAT IS WHAT KEEPS THIS LIVE. tStatic resolves through +// getRuntimeLocale() at CALL time, but a computed only calls again when a REACTIVE dependency changes — +// and config.value does not change when the host switches language. So this heading resolved once and +// stayed in the boot language while everything around it moved (upg100 measured it; check:frozen-labels +// catches the shape). hostLang IS reactive, so reading it both re-runs this computed and tells tStatic +// which catalogue to read. useI18n is not an option here: this bundle installs no i18n (index.js), which +// is why the whole element reads through tStatic in the first place. +const heading = computed(() => config.value.title + || tStatic('dashboard.bp_title', hostLang.value || undefined) + || 'Big Picture — Unified Research Dashboard') + // ★ RTL IS A DIRECTION, NOT A TRANSLATION — the half this element was still missing. // Its three sibling composites (dashboard-plus-grid, research-map-plus-grid) and the grid itself all // reflect `dir` onto their custom-element host; this one did not, so a host asking for Arabic got diff --git a/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue b/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue index dbb94b992..9a307e195 100644 --- a/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue +++ b/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue @@ -36,12 +36,14 @@ * mapped to this dashboard's facet keys). Nothing here is dashboard-specific beyond that map. */ import { inject, computed } from 'vue' +import { useI18n } from 'vue-i18n' import { tStatic } from '@map/i18n/index.js' import { useShadowStyles } from '@map/composables/useShadowStyles.js' import SearchFilterHeader from '@map/components/search/SearchFilterHeader.vue' import { getPrayerLevel, LABELS as PRAYER_LABELS } from '@map/colors/prayer-progress.js' import { useTreeLegendLabels } from '../../composables/useTreeLegendLabels.js' +const { locale } = useI18n() const bus = inject('selectionBus', null) const dash = inject('dashboardData', null) const cfg = inject('dashboardConfig', {}) @@ -205,7 +207,12 @@ const activeChips = computed(() => { // in code would get both wrong even with a translated word. It was simply never wired, so ten // locales rendered an English label beside localized facet chips. tStatic takes no params, so // {term} is filled here rather than by the catalogue. - if (term) out.push({ name: '__search__', label: (tStatic('dashboard.search_live') || 'Search: {term}').replace('{term}', term) }) + // ★ locale.value is READ so this computed re-runs on a language switch, and PASSED so tStatic reads + // the catalogue the reader just chose. Without it the chip resolved once and stayed in the boot + // language beside chips that had moved — a frozen label, which check:frozen-labels now catches. + // research-dashboard installs i18n (index.js:83), so useI18n resolves here; big-picture does not, + // which is why its sibling fix pins the locale from hostLang instead. + if (term) out.push({ name: '__search__', label: (tStatic('dashboard.search_live', locale.value) || 'Search: {term}').replace('{term}', term) }) return out })