From 44489a480f124dd3254432be4678fa3f7584bd0c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 17 Nov 2020 14:01:44 +1000 Subject: [PATCH] map gettext strings to keys/modules for ftl conversion --- pylib/anki/consts.py | 8 - qt/po/scripts/extract-po-strings.py | 251 +++++++++++++++++++++++++++- 2 files changed, 250 insertions(+), 9 deletions(-) diff --git a/pylib/anki/consts.py b/pylib/anki/consts.py index 2ae53f01f..d96529f1e 100644 --- a/pylib/anki/consts.py +++ b/pylib/anki/consts.py @@ -103,14 +103,6 @@ def newCardSchedulingLabels() -> Dict[int, Any]: } -def alignmentLabels() -> Dict[int, Any]: - return { - 0: _("Center"), - 1: _("Left"), - 2: _("Right"), - } - - def dynOrderLabels() -> Dict[int, Any]: return { 0: _("Oldest seen first"), diff --git a/qt/po/scripts/extract-po-strings.py b/qt/po/scripts/extract-po-strings.py index 40e779ba0..c779c7e9d 100644 --- a/qt/po/scripts/extract-po-strings.py +++ b/qt/po/scripts/extract-po-strings.py @@ -4,6 +4,8 @@ import os import json import polib +import pprint +import re # Read strings from all .po and .pot files and store them in a JSON file # for quick access. @@ -30,20 +32,251 @@ def get_msgstr(entry): return adjusted[0] return None + +module_map = { + "__init__": "qt-misc", + "about": "about", + "addcards": "adding", + "addfield": "fields", + "addmodel": "notetypes", + "addonconf": "addons", + "addons": "addons", + "anki2": "importing", + "browser": "browsing", + "browserdisp": "browsing", + "browseropts": "browsing", + "changemap": "browsing", + "changemodel": "browsing", + "clayout_top": "card-templates", + "clayout": "card-templates", + "collection": "collection", + "consts": "consts", + "csvfile": "importing", + "customstudy": "custom-study", + "dconf": "scheduling", + "debug": "qt-misc", + "deckbrowser": "decks", + "deckchooser": "qt-misc", + "deckconf": "scheduling", + "decks": "decks", + "dyndconf": "decks", + "dyndeckconf": "decks", + "editaddon": "addons", + "editcurrent": "editing", + "edithtml": "editing", + "editor": "editing", + "errors": "qt-misc", + "exporting": "exporting", + "fields": "fields", + "finddupes": "browsing", + "findreplace": "browsing", + "getaddons": "addons", + "importing": "importing", + "latex": "media", + "main": "qt-misc", + "mnemo": "importing", + "modelchooser": "qt-misc", + "modelopts": "notetypes", + "models": "notetypes", + "noteimp": "importing", + "overview": "studying", + "preferences": "preferences", + "previewer": "qt-misc", + "profiles": "profiles", + "progress": "qt-misc", + "reposition": "browsing", + "reschedule": "browsing", + "reviewer": "studying", + "schedv2": "scheduling", + "setgroup": "browsing", + "setlang": "preferences", + "sidebar": "browsing", + "sound": "media", + "studydeck": "decks", + "taglimit": "custom-study", + "template": "card-templates", + "toolbar": "qt-misc", + "update": "qt-misc", + "utils": "qt-misc", + "webview": "qt-misc", + "stats": "stats", +} + +text_remap = { + "actions": [ + "Add", + "Cancel", + "Choose", + "Close", + "Copy", + "Decks", + "Delete", + "Export", + "Filter", + "Help", + "Import", + "Manage...", + "Name:", + "New name:", + "New", + "Options for %s", + "Options", + "Preview", + "Rebuild", + "Rename Deck", + "Rename", + "Replay Audio", + "Reposition", + "Save", + "Search", + "Shortcut key: %s", + "Suspend Card", + "Blue Flag", + "Green Flag", + "Orange Flag", + "Red Flag", + "Custom Study", + ], + "decks": [ + "Deck", + "New deck name:", + "Decreasing intervals", + "Increasing intervals", + "Latest added first", + "Most lapses", + "Oldest seen first", + "Order added", + "Order due", + "Random", + "Relative overdueness", + ], + "scheduling": [ + "days", + "Lapses", + "Reviews", + "At least one step is required.", + "Steps must be numbers.", + "Show new cards in order added", + "Show new cards in random order", + "Show new cards after reviews", + "Show new cards before reviews", + "Mix new cards and reviews", + "Learning", + "Review", + ], + "fields": ["Add Field"], + "editing": ["Tags", "Cards", "Fields", "LaTeX"], + "notetypes": ["Type", "Note Types"], + "studying": ["Space"], + "qt-misc": ["&Edit", "&Guide...", "&Help", "&Undo", "Unexpected response code: %s"], + "adding": ["Added"], +} + +blacklist = {"Anki", "%", "Dialog", "Center", "Left", "Right", "~", "&Cram..."} + + +def determine_module(text, files): + if text in blacklist: + return None + + if "&" in text: + return "qt-accel" + + for (module, texts) in text_remap.items(): + if text in texts: + return module + + if len(files) == 1: + return list(files)[0] + + assert False + + +modules = dict() + +remap_keys = {"browsing-": "browsing-type-here-to-search"} + + +def generate_key(module: str, text: str) -> str: + key = re.sub("<.*?>", "", text) + key = re.sub("%[dsf.0-9]+", "", key) + key = key.replace("+", "and") + key = re.sub("[^a-z0-9 ]", "", key.lower()) + words = key.split(" ") + if len(words) > 6: + words = words[:6] + key = "-".join(words) + key = re.sub("--+", "-", key) + key = re.sub("-$|^-", "", key) + + key = f"{module}-{key}" + + if key in remap_keys: + key = remap_keys[key] + + return key + + +seen_keys = set() + + +def migrate_entry(entry): + if entry.msgid_plural: + # print("skip plural", entry.msgid) + return + + text = entry.msgid + files = set( + [os.path.splitext(os.path.basename(e[0]))[0] for e in entry.occurrences] + ) + # drop translations only used by old graphs + if len(files) == 1 and "stats" in files: + return None + + files2 = set() + for file in files: + if file == "stats": + continue + file = module_map[file] + files2.add(file) + module = determine_module(text, files2) + if not module: + return + + key = generate_key(module, text) + + if key in seen_keys: + key += "2" + assert key not in seen_keys + seen_keys.add(key) + + modules.setdefault(module, []) + modules[module].append((key, text)) + + return None + + langs = {} # .pot first -base = "repo/desktop" +base = "../../../anki-i18n/qtpo/desktop" pot = os.path.join(base, "anki.pot") pot_cat = polib.pofile(pot) +migration_map = [] + for entry in pot_cat: if entry.msgid_plural: msgstr = [entry.msgid, entry.msgid_plural] else: msgstr = entry.msgid + langs.setdefault("en", {})[entry.msgid] = msgstr + if d := migrate_entry(entry): + migration_map.append(d) + + # then .po files folders = (d for d in os.listdir(base) if d != "anki.pot") for lang in folders: @@ -58,3 +291,19 @@ for lang in folders: with open("strings.json", "w") as file: file.write(json.dumps(langs)) print("wrote to strings.json") + +# old text -> (module, new key) +strings_by_module = {} +keys_by_text = {} +for (module, items) in modules.items(): + items.sort() + strings_by_module[module] = items + for item in items: + (key, text) = item + assert text not in keys_by_text + keys_by_text[text] = (module, key) + +with open("strings_by_module.json", "w") as file: + file.write(json.dumps(strings_by_module)) +with open("keys_by_text.json", "w") as file: + file.write(json.dumps(keys_by_text))