# 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//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]) )