# Gamification Nyelvi Modul Audit és Javítási Javaslat ## Összefoglaló **Dátum:** 2026-06-29 **Auditor:** Rendszer-Architect **Vizsgált fájlok:** 16 db (3 backend + 12 frontend admin + 1 konfig) **Gitea Issue:** #339 --- ## 1. Vizsgált Fájlok Listája ### Backend (3 fájl) | # | Fájl | Sorok | Érintettség | |---|------|-------|-------------| | 1 | `backend/app/models/gamification/gamification.py` | 174 | Nincs hardcoded string (ORM modellek) | | 2 | `backend/app/services/gamification_service.py` | 231 | 1 db hardcoded emoji string (L197) | | 3 | `backend/app/api/v1/endpoints/gamification.py` | 907 | **~25+ hardcoded string** (hibaüzenetek, kvíz, achievementek) | ### Frontend Admin (12 Vue fájl) | # | Fájl | Sorok | Hardcoded szövegek | |---|------|-------|-------------------| | 1 | `frontend_admin/pages/gamification/badges.vue` | 363 | Minden UI szöveg magyar | | 2 | `frontend_admin/pages/gamification/competitions.vue` | 494 | Minden UI szöveg magyar | | 3 | `frontend_admin/pages/gamification/config.vue` | 543 | Minden UI szöveg magyar | | 4 | `frontend_admin/pages/gamification/index.vue` | 220 | Minden UI szöveg magyar | | 5 | `frontend_admin/pages/gamification/leaderboard.vue` | 398 | Minden UI szöveg magyar | | 6 | `frontend_admin/pages/gamification/ledger.vue` | 390 | Minden UI szöveg magyar | | 7 | `frontend_admin/pages/gamification/levels.vue` | 369 | Minden UI szöveg magyar | | 8 | `frontend_admin/pages/gamification/parameters.vue` | 283 | Minden UI szöveg magyar | | 9 | `frontend_admin/pages/gamification/point-rules.vue` | 362 | Minden UI szöveg magyar | | 10 | `frontend_admin/pages/gamification/seasons.vue` | 406 | Minden UI szöveg magyar | | 11 | `frontend_admin/pages/gamification/users/index.vue` | 250 | Minden UI szöveg magyar | | 12 | `frontend_admin/pages/gamification/users/[id].vue` | 390 | Minden UI szöveg magyar | ### Lokációs fájlok (4 db) | # | Fájl | Meglévő kulcsok | Gamification kulcsok | |---|------|-----------------|---------------------| | 1 | `frontend_admin/locales/en.json` | packages, permissions, garages, persons, users, dashboard | **NINCS** | | 2 | `frontend_admin/locales/hu.json` | packages, permissions, garages, persons, users, dashboard | **NINCS** | | 3 | `backend/static/locales/en.json` | AUTH, SENTINEL, COMMON, EMAIL, ORGANIZATION, REGISTRATION_DOCUMENTS | **NINCS** | | 4 | `backend/static/locales/hu.json` | AUTH, SENTINEL, COMMON, EMAIL, ORGANIZATION, REGISTRATION_DOCUMENTS | **NINCS** | --- ## 2. Részletes Hardcoded Stringek ### 2.1 Backend API (`backend/app/api/v1/endpoints/gamification.py`) #### 2.1.1 Magyar nyelvű hibaüzenetek | Sor | Kód | Szöveg | |-----|-----|--------| | 289 | `detail=` | `"Önvédelmi korlátozás miatt nem küldhetsz be új szerviz adatokat."` | | 380 | `detail=` | `f"Hiba a pontozás során: {str(e)}"` | | 387 | `message:` | `"Szerviz beküldve a rendszerbe elemzésre!"` | | 394 | `detail=` | `f"Hiba a beküldés során: {str(e)}"` | #### 2.1.2 Angol nyelvű hibaüzenetek | Sor | Kód | Szöveg | |-----|-----|--------| | 191 | `detail=` | `"Season not found"` | | 488-492 | `detail=` | `"You have already played the daily quiz today..."` | | 810-811 | `detail=` | `"Badge not found"` | #### 2.1.3 Keménykódolt kvíz tartalom (magyar) | Sor | Tartalom | |-----|----------| | 496-517 | 3 db kvíz kérdés teljes egészében magyar nyelven: kérdések, válaszlehetőségek, magyarázatok | | 553-557 | Kvíz válasz adatok magyar nyelven | #### 2.1.4 Achievement címek (angol) | Sor | Cím | |-----|-----| | 857-861 | `"Novice"`, `"Apprentice"`, `"Expert"`, `"Master"` (XP achievementek) | | 884-888 | `"Quiz Beginner"`, `"Quiz Enthusiast"`, `"Quiz Master"` (kvíz achievementek) | ### 2.2 Backend Service (`backend/app/services/gamification_service.py`) | Sor | Kód | |-----|-----| | 197 | `f"🔴 PENALTY: {reason}"` - emoji + angol szöveg a penalty bejegyzéshez | ### 2.3 Frontend Admin Vue Oldalak Minden a 12 gamification Vue oldal **100%-ban hardcoded magyar nyelvű szövegeket** használ. Sehol sem található `useI18n()` vagy `$t()` hívás. #### Kulcskategóriák oldalanként: | Oldal | Hardcoded szövegek kategóriái | |-------|-------------------------------| | **badges.vue** | "Kitüntetések", "Még nincsenek kitüntetések", "Új kitüntetés", "Név", "Leírás", "Ikon URL", "Mégse", "Mentés", "Létrehozás", "Biztosan törölni szeretnéd?" | | **competitions.vue** | "Versenyek", "Szezonális versenyek kezelése", "Új Verseny", "Draft", "Aktív", "Befejezett", "Törölt", "Érvénytelen JSON formátum!" | | **config.vue** | "Rendszer Konfig", "XP Logic", "Conversion Logic", "Penalty Logic", "Level Rewards", "Hatás Kalkulátor", "Nyers JSON Szerkesztő" | | **index.vue** | "Gamification HQ", "Összes Felhasználó", "Aktív Szezon", "Függő Hozzájárulások", "Ma Szerzett XP", "Gyors Műveletek" | | **leaderboard.vue** | "Ranglista", "Keresés", "Név vagy email...", "Szint", "Összes szint", "Minimum XP", "Rendezés", "Enyhe", "Közepes", "Súlyos" | | **ledger.vue** | "Pontnapló", "User ID", "Dátumtól", "Dátumig", "Keresés", "Törlés", "CSV", "Előző", "Következő" | | **levels.vue** | "Szintek", "Új szint", "Rang", "Min. Pont", "Típus", "Büntető", "Normál", "Szint Fa Diagram" | | **parameters.vue** | "Rendszerparaméterek", "Kulcs", "Érték", "Kategória", "Hatósugár", "Scope ID" | | **point-rules.vue** | "Pontszabályok", "Új pontszabály", "Akció Kulcs", "Pont", "Leírás", "Státusz", "Aktív", "Inaktív" | | **seasons.vue** | "Szezonok", "Új Szezon", "Név", "Kezdő dátum", "Záró dátum", "Szezon Aktiválása" | | **users/index.vue** | "Gamification Felhasználók", "Összes szint", "Csak büntetettek", "Csak büntetlenek", "Részletek" | | **users/[id].vue** | "Vissza a listához", "Összes XP", "Büntetőpontok", "Restrikciós Szint", "Büntetés Kiszabása", "Jutalom Kiosztása" | --- ## 3. Javítási Javaslat (Fix Proposal) ### 3.1 Frontend Admin - i18n Migráció #### 3.1.1 Lokációs kulcs struktúra javaslat A `frontend_admin/locales/en.json` és `frontend_admin/locales/hu.json` fájlokba be kell vezetni egy új `gamification` kulcs hierarchical struktúrát. **Teljes kulcs struktúra:** ```json { "gamification": { "dashboard": { "title": "Gamification HQ", "subtitle": "Gamification system overview dashboard", "total_users": "Total Users", "active_season": "Active Season", "pending_contributions": "Pending Contributions", "xp_today": "XP Earned Today", "quick_actions": "Quick Actions", "manage_point_rules": "Manage Point Rules", "manage_badges": "Manage Badges", "manage_seasons": "Manage Seasons", "recent_ledger": "Recent Ledger Entries", "top_5_leaderboard": "Top 5 Leaderboard" }, "badges": { "title": "Badges", "subtitle": "Badge management — CRUD", "loading": "Loading badges...", "error": "Failed to load badges", "create_new": "Create New Badge", "edit": "Edit Badge", "delete": "Delete Badge", "name": "Name", "description": "Description", "icon_url": "Icon URL", "cancel": "Cancel", "save": "Save", "create": "Create", "delete_confirm": "Are you sure you want to delete this badge?", "save_success": "Badge saved successfully!", "create_success": "Badge created successfully!", "no_badges": "No badges yet. Create one with the button above!" }, "competitions": { "title": "Competitions", "subtitle": "Seasonal competition management", "create_new": "Create Competition", "filter_season": "Filter by season:", "all_seasons": "All seasons", "active": "(Active)", "loading": "Loading competitions...", "error": "Failed to load competitions", "no_competitions": "No competitions created yet.", "create_first": "Create First Competition", "name": "Name", "description": "Description", "season": "Season", "start_date": "Start Date", "end_date": "End Date", "status": "Status", "status_draft": "Draft", "status_active": "Active", "status_completed": "Completed", "status_cancelled": "Cancelled", "view_rules": "View Rules", "edit": "Edit", "invalid_json": "Invalid JSON format!" }, "config": { "title": "System Config", "subtitle": "Gamification master configuration editor", "loading": "Loading configuration...", "error": "Failed to load configuration", "last_saved": "Last saved:", "save": "Save Configuration", "xp_logic": "XP Logic", "conversion_logic": "Conversion Logic", "penalty_logic": "Penalty Logic", "level_rewards": "Level Rewards", "impact_calculator": "Impact Calculator", "raw_json_editor": "Raw JSON Editor", "user_level": "User Level:", "daily_activities": "Daily Activities:", "social_points": "Social Points:", "estimated_results": "Estimated Results", "daily_xp": "Daily XP", "xp_to_next_level": "XP Until Next Level" }, "leaderboard": { "title": "Leaderboard", "subtitle": "Gamification leaderboard admin view", "search": "Search", "search_placeholder": "Name or email...", "level": "Level", "all_levels": "All levels", "min_xp": "Minimum XP", "sort": "Sort", "sort_xp": "By XP", "sort_points": "By Points", "sort_social": "By Social Points", "sort_level": "By Level", "filter": "Filter", "reset": "Reset", "ascending": "Ascending", "descending": "Descending", "rank": "#", "user": "User", "email": "Email", "xp": "XP", "level": "Level", "points": "Points", "social": "Social", "penalty": "Penalty", "restriction": "Restriction", "discoveries": "Discoveries", "services": "Services", "last_modified": "Last Modified", "actions": "Actions", "restriction_mild": "Mild", "restriction_moderate": "Moderate", "restriction_severe": "Severe", "prev": "Previous", "next": "Next", "per_page": "/ page" }, "ledger": { "title": "Points Ledger", "subtitle": "Gamification points ledger with filtering and detailed logging", "loading": "Loading ledger...", "error": "Failed to load ledger", "user_id": "User ID", "date_from": "Date From", "date_to": "Date To", "reason": "Reason (text)", "search": "Search", "clear": "Clear", "csv": "CSV Export", "no_results": "No results with the current filters.", "id": "ID", "user": "User", "points": "Points", "penalty": "Penalty", "xp": "XP", "reason_col": "Reason", "source": "Source", "date": "Date", "prev": "Previous", "next": "Next", "of": "of", "showing": "Showing", "filtered": "(filtered)" }, "levels": { "title": "Levels", "subtitle": "Level configurations management — CRUD", "create_new": "Create New Level", "level": "Level", "rank": "Rank", "min_points": "Min Points", "type": "Type", "actions": "Actions", "type_penalty": "Penalty", "type_normal": "Normal", "level_tree": "Level Tree Diagram", "no_levels": "No level configurations yet.", "level_number": "Level Number", "rank_name": "Rank Name", "minimum_points": "Minimum Points", "is_penalty": "Penalty Level", "positive_hint": "Positive: normal level, Negative: penalty level" }, "parameters": { "title": "System Parameters", "subtitle": "Gamification category system parameters", "loading": "Loading parameters...", "key": "Key", "value": "Value", "category": "Category", "scope": "Scope", "scope_id": "Scope ID", "actions": "Actions", "edit": "Edit Parameter", "cancel": "Cancel", "save": "Save" }, "point_rules": { "title": "Point Rules", "subtitle": "Gamification point rules management — CRUD", "create_new": "Create New Point Rule", "action_key": "Action Key", "points": "Points", "description": "Description", "status": "Status", "active": "Active", "inactive": "Inactive", "edit": "Edit Point Rule", "point_value": "Point Value", "cancel": "Cancel", "save": "Save", "delete": "Delete", "duplicate_key": "A rule with this action key already exists.", "save_success": "Point rule saved successfully!", "create_success": "Point rule created successfully!" }, "seasons": { "title": "Seasons", "subtitle": "Gamification season management", "create_new": "Create Season", "loading": "Loading seasons...", "error": "Failed to load seasons", "no_seasons": "No seasons created yet.", "create_first": "Create First Season", "active": "Active", "inactive": "Inactive", "edit": "Edit Season", "new_season": "New Season", "name": "Name", "start_date": "Start Date", "end_date": "End Date", "activate_on_create": "Activate on creation", "activate": "Activate Season", "activate_confirm": "Are you sure you want to activate this season?", "save_success": "Season saved successfully!", "create_success": "Season created successfully!" }, "users": { "title": "Gamification Users", "subtitle": "User gamification statistics management", "level": "Level", "all_levels": "All levels", "min_xp": "Minimum XP", "max_xp": "Maximum XP", "penalty": "Penalty", "all_penalty": "All", "only_penalized": "Penalized only", "only_non_penalized": "Non-penalized only", "filter": "Filter", "reset": "Reset", "user_id": "User ID", "xp": "XP", "level": "Level", "points": "Points", "penalty": "Penalty", "restriction": "Restriction", "services": "Services", "discoveries": "Discoveries", "last_modified": "Last Modified", "actions": "Actions", "restriction_mild": "Mild", "restriction_moderate": "Moderate", "restriction_severe": "Severe", "restriction_none": "None", "details": "Details", "prev": "Previous", "next": "Next", "user_detail_title": "User Gamification Details", "back_to_list": "Back to list", "total_xp": "Total XP", "total_points": "Total Points", "social_points": "Social Points", "penalty_points": "Penalty Points", "restriction_level": "Restriction Level", "penalty_quota": "Penalty Quota", "banned": "Banned", "services": "Services", "discovered_places": "Discovered Places", "validated_places": "Validated Places", "added_providers": "Added Providers", "assign_penalty": "Assign Penalty", "assign_reward": "Assign Reward", "penalty_points_label": "Penalty Points", "reason": "Reason", "xp_reward": "XP Reward", "social_reward": "Social Points Reward", "apply_penalty": "Apply Penalty", "apply_reward": "Apply Reward", "ledger": "Points Ledger", "entries": "entries", "date": "Date", "points": "Points", "penalty": "Penalty", "xp": "XP", "reason": "Reason", "source": "Source" } } } ``` #### 3.1.2 Vue komponensek átalakítása Minden Vue fájlban a következő módosításokat kell elvégezni: **1. `useI18n()` import hozzáadása:** ```typescript const { t } = useI18n() ``` **2. Template szövegek cseréje:** ```vue

