[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [3600] trunk/py/scripts/addons: UI translation from inside Blender UI: second part.

Bastien Montagne montagne29 at wanadoo.fr
Mon Jul 9 16:32:43 CEST 2012


Revision: 3600
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=3600
Author:   mont29
Date:     2012-07-09 14:32:43 +0000 (Mon, 09 Jul 2012)
Log Message:
-----------
UI translation from inside Blender UI: second part.

This is the py addon to use for embeded Blender ui translation. It can edit various UI elements' messages and tips, save the changes to the relevant po, "compile" a new mo placed into user's datafiles dir, and erase that same files (to get back "official" translations).

Notes:
* Still work in progress. Most likely some remaning bugs (even though it works quite well for me)...
* All UI elements in Blender aren't translatable this way (e.g. panel labels just don't react to right mouse clicks currently...)
* RTL languages won't work as well as others, still have to implement some kind of revert-RTL process...
* Having a nicer way than editing user_settings.py in modules/bl_i18n_utils to set up needed parameters is mandatory! Will try to use some kind of user preferences in user's config dir.

And a whole doc to write! :/

Added Paths:
-----------
    trunk/py/scripts/addons/ui_translate/
    trunk/py/scripts/addons/ui_translate/__init__.py
    trunk/py/scripts/addons/ui_translate/utils.py

