Files
christianandClaude Opus 5 c9eef0672c Church Translate, as it runs at the campuses
Live sermon translation into 100+ languages, running entirely on one Mac per
campus: audio in, voice detection, Whisper on the GPU, NLLB-200 in its own
process, and a built-in web server the congregation reads from on their
phones. No audio leaves the building.

This is the source only. What used to sit beside it — 14 GB of speech and
translation models, 8 GB of voice bundles, 6 GB of natural voices, the Electron
caches, and the app data holding the operator PIN and every service transcript
— is machine state and downloaded assets, and stays in the NAS backup where it
belongs. The vendored node, cloudflared and Windows ffmpeg binaries are out too
(533 MB, re-fetched for packaging); the browser-side runtime beside them is
kept, so a checkout still serves the congregation page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 15:41:21 -04:00

70 lines
4.6 KiB
JavaScript

/*
* short-questions.js — one-word questions that must never be guessed at.
*
* MEASURED: "What?" is the single most frequent invented line left after every
* other guard — the shipped NLLB renders it "¿Qué es eso?" (15 archive rows),
* "Yeah?" -> "¿Qué es eso?" (6), "Hello?" -> "¿Qué pasa?" (6), "How?" -> "¿Cómo
* es que lo hizo?" (3). The preacher says "What?" about three times a service.
* Because the SOURCE is itself a question, the hallucination guard rightly
* refuses to blank the output (a Spanish question is the correct shape) — so
* these bypass the model altogether, the way liturgy.js does for "Amen".
*
* Whole-utterance match only, one word, terminal '?' or '.' or nothing. The
* table holds only words whose one-word rendering is not a matter of register
* or denomination. Languages not listed fall through to the model unchanged.
*/
(function (root, factory) {
if (typeof module === 'object' && module.exports) module.exports = factory();
else root.ShortQuestions = factory();
}(typeof self !== 'undefined' ? self : this, function () {
'use strict';
/* word -> { lang: rendering } — the base language stands for its variants. */
const TABLE = {
what: { spa_Latn: '¿Qué?', por_Latn: 'O quê?', fra_Latn: 'Quoi ?', deu_Latn: 'Was?', ita_Latn: 'Cosa?' },
why: { spa_Latn: '¿Por qué?', por_Latn: 'Por quê?', fra_Latn: 'Pourquoi ?', deu_Latn: 'Warum?', ita_Latn: 'Perché?' },
who: { spa_Latn: '¿Quién?', por_Latn: 'Quem?', fra_Latn: 'Qui ?', deu_Latn: 'Wer?', ita_Latn: 'Chi?' },
how: { spa_Latn: '¿Cómo?', por_Latn: 'Como?', fra_Latn: 'Comment ?', deu_Latn: 'Wie?', ita_Latn: 'Come?' },
where: { spa_Latn: '¿Dónde?', por_Latn: 'Onde?', fra_Latn: 'Où ?', deu_Latn: 'Wo?', ita_Latn: 'Dove?' },
when: { spa_Latn: '¿Cuándo?', por_Latn: 'Quando?', fra_Latn: 'Quand ?', deu_Latn: 'Wann?', ita_Latn: 'Quando?' },
which: { spa_Latn: '¿Cuál?', por_Latn: 'Qual?', fra_Latn: 'Lequel ?', deu_Latn: 'Welche?', ita_Latn: 'Quale?' },
yeah: { spa_Latn: '¿Sí?', por_Latn: 'Sim?', fra_Latn: 'Oui ?', deu_Latn: 'Ja?', ita_Latn: 'Sì?' },
yes: { spa_Latn: '¿Sí?', por_Latn: 'Sim?', fra_Latn: 'Oui ?', deu_Latn: 'Ja?', ita_Latn: 'Sì?' },
hello: { spa_Latn: '¿Hola?', por_Latn: 'Olá?', fra_Latn: 'Allô ?', deu_Latn: 'Hallo?', ita_Latn: 'Pronto?' },
right: { spa_Latn: '¿Verdad?', por_Latn: 'Certo?', fra_Latn: "N'est-ce pas ?", deu_Latn: 'Oder?', ita_Latn: 'Vero?' },
really: { spa_Latn: '¿En serio?', por_Latn: 'Sério?', fra_Latn: 'Vraiment ?', deu_Latn: 'Wirklich?', ita_Latn: 'Davvero?' },
okay: { spa_Latn: '¿Está bien?', por_Latn: 'Está bem?', fra_Latn: "D'accord ?", deu_Latn: 'Okay?', ita_Latn: 'Va bene?' },
ok: { spa_Latn: '¿Está bien?', por_Latn: 'Está bem?', fra_Latn: "D'accord ?", deu_Latn: 'Okay?', ita_Latn: 'Va bene?' },
ready: { spa_Latn: '¿Listos?', por_Latn: 'Prontos?', fra_Latn: 'Prêts ?', deu_Latn: 'Bereit?', ita_Latn: 'Pronti?' },
huh: { spa_Latn: '¿Eh?', por_Latn: 'Hã?', fra_Latn: 'Hein ?', deu_Latn: 'Hä?', ita_Latn: 'Eh?' }
};
const VARIANT_OF = { spa_ES: 'spa_Latn', spa_MX: 'spa_Latn', por_PT: 'por_Latn' };
/* "What?", "what.", "WHAT", '"What?"' — one word, optionally punctuated. */
function key(text) {
const t = String(text || '').trim().replace(/^[\s"'“‘(\[]+/u, '').replace(/[\s"'”’)\]]+$/u, '');
const m = t.match(/^([A-Za-z]+)[?.!…]*$/);
return m ? m[1].toLowerCase() : null;
}
/*
* Only a source that IS a question (ends in '?') or is a bare interrogative
* word: "What." is Whisper dropping the mark; "What" alone is the same. A
* bare "yes"/"okay"/"right"/"ready" with a period is an ANSWER, not a
* question, and must reach the model as one — so those need the '?'.
*/
const NEEDS_MARK = new Set(['yeah', 'yes', 'hello', 'right', 'really', 'okay', 'ok', 'ready', 'huh']);
function lookup(text, lang) {
const k = key(text);
if (!k || !TABLE[k]) return null;
const asked = /\?[?!.…]*\s*["'”’)\]]*$/.test(String(text || '').trim());
if (!asked && NEEDS_MARK.has(k)) return null;
const row = TABLE[k];
return row[lang] || row[VARIANT_OF[lang]] || null;
}
return { lookup, TABLE };
}));