diff --git a/src/kleinanzeigen_bot/resources/translations.de.yaml b/src/kleinanzeigen_bot/resources/translations.de.yaml index a40b676..ac37b26 100644 --- a/src/kleinanzeigen_bot/resources/translations.de.yaml +++ b/src/kleinanzeigen_bot/resources/translations.de.yaml @@ -38,17 +38,20 @@ kleinanzeigen_bot/__init__.py: load_ads: "Searching for ad config files...": "Suche nach Anzeigendateien..." " -> found %s": "-> %s gefunden" + "ad config file": "Anzeigendatei" "Start fetch task for the ad(s) with id(s):": "Starte Abrufaufgabe für die Anzeige(n) mit ID(s):" " -> SKIPPED: inactive ad [%s]": " -> ÜBERSPRUNGEN: inaktive Anzeige [%s]" " -> SKIPPED: ad [%s] is not in list of given ids.": " -> ÜBERSPRUNGEN: Anzeige [%s] ist nicht in der Liste der angegebenen IDs." " -> SKIPPED: ad [%s] is not new. already has an id assigned.": " -> ÜBERSPRUNGEN: Anzeige [%s] ist nicht neu. Eine ID wurde bereits zugewiesen." "Category [%s] unknown. Using category [%s] with ID [%s] instead.": "Kategorie [%s] unbekannt. Verwende stattdessen Kategorie [%s] mit ID [%s]." "Loaded %s": "%s geladen" + "ad": "Anzeige" load_config: - " -> found %s": "-> %s gefunden" - "config": "Konfiguration" "Config file %s does not exist. Creating it with default values...": "Konfigurationsdatei %s existiert nicht. Erstelle sie mit Standardwerten..." + "config": "Konfiguration" + " -> found %s": "-> %s gefunden" + "category": "Kategorie" login: "Checking if already logged in...": "Überprüfe, ob bereits eingeloggt..." @@ -64,6 +67,7 @@ kleinanzeigen_bot/__init__.py: delete_ads: "Processing %s/%s: '%s' from [%s]...": "Verarbeite %s/%s: '%s' von [%s]..." "DONE: Deleted %s": "FERTIG: %s gelöscht" + "ad": "Anzeige" delete_ad: "Deleting ad '%s' if already present...": "Lösche Anzeige '%s', falls bereits vorhanden..." @@ -74,6 +78,7 @@ kleinanzeigen_bot/__init__.py: "Processing %s/%s: '%s' from [%s]...": "Verarbeite %s/%s: '%s' von [%s]..." "Skipping because ad is reserved": "Überspringen, da Anzeige reserviert ist" "DONE: (Re-)published %s": "FERTIG: %s (erneut) veröffentlicht" + "ad": "Anzeige" publish_ad: "Publishing ad '%s'...": "Veröffentliche Anzeige '%s'..." @@ -92,6 +97,7 @@ kleinanzeigen_bot/__init__.py: __upload_images: " -> found %s": "-> %s gefunden" + "image": "Bild" " -> uploading image [%s]": " -> Lade Bild [%s] hoch" __check_ad_republication: @@ -109,15 +115,17 @@ kleinanzeigen_bot/__init__.py: download_ads: "Scanning your ad overview...": "Scanne Anzeigenübersicht..." + "%s found.": "%s gefunden." + "ad": "Anzeige" "Starting download of all ads...": "Starte den Download aller Anzeigen..." "%d of %d ads were downloaded from your profile.": "%d von %d Anzeigen wurden aus Ihrem Profil heruntergeladen." "Starting download of not yet downloaded ads...": "Starte den Download noch nicht heruntergeladener Anzeigen..." "The ad with id %d has already been saved.": "Die Anzeige mit der ID %d wurde bereits gespeichert." "%s were downloaded from your profile.": "%s wurden aus Ihrem Profil heruntergeladen." + "new ad": "neue Anzeige" "Starting download of ad(s) with the id(s):": "Starte Download der Anzeige(n) mit den ID(s):" "Downloaded ad with id %d": "Anzeige mit der ID %d heruntergeladen" "The page with the id %d does not exist!": "Die Seite mit der ID %d existiert nicht!" - "%s found.": "%s gefunden." parse_args: "Use --help to display available options.": "Mit --help können die verfügbaren Optionen angezeigt werden." @@ -201,6 +209,14 @@ kleinanzeigen_bot/utils/error_handlers.py: "%s: %s": "%s: %s" "Unknown exception occurred (missing exception info): ex_type=%s, ex_value=%s": "Unbekannter Fehler aufgetreten (fehlende Fehlerinformation): ex_type=%s, ex_value=%s" +################################################# +kleinanzeigen_bot/utils/loggers.py: +################################################# + format: + "CRITICAL": "KRITISCH" + "ERROR": "FEHLER" + "WARNING": "WARNUNG" + ################################################# kleinanzeigen_bot/utils/dicts.py: ################################################# diff --git a/src/kleinanzeigen_bot/utils/i18n.py b/src/kleinanzeigen_bot/utils/i18n.py index e60b602..d1074be 100644 --- a/src/kleinanzeigen_bot/utils/i18n.py +++ b/src/kleinanzeigen_bot/utils/i18n.py @@ -109,6 +109,8 @@ def translate(text:object, caller:inspect.FrameInfo | None) -> str: file_basename = os.path.splitext(os.path.basename(caller.filename))[0] if module_name and module_name.endswith(f".{file_basename}"): module_name = module_name[:-(len(file_basename) + 1)] + if module_name: + module_name = module_name.replace(".", "/") file_key = f"{file_basename}.py" if module_name == file_basename else f"{module_name}/{file_basename}.py" translation = dicts.safe_get(_TRANSLATIONS, file_key, diff --git a/tests/unit/test_translations.py b/tests/unit/test_translations.py index 4496672..0585382 100644 --- a/tests/unit/test_translations.py +++ b/tests/unit/test_translations.py @@ -111,7 +111,7 @@ def _extract_log_messages(file_path:str, exclude_debug:bool = False) -> MessageD if msg not in messages[function]: messages[function][msg] = {msg} - def extract_string_value(node:ast.AST) -> str | None: + def extract_string_constant(node:ast.AST) -> str | None: """Safely extract string value from an AST node.""" if isinstance(node, ast.Constant): value = getattr(node, "value", None) @@ -125,32 +125,38 @@ def _extract_log_messages(file_path:str, exclude_debug:bool = False) -> MessageD function_name = _get_function_name(node) # Extract messages from various call types - if (isinstance(node.func, ast.Attribute) and - isinstance(node.func.value, ast.Name) and - node.func.value.id in {"LOG", "logger", "logging"} and - node.func.attr in {None if exclude_debug else "debug", "info", "warning", "error", "exception", "critical"}): + + # 1) Logging calls: LOG.info(…), logger.warning(…), etc. + if ( + isinstance(node.func, ast.Attribute) and + isinstance(node.func.value, ast.Name) and + node.func.value.id in {"LOG", "logger", "logging"} and + node.func.attr in {None if exclude_debug else "debug", "info", "warning", "error", "exception", "critical"} + ): if node.args: - msg = extract_string_value(node.args[0]) + msg = extract_string_constant(node.args[0]) if msg: add_message(function_name, msg) - # Handle gettext calls - elif ((isinstance(node.func, ast.Name) and node.func.id == "_") or - (isinstance(node.func, ast.Attribute) and node.func.attr == "gettext")): + # 2) gettext: _("…") or obj.gettext("…") + elif ( + (isinstance(node.func, ast.Name) and node.func.id == "_") or + (isinstance(node.func, ast.Attribute) and node.func.attr == "gettext") + ): if node.args: - msg = extract_string_value(node.args[0]) + msg = extract_string_constant(node.args[0]) if msg: add_message(function_name, msg) # Handle other translatable function calls elif isinstance(node.func, ast.Name) and node.func.id in {"ainput", "pluralize", "ensure"}: - arg_index = 0 if node.func.id == "ainput" else 1 + arg_index = 1 if node.func.id == "ensure" else 0 if len(node.args) > arg_index: - msg = extract_string_value(node.args[arg_index]) + msg = extract_string_constant(node.args[arg_index]) if msg: add_message(function_name, msg) - print(f"Messages: {messages}") + print(f"Messages: {len(messages)} in {file_path}") return messages @@ -376,6 +382,12 @@ def test_no_obsolete_translations(lang:str) -> None: """ messages_by_file = _get_all_log_messages(exclude_debug = False) translations = _get_translations_for_language(lang) + + # ignore values that are not in code + del translations["kleinanzeigen_bot/utils/loggers.py"]["format"]["CRITICAL"] + del translations["kleinanzeigen_bot/utils/loggers.py"]["format"]["ERROR"] + del translations["kleinanzeigen_bot/utils/loggers.py"]["format"]["WARNING"] + obsolete_items:list[tuple[str, str, str]] = [] for module, module_trans in translations.items():