Fixes for BMO_error_raise extraction regex, and add hindi to "active" translations.

Also enhanced the ugly py ast parsing code, so that it now can handle (up to some extent) "name" function nodes, and add bpy.app.translations.pgettext func familly to extracted ones...

This ast py parsing becomes more and more ugly, should probably try to refactor it a bit. :/
This commit is contained in:
Bastien Montagne 2013-02-17 14:00:40 +00:00
parent c524b47912
commit 7cdff04f9f
2 changed files with 89 additions and 57 deletions

@ -435,41 +435,71 @@ def dump_py_messages_from_files(messages, check_ctxt, files):
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Gather function names # Gather function names
# so far only 'text' keywords, but we may want others translated later # In addition of UI func, also parse pgettext ones...
translate_kw = ("text", ) # Tuples of (module name, (short names, ...)).
pgettext_variants = (
("pgettext", ("_",)),
("pgettext_iface", ("iface_",)),
("pgettext_tip", ("tip_",))
)
pgettext_variants_args = {"msgid": (0, {"msgctxt": 1})}
# key: func_id # key: msgid keywords.
# val: [(arg_kw, arg_pos), (arg_kw, arg_pos), ...] # val: tuples of ((keywords,), context_getter_func) to get a context for that msgid.
# Note: order is important, first one wins!
translate_kw = {
"text": ((("text_ctxt",), _ctxt_to_ctxt),
(("operator",), _op_to_ctxt),
),
"msgid": ((("msgctxt",), _ctxt_to_ctxt),
),
}
context_kw_set = {}
for k, ctxts in translate_kw.items():
s = set()
for c, _ in ctxts:
s |= set(c)
context_kw_set[k] = s
# {func_id: {msgid: (arg_pos,
# {msgctxt: arg_pos,
# ...
# }
# ),
# ...
# },
# ...
# }
func_translate_args = {} func_translate_args = {}
# as we only have one translate keyword, no need for complex context extraction setup for now... # First, functions from UILayout
# And it's already enough complex like that! # First loop is for msgid args, second one is for msgctxt args.
# Note: order is important, first one wins! for func_id, func in bpy.types.UILayout.bl_rna.functions.items():
context_kw = ((("text_ctxt",), _ctxt_to_ctxt), # check it has one or more arguments as defined in translate_kw
(("operator",), _op_to_ctxt), for arg_pos, (arg_kw, arg) in enumerate(func.parameters.items()):
) if ((arg_kw in translate_kw) and (not arg.is_output) and (arg.type == 'STRING')):
context_kw_set = set() func_translate_args.setdefault(func_id, {})[arg_kw] = (arg_pos, {})
for c, _ in context_kw: for func_id, func in bpy.types.UILayout.bl_rna.functions.items():
context_kw_set |= set(c) if func_id not in func_translate_args:
continue
# Like func_translate_args. for arg_pos, (arg_kw, arg) in enumerate(func.parameters.items()):
func_context_args = {} if (not arg.is_output) and (arg.type == 'STRING'):
for msgid, msgctxts in context_kw_set.items():
if arg_kw in msgctxts:
func_translate_args[func_id][msgid][1][arg_kw] = arg_pos
# We manually add funcs from bpy.app.translations
for func_id, func_ids in pgettext_variants:
func_translate_args[func_id] = pgettext_variants_args
for func_id in func_ids:
func_translate_args[func_id] = pgettext_variants_args
#print(func_translate_args)
# Break recursive nodes look up on some kind of nodes. # Break recursive nodes look up on some kind of nodes.
# E.g. we dont want to get strings inside subscripts (blah["foo"])! # E.g. we dont want to get strings inside subscripts (blah["foo"])!
stopper_nodes = {ast.Subscript, } stopper_nodes = {ast.Subscript}
# Consider strings separate: ("a" if test else "b") # Consider strings separate: ("a" if test else "b")
separate_nodes = {ast.IfExp, } separate_nodes = {ast.IfExp}
# For now only consider functions from UILayout...
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 (not arg.is_output) and (arg.type == 'STRING')):
func_translate_args.setdefault(func_id, []).append((arg_kw, arg_pos))
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 check_ctxt_py = None
if check_ctxt: if check_ctxt:
@ -489,40 +519,42 @@ def dump_py_messages_from_files(messages, check_ctxt, files):
# print("found function at") # print("found function at")
# print("%s:%d" % (fp, node.lineno)) # print("%s:%d" % (fp, node.lineno))
# lambda's # We can't skip such situations! from blah import foo\nfoo("bar") would also be an ast.Name func!
if type(node.func) == ast.Name: if type(node.func) == ast.Name:
func_id = node.func.id
elif hasattr(node.func, "attr"):
func_id = node.func.attr
# Ugly things like getattr(self, con.type)(context, box, con)
else:
continue continue
# getattr(self, con.type)(context, box, con) func_args = func_translate_args.get(func_id, {})
if not hasattr(node.func, "attr"):
continue
# First try to get i18n context. # First try to get i18n contexts, for every possible msgid id.
context_args = func_context_args.get(node.func.attr, ()) contexts = dict.fromkeys(func_args.keys(), "")
context = "" for msgid, (_, context_args) in func_args.items():
context_elements = {} context_elements = {}
for arg_kw, arg_pos in context_args: for arg_kw, arg_pos in context_args.items():
if arg_pos < len(node.args): if arg_pos < len(node.args):
context_elements[arg_kw] = node.args[arg_pos] context_elements[arg_kw] = node.args[arg_pos]
else: else:
for kw in node.keywords: for kw in node.keywords:
if kw.arg == arg_kw: if kw.arg == arg_kw:
context_elements[arg_kw] = kw.value context_elements[arg_kw] = kw.value
break
#print(context_elements)
for kws, proc in translate_kw[msgid]:
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:
contexts[msgid] = ctxt
break 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) #print(translate_args)
# do nothing if not found # do nothing if not found
for arg_kw, arg_pos in translate_args: for arg_kw, (arg_pos, _) in func_args.items():
estr_lst = [(None, ())] estr_lst = [(None, ())]
if arg_pos < len(node.args): if arg_pos < len(node.args):
estr_lst = extract_strings_split(node.args[arg_pos]) estr_lst = extract_strings_split(node.args[arg_pos])
@ -535,7 +567,7 @@ def dump_py_messages_from_files(messages, check_ctxt, files):
#print(estr, nds) #print(estr, nds)
for estr, nds in estr_lst: for estr, nds in estr_lst:
if estr: if estr:
key = (context, estr) key = (contexts[arg_kw], estr)
if nds: if nds:
msgsrc = ["{}:{}".format(fp_rel, sorted({nd.lineno for nd in nds})[0])] msgsrc = ["{}:{}".format(fp_rel, sorted({nd.lineno for nd in nds})[0])]
else: else:

