glTF: fix typo
[blender-addons.git] / ui_translate / update_svn.py
blob3bcfdf48c8ad9a7c18d7537300e6dda76ef21271
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 if "bpy" in locals():
4 import importlib
5 importlib.reload(settings)
6 importlib.reload(utils_i18n)
7 importlib.reload(utils_languages_menu)
8 else:
9 import bpy
10 from bpy.types import Operator
11 from bpy.props import (
12 BoolProperty,
13 EnumProperty,
15 from . import settings
16 from bl_i18n_utils import utils as utils_i18n
17 from bl_i18n_utils import utils_languages_menu
19 import concurrent.futures
20 import io
21 import os
22 import shutil
23 import subprocess
24 import tempfile
27 # Operators ###################################################################
29 def i18n_updatetranslation_svn_branches_callback(pot, lng, settings):
30 if not lng['use']:
31 return
32 if os.path.isfile(lng['po_path']):
33 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
34 po.update(pot)
35 else:
36 po = pot
37 po.write(kind="PO", dest=lng['po_path'])
38 print("{} PO written!".format(lng['uid']))
41 class UI_OT_i18n_updatetranslation_svn_branches(Operator):
42 """Update i18n svn's branches (po files)"""
43 bl_idname = "ui.i18n_updatetranslation_svn_branches"
44 bl_label = "Update I18n Branches"
46 use_skip_pot_gen: BoolProperty(
47 name="Skip POT",
48 description="Skip POT file generation",
49 default=False,
52 def execute(self, context):
53 if not hasattr(self, "settings"):
54 self.settings = settings.settings
55 i18n_sett = context.window_manager.i18n_update_svn_settings
56 self.settings.FILE_NAME_POT = i18n_sett.pot_path
58 context.window_manager.progress_begin(0, len(i18n_sett.langs) + 1)
59 context.window_manager.progress_update(0)
60 if not self.use_skip_pot_gen:
61 env = os.environ.copy()
62 env["ASAN_OPTIONS"] = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "")
63 # Generate base pot from RNA messages (we use another blender instance here, to be able to perfectly
64 # control our environment (factory startup, specific addons enabled/disabled...)).
65 # However, we need to export current user settings about this addon!
66 cmmd = (
67 bpy.app.binary_path,
68 "--background",
69 "--factory-startup",
70 "--python",
71 os.path.join(os.path.dirname(utils_i18n.__file__), "bl_extract_messages.py"),
72 "--",
73 "--settings",
74 self.settings.to_json(),
76 # Not working (UI is not refreshed...).
77 #self.report({'INFO'}, "Extracting messages, this will take some time...")
78 context.window_manager.progress_update(1)
79 ret = subprocess.run(cmmd, env=env)
80 if ret.returncode != 0:
81 self.report({'ERROR'}, "Message extraction process failed!")
82 context.window_manager.progress_end()
83 return {'CANCELLED'}
85 # Now we should have a valid POT file, we have to merge it in all languages po's...
86 with concurrent.futures.ProcessPoolExecutor() as exctr:
87 pot = utils_i18n.I18nMessages(kind='PO', src=self.settings.FILE_NAME_POT, settings=self.settings)
88 num_langs = len(i18n_sett.langs)
89 for progress, _ in enumerate(exctr.map(i18n_updatetranslation_svn_branches_callback,
90 (pot,) * num_langs,
91 [dict(lng.items()) for lng in i18n_sett.langs],
92 (self.settings,) * num_langs,
93 chunksize=4)):
94 context.window_manager.progress_update(progress + 2)
95 context.window_manager.progress_end()
96 return {'FINISHED'}
98 def invoke(self, context, event):
99 wm = context.window_manager
100 return wm.invoke_props_dialog(self)
103 def i18n_cleanuptranslation_svn_branches_callback(lng, settings):
104 if not lng['use']:
105 print("Skipping {} language ({}).".format(lng['name'], lng['uid']))
106 return
107 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
108 errs = po.check(fix=True)
109 cleanedup_commented = po.clean_commented()
110 po.write(kind="PO", dest=lng['po_path'])
111 print("Processing {} language ({}).\n"
112 "Cleaned up {} commented messages.\n".format(lng['name'], lng['uid'], cleanedup_commented) +
113 ("Errors in this po, solved as best as possible!\n\t" + "\n\t".join(errs) if errs else "") + "\n")
116 class UI_OT_i18n_cleanuptranslation_svn_branches(Operator):
117 """Clean up i18n svn's branches (po files)"""
118 bl_idname = "ui.i18n_cleanuptranslation_svn_branches"
119 bl_label = "Clean up I18n Branches"
121 def execute(self, context):
122 if not hasattr(self, "settings"):
123 self.settings = settings.settings
124 i18n_sett = context.window_manager.i18n_update_svn_settings
125 # 'DEFAULT' and en_US are always valid, fully-translated "languages"!
126 stats = {"DEFAULT": 1.0, "en_US": 1.0}
128 context.window_manager.progress_begin(0, len(i18n_sett.langs) + 1)
129 context.window_manager.progress_update(0)
130 with concurrent.futures.ProcessPoolExecutor() as exctr:
131 num_langs = len(i18n_sett.langs)
132 for progress, _ in enumerate(exctr.map(i18n_cleanuptranslation_svn_branches_callback,
133 [dict(lng.items()) for lng in i18n_sett.langs],
134 (self.settings,) * num_langs,
135 chunksize=4)):
136 context.window_manager.progress_update(progress + 1)
138 context.window_manager.progress_end()
140 return {'FINISHED'}
143 def i18n_updatetranslation_svn_trunk_callback(lng, settings):
144 if lng['uid'] in settings.IMPORT_LANGUAGES_SKIP:
145 print("Skipping {} language ({}), edit settings if you want to enable it.\n".format(lng['name'], lng['uid']))
146 return lng['uid'], 0.0
147 if not lng['use']:
148 print("Skipping {} language ({}).\n".format(lng['name'], lng['uid']))
149 return lng['uid'], 0.0
150 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
151 errs = po.check(fix=True)
152 print("Processing {} language ({}).\n"
153 "Cleaned up {} commented messages.\n".format(lng['name'], lng['uid'], po.clean_commented()) +
154 ("Errors in this po, solved as best as possible!\n\t" + "\n\t".join(errs) if errs else "") + "\n")
155 if lng['uid'] in settings.IMPORT_LANGUAGES_RTL:
156 po.write(kind="PO", dest=lng['po_path_trunk'][:-3] + "_raw.po")
157 po.rtl_process()
158 po.write(kind="PO", dest=lng['po_path_trunk'])
159 po.write(kind="PO_COMPACT", dest=lng['po_path_git'])
160 po.write(kind="MO", dest=lng['mo_path_trunk'])
161 po.update_info()
162 return lng['uid'], po.nbr_trans_msgs / po.nbr_msgs
165 class UI_OT_i18n_updatetranslation_svn_trunk(Operator):
166 """Update i18n svn's branches (po files)"""
167 bl_idname = "ui.i18n_updatetranslation_svn_trunk"
168 bl_label = "Update I18n Trunk"
170 def execute(self, context):
171 if not hasattr(self, "settings"):
172 self.settings = settings.settings
173 i18n_sett = context.window_manager.i18n_update_svn_settings
174 # 'DEFAULT' and en_US are always valid, fully-translated "languages"!
175 stats = {"DEFAULT": 1.0, "en_US": 1.0}
177 context.window_manager.progress_begin(0, len(i18n_sett.langs) + 1)
178 context.window_manager.progress_update(0)
179 with concurrent.futures.ProcessPoolExecutor() as exctr:
180 num_langs = len(i18n_sett.langs)
181 for progress, (lng_uid, stats_val) in enumerate(exctr.map(i18n_updatetranslation_svn_trunk_callback,
182 [dict(lng.items()) for lng in i18n_sett.langs],
183 (self.settings,) * num_langs,
184 chunksize=4)):
185 context.window_manager.progress_update(progress + 1)
186 stats[lng_uid] = stats_val
188 # Copy pot file from branches to trunk.
189 shutil.copy2(self.settings.FILE_NAME_POT, self.settings.TRUNK_PO_DIR)
191 print("Generating languages' menu...")
192 context.window_manager.progress_update(progress + 2)
193 # First complete our statistics by checking po files we did not touch this time!
194 po_to_uid = {os.path.basename(lng.po_path): lng.uid for lng in i18n_sett.langs}
195 for po_path in os.listdir(self.settings.TRUNK_PO_DIR):
196 uid = po_to_uid.get(po_path, None)
197 po_path = os.path.join(self.settings.TRUNK_PO_DIR, po_path)
198 if uid and uid not in stats:
199 po = utils_i18n.I18nMessages(uid=uid, kind='PO', src=po_path, settings=self.settings)
200 stats[uid] = po.nbr_trans_msgs / po.nbr_msgs if po.nbr_msgs > 0 else 0
201 utils_languages_menu.gen_menu_file(stats, self.settings)
202 context.window_manager.progress_end()
204 return {'FINISHED'}
207 class UI_OT_i18n_updatetranslation_svn_statistics(Operator):
208 """Create or extend a 'i18n_info.txt' Text datablock"""
209 """(it will contain statistics and checks about current branches and/or trunk)"""
210 bl_idname = "ui.i18n_updatetranslation_svn_statistics"
211 bl_label = "Update I18n Statistics"
213 use_branches: BoolProperty(
214 name="Check Branches",
215 description="Check po files in branches",
216 default=True,
219 use_trunk: BoolProperty(
220 name="Check Trunk",
221 description="Check po files in trunk",
222 default=False,
225 report_name = "i18n_info.txt"
227 def execute(self, context):
228 if not hasattr(self, "settings"):
229 self.settings = settings.settings
230 i18n_sett = context.window_manager.i18n_update_svn_settings
232 buff = io.StringIO()
233 lst = []
234 if self.use_branches:
235 lst += [(lng, lng.po_path) for lng in i18n_sett.langs]
236 if self.use_trunk:
237 lst += [(lng, lng.po_path_trunk) for lng in i18n_sett.langs
238 if lng.uid not in self.settings.IMPORT_LANGUAGES_SKIP]
240 context.window_manager.progress_begin(0, len(lst))
241 context.window_manager.progress_update(0)
242 for progress, (lng, path) in enumerate(lst):
243 context.window_manager.progress_update(progress + 1)
244 if not lng.use:
245 print("Skipping {} language ({}).".format(lng.name, lng.uid))
246 continue
247 buff.write("Processing {} language ({}, {}).\n".format(lng.name, lng.uid, path))
248 po = utils_i18n.I18nMessages(uid=lng.uid, kind='PO', src=path, settings=self.settings)
249 po.print_info(prefix=" ", output=buff.write)
250 errs = po.check(fix=False)
251 if errs:
252 buff.write(" WARNING! Po contains following errors:\n")
253 buff.write(" " + "\n ".join(errs))
254 buff.write("\n")
255 buff.write("\n\n")
257 text = None
258 if self.report_name not in bpy.data.texts:
259 text = bpy.data.texts.new(self.report_name)
260 else:
261 text = bpy.data.texts[self.report_name]
262 data = text.as_string()
263 data = data + "\n" + buff.getvalue()
264 text.from_string(data)
265 self.report({'INFO'}, "Info written to {} text datablock!".format(self.report_name))
266 context.window_manager.progress_end()
268 return {'FINISHED'}
270 def invoke(self, context, event):
271 wm = context.window_manager
272 return wm.invoke_props_dialog(self)
275 classes = (
276 UI_OT_i18n_updatetranslation_svn_branches,
277 UI_OT_i18n_cleanuptranslation_svn_branches,
278 UI_OT_i18n_updatetranslation_svn_trunk,
279 UI_OT_i18n_updatetranslation_svn_statistics,