diff --git a/script/translations_clean.py b/script/translations_clean.py new file mode 100644 index 00000000000000..d801ce0a308562 --- /dev/null +++ b/script/translations_clean.py @@ -0,0 +1,116 @@ +"""Find translation keys that are in Lokalise but no longer defined in source.""" +import json +import pathlib +import sys + +import requests + +INTEGRATION_DIR = pathlib.Path("homeassistant/components") +PROJECT_ID = "130246255a974bd3b5e8a1.51616605" + + +class Lokalise: + """Lokalise API.""" + + def __init__(self, project_id, token): + """Initialize Lokalise API.""" + self.project_id = project_id + self.token = token + + def request(self, method, path, data): + """Make a request to the Lokalise API.""" + method = method.upper() + kwargs = {"headers": {"x-api-token": self.token}} + if method == "GET": + kwargs["params"] = data + else: + kwargs["json"] = data + + req = requests.request( + method, + f"https://api.lokalise.com/api2/projects/{self.project_id}/{path}", + **kwargs, + ) + req.raise_for_status() + return req.json() + + def keys_list(self, params={}): + """Fetch key ID from a name. + + https://app.lokalise.com/api2docs/curl/#transition-list-all-keys-get + """ + return self.request("GET", "keys", params)["keys"] + + def keys_delete_multiple(self, key_ids): + """Delete multiple keys. + + https://app.lokalise.com/api2docs/curl/#transition-delete-multiple-keys-delete + """ + return self.request("DELETE", "keys", {"keys": key_ids}) + + +def find_extra(base, translations, path_prefix, missing_keys): + """Find all keys that are in translations but not in base.""" + for key, value in translations.items(): + cur_path = f"{path_prefix}::{key}" if path_prefix else key + + # Value is either a dict or a string + if isinstance(value, dict): + base_search = None if base is None else base.get(key) + find_extra(base_search, value, cur_path, missing_keys) + + elif base is None or key not in base: + missing_keys.append(cur_path) + + +def find(): + """Find all missing keys.""" + missing_keys = [] + + for int_dir in INTEGRATION_DIR.iterdir(): + strings = int_dir / "strings.json" + + if not strings.is_file(): + continue + + translations = int_dir / ".translations" / "en.json" + + strings_json = json.loads(strings.read_text()) + translations_json = json.loads(translations.read_text()) + + find_extra( + strings_json, translations_json, f"component::{int_dir.name}", missing_keys + ) + + return missing_keys + + +def run(): + """Clean translations.""" + missing_keys = find() + + if not missing_keys: + print("No missing translations!") + return 0 + + lokalise = Lokalise(PROJECT_ID, pathlib.Path(".lokalise_token").read_text().strip()) + + to_delete = [] + + for key in missing_keys: + print("Processing", key) + key_data = lokalise.keys_list({"filter_keys": key}) + if len(key_data) != 1: + print( + f"Lookin up key in Lokalise returns {len(key_data)} results, expected 1" + ) + continue + to_delete.append(key_data[0]["key_id"]) + + print("Deleting keys:", ", ".join(map(str, to_delete))) + print(lokalise.keys_delete_multiple(to_delete)) + return 0 + + +if __name__ == "__main__": + sys.exit(run())