6. März 2025 (zuletzt aktualisiert am 7. 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.

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.
Sprachbeispiel auf Schwedisch: Klingt ganz gut.
Auf jeden Fall sind die Sprachdateien sehr schnell erzeugt. Und die WAV-Dateien liegen in einem Format vor, das für Asterisk geeignet ist.
Das Programm benötigt eine permanente Internetverbindung. Sowohl die Übersetzungen als auch die Erzeugung der Sprachdateien erfolgen mit kostenlosen Diensten von Google. Was kostenlos ist, bietet oft nicht eine optimale Qualität. Für erste Tests und für den internen privaten Gebrauch ist die Qualität jedoch ausreichend.

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.

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.
Windows-Version: Eine Windows-Version ist gerade in der Entwicklung. Erste Test sind vielversprechend. Dazu musste das Python-Skript angepasst werden, damit man 1. nicht ffmpeg auf windows installieren muss und 2. damit die Audio-Datei (WAV für Asterisk) auf dem Desktop oder unter Downloads abgelegt wird. Die GUI wurde abgeändert:


Es sind noch Zischlaute in den Soundfiles (WAV):
Die Beta-Version Windows (ab Win7 32Bit) ist fertig: Sie befindet sich im Zip-File:
Sie läuft eigenständig und braucht keine Installation. Klangbeispiel:

Für mich war das ein lehreiches Experiment, wie man aus Python-Skripten Windows-Programme erzeugen kann. Grundsätzlich ist Linux die ideale Entwicklungsumgebung für Asterisk und Python. Das Arbeiten unter Linux geht schneller uns zuverlässiger.
Leider läuft diese EXE nur auf Win 7 bis jetzt. Win XP meckert, weil keine gültige Win32-Anwendung und Win 10 bekommt panische Angst wegen angeblicher Virenbedrohung. Ausweg: Einen Python-Interpreter auf Windows laufen lassen oder gleich auf Linux umsteigen. Dann hat man weniger Ärger. Siehe auch hier: https://chatgpt.com/share/67cc6aeb-b474-8013-9d40-f25578a99f8f.