Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add script to clean up translations #33802

Merged
merged 1 commit into from
Apr 8, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions script/translations_clean.py
Original file line number Diff line number Diff line change
@@ -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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have find_extra return the missing_keys to avoid side effects? It does add some complexity due to calling itself.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means that we keep copying lists around. I prefer to keep it like this. Generally for recursion one would have 1 "main" function that creates a list and a second recursion function that just operates on that list. Here I skipped the main function.

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())