Skip to content

Refactor langtool #1623

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

Merged
merged 12 commits into from
May 1, 2024
248 changes: 176 additions & 72 deletions dist/langtool.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,104 +1,208 @@
#!/usr/bin/env python3
from pathlib import Path
import sys
import argparse
import json

# This fixes a CJK full-width character input issue
# which makes left halves of deleted characters displayed on screen
# pylint: disable=unused-import
import re
import readline

DEFAULT_LANG = "en_US"
DEFAULT_LANG_PATH = "plugins/*/romfs/lang/"
INVALID_TRANSLATION = ""


def handle_missing_key(command, lang_data, key, value):
if command == "check":
print(f"Error: Translation {lang_data['code']} is missing translation for key '{key}'")
exit(2)
elif command == "translate" or command == "create":
print(f"Key \033[1m'{key}': '{value}'\033[0m is missing in translation '{lang_data['code']}'")
new_value = input("Enter translation: ")
lang_data["translations"][key] = new_value
elif command == "update":
lang_data["translations"][key] = INVALID_TRANSLATION


def main():
if len(sys.argv) < 3:
print(f"Usage: {Path(sys.argv[0]).name} <check|translate|update|create> <lang folder path> <language>")
return 1

command = sys.argv[1]
if command not in ["check", "translate", "update", "create"]:
print(f"Unknown command: {command}")
return 1

print(f"Using langtool in {command} mode")

lang_folder_path = Path(sys.argv[2])
if not lang_folder_path.exists():
print(f"Error: {lang_folder_path} does not exist")
return 1

if not lang_folder_path.is_dir():
print(f"Error: {lang_folder_path} is not a folder")
parser = argparse.ArgumentParser(
prog="langtool",
description="ImHex translate tool",
)
parser.add_argument(
"command",
choices=[
"check",
"translate",
"update",
"create",
"retranslate",
"untranslate",
"fmtzh",
],
)
parser.add_argument(
"-c", "--langdir", default=DEFAULT_LANG_PATH, help="Language folder glob"
)
parser.add_argument("-l", "--lang", default="", help="Language to translate")
parser.add_argument(
"-r", "--reflang", default="", help="Language for reference when translating"
)
parser.add_argument(
"-k", "--keys", help="Keys to re-translate (only in re/untranslate mode)"
)
args = parser.parse_args()

command = args.command
lang = args.lang

print(f"Running in {command} mode")
lang_files_glob = f"{lang}.json" if lang != "" else "*.json"

lang_folders = set(Path(".").glob(args.langdir))
if len(lang_folders) == 0:
print(f"Error: {args.langdir} matches nothing")
return 1

lang = sys.argv[3] if len(sys.argv) > 3 else ""

print(f"Processing language files in {lang_folder_path}...")

default_lang_file_path = lang_folder_path / Path(DEFAULT_LANG + ".json")
if not default_lang_file_path.exists():
print(f"Error: Default language file {default_lang_file_path} does not exist")
return 1

print(f"Using file '{default_lang_file_path.name}' as template language file")

with default_lang_file_path.open("r", encoding="utf-8") as default_lang_file:
default_lang_data = json.load(default_lang_file)
for lang_folder in lang_folders:
if not lang_folder.is_dir():
print(f"Error: {lang_folder} is not a folder")
return 1

default_lang_data = {}
default_lang_path = lang_folder / Path(DEFAULT_LANG + ".json")
if not default_lang_path.exists():
print(
f"Error: Default language file {default_lang_path} does not exist in {lang_folder}"
)
return 1
with default_lang_path.open("r", encoding="utf-8") as file:
default_lang_data = json.load(file)

reference_lang_data = None
reference_lang_path = lang_folder / Path(args.reflang + ".json")
if reference_lang_path.exists():
with reference_lang_path.open("r", encoding="utf-8") as file:
reference_lang_data = json.load(file)

if command == "create" and lang != "":
lang_file_path = lang_folder_path / Path(lang + ".json")
lang_file_path = lang_folder / Path(lang + ".json")
if lang_file_path.exists():
print(f"Error: Language file {lang_file_path} already exists")
return 1
continue

exist_lang_data = None
for lang_folder1 in lang_folders:
lang_file_path1 = lang_folder1 / Path(lang + ".json")
if lang_file_path1.exists():
with lang_file_path1.open("r", encoding="utf-8") as file:
exist_lang_data = json.load(file)
break

