Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Montagne <montagne29@wanadoo.fr>2013-04-09 12:59:56 +0400
committerBastien Montagne <montagne29@wanadoo.fr>2013-04-09 12:59:56 +0400
commit906517bac3a5f975fea03969237436340c320ae2 (patch)
treea5d9f2827a5ed65f4ecb25a5fa66bd4d1967171d /ui_translate
parent7009865c7e02ee9230a5c46a8ce1896492ee371a (diff)
Add base for addons' i18n tools. WARNING: nearly nothing is functionnal yet!
Also a few minor edits (and add a version number to this addon!)...
Diffstat (limited to 'ui_translate')
-rw-r--r--ui_translate/__init__.py6
-rw-r--r--ui_translate/update_addon.py305
-rw-r--r--ui_translate/update_svn.py13
3 files changed, 317 insertions, 7 deletions
diff --git a/ui_translate/__init__.py b/ui_translate/__init__.py
index 673c4ece..bd34760e 100644
--- a/ui_translate/__init__.py
+++ b/ui_translate/__init__.py
@@ -21,7 +21,8 @@
bl_info = {
"name": "Manage UI translations",
"author": "Bastien Montagne",
- "blender": (2, 65, 10),
+ "version": (1, 0, 1),
+ "blender": (2, 66, 5),
"location": "Main \"File\" menu, text editor, any UI control",
"description": "Allow to manage UI translations directly from Blender (update main po files, "
"update scripts' translations, etc.)",
@@ -37,11 +38,13 @@ if "bpy" in locals():
imp.reload(settings)
imp.reload(edit_translation)
imp.reload(update_svn)
+ imp.reload(update_addon)
else:
import bpy
from . import settings
from . import edit_translation
from . import update_svn
+ from . import update_addon
import os
@@ -62,4 +65,5 @@ def register():
def unregister():
del bpy.types.WindowManager.i18n_update_svn_settings
+
bpy.utils.unregister_module(__name__)
diff --git a/ui_translate/update_addon.py b/ui_translate/update_addon.py
new file mode 100644
index 00000000..5e3b3611
--- /dev/null
+++ b/ui_translate/update_addon.py
@@ -0,0 +1,305 @@
+# ##### 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>
+
+if "bpy" in locals():
+ import imp
+ imp.reload(settings)
+ imp.reload(utils_i18n)
+ imp.reload(bl_extract_messages)
+else:
+ import bpy
+ from bpy.props import (BoolProperty,
+ CollectionProperty,
+ EnumProperty,
+ FloatProperty,
+ FloatVectorProperty,
+ IntProperty,
+ PointerProperty,
+ StringProperty,
+ )
+ from . import settings
+ from bl_i18n_utils import utils as utils_i18n
+ from bl_i18n_utils import bl_extract_messages
+
+from bpy.app.translations import pgettext_iface as iface_
+import addon_utils
+
+import io
+import os
+import shutil
+import subprocess
+import tempfile
+
+
+##### Helpers #####
+def validate_module(op, context):
+ module_name = op.module_name
+ addon = getattr(context, "active_addon", None)
+ if addon:
+ module_name = addon.module
+
+ if not module_name:
+ op.report({'ERROR'}, "No addon module given!")
+ return None, None
+
+ mod = utils_i18n.enable_addons(addons={module_name}, check_only=True)
+ if not mod:
+ op.report({'ERROR'}, "Addon '{}' not found!".format(module_name))
+ return None, None
+ return module_name, mod[0]
+
+
+# As it's a bit time heavy, we cache that enum, operators using this should invalidate the cache in Invoke func
+# at least.
+def enum_addons(self, context):
+ items = getattr(self.__class__, "__enum_addons_cache", [])
+ print(items)
+ if not items:
+ setts = getattr(self, "settings", settings.settings)
+ for mod in addon_utils.modules(addon_utils.addons_fake_modules):
+ mod_info = addon_utils.module_bl_info(mod)
+ # Skip OFFICIAL addons, they are already translated in main i18n system (together with Blender itself).
+ if mod_info["support"] in {'OFFICIAL'}:
+ continue
+ src = mod.__file__
+ if src.endswith("__init__.py"):
+ src = os.path.dirname(src)
+ has_translation, _ = utils_i18n.I18n.check_py_module_has_translations(src, setts)
+ name = mod_info["name"]
+ #if has_translation:
+ #name = name + " *"
+ items.append((mod.__name__, name, mod_info["description"]))
+ items.sort(key=lambda i: i[1])
+ if hasattr(self.__class__, "__enum_addons_cache"):
+ self.__class__.__enum_addons_cache = items
+ return items
+
+
+##### Data #####
+
+
+##### UI #####
+#class UI_PT_i18n_update_translations_settings(bpy.types.Panel):
+ #bl_label = "I18n Update Translation Main"
+ #bl_space_type = "PROPERTIES"
+ #bl_region_type = "WINDOW"
+ #bl_context = "render"
+#
+ #def draw(self, context):
+ #layout = self.layout
+ #i18n_sett = context.window_manager.i18n_update_svn_settings
+#
+ #if not i18n_sett.is_init and bpy.ops.ui.i18n_updatetranslation_svn_init_settings.poll():
+ #bpy.ops.ui.i18n_updatetranslation_svn_init_settings()
+#
+ #if not i18n_sett.is_init:
+ #layout.label(text="Could not init languages data!")
+ #layout.label(text="Please edit the preferences of the UI Translate addon")
+ #else:
+ #split = layout.split(0.75)
+ #split.template_list("UI_UL_i18n_languages", "", i18n_sett, "langs", i18n_sett, "active_lang", rows=8)
+ #col = split.column()
+ #col.operator("ui.i18n_updatetranslation_svn_init_settings", text="Reset Settings")
+ #if any(l.use for l in i18n_sett.langs):
+ #col.operator("ui.i18n_updatetranslation_svn_settings_select", text="Deselect All").use_select = False
+ #else:
+ #col.operator("ui.i18n_updatetranslation_svn_settings_select", text="Select All").use_select = True
+ #col.operator("ui.i18n_updatetranslation_svn_settings_select", text="Invert Selection").use_invert = True
+ #col.separator()
+ #col.operator("ui.i18n_updatetranslation_svn_branches", text="Update Branches")
+ #col.operator("ui.i18n_updatetranslation_svn_trunk", text="Update Trunk")
+ #col.operator("ui.i18n_updatetranslation_svn_statistics", text="Statistics")
+#
+ #if i18n_sett.active_lang >= 0 and i18n_sett.active_lang < len(i18n_sett.langs):
+ #lng = i18n_sett.langs[i18n_sett.active_lang]
+ #col = layout.column()
+ #col.active = lng.use
+ #row = col.row()
+ #row.label(text="[{}]: \"{}\" ({})".format(lng.uid, iface_(lng.name), lng.num_id), translate=False)
+ #row.prop(lng, "use", text="")
+ #col.prop(lng, "po_path")
+ #col.prop(lng, "po_path_trunk")
+ #col.prop(lng, "mo_path_trunk")
+ #layout.separator()
+ #layout.prop(i18n_sett, "pot_path")
+
+
+##### Operators #####
+# This one is a helper one, as we sometimes need another invoke function (like e.g. file selection)...
+class UI_OT_i18n_addon_translation_invoke(bpy.types.Operator):
+ """Wrapper operator which will invoke given op after setting its module_name"""
+ bl_idname = "ui.i18n_addon_translation_invoke"
+ bl_label = "Update I18n Addon"
+ bl_property = "module_name"
+
+ module_name = EnumProperty(items=enum_addons, name="Addon", description="Addon to process", options=set())
+ op_id = StringProperty(name="Operator Name", description="Name (id) of the operator to invoke")
+
+ __enum_addons_cache = []
+
+ def invoke(self, context, event):
+ print("op_id:", self.op_id)
+ self.__enum_addons_cache.clear()
+ context.window_manager.invoke_search_popup(self)
+ return {'RUNNING_MODAL'}
+
+ def execute(self, context):
+ print("op_id:", self.op_id)
+ op = bpy.ops
+ for item in self.op_id.split('.'):
+ op = getattr(op, item, None)
+ print(self.op_id, item, op)
+ if op is None:
+ return {'CANCELLED'}
+ op('INVOKE_DEFAULT', module_name=self.module_name)
+
+class UI_OT_i18n_addon_translation_update(bpy.types.Operator):
+ """Update given addon's translation data (found as a py tuple in the addon's source code)"""
+ bl_idname = "ui.i18n_addon_translation_update"
+ bl_label = "Update I18n Addon"
+
+ module_name = EnumProperty(items=enum_addons, name="Addon", description="Addon to process", options=set())
+
+ __enum_addons_cache = []
+
+ def execute(self, context):
+ if not hasattr(self, "settings"):
+ self.settings = settings.settings
+ i18n_sett = context.window_manager.i18n_update_svn_settings
+
+ module_name, mod = validate_module(self, context)
+
+ # Generate addon-specific messages (no need for another blender instance here, this should not have any
+ # influence over the final result).
+ pot = bl_extract_messages.dump_addon_messages(module_name, True, self.settings)
+
+ # Now (try do) get current i18n data from the addon...
+ path = mod.__file__
+ if path.endswith("__init__.py"):
+ path = os.path.dirname(path)
+
+ trans = utils_i18n.I18n(kind='PY', src=path, settings=self.settings)
+
+ uids = set()
+ for lng in i18n_sett.langs:
+ if lng.uid in self.settings.IMPORT_LANGUAGES_SKIP:
+ print("Skipping {} language ({}), edit settings if you want to enable it.".format(lng.name, lng.uid))
+ continue
+ if not lng.use:
+ print("Skipping {} language ({}).".format(lng.name, lng.uid))
+ continue
+ uids.add(lng.uid)
+ # For now, add to processed uids all those not found in "official" list, minus "tech" ones.
+ uids |= (trans.trans.keys() - {lng.uid for lng in i18n_sett.langs} -
+ {self.settings.PARSER_TEMPLATE_ID, self.settings.PARSER_PY_ID})
+
+ # And merge!
+ for uid in uids:
+ if uid in trans.trans:
+ trans.trans[uid].update(pot, keep_old_commented=False)
+ trans.trans[self.settings.PARSER_TEMPLATE_ID] = pot
+
+ # For now we write all languages found in this trans!
+ trans.write(kind='PY')
+
+ return {'FINISHED'}
+
+
+class UI_OT_i18n_addon_translation_export(bpy.types.Operator):
+ """Export given addon's translation data as a PO file"""
+ bl_idname = "ui.i18n_addon_translation_export"
+ bl_label = "I18n Addon Export"
+
+ module_name = EnumProperty(items=enum_addons, name="Addon", description="Addon to process", options=set())
+ use_export_pot = BoolProperty(name="Export POT", default=True, description="Export (generate) a POT file too")
+ use_update_existing = BoolProperty(name="Update Existing", default=True,
+ description="Update existing po files, if any, instead of overwriting them")
+ directory = StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'})
+
+ __enum_addons_cache = []
+
+ def _dst(self, trans, path, uid, kind):
+ if kind == 'PO':
+ if uid == self.settings.PARSER_TEMPLATE_ID:
+ return os.path.join(self.directory, "blender.pot")
+ path = os.path.join(self.directory, uid)
+ if os.path.isdir(path):
+ return os.path.join(path, uid + ".po")
+ return path + ".po"
+ elif kind == 'PY':
+ return trans._dst(trans, path, uid, kind)
+ return path
+
+ def invoke(self, context, event):
+ if not hasattr(self, "settings"):
+ self.settings = settings.settings
+ self.__enum_addons_cache.clear()
+ module_name, mod = validate_module(self, context)
+ if mod:
+ self.directory = os.path.dirname(mod.__file__)
+ self.module_name = module_name
+ context.window_manager.fileselect_add(self)
+ return {'RUNNING_MODAL'}
+
+ def execute(self, context):
+ if not hasattr(self, "settings"):
+ self.settings = settings.settings
+ i18n_sett = context.window_manager.i18n_update_svn_settings
+
+ module_name, mod = validate_module(self, context)
+ if not (module_name and mod):
+ return {'CANCELLED'}
+
+ path = mod.__file__
+ if path.endswith("__init__.py"):
+ path = os.path.dirname(path)
+
+ trans = utils_i18n.I18n(kind='PY', src=path, settings=self.settings)
+ trans.dst = self._dst
+
+ uids = [self.settings.PARSER_TEMPLATE_ID] if self.use_export_pot else []
+ for lng in i18n_sett.langs:
+ if lng.uid in self.settings.IMPORT_LANGUAGES_SKIP:
+ print("Skipping {} language ({}), edit settings if you want to enable it.".format(lng.name, lng.uid))
+ continue
+ if not lng.use:
+ print("Skipping {} language ({}).".format(lng.name, lng.uid))
+ continue
+ uid = utils_i18n.find_best_isocode_matches(lng.uid, trans.trans.keys())
+ if uid:
+ uids.append(uid[0])
+
+ # Try to update existing POs instead of overwriting them, if asked to do so!
+ if self.use_update_existing:
+ for uid in uids:
+ if uid == self.settings.PARSER_TEMPLATE_ID:
+ continue
+ path = trans.dst(trans.src[uid], uid, 'PO')
+ if not os.path.isfile(path):
+ continue
+ msgs = utils_i18n.I18nMessages(kind='PO', src=path, settings=self.settings)
+ msgs.update(trans.msgs[self.settings.PARSER_TEMPLATE_ID])
+ trans.msgs[uid] = msgs
+
+ trans.write(kind='PO', langs=set(uids))
+
+ return {'FINISHED'}
+
+
diff --git a/ui_translate/update_svn.py b/ui_translate/update_svn.py
index efd46c3b..a76d6bce 100644
--- a/ui_translate/update_svn.py
+++ b/ui_translate/update_svn.py
@@ -46,11 +46,6 @@ import shutil
import subprocess
import tempfile
-##### Helpers #####
-def find_best_isocode_matches(uid, iso_codes):
- tmp = ((e, utils_i18n.locale_match(e, uid)) for e in iso_codes)
- return tuple(e[0] for e in sorted((e for e in tmp if e[1] is not ... and e[1] >= 0), key=lambda e: e[1]))
-
##### Data #####
class I18nUpdateTranslationLanguage(bpy.types.PropertyGroup):
@@ -137,6 +132,12 @@ class UI_PT_i18n_update_translations_settings(bpy.types.Panel):
layout.separator()
layout.prop(i18n_sett, "pot_path")
+ layout.separator()
+ layout.label("Addons:")
+ row = layout.row()
+ op = row.operator("UI_OT_i18n_addon_translation_invoke", text="Export PO...")
+ op.op_id = "ui.i18n_addon_translation_export"
+
##### Operators #####
class UI_OT_i18n_updatetranslation_svn_init_settings(bpy.types.Operator):
@@ -164,7 +165,7 @@ class UI_OT_i18n_updatetranslation_svn_init_settings(bpy.types.Operator):
isocodes = ((e, os.path.join(root_br, e, e + ".po")) for e in os.listdir(root_br))
isocodes = dict(e for e in isocodes if os.path.isfile(e[1]))
for num_id, name, uid in self.settings.LANGUAGES[2:]: # Skip "default" and "en" languages!
- best_po = find_best_isocode_matches(uid, isocodes)
+ best_po = utils_i18n.find_best_isocode_matches(uid, isocodes)
#print(uid, "->", best_po)
lng = i18n_sett.langs.add()
lng.uid = uid