Updated py ui message extraction, to find out contexts as much as possible...
authorBastien Montagne <montagne29@wanadoo.fr>
Sat, 9 Feb 2013 18:32:00 +0000 (18:32 +0000)
committerBastien Montagne <montagne29@wanadoo.fr>
Sat, 9 Feb 2013 18:32:00 +0000 (18:32 +0000)
release/scripts/modules/bl_i18n_utils/bl_process_msg.py

index 5d2f90f..6307ca2 100644 (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 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, ())
+                # 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):
+                        context_elements[arg_kw] = node.args[arg_pos]
+                    else:
+                        for kw in node.keywords:
+                            if kw.arg == arg_kw:
+                                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):
-                        extract_strings(fp_rel, node.args[arg_pos])
+                        estr, nds = extract_strings(node.args[arg_pos])
+                        #print(estr, nds)
                     else:
                         for kw in node.keywords:
                             if kw.arg == arg_kw:
-                                extract_strings(fp_rel, kw.value)
+                                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):