From addc1a21188cdcc0e249d83772b605f6d05497ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tanguy=20Maz=C3=A9?= Date: Thu, 20 Nov 2025 01:43:14 +0100 Subject: [PATCH 1/4] Add metadata locale selection for IGDB and ScreenScraper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add provider-specific locale config with UI dropdowns in Settings - ScreenScraper: select language for synopsis/metadata - IGDB: select region for localized names/covers - Fallback warnings when locale not found 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/config/config_manager.py | 41 +++++ backend/endpoints/configs.py | 17 ++ backend/endpoints/responses/config.py | 1 + backend/handler/metadata/igdb_handler.py | 156 +++++++++++------- backend/handler/metadata/ss_handler.py | 23 ++- .../__generated__/models/ConfigResponse.ts | 1 + frontend/src/locales/cs_CZ/scan.json | 5 + frontend/src/locales/de_DE/scan.json | 5 + frontend/src/locales/en_GB/common.json | 2 + frontend/src/locales/en_GB/scan.json | 4 + frontend/src/locales/en_US/common.json | 8 + frontend/src/locales/en_US/scan.json | 4 + frontend/src/locales/es_ES/scan.json | 4 + frontend/src/locales/fr_FR/scan.json | 4 + frontend/src/locales/it_IT/scan.json | 4 + frontend/src/locales/ja_JP/scan.json | 4 + frontend/src/locales/ko_KR/scan.json | 4 + frontend/src/locales/pl_PL/scan.json | 4 + frontend/src/locales/pt_BR/scan.json | 5 + frontend/src/locales/ro_RO/scan.json | 5 + frontend/src/locales/ru_RU/scan.json | 5 + frontend/src/locales/zh_CN/scan.json | 4 + frontend/src/locales/zh_TW/scan.json | 4 + frontend/src/services/api/config.ts | 9 + frontend/src/stores/config.ts | 7 + .../src/views/Settings/MetadataSources.vue | 93 +++++++++++ 26 files changed, 357 insertions(+), 66 deletions(-) diff --git a/backend/config/config_manager.py b/backend/config/config_manager.py index a7bbdba26..5526c8caa 100644 --- a/backend/config/config_manager.py +++ b/backend/config/config_manager.py @@ -84,6 +84,7 @@ class Config: SCAN_REGION_PRIORITY: list[str] SCAN_LANGUAGE_PRIORITY: list[str] SCAN_MEDIA: list[str] + METADATA_PROVIDER_LOCALES: dict[str, str] def __init__(self, **entries): self.__dict__.update(entries) @@ -272,6 +273,11 @@ class ConfigManager: "manual", ], ), + METADATA_PROVIDER_LOCALES=pydash.get( + self._raw_config, + "scan.provider_locales", + {}, + ), ) def _get_ejs_controls(self) -> dict[str, EjsControls]: @@ -461,6 +467,19 @@ class ConfigManager: ) sys.exit(3) + if not isinstance(self.config.METADATA_PROVIDER_LOCALES, dict): + log.critical( + "Invalid config.yml: scan.provider_locales must be a dictionary" + ) + sys.exit(3) + else: + for provider, locale in self.config.METADATA_PROVIDER_LOCALES.items(): + if not isinstance(locale, str): + log.critical( + f"Invalid config.yml: scan.provider_locales.{provider} must be a string" + ) + sys.exit(3) + def get_config(self) -> Config: try: with open(self.config_file, "r") as config_file: @@ -516,6 +535,7 @@ class ConfigManager: "region": self.config.SCAN_REGION_PRIORITY, "language": self.config.SCAN_LANGUAGE_PRIORITY, }, + "provider_locales": self.config.METADATA_PROVIDER_LOCALES, }, } @@ -594,5 +614,26 @@ class ConfigManager: self.config.__setattr__(exclusion_type, config_item) self._update_config_file() + def set_provider_locale(self, provider: str, locale: str) -> None: + provider_locales = self.config.METADATA_PROVIDER_LOCALES + provider_locales[provider] = locale + self.config.METADATA_PROVIDER_LOCALES = provider_locales + self._update_config_file() + + def remove_provider_locale(self, provider: str) -> None: + provider_locales = self.config.METADATA_PROVIDER_LOCALES + + try: + del provider_locales[provider] + except KeyError: + pass + + self.config.METADATA_PROVIDER_LOCALES = provider_locales + self._update_config_file() + + def update_provider_locales(self, locales: dict[str, str]) -> None: + self.config.METADATA_PROVIDER_LOCALES = locales + self._update_config_file() + config_manager = ConfigManager() diff --git a/backend/endpoints/configs.py b/backend/endpoints/configs.py index c223f637f..104651c88 100644 --- a/backend/endpoints/configs.py +++ b/backend/endpoints/configs.py @@ -43,6 +43,7 @@ def get_config() -> ConfigResponse: SCAN_ARTWORK_PRIORITY=cfg.SCAN_ARTWORK_PRIORITY, SCAN_REGION_PRIORITY=cfg.SCAN_REGION_PRIORITY, SCAN_LANGUAGE_PRIORITY=cfg.SCAN_LANGUAGE_PRIORITY, + METADATA_PROVIDER_LOCALES=cfg.METADATA_PROVIDER_LOCALES, ) @@ -139,3 +140,19 @@ async def delete_exclusion( raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=exc.message ) from exc + + +@protected_route(router.put, "/provider-locales", [Scope.PLATFORMS_WRITE]) +async def update_provider_locales(request: Request) -> None: + """Update metadata provider locales in the configuration""" + + data = await request.json() + locales = data.get("locales", {}) + + try: + cm.update_provider_locales(locales) + except ConfigNotWritableException as exc: + log.critical(exc.message) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=exc.message + ) from exc diff --git a/backend/endpoints/responses/config.py b/backend/endpoints/responses/config.py index 30bf94290..214ca9930 100644 --- a/backend/endpoints/responses/config.py +++ b/backend/endpoints/responses/config.py @@ -23,3 +23,4 @@ class ConfigResponse(TypedDict): SCAN_ARTWORK_PRIORITY: list[str] SCAN_REGION_PRIORITY: list[str] SCAN_LANGUAGE_PRIORITY: list[str] + METADATA_PROVIDER_LOCALES: dict[str, str] diff --git a/backend/handler/metadata/igdb_handler.py b/backend/handler/metadata/igdb_handler.py index a45762c56..d8d22131c 100644 --- a/backend/handler/metadata/igdb_handler.py +++ b/backend/handler/metadata/igdb_handler.py @@ -13,6 +13,7 @@ from adapters.services.igdb_types import ( mark_list_expanded, ) from config import IGDB_CLIENT_ID, IGDB_CLIENT_SECRET, IS_PYTEST_RUN +from config.config_manager import config_manager as cm from handler.redis_handler import async_cache from logger.logger import log from utils.context import ctx_httpx_client @@ -209,6 +210,86 @@ def extract_metadata_from_igdb_rom(self: MetadataHandler, rom: Game) -> IGDBMeta ) +def get_igdb_preferred_locale() -> str | None: + """Get IGDB-specific locale from config, if set.""" + config = cm.get_config() + return config.METADATA_PROVIDER_LOCALES.get("igdb") + + +def extract_localized_data(rom: Game, preferred_locale: str | None) -> tuple[str, str]: + """Extract localized name and cover URL based on preferred locale. + + Returns (name, cover_url) - falls back to default if locale not found. + """ + default_name = rom.get("name", "") + default_cover = pydash.get(rom, "cover.url", "") + + if not preferred_locale: + return default_name, default_cover + + game_localizations = rom.get("game_localizations", []) + if not game_localizations: + return default_name, default_cover + + assert mark_list_expanded(game_localizations) + + for loc in game_localizations: + region = loc.get("region") + if not region: + continue + + assert mark_expanded(region) + + # Match locale by region identifier (e.g., "ja-JP", "ko-KR", "EU") + if region.get("identifier") == preferred_locale: + localized_name = loc.get("name") or default_name + localized_cover = loc.get("cover") + + if localized_cover: + assert mark_expanded(localized_cover) + cover_url = localized_cover.get("url", "") or default_cover + else: + cover_url = default_cover + + return localized_name, cover_url + + # Locale not found, fall back to default + log.warning(f"IGDB locale '{preferred_locale}' not found for '{default_name}', using default") + return default_name, default_cover + + +def build_igdb_rom(handler: "IGDBHandler", rom: Game, preferred_locale: str | None) -> "IGDBRom": + """Build an IGDBRom from IGDB game data with localization support. + + Args: + handler: IGDBHandler instance for URL normalization + rom: Game data from IGDB API + preferred_locale: Locale code (e.g., "ja-JP") or None + + Returns: + IGDBRom with localized name/cover if available + """ + rom_screenshots = rom.get("screenshots", []) + assert mark_list_expanded(rom_screenshots) + + localized_name, localized_cover = extract_localized_data(rom, preferred_locale) + + return IGDBRom( + igdb_id=rom["id"], + slug=rom.get("slug", ""), + name=localized_name, + summary=rom.get("summary", ""), + url_cover=handler.normalize_cover_url(localized_cover).replace( + "t_thumb", "t_1080p" + ), + url_screenshots=[ + handler.normalize_cover_url(s.get("url", "")).replace("t_thumb", "t_720p") + for s in rom_screenshots + ], + igdb_metadata=extract_metadata_from_igdb_rom(handler, rom), + ) + + class IGDBHandler(MetadataHandler): def __init__(self) -> None: self.igdb_service = IGDBService(twitch_auth=TwitchAuth()) @@ -464,23 +545,7 @@ class IGDBHandler(MetadataHandler): if not rom: return fallback_rom - rom_screenshots = rom.get("screenshots", []) - assert mark_list_expanded(rom_screenshots) - - return IGDBRom( - igdb_id=rom["id"], - slug=rom.get("slug", ""), - name=rom.get("name", ""), - summary=rom.get("summary", ""), - url_cover=self.normalize_cover_url( - pydash.get(rom, "cover.url", "") - ).replace("t_thumb", "t_1080p"), - url_screenshots=[ - self.normalize_cover_url(s.get("url", "")).replace("t_thumb", "t_720p") - for s in rom_screenshots - ], - igdb_metadata=extract_metadata_from_igdb_rom(self, rom), - ) + return build_igdb_rom(self, rom, get_igdb_preferred_locale()) async def get_rom_by_id(self, igdb_id: int) -> IGDBRom: if not self.is_enabled(): @@ -494,24 +559,7 @@ class IGDBHandler(MetadataHandler): if not roms: return IGDBRom(igdb_id=None) - rom = roms[0] - rom_screenshots = rom.get("screenshots", []) - assert mark_list_expanded(rom_screenshots) - - return IGDBRom( - igdb_id=rom["id"], - slug=rom.get("slug", ""), - name=rom.get("name", ""), - summary=rom.get("summary", ""), - url_cover=self.normalize_cover_url( - pydash.get(rom, "cover.url", "") - ).replace("t_thumb", "t_1080p"), - url_screenshots=[ - self.normalize_cover_url(s.get("url", "")).replace("t_thumb", "t_720p") - for s in rom_screenshots - ], - igdb_metadata=extract_metadata_from_igdb_rom(self, rom), - ) + return build_igdb_rom(self, roms[0], get_igdb_preferred_locale()) async def get_matched_rom_by_id(self, igdb_id: int) -> IGDBRom | None: if not self.is_enabled(): @@ -572,33 +620,8 @@ class IGDBHandler(MetadataHandler): if rom["id"] not in unique_ids ] - return [ - IGDBRom( - { # type: ignore[misc] - k: v - for k, v in { - "igdb_id": rom["id"], - "slug": rom.get("slug", ""), - "name": rom.get("name", ""), - "summary": rom.get("summary", ""), - "url_cover": self.normalize_cover_url( - pydash.get(rom, "cover.url", "").replace( - "t_thumb", "t_1080p" - ) - ), - "url_screenshots": [ - self.normalize_cover_url(s.get("url", "")).replace( # type: ignore[attr-defined] - "t_thumb", "t_720p" - ) - for s in rom.get("screenshots", []) - ], - "igdb_metadata": extract_metadata_from_igdb_rom(self, rom), - }.items() - if v - } - ) - for rom in matched_roms - ] + preferred_locale = get_igdb_preferred_locale() + return [build_igdb_rom(self, rom, preferred_locale) for rom in matched_roms] class TwitchAuth(MetadataHandler): @@ -675,6 +698,8 @@ class TwitchAuth(MetadataHandler): return token +SEARCH_FIELDS = ("game.id", "name") + GAMES_FIELDS = ( "id", "name", @@ -725,10 +750,13 @@ GAMES_FIELDS = ( "similar_games.cover.url", "age_ratings.rating_category", "videos.video_id", + "game_localizations.id", + "game_localizations.name", + "game_localizations.cover.url", + "game_localizations.region.identifier", + "game_localizations.region.category", ) -SEARCH_FIELDS = ("game.id", "name") - IGDB_PLATFORM_CATEGORIES: dict[int, str] = { 0: "Unknown", diff --git a/backend/handler/metadata/ss_handler.py b/backend/handler/metadata/ss_handler.py index 20c776402..b48f9d6d3 100644 --- a/backend/handler/metadata/ss_handler.py +++ b/backend/handler/metadata/ss_handler.py @@ -39,8 +39,20 @@ def get_preferred_regions() -> list[str]: def get_preferred_languages() -> list[str]: - """Get preferred languages from config""" + """Get preferred languages from config + + Uses provider-specific locale if set, otherwise falls back to language priority list. + Always includes English as final fallback. + """ config = cm.get_config() + + # Check for provider-specific locale first + provider_locale = config.METADATA_PROVIDER_LOCALES.get("ss") + if provider_locale: + # Use provider locale with English fallback + return list(dict.fromkeys([provider_locale, "en"])) + + # Fall back to language priority list return list(dict.fromkeys(config.SCAN_LANGUAGE_PRIORITY + ["en", "fr"])) @@ -393,7 +405,9 @@ def build_ss_game(rom: Rom, game: SSGame) -> SSRom: break res_summary = "" - for lang in get_preferred_languages(): + preferred_languages = get_preferred_languages() + used_lang = None + for lang in preferred_languages: res_summary = next( ( synopsis["text"] @@ -403,8 +417,13 @@ def build_ss_game(rom: Rom, game: SSGame) -> SSRom: "", ) if res_summary: + used_lang = lang break + # Log warning if we had to fall back from the preferred locale + if preferred_languages and used_lang and used_lang != preferred_languages[0]: + log.warning(f"ScreenScraper locale '{preferred_languages[0]}' not found for '{res_name}', using '{used_lang}'") + url_cover = ss_metadata["box2d_url"] url_manual = ( ss_metadata["manual_url"] diff --git a/frontend/src/__generated__/models/ConfigResponse.ts b/frontend/src/__generated__/models/ConfigResponse.ts index 0ee03b7c6..548b909dc 100644 --- a/frontend/src/__generated__/models/ConfigResponse.ts +++ b/frontend/src/__generated__/models/ConfigResponse.ts @@ -23,5 +23,6 @@ export type ConfigResponse = { SCAN_ARTWORK_PRIORITY: Array; SCAN_REGION_PRIORITY: Array; SCAN_LANGUAGE_PRIORITY: Array; + METADATA_PROVIDER_LOCALES: Record; }; diff --git a/frontend/src/locales/cs_CZ/scan.json b/frontend/src/locales/cs_CZ/scan.json index 980578fdc..3cbedc1d4 100644 --- a/frontend/src/locales/cs_CZ/scan.json +++ b/frontend/src/locales/cs_CZ/scan.json @@ -15,12 +15,17 @@ "disabled-by-admin": "Zakázáno administrátorem", "hashes": "Přepočítat hashe", "hashes-desc": "Přepočítá hashe pro vybrané platformy", +<<<<<<< HEAD "hashes-disabled-tooltip": "Výpočet hashů zakázán.