Added: trunk/py/scripts/addons/ui_translate/__init__.py
===================================================================
--- trunk/py/scripts/addons/ui_translate/__init__.py	                        (rev 0)
+++ trunk/py/scripts/addons/ui_translate/__init__.py	2012-07-09 14:32:43 UTC (rev 3600)
@@ -0,0 +1,325 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+bl_info = {
+    "name": "Translate UI Messages",
+    "author": "Bastien Montagne",
+    "blender": (2, 6, 3),
+    "location": "Any UI control",
+    "description": "Allow to translate UI directly from Blender",
+    "warning": "",
+    "wiki_url": "",
+    "tracker_url": "",
+    "support": 'OFFICIAL',
+    "category": "System"}
+
+if "bpy" in locals():
+    import imp
+    if "ui_utils" in locals():
+        imp.reload(ui_utils)
+else:
+    import bpy
+    from bpy.props import (BoolProperty,
+                           CollectionProperty,
+                           EnumProperty,
+                           FloatProperty,
+                           FloatVectorProperty,
+                           IntProperty,
+                           PointerProperty,
+                           StringProperty,
+                           )
+    from . import utils as ui_utils
+
+from bl_i18n_utils import utils as i18n_utils
+from bl_i18n_utils import update_mo
+#from bl_i18n_utils import settings
+
+import os
+import shutil
+
+
+# module-level cache, as parsing po files takes a few seconds...
+# Keys are po file paths, data are the results of i18n_utils.parse_messages().
+PO_CACHE = {}
+
+
+def clear_caches(key):
+    del PO_CACHE[key]
+    del ui_utils.WORK_CACHE[key]
+
+
+class UI_OT_edittranslation_update_mo(bpy.types.Operator):
+    """Try to "compile" given po file into relevant blender.mo file """ \
+    """(WARNING: it will replace the official mo file in your user dir!)"""
+    bl_idname = "ui.edittranslation_update_mo"
+    bl_label = "Edit Translation Update Mo"
+
+    # "Parameters"
+    lang = StringProperty(description="Current (translated) language",
+                          options={'SKIP_SAVE'})
+    po_file = StringProperty(description="Path to the matching po file",
+                             subtype='FILE_PATH', options={'SKIP_SAVE'})
+    clean_mo = BoolProperty(description="Clean up (remove) all local "
+                                        "translation files, to be able to use "
+                                        "all system's ones again",
+                            default = False, options={'SKIP_SAVE'})
+
+    def execute(self, context):
+        if self.clean_mo:
+            root = bpy.utils.user_resource('DATAFILES', ui_utils.MO_PATH_ROOT)
+            if root:
+                shutil.rmtree(root)
+
+        elif not self.lang or not self.po_file:
+            return {'CANCELLED'}
+
+        else:
+            mo_dir = bpy.utils.user_resource(
+                         'DATAFILES', ui_utils.MO_PATH_TEMPLATE.format(self.lang),
+                         create=True)
+            mo_file = os.path.join(mo_dir, ui_utils.MO_FILENAME)
+            update_mo.process_po(self.po_file, None, mo_file)
+
+        bpy.ops.ui.reloadtranslation()
+        return {'FINISHED'}
+
+
+class UI_OT_edittranslation(bpy.types.Operator):
+    """Translate the label and tool tip of the property defined by given 'parameters'"""
+    bl_idname = "ui.edittranslation"
+    bl_label = "Edit Translation"
+
+    # "Parameters"
+    but_label = StringProperty(description="Label of the control", options={'SKIP_SAVE'})
+    rna_label = StringProperty(description="RNA-defined label of the control, if any", options={'SKIP_SAVE'})
+    enum_label = StringProperty(description="Label of the enum item of the control, if any", options={'SKIP_SAVE'})
+    but_tip = StringProperty(description="Tip of the control", options={'SKIP_SAVE'})
+    rna_tip = StringProperty(description="RNA-defined tip of the control, if any", options={'SKIP_SAVE'})
+    enum_tip = StringProperty(description="Tip of the enum item of the control, if any", options={'SKIP_SAVE'})
+    rna_struct = StringProperty(description="Identifier of the RNA struct, if any", options={'SKIP_SAVE'})
+    rna_prop = StringProperty(description="Identifier of the RNA property, if any", options={'SKIP_SAVE'})
+    rna_enum = StringProperty(description="Identifier of the RNA enum item, if any", options={'SKIP_SAVE'})
+    rna_ctxt = StringProperty(description="RNA context for label", options={'SKIP_SAVE'})
+
+    lang = StringProperty(description="Current (translated) language", options={'SKIP_SAVE'})
+    po_file = StringProperty(description="Path to the matching po file", subtype='FILE_PATH', options={'SKIP_SAVE'})
+
+    # Found in po file.
+    org_but_label = StringProperty(description="Original label of the control", options={'SKIP_SAVE'})
+    org_rna_label = StringProperty(description="Original RNA-defined label of the control, if any", options={'SKIP_SAVE'})
+    org_enum_label = StringProperty(description="Original label of the enum item of the control, if any", options={'SKIP_SAVE'})
+    org_but_tip = StringProperty(description="Original tip of the control", options={'SKIP_SAVE'})
+    org_rna_tip = StringProperty(description="Original RNA-defined tip of the control, if any", options={'SKIP_SAVE'})
+    org_enum_tip = StringProperty(description="Original tip of the enum item of the control, if any", options={'SKIP_SAVE'})
+
+    flag_items = (('FUZZY', "Fuzzy", "Message is marked as fuzzy in po file"),
+                  ('ERROR', "Error", "Some error occurred with this message"),
+                 )
+    but_label_flags = EnumProperty(items=flag_items, description="Flags about the label of the button", options={'SKIP_SAVE', 'ENUM_FLAG'})
+    rna_label_flags = EnumProperty(items=flag_items, description="Flags about the RNA-defined label of the button", options={'SKIP_SAVE', 'ENUM_FLAG'})
+    enum_label_flags = EnumProperty(items=flag_items, description="Flags about the RNA enum item label of the button", options={'SKIP_SAVE', 'ENUM_FLAG'})
+    but_tip_flags = EnumProperty(items=flag_items, description="Flags about the tip of the button", options={'SKIP_SAVE', 'ENUM_FLAG'})
+    rna_tip_flags = EnumProperty(items=flag_items, description="Flags about the RNA-defined tip of the button", options={'SKIP_SAVE', 'ENUM_FLAG'})
+    enum_tip_flags = EnumProperty(items=flag_items, description="Flags about the RNA enum item tip of the button", options={'SKIP_SAVE', 'ENUM_FLAG'})
+
+    stats_str = StringProperty(description="Stats from opened po", options={'SKIP_SAVE'})
+    update_po = BoolProperty(description="Update po file, try to rebuild mo file, and refresh Blender UI", default = False, options={'SKIP_SAVE'})
+    update_mo = BoolProperty(description="Try to rebuild mo file, and refresh Blender UI (WARNING: you should use a local Blender installation, as you probably have no right to write in the system Blender installation...)", default = False, options={'SKIP_SAVE'})
+    clean_mo = BoolProperty(description="Clean up (remove) all local "
+                                        "translation files, to be able to use "
+                                        "all system's ones again",
+                            default = False, options={'SKIP_SAVE'})
+
+    def execute(self, context):
+        if not hasattr(self, "msgmap"):
+            # We must be invoked() first!
+            return {'CANCELLED'}
+        msgs, state, stats = PO_CACHE[self.po_file]
+
+        done_keys = set()
+        for mmap in self.msgmap.values():
+            if 'ERROR' in getattr(self, mmap["msg_flags"]):
+                continue
+            k = mmap["key"]
+#            print(k)
+            if k not in done_keys and len(k) == 1:
+                k = tuple(k)[0]
+                msgs[k]["msgstr_lines"] = [getattr(self, mmap["msgstr"])]
+                if k in state["fuzzy_msg"] and 'FUZZY' not in getattr(self, mmap["msg_flags"]):
+                    state["fuzzy_msg"].remove(k)
+                elif k not in state["fuzzy_msg"] and 'FUZZY' in getattr(self, mmap["msg_flags"]):
+                    state["fuzzy_msg"].add(k)
+                done_keys.add(k)
+
+        if self.update_po:
+            # Try to overwrite po file, may fail if we have no good rights...
+            try:
+                i18n_utils.write_messages(self.po_file, msgs, state["comm_msg"], state["fuzzy_msg"])
+            except Exception as e:
+                self.report('ERROR', "Could not write to po file ({})".format(str(e)))
+            # Always invalidate all caches afterward!
+            clear_caches(self.po_file)
+        if self.update_mo:
+            bpy.ops.ui.edittranslation_update_mo(po_file=self.po_file, lang=self.lang)
+        elif self.clean_mo:
+            bpy.ops.ui.edittranslation_update_mo(clean_mo=True)
+        return {'FINISHED'}
+
+    def invoke(self, context, event):
+        if self.po_file in PO_CACHE:
+            msgs, state, stats = PO_CACHE[self.po_file]
+        else:
+            msgs, state, stats = PO_CACHE.setdefault(self.po_file, i18n_utils.parse_messages(self.po_file))
+
+        self.msgmap = {"but_label": {"msgstr": "but_label", "msgid": "org_but_label", "msg_flags": "but_label_flags", "key": set()},
+                       "rna_label": {"msgstr": "rna_label", "msgid": "org_rna_label", "msg_flags": "rna_label_flags", "key": set()},
+                       "enum_label": {"msgstr": "enum_label", "msgid": "org_enum_label", "msg_flags": "enum_label_flags", "key": set()},

@@ Diff output truncated at 10240 characters. @@


More information about the Bf-extensions-cvs mailing list