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>
44 lines
4.1 KiB
JavaScript
44 lines
4.1 KiB
JavaScript
/* Run the guard over MEASURED NLLB output: 9 inputs x 10 languages.
|
||
The question is not "does it catch the bad one" — it is "does it leave the
|
||
other 89 alone". A guard that corrupts good output is worse than no guard,
|
||
because nobody in the building can read most of these. */
|
||
const G = require('./mt-guard.js');
|
||
const D = {
|
||
spa_Latn:["Donald Trump. ¿Qué es eso?","- Sí, estoy bien.","¡Aleluya!","Se llamaba John.","Abre al libro, Matthew.","Vuelva conmigo a Juan capítulo 3.","Jesús lloró.","¡Alabado sea el Señor!","Pedro lo negó tres veces."],
|
||
por_Latn:["Donald Trump.","- Não, não.","Aleluia!","O nome dele era John.","Abre-te ao livro, Matthew.","Virem comigo para João capítulo 3.","Jesus chorou.","Louvem o Senhor.","Pedro negou-o três vezes."],
|
||
fra_Latn:["Donald Trump est là.","Je vous en prie.","Je vous en prie.","Il s'appelait John.","Ouvre-toi au livre, Matthew.","Tournez- vous avec moi vers Jean chapitre 3.","Jésus pleurait.","Louez le Seigneur.","Pierre le nia trois fois."],
|
||
deu_Latn:["Donald Trump.","- Das ist doch gut.","Halleluja!","Sein Name war John.","Mach dir das Buch zu, Matthew.","Wenden Sie sich mit mir an Johannes Kapitel 3.","Jesus weinte.","Lob dem Herrn.","Petrus verleugnete ihn dreimal."],
|
||
kor_Hang:["도널드 트럼프","아멘","할렐루야","그의 이름은 존이었다.","책에 들어봐, 매튜","요한 3 장에 나와 함께 가십시오.","예수께서는 울었습니다.","하나님께 영광을 돌리소서","베드로가 그를 세 번 부인했다."],
|
||
zho_Hans:["唐纳德特朗普.","现在,我们要去.","哈利鲁亚!","他的名字是约翰.","开放给书,马修.","转向约翰第三章.","耶稣哭了.","赞美上帝.","彼得否认他三次."],
|
||
rus_Cyrl:["Дональд Трамп.","- Да, да.","- Хэллюя.","Его звали Джон.","Открой глаза на книгу, Мэтью.","Повернись со мной к Иоанну, главе 3.","Иисус заплакал.","Слава Богу.","Петр трижды отрицал его."],
|
||
arb_Arab:["دونالد ترامب","آمين","هليليا","اسمه كان جون","افتح نفسك للكتب يا (ماثيو)","انعطف معي إلى يوحنا الفصل الثالث","يسوع يبكي","-حمد الله","بـطـرس نـكره ثلاث مرات"],
|
||
hat_Latn:["Donald Trump.","Amen.","Aleluya.","Non l te rele John.","Ouvri pou liv la, Matthew.","Fè m 'konnen Jan chapit 3.","Jezi te kriye.","Fè Bondye lwanj.","Pyè te refize l twa fwa."],
|
||
vie_Latn:["Donald Trump.","- A-men.","Hallelujah.","Tên anh ta là John.","Mở ra sách, Matthew.","Hãy cùng tôi đọc Giăng chương 3.","Chúa Giêsu khóc.","Chúc mừng Chúa.","Phi-e-rơ đã từ chối ông ba lần."]
|
||
};
|
||
const SRC = ["Donald Trump.","Amen.","Hallelujah.","His name was John.","Open up to the book, Matthew.","Turn with me to John chapter 3.","Jesus wept.","Praise the Lord.","Peter denied him three times."];
|
||
|
||
/* The ONLY output that should lose words. Everything else may lose a leading
|
||
dash and nothing more. */
|
||
/* index 0 is "Donald Trump." (invented question); index 1 is "Amen.", a
|
||
one-word source whose comma-clause is invented in several languages. */
|
||
const MAY_LOSE_WORDS = new Set(['spa_Latn|0','spa_Latn|1','por_Latn|1','zho_Hans|1','rus_Cyrl|1']);
|
||
|
||
let touched = 0, dashOnly = 0, wordLoss = 0, bad = 0;
|
||
for (const lang of Object.keys(D)) {
|
||
D[lang].forEach((tgt, i) => {
|
||
const r = G.clean(SRC[i], tgt);
|
||
if (r.text === tgt) return;
|
||
touched++;
|
||
const strippedDash = tgt.replace(/^\s*[-‐-―−]\s*/, '').trim();
|
||
if (r.text === strippedDash) { dashOnly++; console.log(' dash ' + lang + ' "' + tgt + '" -> "' + r.text + '"'); return; }
|
||
wordLoss++;
|
||
const allowed = MAY_LOSE_WORDS.has(lang + '|' + i);
|
||
console.log((allowed ? ' TRIM ' : ' !!BAD ') + lang + ' "' + tgt + '" -> "' + r.text + '"');
|
||
if (r.reason) console.log(' ' + r.reason);
|
||
if (!allowed) bad++;
|
||
});
|
||
}
|
||
console.log('\n90 measured outputs: ' + touched + ' touched, ' + dashOnly + ' dash-only, ' +
|
||
wordLoss + ' word-loss, ' + bad + ' UNEXPECTED');
|
||
process.exit(bad ? 1 : 0);
|