Cleanup: pep8
[blender-addons.git] / ui_translate / edit_translation.py
blobc66e2faca438f8e0f9ab8032aadec9b463d9fd33
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 if "bpy" in locals():
22 import importlib
23 importlib.reload(settings)
24 importlib.reload(utils_i18n)
25 else:
26 import bpy
27 from bpy.props import (
28 BoolProperty,
29 CollectionProperty,
30 EnumProperty,
31 FloatProperty,
32 FloatVectorProperty,
33 IntProperty,
34 PointerProperty,
35 StringProperty,
37 from . import settings
38 from bl_i18n_utils import utils as utils_i18n
41 import os
42 import shutil
45 # A global cache for I18nMessages objects, as parsing po files takes a few seconds.
46 PO_CACHE = {}
49 def _get_messages(lang, fname):
50 if fname not in PO_CACHE:
51 PO_CACHE[fname] = utils_i18n.I18nMessages(uid=lang, kind='PO', key=fname, src=fname, settings=settings.settings)
52 return PO_CACHE[fname]
55 class UI_OT_i18n_edittranslation_update_mo(bpy.types.Operator):
56 """Try to "compile" given po file into relevant blender.mo file """ \
57 """(WARNING: it will replace the official mo file in your user dir!)"""
58 bl_idname = "ui.i18n_edittranslation_update_mo"
59 bl_label = "Edit Translation Update Mo"
61 # "Parameters"
62 lang = StringProperty(description="Current (translated) language",
63 options={'SKIP_SAVE'})
64 po_file = StringProperty(description="Path to the matching po file",
65 subtype='FILE_PATH', options={'SKIP_SAVE'})
66 clean_mo = BoolProperty(description="Clean up (remove) all local "
67 "translation files, to be able to use "
68 "all system's ones again",
69 default=False, options={'SKIP_SAVE'})
71 def execute(self, context):
72 if self.clean_mo:
73 root = bpy.utils.user_resource('DATAFILES', settings.settings.MO_PATH_ROOT_RELATIVE)
74 if root:
75 shutil.rmtree(root)
76 elif not (self.lang and self.po_file):
77 return {'CANCELLED'}
78 else:
79 mo_dir = bpy.utils.user_resource('DATAFILES', settings.settings.MO_PATH_TEMPLATE_RELATIVE.format(self.lang),
80 create=True)
81 mo_file = os.path.join(mo_dir, settings.settings.MO_FILE_NAME)
82 _get_messages(self.lang, self.po_file).write(kind='MO', dest=mo_file)
84 bpy.ops.ui.reloadtranslation()
85 return {'FINISHED'}
88 class UI_OT_i18n_edittranslation(bpy.types.Operator):
89 """Translate the label and tooltip of the property defined by given 'parameters'"""
90 bl_idname = "ui.edittranslation"
91 bl_label = "Edit Translation"
93 # "Parameters"
94 but_label = StringProperty(description="Label of the control", options={'SKIP_SAVE'})
95 rna_label = StringProperty(description="RNA-defined label of the control, if any", options={'SKIP_SAVE'})
96 enum_label = StringProperty(description="Label of the enum item of the control, if any", options={'SKIP_SAVE'})
97 but_tip = StringProperty(description="Tip of the control", options={'SKIP_SAVE'})
98 rna_tip = StringProperty(description="RNA-defined tip of the control, if any", options={'SKIP_SAVE'})
99 enum_tip = StringProperty(description="Tip of the enum item of the control, if any", options={'SKIP_SAVE'})
100 rna_struct = StringProperty(description="Identifier of the RNA struct, if any", options={'SKIP_SAVE'})
101 rna_prop = StringProperty(description="Identifier of the RNA property, if any", options={'SKIP_SAVE'})
102 rna_enum = StringProperty(description="Identifier of the RNA enum item, if any", options={'SKIP_SAVE'})
103 rna_ctxt = StringProperty(description="RNA context for label", options={'SKIP_SAVE'})
105 lang = StringProperty(description="Current (translated) language", options={'SKIP_SAVE'})
106 po_file = StringProperty(description="Path to the matching po file", subtype='FILE_PATH', options={'SKIP_SAVE'})
108 # Found in po file.
109 org_but_label = StringProperty(description="Original label of the control", options={'SKIP_SAVE'})
110 org_rna_label = StringProperty(description="Original RNA-defined label of the control, if any",
111 options={'SKIP_SAVE'})
112 org_enum_label = StringProperty(description="Original label of the enum item of the control, if any",
113 options={'SKIP_SAVE'})
114 org_but_tip = StringProperty(description="Original tip of the control", options={'SKIP_SAVE'})
115 org_rna_tip = StringProperty(description="Original RNA-defined tip of the control, if any", options={'SKIP_SAVE'})
116 org_enum_tip = StringProperty(description="Original tip of the enum item of the control, if any",
117 options={'SKIP_SAVE'})
119 flag_items = (('FUZZY', "Fuzzy", "Message is marked as fuzzy in po file"),
120 ('ERROR', "Error", "Some error occurred with this message"),
122 but_label_flags = EnumProperty(items=flag_items, description="Flags about the label of the button",
123 options={'SKIP_SAVE', 'ENUM_FLAG'})
124 rna_label_flags = EnumProperty(items=flag_items, description="Flags about the RNA-defined label of the button",
125 options={'SKIP_SAVE', 'ENUM_FLAG'})
126 enum_label_flags = EnumProperty(items=flag_items, description="Flags about the RNA enum item label of the button",
127 options={'SKIP_SAVE', 'ENUM_FLAG'})
128 but_tip_flags = EnumProperty(items=flag_items, description="Flags about the tip of the button",
129 options={'SKIP_SAVE', 'ENUM_FLAG'})
130 rna_tip_flags = EnumProperty(items=flag_items, description="Flags about the RNA-defined tip of the button",
131 options={'SKIP_SAVE', 'ENUM_FLAG'})
132 enum_tip_flags = EnumProperty(items=flag_items, description="Flags about the RNA enum item tip of the button",
133 options={'SKIP_SAVE', 'ENUM_FLAG'})
135 stats_str = StringProperty(description="Stats from opened po", options={'SKIP_SAVE'})
136 update_po = BoolProperty(description="Update po file, try to rebuild mo file, and refresh Blender UI",
137 default=False, options={'SKIP_SAVE'})
138 update_mo = BoolProperty(description="Try to rebuild mo file, and refresh Blender UI",
139 default=False, options={'SKIP_SAVE'})
140 clean_mo = BoolProperty(description="Clean up (remove) all local translation files, to be able to use "
141 "all system's ones again",
142 default=False, options={'SKIP_SAVE'})
144 def execute(self, context):
145 if not hasattr(self, "msgmap"):
146 self.report('ERROR', "Looks like you did not invoke this operator first!")
147 return {'CANCELLED'}
149 msgs = _get_messages(self.lang, self.po_file)
150 done_keys = set()
151 for mmap in self.msgmap.values():
152 if 'ERROR' in getattr(self, mmap["msg_flags"]):
153 continue
154 k = mmap["key"]
155 # print(k)
156 if k not in done_keys and len(k) == 1:
157 k = tuple(k)[0]
158 msgs.msgs[k].msgstr = getattr(self, mmap["msgstr"])
159 msgs.msgs[k].is_fuzzy = 'FUZZY' in getattr(self, mmap["msg_flags"])
160 done_keys.add(k)
162 if self.update_po:
163 # Try to overwrite po file, may fail if we have no good rights...
164 try:
165 msgs.write(kind='PO', dest=self.po_file)
166 except Exception as e:
167 self.report('ERROR', "Could not write to po file ({})".format(str(e)))
168 # Always invalidate reverse messages cache afterward!
169 msgs.invalidate_reverse_cache()
170 if self.update_mo:
171 lang = os.path.splitext(os.path.basename(self.po_file))[0]
172 bpy.ops.ui.i18n_edittranslation_update_mo(po_file=self.po_file, lang=lang)
173 elif self.clean_mo:
174 bpy.ops.ui.i18n_edittranslation_update_mo(clean_mo=True)
175 return {'FINISHED'}
177 def invoke(self, context, event):
178 self.msgmap = {"but_label": {"msgstr": "but_label", "msgid": "org_but_label",
179 "msg_flags": "but_label_flags", "key": set()},
180 "rna_label": {"msgstr": "rna_label", "msgid": "org_rna_label",
181 "msg_flags": "rna_label_flags", "key": set()},
182 "enum_label": {"msgstr": "enum_label", "msgid": "org_enum_label",
183 "msg_flags": "enum_label_flags", "key": set()},
184 "but_tip": {"msgstr": "but_tip", "msgid": "org_but_tip",
185 "msg_flags": "but_tip_flags", "key": set()},
186 "rna_tip": {"msgstr": "rna_tip", "msgid": "org_rna_tip",
187 "msg_flags": "rna_tip_flags", "key": set()},
188 "enum_tip": {"msgstr": "enum_tip", "msgid": "org_enum_tip",
189 "msg_flags": "enum_tip_flags", "key": set()},
192 msgs = _get_messages(self.lang, self.po_file)
193 msgs.find_best_messages_matches(self, self.msgmap, self.rna_ctxt, self.rna_struct, self.rna_prop, self.rna_enum)
194 msgs.update_info()
195 self.stats_str = "{}: {} messages, {} translated.".format(os.path.basename(self.po_file), msgs.nbr_msgs,
196 msgs.nbr_trans_msgs)
198 for mmap in self.msgmap.values():
199 k = tuple(mmap["key"])
200 if k:
201 if len(k) == 1:
202 k = k[0]
203 ctxt, msgid = k
204 setattr(self, mmap["msgstr"], msgs.msgs[k].msgstr)
205 setattr(self, mmap["msgid"], msgid)
206 if msgs.msgs[k].is_fuzzy:
207 setattr(self, mmap["msg_flags"], {'FUZZY'})
208 else:
209 setattr(self, mmap["msgid"],
210 "ERROR: Button label “{}” matches several messages in po file ({})!"
211 "".format(self.but_label, k))
212 setattr(self, mmap["msg_flags"], {'ERROR'})
213 else:
214 setattr(self, mmap["msgstr"], "")
215 setattr(self, mmap["msgid"], "")
217 wm = context.window_manager
218 return wm.invoke_props_dialog(self, width=600)
220 def draw(self, context):
221 layout = self.layout
222 layout.label(text=self.stats_str)
223 src, _a, _b = bpy.utils.make_rna_paths(self.rna_struct, self.rna_prop, self.rna_enum)
224 if src:
225 layout.label(text=" RNA Path: bpy.types." + src)
226 if self.rna_ctxt:
227 layout.label(text=" RNA Context: " + self.rna_ctxt)
229 if self.org_but_label or self.org_rna_label or self.org_enum_label:
230 # XXX Can't use box, labels are not enough readable in them :/
231 box = layout.box()
232 #box = layout
233 box.label(text="Labels:")
234 split = box.split(percentage=0.15)
235 col1 = split.column()
236 col2 = split.column()
237 if self.org_but_label:
238 col1.label(text="Button Label:")
239 row = col2.row()
240 row.enabled = False
241 if 'ERROR' in self.but_label_flags:
242 row.alert = True
243 else:
244 col1.prop_enum(self, "but_label_flags", 'FUZZY', text="Fuzzy")
245 col2.prop(self, "but_label", text="")
246 row.prop(self, "org_but_label", text="")
247 if self.org_rna_label:
248 col1.label(text="RNA Label:")
249 row = col2.row()
250 row.enabled = False
251 if 'ERROR' in self.rna_label_flags:
252 row.alert = True
253 else:
254 col1.prop_enum(self, "rna_label_flags", 'FUZZY', text="Fuzzy")
255 col2.prop(self, "rna_label", text="")
256 row.prop(self, "org_rna_label", text="")
257 if self.org_enum_label:
258 col1.label(text="Enum Item Label:")
259 row = col2.row()
260 row.enabled = False
261 if 'ERROR' in self.enum_label_flags:
262 row.alert = True
263 else:
264 col1.prop_enum(self, "enum_label_flags", 'FUZZY', text="Fuzzy")
265 col2.prop(self, "enum_label", text="")
266 row.prop(self, "org_enum_label", text="")
268 if self.org_but_tip or self.org_rna_tip or self.org_enum_tip:
269 # XXX Can't use box, labels are not enough readable in them :/
270 box = layout.box()
271 #box = layout
272 box.label(text="Tool Tips:")
273 split = box.split(percentage=0.15)
274 col1 = split.column()
275 col2 = split.column()
276 if self.org_but_tip:
277 col1.label(text="Button Tip:")
278 row = col2.row()
279 row.enabled = False
280 if 'ERROR' in self.but_tip_flags:
281 row.alert = True
282 else:
283 col1.prop_enum(self, "but_tip_flags", 'FUZZY', text="Fuzzy")
284 col2.prop(self, "but_tip", text="")
285 row.prop(self, "org_but_tip", text="")
286 if self.org_rna_tip:
287 col1.label(text="RNA Tip:")
288 row = col2.row()
289 row.enabled = False
290 if 'ERROR' in self.rna_tip_flags:
291 row.alert = True
292 else:
293 col1.prop_enum(self, "rna_tip_flags", 'FUZZY', text="Fuzzy")
294 col2.prop(self, "rna_tip", text="")
295 row.prop(self, "org_rna_tip", text="")
296 if self.org_enum_tip:
297 col1.label(text="Enum Item Tip:")
298 row = col2.row()
299 row.enabled = False
300 if 'ERROR' in self.enum_tip_flags:
301 row.alert = True
302 else:
303 col1.prop_enum(self, "enum_tip_flags", 'FUZZY', text="Fuzzy")
304 col2.prop(self, "enum_tip", text="")
305 row.prop(self, "org_enum_tip", text="")
307 row = layout.row()
308 row.prop(self, "update_po", text="Save to PO File", toggle=True)
309 row.prop(self, "update_mo", text="Rebuild MO File", toggle=True)
310 row.prop(self, "clean_mo", text="Erase Local MO files", toggle=True)
313 classes = (
314 UI_OT_i18n_edittranslation_update_mo,
315 UI_OT_i18n_edittranslation,