Sprachdateien für Asterisk: Ein Python-Tool mit Text-to-Speech und automatischer Übersetzung

6. März 2025

Dieses Python-Skript übersetzt Texte automatisch in verschiedene Sprachen und wandelt sie in Sprachdateien um – perfekt für Asterisk-Telefonserver. Die Bedienung erfolgt über eine grafische Oberfläche, und die Sprachausgabe wird mit Google Text-to-Speech erstellt, ganz ohne API-Schlüssel. Unterstützt werden Deutsch, Englisch, Schwedisch, Französisch und Spanisch, wobei die englische Stimme derzeit nur mit britischem Akzent verfügbar ist.

Bildschirmfoto zu 2025 03 06 10 13 58
Das Skript für Python 3 erzeugt eine grafische Oberfläche. Bei „Automatisch übersetzen“ einen Haken setzen, wenn die Übersetzungen sofort zum Einsatz kommen sollen. Die erzeugten WAV- und MP3-Files werden im selben Ordner abgespeichert, in dem sich das Skript befindet.

Klangbeispiel als MP3 des Textes, der im oberen Bild zu sehen ist: Der englische Text hat einen kleinen Fehler, denn die Abkürzung KI wurde nicht mit AI übersetzt. Das lässt sich aber im unteren Feld von Hand noch korrigieren.


Klangbeispiel eines deutschen Textes als MP3: Die Stimme ist bei Google gTTS immer weiblich.

Auf jeden Fall sind die Sprachdateien sehr schnell erzeugt. Und die WAV-Dateien liegen in einem Format vor, das für Asterisk geeignet ist.

Bildschirmfoto zu 2025 03 06 14 36 22
Das Fenster lässt sich ein seiner Größe verschieben. Wer will, kann den Code individuell anpassen.

Das Programm bietet folgende Funktionen:

    • Eingabe eines beliebigen Textes über eine grafische Oberfläche
    • Auswahl der Sprache für die Sprachausgabe (Deutsch, US-Englisch, Britisches Englisch, Schwedisch, Französisch, Spanisch)
    • Möglichkeit, den Text vor der Sprachausgabe automatisch in die gewählte Sprache zu übersetzen
    • Speicherung der generierten Audiodateien als MP3 und WAV
    • Nutzung von Google Text-to-Speech (gTTS) und Google Translate zur Umsetzung
    • Keine Notwendigkeit für API-Schlüssel oder Passwörter

Das Skript für Python 3:

import tkinter as tk
from tkinter import scrolledtext, messagebox
import os
import re
from gtts import gTTS
import subprocess
import threading
from googletrans import Translator, LANGUAGES

class TextToSpeechApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Text zu Sprache Konverter")
        self.root.geometry("600x620")  # Erhöhte Höhe für die neuen Elemente
        self.root.configure(bg="#e0e5eb")
        
        # Übersetzer initialisieren
        self.translator = Translator()
        
        # Farben
        self.bg_color = "#e0e5eb"  # Hellblaugrau
        self.button_color = "#5d7793"  # Mittelblaugrau
        self.text_bg = "#f0f2f5"  # Sehr helles Blaugrau
        self.text_fg = "#2c3e50"  # Dunkelblaugrau
        
        # Sprachoptionen mit ISO-Codes für die Übersetzung
        self.languages = {
            "de": "Deutsch",
            "en-us": "US-Englisch",
            "en-gb": "Britisches Englisch", 
            "sv": "Schwedisch",
            "fr": "Französisch",
            "es": "Spanisch"
        }
        
        # Mapping von TTS-Sprachcodes zu googletrans-Sprachcodes und tld-Werten
        self.tts_to_translate_map = {
            "de": {"lang": "de", "tld": "de"},  # Deutscher Akzent
            "en-us": {"lang": "en", "tld": "com"},  # US-Englisch
            "en-gb": {"lang": "en", "tld": "co.uk"},  # Britisches Englisch
            "sv": {"lang": "sv", "tld": "se"},  # Schwedischer Akzent
            "fr": {"lang": "fr", "tld": "fr"},  # Französischer Akzent
            "es": {"lang": "es", "tld": "es"}  # Spanischer Akzent
        }
        
        # Standard Spracheinstellung
        self.selected_language = tk.StringVar()
        self.selected_language.set("de")
        
        # Option für langsames Sprechen
        self.slow_speech = tk.BooleanVar()
        self.slow_speech.set(False)
        
        # Option für automatische Übersetzung
        self.auto_translate = tk.BooleanVar()
        self.auto_translate.set(False)
        
        # Variable für Originaltext speichern
        self.original_text = ""
        
        self.create_widgets()
    
    def create_widgets(self):
        # Frame für Dateinamen
        filename_frame = tk.Frame(self.root, bg=self.bg_color)
        filename_frame.pack(pady=10, fill=tk.X, padx=20)
        
        # Label und Eingabefeld für Dateinamen
        tk.Label(filename_frame, text="Dateiname:", bg=self.bg_color, fg=self.text_fg, 
                 font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 10))
        
        self.filename_entry = tk.Entry(filename_frame, bg=self.text_bg, fg=self.text_fg, width=30)
        self.filename_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
        self.filename_entry.insert(0, "output")  # Standardwert
        
        # Frame für Spracheinstellungen
        language_frame = tk.Frame(self.root, bg=self.bg_color)
        language_frame.pack(pady=5, fill=tk.X, padx=20)
        
        # Label für Sprachauswahl
        tk.Label(language_frame, text="Sprache:", bg=self.bg_color, fg=self.text_fg, 
                 font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 10))
        
        # Radiobuttons für Sprachauswahl
        language_buttons_frame = tk.Frame(self.root, bg=self.bg_color)
        language_buttons_frame.pack(pady=0, fill=tk.X, padx=20)
        
        col = 0
        row = 0
        for lang_code, lang_name in self.languages.items():
            rb = tk.Radiobutton(language_buttons_frame, text=lang_name, value=lang_code, 
                              variable=self.selected_language, bg=self.bg_color, fg=self.text_fg,
                              command=self.on_language_change)
            rb.grid(row=row, column=col, sticky=tk.W, padx=5, pady=2)
            col += 1
            if col > 2:  # 3 Sprachen pro Zeile
                col = 0
                row += 1
        
        # Frame für Checkboxen
        option_frame = tk.Frame(language_buttons_frame, bg=self.bg_color)
        option_frame.grid(row=row+1, column=0, columnspan=3, sticky=tk.W, padx=5, pady=5)
        
        # Checkbox für langsames Sprechen
        slow_check = tk.Checkbutton(option_frame, text="Langsames Sprechen", 
                                   variable=self.slow_speech, bg=self.bg_color, fg=self.text_fg)
        slow_check.pack(side=tk.LEFT, padx=(0, 15))
        
        # Checkbox für automatische Übersetzung
        translate_check = tk.Checkbutton(option_frame, text="Automatisch übersetzen", 
                                        variable=self.auto_translate, bg=self.bg_color, fg=self.text_fg)
        translate_check.pack(side=tk.LEFT)
        
        # Textfeld mit Scrollbar
        tk.Label(self.root, text="Text eingeben:", bg=self.bg_color, fg=self.text_fg, 
                 font=("Arial", 10, "bold")).pack(anchor="w", padx=20)
        
        self.text_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, width=60, height=12,
                                                bg=self.text_bg, fg=self.text_fg)
        self.text_area.pack(padx=20, pady=10, fill=tk.BOTH, expand=True)
        
        # Textfeld für die Übersetzung
        tk.Label(self.root, text="Übersetzter Text:", bg=self.bg_color, fg=self.text_fg, 
                font=("Arial", 10, "bold")).pack(anchor="w", padx=20)
        
        self.translated_text_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, width=60, height=5,
                                                          bg=self.text_bg, fg="#4a6fa5")
        self.translated_text_area.pack(padx=20, pady=10, fill=tk.X)
        
        # Frame für Buttons
        button_frame = tk.Frame(self.root, bg=self.bg_color)
        button_frame.pack(pady=15, padx=20)
        
        # Buttons
        self.translate_button = tk.Button(button_frame, text="Text übersetzen", 
                                        command=self.translate_text, bg=self.button_color, 
                                        fg="white", font=("Arial", 10, "bold"),
                                        activebackground="#49617a",
                                        width=15, height=2)
        self.translate_button.pack(side=tk.LEFT, padx=(0, 10))
        
        self.convert_button = tk.Button(button_frame, text="MP3 und WAV erzeugen", 
                                      command=self.start_conversion, bg=self.button_color, 
                                      fg="white", font=("Arial", 10, "bold"),
                                      activebackground="#49617a",
                                      width=20, height=2)
        self.convert_button.pack(side=tk.LEFT, padx=(0, 10))
        
        self.clear_button = tk.Button(button_frame, text="Text löschen", 
                                    command=self.clear_text, bg=self.button_color, 
                                    fg="white", font=("Arial", 10, "bold"), 
                                    activebackground="#49617a",
                                    width=15, height=2)
        self.clear_button.pack(side=tk.LEFT)
        
        # Status Label
        self.status_label = tk.Label(self.root, text="", bg=self.bg_color, fg=self.text_fg)
        self.status_label.pack(pady=5)

    def clean_text(self, text):
        """Entfernt Sonderzeichen, Bindestriche, Gänsefüßchen und Smileys aus dem Text."""
        invalid_chars = r"[\"'""''`´…\-–—:;(){}\[\]<>|@#$%^&*_+=/\\😊😂👍]"
        cleaned_text = re.sub(invalid_chars, ' ', text)
        return cleaned_text
    
    def on_language_change(self):
        """Wird aufgerufen, wenn die Sprache geändert wird."""
        if self.auto_translate.get() and self.original_text:
            self.translate_text()
    
    def translate_text(self):
        """Übersetzt den Text in die ausgewählte Sprache."""
        # Text aus dem Textfeld holen
        text = self.text_area.get("1.0", tk.END).strip()
        
        if not text:
            self.status_label.config(text="Fehler: Kein Text eingegeben!")
            return
        
        # Originaltext speichern
        self.original_text = text
        
        # Ausgewählte Sprache holen und zum Übersetzen anpassen
        selected_lang = self.selected_language.get()
        translate_lang = self.tts_to_translate_map[selected_lang]["lang"]  # Nur den 'lang'-Teil verwenden
        
        # Übersetzung in einem separaten Thread durchführen
        self.translate_button.config(state=tk.DISABLED)
        self.status_label.config(text="Übersetze...")
        
        threading.Thread(target=self.perform_translation, 
                        args=(text, translate_lang), 
                        daemon=True).start()
    
    def perform_translation(self, text, dest_lang):
        """Führt die eigentliche Übersetzung durch."""
        try:
            # Übersetzung durchführen
            translation = self.translator.translate(text, dest=dest_lang)
            
            # Übersetzten Text anzeigen
            self.translated_text_area.delete("1.0", tk.END)
            self.translated_text_area.insert("1.0", translation.text)
            
            self.status_label.config(text=f"Übersetzung von {LANGUAGES.get(translation.src, 'Unbekannt')} "
                                     f"nach {LANGUAGES.get(translation.dest, 'Unbekannt')} abgeschlossen")
            
        except Exception as e:
            self.status_label.config(text=f"Fehler bei der Übersetzung: {str(e)}")
            self.translated_text_area.delete("1.0", tk.END)
            self.translated_text_area.insert("1.0", "Übersetzungsfehler. Bitte erneut versuchen.")
        
        finally:
            # Button wieder aktivieren
            self.translate_button.config(state=tk.NORMAL)
    
    def convert_to_speech(self):
        # Text aus dem korrekten Textfeld holen
        if self.auto_translate.get():
            text = self.translated_text_area.get("1.0", tk.END).strip()
            if not text:
                self.translate_text()  # Falls noch keine Übersetzung vorliegt
                text = self.translated_text_area.get("1.0", tk.END).strip()
        else:
            text = self.text_area.get("1.0", tk.END).strip()
        
        if not text:
            self.status_label.config(text="Fehler: Kein Text eingegeben!")
            return
        
        # Dateinamen aus dem Eingabefeld holen
        filename = self.filename_entry.get().strip()
        if not filename:
            filename = "output"
        
        # Dateipfade im selben Verzeichnis wie das Skript
        script_dir = os.path.dirname(os.path.abspath(__file__))
        output_mp3 = os.path.join(script_dir, f"{filename}.mp3")
        output_wav = os.path.join(script_dir, f"{filename}.wav")
        
        try:
            # Text bereinigen
            cleaned_text = self.clean_text(text)
            
            if not cleaned_text:
                self.status_label.config(text="Fehler: Der Text ist nach der Bereinigung leer!")
                return
            
            # Ausgewählte Sprache und Sprechgeschwindigkeit holen
            selected_lang = self.selected_language.get()
            lang_settings = self.tts_to_translate_map[selected_lang]
            is_slow = self.slow_speech.get()
            
            # Debug-Ausgabe, um die verwendeten Werte zu überprüfen
            debug_msg = f"Erzeuge TTS mit lang={lang_settings['lang']}, tld={lang_settings['tld']}, slow={is_slow}"
            print(debug_msg)  # In der Konsole sichtbar
            self.status_label.config(text=debug_msg)
            
            # Text in Sprache umwandeln mit entsprechendem tld
            tts = gTTS(
                text=cleaned_text,
                lang=lang_settings["lang"],
                tld=lang_settings["tld"],
                slow=is_slow
            )
            
            # MP3 speichern
            tts.save(output_mp3)
            
            # MP3 zu WAV konvertieren (benötigt ffmpeg)
            try:
                subprocess.run(
                    ["ffmpeg", "-i", output_mp3, "-ar", "8000", "-ac", "1", "-y", output_wav],
                    check=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
                # Sprachname für die Statusanzeige ermitteln
                lang_name = self.languages[selected_lang]
                speed_text = "langsam" if is_slow else "normal"
                translate_text = "übersetzt" if self.auto_translate.get() else "original"
                
                self.status_label.config(
                    text=f"Erfolgreich erstellt: {output_mp3} und {output_wav} ({lang_name}, {speed_text}, {translate_text})"
                )
            except subprocess.CalledProcessError:
                self.status_label.config(
                    text=f"MP3 erstellt, aber Fehler bei WAV-Konvertierung. Ist ffmpeg installiert?"
                )
            
        except Exception as e:
            self.status_label.config(text=f"Fehler: {str(e)}")
            
        finally:
            # Buttons wieder aktivieren
            self.convert_button.config(state=tk.NORMAL)
    
    def start_conversion(self):
        # Button deaktivieren während der Konvertierung
        self.convert_button.config(state=tk.DISABLED)
        self.status_label.config(text="Konvertiere...")
        
        # Wenn Übersetzung aktiviert ist und noch keine Übersetzung vorliegt
        if self.auto_translate.get() and not self.translated_text_area.get("1.0", tk.END).strip():
            self.translate_text()
        
        # Konvertierung in einem separaten Thread ausführen
        threading.Thread(target=self.convert_to_speech, daemon=True).start()
    
    def clear_text(self):
        self.text_area.delete("1.0", tk.END)
        self.translated_text_area.delete("1.0", tk.END)
        self.original_text = ""
        self.status_label.config(text="Text gelöscht")

def main():
    root = tk.Tk()
    app = TextToSpeechApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()

Folgende Python-Bibliotheken müssen eventuell installiert werden:

pip install tkinter  # Wird oft mit Python mitgeliefert, aber falls nicht, installieren
pip install gtts  # Google Text-to-Speech für die Sprachsynthese
pip install googletrans==4.0.0-rc1  # Google Übersetzungsbibliothek (spezielle Version empfohlen)

 

Hier kannst du dir das Skript herunterladen: Es ist mit ZIP gepackt. Entpacke die es und probiere die py-Datei aus mit „python3 text-to-speech-translate02.py“ im Linux-Terminal (Ubuntu)

text-to-speech-translate02.py.zip

Mit dieser Batchdatei für Linux kannst du das Skript per Mausklick starten: Nenne sie start_tts.sh oder so ähnlich. Mache die Datei ausführbar mit „chmod +x start_tts.sh“.

#!/bin/bash
cd "$(dirname "$0")"  # Wechselt in das Verzeichnis, in dem das Skript liegt
python3 text-to-speech-translate02.py

Wie ist dieses Python Skript entstanden? Vor einigen Wochen hatte ich mir mit einer KI (ich glaube es war ChatGPT) ein einfaches Python-Skript erstellen ohne Oberfläche erstellen lassen, das aus einer Textdatei mit Google gTTS eine MP3- und WAV-Datei, die für Asterisk geeignet ist, erstellt. Mit Hilfe Claude 3.7 Sonnet ließ ich eine Oberfläche dafür erzeugen nach meinen Vorstellungen erzeugen. In einem weiteren Schritt kamen Radio-Buttons hinzu, um Sprachen auswählen zu können. Dann kam mir die Idee, dass die Texte gleich übersetzt werden sollte. Claude schlug mir googletrans vor, da es wie gTTS ohne Anmeldung oder Token auskommt. Diese Erweiterung hatte dann auch auf Anhieb geklappt. Insgesamt lag der Zeitaufwand bei 1 bis 2 Stunden. Die meisten Zeit wurde für das Testen benötigt. Fehler im Code konnte ich nicht finden. Leider kann gTTS offenbar keine us-amerikanische Stimme liefern.

Bildschirmfoto zu 2025 03 06 08 27 55
Mit der kostenlosen Version von Claude 3.7. Sonnet wurde das Programm entwickelt.

Claude 3.7 Sonnet hat nicht nur eine praktische Oberfläche für das Coding. Man schreibt einen ausführlichen Prompt und diese KI macht das, was man will. In den allermeisten Fällen laufen die Skripte auf Anhieb.