fix: add missing translations and fix translation loading/testing

This commit is contained in:
sebthom
2025-05-13 19:27:43 +02:00
parent 21d7cc557d
commit 1f9895850f
3 changed files with 46 additions and 16 deletions

View File

@@ -38,17 +38,20 @@ kleinanzeigen_bot/__init__.py:
load_ads: load_ads:
"Searching for ad config files...": "Suche nach Anzeigendateien..." "Searching for ad config files...": "Suche nach Anzeigendateien..."
" -> found %s": "-> %s gefunden" " -> 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):" "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: 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 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." " -> 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]." "Category [%s] unknown. Using category [%s] with ID [%s] instead.": "Kategorie [%s] unbekannt. Verwende stattdessen Kategorie [%s] mit ID [%s]."
"Loaded %s": "%s geladen" "Loaded %s": "%s geladen"
"ad": "Anzeige"
load_config: 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 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: login:
"Checking if already logged in...": "Überprüfe, ob bereits eingeloggt..." "Checking if already logged in...": "Überprüfe, ob bereits eingeloggt..."
@@ -64,6 +67,7 @@ kleinanzeigen_bot/__init__.py:
delete_ads: delete_ads:
"Processing %s/%s: '%s' from [%s]...": "Verarbeite %s/%s: '%s' von [%s]..." "Processing %s/%s: '%s' from [%s]...": "Verarbeite %s/%s: '%s' von [%s]..."
"DONE: Deleted %s": "FERTIG: %s gelöscht" "DONE: Deleted %s": "FERTIG: %s gelöscht"
"ad": "Anzeige"
delete_ad: delete_ad:
"Deleting ad '%s' if already present...": "Lösche Anzeige '%s', falls bereits vorhanden..." "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]..." "Processing %s/%s: '%s' from [%s]...": "Verarbeite %s/%s: '%s' von [%s]..."
"Skipping because ad is reserved": "Überspringen, da Anzeige reserviert ist" "Skipping because ad is reserved": "Überspringen, da Anzeige reserviert ist"
"DONE: (Re-)published %s": "FERTIG: %s (erneut) veröffentlicht" "DONE: (Re-)published %s": "FERTIG: %s (erneut) veröffentlicht"
"ad": "Anzeige"
publish_ad: publish_ad:
"Publishing ad '%s'...": "Veröffentliche Anzeige '%s'..." "Publishing ad '%s'...": "Veröffentliche Anzeige '%s'..."
@@ -92,6 +97,7 @@ kleinanzeigen_bot/__init__.py:
__upload_images: __upload_images:
" -> found %s": "-> %s gefunden" " -> found %s": "-> %s gefunden"
"image": "Bild"
" -> uploading image [%s]": " -> Lade Bild [%s] hoch" " -> uploading image [%s]": " -> Lade Bild [%s] hoch"
__check_ad_republication: __check_ad_republication:
@@ -109,15 +115,17 @@ kleinanzeigen_bot/__init__.py:
download_ads: download_ads:
"Scanning your ad overview...": "Scanne Anzeigenübersicht..." "Scanning your ad overview...": "Scanne Anzeigenübersicht..."
"%s found.": "%s gefunden."
"ad": "Anzeige"
"Starting download of all ads...": "Starte den Download aller Anzeigen..." "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." "%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..." "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." "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." "%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):" "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" "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!" "The page with the id %d does not exist!": "Die Seite mit der ID %d existiert nicht!"
"%s found.": "%s gefunden."
parse_args: parse_args:
"Use --help to display available options.": "Mit --help können die verfügbaren Optionen angezeigt werden." "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" "%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" "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: kleinanzeigen_bot/utils/dicts.py:
################################################# #################################################

View File

@@ -109,6 +109,8 @@ def translate(text:object, caller:inspect.FrameInfo | None) -> str:
file_basename = os.path.splitext(os.path.basename(caller.filename))[0] file_basename = os.path.splitext(os.path.basename(caller.filename))[0]
if module_name and module_name.endswith(f".{file_basename}"): if module_name and module_name.endswith(f".{file_basename}"):
module_name = module_name[:-(len(file_basename) + 1)] 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" file_key = f"{file_basename}.py" if module_name == file_basename else f"{module_name}/{file_basename}.py"
translation = dicts.safe_get(_TRANSLATIONS, translation = dicts.safe_get(_TRANSLATIONS,
file_key, file_key,

View File

@@ -111,7 +111,7 @@ def _extract_log_messages(file_path:str, exclude_debug:bool = False) -> MessageD
if msg not in messages[function]: if msg not in messages[function]:
messages[function][msg] = {msg} 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.""" """Safely extract string value from an AST node."""
if isinstance(node, ast.Constant): if isinstance(node, ast.Constant):
value = getattr(node, "value", None) 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) function_name = _get_function_name(node)
# Extract messages from various call types # Extract messages from various call types
if (isinstance(node.func, ast.Attribute) and
isinstance(node.func.value, ast.Name) and # 1) Logging calls: LOG.info(…), logger.warning(…), etc.
node.func.value.id in {"LOG", "logger", "logging"} and if (
node.func.attr in {None if exclude_debug else "debug", "info", "warning", "error", "exception", "critical"}): 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: if node.args:
msg = extract_string_value(node.args[0]) msg = extract_string_constant(node.args[0])
if msg: if msg:
add_message(function_name, msg) add_message(function_name, msg)
# Handle gettext calls # 2) gettext: _("…") or obj.gettext("…")
elif ((isinstance(node.func, ast.Name) and node.func.id == "_") or elif (
(isinstance(node.func, ast.Attribute) and node.func.attr == "gettext")): (isinstance(node.func, ast.Name) and node.func.id == "_") or
(isinstance(node.func, ast.Attribute) and node.func.attr == "gettext")
):
if node.args: if node.args:
msg = extract_string_value(node.args[0]) msg = extract_string_constant(node.args[0])
if msg: if msg:
add_message(function_name, msg) add_message(function_name, msg)
# Handle other translatable function calls # Handle other translatable function calls
elif isinstance(node.func, ast.Name) and node.func.id in {"ainput", "pluralize", "ensure"}: 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: if len(node.args) > arg_index:
msg = extract_string_value(node.args[arg_index]) msg = extract_string_constant(node.args[arg_index])
if msg: if msg:
add_message(function_name, msg) add_message(function_name, msg)
print(f"Messages: {messages}") print(f"Messages: {len(messages)} in {file_path}")
return messages return messages
@@ -376,6 +382,12 @@ def test_no_obsolete_translations(lang:str) -> None:
""" """
messages_by_file = _get_all_log_messages(exclude_debug = False) messages_by_file = _get_all_log_messages(exclude_debug = False)
translations = _get_translations_for_language(lang) 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]] = [] obsolete_items:list[tuple[str, str, str]] = []
for module, module_trans in translations.items(): for module, module_trans in translations.items():