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>
This commit is contained in:
2026-09-23 15:41:21 -04:00
co-authored by Claude Opus 5
commit c9eef0672c
118 changed files with 36031 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
/* Verify the audit's claims against the REAL functions, not by reading. */
const fs=require('fs'), vm=require('vm');
const src=fs.readFileSync('engine.js','utf8');
const grab=(n)=>{const i=src.indexOf('function '+n+'(');let d=0;for(let k=src.indexOf('{',i);k<src.length;k++){if(src[k]==='{')d++;else if(src[k]==='}'&&--d===0)return src.slice(i,k+1);}};
const sb={console};vm.createContext(sb);
vm.runInContext(src.slice(src.indexOf('const SCRIPT_NO_SPACES'), src.indexOf('function applyFixes')), sb);
vm.runInContext(src.match(/^const ABBREV_TAIL = .*$/m)[0], sb);
vm.runInContext(grab('splitSentences'), sb);
console.log('CLAIM A1 — \\b is ASCII-only, so non-Latin terms get NO boundary guard');
for (const t of ['Марк','Иоанн','الرب','यूहन्ना','José']) {
const re = sb.termMatcher(t);
console.log(' ' + t.padEnd(10) + ' -> /' + re.source + '/' + re.flags);
}
console.log(' consequence:');
for (const [term,hay] of [['Марк','Маркса продали'],['Иоанн','Иоанном говорил'],['الرب','الربا حرام']]) {
const re=sb.termMatcher(term); re.lastIndex=0;
const hit = sb.applyTable(hay,[{from:term,to:'<<X>>'}]);
console.log(' "'+hay+'" -> "'+hit+'"');
}
console.log('\nCLAIM A2 — accented capitals fall through to case-INSENSITIVE');
for (const t of ['Éxodo','Ámos','Exodus']) {
const re=sb.termMatcher(t);
console.log(' '+t.padEnd(8)+' flags='+re.flags+(re.flags.includes('i')?' <-- matches lowercase too':''));
}
console.log(' "el éxodo de los jóvenes" -> "' + sb.applyTable('el éxodo de los jóvenes',[{from:'Éxodo',to:'Exodo-BOOK'}]) + '"');
console.log('\nCLAIM A3 — 2-char CJK entries are unmatchable in running text');
for (const [term,hay] of [['约翰','约翰说了'],['摩西','摩西带领'],['哈巴谷书','向哈巴谷书第一章']]) {
const out = sb.applyTable(hay,[{from:term,to:'<<X>>'}]);
console.log(' '+term.padEnd(6)+' in "'+hay+'" -> "'+out+'"'+(out===hay?' <-- DID NOT FIRE':''));
}
console.log('\nCLAIM C1 — splitSentences glues short trailing sentences');
for (const s of ['God is good. Amen.','It cost 3.5 million.','Dr. King said God is good.','Jesus wept. Amen.']) {
console.log(' "'+s+'"\n -> '+JSON.stringify(sb.splitSentences(s)));
}