20 KiB
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:
{
"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:
const { t } = useI18n()
2. Template szövegek cseréje:
<!-- Eredeti: -->
<h1 class="text-2xl font-bold text-white">Kitüntetések</h1>
<!-- Javított: -->
<h1 class="text-2xl font-bold text-white">{{ t('gamification.badges.title') }}</h1>
3. Script szekcióban használt szövegek:
// 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:
{
"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):
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:
# 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:
- A frontend admin felület 12 Vue oldalán 0% az i18n használat - minden UI szöveg hardcoded magyar nyelven van.
- A lokációs fájlok egyetlen gamification kulcsot sem tartalmaznak - a teljes gamification szótár hiányzik.
- A backend API vegyesen használ magyar és angol hardcoded szövegeket.
- A kvíz rendszer teljes egészében hardcoded magyar nyelvű.
- 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.