# /opt/docker/dev/service_finder/backend/app/services/translation_service.py import json import os import logging from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, update from app.models.translation import Translation from app.core.config import settings from typing import Dict, Any, Optional logger = logging.getLogger(__name__) class TranslationService: """ Dinamikus fordítás-kezelő szerviz. Támogatja a szerveroldali cache-elést és a frontend JSON exportot. """ # Memória-cache a szerveroldali hibaüzenetekhez és emailekhez _published_cache: Dict[str, Dict[str, str]] = {} @classmethod async def load_cache(cls, db: AsyncSession): """ Betölti a publikált szövegeket a memóriába az adatbázisból. """ stmt = select(Translation).where(Translation.is_published == True) result = await db.execute(stmt) translations = result.scalars().all() cls._published_cache = {} for t in translations: # JAVÍTVA: t.lang_code helyett t.lang if t.lang not in cls._published_cache: cls._published_cache[t.lang] = {} cls._published_cache[t.lang][t.key] = t.value logger.info(f"🌍 i18n Motor: {len(translations)} szöveg aktiválva a memóriában.") @classmethod def get_text(cls, key: str, lang: str = "hu", variables: Optional[Dict[str, Any]] = None) -> str: """ Szerveroldali lekérés Fallback (EN) logikával és változó behelyettesítéssel. Példa: get_text("AUTH.WELCOME", "hu", {"name": "Péter"}) """ # 1. Kért nyelv lekérése text = cls._published_cache.get(lang, {}).get(key) # 2. Fallback angolra, ha nincs meg a kért nyelven if not text and lang != "en": text = cls._published_cache.get("en", {}).get(key) # 3. Ha sehol nincs meg, adjuk vissza a kulcsot if not text: return f"[{key}]" # 4. Változók behelyettesítése (pl. {{name}}) if variables: for k, v in variables.items(): text = text.replace(f"{{{{{k}}}}}", str(v)) return text @classmethod async def publish_all(cls, db: AsyncSession): """ Minden piszkozatot élesít, frissíti a memóriát és legenerálja a JSON-öket. """ await db.execute( update(Translation).where(Translation.is_published == False).values(is_published=True) ) await db.commit() await cls.load_cache(db) await cls.export_to_json(db) return True @staticmethod async def export_to_json(db: AsyncSession): """ Adatbázis -> Hierarchikus JSON struktúra generálása a Frontend számára. 'AUTH.LOGIN.TITLE' -> { "AUTH": { "LOGIN": { "TITLE": "..." } } } """ stmt = select(Translation).where(Translation.is_published == True) result = await db.execute(stmt) translations = result.scalars().all() languages: Dict[str, Any] = {} for t in translations: # JAVÍTVA: t.lang_code helyett t.lang if t.lang not in languages: languages[t.lang] = {} # Kulcs felbontása szintekre hierarchikus struktúrához parts = t.key.split('.') current_level = languages[t.lang] for part in parts[:-1]: if part not in current_level: current_level[part] = {} current_level = current_level[part] current_level[parts[-1]] = t.value # Fájlok fizikai mentése a static könyvtárba locales_path = os.path.join(settings.STATIC_DIR, "locales") os.makedirs(locales_path, exist_ok=True) for lang, content in languages.items(): file_path = os.path.join(locales_path, f"{lang}.json") try: with open(file_path, "w", encoding="utf-8") as f: json.dump(content, f, ensure_ascii=False, indent=2) logger.info(f"✅ Nyelvi fájl (JSON) frissítve: {file_path}") except Exception as e: logger.error(f"❌ Hiba a fájl mentésekor ({lang}): {e}") return True translation_service = TranslationService()