C4 wave 3b, cities-internet: three identical guards become one call

All three files carried the same block byte-for-byte — enConnectivity + enLookup +
fill + try/catch — and now call useOptionalT('connectivity', { fallback: enLookup }).
English is unchanged on a bare host, because each passes its own enLookup rather
than taking the catalogue default.

gospel-plugin chose the catalogue default for their bundle, on the argument that a
partner host without vue-i18n is served English while the bundle already ships all
11 catalogues. The same one-word choice is open to cities-internet's owner; it is
not a migration's to take, so this preserves what they have.

Their local fill() used `k in params`, so the shared fillParams also removes a way
for these three to render the literal string "null" into a sentence.

Also records, in the stripper itself, the two things codeOnly does NOT strip —
CSS comments inside a JS template literal, and HTML comments in a .vue template.
Both have already cost a real measurement here: a specificity read of (0,5,53),
and a bare-useI18n census that read 3 where the code had 2. gospel-plugin's
generalisation is the one worth keeping: any <!-- --> that quotes an identifier
will keep doing that to you. The stripper must match the LANGUAGE of the text.
This commit is contained in:
Doxa Maps 2026-09-26 18:29:32 +00:00
parent 9816a4244f
commit ef5ea1840a
5 changed files with 43 additions and 21 deletions

View file

@ -129,6 +129,12 @@ const SITES = [
// as a semantic conflict on main before it landed. The row asserts the '' explicitly so a later
// edit cannot quietly re-add a namespace.
['plugin-designs/gospel-resources/resources-map.vue', "useOptionalT(''", null],
// wave 3b — cities-internet. All three were byte-identical in the guard block; each keeps its own
// enLookup fallback, so a bare host renders the same English. Their local fill() used `k in params`,
// so the shared fillParams also removes a way for them to print the literal "null".
['plugin-designs/cities-internet/connectivity-list.vue', 'useOptionalT', 'fallback: enLookup'],
['plugin-designs/cities-internet/connectivity-map.vue', 'useOptionalT', 'fallback: enLookup'],
['plugin-designs/cities-internet/data-tables.vue', 'useOptionalT', 'fallback: enLookup'],
]
// ★ ONE FILE KEEPS TWO BARE useI18n CALLS ON PURPOSE, and the count is asserted rather than waived.
// resources-map.vue reads the LOCALE twice (`useI18n().locale.value`) to decide isEnglish and the

View file

@ -34,7 +34,7 @@
import { computed, inject, ref } from 'vue'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import data from './data/connectivity.json'
import { useI18n } from 'vue-i18n'
import { useOptionalT } from '@map/composables/useOptionalT.js'
import enConnectivity from '@map/i18n/locales/en/connectivity.json'
const profileConfig = inject('profileConfig', ref(null))
@ -44,12 +44,14 @@ const profileConfig = inject('profileConfig', ref(null))
// shipped catalog is the fallback, never a blank or a key. Region and country names are data.
const enLookup = (key) =>
String(key).replace(/^connectivity\./, '').split('.').reduce((o, k) => (o == null ? o : o[k]), enConnectivity)
const fill = (str, params) => (params ? String(str).replace(/\{(\w+)\}/g, (m, k) => (k in params ? params[k] : m)) : str)
let t = (key, params) => fill(enLookup(key) ?? String(key), params)
try {
const i18n = useI18n()
t = (key, params) => { const v = i18n.t(key, params || {}); return v === key ? fill(enLookup(key) ?? key, params) : v }
} catch (_) { /* no i18n on this host: the English catalog above is the fallback */ }
// C4 wave 3b: the guard is library/composables/useOptionalT.js now. enLookup STAYS as the fallback,
// passed in, so a bare host renders the same ENGLISH it always did. gospel-plugin chose the
// locale-honouring catalogue default for their bundle instead ("a partner host that never installed
// vue-i18n serves its readers English while the bundle already carries all 11 catalogues"); the same
// choice is open to cities-internet's owner and is one word to make — it is not a migration's to take.
// The local fill() is gone with the guard: fillParams applies the same interpolation, except it leaves
// a placeholder for a NULL value where this fill() rendered the string "null".
const t = useOptionalT('connectivity', { fallback: enLookup })
// NUMERALS ARE HELD for the Driver (dx-shepherd, 2026-09-26): a locale-following format would switch
// 'ar' to Arabic-Indic digits. Until that is decided, numbers keep the browser-default format they
// had before this pass; only the TEXT is translated.

View file

