1 # SPDX-License-Identifier: GPL-2.0-or-later
7 importlib
.reload(settings
)
8 importlib
.reload(utils_i18n
)
11 from bpy
.types
import Operator
12 from bpy
.props
import (
17 from . import settings
18 from bl_i18n_utils
import utils
as utils_i18n
21 # A global cache for I18nMessages objects, as parsing po files takes a few seconds.
25 def _get_messages(lang
, fname
):
26 if fname
not in PO_CACHE
:
27 PO_CACHE
[fname
] = utils_i18n
.I18nMessages(uid
=lang
, kind
='PO', key
=fname
, src
=fname
, settings
=settings
.settings
)
28 return PO_CACHE
[fname
]
31 class UI_OT_i18n_edittranslation_update_mo(Operator
):
32 """Try to "compile" given po file into relevant blender.mo file"""
33 """(WARNING: it will replace the official mo file in your user dir!)"""
34 bl_idname
= "ui.i18n_edittranslation_update_mo"
35 bl_label
= "Edit Translation Update Mo"
39 description
="Current (translated) language",
40 options
={'SKIP_SAVE'},
43 po_file
: StringProperty(
44 description
="Path to the matching po file",
46 options
={'SKIP_SAVE'},
49 clean_mo
: BoolProperty(
50 description
="Remove all local translation files, to be able to use the system ones again",
54 # /End Operator Arguments
56 def execute(self
, context
):
58 root
= bpy
.utils
.user_resource('DATAFILES', path
=settings
.settings
.MO_PATH_ROOT_RELATIVE
)
61 elif not (self
.lang
and self
.po_file
):
64 mo_dir
= bpy
.utils
.user_resource(
66 path
=settings
.settings
.MO_PATH_TEMPLATE_RELATIVE
.format(self
.lang
),
69 mo_file
= os
.path
.join(mo_dir
, settings
.settings
.MO_FILE_NAME
)
70 _get_messages(self
.lang
, self
.po_file
).write(kind
='MO', dest
=mo_file
)
72 bpy
.ops
.ui
.reloadtranslation()
76 class UI_OT_i18n_edittranslation(Operator
):
77 """Translate the label and tooltip of the given property"""
78 bl_idname
= "ui.edittranslation"
79 bl_label
= "Edit Translation"
82 but_label
: StringProperty(
83 description
="Label of the control",
84 options
={'SKIP_SAVE'},
87 rna_label
: StringProperty(
88 description
="RNA-defined label of the control, if any",
89 options
={'SKIP_SAVE'},
92 enum_label
: StringProperty(
93 description
="Label of the enum item of the control, if any",
94 options
={'SKIP_SAVE'},
97 but_tip
: StringProperty(
98 description
="Tip of the control",
99 options
={'SKIP_SAVE'},
102 rna_tip
: StringProperty(
103 description
="RNA-defined tip of the control, if any",
104 options
={'SKIP_SAVE'},
107 enum_tip
: StringProperty(
108 description
="Tip of the enum item of the control, if any",
109 options
={'SKIP_SAVE'},
112 rna_struct
: StringProperty(
113 description
="Identifier of the RNA struct, if any",
114 options
={'SKIP_SAVE'},
117 rna_prop
: StringProperty(
118 description
="Identifier of the RNA property, if any",
119 options
={'SKIP_SAVE'},
122 rna_enum
: StringProperty(
123 description
="Identifier of the RNA enum item, if any",
124 options
={'SKIP_SAVE'},
127 rna_ctxt
: StringProperty(
128 description
="RNA context for label",
129 options
={'SKIP_SAVE'},
132 lang
: StringProperty(
133 description
="Current (translated) language",
134 options
={'SKIP_SAVE'},
137 po_file
: StringProperty(
138 description
="Path to the matching po file",
140 options
={'SKIP_SAVE'},
144 org_but_label
: StringProperty(
145 description
="Original label of the control",
146 options
={'SKIP_SAVE'},
149 org_rna_label
: StringProperty(
150 description
="Original RNA-defined label of the control, if any",
151 options
={'SKIP_SAVE'},
154 org_enum_label
: StringProperty(
155 description
="Original label of the enum item of the control, if any",
156 options
={'SKIP_SAVE'},
159 org_but_tip
: StringProperty(
160 description
="Original tip of the control",
161 options
={'SKIP_SAVE'},
164 org_rna_tip
: StringProperty(
165 description
="Original RNA-defined tip of the control, if any", options
={'SKIP_SAVE'}
168 org_enum_tip
: StringProperty(
169 description
="Original tip of the enum item of the control, if any",
170 options
={'SKIP_SAVE'},
174 ('FUZZY', "Fuzzy", "Message is marked as fuzzy in po file"),
175 ('ERROR', "Error", "Some error occurred with this message"),
178 but_label_flags
: EnumProperty(
179 description
="Flags about the label of the button",
181 options
={'SKIP_SAVE', 'ENUM_FLAG'},
184 rna_label_flags
: EnumProperty(
185 description
="Flags about the RNA-defined label of the button",
187 options
={'SKIP_SAVE', 'ENUM_FLAG'},
190 enum_label_flags
: EnumProperty(
191 description
="Flags about the RNA enum item label of the button",
193 options
={'SKIP_SAVE', 'ENUM_FLAG'},
196 but_tip_flags
: EnumProperty(
197 description
="Flags about the tip of the button",
199 options
={'SKIP_SAVE', 'ENUM_FLAG'},
202 rna_tip_flags
: EnumProperty(
203 description
="Flags about the RNA-defined tip of the button",
205 options
={'SKIP_SAVE', 'ENUM_FLAG'},
208 enum_tip_flags
: EnumProperty(
209 description
="Flags about the RNA enum item tip of the button",
211 options
={'SKIP_SAVE', 'ENUM_FLAG'},
214 stats_str
: StringProperty(
215 description
="Stats from opened po", options
={'SKIP_SAVE'})
217 update_po
: BoolProperty(
218 description
="Update po file, try to rebuild mo file, and refresh Blender's UI",
220 options
={'SKIP_SAVE'},
223 update_mo
: BoolProperty(
224 description
="Try to rebuild mo file, and refresh Blender's UI",
226 options
={'SKIP_SAVE'},
229 clean_mo
: BoolProperty(
230 description
="Remove all local translation files, to be able to use the system ones again",
232 options
={'SKIP_SAVE'},
234 # /End Operator Arguments
236 def execute(self
, context
):
237 if not hasattr(self
, "msgmap"):
238 self
.report('ERROR', "invoke() needs to be called before execute()")
241 msgs
= _get_messages(self
.lang
, self
.po_file
)
243 for mmap
in self
.msgmap
.values():
244 if 'ERROR' in getattr(self
, mmap
["msg_flags"]):
247 if k
not in done_keys
and len(k
) == 1:
249 msgs
.msgs
[k
].msgstr
= getattr(self
, mmap
["msgstr"])
250 msgs
.msgs
[k
].is_fuzzy
= 'FUZZY' in getattr(self
, mmap
["msg_flags"])
254 # Try to overwrite .po file, may fail if there are no permissions.
256 msgs
.write(kind
='PO', dest
=self
.po_file
)
257 except Exception as e
:
258 self
.report('ERROR', "Could not write to po file ({})".format(str(e
)))
259 # Always invalidate reverse messages cache afterward!
260 msgs
.invalidate_reverse_cache()
262 lang
= os
.path
.splitext(os
.path
.basename(self
.po_file
))[0]
263 bpy
.ops
.ui
.i18n_edittranslation_update_mo(po_file
=self
.po_file
, lang
=lang
)
265 bpy
.ops
.ui
.i18n_edittranslation_update_mo(clean_mo
=True)
268 def invoke(self
, context
, event
):
271 "msgstr": "but_label", "msgid": "org_but_label", "msg_flags": "but_label_flags", "key": set()},
273 "msgstr": "rna_label", "msgid": "org_rna_label", "msg_flags": "rna_label_flags", "key": set()},
275 "msgstr": "enum_label", "msgid": "org_enum_label", "msg_flags": "enum_label_flags", "key": set()},
277 "msgstr": "but_tip", "msgid": "org_but_tip", "msg_flags": "but_tip_flags", "key": set()},
279 "msgstr": "rna_tip", "msgid": "org_rna_tip", "msg_flags": "rna_tip_flags", "key": set()},
281 "msgstr": "enum_tip", "msgid": "org_enum_tip", "msg_flags": "enum_tip_flags", "key": set()},
284 msgs
= _get_messages(self
.lang
, self
.po_file
)
285 msgs
.find_best_messages_matches(self
, self
.msgmap
, self
.rna_ctxt
, self
.rna_struct
, self
.rna_prop
, self
.rna_enum
)
287 self
.stats_str
= "{}: {} messages, {} translated.".format(os
.path
.basename(self
.po_file
), msgs
.nbr_msgs
,
290 for mmap
in self
.msgmap
.values():
291 k
= tuple(mmap
["key"])
296 setattr(self
, mmap
["msgstr"], msgs
.msgs
[k
].msgstr
)
297 setattr(self
, mmap
["msgid"], msgid
)
298 if msgs
.msgs
[k
].is_fuzzy
:
299 setattr(self
, mmap
["msg_flags"], {'FUZZY'})
301 setattr(self
, mmap
["msgid"],
302 "ERROR: Button label “{}” matches several messages in po file ({})!"
303 "".format(self
.but_label
, k
))
304 setattr(self
, mmap
["msg_flags"], {'ERROR'})
306 setattr(self
, mmap
["msgstr"], "")
307 setattr(self
, mmap
["msgid"], "")
309 wm
= context
.window_manager
310 return wm
.invoke_props_dialog(self
, width
=600)
312 def draw(self
, context
):
314 layout
.label(text
=self
.stats_str
)
315 src
, _a
, _b
= bpy
.utils
.make_rna_paths(self
.rna_struct
, self
.rna_prop
, self
.rna_enum
)
317 layout
.label(text
=" RNA Path: bpy.types." + src
)
319 layout
.label(text
=" RNA Context: " + self
.rna_ctxt
)
321 if self
.org_but_label
or self
.org_rna_label
or self
.org_enum_label
:
322 # XXX Can't use box, labels are not enough readable in them :/
324 box
.label(text
="Labels:")
325 split
= box
.split(factor
=0.15)
326 col1
= split
.column()
327 col2
= split
.column()
328 if self
.org_but_label
:
329 col1
.label(text
="Button Label:")
332 if 'ERROR' in self
.but_label_flags
:
335 col1
.prop_enum(self
, "but_label_flags", 'FUZZY', text
="Fuzzy")
336 col2
.prop(self
, "but_label", text
="")
337 row
.prop(self
, "org_but_label", text
="")
338 if self
.org_rna_label
:
339 col1
.label(text
="RNA Label:")
342 if 'ERROR' in self
.rna_label_flags
:
345 col1
.prop_enum(self
, "rna_label_flags", 'FUZZY', text
="Fuzzy")
346 col2
.prop(self
, "rna_label", text
="")
347 row
.prop(self
, "org_rna_label", text
="")
348 if self
.org_enum_label
:
349 col1
.label(text
="Enum Item Label:")
352 if 'ERROR' in self
.enum_label_flags
:
355 col1
.prop_enum(self
, "enum_label_flags", 'FUZZY', text
="Fuzzy")
356 col2
.prop(self
, "enum_label", text
="")
357 row
.prop(self
, "org_enum_label", text
="")
359 if self
.org_but_tip
or self
.org_rna_tip
or self
.org_enum_tip
:
360 # XXX Can't use box, labels are not enough readable in them :/
362 box
.label(text
="Tool Tips:")
363 split
= box
.split(factor
=0.15)
364 col1
= split
.column()
365 col2
= split
.column()
367 col1
.label(text
="Button Tip:")
370 if 'ERROR' in self
.but_tip_flags
:
373 col1
.prop_enum(self
, "but_tip_flags", 'FUZZY', text
="Fuzzy")
374 col2
.prop(self
, "but_tip", text
="")
375 row
.prop(self
, "org_but_tip", text
="")
377 col1
.label(text
="RNA Tip:")
380 if 'ERROR' in self
.rna_tip_flags
:
383 col1
.prop_enum(self
, "rna_tip_flags", 'FUZZY', text
="Fuzzy")
384 col2
.prop(self
, "rna_tip", text
="")
385 row
.prop(self
, "org_rna_tip", text
="")
386 if self
.org_enum_tip
:
387 col1
.label(text
="Enum Item Tip:")
390 if 'ERROR' in self
.enum_tip_flags
:
393 col1
.prop_enum(self
, "enum_tip_flags", 'FUZZY', text
="Fuzzy")
394 col2
.prop(self
, "enum_tip", text
="")
395 row
.prop(self
, "org_enum_tip", text
="")
398 row
.prop(self
, "update_po", text
="Save to PO File", toggle
=True)
399 row
.prop(self
, "update_mo", text
="Rebuild MO File", toggle
=True)
400 row
.prop(self
, "clean_mo", text
="Erase Local MO files", toggle
=True)
404 UI_OT_i18n_edittranslation_update_mo
,
405 UI_OT_i18n_edittranslation
,