#!/usr/bin/env python3 import os import re from pathlib import Path def parse_strings_manually(path): """Parses strings.xml manually to be resilient to invalid XML (text between tags).""" try: content = path.read_text(encoding='utf-8') except Exception as e: print(f"Error reading {path}: {e}") return {} # Find all string tags. Handle attributes like 'comment' or 'translatable'. pattern = re.compile(r']*)>(.*?)', re.DOTALL) strings = {} for match in pattern.finditer(content): name = match.group(1) attrs_str = match.group(2) value = match.group(3) # Parse attributes attrs = {} attr_pattern = re.compile(r'(\w+)="([^"]*)"') for attr_match in attr_pattern.finditer(attrs_str): attrs[attr_match.group(1)] = attr_match.group(2) strings[name] = {'value': value, 'attrs': attrs} return strings def write_strings(path, strings, keys_order): lines = ['', ''] for key in keys_order: if key in strings: s = strings[key] attrs_parts = [f'name="{key}"'] # Sort attributes, but keep name first for k, v in sorted(s['attrs'].items()): if k != 'name': attrs_parts.append(f'{k}="{v}"') attrs_str = ' '.join(attrs_parts) lines.append(f' {s["value"]}') lines.append('') path.write_text('\n'.join(lines) + '\n', encoding='utf-8') def main(): # Detect project root script_dir = Path(__file__).resolve().parent project_root = script_dir.parent res_dir = project_root / "app/src/main/res" base_path = res_dir / "values/strings.xml" if not base_path.exists(): print(f"Base strings.xml not found at {base_path}") return # 1. Load and sort master master_strings = parse_strings_manually(base_path) sorted_keys = sorted(master_strings.keys()) # Update master file (sorted) write_strings(base_path, master_strings, sorted_keys) print(f"Sorted {base_path}") # 2. Process all translations translation_files = sorted(list(res_dir.glob("values-*/strings.xml"))) for trans_path in translation_files: trans_strings = parse_strings_manually(trans_path) # Sync: Add missing keys from master for key in sorted_keys: if key not in trans_strings: # Use master value as placeholder trans_strings[key] = { 'value': master_strings[key]['value'], 'attrs': {} # Don't copy comments to translations } # Remove keys that are NOT in master to_remove = [k for k in trans_strings if k not in sorted_keys] for k in to_remove: del trans_strings[k] # Write back sorted and synced write_strings(trans_path, trans_strings, sorted_keys) print(f"Synced and sorted {trans_path}") if __name__ == "__main__": main()