Kitüntetések

{{ t('gamification.badges.title') }}

``` **3. Script szekcióban használt szövegek:** ```typescript // Eredeti: message.value = 'Kitüntetés sikeresen frissítve!' // Javított: message.value = t('gamification.badges.save_success') ``` ### 3.2 Backend - i18n Migráció #### 3.2.1 Lokációs kulcs struktúra backendhez A `backend/static/locales/en.json` és `backend/static/locales/hu.json` fájlokba be kell vezetni: ```json { "GAMIFICATION": { "SUBMIT_SERVICE": { "SELF_DEFENSE_BLOCKED": "You cannot submit new service data due to self-defense restriction.", "SUCCESS": "Service submitted to the system for analysis!", "POINTS_ERROR": "Error during scoring: {error}", "SUBMIT_ERROR": "Error during submission: {error}" }, "QUIZ": { "ALREADY_PLAYED": "You have already played the daily quiz today. Come back tomorrow!", "NO_QUIZ_AVAILABLE": "No quiz available for today." }, "BADGE": { "NOT_FOUND": "Badge not found.", "ALREADY_AWARDED": "Badge already awarded to this user." }, "SEASON": { "NOT_FOUND": "Season not found." }, "ACHIEVEMENTS": { "XP_NOVICE": "Novice", "XP_APPRENTICE": "Apprentice", "XP_EXPERT": "Expert", "XP_MASTER": "Master", "QUIZ_BEGINNER": "Quiz Beginner", "QUIZ_ENTHUSIAST": "Quiz Enthusiast", "QUIZ_MASTER": "Quiz Master" } } } ``` #### 3.2.2 Backend API módosítások A backend végpontokban a hardcoded stringek helyett egy i18n/lokalizációs segédfüggvényt kell használni, amely a kérés nyelvi preferenciája (`Accept-Language` header vagy a felhasználó beállított nyelve) alapján választja ki a megfelelő szöveget. Példa implementáció (`app/core/i18n.py`): ```python import json from pathlib import Path _LOCALES_DIR = Path(__file__).parent.parent.parent / "static" / "locales" _cache = {} def get_locale(lang: str = "hu") -> dict: if lang not in _cache: path = _LOCALES_DIR / f"{lang}.json" if path.exists(): with open(path, "r", encoding="utf-8") as f: _cache[lang] = json.load(f) else: _cache[lang] = {} return _cache[lang] def t(key: str, lang: str = "hu", **kwargs) -> str: """Translate a dot-notation key using the locale file.""" locale = get_locale(lang) parts = key.split(".") value = locale for part in parts: if isinstance(value, dict): value = value.get(part, key) else: return key if isinstance(value, str) and kwargs: return value.format(**kwargs) return value if isinstance(value, str) else key ``` #### 3.2.3 Kvíz tartalom lokalizációja A kvíz kérdéseket ki kell venni a kódból, és adatbázisba vagy JSON fájlba kell helyezni nyelvi kulcsokkal. ### 3.3 Backend Service módosítás A `backend/app/services/gamification_service.py` L197-es sorában: ```python # Eredeti: f"🔴 PENALTY: {reason}" # Javasolt: # A reason paraméter tartalmazza a lokalizált szöveget, amit a hívó fél (API endpoint) már lefordított reason ``` ### 3.4 Migrációs Terv (Ütemezés) | Fázis | Feladat | Érintett fájlok | Logikai készültség | |-------|---------|----------------|-------------------| | **1. fázis** | Gamification i18n kulcsok felvétele a `frontend_admin/locales/*.json` fájlokba | 2 db | Előkészület | | **2. fázis** | 12 Vue oldal átalakítása: `useI18n()` bevezetése, `t()` hívások | 12 db | Tervezés alatt | | **3. fázis** | Backend i18n helper megírása (`app/core/i18n.py`) | 1 db | Tervezés alatt | | **4. fázis** | Gamification kulcsok felvétele a `backend/static/locales/*.json` fájlokba | 2 db | Előkészület | | **5. fázis** | Backend API endpointok átalakítása, hardcoded stringek eltávolítása | 1 db | Tervezés alatt | | **6. fázis** | Kvíz tartalom kiszervezése JSON/DB-be, nyelvi kulcsokkal | 1 db | Tervezés alatt | | **7. fázis** | Tesztelés: magyar és angol nyelvű felület ellenőrzése | - | Függőben | --- ## 4. Konklúzió A gamification modulban **súlyos nyelvi modul hiányosság** áll fenn: 1. **A frontend admin felület 12 Vue oldalán 0% az i18n használat** - minden UI szöveg hardcoded magyar nyelven van. 2. **A lokációs fájlok egyetlen gamification kulcsot sem tartalmaznak** - a teljes gamification szótár hiányzik. 3. **A backend API vegyesen használ magyar és angol hardcoded szövegeket**. 4. **A kvíz rendszer teljes egészében hardcoded magyar nyelvű**. 5. **A meglévő i18n infrastruktúra (`@nuxtjs/i18n`) jól konfigurált** - csak a gamification modul nem használja. A javítási javaslat 7 fázisban, körülbelül 15-20 fájl módosításával oldható meg.