1 # SPDX-FileCopyrightText: 2012-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
9 importlib
.reload(settings
)
10 importlib
.reload(utils_i18n
)
13 from bpy
.types
import Operator
14 from bpy
.props
import (
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.
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"
41 description
="Current (translated) language",
42 options
={'SKIP_SAVE'},
45 po_file
: StringProperty(
46 description
="Path to the matching po file",
48 options
={'SKIP_SAVE'},
51 clean_mo
: BoolProperty(
52 description
="Remove all local translation files, to be able to use the system ones again",
56 # /End Operator Arguments
58 def execute(self
, context
):
60 root
= bpy
.utils
.user_resource('DATAFILES', path
=settings
.settings
.MO_PATH_ROOT_RELATIVE
)
63 elif not (self
.lang
and self
.po_file
):
66 mo_dir
= bpy
.utils
.user_resource(
68 path
=settings
.settings
.MO_PATH_TEMPLATE_RELATIVE
.format(self
.lang
),
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()
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"
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",
142 options
={'SKIP_SAVE'},
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'},
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",
183 options
={'SKIP_SAVE', 'ENUM_FLAG'},
186 rna_label_flags
: EnumProperty(
187 description
="Flags about the RNA-defined label of the button",
189 options
={'SKIP_SAVE', 'ENUM_FLAG'},
192 enum_label_flags
: EnumProperty(
193 description
="Flags about the RNA enum item label of the button",
195 options
={'SKIP_SAVE', 'ENUM_FLAG'},
198 but_tip_flags
: EnumProperty(
199 description
="Flags about the tip of the button",
201 options
={'SKIP_SAVE', 'ENUM_FLAG'},
204 rna_tip_flags
: EnumProperty(
205 description
="Flags about the RNA-defined tip of the button",
207 options
={'SKIP_SAVE', 'ENUM_FLAG'},
210 enum_tip_flags
: EnumProperty(
211 description
="Flags about the RNA enum item tip of the button",
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",
222 options
={'SKIP_SAVE'},
225 update_mo
: BoolProperty(
226 description
="Try to rebuild mo file, and refresh Blender's UI",
228 options
={'SKIP_SAVE'},
231 clean_mo
: BoolProperty(
232 description
="Remove all local translation files, to be able to use the system ones again",
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()")
243 msgs
= _get_messages(self
.lang
, self
.po_file
)
245 for mmap
in self
.msgmap
.values():
246 if 'ERROR' in getattr(self
, mmap
["msg_flags"]):
249 if k
not in done_keys
and len(k
) == 1:
251 msgs
.msgs
[k
].msgstr
= getattr(self
, mmap
["msgstr"])
252 msgs
.msgs
[k
].is_fuzzy
= 'FUZZY' in getattr(self
, mmap
["msg_flags"])
256 # Try to overwrite .po file, may fail if there are no permissions.
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()
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
)
267 bpy
.ops
.ui
.i18n_edittranslation_update_mo(clean_mo
=True)
270 def invoke(self
, context
, event
):
273 "msgstr": "but_label", "msgid": "org_but_label", "msg_flags": "but_label_flags", "key": set()},
275 "msgstr": "rna_label", "msgid": "org_rna_label", "msg_flags": "rna_label_flags", "key": set()},
277 "msgstr": "enum_label", "msgid": "org_enum_label", "msg_flags": "enum_label_flags", "key": set()},
279 "msgstr": "but_tip", "msgid": "org_but_tip", "msg_flags": "but_tip_flags", "key": set()},
281 "msgstr": "rna_tip", "msgid": "org_rna_tip", "msg_flags": "rna_tip_flags", "key": set()},
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
)
289 self
.stats_str
= "{}: {} messages, {} translated.".format(os
.path
.basename(self
.po_file
), msgs
.nbr_msgs
,
292 for mmap
in self
.msgmap
.values():
293 k
= tuple(mmap
["key"])
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'})
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'})
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
):
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
)
319 layout
.label(text
=" RNA Path: bpy.types." + src
)
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 :/
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:")
334 if 'ERROR' in self
.but_label_flags
:
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:")
344 if 'ERROR' in self
.rna_label_flags
:
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:")
354 if 'ERROR' in self
.enum_label_flags
:
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 :/
364 box
.label(text
="Tool Tips:")
365 split
= box
.split(factor
=0.15)
366 col1
= split
.column()
367 col2
= split
.column()
369 col1
.label(text
="Button Tip:")
372 if 'ERROR' in self
.but_tip_flags
:
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
="")
379 col1
.label(text
="RNA Tip:")
382 if 'ERROR' in self
.rna_tip_flags
:
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:")
392 if 'ERROR' in self
.enum_tip_flags
:
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
="")
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)
406 UI_OT_i18n_edittranslation_update_mo
,
407 UI_OT_i18n_edittranslation
,