feat(transcribe): English UI default, language switcher (NO/UK/PL), fix 504 timeout
- Default UI language to English; lang switcher (EN/NO/UK/PL) persisted in localStorage - Rename 'rettssak/tingrett' preset to 'Mediation / legal meeting' — court recording is illegal - Add Ukrainian (uk) and Polish (pl) as selectable audio transcription languages - TRANSCRIBE_I18N translation object drives all status messages, labels, and trace text - Apache ProxyTimeout raised to 1800s on server (was 300s — caused 504 on large files) - set_time_limit(0) + ignore_user_abort(true) in api/transcribe.php - applyTranscribeI18n() patches data-i18n / data-i18n-placeholder / data-i18n-aria attrs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+51
-42
@@ -8,97 +8,106 @@ require_once __DIR__ . '/includes/layout.php';
|
||||
?>
|
||||
<form id="toolForm" class="tool-form">
|
||||
|
||||
<div class="lang-switcher" id="uiLangSwitcher" role="group" aria-label="UI language">
|
||||
<button type="button" class="lang-btn is-active" data-lang="en">🇬🇧 EN</button>
|
||||
<button type="button" class="lang-btn" data-lang="no">🇳🇴 NO</button>
|
||||
<button type="button" class="lang-btn" data-lang="uk">🇺🇦 UK</button>
|
||||
<button type="button" class="lang-btn" data-lang="pl">🇵🇱 PL</button>
|
||||
</div>
|
||||
|
||||
<div class="control-row" id="engineControl">
|
||||
<span class="control-label">Engine</span>
|
||||
<label><input type="radio" name="engine" value="gpu" checked id="engineGpu"> GPU (cuttlefish RTX 3060)</label>
|
||||
<label><input type="radio" name="engine" value="openai" id="engineOpenai"> OpenAI Whisper API</label>
|
||||
<label><input type="radio" name="engine" value="azure" id="engineAzure"> Azure AI Speech (nb-NO)</label>
|
||||
<span class="control-label" data-i18n="engine">Engine</span>
|
||||
<label><input type="radio" name="engine" value="gpu" checked id="engineGpu"> <span data-i18n="engineGpuLabel">GPU (cuttlefish RTX 3060)</span></label>
|
||||
<label><input type="radio" name="engine" value="openai" id="engineOpenai"> <span data-i18n="engineOpenaiLabel">OpenAI Whisper API</span></label>
|
||||
<label><input type="radio" name="engine" value="azure" id="engineAzure"> <span data-i18n="engineAzureLabel">Azure AI Speech (nb-NO)</span></label>
|
||||
</div>
|
||||
|
||||
<div class="control-row is-hidden" id="openaiKeyControl">
|
||||
<span class="control-label">API Key</span>
|
||||
<span class="control-label" data-i18n="apiKey">API Key</span>
|
||||
<input type="password" id="openaiKeyInput" name="openai_key" placeholder="sk-…" class="byok-input" autocomplete="off">
|
||||
<small class="control-hint inline-hint">Used for this request only, never stored. Max 25 MB.</small>
|
||||
<small class="control-hint inline-hint" data-i18n="apiKeyHint">Used for this request only, never stored. Max 25 MB.</small>
|
||||
</div>
|
||||
|
||||
<div class="control-row is-hidden" id="azureKeyControl">
|
||||
<span class="control-label">API Key</span>
|
||||
<span class="control-label" data-i18n="apiKey">API Key</span>
|
||||
<input type="password" id="azureKeyInput" name="azure_key" placeholder="Azure Speech key" class="byok-input" autocomplete="off">
|
||||
<span class="control-label" style="margin-left:1.25rem">Region</span>
|
||||
<span class="control-label" style="margin-left:1.25rem" data-i18n="region">Region</span>
|
||||
<input type="text" id="azureRegionInput" name="azure_region" placeholder="norwayeast" class="byok-input byok-input--short" value="norwayeast">
|
||||
</div>
|
||||
|
||||
<div class="control-row" id="modelControl">
|
||||
<span class="control-label">Model</span>
|
||||
<label><input type="radio" name="model" value="small"> Raskest <small class="control-hint">(small)</small></label>
|
||||
<label><input type="radio" name="model" value="medium"> Balansert <small class="control-hint">(medium)</small></label>
|
||||
<label><input type="radio" name="model" value="large-v3" checked> Beste kvalitet ★ <small class="control-hint">(large-v3)</small></label>
|
||||
<span class="control-label" data-i18n="model">Model</span>
|
||||
<label><input type="radio" name="model" value="small"> <span data-i18n="modelFastest">Fastest</span> <small class="control-hint">(small)</small></label>
|
||||
<label><input type="radio" name="model" value="medium"> <span data-i18n="modelBalanced">Balanced</span> <small class="control-hint">(medium)</small></label>
|
||||
<label><input type="radio" name="model" value="large-v3" checked> <span data-i18n="modelBest">Best quality</span> ★ <small class="control-hint">(large-v3)</small></label>
|
||||
</div>
|
||||
|
||||
<div class="control-row" id="transcribeLangControl">
|
||||
<span class="control-label">Språk</span>
|
||||
<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"> Українська</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çais</label>
|
||||
<label><input type="radio" name="transcribeLang" value="auto"> Auto-detect <small class="control-hint">(kan forveksle nb/da/sv)</small></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">Talere</span>
|
||||
<label><input type="checkbox" id="diarizeCheck" name="diarize"> Skill ut talere</label>
|
||||
<span class="control-label" style="margin-left:1.25rem">Antall</span>
|
||||
<input type="number" id="numSpeakersInput" name="num_speakers" min="2" max="20" placeholder="auto" class="num-speakers-input" aria-label="Forventet antall talere">
|
||||
<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">Ordliste</span>
|
||||
<button type="button" class="vocab-btn" data-preset="barnerett">Barnerett / CPS</button>
|
||||
<button type="button" class="vocab-btn" data-preset="rettssak">Rettssak / tingrett</button>
|
||||
<button type="button" class="vocab-btn" data-preset="generell">Generell norsk</button>
|
||||
<button type="button" class="vocab-btn" data-preset="custom">Egendefinert</button>
|
||||
<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="Fagord og navn Whisper skal gjenkjenne, f.eks. Barnevernet, Fylkesnemnda, advokat, tingrett…" class="prompt-textarea"></textarea>
|
||||
<p class="upload-hint">Hjelper Whisper gjenkjenne fagtermer. Ikke inkludert i utskriften.</p>
|
||||
<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>
|
||||
<p class="upload-hint" data-i18n="vocabHint">Helps Whisper recognise technical terms. Not included in the transcript.</p>
|
||||
</div>
|
||||
|
||||
<div class="upload-zone" id="audioZone" role="region" aria-label="Audio upload">
|
||||
<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">▶</span>
|
||||
<p>Slipp lydfil(er) her, eller <label for="audioInput" class="upload-browse">bla</label></p>
|
||||
<p class="upload-hint"><strong>MP3</strong>, <strong>WAV</strong>, <strong>OGG</strong>, <strong>M4A</strong>, <strong>FLAC</strong>, <strong>WEBM</strong> — maks 200 MB per fil</p>
|
||||
<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> — <span data-i18n="uploadHint">max 200 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">+ Legg til filer</label>
|
||||
<button type="button" id="audioClear" class="upload-clear" aria-label="Tøm kø">× Tøm kø</button>
|
||||
<label for="audioInput" class="upload-browse" data-i18n="uploadAddFiles">+ Add files</label>
|
||||
<button type="button" id="audioClear" class="upload-clear" data-i18n="uploadClearQueue">× Clear queue</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details class="expert-settings" id="expertSettings">
|
||||
<summary class="expert-summary">Ekspertinnstillinger</summary>
|
||||
<summary class="expert-summary" data-i18n="expertSettings">Advanced settings</summary>
|
||||
<div class="expert-body">
|
||||
<div class="control-row">
|
||||
<span class="control-label">Oppgave</span>
|
||||
<label><input type="radio" name="task" value="transcribe" checked> Transkriber</label>
|
||||
<label><input type="radio" name="task" value="translate"> Oversett til engelsk</label>
|
||||
<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">Beam size</span>
|
||||
<label><input type="radio" name="beam_size" value="1"> 1 <small class="control-hint">(raskest)</small></label>
|
||||
<span class="control-label" data-i18n="beamSize">Beam size</span>
|
||||
<label><input type="radio" name="beam_size" value="1"> 1 <small class="control-hint" data-i18n="beamFastest">(fastest)</small></label>
|
||||
<label><input type="radio" name="beam_size" value="3"> 3</label>
|
||||
<label><input type="radio" name="beam_size" value="5" checked> 5 <small class="control-hint">(best)</small></label>
|
||||
<label><input type="radio" name="beam_size" value="5" checked> 5 <small class="control-hint" data-i18n="beamBest">(best)</small></label>
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<span class="control-label">VAD-filter</span>
|
||||
<label><input type="checkbox" name="vad_filter" id="vadFilterCheck" value="1" checked> Fjern stillhet</label>
|
||||
<span class="control-label" data-i18n="vadFilter">VAD filter</span>
|
||||
<label><input type="checkbox" name="vad_filter" id="vadFilterCheck" value="1" checked> <span data-i18n="vadFilterLabel">Remove silence</span></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
@@ -120,14 +129,14 @@ require_once __DIR__ . '/includes/layout.php';
|
||||
|
||||
<div class="form-footer">
|
||||
<p id="toolStatus" class="form-status" role="status" aria-live="polite"></p>
|
||||
<button id="runButton" type="submit">Kjør</button>
|
||||
<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>Klar</h3>
|
||||
<p>Velg et verktøy, kjør en forespørsel, og svaret vises her.</p>
|
||||
<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'; ?>
|
||||
|
||||
Reference in New Issue
Block a user