Newly generated

master
Joachim Stolberg 2022-04-26 18:50:02 +02:00
parent e2d0247f7c
commit ce69568172
1 changed files with 78 additions and 52 deletions

120
i18n.py
View File

@ -21,20 +21,28 @@ params = {"recursive": False,
"mods": False, "mods": False,
"verbose": False, "verbose": False,
"folders": [], "folders": [],
"no-old-file": False "no-old-file": False,
"break-long-lines": False,
"sort": False,
"print-source": False,
"truncate-unused": False,
} }
# Available CLI options # Available CLI options
options = {"recursive": ['--recursive', '-r'], options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'], "help": ['--help', '-h'],
"mods": ['--installed-mods'], "mods": ['--installed-mods', '-m'],
"verbose": ['--verbose', '-v'], "verbose": ['--verbose', '-v'],
"no-old-file": ['--no-old-file'] "no-old-file": ['--no-old-file', '-O'],
"break-long-lines": ['--break-long-lines', '-b'],
"sort": ['--sort', '-s'],
"print-source": ['--print-source', '-p'],
"truncate-unused": ['--truncate-unused', '-t'],
} }
# Strings longer than this will have extra space added between # Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their # them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance # beginnings and endings at a glance
doublespace_threshold = 60 doublespace_threshold = 80
def set_params_folders(tab: list): def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.''' '''Initialize params["folders"] from CLI arguments.'''
@ -69,8 +77,16 @@ DESCRIPTION
run on locally installed modules run on locally installed modules
{', '.join(options["no-old-file"])} {', '.join(options["no-old-file"])}
do not create *.old files do not create *.old files
{', '.join(options["sort"])}
sort output strings alphabetically
{', '.join(options["break-long-lines"])}
add extra line breaks before and after long strings
{', '.join(options["print-source"])}
add comments denoting the source file
{', '.join(options["verbose"])} {', '.join(options["verbose"])}
add output information add output information
{', '.join(options["truncate-unused"])}
delete unused strings from files
''') ''')
@ -89,8 +105,8 @@ def main():
print("recursively ", end='') print("recursively ", end='')
# Running # Running
if params["mods"]: if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}") print(f"on all locally installed modules in {os.path.expanduser('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods") run_all_subfolders(os.path.expanduser("~/.minetest/mods"))
elif len(params["folders"]) >= 2: elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"]) print("on folder list:", params["folders"])
for f in params["folders"]: for f in params["folders"]:
@ -126,7 +142,7 @@ pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$') pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$') pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure #attempt to read the mod's name from the mod.conf file or folder name. Returns None on failure
def get_modname(folder): def get_modname(folder):
try: try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf: with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
@ -135,7 +151,15 @@ def get_modname(folder):
if match: if match:
return match.group(1) return match.group(1)
except FileNotFoundError: except FileNotFoundError:
pass if not os.path.isfile(os.path.join(folder, "modpack.txt")):
folder_name = os.path.basename(folder)
# Special case when run in Minetest's builtin directory
if folder_name == "builtin":
return "__builtin"
else:
return folder_name
else:
return None
return None return None
#If there are already .tr files in /locale, returns a list of their names #If there are already .tr files in /locale, returns a list of their names
@ -183,7 +207,7 @@ def process_po_files(folder, modname):
if code_match == None: if code_match == None:
continue continue
language_code = code_match.group(1) language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr" tr_name = f'{modname}.{language_code}.tr'
tr_file = os.path.join(root, tr_name) tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file): if os.path.exists(tr_file):
if params["verbose"]: if params["verbose"]:
@ -213,7 +237,7 @@ def mkdir_p(path):
# dOld is a dictionary of existing translations and comments from # dOld is a dictionary of existing translations and comments from
# the previous version of this text # the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name, header_comments): def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
lOut = [f"# textdomain: {mod_name}\n"] lOut = [f"# textdomain: {mod_name}"]
if header_comments is not None: if header_comments is not None:
lOut.append(header_comments) lOut.append(header_comments)
@ -221,7 +245,8 @@ def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
for key in dkeyStrings: for key in dkeyStrings:
sourceList = list(dkeyStrings[key]) sourceList = list(dkeyStrings[key])
sourceList.sort() if params["sort"]:
sourceList.sort()
sourceString = "\n".join(sourceList) sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, []) listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key) listForSource.append(key)
@ -231,42 +256,45 @@ def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
lSourceKeys.sort() lSourceKeys.sort()
for source in lSourceKeys: for source in lSourceKeys:
localizedStrings = dGroupedBySource[source] localizedStrings = dGroupedBySource[source]
localizedStrings.sort() if params["sort"]:
lOut.append("") localizedStrings.sort()
lOut.append(source) if params["print-source"]:
lOut.append("") if lOut[-1] != "":
lOut.append("")
lOut.append(source)
for localizedString in localizedStrings: for localizedString in localizedStrings:
val = dOld.get(localizedString, {}) val = dOld.get(localizedString, {})
translation = val.get("translation", "") translation = val.get("translation", "")
comment = val.get("comment") comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "": if params["break-long-lines"] and len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("") lOut.append("")
if comment != None: if comment != None and comment != "" and not comment.startswith("# textdomain:"):
lOut.append(comment) lOut.append(comment)
lOut.append(f"{localizedString}={translation}") lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold: if params["break-long-lines"] and len(localizedString) > doublespace_threshold:
lOut.append("") lOut.append("")
unusedExist = False unusedExist = False
for key in dOld: if not params["truncate-unused"]:
if key not in dkeyStrings: for key in dOld:
val = dOld[key] if key not in dkeyStrings:
translation = val.get("translation") val = dOld[key]
comment = val.get("comment") translation = val.get("translation")
# only keep an unused translation if there was translated comment = val.get("comment")
# text or a comment associated with it # only keep an unused translation if there was translated
if translation != None and (translation != "" or comment): # text or a comment associated with it
if not unusedExist: if translation != None and (translation != "" or comment):
unusedExist = True if not unusedExist:
lOut.append("\n\n##### not used anymore #####\n") unusedExist = True
if len(key) > doublespace_threshold and not lOut[-1] == "": lOut.append("\n\n##### not used anymore #####\n")
lOut.append("") if params["break-long-lines"] and len(key) > doublespace_threshold and not lOut[-1] == "":
if comment != None: lOut.append("")
lOut.append(comment) if comment != None:
lOut.append(f"{key}={translation}") lOut.append(comment)
if len(key) > doublespace_threshold: lOut.append(f"{key}={translation}")
lOut.append("") if params["break-long-lines"] and len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n' return "\n".join(lOut) + '\n'
# Writes a template.txt file # Writes a template.txt file
@ -332,11 +360,11 @@ def import_tr_file(tr_file):
latest_comment_block = None latest_comment_block = None
for line in existing_file.readlines(): for line in existing_file.readlines():
line = line.rstrip('\n') line = line.rstrip('\n')
if line[:3] == "###": if line.startswith("###"):
if header_comment is None: if header_comment is None and not latest_comment_block is None:
# Save header comments # Save header comments
header_comment = latest_comment_block header_comment = latest_comment_block
# Stip textdomain line # Strip textdomain line
tmp_h_c = "" tmp_h_c = ""
for l in header_comment.split('\n'): for l in header_comment.split('\n'):
if not l.startswith("# textdomain:"): if not l.startswith("# textdomain:"):
@ -346,7 +374,7 @@ def import_tr_file(tr_file):
# Reset comment block if we hit a header # Reset comment block if we hit a header
latest_comment_block = None latest_comment_block = None
continue continue
if line[:1] == "#": elif line.startswith("#"):
# Save the comment we're inside # Save the comment we're inside
if not latest_comment_block: if not latest_comment_block:
latest_comment_block = line latest_comment_block = line
@ -414,7 +442,6 @@ def update_tr_file(dNew, mod_name, tr_file):
# Updates translation files for the mod in the given folder # Updates translation files for the mod in the given folder
def update_mod(folder): def update_mod(folder):
print(folder)
modname = get_modname(folder) modname = get_modname(folder)
if modname is not None: if modname is not None:
process_po_files(folder, modname) process_po_files(folder, modname)
@ -427,24 +454,23 @@ def update_mod(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file)) update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else: else:
print(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr) print(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr)
#exit(1) exit(1)
# Determines if the folder being pointed to is a mod or a mod pack # Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly # and then runs update_mod accordingly
def update_folder(folder): def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf")) is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack: if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()] subfolders = [f.path for f in os.scandir(folder) if f.is_dir() and not f.name.startswith('.')]
for subfolder in subfolders: for subfolder in subfolders:
update_mod(subfolder + "/") update_mod(subfolder)
else: else:
update_mod(folder) update_mod(folder)
print("Done.") print("Done.")
def run_all_subfolders(folder): def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]: for modfolder in [f.path for f in os.scandir(folder) if f.is_dir() and not f.name.startswith('.')]:
update_folder(modfolder + "/") update_folder(modfolder)
main() main()