@ -94,7 +94,7 @@ LANGUAGES_FILE = "languages"
IMPORT_MIN_LEVEL = -1 IMPORT_MIN_LEVEL = -1
# Languages in /branches we do not want to import in /trunk currently... # Languages in /branches we do not want to import in /trunk currently...
IMPORT_LANGUAGES_SKIP = {'am', 'bg', 'fi', 'el', 'et', 'hi', 'ne', 'pl', 'ro', 'uz', 'uz@cyrillic'} IMPORT_LANGUAGES_SKIP = {'am', 'bg', 'fi', 'el', 'et', 'ne', 'pl', 'ro', 'uz', 'uz@cyrillic'}
# The comment prefix used in generated messages.txt file. # The comment prefix used in generated messages.txt file.
MSG_COMMENT_PREFIX = "#~ " MSG_COMMENT_PREFIX = "#~ "
@ -229,7 +229,7 @@ PYGETTEXT_KEYWORDS = (() +
tuple(("{}\\((?:[^\"',]+,){{1,2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) tuple(("{}\\((?:[^\"',]+,){{1,2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it)
for it in ("BKE_report", "BKE_reportf", "BKE_reports_prepend", "BKE_reports_prependf")) + for it in ("BKE_report", "BKE_reportf", "BKE_reports_prepend", "BKE_reports_prependf")) +
tuple(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*,").format(it) tuple(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*\)").format(it)
for it in ("BMO_error_raise",)) + for it in ("BMO_error_raise",)) +
tuple(("{}\\((?:[^\"',]+,)\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) tuple(("{}\\((?:[^\"',]+,)\\s*" + _msg_re + r"\s*(?:\)|,)").format(it)