From a1af6e38920abb5cc3d214daceaab6d4b49b12df Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Fri, 16 Jun 2023 17:27:09 +0100 Subject: [PATCH] scripts: add langmover a tool to move strings between language file sections. Will be used to move login strings from admin into their own "login" file section. --- scripts/langmover/README.md | 33 +++++++++ scripts/langmover/langmover.py | 127 ++++++++++++++++++++++++++++++++ scripts/langmover/template.json | 15 ++++ 3 files changed, 175 insertions(+) create mode 100644 scripts/langmover/README.md create mode 100644 scripts/langmover/langmover.py create mode 100644 scripts/langmover/template.json diff --git a/scripts/langmover/README.md b/scripts/langmover/README.md new file mode 100644 index 0000000..d054580 --- /dev/null +++ b/scripts/langmover/README.md @@ -0,0 +1,33 @@ +# *langmover* + +* Makes moving strings between language files a little easier. + +# Usage + +You'll need to create a template file. See example `template.json`: +```json +{ + "meta": { + "explanation": "values here can either be folder, folder:section, or folder:section:subkey, and then either nothing, or /keyname. It all depends on whether the sections and keys match up, or if you want to pull a plural/singular only or not." + }, + "strings": { + "inviteInfiniteUsesWarning": "admin", // Resolves to admin/strings/inviteInfiniteUsesWarning + "emailAddress": "form:strings/emailAddress", // Resolves to form/strings/emailAddress + "modifySettingsFor": "admin:quantityStrings:plural/", // Resolves to admin/quantityStrings/modifySettingsFor/plural + "deleteNUsers": "admin:quantityStrings:singular/deleteNUsers" // Resolves to admin/quantityStrings/deleteNUsers/singular + }, + "quantityStrings": { + "reEnableUsers": "admin" // Resolves to admin/quantityStrings/reEnableUsers + } + +} +``` + + +Args: +* `--source`: Source `lang/` directory. **Always run on a copy, to avoid data loss** +* `--template`: Template JSON file. +* `--output`: Output directory. Will be filled with lang files (e.g. "en-us.json", "fa-ir.json", ...). +* `--extract`: Passing will remove the templated strings from their source file. **Modifies the source directory**. + + diff --git a/scripts/langmover/langmover.py b/scripts/langmover/langmover.py new file mode 100644 index 0000000..3e3ceef --- /dev/null +++ b/scripts/langmover/langmover.py @@ -0,0 +1,127 @@ +import json, argparse, os +from pathlib import Path + +ROOT = "en-us.json" + +# Tree structure: // +def generateTree(src: Path): + tree = {} + langs = {} + directories = [] + + def readLangFile(path: Path): + with open(path, 'r') as f: + content = json.load(f) + + return content + + + for directory in os.scandir(src): + if not directory.is_dir(): continue + directories.append(directory.name) + + # tree[directory.name] = {} + + for lang in os.scandir(directory.path): + if not lang.is_file(): continue + if not ".json" in lang.name: continue + if lang.name not in langs: + langs[lang.name] = True + if lang.name.lower() not in tree: + tree[lang.name.lower()] = {} + + for lang in langs: + for directory in directories: + filepath = Path(src) / Path(directory) / Path(lang) + if not filepath.exists(): continue + tree[lang.lower()][directory] = readLangFile(filepath) + + return tree + +def parseKey(langTree, currentSection: str, fieldName: str, key: str, extract=False): + temp = key.split("/") + loc = temp[0] + k = "" + if len(temp) > 1: + k = temp[1] + + sections = loc.split(":") + + # folder, folder:section or folder:section:subkey + folder = sections[0] + section = currentSection + subkey = None + if len(sections) > 1: + section = sections[1] + if len(sections) > 2: + subkey = sections[2] + + + if k == '': + k = fieldName + + value = "" + if folder in langTree and section in langTree[folder] and k in langTree[folder][section]: + value = langTree[folder][section][k] + if extract: + s = langTree[folder][section] + del s[k] + langTree[folder][section] = s + + if subkey is not None and subkey in value: + value = value[subkey] + + return (langTree, folder, value) + + +def generate(templ: Path, source: Path, output: Path, extract: bool, tree): + with open(templ, "r") as f: + template = json.load(f) + + if not output.exists(): + output.mkdir() + + for lang in tree: + out = {} + for section in template: + if section == "meta": + # grab a meta section from the first file we find + for file in tree[lang]: + out["meta"] = tree[lang][file]["meta"] + break + + continue + + modifiedTree = {} + folder = "" + out[section] = {} + for key in template[section]: + (modifiedTree, folder, val) = parseKey(tree[lang], section, key, template[section][key], extract) + if val != "": + out[section][key] = val + + if extract and val != "": + with open(source / folder / lang, "w") as f: + json.dump(modifiedTree[folder], f, indent=4) + + with open(output / Path(lang), "w") as f: + json.dump(out, f, indent=4) + + + +parser = argparse.ArgumentParser() + +parser.add_argument("--source", help="source \"lang/\" folder.") +parser.add_argument("--template", help="template file. see template.json for an example of how it works.") +parser.add_argument("--output", help="output directory for new files.") +parser.add_argument("--extract", help="remove strings from original file as they are copied.", action="store_true") + +args = parser.parse_args() + +source = Path(args.source) + +tree = generateTree(source) + +generate(Path(args.template), source, Path(args.output), args.extract, tree) + +# print(json.dumps(tree, sort_keys=True, indent=4)) diff --git a/scripts/langmover/template.json b/scripts/langmover/template.json new file mode 100644 index 0000000..b72e85c --- /dev/null +++ b/scripts/langmover/template.json @@ -0,0 +1,15 @@ +{ + "meta": { + "explanation": "values here can either be folder, folder:section, or folder:section:subkey, and then either nothing, or /keyname. It all depends on whether the sections and keys match up, or if you want to pull a plural/singular only or not." + }, + "strings": { + "inviteInfiniteUsesWarning": "admin", + "emailAddress": "form:strings/emailAddress", + "modifySettingsFor": "admin:quantityStrings:plural/", + "deleteNUsers": "admin:quantityStrings:singular/deleteNUsers" + }, + "quantityStrings": { + "reEnableUsers": "admin" + } + +}