Heute widmen wir uns einem Projekt, das nicht nur wahnsinnig praktisch ist, sondern auch komplett ohne Internet auskommt. Wir bauen einen eigenen, lokalen KI-Sprachübersetzer, der die gesprochenen Wörter in die gewünschte Sprache übersetzt – und umgekehrt. Derzeit über 30 Sprachen möglich: rhasspy/piper-voices at main
Das Beste daran ist, das die Privatsphäre erhalten bleibt, da keine persönlichen Inforationen in das unbekannte Internet, Cloud oder KI verschickt. Alles bleibt schön lokal auf deinem Raspberry

Wie funktioniert das Ganze?
Hinter der gesamten Lösung stecken mehrere Programm die hintereinander ablaufen. Beispiel von Deutsch ins Englisch:
- Das Ohr (Audio-Aufnahme): Zuerst wird die deutsche Sprache mit „arecord“ über das Mikrofon aufgenommen und als kleine Tondatei zwischengespeichert.
- Sprache zu Text: Das Programm Whisper tritt danach in Aktion. Es hört sich die Tondatei (Aufnahme) an und tippt sie sozusagen als deutschen Text ab (STT – Speech to Text).
- Das Gehirn (Übersetzen): Nun kommt die eigentliche Künstliche Intelligenz (KI) ins Spiel. Die Software Ollama nutzt ein Modell namens Gemma (genauer: gemma3:1b), um den abgetippten deutschen Text ins Englische zu übersetzen.
- Der Mund (Text zu Sprache): Das englische Übersetzungsergebnis liegt nun als Text vor. Das Programm Piper TTS (Text-to-Speech / Text-zu-Sprache) nimmt diesen Text und erzeugt daraus eine neue Audiodatei mit einer verständlichen englischen Stimme.
- Die Ausgabe: Ganz am Ende wird diese neue Tondatei einfach über euren Lautsprecher (oder Monitor) abgespielt.

