Name the rings on the packet's centered set
The figure printed rings without saying what they mean -- honest but thin.
It now names each one with the vocabulary gospel and exp settled, the same
words the screen legend renders, so a printed sheet cannot disagree with
the screen.
COMPOSED FROM band x culture, NOT A FLAT 1..10 TABLE, and that is the
whole design decision. "Same people" in column 1 is provisional on the
Driver's row 89 -- and it appears in rings 1, 4 AND 7. A ten-string table
would need three edits kept in step, which is exactly how two of them end
up saying different things. Composed, it is one string
(ring_culture_same), and if he picks "culture" the ring numbering, the
columns and the colours are all untouched.
Ring 10 stands alone ("Nearby only") because it is not a band-and-culture
reading at all -- it is what is left when no linguistic relation was
found. The gate asserts that separately, and a mutant that composes it
like the other nine fails.
ONLY THE RINGS THAT HOLD SOMEONE. Ten rows of vocabulary beside a figure
is a glossary, not a legend, and a named ring with no dot on it invites
the reader to hunt for something that is not there. ringTally's per-ring
counts drive the list, so it states what this zone actually contains.
Mutation-tested: a wrong band order, listing all ten rings regardless of
count, and composing ring 10 like the others each fail with their own
message. Eight new strings x eleven locales; packet-strings-parity green.
This commit is contained in:
parent
3c1d513cc3
commit
fccf6ee2fe
3 changed files with 171 additions and 11 deletions
|
|
@ -102,6 +102,32 @@ ok(count(full, /class="sheet/g) - count(light, /class="sheet/g) === 3,
|
|||
ok(/10/.test(caps.join(' ')), 'the bounded tally does not state its unknown count — an un-inked category reads as zero in print')
|
||||
}
|
||||
|
||||
// ── THE RING LEGEND: THE AGREED VOCABULARY, AND ONLY THE RINGS THAT HOLD SOMEONE ────────────────
|
||||
// ★ The names are COMPOSED from band x culture rather than a flat 1..10 table, because "same
|
||||
// people" (provisional on the Driver's row 89) appears in rings 1, 4 AND 7 — three cells that a
|
||||
// flat table would have to keep in step. This asserts the composition produces the vocabulary
|
||||
// gospel and exp settled, so a printed sheet cannot disagree with the screen legend.
|
||||
{
|
||||
const only = (rings) => {
|
||||
const html = R.buildPacket(zone, groups, new Map(), '<svg/>', funnel, christian, 'full',
|
||||
{ bounded: [{ id: 'x', reached: true }], centered: rings.map((r, i) => ({ id: `r${i}`, ring: r })) })
|
||||
return (html.match(/<li><b>[^<]*<\/b>([^<]*)<\/li>/g) || []).map((s) => s.replace(/<[^>]+>/g, '').trim())
|
||||
}
|
||||
const all = only([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
ok(all.length === 10, `all ten rings populated should list ten names, got ${all.length}`)
|
||||
const want = ['Same dialect, same people', 'Same dialect, near culture', 'Same dialect, different culture',
|
||||
'Same language, same people', 'Same language, near culture', 'Same language, different culture',
|
||||
'Same family, same people', 'Same family, near culture', 'Same family, different culture', 'Nearby only']
|
||||
want.forEach((w, i) => ok((all[i] || '').endsWith(w),
|
||||
`ring ${i + 1} should read ${JSON.stringify(w)}, got ${JSON.stringify(all[i])}`))
|
||||
// ⚠ ONLY THE POPULATED RINGS. Ten rows of vocabulary beside a figure is a glossary, not a legend,
|
||||
// and a named ring with no dot invites the reader to hunt for something that is not there.
|
||||
const sparse = only([1, 1, 3, 7, 7, 7])
|
||||
ok(sparse.length === 3, `only the populated rings should be listed, got ${sparse.length}: ${JSON.stringify(sparse)}`)
|
||||
ok(/^2 /.test(sparse[0]) && /^1 /.test(sparse[1]) && /^3 /.test(sparse[2]),
|
||||
`the legend should carry each ring's own count, got ${JSON.stringify(sparse)}`)
|
||||
}
|
||||
|
||||
// An unknown variant must print the FULL packet. A typo that printed a SHORTER packet would look
|
||||
// like a successful print.
|
||||
ok(build('nonsense-typo') === full, 'an unknown variant did not fall back to the full packet')
|
||||
|
|
|
|||
|
|
@ -207,6 +207,11 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
.set-unk{fill:#fff;stroke:#b9b2a0;stroke-width:1.5}
|
||||
.set-centre{fill:#2f6d4f}
|
||||
.set-cap{margin:10px 0 0;font:400 15px Georgia,serif;color:#413d35}
|
||||
/* The ring legend: only the rings this zone actually fills, in ring order. Left-aligned inside
|
||||
the centred column because a ragged-right list of names reads far better than centred prose. */
|
||||
.set-rings{margin:10px 0 0;padding:0;list-style:none;text-align:start;
|
||||
font:400 13px Georgia,serif;color:#6f6a5c;line-height:1.5}
|
||||
.set-rings b{color:#1b1a17;font-weight:700}
|
||||
|
||||
/* ── THE THREE PRAYER COLUMNS, now a HORIZONTAL strip at the end of each row ──────────
|
||||
They were a vertical stack in the old 683x160 card. In a ${CW}x${CH.toFixed(0)} ROW there is no
|
||||
|
|
@ -820,6 +825,46 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
// it. If the figure does not fit, SHRINK THE WHOLE FIGURE — never compress the gap. Measured on
|
||||
// the real module with 40 groups: inside max 85.3 of 92 allowed, outside min 119.8 of 112
|
||||
// required. The scale below is applied to the viewBox, so it cannot touch the gap.
|
||||
// ★ THE AGREED RING VOCABULARY, COMPOSED — not a flat 1..10 table. Settled between gospel and exp
|
||||
// and derived from exp's ringFor(), so the numbering cannot drift from the code that produces it;
|
||||
// the screen legend renders the same words, so a printed sheet cannot disagree with the screen.
|
||||
// 1 same dialect, same people 6 same language, different culture
|
||||
// 2 same dialect, near culture 7 same family, same people
|
||||
// 3 same dialect, different culture 8 same family, near culture
|
||||
// 4 same language, same people 9 same family, different culture
|
||||
// 5 same language, near culture 10 nearby only
|
||||
// ⚠ COMPOSED FROM band x culture BECAUSE ONE CELL IS PROVISIONAL. "same people" vs the brief's
|
||||
// "same culture" in column 1 is open on the Driver's row 89, and it appears in rings 1, 4 AND 7 —
|
||||
// so a flat ten-string table would need THREE edits kept in step, which is exactly how two of them
|
||||
// end up saying different things. Composed, it is ONE string (ring_culture_same) and the ring
|
||||
// numbering, the columns and the colours are untouched either way.
|
||||
const BANDS = ['dialect', 'dialect', 'dialect', 'language', 'language', 'language', 'family', 'family', 'family'];
|
||||
const CULTURES = ['same', 'near', 'different', 'same', 'near', 'different', 'same', 'near', 'different'];
|
||||
function ringName(n) {
|
||||
if (n === RING_MAX) return t('ring_band_geography'); // 10 stands alone
|
||||
const i = n - 1;
|
||||
if (i < 0 || i >= BANDS.length) return '';
|
||||
return fill(t('ring_join'), { band: t(`ring_band_${BANDS[i]}`), culture: t(`ring_culture_${CULTURES[i]}`) });
|
||||
}
|
||||
|
||||
// ONLY THE RINGS THAT HOLD SOMEONE. Ten rows of vocabulary beside a figure is a glossary, not a
|
||||
// legend, and nine of the ten are routinely empty for one zone. Printing a named ring with no
|
||||
// group on it also invites the reader to look for a dot that is not there. `ringTally` gives the
|
||||
// per-ring counts, so the legend states what this zone actually contains, in ring order.
|
||||
function ringLegend(tally) {
|
||||
const counts = tally && tally.counts;
|
||||
if (!counts || typeof counts.get !== 'function') return '';
|
||||
const rows = [];
|
||||
for (let n = 1; n <= RING_MAX; n++) {
|
||||
const c = Number(counts.get(n) || 0);
|
||||
if (!c) continue;
|
||||
const name = ringName(n);
|
||||
if (!name) continue;
|
||||
rows.push(`<li><b>${num(c)}</b> ${esc(name)}</li>`);
|
||||
}
|
||||
return rows.length ? `<ul class="set-rings">${rows.join('')}</ul>` : '';
|
||||
}
|
||||
|
||||
function setsPage(zone, sets) {
|
||||
// '' WITHOUT DATA, exactly like funnelPage and christianPage — a page that cannot state true
|
||||
// figures must not appear at all. This is what keeps FULL valid before a host supplies sets.
|
||||
|
|
@ -871,6 +916,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
</svg>
|
||||
<p class="set-cap">${esc(fill(t('sets_centered_tally'),
|
||||
{ placed: num(T.placed), centre: num(T.centre), unplaced: num(T.unplaced) }))}</p>
|
||||
${ringLegend(T)}
|
||||
</div>`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,15 @@
|
|||
"sets_bounded_title": "Reached, and not",
|
||||
"sets_centered_title": "Distance from a neighbour",
|
||||
"sets_bounded_tally": "{inside} inside · {outside} outside · {unknown} unknown",
|
||||
"sets_centered_tally": "{placed} placed · {centre} at the centre · {unplaced} unplaced"
|
||||
"sets_centered_tally": "{placed} placed · {centre} at the centre · {unplaced} unplaced",
|
||||
"ring_band_dialect": "Same dialect",
|
||||
"ring_band_language": "Same language",
|
||||
"ring_band_family": "Same family",
|
||||
"ring_band_geography": "Nearby only",
|
||||
"ring_culture_same": "same people",
|
||||
"ring_culture_near": "near culture",
|
||||
"ring_culture_different": "different culture",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"es": {
|
||||
"map_page_kicker": "GRUPOS ÉTNICOS NO EVANGELIZADOS",
|
||||
|
|
@ -431,7 +439,15 @@
|
|||
"sets_bounded_title": "Alcanzados y no alcanzados",
|
||||
"sets_centered_title": "Distancia a un vecino",
|
||||
"sets_bounded_tally": "{inside} dentro · {outside} fuera · {unknown} desconocido",
|
||||
"sets_centered_tally": "{placed} ubicados · {centre} en el centro · {unplaced} sin ubicar"
|
||||
"sets_centered_tally": "{placed} ubicados · {centre} en el centro · {unplaced} sin ubicar",
|
||||
"ring_band_dialect": "Mismo dialecto",
|
||||
"ring_band_language": "Misma lengua",
|
||||
"ring_band_family": "Misma familia",
|
||||
"ring_band_geography": "Solo cercanía",
|
||||
"ring_culture_same": "mismo pueblo",
|
||||
"ring_culture_near": "cultura cercana",
|
||||
"ring_culture_different": "cultura distinta",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"fr": {
|
||||
"map_page_kicker": "GROUPES ETHNIQUES NON ATTEINTS",
|
||||
|
|
@ -644,7 +660,15 @@
|
|||
"sets_bounded_title": "Atteints, ou non",
|
||||
"sets_centered_title": "Distance d’un voisin",
|
||||
"sets_bounded_tally": "{inside} à l’intérieur · {outside} à l’extérieur · {unknown} inconnu",
|
||||
"sets_centered_tally": "{placed} placés · {centre} au centre · {unplaced} non placés"
|
||||
"sets_centered_tally": "{placed} placés · {centre} au centre · {unplaced} non placés",
|
||||
"ring_band_dialect": "Même dialecte",
|
||||
"ring_band_language": "Même langue",
|
||||
"ring_band_family": "Même famille",
|
||||
"ring_band_geography": "Proximité seule",
|
||||
"ring_culture_same": "même peuple",
|
||||
"ring_culture_near": "culture proche",
|
||||
"ring_culture_different": "culture différente",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"pt": {
|
||||
"map_page_kicker": "GRUPOS ÉTNICOS NÃO ALCANÇADOS",
|
||||
|
|
@ -855,7 +879,15 @@
|
|||
"sets_bounded_title": "Alcançados e não alcançados",
|
||||
"sets_centered_title": "Distância de um vizinho",
|
||||
"sets_bounded_tally": "{inside} dentro · {outside} fora · {unknown} desconhecido",
|
||||
"sets_centered_tally": "{placed} posicionados · {centre} no centro · {unplaced} sem posição"
|
||||
"sets_centered_tally": "{placed} posicionados · {centre} no centro · {unplaced} sem posição",
|
||||
"ring_band_dialect": "Mesmo dialeto",
|
||||
"ring_band_language": "Mesma língua",
|
||||
"ring_band_family": "Mesma família",
|
||||
"ring_band_geography": "Apenas proximidade",
|
||||
"ring_culture_same": "mesmo povo",
|
||||
"ring_culture_near": "cultura próxima",
|
||||
"ring_culture_different": "cultura diferente",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"de": {
|
||||
"map_page_kicker": "UNERREICHTE VOLKSGRUPPEN",
|
||||
|
|
@ -1068,7 +1100,15 @@
|
|||
"sets_bounded_title": "Erreicht und nicht erreicht",
|
||||
"sets_centered_title": "Abstand zu einem Nachbarn",
|
||||
"sets_bounded_tally": "{inside} innen · {outside} außen · {unknown} unbekannt",
|
||||
"sets_centered_tally": "{placed} platziert · {centre} in der Mitte · {unplaced} ohne Platz"
|
||||
"sets_centered_tally": "{placed} platziert · {centre} in der Mitte · {unplaced} ohne Platz",
|
||||
"ring_band_dialect": "Gleicher Dialekt",
|
||||
"ring_band_language": "Gleiche Sprache",
|
||||
"ring_band_family": "Gleiche Sprachfamilie",
|
||||
"ring_band_geography": "Nur benachbart",
|
||||
"ring_culture_same": "gleiches Volk",
|
||||
"ring_culture_near": "verwandte Kultur",
|
||||
"ring_culture_different": "andere Kultur",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"it": {
|
||||
"map_page_kicker": "GRUPPI ETNICI NON RAGGIUNTI",
|
||||
|
|
@ -1279,7 +1319,15 @@
|
|||
"sets_bounded_title": "Raggiunti e non raggiunti",
|
||||
"sets_centered_title": "Distanza da un vicino",
|
||||
"sets_bounded_tally": "{inside} dentro · {outside} fuori · {unknown} sconosciuto",
|
||||
"sets_centered_tally": "{placed} collocati · {centre} al centro · {unplaced} non collocati"
|
||||
"sets_centered_tally": "{placed} collocati · {centre} al centro · {unplaced} non collocati",
|
||||
"ring_band_dialect": "Stesso dialetto",
|
||||
"ring_band_language": "Stessa lingua",
|
||||
"ring_band_family": "Stessa famiglia",
|
||||
"ring_band_geography": "Solo vicinanza",
|
||||
"ring_culture_same": "stesso popolo",
|
||||
"ring_culture_near": "cultura vicina",
|
||||
"ring_culture_different": "cultura diversa",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"ro": {
|
||||
"map_page_kicker": "GRUPURI ETNICE NEATINSE",
|
||||
|
|
@ -1490,7 +1538,15 @@
|
|||
"sets_bounded_title": "Atinse și neatinse",
|
||||
"sets_centered_title": "Distanța față de un vecin",
|
||||
"sets_bounded_tally": "{inside} înăuntru · {outside} în afară · {unknown} necunoscut",
|
||||
"sets_centered_tally": "{placed} plasate · {centre} în centru · {unplaced} neplasate"
|
||||
"sets_centered_tally": "{placed} plasate · {centre} în centru · {unplaced} neplasate",
|
||||
"ring_band_dialect": "Același dialect",
|
||||
"ring_band_language": "Aceeași limbă",
|
||||
"ring_band_family": "Aceeași familie",
|
||||
"ring_band_geography": "Doar în apropiere",
|
||||
"ring_culture_same": "același popor",
|
||||
"ring_culture_near": "cultură apropiată",
|
||||
"ring_culture_different": "cultură diferită",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"ru": {
|
||||
"map_page_kicker": "НЕОХВАЧЕННЫЕ НАРОДЫ",
|
||||
|
|
@ -1703,7 +1759,15 @@
|
|||
"sets_bounded_title": "Достигнутые и нет",
|
||||
"sets_centered_title": "Расстояние до соседа",
|
||||
"sets_bounded_tally": "{inside} внутри · {outside} снаружи · {unknown} неизвестно",
|
||||
"sets_centered_tally": "{placed} размещено · {centre} в центре · {unplaced} без места"
|
||||
"sets_centered_tally": "{placed} размещено · {centre} в центре · {unplaced} без места",
|
||||
"ring_band_dialect": "Тот же диалект",
|
||||
"ring_band_language": "Тот же язык",
|
||||
"ring_band_family": "Та же семья",
|
||||
"ring_band_geography": "Только соседство",
|
||||
"ring_culture_same": "тот же народ",
|
||||
"ring_culture_near": "близкая культура",
|
||||
"ring_culture_different": "другая культура",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"ar": {
|
||||
"map_page_kicker": "شعوب غير مبلَّغة",
|
||||
|
|
@ -1916,7 +1980,15 @@
|
|||
"sets_bounded_title": "وصلتهم البشارة ومن لم تصلهم",
|
||||
"sets_centered_title": "المسافة إلى جار",
|
||||
"sets_bounded_tally": "{inside} في الداخل · {outside} في الخارج · {unknown} غير معروف",
|
||||
"sets_centered_tally": "{placed} موضوعون · {centre} في المركز · {unplaced} بلا موضع"
|
||||
"sets_centered_tally": "{placed} موضوعون · {centre} في المركز · {unplaced} بلا موضع",
|
||||
"ring_band_dialect": "اللهجة نفسها",
|
||||
"ring_band_language": "اللغة نفسها",
|
||||
"ring_band_family": "العائلة اللغوية نفسها",
|
||||
"ring_band_geography": "الجوار فقط",
|
||||
"ring_culture_same": "الشعب نفسه",
|
||||
"ring_culture_near": "ثقافة قريبة",
|
||||
"ring_culture_different": "ثقافة مختلفة",
|
||||
"ring_join": "{band}، {culture}"
|
||||
},
|
||||
"hi": {
|
||||
"map_page_kicker": "अनछुए जनसमूह",
|
||||
|
|
@ -2127,7 +2199,15 @@
|
|||
"sets_bounded_title": "पहुँचे और नहीं पहुँचे",
|
||||
"sets_centered_title": "पड़ोसी से दूरी",
|
||||
"sets_bounded_tally": "{inside} भीतर · {outside} बाहर · {unknown} अज्ञात",
|
||||
"sets_centered_tally": "{placed} स्थित · {centre} केंद्र में · {unplaced} अस्थित"
|
||||
"sets_centered_tally": "{placed} स्थित · {centre} केंद्र में · {unplaced} अस्थित",
|
||||
"ring_band_dialect": "वही बोली",
|
||||
"ring_band_language": "वही भाषा",
|
||||
"ring_band_family": "वही भाषा-परिवार",
|
||||
"ring_band_geography": "केवल निकटता",
|
||||
"ring_culture_same": "वही जन",
|
||||
"ring_culture_near": "निकट संस्कृति",
|
||||
"ring_culture_different": "भिन्न संस्कृति",
|
||||
"ring_join": "{band}, {culture}"
|
||||
},
|
||||
"zh": {
|
||||
"map_page_kicker": "未得之民",
|
||||
|
|
@ -2338,6 +2418,14 @@
|
|||
"sets_bounded_title": "已触及与未触及",
|
||||
"sets_centered_title": "与邻近族群的距离",
|
||||
"sets_bounded_tally": "{inside} 圈内 · {outside} 圈外 · {unknown} 未知",
|
||||
"sets_centered_tally": "{placed} 已定位 · {centre} 在中心 · {unplaced} 未定位"
|
||||
"sets_centered_tally": "{placed} 已定位 · {centre} 在中心 · {unplaced} 未定位",
|
||||
"ring_band_dialect": "同一方言",
|
||||
"ring_band_language": "同一语言",
|
||||
"ring_band_family": "同一语系",
|
||||
"ring_band_geography": "仅地理相邻",
|
||||
"ring_culture_same": "同一族群",
|
||||
"ring_culture_near": "相近文化",
|
||||
"ring_culture_different": "不同文化",
|
||||
"ring_join": "{band},{culture}"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue