gateway/tests/validation/test_featureCatalogLabels_i18n.py
2026-04-21 23:49:46 +02:00

58 lines
2.3 KiB
Python

# Copyright (c) 2026 Patrick Motsch
# All rights reserved.
"""Validation: every label in feature ``main*.py`` catalog lists must be wrapped in ``t(...)``.
Background:
``UI_OBJECTS``, ``DATA_OBJECTS``, ``RESOURCE_OBJECTS``, ``WORKFLOW_DEFINITIONS`` etc.
in ``gateway/modules/features/<x>/main*.py`` define labels that the UDB and other
UI surfaces display. Bare-string labels (``"label": "Konfiguration"``) are not
registered with the i18n catalog at module-import time, so non-DE renders show
them as ``[Konfiguration]`` (the missing-translation marker from
``modules.shared.i18nRegistry.t()``).
This test scans every ``main*.py`` under ``gateway/modules/features`` and fails
if it finds bare-string labels, blocking the regression in CI.
Allowed exceptions:
- Labels that are NOT user-visible (e.g. internal demo seed data, fixtures).
Add their file to ``_ALLOWED_FILES_WITH_BARE_LABELS`` with a justification.
"""
from __future__ import annotations
import re
from pathlib import Path
import pytest
_FEATURES_DIR = Path(__file__).resolve().parents[2] / "modules" / "features"
_BARE_LABEL_PATTERN = re.compile(r'^\s*"label"\s*:\s*"[^"]+"', re.MULTILINE)
# mainRealEstate.py contains "label": "AA1704" inside a multi-line f-string
# that is used as a JSON example in an AI prompt -- not a real catalog entry.
_ALLOWED_FILES_WITH_BARE_LABELS: set[str] = {
"mainRealEstate.py",
}
def _findFeatureMainFiles() -> list[Path]:
return sorted(_FEATURES_DIR.glob("*/main*.py"))
@pytest.mark.parametrize("mainFile", _findFeatureMainFiles(), ids=lambda p: p.name)
def test_noBareLabelsInFeatureCatalog(mainFile: Path) -> None:
if mainFile.name in _ALLOWED_FILES_WITH_BARE_LABELS:
pytest.skip(f"{mainFile.name} explicitly allowed (legacy seed data)")
text = mainFile.read_text(encoding="utf-8")
matches = _BARE_LABEL_PATTERN.findall(text)
assert not matches, (
f"\n{mainFile.relative_to(_FEATURES_DIR.parent.parent)} contains "
f"{len(matches)} bare-string labels that are NOT registered with i18n.\n"
f"Wrap each label with t(\"...\", context=\"UI\") so non-DE locales\n"
f"don't render them as [missing-key].\n\n"
f"Sample offending lines:\n "
+ "\n ".join(matches[:5])
)