Files
dobetternorge-tools/transcribe.php
T
daveadmin b84827ecea Add Transcribe docs (about/guide/tech) + refresh Redact docs
- New: transcribe-about.php, transcribe-guide.php, transcribe-tech.php
  with full en/no/uk/pl translations (3-engine cascade, diarization, vocab)
- New: translations/transcribe-about|guide|tech.php (4-lang strings)
- New: scripts/translate-pages.php (Azure gpt-4o CLI translation helper)
- Add korr-doc-links nav to transcribe.php
- Refresh redact-about|guide|tech.php — point to assets/images/redact/
- Fix all "never written to disk" wording in redact translations
- Add Min Sak/corpus save workflow to redact guide and tech privacy section
- redact.php upload hint: correct in-memory wording

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 10:28:06 +02:00

157 lines
12 KiB
PHP

<?php
declare(strict_types=1);
$toolName = 'transcribe';
$toolTitle = 'Transcribe audio';
$toolKind = 'Audio Transcription';
$toolBadge = 'Azure · Google · Whisper';
require_once __DIR__ . '/includes/layout.php';
?>
<form id="toolForm" class="tool-form">
<div class="korr-doc-links">
<a href="/transcribe-about.php" target="_blank">About this tool</a>
<span aria-hidden="true">&middot;</span>
<a href="/transcribe-guide.php" target="_blank">User guide</a>
<span aria-hidden="true">&middot;</span>
<a href="/transcribe-tech.php" target="_blank">How it works</a>
</div>
<div class="lang-switcher" id="uiLangSwitcher" role="group" aria-label="UI language">
<button type="button" class="lang-btn is-active" data-lang="en">&#127468;&#127463; EN</button>
<button type="button" class="lang-btn" data-lang="no">&#127475;&#127476; NO</button>
<button type="button" class="lang-btn" data-lang="uk">&#127482;&#127462; UK</button>
<button type="button" class="lang-btn" data-lang="pl">&#127477;&#127473; PL</button>
</div>
<div class="control-row" id="transcribeLangControl">
<span class="control-label" data-i18n="transcribeLang">Audio language</span>
<label><input type="radio" name="transcribeLang" value="no" checked> Norsk (nb)</label>
<label><input type="radio" name="transcribeLang" value="nn"> Nynorsk</label>
<label><input type="radio" name="transcribeLang" value="en"> English</label>
<label><input type="radio" name="transcribeLang" value="pl"> Polski</label>
<label><input type="radio" name="transcribeLang" value="uk"> &#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;</label>
<label><input type="radio" name="transcribeLang" value="sv"> Svenska</label>
<label><input type="radio" name="transcribeLang" value="da"> Dansk</label>
<label><input type="radio" name="transcribeLang" value="de"> Deutsch</label>
<label><input type="radio" name="transcribeLang" value="fr"> Fran&ccedil;ais</label>
<label><input type="radio" name="transcribeLang" value="auto"> Auto-detect <small class="control-hint" data-i18n="autoDetectHint">(may confuse nb/da/sv)</small></label>
</div>
<div class="control-row" id="diarizeControl">
<span class="control-label" data-i18n="speakers">Speakers</span>
<label><input type="checkbox" id="diarizeCheck" name="diarize"> <span data-i18n="identifySpeakers">Identify speakers</span></label>
<span class="control-label" style="margin-left:1.25rem" data-i18n="speakersCount">Count</span>
<input type="number" id="numSpeakersInput" name="num_speakers" min="2" max="20" placeholder="auto" class="num-speakers-input" data-i18n-placeholder="speakersPlaceholder" data-i18n-aria="speakersAriaLabel" aria-label="Expected number of speakers">
</div>
<div class="expert-field" id="vocabControl">
<div class="vocab-presets" id="vocabPresets">
<span class="control-label" data-i18n="vocabulary">Vocabulary</span>
<button type="button" class="vocab-btn" data-preset="barnerett" data-i18n="vocabPresetChildWelfare">Child welfare / CPS</button>
<button type="button" class="vocab-btn" data-preset="mediation" data-i18n="vocabPresetMediation">Mediation / legal meeting</button>
<button type="button" class="vocab-btn" data-preset="generell" data-i18n="vocabPresetGeneral">General Norwegian</button>
<button type="button" class="vocab-btn" data-preset="custom" data-i18n="vocabPresetCustom">Custom</button>
</div>
<textarea id="initPromptInput" name="initial_prompt" rows="2" placeholder="Technical terms and names for Whisper to recognise, e.g. Barnevernet, mediation, family services…" class="prompt-textarea" data-i18n-placeholder="vocabPlaceholder"></textarea>
<div class="vocab-footer">
<p class="upload-hint" data-i18n="vocabHint">Helps Whisper recognise technical terms. Not included in the transcript.</p>
<small class="control-hint vocab-char-count"><span id="vocabCharCount">0</span> / 500</small>
</div>
</div>
<details id="advancedOptions" class="expert-field">
<summary data-i18n="advancedOptions">Advanced options</summary>
<div class="control-row" id="taskControl">
<span class="control-label" data-i18n="task">Task</span>
<label><input type="radio" name="task" value="transcribe" checked> <span data-i18n="taskTranscribe">Transcribe</span></label>
<label><input type="radio" name="task" value="translate"> <span data-i18n="taskTranslate">Translate to English</span></label>
</div>
<div class="control-row">
<span class="control-label" data-i18n="vadFilter">VAD filter</span>
<label><input type="checkbox" id="vadFilterCheck" name="vad_filter"> <span data-i18n="vadFilterLabel">Remove silence / noise</span></label>
<small class="control-hint" data-i18n="vadFilterHint">Improves accuracy on recordings with long pauses.</small>
</div>
<div class="control-row" id="whisperModelControl">
<span class="control-label" data-i18n="whisperModel">Whisper model</span>
<select id="whisperModelSelect" name="whisper_model">
<option value="large-v3" selected>large-v3 (best)</option>
<option value="large-v2">large-v2</option>
<option value="medium">medium (faster)</option>
<option value="small">small</option>
<option value="base">base</option>
<option value="tiny">tiny</option>
</select>
<small class="control-hint" data-i18n="whisperModelHint">Used when Azure/GCP unavailable. large-v3 is the default.</small>
</div>
<div class="control-row" id="postModelControl">
<span class="control-label" data-i18n="postModel">AI cleanup</span>
<label><input type="radio" name="post_model" value="" checked> <span data-i18n="postModelNone">None</span></label>
<label><input type="radio" name="post_model" value="gpt-4o-mini"> <span data-i18n="postModelMini">GPT-4o Mini</span></label>
<label><input type="radio" name="post_model" value="gpt-4o"> <span data-i18n="postModelFull">GPT-4o</span></label>
<small class="control-hint" data-i18n="postModelHint">Fixes errors, punctuation, and domain terms after transcription.</small>
</div>
</details>
<div class="upload-zone" id="audioZone" role="region" aria-label="Audio upload" data-i18n-aria="uploadAria">
<input type="file" id="audioInput" accept="audio/*,video/mp4,video/webm" multiple aria-label="Choose audio files">
<div id="audioPrompt" class="upload-prompt">
<span class="upload-icon" aria-hidden="true">&#9654;</span>
<p><span data-i18n="uploadDrop">Drop audio file(s) here, or</span> <label for="audioInput" class="upload-browse" data-i18n="uploadBrowse">browse</label></p>
<p class="upload-hint"><strong>MP3</strong>, <strong>WAV</strong>, <strong>OGG</strong>, <strong>M4A</strong>, <strong>FLAC</strong>, <strong>WEBM</strong> &mdash; <span data-i18n="uploadHint">max 200&thinsp;MB per file</span></p>
</div>
<div id="audioFileInfo" class="upload-file is-hidden">
<ol id="audioQueueList" class="audio-queue-list"></ol>
<div class="audio-queue-actions">
<label for="audioInput" class="upload-browse" data-i18n="uploadAddFiles">+ Add files</label>
<button type="button" id="audioClear" class="upload-clear" data-i18n="uploadClearQueue">&times; Clear queue</button>
</div>
</div>
</div>
<div id="audioPickerSection" class="doc-picker-section">
<button type="button" id="audioPickerBtn" class="doc-picker-btn" aria-haspopup="dialog">
<svg class="doc-picker-btn__icon" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.4"/><path d="M6 5.5v5l5-2.5-5-2.5z" fill="currentColor"/></svg>
<span data-i18n="audioPickerBtn">Select from My Audio</span>
</button>
<div id="audioPickerChips" class="doc-picker-chips" aria-label="Selected audio file"></div>
<input type="hidden" id="audioPickerDocId" name="audio_doc_id" value="">
</div>
<!-- Hidden stubs so tools.js refs don't crash on this page -->
<div class="is-hidden" id="languageControl" aria-hidden="true">
<input type="radio" name="language" value="en" checked>
<input type="radio" name="language" value="no">
<input type="radio" name="language" value="uk">
<input type="radio" name="language" value="pl">
</div>
<div class="is-hidden" id="redactionControl" aria-hidden="true"></div>
<div class="is-hidden" id="uploadZone" aria-hidden="true">
<input type="file" id="uploadInput" style="display:none">
<div id="uploadPrompt"></div>
<div id="uploadFileInfo"><ul id="uploadFileList"></ul><button type="button" id="uploadClear"></button></div>
</div>
<div class="is-hidden" id="aliasSection" aria-hidden="true">
<button type="button" id="addAliasRow"></button>
<div id="aliasRows"></div>
</div>
<label class="is-hidden" id="inputLabel" for="toolInput"></label>
<textarea id="toolInput" name="toolInput" rows="1" class="is-hidden" aria-hidden="true"></textarea>
<div class="form-footer">
<p id="toolStatus" class="form-status" role="status" aria-live="polite"></p>
<button id="runButton" type="submit" data-i18n="run">Run</button>
</div>
</form>
<section id="results" class="results" aria-live="polite">
<div class="empty-state">
<h3 data-i18n="readyTitle">Ready</h3>
<p data-i18n="readyDesc">Select a tool, run a request, and the result appears here.</p>
</div>
</section>
<?php require_once __DIR__ . '/includes/layout_footer.php'; ?>