gospel-resource-rows.js builds each resource row's display text, and its fallbacks were English literals: a record with no name of its own showed "Scripture in this language", "Audio Scripture", "Listen in a browser" to an Arabic or Chinese reader — precisely the rows whose source data was least complete. check:hardcoded-english does not read .js files, which is why the bundle's sweep never saw them. Thirteen keys in eleven locales, and the translator is PASSED IN rather than imported: the module is a pure function with no component context, and it stays optional, so a caller without one degrades to the English written at the call site instead of throwing. Three publisher names stay verbatim — JESUS Film, Scripture Earth, Zume — but where a name sits inside an ordinary phrase the phrase is keyed and the name is INTERPOLATED, so "Zume discipleship training" translates around a brand a translator never retypes. SE_KIND's values are displayed and take keys; its keys are the upstream feed's codes and must not be touched. Verified by running the module both ways: without a translator every row reads as it does today; with an Arabic one the labels and scopes are Arabic while "Scripture Earth" and "Zume" are unchanged. ROUTED TO STUDIO, not self-landed: the eleven locale files make this a library change. The route test says 11, and the last time I judged a landing by which plugin the work was for rather than which directories it touched, the scan ran against most of the fleet and timed out twice.
112 lines
6.8 KiB
JavaScript
112 lines
6.8 KiB
JavaScript
/**
|
|
* ═══ WHAT COUNTS AS SCRIPTURE SOMEONE CAN READ — ONE DEFINITION ═══════════════════════════════
|
|
* Exported because it had been written FOUR times: in the pin builder's tier function (as `!!c.s`,
|
|
* which is why 2,001 groups were mis-tiered), in that builder's hasReadableScripture (correctly),
|
|
* in check-gospel-coverage-consistent's arm F (narrowly, so it then disagreed with the fix), and
|
|
* implicitly in resourceRows below. A rule with four copies has no owner. This is the owner.
|
|
*
|
|
* Text lives in four places: an eBible/PNG scripture URL, Scripture Earth, a Door43 translation
|
|
* that is not Open Bible Stories, and — the one that was being missed — a Faith Comes By Hearing
|
|
* record whose media list names text.
|
|
*/
|
|
export const hasReadableScripture = (c) =>
|
|
!!c && (!!c.s || !!c.se || !!(c.au && /text/.test(c.au.m || '')) ||
|
|
(c.d || []).some((d) => !/^Open Bible Stories/.test(d.s || '')))
|
|
|
|
/**
|
|
* gospel-resource-rows.js — what a language's codes[] entry offers, in ONE place.
|
|
*
|
|
* PRIVATE TO THIS PLUGIN, deliberately. Nothing outside gospel-resources has a codes[]
|
|
* entry, so this is not a shared component and does not belong in library/ — putting it
|
|
* there would publish a signature only one bundle can call and invite a second consumer to
|
|
* bend it.
|
|
*
|
|
* WHY IT EXISTS. Each surface used to list the resource fields for itself. The map grew
|
|
* chips per type, the charts computed "has nothing" by hand, and the list built its links
|
|
* from `c.s` and `c.d` only. So every new source had to be added in four places, and it
|
|
* never was: when Scripture Earth and GRN landed, the list profile showed neither — a
|
|
* language whose ONLY resource was a Words of Life recording rendered with no links at all,
|
|
* while the map offered a working one for the same language. The charts had the same drift
|
|
* and it took a hand count to find. The fix that lasts is not remembering harder.
|
|
*
|
|
* This mirrors scripts/build-resource-links-table.mjs, which types the shipped table. The
|
|
* two must agree, because one is what the table says and the other is what the page shows,
|
|
* and a reader comparing them is entitled to see the same answer. Types are kept honest in
|
|
* the same way the table keeps them:
|
|
* - Open Bible Stories is Bible Stories, never Scripture — 50 illustrated narratives are
|
|
* not a translation.
|
|
* - Words of Life is Gospel audio, never Scripture — gospel messages with songs.
|
|
* - Zume is Discipleship training, a course.
|
|
* - A bible.is fileset is typed by what it actually carries, not by assumption.
|
|
*/
|
|
|
|
/** Scripture Earth directory names, as a reader would say them. */
|
|
// The VALUES here are displayed (joined into a row's scope), so they are labels and take keys; the
|
|
// KEYS are the upstream feed's own codes and must never be translated. PDF/audio/app stay as they
|
|
// are: an abbreviation and two words that are the same token in the locales we ship.
|
|
const SE_KIND = (tx) => ({ PDF: 'PDF', audio: 'audio', sab: 'app',
|
|
study: tx('gospel.rows.studyNotes', 'study notes'), viewer: tx('gospel.rows.readOnline', 'read online') })
|
|
|
|
/**
|
|
* Every resource a language offers, flattened for a list or a table.
|
|
* @param {object} c one entry of the pin index's codes[]
|
|
* @returns {{url:string,title:string,type:string,scope:string}[]}
|
|
*/
|
|
// ★ THE FALLBACKS BELOW ARE UI TEXT, NOT DATA. Each `c.x || 'English'` is shown when a record
|
|
// carries no name of its own, so an Arabic or Chinese reader saw English for exactly the rows whose
|
|
// source was least complete. check:hardcoded-english does not read .js files yet, which is why these
|
|
// survived the bundle's sweep.
|
|
//
|
|
// `t` is PASSED IN rather than imported: this module is a pure function with no component context,
|
|
// and the plugin already threads its translator the same way. It is optional and falls back to the
|
|
// English written here, so a caller without a translator degrades to today's behaviour.
|
|
//
|
|
// 'JESUS Film' below is deliberately NOT keyed: it is the project's name, not a label.
|
|
export function resourceRows(c, t = null) {
|
|
const tx = (key, en) => { try { const v = t && t(key); return v && v !== key ? v : en } catch (_) { return en } }
|
|
if (!c) return []
|
|
const out = []
|
|
const add = (title, type, url, scope = '') => {
|
|
if (url) out.push({ url, title, type, scope })
|
|
}
|
|
|
|
add(c.sv || tx('gospel.rows.scriptureHere', 'Scripture in this language'), 'Scripture', c.s, c.ss || '')
|
|
/* c.fT is set only where the JESUS Film component does NOT carry this language and a
|
|
different JESUS Film Project feature does — The Savior, Magdalena. Calling that "JESUS
|
|
Film" would be the same class of lie as calling Open Bible Stories Scripture. */
|
|
add(c.fT || 'JESUS Film', 'Film', c.f)
|
|
for (const d of c.d || []) {
|
|
add(d.s, /^Open Bible Stories/.test(d.s) ? 'Bible Stories' : 'Scripture', d.u, d.sc || '')
|
|
}
|
|
if (c.au) {
|
|
const media = String(c.au.m || 'audio')
|
|
add(
|
|
c.au.n || tx('gospel.rows.audio', 'Scripture'),
|
|
/* ⚠ ONE RECORD, TWO CAPABILITIES, AND THE TYPE ONLY SAID ONE. Faith Comes By Hearing's media
|
|
list is often "audio, text, video": this typed every such row 'Audio Scripture', so the
|
|
access table's byType.Scripture never saw it and 2,001 people groups were published as
|
|
"watch or hear, cannot read" while their own readable text sat behind that link. TEXT
|
|
decides the type; the media list stays in the scope so nothing is hidden, and the access
|
|
builder counts a Scripture row whose scope names audio as hearable too. */
|
|
/text/.test(media) ? tx('gospel.rows.audio', 'Scripture') : tx('gospel.rows.audioScripture', 'Audio Scripture'),
|
|
c.au.u,
|
|
media === 'audio' ? tx('gospel.rows.listenBrowser', 'Listen in a browser')
|
|
: tx('gospel.rows.inBrowser', 'In a browser — {media}').replace('{media}', media),
|
|
)
|
|
}
|
|
if (c.bv) add(c.bv.n || tx('gospel.rows.bibleVideo', 'Bible video'), 'Bible video', c.bv.u, c.bv.sc || '')
|
|
if (c.gp) add(c.gp.n || tx('gospel.rows.gospelPresentation', 'Gospel presentation'), 'Gospel presentation', c.gp.u, tx('gospel.rows.readHere', 'Read in this language'))
|
|
if (c.se) {
|
|
add('Scripture Earth', 'Scripture', c.se.u, (c.se.k || []).map((k) => SE_KIND(tx)[k] || k).join(', '))
|
|
}
|
|
if (c.ga) {
|
|
add(c.ga.n ? tx('gospel.rows.wordsOfLife', 'Words of Life — {name}').replace('{name}', c.ga.n)
|
|
: tx('gospel.rows.gospelAudio', 'Gospel audio'), 'Gospel audio', c.ga.u,
|
|
tx('gospel.rows.listenHere', 'Listen in this language'))
|
|
}
|
|
// The brand is INTERPOLATED, not retyped by a translator: "Zume" is the publisher's name and must
|
|
// survive every locale unchanged, while "discipleship training" is an ordinary noun phrase.
|
|
add(tx('gospel.rows.discipleship', '{brand} discipleship training').replace('{brand}', 'Zume'),
|
|
'Discipleship training', c.z)
|
|
return out
|
|
}
|