Initial commit
Dieser Commit ist enthalten in:
272
localization/language_manager.py
Normale Datei
272
localization/language_manager.py
Normale Datei
@ -0,0 +1,272 @@
|
||||
# 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
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren