# 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