Hashe (MD5, SHA1, CRC32) jsou jedinečné otisky, které přesně identifikují soubory ROM.

Bez nich nemohou Hasheous a RetroAchievements porovnávat hry se svými databázemi, ale skenování bude rychlejší.", "hashes-enabled-tooltip": "Výpočet hashů povolen.

Budou vypočítány hashe (MD5, SHA1, CRC32) pro vytvoření jedinečných otisků každého souboru ROM.

To umožňuje Hasheous a RetroAchievements přesně identifikovat hry ve svých databázích.", "hash-calculation-disabled": "Výpočet hash je zakázán", "hasheous-requires-hashes": "Hasheous vyžaduje povolené počítání hashů", "retroachievements-requires-hashes": "RetroAchievements vyžaduje povolené počítání hashů", "manage-library": "Správa knihovny", +======= + "locale-not-supported": "Nepodporováno", + "manage-library": "Spravovat knihovnu", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "metadata-sources": "Zdroje metadat", "new-platforms": "Nové platformy", "new-platforms-desc": "Skenovat pouze nové platformy (nejrychlejší)", diff --git a/frontend/src/locales/de_DE/scan.json b/frontend/src/locales/de_DE/scan.json index 94892b01e..c1b30fe3f 100644 --- a/frontend/src/locales/de_DE/scan.json +++ b/frontend/src/locales/de_DE/scan.json @@ -14,12 +14,17 @@ "connection-successful": "Verbindung erfolgreich", "disabled-by-admin": "Vom Administrator deaktiviert", "hashes": "Hashes neu berechnen", +<<<<<<< HEAD "hashes-desc": "Berechnet Hashes für ausgewählte Plattformen neu", "hashes-disabled-tooltip": "Hash-Berechnung deaktiviert.

Hashes (MD5, SHA1, CRC32) sind eindeutige Fingerabdrücke, die ROM-Dateien präzise identifizieren.

Ohne sie können Hasheous und RetroAchievements Spiele nicht mit ihren Datenbanken abgleichen, aber das Scannen wird schneller.", "hashes-enabled-tooltip": "Hash-Berechnung aktiviert.

Hashes (MD5, SHA1, CRC32) werden berechnet, um eindeutige Fingerabdrücke für jede ROM-Datei zu erstellen.

