taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

check-translations.py (4105B)


      1 #!/usr/bin/env python3
      2 """
      3 check-translations.py
      4 
      5 Finds all Android string resources by first locating the git root (.git folder), allowing to
      6 run it reliably from sub folders.
      7 
      8 Usage:
      9     python3 check-translations.py           # Overview of all languages, across all repo modules
     10     python3 check-translations.py de        # Show concrete missing German strings per-module
     11 """
     12 
     13 import argparse
     14 import xml.etree.ElementTree as ET
     15 from pathlib import Path
     16 from collections import defaultdict
     17 
     18 
     19 def find_git_root(start: Path) -> Path | None:
     20     """Walk upwards until we find a .git directory."""
     21     current = start.resolve()
     22     while current != current.parent:
     23         if (current / ".git").exists():
     24             return current
     25         current = current.parent
     26     return None
     27 
     28 
     29 def get_string_names(xml_path: Path) -> set[str]:
     30     if not xml_path.exists():
     31         return set()
     32     try:
     33         tree = ET.parse(xml_path)
     34         return {
     35             elem.get("name")
     36             for elem in tree.findall(".//string")
     37             # Only propose string names which are thought to be translated.
     38             if elem.get("name") and elem.get("translatable") != "false"
     39         }
     40     except ET.ParseError:
     41         return set()
     42 
     43 
     44 def find_all_res_directories(git_root: Path) -> list[Path]:
     45     """Find all res/ folders inside the git repository."""
     46     res_dirs = []
     47     for values_dir in git_root.rglob("values"):
     48         if (values_dir / "strings.xml").exists():
     49             res_dirs.append(values_dir.parent)
     50     return sorted(set(res_dirs))
     51 
     52 
     53 def main():
     54     parser = argparse.ArgumentParser()
     55     parser.add_argument("lang", nargs="?", help="Language code (e.g., de or it)")
     56     args = parser.parse_args()
     57 
     58     git_root = find_git_root(Path.cwd())
     59 
     60     if not git_root:
     61         print("Could not find a .git directory. Please run from inside a git repository.")
     62         return
     63 
     64     print(f"Git root: {git_root}\n")
     65 
     66     res_dirs = find_all_res_directories(git_root)
     67 
     68     if not res_dirs:
     69         print("No Android string resources found in this repository.")
     70         return
     71 
     72     print(f"Found {len(res_dirs)} resource folder(s)\n")
     73 
     74     lang_data = defaultdict(list)
     75 
     76     for res_dir in res_dirs:
     77         ref_file = res_dir / "values" / "strings.xml"
     78         if not ref_file.exists():
     79             continue
     80 
     81         ref_names = get_string_names(ref_file)
     82         if not ref_names:
     83             continue
     84 
     85         for values_dir in res_dir.glob("values-*"):
     86             lang_code = values_dir.name.removeprefix("values-")
     87             strings_file = values_dir / "strings.xml"
     88             if not strings_file.exists():
     89                 continue
     90 
     91             present = get_string_names(strings_file)
     92             missing = ref_names - present
     93 
     94             lang_data[lang_code].append({
     95                 "res_dir": res_dir,
     96                 "missing_count": len(missing),
     97                 "missing_names": sorted(missing)
     98             })
     99 
    100     # Overview
    101     print("Language   Folders   Total Missing")
    102     print("-" * 35)
    103     for lang in sorted(lang_data.keys()):
    104         entries = lang_data[lang]
    105         total_missing = sum(e["missing_count"] for e in entries)
    106         print(f"{lang:<10} {len(entries):>7}   {total_missing:>6}")
    107 
    108     print()
    109 
    110     if args.lang:
    111         lang = args.lang
    112         if lang not in lang_data:
    113             print(f"No '{lang}' translations found.")
    114             return
    115 
    116         print(f"=== Missing strings in '{lang}' ===\n")
    117 
    118         for entry in lang_data[lang]:
    119             res_dir = entry["res_dir"]
    120             missing_names = entry["missing_names"]
    121 
    122             # Show actual module names (merchant-terminal, cashier, wallet, ...)
    123             try:
    124                 module = res_dir.relative_to(git_root).parts[0]
    125             except Exception:
    126                 module = str(res_dir)
    127 
    128             print(f"→ {module}  ({entry['missing_count']} missing)")
    129 
    130             if missing_names:
    131                 for name in missing_names:
    132                     print(f"   {name}")
    133             else:
    134                 print("   (complete)")
    135             print()
    136 
    137 
    138 if __name__ == "__main__":
    139     main()