From d879c292ddccc91cc3f074bbeb42a0a50a5b017b Mon Sep 17 00:00:00 2001 From: Thomas D <11554546+thomasddn@users.noreply.github.com> Date: Fri, 7 Feb 2025 15:33:44 +0000 Subject: [PATCH] Check translation files for errors --- .github/workflows/ci.yml | 31 +++++++++++++++ scripts/__init__.py | 1 + scripts/check_translations.py | 71 +++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 scripts/__init__.py create mode 100644 scripts/check_translations.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05d42f8..e1f7df9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,6 +81,37 @@ jobs: pip install -r requirements.txt pip install -r requirements.test.txt + translations: + name: Check translations + runs-on: ubuntu-latest + needs: + - info + - prepare + steps: + - name: Check out code + uses: actions/checkout@v4.2.2 + + - name: Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@v5.3.0 + with: + python-version: ${{ env.DEFAULT_PYTHON }} + + - name: Restore Python ${{ env.DEFAULT_PYTHON }} virtual environment + id: cache-venv + uses: actions/cache/restore@v4.2.0 + with: + path: venv + fail-on-cache-miss: true + key: >- + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.info.outputs.python_cache_key }} + + - name: Run translations check + run: | + . venv/bin/activate + python --version + python scripts/check_translations.py --ignore-errors + mypy: name: Check mypy runs-on: ubuntu-latest diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..a9a18d7 --- /dev/null +++ b/scripts/__init__.py @@ -0,0 +1 @@ +"""Scripts.""" diff --git a/scripts/check_translations.py b/scripts/check_translations.py new file mode 100644 index 0000000..59f6211 --- /dev/null +++ b/scripts/check_translations.py @@ -0,0 +1,71 @@ +"""Check translation files for errors.""" + +import json +from pathlib import Path +import sys + +BASE_TRANSLATION = Path("custom_components/volvo_cars/strings.json") +TRANSLATIONS_DIR = Path("custom_components/volvo_cars/translations") + + +def _flatten_items(data: dict, parent: str = "") -> dict: + """Return a dict mapping dot-separated keys to their values.""" + items = {} + for key, value in data.items(): + full_key = f"{parent}::{key}" if parent else key + if isinstance(value, dict): + items.update(_flatten_items(value, full_key)) + else: + items[full_key] = value + return items + + +def _load_json(path: Path) -> dict: + with path.open(encoding="utf-8") as f: + return json.load(f) + + +def _is_empty(value) -> bool: + return value is None or (isinstance(value, str) and not value.strip()) + + +def main(): + """Check the translation files for errors.""" + + ignore_errors = "--ignore-errors" in sys.argv + errors = 0 + + base_data = _load_json(BASE_TRANSLATION) + base_items = _flatten_items(base_data) + base_keys = set(base_items.keys()) + + for file in TRANSLATIONS_DIR.glob("*.json"): + print(f"--- {file.name} ---") + + translation_data = _load_json(file) + trans_items = _flatten_items(translation_data) + trans_keys = set(trans_items.keys()) + + missing = base_keys - trans_keys + orphaned = trans_keys - base_keys + empty_values = [k for k, v in trans_items.items() if _is_empty(v)] + + if missing: + print(" Missing keys:", sorted(missing)) + errors = errors + len(missing) + + if orphaned: + print(" Orphaned keys:", sorted(orphaned)) + errors = errors + len(orphaned) + + if empty_values: + print(" Empty values:", sorted(empty_values)) + errors = errors + len(empty_values) + + print() + + sys.exit(0) if ignore_errors else sys.exit(1 if errors > 0 else 0) + + +if __name__ == "__main__": + main()