From 50b9f9a8a27e93db9899d520ac4b73e1a234a98f Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 04:49:03 +0000 Subject: [PATCH] Two gates:post reds from this landing's own commits, fixed at the source check:optional-t-equivalence evaluates useOptionalT.js on its own, with its imports stripped and tStatic and useI18n handed in. The composable now also imports getRuntimeLocale and cldrChoiceIndex, so every section died with a ReferenceError. The gate now hands in a bare host's getRuntimeLocale and the shipped cldrChoiceIndex, lifted from library/i18n/index.js's own text; its import control accepts tStatic among several names, but still only from '@map/i18n/index.js' (a catalogue import of the module's own still fails). check:frozen-labels caught the research filters' new search placeholder: tStatic inside a computed that reads no locale never re-runs, so the placeholder stayed in the boot language. It now reads and passes locale.value, like the search chip in the same file. --- internal/check-optional-t-equivalence.mjs | 16 +++++++++++++--- .../dashboards/DashboardResearchFilters.vue | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/check-optional-t-equivalence.mjs b/internal/check-optional-t-equivalence.mjs index 41aeb061a..03bf44e6f 100644 --- a/internal/check-optional-t-equivalence.mjs +++ b/internal/check-optional-t-equivalence.mjs @@ -28,8 +28,18 @@ const body = src .replace(/^import .*$/gm, '') // drop the two un-runnable imports .replace(/^export default .*$/gm, '') // and the default re-export (it is not a declaration) .replace(/^export /gm, '') -const make = (tStatic, useI18n) => new Function('tStatic', 'useI18n', - body + '\nreturn { useOptionalT, useOptionalI18n, fillParams, qualify };')(tStatic, useI18n) +// The composable also imports getRuntimeLocale and cldrChoiceIndex from library/i18n/index.js, which +// cannot be imported here (vue-i18n and the @map alias). cldrChoiceIndex is lifted from that file's +// own text, so the plural rule under test is the shipped one; getRuntimeLocale is a bare host's: no +// locale set, which is the English path every section below exercises. +const i18nSrc = readFileSync(new URL('../library/i18n/index.js', import.meta.url), 'utf8') +const lift = (re, what) => { const m = i18nSrc.match(re); if (!m) throw new Error(`library/i18n/index.js no longer defines ${what}`); return m[0] } +const cldrChoiceIndex = new Function( + lift(/^const CLDR_CATEGORY_ORDER = .*$/m, 'CLDR_CATEGORY_ORDER') + '\n' + + lift(/^export function cldrChoiceIndex\([\s\S]*?\n\}$/m, 'cldrChoiceIndex').replace(/^export /, '') + + '\nreturn cldrChoiceIndex;')() +const make = (tStatic, useI18n, getRuntimeLocale = () => '') => new Function('tStatic', 'useI18n', 'getRuntimeLocale', 'cldrChoiceIndex', + body + '\nreturn { useOptionalT, useOptionalI18n, fillParams, qualify };')(tStatic, useI18n, getRuntimeLocale, cldrChoiceIndex) // The English catalogue the old code imported, as a stub tree: messages.en.pplr equivalent. const EN = { greeting: 'Hello {name}', nested: { deep: 'Deep value' } } @@ -89,7 +99,7 @@ const hostI18n = (table) => () => ({ t: (k) => (k in table ? table[k] : k) }) const { useOptionalT: u2 } = make(tStatic, hostI18n({ 'pplr.greeting': 'Bonjour' })) ok(u2('pplr')('greeting') !== 'Hello Ada', 'NEG CONTROL: the host path and the catalogue path are distinguishable') } -ok(/import \{ tStatic \}/.test(src), 'CONTROL: the shipped module really does route through tStatic (not a catalogue import of its own)') +ok(/import \{[^}]*\btStatic\b[^}]*\} from '@map\/i18n\/index\.js'/.test(src), 'CONTROL: the shipped module really does route through tStatic (not a catalogue import of its own)') // ★ codeOnly, because this assertion failed on its OWN DOCUMENTATION: useOptionalT.js's header QUOTES // '@map/i18n/locales/en/pplr.json' as the import it removed, and a whole-file grep counted the prose as // the thing it forbids. Fifth time this session that a detector has read a comment as code. diff --git a/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue b/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue index 18f8f854f..fc4be32a5 100644 --- a/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue +++ b/plugin-designs/research-dashboard/src/components/dashboards/DashboardResearchFilters.vue @@ -111,7 +111,9 @@ const dropdownKeys = computed(() => { const configured = rf.value.dropdowns || ['wagf_region', 'wagf_block', 'country_code', 'people_group', 'religion'] return [PRAYER_KEY, ...configured.filter((k) => k !== PRAYER_KEY)] }) -const searchPlaceholder = computed(() => rf.value.searchPlaceholder || tStatic('dashboard.search_placeholder') || 'Search people groups, place, language, religion…') +// locale.value is read and passed for the same reason as the search chip below: without it this +// computed never re-runs, and the placeholder stays in the boot language after a switch. +const searchPlaceholder = computed(() => rf.value.searchPlaceholder || tStatic('dashboard.search_placeholder', locale.value) || 'Search people groups, place, language, religion…') // Apply a filter-set (facetKey -> label) to a row list — reused for cross-filtered counts. function applyWith(fset, list) {