Dies ermöglicht es Hasheous und RetroAchievements, Spiele in ihren Datenbanken genau zu identifizieren.", "hash-calculation-disabled": "Hash-Berechnung ist deaktiviert", "hasheous-requires-hashes": "Hasheous erfordert aktivierte Hash-Berechnung", "retroachievements-requires-hashes": "RetroAchievements erfordert aktivierte Hash-Berechnung", +======= + "hashes-desc": "Berechne Hashes für ausgewählte Plattformen neu", + "locale-not-supported": "Nicht unterstützt", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Bibliothek verwalten", "metadata-sources": "Quellen für Metadaten", "new-platforms": "Neue Platformen", diff --git a/frontend/src/locales/en_GB/common.json b/frontend/src/locales/en_GB/common.json index 6f976677d..9c558f8b6 100644 --- a/frontend/src/locales/en_GB/common.json +++ b/frontend/src/locales/en_GB/common.json @@ -1,5 +1,7 @@ { "about": "About", + "default": "Default", + "language": "Language", "add": "Add", "administration": "Administration", "apply": "Apply", diff --git a/frontend/src/locales/en_GB/scan.json b/frontend/src/locales/en_GB/scan.json index d194f3501..f0cb3aeb9 100644 --- a/frontend/src/locales/en_GB/scan.json +++ b/frontend/src/locales/en_GB/scan.json @@ -15,10 +15,14 @@ "disabled-by-admin": "Disabled by the administrator", "hashes": "Recalculate hashes", "hashes-desc": "Recalculates hashes for selected platforms", +<<<<<<< HEAD "hashes-disabled-tooltip": "File hash calculation disabled.

Hashes (MD5, SHA1, CRC32) are unique fingerprints that identify ROM files precisely.

Without them, Hasheous and RetroAchievements cannot match games to their databases, but scanning will be faster.", "hashes-enabled-tooltip": "File hash calculation enabled.

Hashes (MD5, SHA1, CRC32) will be calculated to create unique fingerprints for each ROM file.

This enables Hasheous and RetroAchievements to accurately identify games in their databases.", "hash-calculation-disabled": "Hash calculation is disabled", "hasheous-requires-hashes": "Hasheous requires hash calculation to be enabled", +======= + "locale-not-supported": "Not supported", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Manage library", "metadata-sources": "Metadata sources", "new-platforms": "New platforms", diff --git a/frontend/src/locales/en_US/common.json b/frontend/src/locales/en_US/common.json index e253dee71..d7c57c1f6 100644 --- a/frontend/src/locales/en_US/common.json +++ b/frontend/src/locales/en_US/common.json @@ -14,7 +14,11 @@ "confirm-deletion": "Confirm Deletion", "core": "Core", "create": "Create", +<<<<<<< HEAD "delete": "Delete", +======= + "default": "Default", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "dropzone-description": "Drag and drop your ROM files here, or click to browse", "dropzone-drag-over": "Release to upload", "dropzone-title": "Drop files here", @@ -25,7 +29,11 @@ "games-n": "{n} Game | {n} Games", "invalid-email": "Invalid email", "invalid-name": "Invalid name", +<<<<<<< HEAD "last-updated": "Last updated", +======= + "language": "Language", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "library-management": "Library management", "logout": "Logout", "name": "Name", diff --git a/frontend/src/locales/en_US/scan.json b/frontend/src/locales/en_US/scan.json index 2f8b07e67..64449a554 100644 --- a/frontend/src/locales/en_US/scan.json +++ b/frontend/src/locales/en_US/scan.json @@ -15,11 +15,15 @@ "disabled-by-admin": "Disabled by the administrator", "hashes": "Recalculate hashes", "hashes-desc": "Recalculates hashes for selected platforms", +<<<<<<< HEAD "hashes-disabled-tooltip": "File hash calculation disabled.

Hashes (MD5, SHA1, CRC32) are unique fingerprints that identify ROM files precisely.

Without them, Hasheous and RetroAchievements cannot match games to their databases, but scanning will be faster.", "hashes-enabled-tooltip": "File hash calculation enabled.

Hashes (MD5, SHA1, CRC32) will be calculated to create unique fingerprints for each ROM file.

This enables Hasheous and RetroAchievements to accurately identify games in their databases.", "hash-calculation-disabled": "Hash calculation is disabled", "hasheous-requires-hashes": "Hasheous requires hash calculation to be enabled", "retroachievements-requires-hashes": "RetroAchievements requires hash calculation to be enabled", +======= + "locale-not-supported": "Not supported", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Manage library", "metadata-sources": "Metadata sources", "new-platforms": "New platforms", diff --git a/frontend/src/locales/es_ES/scan.json b/frontend/src/locales/es_ES/scan.json index a4dc5c24c..dfe00bb8a 100644 --- a/frontend/src/locales/es_ES/scan.json +++ b/frontend/src/locales/es_ES/scan.json @@ -15,11 +15,15 @@ "disabled-by-admin": "Deshabilitado por el administrador", "hashes": "Recalcular hashes", "hashes-desc": "Recalcula los hashes de las plataformas seleccionadas", +<<<<<<< HEAD "hashes-disabled-tooltip": "Cálculo de hash deshabilitado.

Los hashes (MD5, SHA1, CRC32) son huellas digitales únicas que identifican archivos ROM con precisión.

Sin ellos, Hasheous y RetroAchievements no pueden comparar juegos con sus bases de datos, pero el escaneo será más rápido.", "hashes-enabled-tooltip": "Cálculo de hash habilitado.

Se calcularán hashes (MD5, SHA1, CRC32) para crear huellas digitales únicas de cada archivo ROM.

Esto permite a Hasheous y RetroAchievements identificar juegos con precisión en sus bases de datos.", "hash-calculation-disabled": "El cálculo de hash está deshabilitado", "hasheous-requires-hashes": "Hasheous requiere que el cálculo de hashes esté habilitado", "retroachievements-requires-hashes": "RetroAchievements requiere que el cálculo de hashes esté habilitado", +======= + "locale-not-supported": "No compatible", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gestionar biblioteca", "metadata-sources": "Fuentes de metadatos", "new-platforms": "Plataformas nuevas", diff --git a/frontend/src/locales/fr_FR/scan.json b/frontend/src/locales/fr_FR/scan.json index 4552e6aad..32766f331 100644 --- a/frontend/src/locales/fr_FR/scan.json +++ b/frontend/src/locales/fr_FR/scan.json @@ -15,11 +15,15 @@ "disabled-by-admin": "Désactivé par l'administrateur", "hashes": "Recalculer les hachages", "hashes-desc": "Recalculer les hachages des plateformes sélectionnées", +<<<<<<< HEAD "hashes-disabled-tooltip": "Calcul de hachage désactivé.

Les hachages (MD5, SHA1, CRC32) sont des empreintes uniques qui identifient les fichiers ROM avec précision.

Sans eux, Hasheous et RetroAchievements ne peuvent pas faire correspondre les jeux à leurs bases de données, mais l'analyse sera plus rapide.", "hashes-enabled-tooltip": "Calcul de hachage de fichier activé.

Les hachages (MD5, SHA1, CRC32) seront calculés pour créer des empreintes uniques pour chaque fichier ROM.

Ceci permet à Hasheous et RetroAchievements d'identifier précisément les jeux dans leurs bases de données.", "hash-calculation-disabled": "Le calcul de hachage est désactivé", "hasheous-requires-hashes": "Hasheous nécessite que le calcul de hachage soit activé", "retroachievements-requires-hashes": "RetroAchievements nécessite que le calcul de hachage soit activé", +======= + "locale-not-supported": "Non pris en charge", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gérer la bibliothèque", "metadata-sources": "Sources de métadonnées", "new-platforms": "Nouvelles plateformes", diff --git a/frontend/src/locales/it_IT/scan.json b/frontend/src/locales/it_IT/scan.json index f5c882735..4174f2c29 100644 --- a/frontend/src/locales/it_IT/scan.json +++ b/frontend/src/locales/it_IT/scan.json @@ -15,11 +15,15 @@ "disabled-by-admin": "Disabilitato dall'amministratore", "hashes": "Ricalcola hash", "hashes-desc": "Ricalcola gli hash per le piattaforme selezionate", +<<<<<<< HEAD "hashes-disabled-tooltip": "Calcolo hash disabilitato.

Gli hash (MD5, SHA1, CRC32) sono impronte digitali uniche che identificano i file ROM con precisione.

Senza di essi, Hasheous e RetroAchievements non possono confrontare i giochi con i loro database, ma la scansione sarà più veloce.", "hashes-enabled-tooltip": "Calcolo hash abilitato.

Verranno calcolati gli hash (MD5, SHA1, CRC32) per creare impronte digitali uniche di ogni file ROM.

Questo consente a Hasheous e RetroAchievements di identificare accuratamente i giochi nei loro database.", "hash-calculation-disabled": "Il calcolo dell'hash è disabilitato", "hasheous-requires-hashes": "Hasheous richiede che il calcolo degli hash sia abilitato", "retroachievements-requires-hashes": "RetroAchievements richiede che il calcolo degli hash sia abilitato", +======= + "locale-not-supported": "Non supportato", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gestisci libreria", "metadata-sources": "Fonti metadati", "new-platforms": "Nuove piattaforme", diff --git a/frontend/src/locales/ja_JP/scan.json b/frontend/src/locales/ja_JP/scan.json index db48ced91..ba24792b5 100644 --- a/frontend/src/locales/ja_JP/scan.json +++ b/frontend/src/locales/ja_JP/scan.json @@ -15,10 +15,14 @@ "disabled-by-admin": "管理者によって無効化されています", "hashes": "ハッシュ値の再計算", "hashes-desc": "選択されたプラットフォームのハッシュ値を再計算します", +<<<<<<< HEAD "hashes-disabled-tooltip": "ファイルハッシュ計算が無効。

ハッシュ(MD5、SHA1、CRC32)はROMファイルを正確に識別するユニークな指紋です。

これがないと、HasheousやRetroAchievementsはゲームをデータベースとマッチングできませんが、スキャンは高速になります。", "hashes-enabled-tooltip": "ファイルハッシュ計算が有効です。

各ROMファイルの一意の指紋を作成するために、ハッシュ(MD5、SHA1、CRC32)が計算されます。

これにより、HasheousとRetroAchievementsがデータベース内のゲームを正確に識別できます。", "hash-calculation-disabled": "ハッシュ計算が無効になっています", "hasheous-requires-hashes": "Hasheousはファイルハッシュが必要です", +======= + "locale-not-supported": "非対応", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "ライブラリを編集", "metadata-sources": "メタデータ取得元", "new-platforms": "新規プラットフォーム", diff --git a/frontend/src/locales/ko_KR/scan.json b/frontend/src/locales/ko_KR/scan.json index cec6b7cc7..dc9c4c32d 100644 --- a/frontend/src/locales/ko_KR/scan.json +++ b/frontend/src/locales/ko_KR/scan.json @@ -15,10 +15,14 @@ "disabled-by-admin": "관리자에 의해 비활성화됨", "hashes": "해시", "hashes-desc": "선택된 플랫폼의 해시를 다시 계산", +<<<<<<< HEAD "hashes-disabled-tooltip": "해시 계산이 비활성화됨.

해시(MD5, SHA1, CRC32)는 ROM 파일을 정확히 식별하는 고유한 지문입니다.

해시 없이는 Hasheous와 RetroAchievements가 데이터베이스와 게임을 매치할 수 없지만, 스캔이 더 빨라집니다.", "hashes-enabled-tooltip": "해시 계산이 활성화됨.

각 ROM 파일의 고유한 지문을 생성하기 위해 해시(MD5, SHA1, CRC32)가 계산됩니다.

이를 통해 Hasheous와 RetroAchievements가 데이터베이스에서 게임을 정확히 식별할 수 있습니다.", "hash-calculation-disabled": "해시 계산이 비활성화되어 있습니다", "hasheous-requires-hashes": "Hasheous는 파일 해시가 필요합니다", +======= + "locale-not-supported": "지원되지 않음", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "라이브러리 관리", "metadata-sources": "메타데이터 DB", "new-platforms": "새 플랫폼", diff --git a/frontend/src/locales/pl_PL/scan.json b/frontend/src/locales/pl_PL/scan.json index e3bcc284a..d570b4466 100644 --- a/frontend/src/locales/pl_PL/scan.json +++ b/frontend/src/locales/pl_PL/scan.json @@ -15,11 +15,15 @@ "disabled-by-admin": "Wyłączone przez administratora", "hashes": "Przelicz sumy kontrolne", "hashes-desc": "Przelicza sumy kontrolne dla wybranych platform", +<<<<<<< HEAD "hashes-disabled-tooltip": "Obliczanie skrótów wyłączone.

Skróty (MD5, SHA1, CRC32) to unikalne odciski palców, które precyzyjnie identyfikują pliki ROM.

Bez nich Hasheous i RetroAchievements nie mogą dopasować gier do swoich baz danych, ale skanowanie będzie szybsze.", "hashes-enabled-tooltip": "Obliczanie skrótów włączone.

Zostaną obliczone skróty (MD5, SHA1, CRC32) w celu utworzenia unikalnych odcisków palców każdego pliku ROM.

To pozwala Hasheous i RetroAchievements na dokładną identyfikację gier w ich bazach danych.", "hash-calculation-disabled": "Obliczanie skrótów jest wyłączone", "hasheous-requires-hashes": "Hasheous wymaga włączonego obliczania skrótów", "retroachievements-requires-hashes": "RetroAchievements wymaga włączonego obliczania skrótów", +======= + "locale-not-supported": "Nieobsługiwane", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Zarządzaj biblioteką", "metadata-sources": "Źródła metadanych", "new-platforms": "Nowe platformy", diff --git a/frontend/src/locales/pt_BR/scan.json b/frontend/src/locales/pt_BR/scan.json index 47db866d5..96fd9752a 100644 --- a/frontend/src/locales/pt_BR/scan.json +++ b/frontend/src/locales/pt_BR/scan.json @@ -14,12 +14,17 @@ "connection-successful": "Conexão bem-sucedida", "disabled-by-admin": "Desativado pelo administrador", "hashes": "Recalcular hashes", +<<<<<<< HEAD "hashes-desc": "Recalcula hashes das plataformas selecionadas", "hashes-disabled-tooltip": "Cálculo de hash desabilitado.

Hashes (MD5, SHA1, CRC32) são impressões digitais únicas que identificam arquivos ROM com precisão.

Sem eles, Hasheous e RetroAchievements não podem comparar jogos com seus bancos de dados, mas a varredura será mais rápida.", "hashes-enabled-tooltip": "Cálculo de hash habilitado.

Hashes (MD5, SHA1, CRC32) serão calculados para criar impressões digitais únicas de cada arquivo ROM.

Isso permite que Hasheous e RetroAchievements identifiquem jogos com precisão em seus bancos de dados.", "hash-calculation-disabled": "O cálculo de hash está desabilitado", "hasheous-requires-hashes": "Hasheous requer que o cálculo de hash esteja habilitado", "retroachievements-requires-hashes": "RetroAchievements requer que o cálculo de hash esteja habilitado", +======= + "hashes-desc": "Recalcular hashes para as plataformas selecionadas", + "locale-not-supported": "Não suportado", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gerenciar biblioteca", "metadata-sources": "Fontes de metadados", "new-platforms": "Novas plataformas", diff --git a/frontend/src/locales/ro_RO/scan.json b/frontend/src/locales/ro_RO/scan.json index fd32de711..4f60c2841 100644 --- a/frontend/src/locales/ro_RO/scan.json +++ b/frontend/src/locales/ro_RO/scan.json @@ -14,11 +14,16 @@ "connection-successful": "Conexiune reușită", "disabled-by-admin": "Dezactivat de administrator", "hashes": "Recalculează hash-urile", +<<<<<<< HEAD "hashes-desc": "Recalculează hash-urile pentru platformele selectate", "hashes-disabled-tooltip": "Calculul hash-urilor dezactivat.

Hash-urile (MD5, SHA1, CRC32) sunt amprente digitale unice care identifică fișierele ROM cu precizie.

Fără ele, Hasheous și RetroAchievements nu pot potrivi jocurile cu bazele lor de date, dar scanarea va fi mai rapidă.", "hashes-enabled-tooltip": "Calculul hash-urilor activat.

Se vor calcula hash-uri (MD5, SHA1, CRC32) pentru a crea amprente digitale unice pentru fiecare fișier ROM.

Aceasta permite lui Hasheous și RetroAchievements să identifice cu precizie jocurile în bazele lor de date.", "hash-calculation-disabled": "Calculul hash-ului este dezactivat", "hasheous-requires-hashes": "Hasheous necesită hash-uri de fișiere", +======= + "hashes-desc": "Recalculează hash-urile platformelor selectate", + "locale-not-supported": "Nesuportat", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gestionează biblioteca", "metadata-sources": "Surse de metadate", "new-platforms": "Platforme noi", diff --git a/frontend/src/locales/ru_RU/scan.json b/frontend/src/locales/ru_RU/scan.json index f960c3795..5cbff70d0 100644 --- a/frontend/src/locales/ru_RU/scan.json +++ b/frontend/src/locales/ru_RU/scan.json @@ -14,11 +14,16 @@ "connection-successful": "Соединение успешно установлено", "disabled-by-admin": "Отключено администратором", "hashes": "Пересчитать хеши", +<<<<<<< HEAD "hashes-desc": "Пересчитывает хеши для выбранных платформ", "hashes-disabled-tooltip": "Вычисление хешей отключено.

Хеши (MD5, SHA1, CRC32) - это уникальные отпечатки, которые точно идентифицируют файлы ROM.

Без них Hasheous и RetroAchievements не могут сопоставить игры с своими базами данных, но сканирование будет быстрее.", "hashes-enabled-tooltip": "Вычисление хешей включено.

Будут вычислены хеши (MD5, SHA1, CRC32) для создания уникальных отпечатков каждого файла ROM.

Это позволяет Hasheous и RetroAchievements точно идентифицировать игры в своих базах данных.", "hash-calculation-disabled": "Вычисление хешей отключено", "hasheous-requires-hashes": "Hasheous требует хеши файлов", +======= + "hashes-desc": "Пересчитать хеши для выбранных платформ", + "locale-not-supported": "Не поддерживается", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Управление библиотекой", "metadata-sources": "Источники мета��анных", "new-platforms": "Новые платформы", diff --git a/frontend/src/locales/zh_CN/scan.json b/frontend/src/locales/zh_CN/scan.json index f580a192a..c74eb9813 100644 --- a/frontend/src/locales/zh_CN/scan.json +++ b/frontend/src/locales/zh_CN/scan.json @@ -15,10 +15,14 @@ "disabled-by-admin": "已被管理员禁用", "hashes": "哈希", "hashes-desc": "重新计算选定平台的哈希值", +<<<<<<< HEAD "hashes-disabled-tooltip": "哈希计算已禁用。

哈希值(MD5、SHA1、CRC32)是精确识别 ROM 文件的唯一指纹。

没有它们,Hasheous 和 RetroAchievements 无法将游戏与其数据库进行匹配,但扫描速度会更快。", "hashes-enabled-tooltip": "文件哈希计算已启用。

将计算哈希值(MD5、SHA1、CRC32)为每个 ROM 文件创建唯一指纹。

这使得 Hasheous 和 RetroAchievements 能够在其数据库中准确识别游戏。", "hash-calculation-disabled": "哈希计算已禁用", "hasheous-requires-hashes": "Hasheous 需要文件哈希", +======= + "locale-not-supported": "不支持", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "管理游戏库", "metadata-sources": "元数据源", "new-platforms": "新平台", diff --git a/frontend/src/locales/zh_TW/scan.json b/frontend/src/locales/zh_TW/scan.json index 8a11d6e35..1eda5caf9 100644 --- a/frontend/src/locales/zh_TW/scan.json +++ b/frontend/src/locales/zh_TW/scan.json @@ -15,10 +15,14 @@ "disabled-by-admin": "已被管理員禁用", "hashes": "雜湊", "hashes-desc": "重新計算選定平台的雜湊值", +<<<<<<< HEAD "hashes-disabled-tooltip": "哈希計算已停用。

哈希值(MD5、SHA1、CRC32)是唯一識別 ROM 檔案的數字指紋。

沒有它們,Hasheous 和 RetroAchievements 無法將遊戲與資料庫匹配,但掃描速度會更快。", "hashes-enabled-tooltip": "哈希計算已啟用。

將計算哈希值(MD5、SHA1、CRC32)為每個 ROM 檔案建立唯一指紋。

這使 Hasheous 和 RetroAchievements 能夠準確識別其資料庫中的遊戲。", "hash-calculation-disabled": "雜湊計算已停用", "hasheous-requires-hashes": "Hasheous 需要檔案哈希", +======= + "locale-not-supported": "不支援", +>>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "管理遊戲庫", "metadata-sources": "元數據來源", "new-platforms": "新平台", diff --git a/frontend/src/services/api/config.ts b/frontend/src/services/api/config.ts index 9a314d40d..c5ee5429a 100644 --- a/frontend/src/services/api/config.ts +++ b/frontend/src/services/api/config.ts @@ -53,6 +53,14 @@ async function deleteExclusion({ return api.delete(`/config/exclude/${exclusionType}/${exclusionValue}`); } +async function updateProviderLocales({ + locales, +}: { + locales: Record; +}) { + return api.put("/config/provider-locales", { locales }); +} + export default { addPlatformBindConfig, deletePlatformBindConfig, @@ -60,4 +68,5 @@ export default { deletePlatformVersionConfig, addExclusion, deleteExclusion, + updateProviderLocales, }; diff --git a/frontend/src/stores/config.ts b/frontend/src/stores/config.ts index ba2135395..1e4f1c935 100644 --- a/frontend/src/stores/config.ts +++ b/frontend/src/stores/config.ts @@ -31,6 +31,7 @@ const defaultConfig = { SCAN_ARTWORK_PRIORITY: [], SCAN_REGION_PRIORITY: [], SCAN_LANGUAGE_PRIORITY: [], + METADATA_PROVIDER_LOCALES: {}, } as ConfigResponse; export default defineStore("config", { @@ -77,6 +78,12 @@ export default defineStore("config", { isExclusionType(type: string): type is ExclusionTypes { return Object.keys(this.config).includes(type); }, + setProviderLocale(provider: string, locale: string) { + this.config.METADATA_PROVIDER_LOCALES[provider] = locale; + }, + removeProviderLocale(provider: string) { + delete this.config.METADATA_PROVIDER_LOCALES[provider]; + }, getEJSCoreOptions(core: string | null): Record { const defaultOptions = this.config.EJS_SETTINGS["default"] || {}; if (!core) return defaultOptions; diff --git a/frontend/src/views/Settings/MetadataSources.vue b/frontend/src/views/Settings/MetadataSources.vue index 401b847bf..430b511da 100644 --- a/frontend/src/views/Settings/MetadataSources.vue +++ b/frontend/src/views/Settings/MetadataSources.vue @@ -3,9 +3,56 @@ import { computed, onMounted, ref } from "vue"; import { useI18n } from "vue-i18n"; import RSection from "@/components/common/RSection.vue"; import storeHeartbeat from "@/stores/heartbeat"; +import storeConfig from "@/stores/config"; +import configApi from "@/services/api/config"; const { t } = useI18n(); const heartbeat = storeHeartbeat(); +const configStore = storeConfig(); + +// Available locales for providers that support them +const ssLocales = computed(() => [ + { title: t("common.default"), value: "" }, + { title: "English", value: "en" }, + { title: "Français", value: "fr" }, + { title: "Deutsch", value: "de" }, + { title: "Español", value: "es" }, + { title: "Italiano", value: "it" }, + { title: "Português", value: "pt" }, +]); + +// IGDB locales (uses full region codes like ja-JP) +const igdbLocales = computed(() => [ + { title: t("common.default"), value: "" }, + { title: "日本語 (Japanese)", value: "ja-JP" }, + { title: "한국어 (Korean)", value: "ko-KR" }, + { title: "简体中文 (Chinese Simplified)", value: "zh-CN" }, + { title: "繁體中文 (Chinese Traditional)", value: "zh-TW" }, + { title: "Europe", value: "EU" }, +]); + +// Get the current locale for a provider +function getProviderLocale(provider: string): string { + return configStore.config.METADATA_PROVIDER_LOCALES[provider] || ""; +} + +// Update locale for a provider +async function updateProviderLocale(provider: string, locale: string) { + const newLocales = { ...configStore.config.METADATA_PROVIDER_LOCALES }; + + if (locale) { + newLocales[provider] = locale; + } else { + delete newLocales[provider]; + } + + try { + await configApi.updateProviderLocales({ locales: newLocales }); + configStore.config.METADATA_PROVIDER_LOCALES = newLocales; + } catch (error) { + console.error("Error updating provider locale:", error); + } +} const heartbeatStatus = ref>({ igdb: undefined, @@ -27,6 +74,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/igdb.png", disabled: !heartbeat.value.METADATA_SOURCES?.IGDB_API_ENABLED, heartbeat: heartbeatStatus.value.igdb, + supportsLocale: true, + locales: igdbLocales.value, }, { name: "MobyGames", @@ -34,6 +83,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/moby.png", disabled: !heartbeat.value.METADATA_SOURCES?.MOBY_API_ENABLED, heartbeat: heartbeatStatus.value.moby, + supportsLocale: false, + locales: [], }, { name: "ScreenScrapper", @@ -41,6 +92,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/ss.png", disabled: !heartbeat.value.METADATA_SOURCES?.SS_API_ENABLED, heartbeat: heartbeatStatus.value.ss, + supportsLocale: true, + locales: ssLocales.value, }, { name: "RetroAchievements", @@ -48,6 +101,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/ra.png", disabled: !heartbeat.value.METADATA_SOURCES?.RA_API_ENABLED, heartbeat: heartbeatStatus.value.ra, + supportsLocale: false, + locales: [], }, { name: "Hasheous", @@ -55,6 +110,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/hasheous.png", disabled: !heartbeat.value.METADATA_SOURCES?.HASHEOUS_API_ENABLED, heartbeat: heartbeatStatus.value.hasheous, + supportsLocale: false, + locales: [], }, { name: "Launchbox", @@ -62,6 +119,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/launchbox.png", disabled: !heartbeat.value.METADATA_SOURCES?.LAUNCHBOX_API_ENABLED, heartbeat: heartbeatStatus.value.launchbox, + supportsLocale: false, + locales: [], }, { name: "Flashpoint Project", @@ -69,6 +128,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/flashpoint.png", disabled: !heartbeat.value.METADATA_SOURCES?.FLASHPOINT_API_ENABLED, heartbeat: heartbeatStatus.value.flashpoint, + supportsLocale: false, + locales: [], }, { name: "HowLongToBeat", @@ -76,6 +137,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/hltb.png", disabled: !heartbeat.value.METADATA_SOURCES?.HLTB_API_ENABLED, heartbeat: heartbeatStatus.value.hltb, + supportsLocale: false, + locales: [], }, { name: "SteamgridDB", @@ -83,6 +146,8 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/sgdb.png", disabled: !heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_API_ENABLED, heartbeat: heartbeatStatus.value.sgdb, + supportsLocale: false, + locales: [], }, ]); @@ -131,6 +196,7 @@ function getConnectionStatusTooltip(source: { } onMounted(() => { + configStore.fetchConfig(); fetchAllHeartbeats(); }); @@ -207,6 +273,33 @@ onMounted(() => { + + + + + + + + From 6a85f9a4391cecc18eecb616aeba576529329ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tanguy=20Maz=C3=A9?= Date: Fri, 21 Nov 2025 00:32:01 +0100 Subject: [PATCH 2/4] Remove unused provider locale methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/config/config_manager.py | 17 ----------------- frontend/src/stores/config.ts | 6 ------ 2 files changed, 23 deletions(-) diff --git a/backend/config/config_manager.py b/backend/config/config_manager.py index 5526c8caa..a64704e74 100644 --- a/backend/config/config_manager.py +++ b/backend/config/config_manager.py @@ -614,23 +614,6 @@ class ConfigManager: self.config.__setattr__(exclusion_type, config_item) self._update_config_file() - def set_provider_locale(self, provider: str, locale: str) -> None: - provider_locales = self.config.METADATA_PROVIDER_LOCALES - provider_locales[provider] = locale - self.config.METADATA_PROVIDER_LOCALES = provider_locales - self._update_config_file() - - def remove_provider_locale(self, provider: str) -> None: - provider_locales = self.config.METADATA_PROVIDER_LOCALES - - try: - del provider_locales[provider] - except KeyError: - pass - - self.config.METADATA_PROVIDER_LOCALES = provider_locales - self._update_config_file() - def update_provider_locales(self, locales: dict[str, str]) -> None: self.config.METADATA_PROVIDER_LOCALES = locales self._update_config_file() diff --git a/frontend/src/stores/config.ts b/frontend/src/stores/config.ts index 1e4f1c935..16aae969c 100644 --- a/frontend/src/stores/config.ts +++ b/frontend/src/stores/config.ts @@ -78,12 +78,6 @@ export default defineStore("config", { isExclusionType(type: string): type is ExclusionTypes { return Object.keys(this.config).includes(type); }, - setProviderLocale(provider: string, locale: string) { - this.config.METADATA_PROVIDER_LOCALES[provider] = locale; - }, - removeProviderLocale(provider: string) { - delete this.config.METADATA_PROVIDER_LOCALES[provider]; - }, getEJSCoreOptions(core: string | null): Record { const defaultOptions = this.config.EJS_SETTINGS["default"] || {}; if (!core) return defaultOptions; From 28823d39dd11e70f7fdda0fa15cf1bcb11a25e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tanguy=20Maz=C3=A9?= Date: Sun, 23 Nov 2025 02:01:13 +0100 Subject: [PATCH 3/4] Refactor to use scan.priority.region for IGDB localization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove provider-specific locale configuration and use existing scan.priority.region for IGDB regional variants. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/config/config_manager.py | 24 ----- backend/endpoints/configs.py | 17 ---- backend/endpoints/responses/config.py | 1 - backend/handler/metadata/igdb_handler.py | 31 ++++++- backend/handler/metadata/ss_handler.py | 13 +-- .../__generated__/models/ConfigResponse.ts | 1 - frontend/src/locales/cs_CZ/scan.json | 5 -- frontend/src/locales/de_DE/scan.json | 5 -- frontend/src/locales/en_GB/common.json | 2 - frontend/src/locales/en_GB/scan.json | 4 - frontend/src/locales/en_US/common.json | 8 -- frontend/src/locales/en_US/scan.json | 4 - frontend/src/locales/es_ES/scan.json | 4 - frontend/src/locales/fr_FR/scan.json | 4 - frontend/src/locales/it_IT/scan.json | 4 - frontend/src/locales/ja_JP/scan.json | 4 - frontend/src/locales/ko_KR/scan.json | 4 - frontend/src/locales/pl_PL/scan.json | 4 - frontend/src/locales/pt_BR/scan.json | 5 -- frontend/src/locales/ro_RO/scan.json | 5 -- frontend/src/locales/ru_RU/scan.json | 5 -- frontend/src/locales/zh_CN/scan.json | 4 - frontend/src/locales/zh_TW/scan.json | 4 - frontend/src/services/api/config.ts | 9 -- frontend/src/stores/config.ts | 1 - .../src/views/Settings/MetadataSources.vue | 90 ------------------- 26 files changed, 31 insertions(+), 231 deletions(-) diff --git a/backend/config/config_manager.py b/backend/config/config_manager.py index a64704e74..a7bbdba26 100644 --- a/backend/config/config_manager.py +++ b/backend/config/config_manager.py @@ -84,7 +84,6 @@ class Config: SCAN_REGION_PRIORITY: list[str] SCAN_LANGUAGE_PRIORITY: list[str] SCAN_MEDIA: list[str] - METADATA_PROVIDER_LOCALES: dict[str, str] def __init__(self, **entries): self.__dict__.update(entries) @@ -273,11 +272,6 @@ class ConfigManager: "manual", ], ), - METADATA_PROVIDER_LOCALES=pydash.get( - self._raw_config, - "scan.provider_locales", - {}, - ), ) def _get_ejs_controls(self) -> dict[str, EjsControls]: @@ -467,19 +461,6 @@ class ConfigManager: ) sys.exit(3) - if not isinstance(self.config.METADATA_PROVIDER_LOCALES, dict): - log.critical( - "Invalid config.yml: scan.provider_locales must be a dictionary" - ) - sys.exit(3) - else: - for provider, locale in self.config.METADATA_PROVIDER_LOCALES.items(): - if not isinstance(locale, str): - log.critical( - f"Invalid config.yml: scan.provider_locales.{provider} must be a string" - ) - sys.exit(3) - def get_config(self) -> Config: try: with open(self.config_file, "r") as config_file: @@ -535,7 +516,6 @@ class ConfigManager: "region": self.config.SCAN_REGION_PRIORITY, "language": self.config.SCAN_LANGUAGE_PRIORITY, }, - "provider_locales": self.config.METADATA_PROVIDER_LOCALES, }, } @@ -614,9 +594,5 @@ class ConfigManager: self.config.__setattr__(exclusion_type, config_item) self._update_config_file() - def update_provider_locales(self, locales: dict[str, str]) -> None: - self.config.METADATA_PROVIDER_LOCALES = locales - self._update_config_file() - config_manager = ConfigManager() diff --git a/backend/endpoints/configs.py b/backend/endpoints/configs.py index 104651c88..c223f637f 100644 --- a/backend/endpoints/configs.py +++ b/backend/endpoints/configs.py @@ -43,7 +43,6 @@ def get_config() -> ConfigResponse: SCAN_ARTWORK_PRIORITY=cfg.SCAN_ARTWORK_PRIORITY, SCAN_REGION_PRIORITY=cfg.SCAN_REGION_PRIORITY, SCAN_LANGUAGE_PRIORITY=cfg.SCAN_LANGUAGE_PRIORITY, - METADATA_PROVIDER_LOCALES=cfg.METADATA_PROVIDER_LOCALES, ) @@ -140,19 +139,3 @@ async def delete_exclusion( raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=exc.message ) from exc - - -@protected_route(router.put, "/provider-locales", [Scope.PLATFORMS_WRITE]) -async def update_provider_locales(request: Request) -> None: - """Update metadata provider locales in the configuration""" - - data = await request.json() - locales = data.get("locales", {}) - - try: - cm.update_provider_locales(locales) - except ConfigNotWritableException as exc: - log.critical(exc.message) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=exc.message - ) from exc diff --git a/backend/endpoints/responses/config.py b/backend/endpoints/responses/config.py index 214ca9930..30bf94290 100644 --- a/backend/endpoints/responses/config.py +++ b/backend/endpoints/responses/config.py @@ -23,4 +23,3 @@ class ConfigResponse(TypedDict): SCAN_ARTWORK_PRIORITY: list[str] SCAN_REGION_PRIORITY: list[str] SCAN_LANGUAGE_PRIORITY: list[str] - METADATA_PROVIDER_LOCALES: dict[str, str] diff --git a/backend/handler/metadata/igdb_handler.py b/backend/handler/metadata/igdb_handler.py index d8d22131c..8f5160fc3 100644 --- a/backend/handler/metadata/igdb_handler.py +++ b/backend/handler/metadata/igdb_handler.py @@ -210,10 +210,37 @@ def extract_metadata_from_igdb_rom(self: MetadataHandler, rom: Game) -> IGDBMeta ) +# Mapping from scan.priority.region codes to IGDB game_localizations region identifiers +# IGDB's game_localizations provides regional titles and cover art, but NOT localized descriptions +REGION_TO_IGDB_LOCALE: dict[str, str | None] = { + "us": None, # United States - use default (no localization needed) + "wor": None, # World - use default + "eu": "EU", # Europe region + "jp": "ja-JP", # Japan + "kr": "ko-KR", # Korea + "cn": "zh-CN", # China (Simplified Chinese) + "tw": "zh-TW", # Taiwan (Traditional Chinese) +} + + def get_igdb_preferred_locale() -> str | None: - """Get IGDB-specific locale from config, if set.""" + """Get IGDB locale from scan.priority.region configuration. + + Maps region priority codes to IGDB's game_localizations region identifiers. + Returns the first matching region from the priority list, or None for default. + + Returns: + IGDB region identifier (e.g., "ja-JP", "EU") or None for default + """ config = cm.get_config() - return config.METADATA_PROVIDER_LOCALES.get("igdb") + + # Check each region in priority order and return first match + for region in config.SCAN_REGION_PRIORITY: + igdb_locale = REGION_TO_IGDB_LOCALE.get(region.lower()) + if igdb_locale is not None: + return igdb_locale + + return None # Default - no localization def extract_localized_data(rom: Game, preferred_locale: str | None) -> tuple[str, str]: diff --git a/backend/handler/metadata/ss_handler.py b/backend/handler/metadata/ss_handler.py index b48f9d6d3..9ecfbfd31 100644 --- a/backend/handler/metadata/ss_handler.py +++ b/backend/handler/metadata/ss_handler.py @@ -39,20 +39,11 @@ def get_preferred_regions() -> list[str]: def get_preferred_languages() -> list[str]: - """Get preferred languages from config + """Get preferred languages from config. - Uses provider-specific locale if set, otherwise falls back to language priority list. - Always includes English as final fallback. + Returns language priority list with default fallbacks. """ config = cm.get_config() - - # Check for provider-specific locale first - provider_locale = config.METADATA_PROVIDER_LOCALES.get("ss") - if provider_locale: - # Use provider locale with English fallback - return list(dict.fromkeys([provider_locale, "en"])) - - # Fall back to language priority list return list(dict.fromkeys(config.SCAN_LANGUAGE_PRIORITY + ["en", "fr"])) diff --git a/frontend/src/__generated__/models/ConfigResponse.ts b/frontend/src/__generated__/models/ConfigResponse.ts index 548b909dc..0ee03b7c6 100644 --- a/frontend/src/__generated__/models/ConfigResponse.ts +++ b/frontend/src/__generated__/models/ConfigResponse.ts @@ -23,6 +23,5 @@ export type ConfigResponse = { SCAN_ARTWORK_PRIORITY: Array; SCAN_REGION_PRIORITY: Array; SCAN_LANGUAGE_PRIORITY: Array; - METADATA_PROVIDER_LOCALES: Record; }; diff --git a/frontend/src/locales/cs_CZ/scan.json b/frontend/src/locales/cs_CZ/scan.json index 3cbedc1d4..980578fdc 100644 --- a/frontend/src/locales/cs_CZ/scan.json +++ b/frontend/src/locales/cs_CZ/scan.json @@ -15,17 +15,12 @@ "disabled-by-admin": "Zakázáno administrátorem", "hashes": "Přepočítat hashe", "hashes-desc": "Přepočítá hashe pro vybrané platformy", -<<<<<<< HEAD "hashes-disabled-tooltip": "Výpočet hashů zakázán.

