diff --git a/internal/check-hardcoded-english.mjs b/internal/check-hardcoded-english.mjs
index daf887e6e..0a1a6b7c5 100644
--- a/internal/check-hardcoded-english.mjs
+++ b/internal/check-hardcoded-english.mjs
@@ -351,6 +351,36 @@ function isDataComparison(node, parent) {
// resource not found means NOT FOUND in the pools checked" is a sentence that happens to be used as
// a key, and that is a real leak — gospel found twelve defects hiding in exactly that kind of
// already-classified bucket. Anything with sentence punctuation stays loud whatever its value is.
+// ★ REGION IDENTITIES, EXEMPT ONLY WHILE THEY ARE TRANSLATABLE (studio's judgement, R26).
+// legend-tree's REGION_OF maps a zone slug to a region NAME, and that name is an identity: node ids
+// derive from it, and every surface that shows one translates it at render through REGION_KEY —
+// upg-100-list.vue:440 `REGION_KEY[english] ? t(\`poster.${…}\`) : english`, poster-wall.vue:129 the
+// same. So the English never reaches a reader.
+//
+// ⚠ THE EXEMPTION IS CONDITIONAL, AND THAT IS THE WHOLE POINT. It holds only for a name that IS a
+// key of REGION_KEY. A new region added to REGION_OF without a REGION_KEY entry falls through that
+// `|| english` and renders English in all eleven locales — which is precisely the defect upg100
+// already shipped once with Americas and Europe. So the rule flags it, and the gate keeps working as
+// the table grows instead of blessing the file.
+//
+// Deliberately NOT "object values" and NOT "legend-tree.js": either would exempt the next table
+// nobody has checked. The membership test is what makes this safe to have at all.
+function regionKeyNames(code) {
+ // ⚠ NO NEWLINE REQUIREMENT. The first version anchored on `\n}` and so never matched a REGION_KEY
+ // written on ONE LINE — regionKeyNames returned null, the rule exited early, and BOTH two-sided
+ // cases passed without the exemption ever running. Found by poisoning the membership test and
+ // seeing nothing fail.
+ const m = /\bREGION_KEY\s*=\s*\{([\s\S]*?)\}/.exec(code)
+ if (!m) return null
+ return new Set([...m[1].matchAll(/'([^']+)'\s*:/g)].map((x) => x[1]))
+}
+function isTranslatedRegionIdentity(text, code) {
+ const names = regionKeyNames(code)
+ if (!names || !names.has(text)) return false
+ // it must actually be used as a region identity in this file, not merely share a spelling
+ return /const REGION_OF\s*=/.test(code) || /regionFor\s*=/.test(code)
+}
+
const DATA_MAP_MIN_ENTRIES = 6
function isPureDataMapKey(node, parent, grand) {
if (!parent || parent.type !== 'ObjectProperty' || parent.key !== node) return false
@@ -610,6 +640,8 @@ function scanScript(code, file, out, lineBase = 0) {
if (!text.length || !isProse(text)) return // gospel: empty matches were being reported
if (!/\s/.test(text)) return // ⚠ the single-word blind spot, documented above
if (allowedBy(text)) return
+ // A region identity translated at render — exempt only while REGION_KEY names it.
+ if (isTranslatedRegionIdentity(text, code)) return
out.push({ file, kind: 'script-string', text, line: lineOf(node, lineBase) })
return
}
@@ -977,6 +1009,17 @@ const RENDERED = { africa: 'A sentence the reader actually sees here', asia: 'An
// names, values that are prose a reader sees: the keys must STILL count, because this is a
// label table, not a join table. Without this case the allData guard can be removed and every
// test still passes — measured by poisoning it.
+ // ★ REGION IDENTITIES (studio, R26): exempt ONLY while REGION_KEY names them. A region added
+ // without a key falls through `|| english` and renders English in eleven locales — the defect
+ // upg100 already shipped once with Americas and Europe. The conditional IS the rule.
+ {
+ const KEYED = "const REGION_KEY={'Africa':'region_africa','Middle East':'region_middle_east','India':'region_india','Other':'region_other','China + Mongolia':'region_china_mongolia','Central Asia':'region_central_asia'}"
+ const mk = (of) => `{{ x }}
\n`
+ ok(!scanSfc(mk("{ 'west-africa': 'Africa', 'middle-east-core': 'Middle East' }"), 'rg.vue').some((f) => f.text === 'Africa'),
+ 'QUIET on a region identity that REGION_KEY names')
+ ok(scanSfc(mk("{ 'west-africa': 'Africa', 'new-place': 'Antarctica Region' }"), 'rb.vue').some((f) => f.text === 'Antarctica Region'),
+ 'COUNTS a region with NO REGION_KEY entry — it would render English through the fallback')
+ }
ok(scanSfc(`{{ x }}