Fix mod translation updater bugs (#13974)

This commit is contained in:
Wuzzy 2023-11-19 20:46:52 +01:00 committed by GitHub
parent 31ee7af3ab
commit 61db32beee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

51
util/mod_translation_updater.py Normal file → Executable file

@ -8,7 +8,6 @@
# 2023 Wuzzy. # 2023 Wuzzy.
# License: LGPLv2.1 or later (see LICENSE file for details) # License: LGPLv2.1 or later (see LICENSE file for details)
from __future__ import print_function
import os, fnmatch, re, shutil, errno import os, fnmatch, re, shutil, errno
from sys import argv as _argv from sys import argv as _argv
from sys import stderr as _stderr from sys import stderr as _stderr
@ -121,17 +120,43 @@ def main():
# Group 2 will be the string, groups 1 and 3 will be the delimiters (" or ') # Group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
# See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote # See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua_quoted = re.compile(r'[\.=^\t,{\(\s]N?F?S\s*\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL) pattern_lua_quoted = re.compile(
r'(?:^|[\.=,{\(\s])' # Look for beginning of file or anything that isn't a function identifier
r'N?F?S\s*\(\s*' # Matches S, FS, NS or NFS function call
r'(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)' # Quoted string
r'[\s,\)]', # End of call or argument
re.DOTALL)
# Handles the [[ ... ]] string delimiters # Handles the [[ ... ]] string delimiters
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?F?S\s*\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL) pattern_lua_bracketed = re.compile(
r'(?:^|[\.=,{\(\s])' # Same as for pattern_lua_quoted
r'N?F?S\s*\(\s*' # Same as for pattern_lua_quoted
r'\[\[(.*?)\]\]' # [[ ... ]] string delimiters
r'[\s,\)]', # Same as for pattern_lua_quoted
re.DOTALL)
# Handles "concatenation" .. " of strings" # Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL) pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.*?[^@])=(.*)') # Handles a translation line in *.tr file.
# Group 1 is the source string left of the equals sign.
# Group 2 is the translated string, right of the equals sign.
pattern_tr = re.compile(
r'(.*)' # Source string
# the separating equals sign, if NOT preceded by @, unless
# that @ is preceded by another @
r'(?:(?<!(?<!@)@)=)'
r'(.*)' # Translation string
)
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)') pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$') pattern_tr_filename = re.compile(r'\.tr$')
# Matches bad use of @ signs in Lua string
pattern_bad_luastring = re.compile(
r'^@$|' # single @, OR
r'[^@]@$|' # trailing unescaped @, OR
r'(?<!@)@(?=[^@1-9])' # an @ that is not escaped or part of a placeholder
)
# Attempt to read the mod's name from the mod.conf file or folder name. 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:
@ -263,8 +288,10 @@ def read_lua_file_strings(lua_file):
strings.append(s) strings.append(s)
for s in strings: for s in strings:
s = re.sub(r'"\.\.\s+"', "", s) found_bad = pattern_bad_luastring.search(s)
s = re.sub("@[^@=0-9]", "@@", s) if found_bad:
print("SYNTAX ERROR: Unescaped '@' in Lua string: " + s)
continue
s = s.replace('\\"', '"') s = s.replace('\\"', '"')
s = s.replace("\\'", "'") s = s.replace("\\'", "'")
s = s.replace("\n", "@n") s = s.replace("\n", "@n")
@ -304,11 +331,14 @@ def import_tr_file(tr_file):
if header_comments != None: if header_comments != None:
in_header = False in_header = False
continue continue
# comment lines # Comment lines
elif line.startswith("#"): elif line.startswith("#"):
# source file comments: ##[ file.lua ] ## # Source file comments: ##[ file.lua ]##
if line.startswith(symbol_source_prefix) and line.endswith(symbol_source_suffix): if line.startswith(symbol_source_prefix) and line.endswith(symbol_source_suffix):
# remove those comments; they may be added back automatically # This line marks the end of header comments.
if params["print-source"]:
in_header = False
# Remove those comments; they may be added back automatically.
continue continue
# Store first occurance of textdomain # Store first occurance of textdomain
@ -343,8 +373,7 @@ def import_tr_file(tr_file):
# if there was a comment, record that. # if there was a comment, record that.
outval["comment"] = latest_comment_block outval["comment"] = latest_comment_block
latest_comment_block = None latest_comment_block = None
if header_comments != None: in_header = False
in_header = False
dOut[match.group(1)] = outval dOut[match.group(1)] = outval
return (dOut, text, header_comments, textdomain) return (dOut, text, header_comments, textdomain)