Introduces the new Studio section (Source -> Characters -> Voices -> Perform & Export) that reuses the existing Read Aloud/Library/Script Rehearsal code via DOM reparenting instead of duplicating it, and rolls up a long tail of bugs found while producing a real audiobook through it: umlaut-eating name sanitizers, a voice picker that mispositioned itself and capped results at 60, PDF pagination silently breaking on trimmed \f markers, a race letting stale audio keep playing after a new line was clicked, an alias-overlap bug that could silently redirect a voice/image save onto the wrong character, voice design failing outright during brief TTS backend restarts instead of retrying, sparse cast entries defaulting to English/wrong gender, and a reassigned voice never reaching an already-open Stage session or invalidating its cached audio. Also adds a persistent per-line audio cache, audiobook export browsing/download, and an inline voice-design prompt editor. Full details in CHANGELOG.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
484 lines
36 KiB
JavaScript
484 lines
36 KiB
JavaScript
// ── Lightweight i18n ─────────────────────────────────────────────────────────
|
||
// English is the source language (keys = the exact English UI text). Switching
|
||
// language persists the choice and reloads, so translations are applied once to the
|
||
// freshly-rendered English DOM (no fragile reverse-translation / re-render tracking).
|
||
//
|
||
// Coverage v1: the static UI chrome (nav, section titles/subtitles, common
|
||
// buttons/labels). Add more by adding "English": "translation" pairs below, or call
|
||
// window.t('English') for JS-generated strings. Dynamic lists can be translated by
|
||
// calling window.applyI18n(container) after rendering.
|
||
|
||
const I18N_LANGS = {
|
||
en: 'English', de: 'Deutsch', fr: 'Français', es: 'Español',
|
||
it: 'Italiano', pt: 'Português', nl: 'Nederlands', pl: 'Polski',
|
||
};
|
||
|
||
// Every language block below covers the exact same key set — the English UI
|
||
// chrome strings (nav, section titles/subtitles, common buttons/labels).
|
||
// Adding a new key? Add it to ALL blocks, or it silently falls back to
|
||
// English for languages that don't have it yet (see window.t / applyI18n).
|
||
const I18N_DICT = {
|
||
de: {
|
||
// Brand / sidebar groups
|
||
'Voice Creator': 'Voice Creator',
|
||
'Clone · Design · Deploy': 'Klonen · Entwerfen · Bereitstellen',
|
||
'Voices': 'Stimmen', 'Setup': 'Einrichtung', 'Tags': 'Tags',
|
||
// Sidebar nav
|
||
'My Voices': 'Meine Stimmen', 'All voices': 'Alle Stimmen', 'Cloned': 'Geklont',
|
||
'Designed': 'Entworfen', 'Favorites': 'Favoriten', 'Hidden': 'Ausgeblendet',
|
||
'Library tools': 'Bibliothekswerkzeuge',
|
||
'Clone a Voice': 'Stimme klonen', 'Design a Voice': 'Stimme entwerfen',
|
||
'Get Voices Online': 'Stimmen online holen', 'Try It Out': 'Ausprobieren',
|
||
'Read Aloud': 'Vorlesen',
|
||
'Script Rehearser': 'Skript-Probe', 'Library': 'Bibliothek', 'Cast': 'Besetzung',
|
||
'Stage': 'Bühne', 'Summary': 'Zusammenfassung', 'Import / Export': 'Import / Export',
|
||
'Conversation': 'Unterhaltung', 'Benchmark': 'Benchmark', 'Engines': 'Engines',
|
||
'Language Models': 'Sprachmodelle', 'Speech to Text': 'Sprache-zu-Text',
|
||
'Text to Speech': 'Text-zu-Sprache', 'App Routing': 'App-Routing',
|
||
'Connect Apps': 'Apps verbinden', 'Settings': 'Einstellungen',
|
||
// Section titles
|
||
'Conversation Playground': 'Unterhaltungs-Spielplatz',
|
||
// Section subtitles
|
||
'Pick a voice on the left, edit on the right.': 'Wähle links eine Stimme, bearbeite sie rechts.',
|
||
'Capture 3–20 seconds of clean speech, trim it, name it, then save it as a reusable voice clone.':
|
||
'Nimm 3–20 Sekunden saubere Sprache auf, schneide sie zu, benenne sie und speichere sie als wiederverwendbaren Stimmklon.',
|
||
'Describe a voice in words and let the AI create it. No recording needed.':
|
||
'Beschreibe eine Stimme in Worten und lass sie von der KI erstellen. Keine Aufnahme nötig.',
|
||
'Browse public voice clip sources, preview direct audio files, and import voices from the web.':
|
||
'Durchsuche öffentliche Stimm-Quellen, höre Audiodateien vorab an und importiere Stimmen aus dem Web.',
|
||
'Generate speech from text using any backend and voice. Also transcribe audio and re-speak it.':
|
||
'Erzeuge Sprache aus Text mit beliebigem Backend und Stimme. Transkribiere Audio und sprich es neu ein.',
|
||
'Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.':
|
||
'Lade ein Skript hoch, besetze Figuren mit TTS-Stimmen oder deinem Mikrofon und probe Szene für Szene.',
|
||
'Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.':
|
||
'Importiere ein PDF- oder Textdokument, wähle Stimme und Tempo und lass es dir vorlesen, während das gerade gesprochene Wort hervorgehoben wird.',
|
||
// Read Aloud UI
|
||
'My books': 'Meine Bücher', 'Voice consistency': 'Stimmkonstanz',
|
||
'Normalise loudness': 'Lautstärke angleichen', 'Export MP3': 'MP3 exportieren',
|
||
'Select range': 'Bereich wählen', 'Synthesise': 'Synthetisieren',
|
||
'Tone / style': 'Ton / Stil', 'Temperature': 'Temperatur', 'Speed': 'Tempo',
|
||
'Saved documents with their synthesised audio — reopen to continue.':
|
||
'Gespeicherte Dokumente mit ihrem synthetisierten Audio — zum Fortsetzen erneut öffnen.',
|
||
// Common buttons / labels
|
||
'Save changes': 'Änderungen speichern', 'Save to library': 'In Bibliothek speichern',
|
||
'Save to Voice Library': 'In Stimmbibliothek speichern', 'Delete voice': 'Stimme löschen',
|
||
'Cancel': 'Abbrechen', 'Refresh': 'Aktualisieren', 'Delete': 'Löschen', 'Save': 'Speichern',
|
||
'Back': 'Zurück', 'Apply →': 'Anwenden →', 'Browse': 'Durchsuchen', 'Download': 'Herunterladen',
|
||
'Record': 'Aufnehmen', 'Stop': 'Stopp', 'Play': 'Abspielen', 'Play selection': 'Auswahl abspielen',
|
||
'Check level': 'Pegel prüfen', 'Stop monitor': 'Monitor stoppen', 'Auto trim': 'Auto-Zuschnitt',
|
||
'Auto-transcribe': 'Auto-Transkription', 'Active': 'Aktiv', 'copy ID': 'ID kopieren',
|
||
'edit ID': 'ID bearbeiten', 'New voice': 'Neue Stimme', 'All languages': 'Alle Sprachen',
|
||
'All genders': 'Alle Geschlechter', 'Microphone': 'Mikrofon', 'Upload file': 'Datei hochladen',
|
||
'Sort': 'Sortieren', 'Cards': 'Karten', 'List': 'Liste', 'Develop': 'Ausarbeiten',
|
||
'Match local': 'Lokal zuordnen', 'Match online': 'Online zuordnen', 'Design all': 'Alle entwerfen',
|
||
'I play this': 'Ich spiele das',
|
||
'Open rehearsal': 'Probe öffnen', 'Fetch voices': 'Stimmen abrufen',
|
||
'Name your voice': 'Benenne deine Stimme', 'Reference transcript': 'Referenz-Transkript',
|
||
'Preview': 'Vorschau', 'Trim your sample': 'Probe zuschneiden', 'Language': 'Sprache',
|
||
'Gender': 'Geschlecht', 'Voice': 'Stimme', 'Speaking style · voice-design prompt': 'Sprechstil · Voice-Design-Prompt',
|
||
// Common placeholders
|
||
'Search voices…': 'Stimmen suchen…', 'Search voices...': 'Stimmen suchen...',
|
||
'Filter name or tag…': 'Name oder Tag filtern…',
|
||
},
|
||
|
||
fr: {
|
||
'Voice Creator': 'Voice Creator',
|
||
'Clone · Design · Deploy': 'Cloner · Concevoir · Déployer',
|
||
'Voices': 'Voix', 'Setup': 'Configuration', 'Tags': 'Étiquettes',
|
||
'My Voices': 'Mes voix', 'All voices': 'Toutes les voix', 'Cloned': 'Clonées',
|
||
'Designed': 'Conçues', 'Favorites': 'Favoris', 'Hidden': 'Masquées',
|
||
'Library tools': 'Outils de bibliothèque',
|
||
'Clone a Voice': 'Cloner une voix', 'Design a Voice': 'Concevoir une voix',
|
||
'Get Voices Online': 'Obtenir des voix en ligne', 'Try It Out': 'Essayer',
|
||
'Read Aloud': 'Lecture à voix haute',
|
||
'Script Rehearser': 'Répétition de script', 'Library': 'Bibliothèque', 'Cast': 'Distribution',
|
||
'Stage': 'Scène', 'Summary': 'Résumé', 'Import / Export': 'Import / Export',
|
||
'Conversation': 'Conversation', 'Benchmark': 'Benchmark', 'Engines': 'Moteurs',
|
||
'Language Models': 'Modèles de langage', 'Speech to Text': 'Voix vers texte',
|
||
'Text to Speech': 'Texte vers voix', 'App Routing': "Routage de l'app",
|
||
'Connect Apps': 'Connecter des apps', 'Settings': 'Paramètres',
|
||
'Conversation Playground': 'Terrain de jeu conversation',
|
||
'Pick a voice on the left, edit on the right.': 'Choisissez une voix à gauche, modifiez-la à droite.',
|
||
'Capture 3–20 seconds of clean speech, trim it, name it, then save it as a reusable voice clone.':
|
||
"Enregistrez 3 à 20 secondes de parole claire, découpez-la, nommez-la, puis enregistrez-la comme clone de voix réutilisable.",
|
||
'Describe a voice in words and let the AI create it. No recording needed.':
|
||
"Décrivez une voix avec des mots et laissez l'IA la créer. Aucun enregistrement nécessaire.",
|
||
'Browse public voice clip sources, preview direct audio files, and import voices from the web.':
|
||
"Parcourez des sources publiques d'extraits vocaux, prévisualisez des fichiers audio et importez des voix depuis le web.",
|
||
'Generate speech from text using any backend and voice. Also transcribe audio and re-speak it.':
|
||
"Générez de la parole à partir de texte avec n'importe quel moteur et voix. Transcrivez aussi l'audio et refaites-le parler.",
|
||
'Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.':
|
||
'Téléversez un script, attribuez des voix TTS ou votre micro aux personnages, puis répétez scène par scène.',
|
||
'Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.':
|
||
"Importez un PDF ou un document texte, choisissez une voix et une vitesse, puis faites-le lire à voix haute avec surlignage du mot prononcé.",
|
||
'My books': 'Mes livres', 'Voice consistency': 'Cohérence de la voix',
|
||
'Normalise loudness': 'Normaliser le volume', 'Export MP3': 'Exporter en MP3',
|
||
'Select range': 'Sélectionner une plage', 'Synthesise': 'Synthétiser',
|
||
'Tone / style': 'Ton / style', 'Temperature': 'Température', 'Speed': 'Vitesse',
|
||
'Saved documents with their synthesised audio — reopen to continue.':
|
||
'Documents enregistrés avec leur audio synthétisé — rouvrez pour continuer.',
|
||
'Save changes': 'Enregistrer les modifications', 'Save to library': 'Enregistrer dans la bibliothèque',
|
||
'Save to Voice Library': 'Enregistrer dans la bibliothèque de voix', 'Delete voice': 'Supprimer la voix',
|
||
'Cancel': 'Annuler', 'Refresh': 'Actualiser', 'Delete': 'Supprimer', 'Save': 'Enregistrer',
|
||
'Back': 'Retour', 'Apply →': 'Appliquer →', 'Browse': 'Parcourir', 'Download': 'Télécharger',
|
||
'Record': 'Enregistrer', 'Stop': 'Arrêter', 'Play': 'Lire', 'Play selection': 'Lire la sélection',
|
||
'Check level': 'Vérifier le niveau', 'Stop monitor': 'Arrêter le moniteur', 'Auto trim': 'Découpe auto',
|
||
'Auto-transcribe': 'Transcription auto', 'Active': 'Actif', 'copy ID': "copier l'ID",
|
||
'edit ID': "modifier l'ID", 'New voice': 'Nouvelle voix', 'All languages': 'Toutes les langues',
|
||
'All genders': 'Tous les genres', 'Microphone': 'Microphone', 'Upload file': 'Téléverser un fichier',
|
||
'Sort': 'Trier', 'Cards': 'Cartes', 'List': 'Liste', 'Develop': 'Développer',
|
||
'Match local': 'Correspondance locale', 'Match online': 'Correspondance en ligne', 'Design all': 'Tout concevoir',
|
||
'I play this': 'Je joue ce personnage',
|
||
'Open rehearsal': 'Ouvrir la répétition', 'Fetch voices': 'Récupérer les voix',
|
||
'Name your voice': 'Nommez votre voix', 'Reference transcript': 'Transcription de référence',
|
||
'Preview': 'Aperçu', 'Trim your sample': 'Découpez votre échantillon', 'Language': 'Langue',
|
||
'Gender': 'Genre', 'Voice': 'Voix', 'Speaking style · voice-design prompt': "Style d'élocution · invite de conception vocale",
|
||
'Search voices…': 'Rechercher des voix…', 'Search voices...': 'Rechercher des voix...',
|
||
'Filter name or tag…': 'Filtrer par nom ou étiquette…',
|
||
},
|
||
|
||
es: {
|
||
'Voice Creator': 'Voice Creator',
|
||
'Clone · Design · Deploy': 'Clonar · Diseñar · Implementar',
|
||
'Voices': 'Voces', 'Setup': 'Configuración', 'Tags': 'Etiquetas',
|
||
'My Voices': 'Mis voces', 'All voices': 'Todas las voces', 'Cloned': 'Clonadas',
|
||
'Designed': 'Diseñadas', 'Favorites': 'Favoritos', 'Hidden': 'Ocultas',
|
||
'Library tools': 'Herramientas de biblioteca',
|
||
'Clone a Voice': 'Clonar una voz', 'Design a Voice': 'Diseñar una voz',
|
||
'Get Voices Online': 'Obtener voces en línea', 'Try It Out': 'Probarlo',
|
||
'Read Aloud': 'Leer en voz alta',
|
||
'Script Rehearser': 'Ensayo de guion', 'Library': 'Biblioteca', 'Cast': 'Reparto',
|
||
'Stage': 'Escenario', 'Summary': 'Resumen', 'Import / Export': 'Importar / Exportar',
|
||
'Conversation': 'Conversación', 'Benchmark': 'Benchmark', 'Engines': 'Motores',
|
||
'Language Models': 'Modelos de lenguaje', 'Speech to Text': 'Voz a texto',
|
||
'Text to Speech': 'Texto a voz', 'App Routing': 'Enrutamiento de la app',
|
||
'Connect Apps': 'Conectar apps', 'Settings': 'Ajustes',
|
||
'Conversation Playground': 'Zona de pruebas de conversación',
|
||
'Pick a voice on the left, edit on the right.': 'Elige una voz a la izquierda, edítala a la derecha.',
|
||
'Capture 3–20 seconds of clean speech, trim it, name it, then save it as a reusable voice clone.':
|
||
'Graba de 3 a 20 segundos de habla limpia, recórtala, nómbrala y guárdala como un clon de voz reutilizable.',
|
||
'Describe a voice in words and let the AI create it. No recording needed.':
|
||
'Describe una voz con palabras y deja que la IA la cree. No se necesita grabación.',
|
||
'Browse public voice clip sources, preview direct audio files, and import voices from the web.':
|
||
'Explora fuentes públicas de clips de voz, previsualiza archivos de audio e importa voces desde la web.',
|
||
'Generate speech from text using any backend and voice. Also transcribe audio and re-speak it.':
|
||
'Genera voz a partir de texto con cualquier motor y voz. También transcribe audio y vuelve a reproducirlo hablado.',
|
||
'Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.':
|
||
'Sube un guion, asigna voces TTS o tu propio micrófono a los personajes y ensaya escena por escena.',
|
||
'Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.':
|
||
'Importa un PDF o documento de texto, elige una voz y velocidad, y escúchalo mientras se resalta la palabra que se está leyendo.',
|
||
'My books': 'Mis libros', 'Voice consistency': 'Consistencia de voz',
|
||
'Normalise loudness': 'Normalizar volumen', 'Export MP3': 'Exportar MP3',
|
||
'Select range': 'Seleccionar rango', 'Synthesise': 'Sintetizar',
|
||
'Tone / style': 'Tono / estilo', 'Temperature': 'Temperatura', 'Speed': 'Velocidad',
|
||
'Saved documents with their synthesised audio — reopen to continue.':
|
||
'Documentos guardados con su audio sintetizado — vuelve a abrirlos para continuar.',
|
||
'Save changes': 'Guardar cambios', 'Save to library': 'Guardar en la biblioteca',
|
||
'Save to Voice Library': 'Guardar en la biblioteca de voces', 'Delete voice': 'Eliminar voz',
|
||
'Cancel': 'Cancelar', 'Refresh': 'Actualizar', 'Delete': 'Eliminar', 'Save': 'Guardar',
|
||
'Back': 'Atrás', 'Apply →': 'Aplicar →', 'Browse': 'Explorar', 'Download': 'Descargar',
|
||
'Record': 'Grabar', 'Stop': 'Detener', 'Play': 'Reproducir', 'Play selection': 'Reproducir selección',
|
||
'Check level': 'Comprobar nivel', 'Stop monitor': 'Detener monitor', 'Auto trim': 'Recorte automático',
|
||
'Auto-transcribe': 'Transcripción automática', 'Active': 'Activo', 'copy ID': 'copiar ID',
|
||
'edit ID': 'editar ID', 'New voice': 'Nueva voz', 'All languages': 'Todos los idiomas',
|
||
'All genders': 'Todos los géneros', 'Microphone': 'Micrófono', 'Upload file': 'Subir archivo',
|
||
'Sort': 'Ordenar', 'Cards': 'Tarjetas', 'List': 'Lista', 'Develop': 'Desarrollar',
|
||
'Match local': 'Coincidencia local', 'Match online': 'Coincidencia en línea', 'Design all': 'Diseñar todo',
|
||
'I play this': 'Yo interpreto esto',
|
||
'Open rehearsal': 'Abrir ensayo', 'Fetch voices': 'Obtener voces',
|
||
'Name your voice': 'Nombra tu voz', 'Reference transcript': 'Transcripción de referencia',
|
||
'Preview': 'Vista previa', 'Trim your sample': 'Recorta tu muestra', 'Language': 'Idioma',
|
||
'Gender': 'Género', 'Voice': 'Voz', 'Speaking style · voice-design prompt': 'Estilo de habla · prompt de diseño de voz',
|
||
'Search voices…': 'Buscar voces…', 'Search voices...': 'Buscar voces...',
|
||
'Filter name or tag…': 'Filtrar por nombre o etiqueta…',
|
||
},
|
||
|
||
it: {
|
||
'Voice Creator': 'Voice Creator',
|
||
'Clone · Design · Deploy': 'Clona · Progetta · Distribuisci',
|
||
'Voices': 'Voci', 'Setup': 'Configurazione', 'Tags': 'Tag',
|
||
'My Voices': 'Le mie voci', 'All voices': 'Tutte le voci', 'Cloned': 'Clonate',
|
||
'Designed': 'Progettate', 'Favorites': 'Preferiti', 'Hidden': 'Nascoste',
|
||
'Library tools': 'Strumenti libreria',
|
||
'Clone a Voice': 'Clona una voce', 'Design a Voice': 'Progetta una voce',
|
||
'Get Voices Online': 'Ottieni voci online', 'Try It Out': 'Prova',
|
||
'Read Aloud': 'Leggi ad alta voce',
|
||
'Script Rehearser': 'Prova script', 'Library': 'Libreria', 'Cast': 'Cast',
|
||
'Stage': 'Palco', 'Summary': 'Riepilogo', 'Import / Export': 'Importa / Esporta',
|
||
'Conversation': 'Conversazione', 'Benchmark': 'Benchmark', 'Engines': 'Motori',
|
||
'Language Models': 'Modelli linguistici', 'Speech to Text': 'Voce in testo',
|
||
'Text to Speech': 'Testo in voce', 'App Routing': 'Instradamento app',
|
||
'Connect Apps': 'Connetti app', 'Settings': 'Impostazioni',
|
||
'Conversation Playground': 'Area di prova conversazione',
|
||
'Pick a voice on the left, edit on the right.': 'Scegli una voce a sinistra, modificala a destra.',
|
||
'Capture 3–20 seconds of clean speech, trim it, name it, then save it as a reusable voice clone.':
|
||
'Registra 3-20 secondi di voce pulita, taglia, assegna un nome e salva come clone vocale riutilizzabile.',
|
||
'Describe a voice in words and let the AI create it. No recording needed.':
|
||
"Descrivi una voce a parole e lascia che l'IA la crei. Nessuna registrazione necessaria.",
|
||
'Browse public voice clip sources, preview direct audio files, and import voices from the web.':
|
||
'Sfoglia fonti pubbliche di clip vocali, anteprima file audio e importa voci dal web.',
|
||
'Generate speech from text using any backend and voice. Also transcribe audio and re-speak it.':
|
||
"Genera voce dal testo con qualsiasi backend e voce. Trascrivi anche l'audio e falla riparlare.",
|
||
'Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.':
|
||
'Carica uno script, assegna voci TTS o il tuo microfono ai personaggi, poi prova scena per scena.',
|
||
'Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.':
|
||
"Importa un PDF o un documento di testo, scegli voce e velocità, e fallo leggere ad alta voce con evidenziazione della parola pronunciata.",
|
||
'My books': 'I miei libri', 'Voice consistency': 'Coerenza vocale',
|
||
'Normalise loudness': 'Normalizza volume', 'Export MP3': 'Esporta MP3',
|
||
'Select range': 'Seleziona intervallo', 'Synthesise': 'Sintetizza',
|
||
'Tone / style': 'Tono / stile', 'Temperature': 'Temperatura', 'Speed': 'Velocità',
|
||
'Saved documents with their synthesised audio — reopen to continue.':
|
||
'Documenti salvati con il loro audio sintetizzato — riaprili per continuare.',
|
||
'Save changes': 'Salva modifiche', 'Save to library': 'Salva nella libreria',
|
||
'Save to Voice Library': 'Salva nella libreria vocale', 'Delete voice': 'Elimina voce',
|
||
'Cancel': 'Annulla', 'Refresh': 'Aggiorna', 'Delete': 'Elimina', 'Save': 'Salva',
|
||
'Back': 'Indietro', 'Apply →': 'Applica →', 'Browse': 'Sfoglia', 'Download': 'Scarica',
|
||
'Record': 'Registra', 'Stop': 'Ferma', 'Play': 'Riproduci', 'Play selection': 'Riproduci selezione',
|
||
'Check level': 'Controlla livello', 'Stop monitor': 'Ferma monitor', 'Auto trim': 'Taglio automatico',
|
||
'Auto-transcribe': 'Trascrizione automatica', 'Active': 'Attivo', 'copy ID': 'copia ID',
|
||
'edit ID': 'modifica ID', 'New voice': 'Nuova voce', 'All languages': 'Tutte le lingue',
|
||
'All genders': 'Tutti i generi', 'Microphone': 'Microfono', 'Upload file': 'Carica file',
|
||
'Sort': 'Ordina', 'Cards': 'Schede', 'List': 'Elenco', 'Develop': 'Sviluppa',
|
||
'Match local': 'Abbinamento locale', 'Match online': 'Abbinamento online', 'Design all': 'Progetta tutto',
|
||
'I play this': 'Interpreto io questo',
|
||
'Open rehearsal': 'Apri prova', 'Fetch voices': 'Recupera voci',
|
||
'Name your voice': 'Assegna un nome alla voce', 'Reference transcript': 'Trascrizione di riferimento',
|
||
'Preview': 'Anteprima', 'Trim your sample': 'Taglia il tuo campione', 'Language': 'Lingua',
|
||
'Gender': 'Genere', 'Voice': 'Voce', 'Speaking style · voice-design prompt': 'Stile di parlato · prompt di progettazione vocale',
|
||
'Search voices…': 'Cerca voci…', 'Search voices...': 'Cerca voci...',
|
||
'Filter name or tag…': 'Filtra per nome o tag…',
|
||
},
|
||
|
||
pt: {
|
||
'Voice Creator': 'Voice Creator',
|
||
'Clone · Design · Deploy': 'Clonar · Projetar · Implantar',
|
||
'Voices': 'Vozes', 'Setup': 'Configuração', 'Tags': 'Etiquetas',
|
||
'My Voices': 'Minhas vozes', 'All voices': 'Todas as vozes', 'Cloned': 'Clonadas',
|
||
'Designed': 'Projetadas', 'Favorites': 'Favoritos', 'Hidden': 'Ocultas',
|
||
'Library tools': 'Ferramentas da biblioteca',
|
||
'Clone a Voice': 'Clonar uma voz', 'Design a Voice': 'Projetar uma voz',
|
||
'Get Voices Online': 'Obter vozes online', 'Try It Out': 'Experimentar',
|
||
'Read Aloud': 'Leitura em voz alta',
|
||
'Script Rehearser': 'Ensaio de roteiro', 'Library': 'Biblioteca', 'Cast': 'Elenco',
|
||
'Stage': 'Palco', 'Summary': 'Resumo', 'Import / Export': 'Importar / Exportar',
|
||
'Conversation': 'Conversa', 'Benchmark': 'Benchmark', 'Engines': 'Motores',
|
||
'Language Models': 'Modelos de linguagem', 'Speech to Text': 'Voz para texto',
|
||
'Text to Speech': 'Texto para voz', 'App Routing': 'Roteamento do app',
|
||
'Connect Apps': 'Conectar apps', 'Settings': 'Configurações',
|
||
'Conversation Playground': 'Espaço de teste de conversa',
|
||
'Pick a voice on the left, edit on the right.': 'Escolha uma voz à esquerda, edite à direita.',
|
||
'Capture 3–20 seconds of clean speech, trim it, name it, then save it as a reusable voice clone.':
|
||
'Grave de 3 a 20 segundos de fala limpa, corte, nomeie e salve como um clone de voz reutilizável.',
|
||
'Describe a voice in words and let the AI create it. No recording needed.':
|
||
'Descreva uma voz com palavras e deixe a IA criá-la. Não é necessária gravação.',
|
||
'Browse public voice clip sources, preview direct audio files, and import voices from the web.':
|
||
'Navegue por fontes públicas de clipes de voz, pré-visualize arquivos de áudio e importe vozes da web.',
|
||
'Generate speech from text using any backend and voice. Also transcribe audio and re-speak it.':
|
||
'Gere fala a partir de texto com qualquer backend e voz. Também transcreva áudio e reproduza-o falado.',
|
||
'Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.':
|
||
'Envie um roteiro, atribua vozes TTS ou seu próprio microfone aos personagens e ensaie cena por cena.',
|
||
'Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.':
|
||
'Importe um PDF ou documento de texto, escolha uma voz e velocidade, e ouça-o enquanto a palavra falada é destacada.',
|
||
'My books': 'Meus livros', 'Voice consistency': 'Consistência de voz',
|
||
'Normalise loudness': 'Normalizar volume', 'Export MP3': 'Exportar MP3',
|
||
'Select range': 'Selecionar intervalo', 'Synthesise': 'Sintetizar',
|
||
'Tone / style': 'Tom / estilo', 'Temperature': 'Temperatura', 'Speed': 'Velocidade',
|
||
'Saved documents with their synthesised audio — reopen to continue.':
|
||
'Documentos salvos com seu áudio sintetizado — reabra para continuar.',
|
||
'Save changes': 'Salvar alterações', 'Save to library': 'Salvar na biblioteca',
|
||
'Save to Voice Library': 'Salvar na biblioteca de vozes', 'Delete voice': 'Excluir voz',
|
||
'Cancel': 'Cancelar', 'Refresh': 'Atualizar', 'Delete': 'Excluir', 'Save': 'Salvar',
|
||
'Back': 'Voltar', 'Apply →': 'Aplicar →', 'Browse': 'Procurar', 'Download': 'Baixar',
|
||
'Record': 'Gravar', 'Stop': 'Parar', 'Play': 'Reproduzir', 'Play selection': 'Reproduzir seleção',
|
||
'Check level': 'Verificar nível', 'Stop monitor': 'Parar monitor', 'Auto trim': 'Corte automático',
|
||
'Auto-transcribe': 'Transcrição automática', 'Active': 'Ativo', 'copy ID': 'copiar ID',
|
||
'edit ID': 'editar ID', 'New voice': 'Nova voz', 'All languages': 'Todos os idiomas',
|
||
'All genders': 'Todos os gêneros', 'Microphone': 'Microfone', 'Upload file': 'Enviar arquivo',
|
||
'Sort': 'Ordenar', 'Cards': 'Cartões', 'List': 'Lista', 'Develop': 'Desenvolver',
|
||
'Match local': 'Correspondência local', 'Match online': 'Correspondência online', 'Design all': 'Projetar tudo',
|
||
'I play this': 'Eu interpreto isto',
|
||
'Open rehearsal': 'Abrir ensaio', 'Fetch voices': 'Buscar vozes',
|
||
'Name your voice': 'Nomeie sua voz', 'Reference transcript': 'Transcrição de referência',
|
||
'Preview': 'Pré-visualização', 'Trim your sample': 'Corte sua amostra', 'Language': 'Idioma',
|
||
'Gender': 'Gênero', 'Voice': 'Voz', 'Speaking style · voice-design prompt': 'Estilo de fala · prompt de design de voz',
|
||
'Search voices…': 'Buscar vozes…', 'Search voices...': 'Buscar vozes...',
|
||
'Filter name or tag…': 'Filtrar por nome ou etiqueta…',
|
||
},
|
||
|
||
nl: {
|
||
'Voice Creator': 'Voice Creator',
|
||
'Clone · Design · Deploy': 'Klonen · Ontwerpen · Implementeren',
|
||
'Voices': 'Stemmen', 'Setup': 'Instellingen', 'Tags': 'Tags',
|
||
'My Voices': 'Mijn stemmen', 'All voices': 'Alle stemmen', 'Cloned': 'Gekloond',
|
||
'Designed': 'Ontworpen', 'Favorites': 'Favorieten', 'Hidden': 'Verborgen',
|
||
'Library tools': 'Bibliotheektools',
|
||
'Clone a Voice': 'Een stem klonen', 'Design a Voice': 'Een stem ontwerpen',
|
||
'Get Voices Online': 'Stemmen online ophalen', 'Try It Out': 'Uitproberen',
|
||
'Read Aloud': 'Voorlezen',
|
||
'Script Rehearser': 'Scriptrepetitie', 'Library': 'Bibliotheek', 'Cast': 'Cast',
|
||
'Stage': 'Podium', 'Summary': 'Samenvatting', 'Import / Export': 'Importeren / Exporteren',
|
||
'Conversation': 'Gesprek', 'Benchmark': 'Benchmark', 'Engines': 'Engines',
|
||
'Language Models': 'Taalmodellen', 'Speech to Text': 'Spraak naar tekst',
|
||
'Text to Speech': 'Tekst naar spraak', 'App Routing': 'App-routering',
|
||
'Connect Apps': 'Apps verbinden', 'Settings': 'Instellingen',
|
||
'Conversation Playground': 'Gesprek-speeltuin',
|
||
'Pick a voice on the left, edit on the right.': 'Kies links een stem, bewerk rechts.',
|
||
'Capture 3–20 seconds of clean speech, trim it, name it, then save it as a reusable voice clone.':
|
||
'Neem 3–20 seconden heldere spraak op, knip bij, geef een naam en sla op als herbruikbare stemkloon.',
|
||
'Describe a voice in words and let the AI create it. No recording needed.':
|
||
'Beschrijf een stem in woorden en laat de AI hem maken. Geen opname nodig.',
|
||
'Browse public voice clip sources, preview direct audio files, and import voices from the web.':
|
||
'Blader door openbare stemclipbronnen, bekijk audiobestanden vooraf en importeer stemmen van het web.',
|
||
'Generate speech from text using any backend and voice. Also transcribe audio and re-speak it.':
|
||
'Genereer spraak uit tekst met elke backend en stem. Transcribeer ook audio en spreek het opnieuw uit.',
|
||
'Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.':
|
||
'Upload een script, wijs TTS-stemmen of je eigen microfoon toe aan personages en repeteer scène voor scène.',
|
||
'Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.':
|
||
'Importeer een PDF of tekstdocument, kies een stem en snelheid, en laat het voorlezen terwijl het gesproken woord wordt gemarkeerd.',
|
||
'My books': 'Mijn boeken', 'Voice consistency': 'Stemconsistentie',
|
||
'Normalise loudness': 'Volume normaliseren', 'Export MP3': 'MP3 exporteren',
|
||
'Select range': 'Bereik selecteren', 'Synthesise': 'Synthetiseren',
|
||
'Tone / style': 'Toon / stijl', 'Temperature': 'Temperatuur', 'Speed': 'Snelheid',
|
||
'Saved documents with their synthesised audio — reopen to continue.':
|
||
'Opgeslagen documenten met hun gesynthetiseerde audio — heropen om verder te gaan.',
|
||
'Save changes': 'Wijzigingen opslaan', 'Save to library': 'Opslaan in bibliotheek',
|
||
'Save to Voice Library': 'Opslaan in stembibliotheek', 'Delete voice': 'Stem verwijderen',
|
||
'Cancel': 'Annuleren', 'Refresh': 'Vernieuwen', 'Delete': 'Verwijderen', 'Save': 'Opslaan',
|
||
'Back': 'Terug', 'Apply →': 'Toepassen →', 'Browse': 'Bladeren', 'Download': 'Downloaden',
|
||
'Record': 'Opnemen', 'Stop': 'Stoppen', 'Play': 'Afspelen', 'Play selection': 'Selectie afspelen',
|
||
'Check level': 'Niveau controleren', 'Stop monitor': 'Monitor stoppen', 'Auto trim': 'Automatisch bijsnijden',
|
||
'Auto-transcribe': 'Automatisch transcriberen', 'Active': 'Actief', 'copy ID': 'ID kopiëren',
|
||
'edit ID': 'ID bewerken', 'New voice': 'Nieuwe stem', 'All languages': 'Alle talen',
|
||
'All genders': 'Alle geslachten', 'Microphone': 'Microfoon', 'Upload file': 'Bestand uploaden',
|
||
'Sort': 'Sorteren', 'Cards': 'Kaarten', 'List': 'Lijst', 'Develop': 'Ontwikkelen',
|
||
'Match local': 'Lokaal matchen', 'Match online': 'Online matchen', 'Design all': 'Alles ontwerpen',
|
||
'I play this': 'Ik speel dit',
|
||
'Open rehearsal': 'Repetitie openen', 'Fetch voices': 'Stemmen ophalen',
|
||
'Name your voice': 'Geef je stem een naam', 'Reference transcript': 'Referentietranscript',
|
||
'Preview': 'Voorbeeld', 'Trim your sample': 'Knip je sample bij', 'Language': 'Taal',
|
||
'Gender': 'Geslacht', 'Voice': 'Stem', 'Speaking style · voice-design prompt': 'Spreekstijl · stemontwerp-prompt',
|
||
'Search voices…': 'Stemmen zoeken…', 'Search voices...': 'Stemmen zoeken...',
|
||
'Filter name or tag…': 'Filter op naam of tag…',
|
||
},
|
||
|
||
pl: {
|
||
'Voice Creator': 'Voice Creator',
|
||
'Clone · Design · Deploy': 'Klonuj · Projektuj · Wdrażaj',
|
||
'Voices': 'Głosy', 'Setup': 'Konfiguracja', 'Tags': 'Tagi',
|
||
'My Voices': 'Moje głosy', 'All voices': 'Wszystkie głosy', 'Cloned': 'Sklonowane',
|
||
'Designed': 'Zaprojektowane', 'Favorites': 'Ulubione', 'Hidden': 'Ukryte',
|
||
'Library tools': 'Narzędzia biblioteki',
|
||
'Clone a Voice': 'Sklonuj głos', 'Design a Voice': 'Zaprojektuj głos',
|
||
'Get Voices Online': 'Pobierz głosy online', 'Try It Out': 'Wypróbuj',
|
||
'Read Aloud': 'Czytanie na głos',
|
||
'Script Rehearser': 'Próba scenariusza', 'Library': 'Biblioteka', 'Cast': 'Obsada',
|
||
'Stage': 'Scena', 'Summary': 'Podsumowanie', 'Import / Export': 'Import / Eksport',
|
||
'Conversation': 'Rozmowa', 'Benchmark': 'Benchmark', 'Engines': 'Silniki',
|
||
'Language Models': 'Modele językowe', 'Speech to Text': 'Mowa na tekst',
|
||
'Text to Speech': 'Tekst na mowę', 'App Routing': 'Routing aplikacji',
|
||
'Connect Apps': 'Połącz aplikacje', 'Settings': 'Ustawienia',
|
||
'Conversation Playground': 'Plac zabaw rozmów',
|
||
'Pick a voice on the left, edit on the right.': 'Wybierz głos po lewej, edytuj po prawej.',
|
||
'Capture 3–20 seconds of clean speech, trim it, name it, then save it as a reusable voice clone.':
|
||
'Nagraj 3–20 sekund czystej mowy, przytnij, nazwij, a następnie zapisz jako wielokrotnego użytku klon głosu.',
|
||
'Describe a voice in words and let the AI create it. No recording needed.':
|
||
'Opisz głos słowami i pozwól AI go stworzyć. Nagrywanie nie jest potrzebne.',
|
||
'Browse public voice clip sources, preview direct audio files, and import voices from the web.':
|
||
'Przeglądaj publiczne źródła klipów głosowych, podglądaj pliki audio i importuj głosy z sieci.',
|
||
'Generate speech from text using any backend and voice. Also transcribe audio and re-speak it.':
|
||
'Generuj mowę z tekstu za pomocą dowolnego silnika i głosu. Transkrybuj też audio i wypowiedz je ponownie.',
|
||
'Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.':
|
||
'Prześlij scenariusz, przypisz głosy TTS lub własny mikrofon do postaci, a następnie ćwicz scenę po scenie.',
|
||
'Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.':
|
||
'Zaimportuj PDF lub dokument tekstowy, wybierz głos i prędkość, a następnie posłuchaj czytania z podświetlaniem wypowiadanego słowa.',
|
||
'My books': 'Moje książki', 'Voice consistency': 'Spójność głosu',
|
||
'Normalise loudness': 'Normalizuj głośność', 'Export MP3': 'Eksportuj MP3',
|
||
'Select range': 'Wybierz zakres', 'Synthesise': 'Syntetyzuj',
|
||
'Tone / style': 'Ton / styl', 'Temperature': 'Temperatura', 'Speed': 'Prędkość',
|
||
'Saved documents with their synthesised audio — reopen to continue.':
|
||
'Zapisane dokumenty z zsyntetyzowanym audio — otwórz ponownie, aby kontynuować.',
|
||
'Save changes': 'Zapisz zmiany', 'Save to library': 'Zapisz w bibliotece',
|
||
'Save to Voice Library': 'Zapisz w bibliotece głosów', 'Delete voice': 'Usuń głos',
|
||
'Cancel': 'Anuluj', 'Refresh': 'Odśwież', 'Delete': 'Usuń', 'Save': 'Zapisz',
|
||
'Back': 'Wstecz', 'Apply →': 'Zastosuj →', 'Browse': 'Przeglądaj', 'Download': 'Pobierz',
|
||
'Record': 'Nagraj', 'Stop': 'Zatrzymaj', 'Play': 'Odtwórz', 'Play selection': 'Odtwórz zaznaczenie',
|
||
'Check level': 'Sprawdź poziom', 'Stop monitor': 'Zatrzymaj monitor', 'Auto trim': 'Automatyczne przycinanie',
|
||
'Auto-transcribe': 'Automatyczna transkrypcja', 'Active': 'Aktywny', 'copy ID': 'kopiuj ID',
|
||
'edit ID': 'edytuj ID', 'New voice': 'Nowy głos', 'All languages': 'Wszystkie języki',
|
||
'All genders': 'Wszystkie płcie', 'Microphone': 'Mikrofon', 'Upload file': 'Prześlij plik',
|
||
'Sort': 'Sortuj', 'Cards': 'Karty', 'List': 'Lista', 'Develop': 'Rozwiń',
|
||
'Match local': 'Dopasuj lokalnie', 'Match online': 'Dopasuj online', 'Design all': 'Zaprojektuj wszystko',
|
||
'I play this': 'Ja to gram',
|
||
'Open rehearsal': 'Otwórz próbę', 'Fetch voices': 'Pobierz głosy',
|
||
'Name your voice': 'Nazwij swój głos', 'Reference transcript': 'Transkrypcja referencyjna',
|
||
'Preview': 'Podgląd', 'Trim your sample': 'Przytnij próbkę', 'Language': 'Język',
|
||
'Gender': 'Płeć', 'Voice': 'Głos', 'Speaking style · voice-design prompt': 'Styl mówienia · prompt projektowania głosu',
|
||
'Search voices…': 'Szukaj głosów…', 'Search voices...': 'Szukaj głosów...',
|
||
'Filter name or tag…': 'Filtruj po nazwie lub tagu…',
|
||
},
|
||
};
|
||
|
||
let _appLang = 'en';
|
||
try { _appLang = localStorage.getItem('app-lang') || 'en'; } catch (_) {}
|
||
window.__APP_LANG__ = _appLang;
|
||
|
||
// Translate a single source string (for JS-generated UI).
|
||
window.t = function (s) {
|
||
if (_appLang === 'en' || s == null) return s;
|
||
const d = I18N_DICT[_appLang];
|
||
return (d && d[String(s).trim()] != null) ? d[String(s).trim()] : s;
|
||
};
|
||
|
||
const _I18N_SKIP = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'CODE', 'PRE', 'OPTION']);
|
||
|
||
// Translate visible text nodes + common attributes within `root` (default: whole doc).
|
||
window.applyI18n = function (root) {
|
||
if (_appLang === 'en') return;
|
||
const dict = I18N_DICT[_appLang]; if (!dict) return;
|
||
root = root || document.body; if (!root) return;
|
||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
||
acceptNode(n) {
|
||
if (!n.nodeValue || !n.nodeValue.trim()) return NodeFilter.FILTER_REJECT;
|
||
const p = n.parentNode;
|
||
if (p && _I18N_SKIP.has(p.nodeName)) return NodeFilter.FILTER_REJECT;
|
||
return NodeFilter.FILTER_ACCEPT;
|
||
},
|
||
});
|
||
const nodes = []; while (walker.nextNode()) nodes.push(walker.currentNode);
|
||
for (const n of nodes) {
|
||
const key = n.nodeValue.trim();
|
||
if (dict[key] != null) n.nodeValue = n.nodeValue.replace(key, dict[key]);
|
||
}
|
||
root.querySelectorAll('[placeholder],[title],[aria-label]').forEach(el => {
|
||
['placeholder', 'title', 'aria-label'].forEach(a => {
|
||
const v = el.getAttribute(a);
|
||
if (v && dict[v.trim()] != null) el.setAttribute(a, dict[v.trim()]);
|
||
});
|
||
});
|
||
};
|
||
|
||
// Language change → persist + reload (translations re-apply to fresh English DOM).
|
||
window.setAppLang = function (lang) {
|
||
if (!I18N_LANGS[lang]) return;
|
||
try { localStorage.setItem('app-lang', lang); } catch (_) {}
|
||
location.reload();
|
||
};
|
||
|
||
// ── Language picker — lives in Settings → General ────────────────────────────
|
||
// Populate + sync the <select id="s-lang-select">. The Settings fragment loads
|
||
// lazily, so this is idempotent and safe to call whenever Settings opens.
|
||
window.populateLangSelect = function () {
|
||
const sel = document.getElementById('s-lang-select');
|
||
if (!sel) return;
|
||
if (!sel.dataset.filled) {
|
||
sel.innerHTML = Object.keys(I18N_LANGS)
|
||
.map(c => `<option value="${c}">${I18N_LANGS[c]}</option>`).join('');
|
||
sel.dataset.filled = '1';
|
||
}
|
||
sel.value = _appLang;
|
||
};
|
||
|
||
// Back-compat: loader.js calls initLangPicker() once at startup. The picker now
|
||
// lives in Settings → General, so this just tries to populate it.
|
||
window.initLangPicker = function () { window.populateLangSelect(); };
|