upg-100 legend: the title was hardcoded English in every locale
Some checks are pending
build-gate / build (push) Waiting to run

MEASURED on the live Chinese render: the legend header read "My UPG 100 List —
Legend" in full English, inside an otherwise localized panel — beside translated
tabs (区域 · UPG-100 名单) and a translated column header (人口). It was a plain
string literal, and SemanticTreeLegend passes `title` through RAW, so the caller
owns localizing it.

The Driver's gift policy names this exactly: "BEAUTIFUL: no clipped text, no
placeholders, no English leaking." An English title on the plugin chosen as the
mobile showcase is that.

★ FOUND BY SEPARATING CHROME FROM DATA, which is the part worth keeping. A naive
Latin-script sweep of the Chinese render returned 31 hits and would have been a
wildly overstated report. Rendering the same elements in en and zh and keeping
only those that are IDENTICAL narrows it to 24, and those fall in three classes:
  · Mapbox attribution (© Mapbox, Improve this map) — third-party, a LICENCE
    obligation, must not be touched
  · zone and region names (Middle East Core, Sudan) — API DATA; translating
    place names is a domain decision, not a bug
  · the legend title and "UUPGs" — product UI strings, and the only real leaks
So 31 hits reduce to 2, one of which is already carded.

★ AND THE HOUSE STYLE ALREADY EXISTED, so no terminology was invented. This
catalog renders "Doxa Regions" as "Doxa 区域" / "مناطق Doxa" / "Регионы Doxa" —
the proper noun stays Latin, the frame translates. "UPG 100" is treated the same
way across all 11 locales, following a convention the catalog demonstrates
rather than coining acronyms (the trap I stopped at on the UUPGs header).

COMPUTED, not a constant: a locale change must move it. A const captured at
setup keeps the mounting language forever, which is the subtler half of the same
bug. That also forced the definition below useI18n(), since a computed
referencing t() above it throws at setup.