@ -33,7 +33,7 @@
* carrier flag is a NAME-PATTERN HEURISTIC, not a verified classification.
*/
import { computed, inject, ref, shallowRef, markRaw, onMounted, onBeforeUnmount } from 'vue'
import { useI18n } from 'vue-i18n'
import { useOptionalT } from '@map/composables/useOptionalT.js'
import enConnectivity from '@map/i18n/locales/en/connectivity.json'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { addMapAttribution } from '@map/composables/useMapAttribution'
@ -208,12 +208,14 @@ function paintGroups(map) {
// Carrier and platform names are data and are not translated.
const enLookup = (key) =>
String(key).replace(/^connectivity\./, '').split('.').reduce((o, k) => (o == null ? o : o[k]), enConnectivity)
const fill = (str, params) => (params ? String(str).replace(/\{(\w+)\}/g, (m, k) => (k in params ? params[k] : m)) : str)
let t = (key, params) => fill(enLookup(key) ?? String(key), params)
try {
const i18n = useI18n()
t = (key, params) => { const v = i18n.t(key, params || {}); return v === key ? fill(enLookup(key) ?? key, params) : v }
} catch (_) { /* no i18n on this host: the English catalog above is the fallback */ }
// C4 wave 3b: the guard is library/composables/useOptionalT.js now. enLookup STAYS as the fallback,
// passed in, so a bare host renders the same ENGLISH it always did. gospel-plugin chose the
// locale-honouring catalogue default for their bundle instead ("a partner host that never installed
// vue-i18n serves its readers English while the bundle already carries all 11 catalogues"); the same
// choice is open to cities-internet's owner and is one word to make — it is not a migration's to take.
// The local fill() is gone with the guard: fillParams applies the same interpolation, except it leaves
// a placeholder for a NULL value where this fill() rendered the string "null".
const t = useOptionalT('connectivity', { fallback: enLookup })
// Bands, not a continuous ramp: a reader can hold four categories, not a gradient.
const BANDS = [

View file

@ -25,7 +25,7 @@ import { computed, inject, ref, watch } from 'vue'
import DataTable from '@map/components/tables/DataTable.vue'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { resolveAssetsBase } from '@map/utils/embedBase.js'
import { useI18n } from 'vue-i18n'
import { useOptionalT } from '@map/composables/useOptionalT.js'
import enConnectivity from '@map/i18n/locales/en/connectivity.json'
@ -38,12 +38,14 @@ const assetsBase = computed(() => resolveAssetsBase(profileConfig.value))
// shipped catalog is the fallback, never a blank or a key. Region and country names are data.
const enLookup = (key) =>
String(key).replace(/^connectivity\./, '').split('.').reduce((o, k) => (o == null ? o : o[k]), enConnectivity)
const fill = (str, params) => (params ? String(str).replace(/\{(\w+)\}/g, (m, k) => (k in params ? params[k] : m)) : str)
let t = (key, params) => fill(enLookup(key) ?? String(key), params)
try {
const i18n = useI18n()
t = (key, params) => { const v = i18n.t(key, params || {}); return v === key ? fill(enLookup(key) ?? key, params) : v }
} catch (_) { /* no i18n on this host: the English catalog above is the fallback */ }
// C4 wave 3b: the guard is library/composables/useOptionalT.js now. enLookup STAYS as the fallback,
// passed in, so a bare host renders the same ENGLISH it always did. gospel-plugin chose the
// locale-honouring catalogue default for their bundle instead ("a partner host that never installed
// vue-i18n serves its readers English while the bundle already carries all 11 catalogues"); the same
// choice is open to cities-internet's owner and is one word to make — it is not a migration's to take.
// The local fill() is gone with the guard: fillParams applies the same interpolation, except it leaves
// a placeholder for a NULL value where this fill() rendered the string "null".
const t = useOptionalT('connectivity', { fallback: enLookup })
// NUMERALS ARE HELD for the Driver (dx-shepherd, 2026-09-26): a locale-following format would switch
// 'ar' to Arabic-Indic digits. Until that is decided, numbers keep the browser-default format they
// had before this pass; only the TEXT is translated.

View file

@ -21,6 +21,16 @@
//
// Comments are replaced by SPACES, preserving length and line structure, so line numbers reported
// against the result still point at the real file.
// ★ WHAT THIS DOES NOT STRIP, and both gaps have already cost a real measurement:
// - CSS comments inside a JS TEMPLATE LITERAL. A string is copied through verbatim, correctly for
// JS, but useShadowStyles(`...`) contains CSS whose /* */ the BROWSER honours. Reading specificity
// off such a file once gave (0,5,53) because a comment landed inside the selector.
// - HTML comments in a .vue TEMPLATE. `<!-- ... -->` is invisible here, so any identifier quoted in
// one is counted as code. That made a bare-useI18n census read 3 where the code had 2.
// gospel-plugin's generalisation, worth keeping: "any <!-- --> in a template that quotes an identifier
// will keep doing that to you". THE RULE: the stripper must match the LANGUAGE of the text being read.
// For a .vue file, strip HTML comments first, then call this for the script block.
export function codeOnly(src) {
let out = ''
let i = 0