Updated py ui message extraction, to find out contexts as much as possible...
This commit is contained in:
@@ -358,15 +358,68 @@ def dump_py_messages_from_files(messages, check_ctxt, files):
|
||||
"""
|
||||
import ast
|
||||
|
||||
bpy_struct = bpy.types.ID.__base__
|
||||
|
||||
# Helper function
|
||||
def extract_strings(node):
|
||||
"""
|
||||
Recursively get strings, needed in case we have "Blah" + "Blah", passed as an argument in that case it won't
|
||||
evaluate to a string. However, break on some kind of stopper nodes, like e.g. Subscript.
|
||||
"""
|
||||
if type(node) == ast.Str:
|
||||
eval_str = ast.literal_eval(node)
|
||||
if eval_str:
|
||||
return eval_str, (node,)
|
||||
return None, ()
|
||||
|
||||
eval_str = []
|
||||
nodes = []
|
||||
for nd in ast.iter_child_nodes(node):
|
||||
if type(nd) not in stopper_nodes:
|
||||
estr, nds = extract_strings(nd)
|
||||
eval_str.append(estr)
|
||||
nodes += nds
|
||||
if eval_str:
|
||||
return "".join(s for s in eval_str if s is not None), tuple(n for n in nodes if n is not None)
|
||||
return None, ()
|
||||
|
||||
def _ctxt_to_ctxt(node):
|
||||
return extract_strings(node)[0]
|
||||
|
||||
def _op_to_ctxt(node):
|
||||
opname, _ = extract_strings(node)
|
||||
if not opname:
|
||||
return ""
|
||||
op = bpy.ops
|
||||
for n in opname.split('.'):
|
||||
op = getattr(op, n)
|
||||
try:
|
||||
return op.get_rna().bl_rna.translation_context
|
||||
except Exception as e:
|
||||
print("ERROR: ", str(e))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Gather function names
|
||||
|
||||
# so far only 'text' keywords, but we may want others translated later
|
||||
translate_kw = ("text", )
|
||||
|
||||
# key: func_id
|
||||
# val: [(arg_kw, arg_pos), (arg_kw, arg_pos), ...]
|
||||
func_translate_args = {}
|
||||
|
||||
# so far only 'text' keywords, but we may want others translated later
|
||||
translate_kw = ("text", )
|
||||
# as we only have one translate keyword, no need for complex context extraction setup for now...
|
||||
# And it's already enough complex like that!
|
||||
# Note: order is important, first one wins!
|
||||
context_kw = ((("text_ctxt",), _ctxt_to_ctxt),
|
||||
(("operator",), _op_to_ctxt),
|
||||
)
|
||||
context_kw_set = set()
|
||||
for c, _ in context_kw:
|
||||
context_kw_set |= set(c)
|
||||
|
||||
# Like func_translate_args.
|
||||
func_context_args = {}
|
||||
|
||||
# Break recursive nodes look up on some kind of nodes.
|
||||
# E.g. we don’t want to get strings inside subscripts (blah["foo"])!
|
||||
@@ -376,9 +429,11 @@ def dump_py_messages_from_files(messages, check_ctxt, files):
|
||||
for func_id, func in bpy.types.UILayout.bl_rna.functions.items():
|
||||
# check it has one or more arguments as defined in translate_kw
|
||||
for (arg_pos, (arg_kw, arg)) in enumerate(func.parameters.items()):
|
||||
if ((arg_kw in translate_kw) and (arg.is_output is False) and (arg.type == 'STRING')):
|
||||
if ((arg_kw in translate_kw) and (not arg.is_output) and (arg.type == 'STRING')):
|
||||
func_translate_args.setdefault(func_id, []).append((arg_kw, arg_pos))
|
||||
# print(func_translate_args)
|
||||
elif ((arg_kw in context_kw_set) and (not arg.is_output) and (arg.type == 'STRING')):
|
||||
func_context_args.setdefault(func_id, []).append((arg_kw, arg_pos))
|
||||
#print(func_context_args)
|
||||
|
||||
check_ctxt_py = None
|
||||
if check_ctxt:
|
||||
@@ -387,32 +442,6 @@ def dump_py_messages_from_files(messages, check_ctxt, files):
|
||||
"not_capitalized": check_ctxt["not_capitalized"],
|
||||
"end_point": check_ctxt["end_point"]}
|
||||
|
||||
# Helper function
|
||||
def extract_strings(fp_rel, node):
|
||||
"""
|
||||
Recursively get strings, needed in case we have "Blah" + "Blah", passed as an argument in that case it won't
|
||||
evaluate to a string. However, break on some kind of stopper nodes, like e.g. Subscript.
|
||||
"""
|
||||
if type(node) == ast.Str:
|
||||
eval_str = ast.literal_eval(node)
|
||||
if eval_str:
|
||||
# Parse optional context included in string!
|
||||
# XXX Not yet!
|
||||
#if bpy.app.i18n.context_sep in eval_str:
|
||||
#key = eval_str.split(bpy.app.i18n.context_sep, 1)
|
||||
if 0:
|
||||
pass
|
||||
else:
|
||||
key = (CONTEXT_DEFAULT, eval_str)
|
||||
msgsrc = "{}:{}".format(fp_rel, node.lineno)
|
||||
check(check_ctxt_py, messages, key, msgsrc)
|
||||
messages.setdefault(key, []).append(msgsrc)
|
||||
return
|
||||
|
||||
for nd in ast.iter_child_nodes(node):
|
||||
if type(nd) not in stopper_nodes:
|
||||
extract_strings(fp_rel, nd)
|
||||
|
||||
for fp in files:
|
||||
with open(fp, 'r', encoding="utf8") as filedata:
|
||||
root_node = ast.parse(filedata.read(), fp, 'exec')
|
||||
@@ -432,16 +461,50 @@ def dump_py_messages_from_files(messages, check_ctxt, files):
|
||||
if not hasattr(node.func, "attr"):
|
||||
continue
|
||||
|
||||
translate_args = func_translate_args.get(node.func.attr, ())
|
||||
|
||||
# do nothing if not found
|
||||
for arg_kw, arg_pos in translate_args:
|
||||
# First try to get i18n context.
|
||||
context_args = func_context_args.get(node.func.attr, ())
|
||||
context = ""
|
||||
context_elements = {}
|
||||
for arg_kw, arg_pos in context_args:
|
||||
if arg_pos < len(node.args):
|
||||
extract_strings(fp_rel, node.args[arg_pos])
|
||||
context_elements[arg_kw] = node.args[arg_pos]
|
||||
else:
|
||||
for kw in node.keywords:
|
||||
if kw.arg == arg_kw:
|
||||
extract_strings(fp_rel, kw.value)
|
||||
context_elements[arg_kw] = kw.value
|
||||
break
|
||||
#print(context_elements)
|
||||
for kws, proc in context_kw:
|
||||
if set(kws) <= context_elements.keys():
|
||||
args = tuple(context_elements[k] for k in kws)
|
||||
#print("running ", proc, " with ", args)
|
||||
ctxt = proc(*args)
|
||||
if ctxt:
|
||||
context = ctxt
|
||||
break
|
||||
|
||||
translate_args = func_translate_args.get(node.func.attr, ())
|
||||
#print(translate_args)
|
||||
# do nothing if not found
|
||||
for arg_kw, arg_pos in translate_args:
|
||||
estr, nds = None, ()
|
||||
if arg_pos < len(node.args):
|
||||
estr, nds = extract_strings(node.args[arg_pos])
|
||||
#print(estr, nds)
|
||||
else:
|
||||
for kw in node.keywords:
|
||||
if kw.arg == arg_kw:
|
||||
estr, nds = extract_strings(kw.value)
|
||||
break
|
||||
#print(estr, nds)
|
||||
if estr:
|
||||
key = (context, estr)
|
||||
if nds:
|
||||
msgsrc = ["{}:{}".format(fp_rel, sorted({nd.lineno for nd in nds})[0])]
|
||||
else:
|
||||
msgsrc = ["{}:???".format(fp_rel)]
|
||||
check(check_ctxt_py, messages, key, msgsrc)
|
||||
messages.setdefault(key, []).extend(msgsrc)
|
||||
|
||||
|
||||
def dump_py_messages(messages, check_ctxt, addons):
|
||||
|
||||
Reference in New Issue
Block a user