Updated py ui message extraction, to find out contexts as much as possible...

This commit is contained in:
2013-02-09 18:32:00 +00:00
parent 82c86af7f7
commit e14b21dd6d

View File

@@ -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 dont 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):