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

90 lines
3.8 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=5">
<meta name="theme-color" content="#0b0d12">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="color-scheme" content="dark">
<title>Live Translation</title>
<link rel="manifest" href="/app.webmanifest">
<link rel="stylesheet" href="/listen.css">
</head>
<body>
<!-- ───────────────────────── language picker ───────────────────────── -->
<section id="picker" class="screen is-active">
<div class="picker-inner">
<header class="picker-head">
<div class="church" id="churchName">Live Translation</div>
<h1 id="chooseTitle">Choose your language</h1>
<!-- Cycles through the same question in other languages, so anyone can
tell what this page is for without reading English. -->
<div class="rotator" id="rotator" aria-hidden="true"></div>
</header>
<div class="featured" id="featured"></div>
<div class="search-wrap">
<div class="search-box">
<input id="search" type="search" inputmode="search" autocomplete="off" enterkeyhint="search"
autocorrect="off" spellcheck="false" placeholder="Search all languages">
<button id="searchClear" class="search-clear" type="button" aria-label="Clear search" hidden>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
</button>
</div>
<div class="results" id="results" role="listbox"></div>
</div>
</div>
</section>
<!-- ───────────────────────── caption reader ────────────────────────── -->
<section id="reader" class="screen">
<header class="bar">
<button id="btnBack" class="bar-btn" aria-label="Change language">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 5l-7 7 7 7"/></svg>
</button>
<div class="bar-lang">
<span id="barLangName">—</span>
<span id="liveDot" class="dot" title="Live"></span>
</div>
<div class="bar-actions">
<button id="btnSpeak" class="bar-btn" aria-label="Speak aloud" aria-pressed="false">
<svg viewBox="0 0 24 24" aria-hidden="true" class="ic-off"><path d="M11 5L6 9H2v6h4l5 4V5z"/><path d="M22 9l-6 6M16 9l6 6"/></svg>
<svg viewBox="0 0 24 24" aria-hidden="true" class="ic-on"><path d="M11 5L6 9H2v6h4l5 4V5z"/><path d="M15.5 8.5a5 5 0 010 7M18.5 5.5a9 9 0 010 13"/></svg>
</button>
<button id="btnOriginal" class="bar-btn bar-btn-text bar-btn-src" aria-label="Show original" aria-pressed="false">EN</button>
<button id="btnSize" class="bar-btn bar-btn-text" aria-label="Text size">Aa</button>
</div>
</header>
<div id="voiceBar" class="voicebar" hidden>
<label for="voicePick">Voice</label>
<select id="voicePick"></select>
</div>
<p id="voiceHint" class="voicehint" hidden></p>
<main id="stream" class="stream" tabindex="0">
<div id="waiting" class="waiting">
<div class="pulse"><span></span><span></span><span></span></div>
<p id="waitingText">The message hasn't started yet</p>
</div>
<div id="lines" class="lines"></div>
<div id="partial" class="line line-partial" hidden></div>
</main>
<button id="jumpLive" class="jump" hidden>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
<span id="jumpLabel">Live</span>
</button>
<div id="toast" class="toast" hidden></div>
</section>
<script src="/languages.js"></script>
<script src="/ui-strings.js"></script>
<script src="/listen.js"></script>
</body>
</html>