Hashe (MD5, SHA1, CRC32) jsou jedinečné otisky, které přesně identifikují soubory ROM.

Bez nich nemohou Hasheous a RetroAchievements porovnávat hry se svými databázemi, ale skenování bude rychlejší.", "hashes-enabled-tooltip": "Výpočet hashů povolen.

Budou vypočítány hashe (MD5, SHA1, CRC32) pro vytvoření jedinečných otisků každého souboru ROM.

To umožňuje Hasheous a RetroAchievements přesně identifikovat hry ve svých databázích.", "hash-calculation-disabled": "Výpočet hash je zakázán", "hasheous-requires-hashes": "Hasheous vyžaduje povolené počítání hashů", "retroachievements-requires-hashes": "RetroAchievements vyžaduje povolené počítání hashů", "manage-library": "Správa knihovny", -======= - "locale-not-supported": "Nepodporováno", - "manage-library": "Spravovat knihovnu", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "metadata-sources": "Zdroje metadat", "new-platforms": "Nové platformy", "new-platforms-desc": "Skenovat pouze nové platformy (nejrychlejší)", diff --git a/frontend/src/locales/de_DE/scan.json b/frontend/src/locales/de_DE/scan.json index c1b30fe3f..94892b01e 100644 --- a/frontend/src/locales/de_DE/scan.json +++ b/frontend/src/locales/de_DE/scan.json @@ -14,17 +14,12 @@ "connection-successful": "Verbindung erfolgreich", "disabled-by-admin": "Vom Administrator deaktiviert", "hashes": "Hashes neu berechnen", -<<<<<<< HEAD "hashes-desc": "Berechnet Hashes für ausgewählte Plattformen neu", "hashes-disabled-tooltip": "Hash-Berechnung deaktiviert.