Was wir für dieses Projekt brauchen
- Raspberry Pi 5 (ab 4 GB RAM, aktive Kühlung dringend empfohlen!)
- USB-Mikrofon (in meinem Fall ein Q1 USB-Mikrofon und auch alte Web-Cam mit Mikro funktionierte)
- Sprachausgabe: HDMI Monitor mit Lautsprecher
- Eine MicroSD-Karte (mindestens 64 GB) mit aktuellem Raspberry Pi OS (64 Bit)
- Internetverbindung (wird nur für die Installation benötigt!)
Optional:
- 3.5 Zoll LCD Touch-Display (Liquid Crystal Display, 320×480 Pixel Auflösung)
- GPIO Adapter
- USB Soundkarte für Kopfhörer, Lautsprecher und Mikrofon mit 3.5 TRS Klinkenstecker
- Mini Speaker
Teil 1: Das Basissystem und die Audio-Einrichtung
Mit Raspberry Imager ein aktuelles Linux Betreibsystem auf SD Karte oder NVME SSD bringen. Siehe Beitrag:
Bevor wir loslegen, bringen wir den Raspberry Pi per sicherer Netzwerkverbindung (Secure Shell, kurz SSH) auf den neuesten Stand und installieren alle wichtigen Werkzeuge in einem Rutsch:
sudo apt update sudo apt full-upgrade -y sudo apt install -y git cmake build-essential curl jq python3 python3-venv python3-pip alsa-utils pipewire-alsa ffmpeg libopenblas-dev rfkill
Nach dem Update empfehle ich einen Neustart mit sudo reboot.
Danach legen wir die Ordnerstruktur an und suchen die genauen internen Namen unserer Geräte:
mkdir -p ~/translator/{app,log,test,voices/de,voices/en,work}
1.1 Audio-Geräte ermitteln:
Mikrofon und Lautsprecher einstellen.
Damit der Übersetzer etwas hört und spricht, müssen wir die korrekten Adressen deiner USB-Hardware herausfinden.
- Aufnahmegeräte (Mikrofone) auflisten:
arecord -l - Wiedergabegeräte (Lautsprecher) auflisten:
aplay -l
Notiere dir aus der Liste jeweils die Card– und Device-Nummer (z.B. card 2, device 0). Daraus setzt sich später im Skript die Adresse zusammen (in diesem Beispiel: plughw:2,0).
Audio-Test: Nimm eine kurze Testdatei auf, um das Mikrofon zu prüfen:
arecord -D plughw:2,0 -f S16_LE -r 16000 -c 1 -d 10 ~/translator/test/mic-test.wav
und höre dir die Aufnahme an mit:
aplay ~/translator/test/mic-test.wav
Du solltest nun deine eigene Stimme hören (16 kHz, Mono, PCM 16 Bit).
Teil 2: Installation und Test der KI-Komponenten
Ich beginne mit der Spracherkennung und teste danach jeden weiteren Baustein einzeln, bevor wir alles zusammensetzen.
2.1 Whisper.cpp (Spracherkennung)
Whisper sorgt dafür, dass das gesprochene Wort in Text umgewandelt wird. Wir laden den Code herunter und kompilieren ihn direkt auf dem Pi:
git clone https://github.com/ggml-org/whisper.cpp.git ~/whisper.cpp cd ~/whisper.cpp cmake -B build -DGGML_BLAS=1 -DCMAKE_BUILD_TYPE=Release cmake --build build -j4 --config Release bash models/download-ggml-model.sh base
Test: Wir lassen Whisper unsere zuvor aufgenommene Test-Datei übersetzen:
./build/bin/whisper-cli -m models/ggml-base.bin -f ~/translator/test/mic-test.wav -l de -nt -otxt -of ~/translator/test/transkript cat ~/translator/test/transkript.txt
Soll-Ergebnis: Der von dir gesprochene deutsche Satz sollte nun als Text in der Konsole stehen.
2.2 Ollama und Gemma 3 (Übersetzung)
Ollama ist unsere lokale KI-Umgebung. Wir nutzen das Modell gemma3:1b, da es perfekt für unsere 4 GB RAM geeignet ist.
curl -fsSL https://ollama.com/install.sh | sh sudo systemctl enable --now ollama ollama pull gemma3:1b
Test: Wir schicken einen Test-Satz direkt an die KI, um die Übersetzung zu prüfen:
curl -s http://127.0.0.1:11434/api/generate -H 'Content-Type: application/json' -d '{"model":"gemma3:1b","prompt":"Übersetze exakt ins Englische. Gib nur die Übersetzung aus: Wo befindet sich der Bahnhof?","stream":false,"options":{"temperature":0}}' | jq -r '.response'
Soll-Ergebnis: Die Ausgabe sollte „Where is the train station?“ lauten.
2.3 Piper TTS (Sprachausgabe)
Piper liest den übersetzten Text am Ende laut vor. Dafür richten wir eine geschützte Python-Umgebung (Virtual Environment) ein:
python3 -m venv ~/translator/venv source ~/translator/venv/bin/activate python -m pip install --upgrade pip python -m pip install piper-tts requests cd ~/translator/voices/de python -m piper.download_voices de_DE-thorsten-medium cd ~/translator/voices/en python -m piper.download_voices en_GB-alan-medium find ~/translator/voices -type f \( -name "*.onnx" -o -name "*.onnx.json" \) -print
Test: Lass dir einen Text von der deutschen Stimme vorlesen:
echo 'Guten Tag. Der Übersetzer ist bereit.' | piper --model ~/translator/voices/de/de_DE-thorsten-medium.onnx --output_file ~/translator/test/tts-de.wav aplay ~/translator/test/tts-de.wav
Soll-Ergebnis: Eine klare deutsche Computerstimme liest den Satz vor.
Optional: Weitere Sprachen (siehe Anhang)
Teil 3: Das Hauptprogramm (translator.py)
Erstelle die Datei ~/translator/app/translator.py und füge den folgenden Code ein.
Wichtig:
Passe oben im Code bei MIC_DEVICE und PLAYBACK_DEVICE die Namen an, die du in Teil 1 herausgefunden hast!
Und… achte darauf das die Zeilen so wie hier vorgehen auch eingerückt bleiben.
Hier klicken zum öffnen
#!/usr/bin/env python3
import argparse, logging, subprocess, sys
from pathlib import Path
import requests
BASE = Path.home() / "translator"
WORK = BASE / "work"
LOG = BASE / "log/translator.log"
WHISPER = Path.home() / "whisper.cpp/build/bin/whisper-cli"
WHISPER_MODEL = Path.home() / "whisper.cpp/models/ggml-base.bin"
OLLAMA_URL = "http://127.0.0.1:11434/api/generate"
GEMMA_MODEL = "gemma3:1b"
# HIER DEINE AUDIO-GERÄTE EINTRAGEN:
MIC_DEVICE = "plughw:2,0"
PLAYBACK_DEVICE = "default"
VOICES = {
"de": BASE / "voices/de/de_DE-thorsten-medium.onnx",
"en": BASE / "voices/en/en_GB-alan-medium.onnx",
}
WORK.mkdir(parents=True, exist_ok=True)
LOG.parent.mkdir(parents=True, exist_ok=True)
logging.basicConfig(filename=LOG, level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
def run(cmd, input_text=None):
cmd=[str(x) for x in cmd]
logging.info("CMD: %s", " ".join(cmd))
r=subprocess.run(cmd,input=input_text,text=True,capture_output=True)
if r.stdout: logging.info("STDOUT: %s",r.stdout.strip())
if r.stderr: logging.info("STDERR: %s",r.stderr.strip())
if r.returncode:
raise RuntimeError(f"Befehl fehlgeschlagen ({r.returncode}): {' '.join(cmd)}\n{r.stderr.strip()}")
return r
def record(seconds):
wav=WORK/"input.wav"; wav.unlink(missing_ok=True)
run(["arecord","-D",MIC_DEVICE,"-f","S16_LE","-r","16000","-c","1","-d",seconds,wav])
if not wav.is_file() or wav.stat().st_size<1000: raise RuntimeError("Aufnahme fehlt oder ist zu klein")
return wav
def transcribe(wav,lang):
stem=WORK/"transcript"; txt=stem.with_suffix(".txt"); txt.unlink(missing_ok=True)
run([WHISPER,"-m",WHISPER_MODEL,"-f",wav,"-l",lang,"-nt","-otxt","-of",stem])
if not txt.is_file(): raise RuntimeError("Whisper erzeugte keine Textdatei")
text=txt.read_text(encoding="utf-8",errors="replace").strip()
if not text: raise RuntimeError("Whisper lieferte keinen Text")
return text
def translate(text,target):
language="Deutsch" if target=="de" else "Englisch"
prompt=f"Übersetze exakt ins {language}. Gib ausschließlich die Übersetzung aus, ohne Erklärung, Anführungszeichen oder Präfix. Text: {text}"
r=requests.post(OLLAMA_URL,json={"model":GEMMA_MODEL,"prompt":prompt,"stream":False,"options":{"temperature":0}},timeout=180)
r.raise_for_status(); out=r.json()["response"].strip()
if not out: raise RuntimeError("Gemma lieferte keine Übersetzung")
return out
def speak(text,target):
model=VOICES[target]; config=Path(str(model)+".json")
if not model.is_file(): raise FileNotFoundError(f"TTS-Modell fehlt: {model}")
if not config.is_file(): raise FileNotFoundError(f"TTS-Konfiguration fehlt: {config}")
out=WORK/"translation.wav"; out.unlink(missing_ok=True)
run([BASE/"venv/bin/piper","--model",model,"--output_file",out],text)
run(["aplay","-D",PLAYBACK_DEVICE,out]); return out
def main():
ap=argparse.ArgumentParser();
ap.add_argument("--from",dest="source",choices=["de","en"],required=True)
ap.add_argument("--to",dest="target",choices=["de","en"],required=True);
ap.add_argument("--seconds",type=int,default=7)
a=ap.parse_args()
if a.source==a.target: sys.exit("Quell- und Zielsprache müssen verschieden sein")
try:
print("Aufnahme startet ...")
text=transcribe(record(a.seconds),a.source)
print("Erkannt:",text)
out=translate(text,a.target)
print("Übersetzt:",out)
speak(out,a.target)
except Exception as e:
logging.exception("Fehler")
print(f"FEHLER: {e}",file=sys.stderr)
sys.exit(1)
if __name__=="__main__": main()
Mache das Skript anschließend ausführbar mit:
chmod +x ~/translator/app/translator.py
Erster finaler Test sollte vorab WIFI oder LAN Kabel getrennt werden um zu testen das es lokal sich ausführen lässt. Dann kann nun getestet werden:
Sofern nicht in der virtuellen umgebung hier mit aktivieren:
source ~/translator/venv/bin/activate
und dann einen der beiden Varianten Englisch zu Deutsch oder umgekehrt, mit 7 Sekunden Ausnahme Zeit
python ~/translator/app/translator.py –from de –to en –seconds 7
python ~/translator/app/translator.py –from en –to de –seconds 7
Teil 4: Die grafische Touch-Oberfläche (GUI)
Wir wollen keine Befehle mehr tippen, sondern dicke Buttons auf dem Display drücken. Dazu wird eine neue Datei erstellt:
nano ~/translator/gui.py
Kopiere den folgenden Code hinein. Wichtig: Dieser Code nutzt eine Technik namens „Threading“ (parallele Ausführung). Dadurch friert das Display nicht ein, während im Hintergrund die rechenintensive Übersetzung läuft.
import tkinter as tk
import subprocess
import threading
def starte_uebersetzung(quellsprache, zielsprache):
# Buttons sperren, damit man nicht doppelt klickt
button_de_en.config(state=tk.DISABLED)
button_en_de.config(state=tk.DISABLED)
text_feld.delete(1.0, tk.END)
text_feld.insert(tk.END, f"Aufnahme läuft... Bitte 7 Sekunden sprechen!\n\n")
def aufgabe():
# Passe hier /home/admin/ an deinen tatsächlichen Benutzerpfad an!
befehl = [
"/home/admin/translator/venv/bin/python",
"/home/admin/translator/app/translator.py",
"--from", quellsprache,
"--to", zielsprache,
"--seconds", "7"
]
try:
prozess = subprocess.Popen(
befehl, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
)
for zeile in prozess.stdout:
root.after(0, text_feld.insert, tk.END, zeile)
root.after(0, text_feld.see, tk.END)
prozess.wait()
root.after(0, text_feld.insert, tk.END, "\nFertig!")
except Exception as fehler:
root.after(0, text_feld.insert, tk.END, f"\nFehler: {fehler}")
finally:
root.after(0, button_de_en.config, {'state': tk.NORMAL})
root.after(0, button_en_de.config, {'state': tk.NORMAL})
threading.Thread(target=aufgabe, daemon=True).start()
root = tk.Tk()
root.title("Prilchen LABS - Übersetzer")
root.geometry("480x320")
# Für den Vollbildmodus auf dem LCD die # in der nächsten Zeile entfernen:
# root.attributes("-fullscreen", True)
frame_buttons = tk.Frame(root)
frame_buttons.pack(pady=10)
button_de_en = tk.Button(frame_buttons, text="DE -> EN", font=("Arial", 20, "bold"), width=10, height=3, command=lambda: starte_uebersetzung("de", "en"))
button_de_en.pack(side=tk.LEFT, padx=10)
button_en_de = tk.Button(frame_buttons, text="EN -> DE", font=("Arial", 20, "bold"), width=10, height=3, command=lambda: starte_uebersetzung("en", "de"))
button_en_de.pack(side=tk.RIGHT, padx=10)
text_feld = tk.Text(root, font=("Arial", 14), wrap=tk.WORD, height=8)
text_feld.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
root.mainloop()
Damit der Aufruf leichter wird, legen wir eine kleine Startdatei an:
echo '#!/bin/bash' > ~/translator/gui.sh echo 'python3 ~/translator/gui.py' >> ~/translator/gui.sh chmod +x ~/translator/gui.sh
Teil 5: Das LCD Touch-Display und VNC einrichten
Jetzt ist die Software komplett getestet. Damit wir das Projekt als Standalone-Gerät ohne großen Computermonitor bedienen können, stecken wir das 3.5 Zoll LCD auf den Raspberry Pi. Die Treiber installieren wir mit folgenden Befehlen:
sudo rm -rf LCD-show git clone https://github.com/goodtft/LCD-show.git chmod -R 755 LCD-show cd LCD-show/ sudo ./LCD35-show
Der Pi startet danach automatisch neu.
Falls du das fertige Touch-Display zur Fernwartung auf deinem Hauptrechner spiegeln möchtest, nutzen wir Virtual Network Computing (VNC). Ein normales VNC funktioniert mit dem aufgesteckten Display manchmal nicht direkt. Der Trick ist ein virtueller Bildschirm!
Verbinde dich per SSH (ssh admin@deine-ip-adresse) und starte den Server so:
vncserver-virtual -randr=1920x1080
In deinem VNC-Programm (z. B. RealVNC) gibst du nun die IP-Adresse mit dem angezeigten Port ein (z. B. 10.10.20.20:1). Schon hast du den Desktop vor dir und das Touch-Display funktioniert ebenfalls!
Teil 6: Autostart einrichten (Optional)
Damit sich unsere grafische Oberfläche (GUI) beim Einschalten des Pi von ganz alleine öffnet, legen wir einen Autostart an.
Erstelle zuerst den nötigen Ordner (falls er noch nicht existiert):
mkdir -p ~/.config/autostart
Lege nun die Autostart-Datei an:
nano ~/.config/autostart/translator_gui.desktop
Fülle die Datei mit diesem Inhalt (Pfad bei Exec= ggf. an deinen Benutzernamen anpassen!):
[Desktop Entry] Type=Application Name=Translator GUI Autostart Exec=/home/admin/translator/gui.sh Terminal=false
Wenn du nun den Raspberry Pi neu startest (sudo reboot), lädt die normale Desktop-Oberfläche und startet sofort unsere beiden großen Übersetzer-Buttons in den Vordergrund!
Ein wichtiger Hinweis zum Datenschutz
Da unser System temporäre WAV-Dateien (Audios) und transkribierte Texte auf der SD-Karte ablegt, denkt an ein Löschkonzept, falls ihr vertrauliche Gespräche übersetzt. Für Daueraufnahmen in öffentlichen Bereichen benötigt ihr zudem eine Einwilligung!
Fazit
Ein 4-GB-Raspberry, ein kleines Touch-Display und quelloffene KI-Modelle genügen, um einen völlig autarken Universalübersetzer zu bauen. Keine Cloud, keine Abos, keine versteckten Datenübertragungen. Die Privatsphäre ist somit erhalten geblieben. Manche umständlichen Sätze hat jedoch die gemma3:4b auf dem RPi mit 8GB schöner Übersetzt.
Wenn euch das Tutorial geholfen hat oder ihr eigene Anpassungen für das Gehäuse bastelt (vielleicht etwas Schickes in OpenSCAD?), lasst es mich in den Kommentaren wissen oder schaut auf meinem YouTube-Kanal vorbei!
Youtube Video:
Optional: Weitere Sprachen
So ergänzt du weitere Sprachen – beispielsweise Spanisch und Französisch
Unser Übersetzer kann weit mehr als nur Deutsch und Englisch. Das Programm Piper TTS (Text-to-Speech / Text-zu-Sprache) unterstützt wie oben erwähnt viele weitere Sprachen. Um zum Beispiel Spanisch und Französisch hinzuzufügen, müssen wir nur die passenden Stimmen herunterladen und unserem Hauptprogramm mitteilen, dass es diese neuen Sprachen gibt.
1. Ordner für die neuen Sprachen anlegen
Zuerst erstellen wir zwei neue Ordner für die spanischen und französischen Stimmen-Dateien. Gib dafür folgenden Befehl in deine Konsole ein:
mkdir -p ~/translator/voices/es ~/translator/voices/fr
2. Die Sprachmodelle herunterladen
Jetzt laden wir die passenden Stimmen herunter. Dafür müssen wir zuerst wieder unsere geschützte Python-Umgebung (Virtual Environment, kurz venv) aktivieren, falls du sie zwischenzeitlich geschlossen hast:
source ~/translator/venv/bin/activate
Wechsle nun in den neuen spanischen Ordner und lade die Stimme herunter:
cd ~/translator/voices/es
python -m piper.download_voices es_ES-sharvard-medium
Danach machst du genau das Gleiche für Französisch:
cd ~/translator/voices/fr
python -m piper.download_voices fr_FR-siwis-medium
3. Das Hauptprogramm anpassen
Damit das System die neuen Sprachen auch nutzt, musst du die Datei translator.py aktualisieren. Ersetze deinen alten Code einfach durch den unten stehenden, überarbeiteten Code.
Darin sind die wichtigen Dinge bereits angepasst:
- Im Bereich VOICES (Stimmen) sind die genauen Dateipfade zu den neuen spanischen und französischen Stimmen-Dateien hinterlegt.
- Die Funktion translate (Übersetzen) weiß nun, dass sie die Künstliche Intelligenz (KI) anweisen muss, exakt ins „Spanische“ oder „Französische“ zu übersetzen.
Hier klicken zum öffnen
#!/usr/bin/env python3
import argparse, logging, subprocess, sys
from pathlib import Path
import requests
BASE = Path.home() / "translator"
WORK = BASE / "work"
LOG = BASE / "log/translator.log"
WHISPER = Path.home() / "whisper.cpp/build/bin/whisper-cli"
WHISPER_MODEL = Path.home() / "whisper.cpp/models/ggml-base.bin"
OLLAMA_URL = "http://127.0.0.1:11434/api/generate"
GEMMA_MODEL = "gemma3:1b"
# HIER DEINE AUDIO-GERÄTE EINTRAGEN:
MIC_DEVICE = "plughw:2,0"
PLAYBACK_DEVICE = "default"
# Absoluter Pfad zur Piper-Datei in der virtuellen Umgebung (Virtual Environment / venv)
PIPER = BASE / "venv/bin/piper"
# Stimmen für die verschiedenen Sprachen
VOICES = {
"de": BASE / "voices/de/de_DE-thorsten-medium.onnx",
"en": BASE / "voices/en/en_GB-alan-medium.onnx",
"es": BASE / "voices/es/es_ES-sharvard-medium.onnx",
"fr": BASE / "voices/fr/fr_FR-siwis-medium.onnx",
}
WORK.mkdir(parents=True, exist_ok=True)
LOG.parent.mkdir(parents=True, exist_ok=True)
logging.basicConfig(filename=LOG, level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
def run(cmd, input_text=None):
cmd=[str(x) for x in cmd]
logging.info("CMD: %s", " ".join(cmd))
r=subprocess.run(cmd,input=input_text,text=True,capture_output=True)
if r.stdout: logging.info("STDOUT: %s",r.stdout.strip())
if r.stderr: logging.info("STDERR: %s",r.stderr.strip())
if r.returncode:
raise RuntimeError(f"Befehl fehlgeschlagen ({r.returncode}): {' '.join(cmd)}\n{r.stderr.strip()}")
return r
def record(seconds):
wav=WORK/"input.wav"
wav.unlink(missing_ok=True)
run(["arecord","-D",MIC_DEVICE,"-f","S16_LE","-r","16000","-c","1","-d",seconds,wav])
if not wav.is_file() or wav.stat().st_size<1000:
raise RuntimeError("Aufnahme fehlt oder ist zu klein")
return wav
def transcribe(wav,lang):
stem=WORK/"transcript"
txt=stem.with_suffix(".txt")
txt.unlink(missing_ok=True)
run([WHISPER,"-m",WHISPER_MODEL,"-f",wav,"-l",lang,"-nt","-otxt","-of",stem])
if not txt.is_file():
raise RuntimeError("Whisper erzeugte keine Textdatei (Text file)")
text=txt.read_text(encoding="utf-8",errors="replace").strip()
if not text:
raise RuntimeError("Kein Text erkannt")
return text
def translate(text, target_lang):
lang_names = {"en": "Englische", "de": "Deutsche", "es": "Spanische", "fr": "Französische"}
prompt = f"Übersetze exakt ins {lang_names[target_lang]}. Gib nur die Übersetzung aus: {text}"
payload = {
"model": GEMMA_MODEL,
"prompt": prompt,
"stream": False,
"options": {"temperature": 0}
}
r = requests.post(OLLAMA_URL, json=payload)
r.raise_for_status()
return r.json().get("response", "").strip()
def speak(text, lang):
voice = VOICES.get(lang)
if not voice:
raise RuntimeError(f"Keine Stimme für die Sprache {lang} gefunden")
out_wav = WORK / "output.wav"
out_wav.unlink(missing_ok=True)
piper_cmd = [str(PIPER), "--model", str(voice), "--output_file", str(out_wav)]
logging.info("CMD: echo %s | %s", text, " ".join(piper_cmd))
p = subprocess.run(piper_cmd, input=text, text=True, capture_output=True)
if not out_wav.is_file():
raise RuntimeError(f"Piper TTS (Text-zu-Sprache) Fehler: {p.stderr}")
run(["aplay", "-D", PLAYBACK_DEVICE, out_wav])
def main():
parser = argparse.ArgumentParser(description="Lokaler KI Sprachübersetzer")
parser.add_argument("--from", dest="from_lang", choices=["de", "en", "es", "fr"], default="de", help="Quellsprache")
parser.add_argument("--to", dest="to_lang", choices=["de", "en", "es", "fr"], default="en", help="Zielsprache")
parser.add_argument("--seconds", type=int, default=5, help="Aufnahmedauer in Sekunden")
args = parser.parse_args()
print(f"[{args.from_lang.upper()} -> {args.to_lang.upper()}] Aufnahme startet für {args.seconds} Sekunden...")
wav = record(args.seconds)
print("Spracherkennung (Speech-to-Text / STT) läuft...")
text = transcribe(wav, args.from_lang)
print(f"Erkannt: {text}")
print("Übersetzung durch Künstliche Intelligenz (KI) läuft...")
translated = translate(text, args.to_lang)
print(f"Übersetzt: {translated}")
print("Sprachausgabe (Text-to-Speech / TTS) läuft...")
speak(translated, args.to_lang)
print("Fertig.")
if __name__ == "__main__":
try:
main()
except Exception as e:
logging.error("Fehler: %s", e)
print(f"Fehler: {e}", file=sys.stderr)
sys.exit(1)
So startest du den Übersetzer mit den neuen Sprachen:
Möchtest du nun von Deutsch nach Spanisch übersetzen, übergibst du beim Starten des Programms einfach die entsprechenden Sprachkürzel:
~/translator/venv/bin/python3 ~/translator/app/translator.py –from de –to es
(Tipp: Durch die direkte Angabe von venv/bin/python3 nutzen wir automatisch die richtige Umgebung, ohne sie vorher per Hand aktivieren zu müssen!)
Erste Ausgabe:

Hier die Anpassung der GUI.py:
Hier klicken zum öffnen
#!/usr/bin/env python3
import tkinter as tk
from tkinter import font
import subprocess
from pathlib import Path
import threading
# --- FARBANPASSUNGEN (Prilchen LABS Design) ---
BG_COLOR = "#ecefef"
BTN_COLOR = "#1fbc9c"
BTN_ACTIVE = "#17997f"
FRAME_BG = "#747474"
TEXT_COLOR = "#222222"
BTN_TEXT = "#ffffff"
FONT_NAME = "Helvetica"
# --- PFADE ZU DEN PROGRAMMEN ---
BASE_DIR = Path.home() / "translator"
TRANSLATOR_SCRIPT = BASE_DIR / "app/translator.py"
PYTHON_EXEC = BASE_DIR / "venv/bin/python3"
# --- SPRACHEN DEFINIEREN ---
LANGUAGES = {
"Deutsch": "de",
"Englisch": "en",
"Spanisch": "es",
"Französisch": "fr"
}
class TranslatorGUI:
def __init__(self, root):
self.root = root
self.root.title("Prilchen LABS - Sprachübersetzer")
self.root.geometry("480x320")
self.root.configure(bg=BG_COLOR)
# Schriften verkleinert (size 12 und 14) für mehr Platz
self.custom_font = font.Font(family=FONT_NAME, size=12, weight="bold")
self.title_font = font.Font(family=FONT_NAME, size=14, weight="bold")
self.from_lang_name = tk.StringVar(value="Deutsch")
self.to_lang_name = tk.StringVar(value="Englisch")
self.build_interface()
def build_interface(self):
# Titel (Kompakter)
title = tk.Label(self.root, text="Offline Übersetzer", bg=BG_COLOR, fg=TEXT_COLOR, font=self.title_font)
title.pack(pady=5)
# Rahmen für die Sprachauswahl (Kompakter)
lang_frame = tk.Frame(self.root, bg=FRAME_BG, padx=5, pady=5)
lang_frame.pack(pady=2, fill="x", padx=10)
# Linke Seite: Von Sprache
from_frame = tk.Frame(lang_frame, bg=FRAME_BG)
from_frame.pack(side="left", expand=True)
tk.Label(from_frame, text="Von:", bg=FRAME_BG, fg=BTN_TEXT, font=self.custom_font).pack(side="left")
from_menu = tk.OptionMenu(from_frame, self.from_lang_name, *LANGUAGES.keys())
from_menu.config(bg=BTN_COLOR, fg=BTN_TEXT, font=self.custom_font, activebackground=BTN_ACTIVE, highlightthickness=0)
from_menu["menu"].config(bg=BG_COLOR, fg=TEXT_COLOR, font=self.custom_font)
from_menu.pack(side="left", padx=5)
# Rechte Seite: Zu Sprache
to_frame = tk.Frame(lang_frame, bg=FRAME_BG)
to_frame.pack(side="right", expand=True)
tk.Label(to_frame, text="Nach:", bg=FRAME_BG, fg=BTN_TEXT, font=self.custom_font).pack(side="left")
to_menu = tk.OptionMenu(to_frame, self.to_lang_name, *LANGUAGES.keys())
to_menu.config(bg=BTN_COLOR, fg=BTN_TEXT, font=self.custom_font, activebackground=BTN_ACTIVE, highlightthickness=0)
to_menu["menu"].config(bg=BG_COLOR, fg=TEXT_COLOR, font=self.custom_font)
to_menu.pack(side="left", padx=5)
# Statusanzeige
self.status_label = tk.Label(self.root, text="Bereit für die Aufnahme.", bg=BG_COLOR, fg=TEXT_COLOR, font=self.custom_font)
self.status_label.pack(pady=2)
# Aufnahme-Button (Kompakter, in der Mitte platziert)
self.record_btn = tk.Button(self.root, text="🎤 Aufnahme", bg=BTN_COLOR, fg=BTN_TEXT, font=self.title_font,
activebackground=BTN_ACTIVE, command=self.start_translation, height=1, width=15, relief="flat")
self.record_btn.pack(pady=2)
# Neues Textfeld für die Terminal-Ausgabe (Texterkennung / Fehler)
self.output_text = tk.Text(self.root, height=6, bg="#ffffff", fg=TEXT_COLOR, font=("Courier", 10))
self.output_text.pack(pady=5, padx=10, fill="both", expand=True)
self.output_text.insert(tk.END, "Hier erscheint die Texterkennung...\n")
# Textfeld vorübergehend schreibgeschützt machen
self.output_text.config(state="disabled")
def log_output(self, text):
"""Hilfsfunktion, um Text in das untere Textfeld zu schreiben"""
self.output_text.config(state="normal")
self.output_text.insert(tk.END, text + "\n")
self.output_text.see(tk.END) # Automatisch nach unten scrollen
self.output_text.config(state="disabled")
def start_translation(self):
self.record_btn.config(state="disabled", text="Arbeitet...", bg=FRAME_BG)
self.status_label.config(text="Aufnahme läuft...")
# Textfeld für neuen Vorgang leeren
self.output_text.config(state="normal")
self.output_text.delete(1.0, tk.END)
self.output_text.config(state="disabled")
thread = threading.Thread(target=self.run_translator_script)
thread.start()
def run_translator_script(self):
from_code = LANGUAGES.get(self.from_lang_name.get(), "de")
to_code = LANGUAGES.get(self.to_lang_name.get(), "en")
cmd = [str(PYTHON_EXEC), str(TRANSLATOR_SCRIPT), "--from", from_code, "--to", to_code, "--seconds", "5"]
try:
# subprocess mit stdout=subprocess.PIPE starten, um die Text-Ausgabe der translator.py abzufangen
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
# Die Textausgaben Zeile für Zeile abfangen und ins GUI-Textfeld schreiben
for line in process.stdout:
# after() nutzen, da wir aus einem Hintergrund-Thread die GUI aktualisieren
self.root.after(0, self.log_output, line.strip())
process.wait() # Warten, bis das Hauptprogramm fertig ist
if process.returncode == 0:
self.root.after(0, lambda: self.status_label.config(text="Übersetzung abgeschlossen."))
else:
self.root.after(0, lambda: self.status_label.config(text="Fehler bei der Ausführung!"))
except Exception as e:
self.root.after(0, lambda: self.status_label.config(text="Ein Fehler ist aufgetreten."))
self.root.after(0, self.log_output, f"Fehler: {str(e)}")
finally:
self.root.after(3000, lambda: self.status_label.config(text="Bereit für die Aufnahme."))
self.root.after(0, lambda: self.record_btn.config(state="normal", text="Aufnahme", bg=BTN_COLOR))
if __name__ == "__main__":
root = tk.Tk()
app = TranslatorGUI(root)
root.mainloop()

