Red while the mic is hot (live or recording), blue when off; label toggles Stop/Start microphone (DE+EN); pulse only without reduced motion; reset to blue when microphone access fails. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
585 lines
41 KiB
JavaScript
585 lines
41 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.
|
||
|
||
(function (root) {
|
||
|
||
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: {
|
||
// Voice archetype gallery
|
||
'Voice gallery': 'Stimmengalerie',
|
||
'40 ready-made German-first voices. Preview, save or open in the editor below.': '40 fertige Stimmen, Deutsch zuerst. Anhören, speichern oder unten im Editor öffnen.',
|
||
'Favourites only': 'Nur Favoriten',
|
||
'Use voice': 'Stimme verwenden',
|
||
'Favourite': 'Favorit',
|
||
'Open in Design': 'In Design öffnen',
|
||
'Loading…': 'Lädt…',
|
||
'No voices match.': 'Keine passende Stimme.',
|
||
'Could not load the gallery.': 'Galerie konnte nicht geladen werden.',
|
||
'Rendering preview…': 'Vorschau wird erzeugt…',
|
||
'Preview ready': 'Vorschau bereit',
|
||
'Preview failed': 'Vorschau fehlgeschlagen',
|
||
'Saving voice…': 'Stimme wird gespeichert…',
|
||
'Voice saved': 'Stimme gespeichert',
|
||
'Save failed': 'Speichern fehlgeschlagen',
|
||
'All categories': 'Alle Kategorien',
|
||
'Category': 'Kategorie',
|
||
'Load more': 'Mehr laden',
|
||
|
||
|
||
'male': 'männlich',
|
||
'female': 'weiblich',
|
||
'neutral': 'neutral',
|
||
|
||
|
||
|
||
|
||
|
||
'high': 'hoch',
|
||
'mid': 'mittel',
|
||
'low': 'tief',
|
||
'none': 'ohne',
|
||
'british': 'britisch',
|
||
'american': 'amerikanisch',
|
||
'australian': 'australisch',
|
||
'irish': 'irisch',
|
||
'german': 'deutsch',
|
||
'southern': 'südlich',
|
||
'German': 'Deutsch',
|
||
'English': 'Englisch',
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// Seed Finder auto-pick
|
||
'Generates up to 8 candidates, scores them and highlights the best. Nothing is saved until you click Use seed.': 'Erzeugt bis zu 8 Kandidaten, bewertet sie und hebt den besten hervor. Gespeichert wird erst nach Klick auf „Seed verwenden“.',
|
||
'Auto-pick best': 'Besten automatisch wählen',
|
||
'Enter a test sentence.': 'Testsatz eingeben.',
|
||
'Scoring': 'Bewerte',
|
||
'candidates… this can take a few minutes.': 'Kandidaten … das kann einige Minuten dauern.',
|
||
'words': 'Wörter',
|
||
'audio problems': 'Audioprobleme',
|
||
'score': 'Wertung',
|
||
'level': 'Pegel',
|
||
'Best': 'Bester',
|
||
'Best seed': 'Bester Seed',
|
||
'Listen, then click Use seed to save.': 'Anhören, dann „Seed verwenden“ zum Speichern.',
|
||
'No candidate could be generated.': 'Es konnte kein Kandidat erzeugt werden.',
|
||
'Speech-to-text unavailable: scored by level and audio problems only.': 'Sprache-zu-Text nicht verfügbar: Wertung nur nach Pegel und Audioproblemen.',
|
||
'Auto-pick failed': 'Automatische Wahl fehlgeschlagen',
|
||
// Conversation LLM endpoint picker
|
||
'Custom…': 'Benutzerdefiniert…',
|
||
'Custom LLM base URL': 'Eigene LLM-Basis-URL',
|
||
'Fetch models': 'Modelle abrufen',
|
||
'Add language models under Engines → Language Models': 'Sprachmodelle unter Engines → Language Models hinzufügen',
|
||
// Conversation thinking switch
|
||
'Thinking': 'Denken',
|
||
'on': 'an', 'off': 'aus',
|
||
'Off = faster answers. On = better reasoning but slower, and some models may return an empty reply.': 'Aus = schnellere Antworten. An = besseres Schlussfolgern, aber langsamer; manche Modelle liefern eine leere Antwort.',
|
||
// Conversation mic button
|
||
'Stop microphone': 'Mikrofon stoppen', 'Start microphone': 'Mikrofon starten',
|
||
// Conversation metrics view
|
||
'Token Usage': 'Token-Verbrauch', 'Time (ms)': 'Zeit (ms)',
|
||
'Prompt': 'Prompt', 'Completion': 'Completion',
|
||
'n/a': 'k. A.', 'LLM first token': 'LLM erstes Token', 'TTS first audio': 'TTS erstes Audio',
|
||
'last': 'zuletzt', 'points': 'Punkte', 'no data': 'keine Daten', 'Show as table': 'Als Tabelle anzeigen', 'Time': 'Zeit',
|
||
// Inline text tags
|
||
'Inline text tags': 'Inline-Texttags',
|
||
'Off by default. When on, tags shape the speech in Try It Out, Read Aloud, the speech API and audiobooks; streaming and magazine audio only remove them.': 'Standardmäßig aus. Wenn aktiv, steuern Tags die Sprache in „Ausprobieren“, „Vorlesen“, der Sprach-API und Hörbüchern; Streaming und Magazin-Audio entfernen sie nur.',
|
||
'Inline tags (enable in Settings → General):': 'Inline-Tags (aktivieren unter Einstellungen → Allgemein):',
|
||
// 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',
|
||
// Voice-design attribute chips
|
||
'Pitch': 'Tonhöhe', 'Pace': 'Tempo', 'Age': 'Alter', 'Accent / region': 'Akzent / Region', 'Tone / timbre': 'Klang / Timbre', 'Emotion': 'Emotion', 'male voice': 'männliche Stimme', 'female voice': 'weibliche Stimme', 'neutral voice': 'neutrale Stimme', 'child': 'Kind', 'teenager': 'Teenager', 'young adult': 'junger Erwachsener', 'middle-aged': 'mittleren Alters', 'elderly': 'älter', 'high pitch': 'hohe Tonlage', 'mid pitch': 'mittlere Tonlage', 'low pitch': 'tiefe Tonlage', 'fast pace': 'schnell', 'moderate pace': 'mittleres Tempo', 'slow pace': 'langsam', 'British accent': 'britischer Akzent', 'American accent': 'amerikanischer Akzent', 'Australian accent': 'australischer Akzent', 'Irish accent': 'irischer Akzent', 'German accent': 'deutscher Akzent', 'Southern accent': 'Südstaaten-Akzent', 'warm': 'warm', 'husky': 'rauchig', 'crisp': 'klar', 'smooth': 'weich', 'rich': 'voll', 'magnetic': 'magnetisch', 'authoritative': 'autoritär', 'cheerful': 'fröhlich', 'calm': 'ruhig', 'serious': 'ernst', 'gentle': 'sanft', 'enthusiastic': 'begeistert', 'sad': 'traurig',
|
||
'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(); };
|
||
|
||
Object.assign(root, { I18N_LANGS, I18N_DICT });
|
||
root.TTVC = root.TTVC || {};
|
||
root.TTVC.i18n = root.TTVC.i18n || {};
|
||
Object.assign(root.TTVC.i18n, {
|
||
languages: I18N_LANGS,
|
||
dictionary: I18N_DICT,
|
||
get language() { return _appLang; },
|
||
translate: root.t,
|
||
apply: root.applyI18n,
|
||
set: root.setAppLang,
|
||
});
|
||
})(window);
|