Hashes (MD5, SHA1, CRC32) sind eindeutige Fingerabdrücke, die ROM-Dateien präzise identifizieren.

Ohne sie können Hasheous und RetroAchievements Spiele nicht mit ihren Datenbanken abgleichen, aber das Scannen wird schneller.", "hashes-enabled-tooltip": "Hash-Berechnung aktiviert.

Hashes (MD5, SHA1, CRC32) werden berechnet, um eindeutige Fingerabdrücke für jede ROM-Datei zu erstellen.

Dies ermöglicht es Hasheous und RetroAchievements, Spiele in ihren Datenbanken genau zu identifizieren.", "hash-calculation-disabled": "Hash-Berechnung ist deaktiviert", "hasheous-requires-hashes": "Hasheous erfordert aktivierte Hash-Berechnung", "retroachievements-requires-hashes": "RetroAchievements erfordert aktivierte Hash-Berechnung", -======= - "hashes-desc": "Berechne Hashes für ausgewählte Plattformen neu", - "locale-not-supported": "Nicht unterstützt", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Bibliothek verwalten", "metadata-sources": "Quellen für Metadaten", "new-platforms": "Neue Platformen", diff --git a/frontend/src/locales/en_GB/common.json b/frontend/src/locales/en_GB/common.json index 9c558f8b6..6f976677d 100644 --- a/frontend/src/locales/en_GB/common.json +++ b/frontend/src/locales/en_GB/common.json @@ -1,7 +1,5 @@ { "about": "About", - "default": "Default", - "language": "Language", "add": "Add", "administration": "Administration", "apply": "Apply", diff --git a/frontend/src/locales/en_GB/scan.json b/frontend/src/locales/en_GB/scan.json index f0cb3aeb9..d194f3501 100644 --- a/frontend/src/locales/en_GB/scan.json +++ b/frontend/src/locales/en_GB/scan.json @@ -15,14 +15,10 @@ "disabled-by-admin": "Disabled by the administrator", "hashes": "Recalculate hashes", "hashes-desc": "Recalculates hashes for selected platforms", -<<<<<<< HEAD "hashes-disabled-tooltip": "File hash calculation disabled.

Hashes (MD5, SHA1, CRC32) are unique fingerprints that identify ROM files precisely.

Without them, Hasheous and RetroAchievements cannot match games to their databases, but scanning will be faster.", "hashes-enabled-tooltip": "File hash calculation enabled.

Hashes (MD5, SHA1, CRC32) will be calculated to create unique fingerprints for each ROM file.

This enables Hasheous and RetroAchievements to accurately identify games in their databases.", "hash-calculation-disabled": "Hash calculation is disabled", "hasheous-requires-hashes": "Hasheous requires hash calculation to be enabled", -======= - "locale-not-supported": "Not supported", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Manage library", "metadata-sources": "Metadata sources", "new-platforms": "New platforms", diff --git a/frontend/src/locales/en_US/common.json b/frontend/src/locales/en_US/common.json index d7c57c1f6..e253dee71 100644 --- a/frontend/src/locales/en_US/common.json +++ b/frontend/src/locales/en_US/common.json @@ -14,11 +14,7 @@ "confirm-deletion": "Confirm Deletion", "core": "Core", "create": "Create", -<<<<<<< HEAD "delete": "Delete", -======= - "default": "Default", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "dropzone-description": "Drag and drop your ROM files here, or click to browse", "dropzone-drag-over": "Release to upload", "dropzone-title": "Drop files here", @@ -29,11 +25,7 @@ "games-n": "{n} Game | {n} Games", "invalid-email": "Invalid email", "invalid-name": "Invalid name", -<<<<<<< HEAD "last-updated": "Last updated", -======= - "language": "Language", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "library-management": "Library management", "logout": "Logout", "name": "Name", diff --git a/frontend/src/locales/en_US/scan.json b/frontend/src/locales/en_US/scan.json index 64449a554..2f8b07e67 100644 --- a/frontend/src/locales/en_US/scan.json +++ b/frontend/src/locales/en_US/scan.json @@ -15,15 +15,11 @@ "disabled-by-admin": "Disabled by the administrator", "hashes": "Recalculate hashes", "hashes-desc": "Recalculates hashes for selected platforms", -<<<<<<< HEAD "hashes-disabled-tooltip": "File hash calculation disabled.

Hashes (MD5, SHA1, CRC32) are unique fingerprints that identify ROM files precisely.

Without them, Hasheous and RetroAchievements cannot match games to their databases, but scanning will be faster.", "hashes-enabled-tooltip": "File hash calculation enabled.

Hashes (MD5, SHA1, CRC32) will be calculated to create unique fingerprints for each ROM file.

This enables Hasheous and RetroAchievements to accurately identify games in their databases.", "hash-calculation-disabled": "Hash calculation is disabled", "hasheous-requires-hashes": "Hasheous requires hash calculation to be enabled", "retroachievements-requires-hashes": "RetroAchievements requires hash calculation to be enabled", -======= - "locale-not-supported": "Not supported", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Manage library", "metadata-sources": "Metadata sources", "new-platforms": "New platforms", diff --git a/frontend/src/locales/es_ES/scan.json b/frontend/src/locales/es_ES/scan.json index dfe00bb8a..a4dc5c24c 100644 --- a/frontend/src/locales/es_ES/scan.json +++ b/frontend/src/locales/es_ES/scan.json @@ -15,15 +15,11 @@ "disabled-by-admin": "Deshabilitado por el administrador", "hashes": "Recalcular hashes", "hashes-desc": "Recalcula los hashes de las plataformas seleccionadas", -<<<<<<< HEAD "hashes-disabled-tooltip": "Cálculo de hash deshabilitado.

Los hashes (MD5, SHA1, CRC32) son huellas digitales únicas que identifican archivos ROM con precisión.

Sin ellos, Hasheous y RetroAchievements no pueden comparar juegos con sus bases de datos, pero el escaneo será más rápido.", "hashes-enabled-tooltip": "Cálculo de hash habilitado.

Se calcularán hashes (MD5, SHA1, CRC32) para crear huellas digitales únicas de cada archivo ROM.

Esto permite a Hasheous y RetroAchievements identificar juegos con precisión en sus bases de datos.", "hash-calculation-disabled": "El cálculo de hash está deshabilitado", "hasheous-requires-hashes": "Hasheous requiere que el cálculo de hashes esté habilitado", "retroachievements-requires-hashes": "RetroAchievements requiere que el cálculo de hashes esté habilitado", -======= - "locale-not-supported": "No compatible", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gestionar biblioteca", "metadata-sources": "Fuentes de metadatos", "new-platforms": "Plataformas nuevas", diff --git a/frontend/src/locales/fr_FR/scan.json b/frontend/src/locales/fr_FR/scan.json index 32766f331..4552e6aad 100644 --- a/frontend/src/locales/fr_FR/scan.json +++ b/frontend/src/locales/fr_FR/scan.json @@ -15,15 +15,11 @@ "disabled-by-admin": "Désactivé par l'administrateur", "hashes": "Recalculer les hachages", "hashes-desc": "Recalculer les hachages des plateformes sélectionnées", -<<<<<<< HEAD "hashes-disabled-tooltip": "Calcul de hachage désactivé.

Les hachages (MD5, SHA1, CRC32) sont des empreintes uniques qui identifient les fichiers ROM avec précision.

Sans eux, Hasheous et RetroAchievements ne peuvent pas faire correspondre les jeux à leurs bases de données, mais l'analyse sera plus rapide.", "hashes-enabled-tooltip": "Calcul de hachage de fichier activé.

Les hachages (MD5, SHA1, CRC32) seront calculés pour créer des empreintes uniques pour chaque fichier ROM.

Ceci permet à Hasheous et RetroAchievements d'identifier précisément les jeux dans leurs bases de données.", "hash-calculation-disabled": "Le calcul de hachage est désactivé", "hasheous-requires-hashes": "Hasheous nécessite que le calcul de hachage soit activé", "retroachievements-requires-hashes": "RetroAchievements nécessite que le calcul de hachage soit activé", -======= - "locale-not-supported": "Non pris en charge", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gérer la bibliothèque", "metadata-sources": "Sources de métadonnées", "new-platforms": "Nouvelles plateformes", diff --git a/frontend/src/locales/it_IT/scan.json b/frontend/src/locales/it_IT/scan.json index 4174f2c29..f5c882735 100644 --- a/frontend/src/locales/it_IT/scan.json +++ b/frontend/src/locales/it_IT/scan.json @@ -15,15 +15,11 @@ "disabled-by-admin": "Disabilitato dall'amministratore", "hashes": "Ricalcola hash", "hashes-desc": "Ricalcola gli hash per le piattaforme selezionate", -<<<<<<< HEAD "hashes-disabled-tooltip": "Calcolo hash disabilitato.

Gli hash (MD5, SHA1, CRC32) sono impronte digitali uniche che identificano i file ROM con precisione.

Senza di essi, Hasheous e RetroAchievements non possono confrontare i giochi con i loro database, ma la scansione sarà più veloce.", "hashes-enabled-tooltip": "Calcolo hash abilitato.

Verranno calcolati gli hash (MD5, SHA1, CRC32) per creare impronte digitali uniche di ogni file ROM.

Questo consente a Hasheous e RetroAchievements di identificare accuratamente i giochi nei loro database.", "hash-calculation-disabled": "Il calcolo dell'hash è disabilitato", "hasheous-requires-hashes": "Hasheous richiede che il calcolo degli hash sia abilitato", "retroachievements-requires-hashes": "RetroAchievements richiede che il calcolo degli hash sia abilitato", -======= - "locale-not-supported": "Non supportato", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gestisci libreria", "metadata-sources": "Fonti metadati", "new-platforms": "Nuove piattaforme", diff --git a/frontend/src/locales/ja_JP/scan.json b/frontend/src/locales/ja_JP/scan.json index ba24792b5..db48ced91 100644 --- a/frontend/src/locales/ja_JP/scan.json +++ b/frontend/src/locales/ja_JP/scan.json @@ -15,14 +15,10 @@ "disabled-by-admin": "管理者によって無効化されています", "hashes": "ハッシュ値の再計算", "hashes-desc": "選択されたプラットフォームのハッシュ値を再計算します", -<<<<<<< HEAD "hashes-disabled-tooltip": "ファイルハッシュ計算が無効。

ハッシュ(MD5、SHA1、CRC32)はROMファイルを正確に識別するユニークな指紋です。

これがないと、HasheousやRetroAchievementsはゲームをデータベースとマッチングできませんが、スキャンは高速になります。", "hashes-enabled-tooltip": "ファイルハッシュ計算が有効です。

各ROMファイルの一意の指紋を作成するために、ハッシュ(MD5、SHA1、CRC32)が計算されます。

これにより、HasheousとRetroAchievementsがデータベース内のゲームを正確に識別できます。", "hash-calculation-disabled": "ハッシュ計算が無効になっています", "hasheous-requires-hashes": "Hasheousはファイルハッシュが必要です", -======= - "locale-not-supported": "非対応", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "ライブラリを編集", "metadata-sources": "メタデータ取得元", "new-platforms": "新規プラットフォーム", diff --git a/frontend/src/locales/ko_KR/scan.json b/frontend/src/locales/ko_KR/scan.json index dc9c4c32d..cec6b7cc7 100644 --- a/frontend/src/locales/ko_KR/scan.json +++ b/frontend/src/locales/ko_KR/scan.json @@ -15,14 +15,10 @@ "disabled-by-admin": "관리자에 의해 비활성화됨", "hashes": "해시", "hashes-desc": "선택된 플랫폼의 해시를 다시 계산", -<<<<<<< HEAD "hashes-disabled-tooltip": "해시 계산이 비활성화됨.

해시(MD5, SHA1, CRC32)는 ROM 파일을 정확히 식별하는 고유한 지문입니다.

해시 없이는 Hasheous와 RetroAchievements가 데이터베이스와 게임을 매치할 수 없지만, 스캔이 더 빨라집니다.", "hashes-enabled-tooltip": "해시 계산이 활성화됨.

각 ROM 파일의 고유한 지문을 생성하기 위해 해시(MD5, SHA1, CRC32)가 계산됩니다.

이를 통해 Hasheous와 RetroAchievements가 데이터베이스에서 게임을 정확히 식별할 수 있습니다.", "hash-calculation-disabled": "해시 계산이 비활성화되어 있습니다", "hasheous-requires-hashes": "Hasheous는 파일 해시가 필요합니다", -======= - "locale-not-supported": "지원되지 않음", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "라이브러리 관리", "metadata-sources": "메타데이터 DB", "new-platforms": "새 플랫폼", diff --git a/frontend/src/locales/pl_PL/scan.json b/frontend/src/locales/pl_PL/scan.json index d570b4466..e3bcc284a 100644 --- a/frontend/src/locales/pl_PL/scan.json +++ b/frontend/src/locales/pl_PL/scan.json @@ -15,15 +15,11 @@ "disabled-by-admin": "Wyłączone przez administratora", "hashes": "Przelicz sumy kontrolne", "hashes-desc": "Przelicza sumy kontrolne dla wybranych platform", -<<<<<<< HEAD "hashes-disabled-tooltip": "Obliczanie skrótów wyłączone.

Skróty (MD5, SHA1, CRC32) to unikalne odciski palców, które precyzyjnie identyfikują pliki ROM.

Bez nich Hasheous i RetroAchievements nie mogą dopasować gier do swoich baz danych, ale skanowanie będzie szybsze.", "hashes-enabled-tooltip": "Obliczanie skrótów włączone.

Zostaną obliczone skróty (MD5, SHA1, CRC32) w celu utworzenia unikalnych odcisków palców każdego pliku ROM.

To pozwala Hasheous i RetroAchievements na dokładną identyfikację gier w ich bazach danych.", "hash-calculation-disabled": "Obliczanie skrótów jest wyłączone", "hasheous-requires-hashes": "Hasheous wymaga włączonego obliczania skrótów", "retroachievements-requires-hashes": "RetroAchievements wymaga włączonego obliczania skrótów", -======= - "locale-not-supported": "Nieobsługiwane", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Zarządzaj biblioteką", "metadata-sources": "Źródła metadanych", "new-platforms": "Nowe platformy", diff --git a/frontend/src/locales/pt_BR/scan.json b/frontend/src/locales/pt_BR/scan.json index 96fd9752a..47db866d5 100644 --- a/frontend/src/locales/pt_BR/scan.json +++ b/frontend/src/locales/pt_BR/scan.json @@ -14,17 +14,12 @@ "connection-successful": "Conexão bem-sucedida", "disabled-by-admin": "Desativado pelo administrador", "hashes": "Recalcular hashes", -<<<<<<< HEAD "hashes-desc": "Recalcula hashes das plataformas selecionadas", "hashes-disabled-tooltip": "Cálculo de hash desabilitado.

Hashes (MD5, SHA1, CRC32) são impressões digitais únicas que identificam arquivos ROM com precisão.

Sem eles, Hasheous e RetroAchievements não podem comparar jogos com seus bancos de dados, mas a varredura será mais rápida.", "hashes-enabled-tooltip": "Cálculo de hash habilitado.

Hashes (MD5, SHA1, CRC32) serão calculados para criar impressões digitais únicas de cada arquivo ROM.

Isso permite que Hasheous e RetroAchievements identifiquem jogos com precisão em seus bancos de dados.", "hash-calculation-disabled": "O cálculo de hash está desabilitado", "hasheous-requires-hashes": "Hasheous requer que o cálculo de hash esteja habilitado", "retroachievements-requires-hashes": "RetroAchievements requer que o cálculo de hash esteja habilitado", -======= - "hashes-desc": "Recalcular hashes para as plataformas selecionadas", - "locale-not-supported": "Não suportado", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gerenciar biblioteca", "metadata-sources": "Fontes de metadados", "new-platforms": "Novas plataformas", diff --git a/frontend/src/locales/ro_RO/scan.json b/frontend/src/locales/ro_RO/scan.json index 4f60c2841..fd32de711 100644 --- a/frontend/src/locales/ro_RO/scan.json +++ b/frontend/src/locales/ro_RO/scan.json @@ -14,16 +14,11 @@ "connection-successful": "Conexiune reușită", "disabled-by-admin": "Dezactivat de administrator", "hashes": "Recalculează hash-urile", -<<<<<<< HEAD "hashes-desc": "Recalculează hash-urile pentru platformele selectate", "hashes-disabled-tooltip": "Calculul hash-urilor dezactivat.

Hash-urile (MD5, SHA1, CRC32) sunt amprente digitale unice care identifică fișierele ROM cu precizie.

Fără ele, Hasheous și RetroAchievements nu pot potrivi jocurile cu bazele lor de date, dar scanarea va fi mai rapidă.", "hashes-enabled-tooltip": "Calculul hash-urilor activat.

Se vor calcula hash-uri (MD5, SHA1, CRC32) pentru a crea amprente digitale unice pentru fiecare fișier ROM.

Aceasta permite lui Hasheous și RetroAchievements să identifice cu precizie jocurile în bazele lor de date.", "hash-calculation-disabled": "Calculul hash-ului este dezactivat", "hasheous-requires-hashes": "Hasheous necesită hash-uri de fișiere", -======= - "hashes-desc": "Recalculează hash-urile platformelor selectate", - "locale-not-supported": "Nesuportat", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Gestionează biblioteca", "metadata-sources": "Surse de metadate", "new-platforms": "Platforme noi", diff --git a/frontend/src/locales/ru_RU/scan.json b/frontend/src/locales/ru_RU/scan.json index 5cbff70d0..f960c3795 100644 --- a/frontend/src/locales/ru_RU/scan.json +++ b/frontend/src/locales/ru_RU/scan.json @@ -14,16 +14,11 @@ "connection-successful": "Соединение успешно установлено", "disabled-by-admin": "Отключено администратором", "hashes": "Пересчитать хеши", -<<<<<<< HEAD "hashes-desc": "Пересчитывает хеши для выбранных платформ", "hashes-disabled-tooltip": "Вычисление хешей отключено.

Хеши (MD5, SHA1, CRC32) - это уникальные отпечатки, которые точно идентифицируют файлы ROM.

Без них Hasheous и RetroAchievements не могут сопоставить игры с своими базами данных, но сканирование будет быстрее.", "hashes-enabled-tooltip": "Вычисление хешей включено.

Будут вычислены хеши (MD5, SHA1, CRC32) для создания уникальных отпечатков каждого файла ROM.

Это позволяет Hasheous и RetroAchievements точно идентифицировать игры в своих базах данных.", "hash-calculation-disabled": "Вычисление хешей отключено", "hasheous-requires-hashes": "Hasheous требует хеши файлов", -======= - "hashes-desc": "Пересчитать хеши для выбранных платформ", - "locale-not-supported": "Не поддерживается", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "Управление библиотекой", "metadata-sources": "Источники мета��анных", "new-platforms": "Новые платформы", diff --git a/frontend/src/locales/zh_CN/scan.json b/frontend/src/locales/zh_CN/scan.json index c74eb9813..f580a192a 100644 --- a/frontend/src/locales/zh_CN/scan.json +++ b/frontend/src/locales/zh_CN/scan.json @@ -15,14 +15,10 @@ "disabled-by-admin": "已被管理员禁用", "hashes": "哈希", "hashes-desc": "重新计算选定平台的哈希值", -<<<<<<< HEAD "hashes-disabled-tooltip": "哈希计算已禁用。

哈希值(MD5、SHA1、CRC32)是精确识别 ROM 文件的唯一指纹。

没有它们,Hasheous 和 RetroAchievements 无法将游戏与其数据库进行匹配,但扫描速度会更快。", "hashes-enabled-tooltip": "文件哈希计算已启用。

将计算哈希值(MD5、SHA1、CRC32)为每个 ROM 文件创建唯一指纹。

这使得 Hasheous 和 RetroAchievements 能够在其数据库中准确识别游戏。", "hash-calculation-disabled": "哈希计算已禁用", "hasheous-requires-hashes": "Hasheous 需要文件哈希", -======= - "locale-not-supported": "不支持", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "管理游戏库", "metadata-sources": "元数据源", "new-platforms": "新平台", diff --git a/frontend/src/locales/zh_TW/scan.json b/frontend/src/locales/zh_TW/scan.json index 1eda5caf9..8a11d6e35 100644 --- a/frontend/src/locales/zh_TW/scan.json +++ b/frontend/src/locales/zh_TW/scan.json @@ -15,14 +15,10 @@ "disabled-by-admin": "已被管理員禁用", "hashes": "雜湊", "hashes-desc": "重新計算選定平台的雜湊值", -<<<<<<< HEAD "hashes-disabled-tooltip": "哈希計算已停用。

哈希值(MD5、SHA1、CRC32)是唯一識別 ROM 檔案的數字指紋。

沒有它們,Hasheous 和 RetroAchievements 無法將遊戲與資料庫匹配,但掃描速度會更快。", "hashes-enabled-tooltip": "哈希計算已啟用。

將計算哈希值(MD5、SHA1、CRC32)為每個 ROM 檔案建立唯一指紋。

這使 Hasheous 和 RetroAchievements 能夠準確識別其資料庫中的遊戲。", "hash-calculation-disabled": "雜湊計算已停用", "hasheous-requires-hashes": "Hasheous 需要檔案哈希", -======= - "locale-not-supported": "不支援", ->>>>>>> da0735a29 (Add metadata locale selection for IGDB and ScreenScraper) "manage-library": "管理遊戲庫", "metadata-sources": "元數據來源", "new-platforms": "新平台", diff --git a/frontend/src/services/api/config.ts b/frontend/src/services/api/config.ts index c5ee5429a..9a314d40d 100644 --- a/frontend/src/services/api/config.ts +++ b/frontend/src/services/api/config.ts @@ -53,14 +53,6 @@ async function deleteExclusion({ return api.delete(`/config/exclude/${exclusionType}/${exclusionValue}`); } -async function updateProviderLocales({ - locales, -}: { - locales: Record; -}) { - return api.put("/config/provider-locales", { locales }); -} - export default { addPlatformBindConfig, deletePlatformBindConfig, @@ -68,5 +60,4 @@ export default { deletePlatformVersionConfig, addExclusion, deleteExclusion, - updateProviderLocales, }; diff --git a/frontend/src/stores/config.ts b/frontend/src/stores/config.ts index 16aae969c..ba2135395 100644 --- a/frontend/src/stores/config.ts +++ b/frontend/src/stores/config.ts @@ -31,7 +31,6 @@ const defaultConfig = { SCAN_ARTWORK_PRIORITY: [], SCAN_REGION_PRIORITY: [], SCAN_LANGUAGE_PRIORITY: [], - METADATA_PROVIDER_LOCALES: {}, } as ConfigResponse; export default defineStore("config", { diff --git a/frontend/src/views/Settings/MetadataSources.vue b/frontend/src/views/Settings/MetadataSources.vue index 430b511da..77c1db5b6 100644 --- a/frontend/src/views/Settings/MetadataSources.vue +++ b/frontend/src/views/Settings/MetadataSources.vue @@ -4,56 +4,11 @@ import { useI18n } from "vue-i18n"; import RSection from "@/components/common/RSection.vue"; import storeHeartbeat from "@/stores/heartbeat"; import storeConfig from "@/stores/config"; -import configApi from "@/services/api/config"; const { t } = useI18n(); const heartbeat = storeHeartbeat(); const configStore = storeConfig(); -// Available locales for providers that support them -const ssLocales = computed(() => [ - { title: t("common.default"), value: "" }, - { title: "English", value: "en" }, - { title: "Français", value: "fr" }, - { title: "Deutsch", value: "de" }, - { title: "Español", value: "es" }, - { title: "Italiano", value: "it" }, - { title: "Português", value: "pt" }, -]); - -// IGDB locales (uses full region codes like ja-JP) -const igdbLocales = computed(() => [ - { title: t("common.default"), value: "" }, - { title: "日本語 (Japanese)", value: "ja-JP" }, - { title: "한국어 (Korean)", value: "ko-KR" }, - { title: "简体中文 (Chinese Simplified)", value: "zh-CN" }, - { title: "繁體中文 (Chinese Traditional)", value: "zh-TW" }, - { title: "Europe", value: "EU" }, -]); - -// Get the current locale for a provider -function getProviderLocale(provider: string): string { - return configStore.config.METADATA_PROVIDER_LOCALES[provider] || ""; -} - -// Update locale for a provider -async function updateProviderLocale(provider: string, locale: string) { - const newLocales = { ...configStore.config.METADATA_PROVIDER_LOCALES }; - - if (locale) { - newLocales[provider] = locale; - } else { - delete newLocales[provider]; - } - - try { - await configApi.updateProviderLocales({ locales: newLocales }); - configStore.config.METADATA_PROVIDER_LOCALES = newLocales; - } catch (error) { - console.error("Error updating provider locale:", error); - } -} - const heartbeatStatus = ref>({ igdb: undefined, moby: undefined, @@ -74,8 +29,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/igdb.png", disabled: !heartbeat.value.METADATA_SOURCES?.IGDB_API_ENABLED, heartbeat: heartbeatStatus.value.igdb, - supportsLocale: true, - locales: igdbLocales.value, }, { name: "MobyGames", @@ -83,8 +36,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/moby.png", disabled: !heartbeat.value.METADATA_SOURCES?.MOBY_API_ENABLED, heartbeat: heartbeatStatus.value.moby, - supportsLocale: false, - locales: [], }, { name: "ScreenScrapper", @@ -92,8 +43,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/ss.png", disabled: !heartbeat.value.METADATA_SOURCES?.SS_API_ENABLED, heartbeat: heartbeatStatus.value.ss, - supportsLocale: true, - locales: ssLocales.value, }, { name: "RetroAchievements", @@ -101,8 +50,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/ra.png", disabled: !heartbeat.value.METADATA_SOURCES?.RA_API_ENABLED, heartbeat: heartbeatStatus.value.ra, - supportsLocale: false, - locales: [], }, { name: "Hasheous", @@ -110,8 +57,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/hasheous.png", disabled: !heartbeat.value.METADATA_SOURCES?.HASHEOUS_API_ENABLED, heartbeat: heartbeatStatus.value.hasheous, - supportsLocale: false, - locales: [], }, { name: "Launchbox", @@ -119,8 +64,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/launchbox.png", disabled: !heartbeat.value.METADATA_SOURCES?.LAUNCHBOX_API_ENABLED, heartbeat: heartbeatStatus.value.launchbox, - supportsLocale: false, - locales: [], }, { name: "Flashpoint Project", @@ -128,8 +71,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/flashpoint.png", disabled: !heartbeat.value.METADATA_SOURCES?.FLASHPOINT_API_ENABLED, heartbeat: heartbeatStatus.value.flashpoint, - supportsLocale: false, - locales: [], }, { name: "HowLongToBeat", @@ -137,8 +78,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/hltb.png", disabled: !heartbeat.value.METADATA_SOURCES?.HLTB_API_ENABLED, heartbeat: heartbeatStatus.value.hltb, - supportsLocale: false, - locales: [], }, { name: "SteamgridDB", @@ -146,8 +85,6 @@ const metadataOptions = computed(() => [ logo_path: "/assets/scrappers/sgdb.png", disabled: !heartbeat.value.METADATA_SOURCES?.STEAMGRIDDB_API_ENABLED, heartbeat: heartbeatStatus.value.sgdb, - supportsLocale: false, - locales: [], }, ]); @@ -273,33 +210,6 @@ onMounted(() => { - - - - - - - - From 9dfad6cb87d533b7bc898ff1db735dfa1f15ea9b Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Wed, 26 Nov 2025 11:15:44 -0500 Subject: [PATCH 4/4] run trunk fmt --- backend/handler/metadata/igdb_handler.py | 24 +++++++++++-------- backend/handler/metadata/ss_handler.py | 4 +++- examples/config.example.yml | 4 ++-- .../src/views/Settings/MetadataSources.vue | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/backend/handler/metadata/igdb_handler.py b/backend/handler/metadata/igdb_handler.py index 8f5160fc3..d5d27692d 100644 --- a/backend/handler/metadata/igdb_handler.py +++ b/backend/handler/metadata/igdb_handler.py @@ -213,13 +213,13 @@ def extract_metadata_from_igdb_rom(self: MetadataHandler, rom: Game) -> IGDBMeta # Mapping from scan.priority.region codes to IGDB game_localizations region identifiers # IGDB's game_localizations provides regional titles and cover art, but NOT localized descriptions REGION_TO_IGDB_LOCALE: dict[str, str | None] = { - "us": None, # United States - use default (no localization needed) - "wor": None, # World - use default - "eu": "EU", # Europe region - "jp": "ja-JP", # Japan - "kr": "ko-KR", # Korea - "cn": "zh-CN", # China (Simplified Chinese) - "tw": "zh-TW", # Taiwan (Traditional Chinese) + "us": None, # United States - use default (no localization needed) + "wor": None, # World - use default + "eu": "EU", # Europe region + "jp": "ja-JP", # Japan + "kr": "ko-KR", # Korea + "cn": "zh-CN", # China (Simplified Chinese) + "tw": "zh-TW", # Taiwan (Traditional Chinese) } @@ -240,7 +240,7 @@ def get_igdb_preferred_locale() -> str | None: if igdb_locale is not None: return igdb_locale - return None # Default - no localization + return None def extract_localized_data(rom: Game, preferred_locale: str | None) -> tuple[str, str]: @@ -281,11 +281,15 @@ def extract_localized_data(rom: Game, preferred_locale: str | None) -> tuple[str return localized_name, cover_url # Locale not found, fall back to default - log.warning(f"IGDB locale '{preferred_locale}' not found for '{default_name}', using default") + log.warning( + f"IGDB locale '{preferred_locale}' not found for '{default_name}', using default" + ) return default_name, default_cover -def build_igdb_rom(handler: "IGDBHandler", rom: Game, preferred_locale: str | None) -> "IGDBRom": +def build_igdb_rom( + handler: "IGDBHandler", rom: Game, preferred_locale: str | None +) -> "IGDBRom": """Build an IGDBRom from IGDB game data with localization support. Args: diff --git a/backend/handler/metadata/ss_handler.py b/backend/handler/metadata/ss_handler.py index 9ecfbfd31..a87ec0ec4 100644 --- a/backend/handler/metadata/ss_handler.py +++ b/backend/handler/metadata/ss_handler.py @@ -413,7 +413,9 @@ def build_ss_game(rom: Rom, game: SSGame) -> SSRom: # Log warning if we had to fall back from the preferred locale if preferred_languages and used_lang and used_lang != preferred_languages[0]: - log.warning(f"ScreenScraper locale '{preferred_languages[0]}' not found for '{res_name}', using '{used_lang}'") + log.warning( + f"ScreenScraper locale '{preferred_languages[0]}' not found for '{res_name}', using '{used_lang}'" + ) url_cover = ss_metadata["box2d_url"] url_manual = ( diff --git a/examples/config.example.yml b/examples/config.example.yml index f870f8c6f..d349d871e 100644 --- a/examples/config.example.yml +++ b/examples/config.example.yml @@ -71,13 +71,13 @@ filesystem: {} # { roms_folder: 'roms' } For example if your folder structure is # - "hasheous" # Hasheous # - "flashpoint" # Flashpoint Project # - "hltb" # HowLongToBeat -# region: # Cover art and game title (only used by Screenscraper) +# region: # Used by IGDB and ScreenScraper for regional variants # - "us" # - "wor" # - "ss" # - "eu" # - "jp" -# language: # Cover art and game title (only used by Screenscraper) +# language: # Used by ScreenScraper for descriptions # - "en" # - "fr" # # Media assets to download diff --git a/frontend/src/views/Settings/MetadataSources.vue b/frontend/src/views/Settings/MetadataSources.vue index 77c1db5b6..44b140f0a 100644 --- a/frontend/src/views/Settings/MetadataSources.vue +++ b/frontend/src/views/Settings/MetadataSources.vue @@ -2,8 +2,8 @@ import { computed, onMounted, ref } from "vue"; import { useI18n } from "vue-i18n"; import RSection from "@/components/common/RSection.vue"; -import storeHeartbeat from "@/stores/heartbeat"; import storeConfig from "@/stores/config"; +import storeHeartbeat from "@/stores/heartbeat"; const { t } = useI18n(); const heartbeat = storeHeartbeat();