print(f"Creating new language file '{lang_file_path.name}'")
print(f"Creating new language file '{lang_file_path}'")

with lang_file_path.open("w", encoding="utf-8") as new_lang_file:
new_lang_data = {
"code": lang,
"language": input("Enter language: "),
"country": input("Enter country: "),
"translations": {}
"language": (
exist_lang_data["language"]
if exist_lang_data
else input("Enter language name: ")
),
"country": (
exist_lang_data["country"]
if exist_lang_data
else input("Enter country name: ")
),
"translations": {},
}
json.dump(new_lang_data, new_lang_file, indent=4, ensure_ascii=False)

for additional_lang_file_path in lang_folder_path.glob("*.json"):
if not lang == "" and not additional_lang_file_path.stem == lang:
lang_files = set(lang_folder.glob(lang_files_glob))
if len(lang_files) == 0:
print(f"Warn: Language file for '{lang}' does not exist in '{lang_folder}'")
for lang_file_path in lang_files:
if (
lang_file_path.stem == f"{DEFAULT_LANG}.json"
or lang_file_path.stem == f"{args.reflang}.json"
):
continue

if additional_lang_file_path.name.startswith(DEFAULT_LANG):
continue
print(f"\nProcessing '{lang_file_path}'")
if not (command == "update" or command == "create"):
print("\n----------------------------\n")

print(f"\nProcessing file '{additional_lang_file_path.name}'\n----------------------------\n")

with additional_lang_file_path.open("r+", encoding="utf-8") as additional_lang_file:
additional_lang_data = json.load(additional_lang_file)
with lang_file_path.open("r+", encoding="utf-8") as target_lang_file:
lang_data = json.load(target_lang_file)

for key, value in default_lang_data["translations"].items():
if key not in additional_lang_data["translations"] or additional_lang_data["translations"][key] == INVALID_TRANSLATION:
handle_missing_key(command, additional_lang_data, key, value)
has_translation = (
key in lang_data["translations"]
and lang_data["translations"][key] != INVALID_TRANSLATION
)
if not has_translation and not (
(command == "retranslate" or command == "untranslate")
and re.compile(args.keys).fullmatch(key)
):
continue
if command == "check":
print(
f"Error: Translation {lang_data['code']} is missing translation for key '{key}'"
)
exit(2)
elif (
command == "translate"
or command == "retranslate"
or command == "untranslate"
):
if command == "untranslate" and not has_translation:
continue
reference_tranlsation = (
" '%s'" % reference_lang_data["translations"][key]
if reference_lang_data
else ""
)
print(
f"\033[1m'{key}' '{value}'{reference_tranlsation}\033[0m => {lang_data['language']}",
end="",
)
if has_translation:
translation = lang_data["translations"][key]
print(f" <= \033[1m'{translation}'\033[0m")
print() # for a new line
if command == "untranslate":
lang_data["translations"][key] = INVALID_TRANSLATION
continue
try:
new_value = input("=> ")
lang_data["translations"][key] = new_value
except KeyboardInterrupt:
break
elif command == "update" or command == "create":
lang_data["translations"][key] = INVALID_TRANSLATION
elif command == "fmtzh":
if has_translation:
lang_data["translations"][key] = fmtzh(
lang_data["translations"][key]
)

keys_to_remove = []
for key, value in additional_lang_data["translations"].items():
for key, value in lang_data["translations"].items():
if key not in default_lang_data["translations"]:
keys_to_remove.append(key)

for key in keys_to_remove:
additional_lang_data["translations"].pop(key)
print(f"Removed unused key '{key}' from translation '{additional_lang_data['code']}'")

additional_lang_file.seek(0)
additional_lang_file.truncate()
json.dump(additional_lang_data, additional_lang_file, indent=4, sort_keys=True, ensure_ascii=False)


if __name__ == '__main__':
lang_data["translations"].pop(key)
print(
f"Removed unused key '{key}' from translation '{lang_data['code']}'"
)

target_lang_file.seek(0)
target_lang_file.truncate()
json.dump(
lang_data,
target_lang_file,
indent=4,
sort_keys=True,
ensure_ascii=False,
)


def fmtzh(text: str) -> str:
text = re.sub(r"(\.{3}|\.{6})", "……", text)
text = text.replace("!", "!")
text = re.sub(r"([^\.\na-zA-Z\d])\.$", "\1。", text, flags=re.M)
text = text.replace("?", "?")
return text


if __name__ == "__main__":
exit(main())
Loading
Loading