VERIFIED at the rendered layer: en "My UPG 100 List", es "Mi lista UPG 100",
zh "我的 UPG 100 名单", ar "قائمة UPG 100 الخاصة بي" (RTL), ru "Мой список UPG 100"
— 0 clipped in all five, including the longest Russian form.
check:legend-info-localized PASS, check:poster-card-fits PASS, SRI consistent.
This commit is contained in:
doxa-maps-88655c70 2026-09-25 03:43:52 +00:00
parent a5134c770b
commit 879f08ba08
17 changed files with 127 additions and 104 deletions

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "تقدم الصلاة",
"engagement": "تقدم الانخراط",
"adoption": "تقدم التبني",
"languageFamilies": "العائلات اللغوية",
"doxaRegions": "مناطق Doxa",
"affinityBlocs": "كتل القرابة",
"gospelResources": "موارد الإنجيل"
"doxaRegions": "مناطق Doxa",
"engagement": "تقدم الانخراط",
"gospelResources": "موارد الإنجيل",
"languageFamilies": "العائلات اللغوية",
"prayer": "تقدم الصلاة",
"upg100List": "قائمة UPG 100 الخاصة بي"
},
"header": {
"collapsed": "وسيلة الإيضاح",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Gebetsfortschritt",
"engagement": "Engagement-Fortschritt",
"adoption": "Adoptionsfortschritt",
"languageFamilies": "Sprachfamilien",
"doxaRegions": "Doxa-Regionen",
"affinityBlocs": "Affinitätsblöcke",
"gospelResources": "Evangeliumsressourcen"
"doxaRegions": "Doxa-Regionen",
"engagement": "Engagement-Fortschritt",
"gospelResources": "Evangeliumsressourcen",
"languageFamilies": "Sprachfamilien",
"prayer": "Gebetsfortschritt",
"upg100List": "Meine UPG-100-Liste"
},
"header": {
"collapsed": "Legende",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Prayer Progress",
"engagement": "Engagement Progress",
"adoption": "Adoption Progress",
"languageFamilies": "Language Families",
"doxaRegions": "Doxa Regions",
"affinityBlocs": "Affinity Blocks",
"gospelResources": "Gospel Resources"
"doxaRegions": "Doxa Regions",
"engagement": "Engagement Progress",
"gospelResources": "Gospel Resources",
"languageFamilies": "Language Families",
"prayer": "Prayer Progress",
"upg100List": "My UPG 100 List"
},
"header": {
"default": "Legend",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Progreso de oración",
"engagement": "Progreso de implicación",
"adoption": "Progreso de adopción",
"languageFamilies": "Familias lingüísticas",
"doxaRegions": "Regiones Doxa",
"affinityBlocs": "Bloques de afinidad",
"gospelResources": "Recursos del Evangelio"
"doxaRegions": "Regiones Doxa",
"engagement": "Progreso de implicación",
"gospelResources": "Recursos del Evangelio",
"languageFamilies": "Familias lingüísticas",
"prayer": "Progreso de oración",
"upg100List": "Mi lista UPG 100"
},
"header": {
"collapsed": "Leyenda",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Progression de la prière",
"engagement": "Progression de l'engagement",
"adoption": "Progression de l'adoption",
"languageFamilies": "Familles de langues",
"doxaRegions": "Régions Doxa",
"affinityBlocs": "Blocs d'affinité",
"gospelResources": "Ressources de l'Évangile"
"doxaRegions": "Régions Doxa",
"engagement": "Progression de l'engagement",
"gospelResources": "Ressources de l'Évangile",
"languageFamilies": "Familles de langues",
"prayer": "Progression de la prière",
"upg100List": "Ma liste UPG 100"
},
"header": {
"collapsed": "Légende",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "प्रार्थना प्रगति",
"engagement": "सहभागिता प्रगति",
"adoption": "गोद लेने की प्रगति",
"languageFamilies": "भाषा परिवार",
"doxaRegions": "Doxa क्षेत्र",
"affinityBlocs": "समानता समूह",
"gospelResources": "सुसमाचार संसाधन"
"doxaRegions": "Doxa क्षेत्र",
"engagement": "सहभागिता प्रगति",
"gospelResources": "सुसमाचार संसाधन",
"languageFamilies": "भाषा परिवार",
"prayer": "प्रार्थना प्रगति",
"upg100List": "मेरी UPG 100 सूची"
},
"header": {
"collapsed": "किंवदंती",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Avanzamento della preghiera",
"engagement": "Avanzamento dell'impegno",
"adoption": "Avanzamento dell'adozione",
"languageFamilies": "Famiglie linguistiche",
"doxaRegions": "Regioni Doxa",
"affinityBlocs": "Blocchi di affinità",
"gospelResources": "Risorse del Vangelo"
"doxaRegions": "Regioni Doxa",
"engagement": "Avanzamento dell'impegno",
"gospelResources": "Risorse del Vangelo",
"languageFamilies": "Famiglie linguistiche",
"prayer": "Avanzamento della preghiera",
"upg100List": "La mia lista UPG 100"
},
"header": {
"collapsed": "Legenda",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Progresso da oração",
"engagement": "Progresso do envolvimento",
"adoption": "Progresso da adoção",
"languageFamilies": "Famílias linguísticas",
"doxaRegions": "Regiões Doxa",
"affinityBlocs": "Blocos de afinidade",
"gospelResources": "Recursos do Evangelho"
"doxaRegions": "Regiões Doxa",
"engagement": "Progresso do envolvimento",
"gospelResources": "Recursos do Evangelho",
"languageFamilies": "Famílias linguísticas",
"prayer": "Progresso da oração",
"upg100List": "A minha lista UPG 100"
},
"header": {
"collapsed": "Legenda",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Progresul rugăciunii",
"engagement": "Progresul implicării",
"adoption": "Progresul adopției",
"languageFamilies": "Familii de limbi",
"doxaRegions": "Regiuni Doxa",
"affinityBlocs": "Blocuri de afinitate",
"gospelResources": "Resurse ale Evangheliei"
"doxaRegions": "Regiuni Doxa",
"engagement": "Progresul implicării",
"gospelResources": "Resurse ale Evangheliei",
"languageFamilies": "Familii de limbi",
"prayer": "Progresul rugăciunii",
"upg100List": "Lista mea UPG 100"
},
"header": {
"collapsed": "Legendă",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "Прогресс молитвы",
"engagement": "Прогресс вовлечённости",
"adoption": "Прогресс усыновления",
"languageFamilies": "Языковые семьи",
"doxaRegions": "Регионы Doxa",
"affinityBlocs": "Блоки родства",
"gospelResources": "Евангельские ресурсы"
"doxaRegions": "Регионы Doxa",
"engagement": "Прогресс вовлечённости",
"gospelResources": "Евангельские ресурсы",
"languageFamilies": "Языковые семьи",
"prayer": "Прогресс молитвы",
"upg100List": "Мой список UPG 100"
},
"header": {
"collapsed": "Условные обозначения",

View file

@ -1,12 +1,13 @@
{
"title": {
"prayer": "祷告进度",
"engagement": "参与进度",
"adoption": "认领进度",
"languageFamilies": "语系",
"doxaRegions": "Doxa 区域",
"affinityBlocs": "亲缘板块",
"gospelResources": "福音资源"
"doxaRegions": "Doxa 区域",
"engagement": "参与进度",
"gospelResources": "福音资源",
"languageFamilies": "语系",
"prayer": "祷告进度",
"upg100List": "我的 UPG 100 名单"
},
"header": {
"collapsed": "图例",

View file

@ -271,7 +271,6 @@ const geocoderRef = ref(null) // <GeocoderComponent> exposed ref
// generic list rather than this map's own — the drift the rebuild spec flagged.
// The legend names the MAP, not just the taxonomy — with several maps in one app
// "Legend" alone does not say which one you are looking at.
const LEGEND_TITLE = 'My UPG 100 List — Legend'
// ★ NAME THE LEGEND'S DEPTH TABS. Without a `tabs` prop SemanticTreeLegend falls through to
// `src[i] ?? { id: 'gen'+(i+1), label: 'Generation '+(i+1) }` (SemanticTreeLegend.vue:157) — a
@ -357,7 +356,20 @@ const API_BASE = (typeof window !== 'undefined' && window.MAP_APP_API_URL)
// shared usePosterPrints composable — it prints through a hidden same-page iframe,
// so the dialog opens in this tab rather than a popup a browser may block. The
// packet's locale follows the app's, so a reader gets it in their own language.
const { locale: packetLocale } = useI18n()
const { locale: packetLocale, t } = useI18n()
// ⚠ IT WAS A HARDCODED ENGLISH LITERAL, and SemanticTreeLegend passes `title` through RAW —
// the caller owns localizing it. MEASURED 2026-09-25 on the live Chinese render: the legend
// header read "My UPG 100 List — Legend" in full English inside an otherwise localized panel,
// beside translated tabs (区域 · UPG-100 名单) and a translated column header (人口).
// The Driver's gift policy is explicit — "BEAUTIFUL: no clipped text, no placeholders, no English
// leaking" — and an English title on the mobile showcase is exactly that.
// ★ THE HOUSE STYLE FOR A PROPER NOUN INSIDE A TRANSLATED PHRASE ALREADY EXISTS in this catalog:
// "Doxa Regions" renders as "Doxa 区域" / "مناطق Doxa" / "Регионы Doxa" — the identifier stays
// Latin, the frame translates. "UPG 100" is treated the same way rather than transliterated, so
// this follows a convention demonstrated across 11 locales instead of inventing terminology.
// COMPUTED, not a constant: a locale change must move it. A `const` captured at setup would keep
// the mounting language forever, which is the subtler half of the same bug.
const LEGEND_TITLE = computed(() => t('legend.title.upg100List'))
const DEAF_COUNT = 123
// Shadow-root CSS. (No backticks inside this string — a stray backtick closes the

View file

@ -681,7 +681,7 @@
"bundle": "my-upg-100-list",
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"output": "doxa-maps/my-upg-100-list/my-upg-100-list.js",
"integrity": "sha384-ep76LkuPyJeFgArqsscDmCVlXnx4efDXp7txdVkBRXp74sT89dr+Tgdv/HUKsI/F",
"integrity": "sha384-nzolEnrOfJrY1qd67PyWpwNB6JFgKTsmIaCpvpL+XAdfmuMYmNGf0NAlk/Gi79Ak",
"elements": [
"my-upg-100-list-map"
],
@ -701,7 +701,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ep76LkuPyJeFgArqsscDmCVlXnx4efDXp7txdVkBRXp74sT89dr+Tgdv/HUKsI/F\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-nzolEnrOfJrY1qd67PyWpwNB6JFgKTsmIaCpvpL+XAdfmuMYmNGf0NAlk/Gi79Ak\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -715,7 +715,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ep76LkuPyJeFgArqsscDmCVlXnx4efDXp7txdVkBRXp74sT89dr+Tgdv/HUKsI/F\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-nzolEnrOfJrY1qd67PyWpwNB6JFgKTsmIaCpvpL+XAdfmuMYmNGf0NAlk/Gi79Ak\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -729,7 +729,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ep76LkuPyJeFgArqsscDmCVlXnx4efDXp7txdVkBRXp74sT89dr+Tgdv/HUKsI/F\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-nzolEnrOfJrY1qd67PyWpwNB6JFgKTsmIaCpvpL+XAdfmuMYmNGf0NAlk/Gi79Ak\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
}
]

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-ep76LkuPyJeFgArqsscDmCVlXnx4efDXp7txdVkBRXp74sT89dr+Tgdv/HUKsI/F" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-nzolEnrOfJrY1qd67PyWpwNB6JFgKTsmIaCpvpL+XAdfmuMYmNGf0NAlk/Gi79Ak" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-ep76LkuPyJeFgArqsscDmCVlXnx4efDXp7txdVkBRXp74sT89dr+Tgdv/HUKsI/F" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-nzolEnrOfJrY1qd67PyWpwNB6JFgKTsmIaCpvpL+XAdfmuMYmNGf0NAlk/Gi79Ak" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-ep76LkuPyJeFgArqsscDmCVlXnx4efDXp7txdVkBRXp74sT89dr+Tgdv/HUKsI/F" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-nzolEnrOfJrY1qd67PyWpwNB6JFgKTsmIaCpvpL+XAdfmuMYmNGf0NAlk/Gi79Ak" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: