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
+42
View File
@@ -0,0 +1,42 @@
import { pipeline, env } from '@huggingface/transformers';
import os from 'os'; import path from 'path';
env.allowLocalModels=false;
env.cacheDir=path.join(os.homedir(),'Library/Application Support/Church Translate Models');
const pipe=await pipeline('translation','Xenova/nllb-200-distilled-600M',{dtype:'fp32'});
const say=async(t)=>{const o=await pipe(t,{src_lang:'eng_Latn',tgt_lang:'spa_Latn'});return (Array.isArray(o)?o[0]:o).translation_text.trim();};
/* Classify which form of address NLLB reached for. */
function reg(s){
const hits=[];
if (/\bvosotros\b|\bvuestr[oa]s?\b|\bos\b(?!\s*[.,])/i.test(s)) hits.push('VOSOTROS');
if (/\bustedes\b/i.test(s)) hits.push('ustedes');
if (/\busted\b/i.test(s)) hits.push('usted');
/* vosotros verb endings */
if (/\w(áis|éis|ís)\b/.test(s)) hits.push('VOSOTROS-verb');
/* tú imperatives / 2sg endings */
if (/\b(mira|abre|vuelve|ven|escucha|piensa|deja|pon|ve|di|haz|sé)\b/i.test(s)) hits.push('tú');
/* ustedes imperative/3pl */
if (/\w(en|an)\b/.test(s) && /\b(abran|miren|vengan|escuchen|piensen|dejen|pongan|vayan|digan|hagan|estén|sean|levanten|inclinen)\b/i.test(s)) hits.push('ustedes-verb');
return hits.length?hits.join('+'):'—';
}
const LINES=[
'Open your Bibles.','Open your Bibles to John chapter three.','Bow your heads with me.',
'Stand to your feet.','Turn to the person next to you.','Listen to me carefully.',
'I want you to understand this.','God has a plan for you.','You are the light of the world.',
'You cannot serve two masters.','Do you believe that?','Are you following me?',
'Lift your hands.','Close your eyes.','Repeat after me.','Come to the altar.',
'Give thanks to the Lord.','Pray with me.','Look at verse four with me.',
'Remember what He did for you.','You have been forgiven.','Do not be afraid.',
'Love one another.','Forgive those who hurt you.','Trust in the Lord with all your heart.'
];
console.log('English -> Spanish [register NLLB chose]');
console.log('='.repeat(78));
const tally={};
for (const t of LINES) {
const s=await say(t); const r=reg(s);
for (const k of r.split('+')) tally[k]=(tally[k]||0)+1;
console.log(' '+t+'\n '+s+'\n ['+r+']');
}
console.log('\nTALLY across '+LINES.length+' direct-address sentences:');
for (const [k,v] of Object.entries(tally).sort((a,b)=>b[1]-a[1])) console.log(' '+String(v).padStart(3)+' '+k);