272 Zeilen
10 KiB
Python
272 Zeilen
10 KiB
Python
# Path: localization/language_manager.py
|
|
|
|
"""
|
|
Sprachmanager für die Übersetzung der Benutzeroberfläche.
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import logging
|
|
import time
|
|
from typing import Dict, Any, Optional
|
|
from PyQt5.QtCore import QObject, pyqtSignal, QSettings
|
|
|
|
logger = logging.getLogger("language_manager")
|
|
|
|
class LanguageManager(QObject):
|
|
"""Verwaltet die Sprachen und Übersetzungen der Anwendung."""
|
|
|
|
# Signal, das ausgelöst wird, wenn sich die Sprache ändert
|
|
language_changed = pyqtSignal(str)
|
|
|
|
# Signal, das den Status des Sprachwechsels anzeigt (True = läuft, False = abgeschlossen)
|
|
language_change_status = pyqtSignal(bool)
|
|
|
|
def __init__(self, app=None):
|
|
"""
|
|
Initialisiert den Sprachmanager.
|
|
|
|
Args:
|
|
app: Die QApplication-Instanz
|
|
"""
|
|
super().__init__()
|
|
self.app = app
|
|
self.current_language = "de" # Standard ist Deutsch
|
|
self.translations = {}
|
|
self.available_languages = {}
|
|
self.last_change_time = 0 # Zeitpunkt des letzten Sprachwechsels
|
|
self.change_cooldown = 0.5 # Cooldown in Sekunden zwischen Sprachwechseln
|
|
self.is_changing_language = False # Status-Variable für laufenden Sprachwechsel
|
|
|
|
# Basisverzeichnis für Sprachdateien ermitteln
|
|
self.base_dir = os.path.dirname(os.path.abspath(__file__))
|
|
self.languages_dir = os.path.join(self.base_dir, "languages")
|
|
|
|
# Verfügbare Sprachen ermitteln und laden
|
|
self._discover_languages()
|
|
|
|
# Lade die gespeicherte Sprache, falls vorhanden
|
|
self.settings = QSettings("Chimaira", "SocialMediaAccountGenerator")
|
|
saved_language = self.settings.value("language", "de")
|
|
|
|
if saved_language in self.available_languages:
|
|
self.current_language = saved_language
|
|
|
|
self._load_language(self.current_language)
|
|
|
|
logger.info(f"Sprachmanager initialisiert mit Sprache: {self.current_language}")
|
|
|
|
def _discover_languages(self):
|
|
"""Ermittelt die verfügbaren Sprachen aus den Sprachdateien."""
|
|
self.available_languages = {}
|
|
|
|
try:
|
|
# Alle JSON-Dateien im Sprachverzeichnis suchen
|
|
language_files = []
|
|
for filename in os.listdir(self.languages_dir):
|
|
if filename.endswith(".json"):
|
|
language_code = filename.split(".")[0]
|
|
language_path = os.path.join(self.languages_dir, filename)
|
|
language_files.append((language_code, language_path))
|
|
|
|
# Definierte Reihenfolge der Sprachen
|
|
ordered_codes = ["de", "en", "fr", "es", "ja"]
|
|
|
|
# Sortierte Liste erstellen
|
|
for code in ordered_codes:
|
|
for language_code, language_path in language_files:
|
|
if language_code == code:
|
|
# Sprachinformationen aus der Datei lesen
|
|
try:
|
|
with open(language_path, 'r', encoding='utf-8') as file:
|
|
language_data = json.load(file)
|
|
language_name = language_data.get("language_name", language_code)
|
|
self.available_languages[language_code] = {
|
|
"name": language_name,
|
|
"path": language_path
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Laden der Sprachinformationen für {language_code}: {e}")
|
|
|
|
# Eventuelle restliche Sprachen hinzufügen
|
|
for language_code, language_path in language_files:
|
|
if language_code not in self.available_languages:
|
|
try:
|
|
with open(language_path, 'r', encoding='utf-8') as file:
|
|
language_data = json.load(file)
|
|
language_name = language_data.get("language_name", language_code)
|
|
self.available_languages[language_code] = {
|
|
"name": language_name,
|
|
"path": language_path
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Laden der Sprachinformationen für {language_code}: {e}")
|
|
|
|
logger.info(f"Verfügbare Sprachen: {', '.join(self.available_languages.keys())}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Ermitteln der verfügbaren Sprachen: {e}")
|
|
|
|
def _load_language(self, language_code: str) -> bool:
|
|
"""
|
|
Lädt die Übersetzungen für eine Sprache.
|
|
|
|
Args:
|
|
language_code: Der Sprachcode (z.B. "de", "en")
|
|
|
|
Returns:
|
|
bool: True bei Erfolg, False bei Fehler
|
|
"""
|
|
if language_code not in self.available_languages:
|
|
logger.error(f"Sprache {language_code} nicht verfügbar")
|
|
return False
|
|
|
|
try:
|
|
language_path = self.available_languages[language_code]["path"]
|
|
|
|
with open(language_path, 'r', encoding='utf-8') as file:
|
|
self.translations = json.load(file)
|
|
|
|
self.current_language = language_code
|
|
logger.info(f"Sprache {language_code} geladen")
|
|
|
|
# Signal auslösen, dass sich die Sprache geändert hat
|
|
self.language_changed.emit(language_code)
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Laden der Sprache {language_code}: {e}")
|
|
return False
|
|
|
|
def get_text(self, key: str, default: str = None) -> str:
|
|
"""
|
|
Gibt den übersetzten Text für einen Schlüssel zurück.
|
|
|
|
Args:
|
|
key: Der Schlüssel für den Text (z.B. "main.title")
|
|
default: Der Standardtext, falls der Schlüssel nicht gefunden wurde
|
|
|
|
Returns:
|
|
str: Der übersetzte Text oder der Standardtext
|
|
"""
|
|
# Wenn kein Standardtext angegeben wurde, verwende den Schlüssel
|
|
if default is None:
|
|
default = key
|
|
|
|
# Versuche, den Text aus den Übersetzungen zu holen
|
|
try:
|
|
# Unterstütze verschachtelte Schlüssel mit Punktnotation
|
|
parts = key.split('.')
|
|
result = self.translations
|
|
|
|
for part in parts:
|
|
if part in result:
|
|
result = result[part]
|
|
else:
|
|
return default
|
|
|
|
# Wenn das Ergebnis ein unterstützter Datentyp ist, gib es zurück
|
|
if isinstance(result, (str, list, dict)):
|
|
return result
|
|
else:
|
|
logger.warning(
|
|
f"Schlüssel {key} hat unerwarteten Typ: {type(result)} - {result}"
|
|
)
|
|
return default
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Fehler beim Abrufen des Textes für Schlüssel {key}: {e}")
|
|
return default
|
|
|
|
def change_language(self, language_code: str) -> bool:
|
|
"""
|
|
Ändert die aktive Sprache.
|
|
|
|
Args:
|
|
language_code: Der Sprachcode (z.B. "de", "en")
|
|
|
|
Returns:
|
|
bool: True bei Erfolg, False bei Fehler
|
|
"""
|
|
# Wenn bereits ein Sprachwechsel läuft, blockiere weitere Wechsel
|
|
if self.is_changing_language:
|
|
logger.debug(f"Ein Sprachwechsel läuft bereits, ignoriere Wechsel zu {language_code}")
|
|
return False
|
|
|
|
# Cooldown prüfen
|
|
current_time = time.time()
|
|
if current_time - self.last_change_time < self.change_cooldown:
|
|
logger.debug("Sprachwechsel zu schnell hintereinander")
|
|
return False
|
|
|
|
# Wenn es die gleiche Sprache ist, nichts tun
|
|
if language_code == self.current_language:
|
|
return True
|
|
|
|
# Status auf "Sprachwechsel läuft" setzen
|
|
self.is_changing_language = True
|
|
self.language_change_status.emit(True)
|
|
|
|
# Versuche die Sprache zu wechseln
|
|
success = self._load_language(language_code)
|
|
|
|
if success:
|
|
# Sprache in Einstellungen speichern
|
|
self.settings.setValue("language", language_code)
|
|
self.settings.sync()
|
|
# Aktualisiere den Zeitpunkt des letzten Sprachwechsels
|
|
self.last_change_time = time.time()
|
|
|
|
# Nach einer kurzen Verzögerung den Status zurücksetzen
|
|
# um sicherzustellen, dass die UI-Aktualisierung abgeschlossen ist
|
|
from PyQt5.QtCore import QTimer
|
|
QTimer.singleShot(500, self._reset_change_status)
|
|
|
|
return success
|
|
|
|
def _reset_change_status(self):
|
|
"""Setzt den Status des Sprachwechsels zurück."""
|
|
self.is_changing_language = False
|
|
self.language_change_status.emit(False)
|
|
logger.debug("Sprachwechsel-Status zurückgesetzt")
|
|
|
|
def get_current_language(self) -> str:
|
|
"""Gibt den Code der aktuellen Sprache zurück."""
|
|
return self.current_language
|
|
|
|
def get_language_name(self, language_code: str = None) -> str:
|
|
"""
|
|
Gibt den Namen einer Sprache zurück.
|
|
|
|
Args:
|
|
language_code: Der Sprachcode oder None für die aktuelle Sprache
|
|
|
|
Returns:
|
|
str: Der Name der Sprache
|
|
"""
|
|
if language_code is None:
|
|
language_code = self.current_language
|
|
|
|
if language_code in self.available_languages:
|
|
return self.available_languages[language_code].get("name", language_code)
|
|
else:
|
|
return language_code
|
|
|
|
def get_available_languages(self) -> Dict[str, str]:
|
|
"""
|
|
Gibt eine Liste der verfügbaren Sprachen zurück.
|
|
|
|
Returns:
|
|
Dict[str, str]: Ein Dictionary mit Sprachcodes als Schlüssel und Sprachnamen als Werten
|
|
"""
|
|
return {code: info.get("name", code) for code, info in self.available_languages.items()}
|
|
|
|
def is_language_change_in_progress(self) -> bool:
|
|
"""
|
|
Gibt zurück, ob gerade ein Sprachwechsel im Gange ist.
|
|
|
|
Returns:
|
|
bool: True wenn ein Sprachwechsel läuft, False sonst
|
|
"""
|
|
return self.is_changing_language |