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

23 lines
1.2 KiB
JavaScript

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, l) => {
const o = await pipe(t, { src_lang: 'eng_Latn', tgt_lang: l });
return (Array.isArray(o) ? o[0] : o).translation_text.trim();
};
const CASES = ['Peter.','John.','Matthew.','Noah.','Jesus.','Mary.','Moses.','Habakkuk.','Donald Trump.',
'Yes.','Okay.','Right.','Exactly.','Amen.','Watch this.','Come on.','Genesis.','Romans.',
'Simon Peter.','John the Baptist.'];
const LANGS = ['spa_Latn','por_Latn','fra_Latn'];
for (const lang of LANGS) {
console.log('\n=== ' + lang + ' ===');
console.log(' input'.padEnd(22) + 'WITH the period'.padEnd(34) + 'period STRIPPED before MT');
for (const t of CASES) {
const withDot = await say(t, lang);
const stripped = await say(t.replace(/[.!?]+$/, ''), lang);
console.log(' ' + t.padEnd(20) + withDot.slice(0, 32).padEnd(34) + stripped.slice(0, 32));
}
}