Node Wrangler: do not add reroutes to unavailable outputs
[blender-addons.git] / ui_translate / edit_translation.py
blobf35b46c707f9883115d8d087c6c2e17a9554288c
1 # SPDX-FileCopyrightText: 2012-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 import os
6 import shutil
7 if "bpy" in locals():
8 import importlib
9 importlib.reload(settings)
10 importlib.reload(utils_i18n)
11 else:
12 import bpy
13 from bpy.types import Operator
14 from bpy.props import (
15 BoolProperty,
16 EnumProperty,
17 StringProperty,
19 from . import settings
20 from bl_i18n_utils import utils as utils_i18n
23 # A global cache for I18nMessages objects, as parsing po files takes a few seconds.
24 PO_CACHE = {}
27 def _get_messages(lang, fname):
28 if fname not in PO_CACHE:
29 PO_CACHE[fname] = utils_i18n.I18nMessages(uid=lang, kind='PO', key=fname, src=fname, settings=settings.settings)
30 return PO_CACHE[fname]
33 class UI_OT_i18n_edittranslation_update_mo(Operator):
34 """Try to "compile" given po file into relevant blender.mo file"""
35 """(WARNING: it will replace the official mo file in your user dir!)"""
36 bl_idname = "ui.i18n_edittranslation_update_mo"
37 bl_label = "Edit Translation Update Mo"
39 # Operator Arguments
40 lang: StringProperty(
41 description="Current (translated) language",
42 options={'SKIP_SAVE'},
45 po_file: StringProperty(
46 description="Path to the matching po file",
47 subtype='FILE_PATH',
48 options={'SKIP_SAVE'},
51 clean_mo: BoolProperty(
52 description="Remove all local translation files, to be able to use the system ones again",
53 default=False,
54 options={'SKIP_SAVE'}
56 # /End Operator Arguments
58 def execute(self, context):
59 if self.clean_mo:
60 root = bpy.utils.user_resource('DATAFILES', path=settings.settings.MO_PATH_ROOT_RELATIVE)
61 if root:
62 shutil.rmtree(root)
63 elif not (self.lang and self.po_file):
64 return {'CANCELLED'}
65 else:
66 mo_dir = bpy.utils.user_resource(
67 'DATAFILES',
68 path=settings.settings.MO_PATH_TEMPLATE_RELATIVE.format(self.lang),
69 create=True,
71 mo_file = os.path.join(mo_dir, settings.settings.MO_FILE_NAME)
72 _get_messages(self.lang, self.po_file).write(kind='MO', dest=mo_file)
74 bpy.ops.ui.reloadtranslation()
75 return {'FINISHED'}
78 class UI_OT_i18n_edittranslation(Operator):
79 """Translate the label and tooltip of the given property"""
80 bl_idname = "ui.edittranslation"
81 bl_label = "Edit Translation"
83 # Operator Arguments
84 but_label: StringProperty(
85 description="Label of the control",
86 options={'SKIP_SAVE'},
89 rna_label: StringProperty(
90 description="RNA-defined label of the control, if any",
91 options={'SKIP_SAVE'},
94 enum_label: StringProperty(
95 description="Label of the enum item of the control, if any",
96 options={'SKIP_SAVE'},
99 but_tip: StringProperty(
100 description="Tip of the control",
101 options={'SKIP_SAVE'},
104 rna_tip: StringProperty(
105 description="RNA-defined tip of the control, if any",
106 options={'SKIP_SAVE'},
109 enum_tip: StringProperty(
110 description="Tip of the enum item of the control, if any",
111 options={'SKIP_SAVE'},
114 rna_struct: StringProperty(
115 description="Identifier of the RNA struct, if any",
116 options={'SKIP_SAVE'},
119 rna_prop: StringProperty(
120 description="Identifier of the RNA property, if any",
121 options={'SKIP_SAVE'},
124 rna_enum: StringProperty(
125 description="Identifier of the RNA enum item, if any",
126 options={'SKIP_SAVE'},
129 rna_ctxt: StringProperty(
130 description="RNA context for label",
131 options={'SKIP_SAVE'},
134 lang: StringProperty(
135 description="Current (translated) language",
136 options={'SKIP_SAVE'},
139 po_file: StringProperty(
140 description="Path to the matching po file",
141 subtype='FILE_PATH',
142 options={'SKIP_SAVE'},
145 # Found in po file.
146 org_but_label: StringProperty(
147 description="Original label of the control",
148 options={'SKIP_SAVE'},
151 org_rna_label: StringProperty(
152 description="Original RNA-defined label of the control, if any",
153 options={'SKIP_SAVE'},
156 org_enum_label: StringProperty(
157 description="Original label of the enum item of the control, if any",
158 options={'SKIP_SAVE'},
161 org_but_tip: StringProperty(
162 description="Original tip of the control",
163 options={'SKIP_SAVE'},
166 org_rna_tip: StringProperty(
167 description="Original RNA-defined tip of the control, if any", options={'SKIP_SAVE'}
170 org_enum_tip: StringProperty(
171 description="Original tip of the enum item of the control, if any",
172 options={'SKIP_SAVE'},
175 flag_items = (
176 ('FUZZY', "Fuzzy", "Message is marked as fuzzy in po file"),
177 ('ERROR', "Error", "Some error occurred with this message"),
180 but_label_flags: EnumProperty(
181 description="Flags about the label of the button",
182 items=flag_items,
183 options={'SKIP_SAVE', 'ENUM_FLAG'},
186 rna_label_flags: EnumProperty(
187 description="Flags about the RNA-defined label of the button",
188 items=flag_items,
189 options={'SKIP_SAVE', 'ENUM_FLAG'},
192 enum_label_flags: EnumProperty(
193 description="Flags about the RNA enum item label of the button",
194 items=flag_items,
195 options={'SKIP_SAVE', 'ENUM_FLAG'},
198 but_tip_flags: EnumProperty(
199 description="Flags about the tip of the button",
200 items=flag_items,
201 options={'SKIP_SAVE', 'ENUM_FLAG'},
204 rna_tip_flags: EnumProperty(
205 description="Flags about the RNA-defined tip of the button",
206 items=flag_items,
207 options={'SKIP_SAVE', 'ENUM_FLAG'},
210 enum_tip_flags: EnumProperty(
211 description="Flags about the RNA enum item tip of the button",
212 items=flag_items,
213 options={'SKIP_SAVE', 'ENUM_FLAG'},
216 stats_str: StringProperty(
217 description="Stats from opened po", options={'SKIP_SAVE'})
219 update_po: BoolProperty(
220 description="Update po file, try to rebuild mo file, and refresh Blender's UI",
221 default=False,
222 options={'SKIP_SAVE'},
225 update_mo: BoolProperty(
226 description="Try to rebuild mo file, and refresh Blender's UI",
227 default=False,
228 options={'SKIP_SAVE'},
231 clean_mo: BoolProperty(
232 description="Remove all local translation files, to be able to use the system ones again",
233 default=False,
234 options={'SKIP_SAVE'},
236 # /End Operator Arguments
238 def execute(self, context):
239 if not hasattr(self, "msgmap"):
240 self.report('ERROR', "invoke() needs to be called before execute()")
241 return {'CANCELLED'}
243 msgs = _get_messages(self.lang, self.po_file)
244 done_keys = set()
245 for mmap in self.msgmap.values():
246 if 'ERROR' in getattr(self, mmap["msg_flags"]):
247 continue
248 k = mmap["key"]
249 if k not in done_keys and len(k) == 1:
250 k = tuple(k)[0]
251 msgs.msgs[k].msgstr = getattr(self, mmap["msgstr"])
252 msgs.msgs[k].is_fuzzy = 'FUZZY' in getattr(self, mmap["msg_flags"])
253 done_keys.add(k)
255 if self.update_po:
256 # Try to overwrite .po file, may fail if there are no permissions.
257 try:
258 msgs.write(kind='PO', dest=self.po_file)
259 except Exception as e:
260 self.report('ERROR', "Could not write to po file ({})".format(str(e)))
261 # Always invalidate reverse messages cache afterward!
262 msgs.invalidate_reverse_cache()
263 if self.update_mo:
264 lang = os.path.splitext(os.path.basename(self.po_file))[0]
265 bpy.ops.ui.i18n_edittranslation_update_mo(po_file=self.po_file, lang=lang)
266 elif self.clean_mo:
267 bpy.ops.ui.i18n_edittranslation_update_mo(clean_mo=True)
268 return {'FINISHED'}
270 def invoke(self, context, event):
271 self.msgmap = {
272 "but_label": {
273 "msgstr": "but_label", "msgid": "org_but_label", "msg_flags": "but_label_flags", "key": set()},
274 "rna_label": {
275 "msgstr": "rna_label", "msgid": "org_rna_label", "msg_flags": "rna_label_flags", "key": set()},
276 "enum_label": {
277 "msgstr": "enum_label", "msgid": "org_enum_label", "msg_flags": "enum_label_flags", "key": set()},
278 "but_tip": {
279 "msgstr": "but_tip", "msgid": "org_but_tip", "msg_flags": "but_tip_flags", "key": set()},
280 "rna_tip": {
281 "msgstr": "rna_tip", "msgid": "org_rna_tip", "msg_flags": "rna_tip_flags", "key": set()},
282 "enum_tip": {
283 "msgstr": "enum_tip", "msgid": "org_enum_tip", "msg_flags": "enum_tip_flags", "key": set()},
286 msgs = _get_messages(self.lang, self.po_file)
287 msgs.find_best_messages_matches(self, self.msgmap, self.rna_ctxt, self.rna_struct, self.rna_prop, self.rna_enum)
288 msgs.update_info()
289 self.stats_str = "{}: {} messages, {} translated.".format(os.path.basename(self.po_file), msgs.nbr_msgs,
290 msgs.nbr_trans_msgs)
292 for mmap in self.msgmap.values():
293 k = tuple(mmap["key"])
294 if k:
295 if len(k) == 1:
296 k = k[0]
297 ctxt, msgid = k
298 setattr(self, mmap["msgstr"], msgs.msgs[k].msgstr)
299 setattr(self, mmap["msgid"], msgid)
300 if msgs.msgs[k].is_fuzzy:
301 setattr(self, mmap["msg_flags"], {'FUZZY'})
302 else:
303 setattr(self, mmap["msgid"],
304 "ERROR: Button label “{}” matches several messages in po file ({})!"
305 "".format(self.but_label, k))
306 setattr(self, mmap["msg_flags"], {'ERROR'})
307 else:
308 setattr(self, mmap["msgstr"], "")
309 setattr(self, mmap["msgid"], "")
311 wm = context.window_manager
312 return wm.invoke_props_dialog(self, width=600)
314 def draw(self, context):
315 layout = self.layout
316 layout.label(text=self.stats_str)
317 src, _a, _b = bpy.utils.make_rna_paths(self.rna_struct, self.rna_prop, self.rna_enum)
318 if src:
319 layout.label(text=" RNA Path: bpy.types." + src)
320 if self.rna_ctxt:
321 layout.label(text=" RNA Context: " + self.rna_ctxt)
323 if self.org_but_label or self.org_rna_label or self.org_enum_label:
324 # XXX Can't use box, labels are not enough readable in them :/
325 box = layout.box()
326 box.label(text="Labels:")
327 split = box.split(factor=0.15)
328 col1 = split.column()
329 col2 = split.column()
330 if self.org_but_label:
331 col1.label(text="Button Label:")
332 row = col2.row()
333 row.enabled = False
334 if 'ERROR' in self.but_label_flags:
335 row.alert = True
336 else:
337 col1.prop_enum(self, "but_label_flags", 'FUZZY', text="Fuzzy")
338 col2.prop(self, "but_label", text="")
339 row.prop(self, "org_but_label", text="")
340 if self.org_rna_label:
341 col1.label(text="RNA Label:")
342 row = col2.row()
343 row.enabled = False
344 if 'ERROR' in self.rna_label_flags:
345 row.alert = True
346 else:
347 col1.prop_enum(self, "rna_label_flags", 'FUZZY', text="Fuzzy")
348 col2.prop(self, "rna_label", text="")
349 row.prop(self, "org_rna_label", text="")
350 if self.org_enum_label:
351 col1.label(text="Enum Item Label:")
352 row = col2.row()
353 row.enabled = False
354 if 'ERROR' in self.enum_label_flags:
355 row.alert = True
356 else:
357 col1.prop_enum(self, "enum_label_flags", 'FUZZY', text="Fuzzy")
358 col2.prop(self, "enum_label", text="")
359 row.prop(self, "org_enum_label", text="")
361 if self.org_but_tip or self.org_rna_tip or self.org_enum_tip:
362 # XXX Can't use box, labels are not enough readable in them :/
363 box = layout.box()
364 box.label(text="Tool Tips:")
365 split = box.split(factor=0.15)
366 col1 = split.column()
367 col2 = split.column()
368 if self.org_but_tip:
369 col1.label(text="Button Tip:")
370 row = col2.row()
371 row.enabled = False
372 if 'ERROR' in self.but_tip_flags:
373 row.alert = True
374 else:
375 col1.prop_enum(self, "but_tip_flags", 'FUZZY', text="Fuzzy")
376 col2.prop(self, "but_tip", text="")
377 row.prop(self, "org_but_tip", text="")
378 if self.org_rna_tip:
379 col1.label(text="RNA Tip:")
380 row = col2.row()
381 row.enabled = False
382 if 'ERROR' in self.rna_tip_flags:
383 row.alert = True
384 else:
385 col1.prop_enum(self, "rna_tip_flags", 'FUZZY', text="Fuzzy")
386 col2.prop(self, "rna_tip", text="")
387 row.prop(self, "org_rna_tip", text="")
388 if self.org_enum_tip:
389 col1.label(text="Enum Item Tip:")
390 row = col2.row()
391 row.enabled = False
392 if 'ERROR' in self.enum_tip_flags:
393 row.alert = True
394 else:
395 col1.prop_enum(self, "enum_tip_flags", 'FUZZY', text="Fuzzy")
396 col2.prop(self, "enum_tip", text="")
397 row.prop(self, "org_enum_tip", text="")
399 row = layout.row()
400 row.prop(self, "update_po", text="Save to PO File", toggle=True)
401 row.prop(self, "update_mo", text="Rebuild MO File", toggle=True)
402 row.prop(self, "clean_mo", text="Erase Local MO files", toggle=True)
405 classes = (
406 UI_OT_i18n_edittranslation_update_mo,
407 UI_OT_i18n_edittranslation,