Node Wrangler: do not add reroutes to unavailable outputs
[blender-addons.git] / ui_translate / update_repo.py
blob9a3792da9efd82994e1beb13991837dc47e2af04
1 # SPDX-FileCopyrightText: 2013-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 if "bpy" in locals():
6 import importlib
7 importlib.reload(settings)
8 importlib.reload(utils_i18n)
9 importlib.reload(utils_languages_menu)
10 else:
11 import bpy
12 from bpy.types import Operator
13 from bpy.props import (
14 BoolProperty,
15 EnumProperty,
17 from . import settings
18 from bl_i18n_utils import utils as utils_i18n
19 from bl_i18n_utils import utils_languages_menu
21 import concurrent.futures
22 import io
23 import os
24 import shutil
25 import subprocess
26 import tempfile
29 # Operators ###################################################################
31 def i18n_updatetranslation_work_repo_callback(pot, lng, settings):
32 if not lng['use']:
33 return
34 if os.path.isfile(lng['po_path']):
35 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
36 po.update(pot)
37 else:
38 po = pot
39 po.write(kind="PO", dest=lng['po_path'])
40 print("{} PO written!".format(lng['uid']))
43 class UI_OT_i18n_updatetranslation_work_repo(Operator):
44 """Update i18n working repository (po files)"""
45 bl_idname = "ui.i18n_updatetranslation_work_repo"
46 bl_label = "Update I18n Work Repository"
48 use_skip_pot_gen: BoolProperty(
49 name="Skip POT",
50 description="Skip POT file generation",
51 default=False,
54 def execute(self, context):
55 if not hasattr(self, "settings"):
56 self.settings = settings.settings
57 i18n_sett = context.window_manager.i18n_update_settings
58 self.settings.FILE_NAME_POT = i18n_sett.pot_path
60 context.window_manager.progress_begin(0, len(i18n_sett.langs) + 1)
61 context.window_manager.progress_update(0)
62 if not self.use_skip_pot_gen:
63 env = os.environ.copy()
64 env["ASAN_OPTIONS"] = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "")
65 # Generate base pot from RNA messages (we use another blender instance here, to be able to perfectly
66 # control our environment (factory startup, specific addons enabled/disabled...)).
67 # However, we need to export current user settings about this addon!
68 cmmd = (
69 bpy.app.binary_path,
70 "--background",
71 "--factory-startup",
72 "--python",
73 os.path.join(os.path.dirname(utils_i18n.__file__), "bl_extract_messages.py"),
74 "--",
75 "--settings",
76 self.settings.to_json(),
78 # Not working (UI is not refreshed...).
79 #self.report({'INFO'}, "Extracting messages, this will take some time...")
80 context.window_manager.progress_update(1)
81 ret = subprocess.run(cmmd, env=env)
82 if ret.returncode != 0:
83 self.report({'ERROR'}, "Message extraction process failed!")
84 context.window_manager.progress_end()
85 return {'CANCELLED'}
87 # Now we should have a valid POT file, we have to merge it in all languages po's...
88 with concurrent.futures.ProcessPoolExecutor() as exctr:
89 pot = utils_i18n.I18nMessages(kind='PO', src=self.settings.FILE_NAME_POT, settings=self.settings)
90 num_langs = len(i18n_sett.langs)
91 for progress, _ in enumerate(exctr.map(i18n_updatetranslation_work_repo_callback,
92 (pot,) * num_langs,
93 [dict(lng.items()) for lng in i18n_sett.langs],
94 (self.settings,) * num_langs,
95 chunksize=4)):
96 context.window_manager.progress_update(progress + 2)
97 context.window_manager.progress_end()
98 return {'FINISHED'}
100 def invoke(self, context, event):
101 wm = context.window_manager
102 return wm.invoke_props_dialog(self)
105 def i18n_cleanuptranslation_work_repo_callback(lng, settings):
106 if not lng['use']:
107 print("Skipping {} language ({}).".format(lng['name'], lng['uid']))
108 return
109 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
110 errs = po.check(fix=True)
111 cleanedup_commented = po.clean_commented()
112 po.write(kind="PO", dest=lng['po_path'])
113 print("Processing {} language ({}).\n"
114 "Cleaned up {} commented messages.\n".format(lng['name'], lng['uid'], cleanedup_commented) +
115 ("Errors in this po, solved as best as possible!\n\t" + "\n\t".join(errs) if errs else "") + "\n")
118 class UI_OT_i18n_cleanuptranslation_work_repo(Operator):
119 """Clean up i18n working repository (po files)"""
120 bl_idname = "ui.i18n_cleanuptranslation_work_repo"
121 bl_label = "Clean up I18n Work Repository"
123 def execute(self, context):
124 if not hasattr(self, "settings"):
125 self.settings = settings.settings
126 i18n_sett = context.window_manager.i18n_update_settings
127 # 'DEFAULT' and en_US are always valid, fully-translated "languages"!
128 stats = {"DEFAULT": 1.0, "en_US": 1.0}
130 context.window_manager.progress_begin(0, len(i18n_sett.langs) + 1)
131 context.window_manager.progress_update(0)
132 with concurrent.futures.ProcessPoolExecutor() as exctr:
133 num_langs = len(i18n_sett.langs)
134 for progress, _ in enumerate(exctr.map(i18n_cleanuptranslation_work_repo_callback,
135 [dict(lng.items()) for lng in i18n_sett.langs],
136 (self.settings,) * num_langs,
137 chunksize=4)):
138 context.window_manager.progress_update(progress + 1)
140 context.window_manager.progress_end()
142 return {'FINISHED'}
145 def i18n_updatetranslation_blender_repo_callback(lng, settings):
146 reports = []
147 if lng['uid'] in settings.IMPORT_LANGUAGES_SKIP:
148 reports.append("Skipping {} language ({}), edit settings if you want to enable it.".format(lng['name'], lng['uid']))
149 return lng['uid'], 0.0, reports
150 if not lng['use']:
151 reports.append("Skipping {} language ({}).".format(lng['name'], lng['uid']))
152 return lng['uid'], 0.0, reports
153 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
154 errs = po.check(fix=True)
155 reports.append("Processing {} language ({}).\n"
156 "Cleaned up {} commented messages.\n".format(lng['name'], lng['uid'], po.clean_commented()) +
157 ("Errors in this po, solved as best as possible!\n\t" + "\n\t".join(errs) if errs else ""))
158 if lng['uid'] in settings.IMPORT_LANGUAGES_RTL:
159 po.rtl_process()
160 po.write(kind="PO_COMPACT", dest=lng['po_path_blender'])
161 po.update_info()
162 return lng['uid'], po.nbr_trans_msgs / po.nbr_msgs, reports
165 class UI_OT_i18n_updatetranslation_blender_repo(Operator):
166 """Update i18n data (po files) in Blender source code repository"""
167 bl_idname = "ui.i18n_updatetranslation_blender_repo"
168 bl_label = "Update I18n Blender Repository"
170 def execute(self, context):
171 if not hasattr(self, "settings"):
172 self.settings = settings.settings
173 i18n_sett = context.window_manager.i18n_update_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, reports) in enumerate(exctr.map(i18n_updatetranslation_blender_repo_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
187 print("".join(reports) + "\n")
189 print("Generating languages' menu...")
190 context.window_manager.progress_update(progress + 2)
191 languages_menu_lines = utils_languages_menu.gen_menu_file(stats, self.settings)
192 with open(os.path.join(self.settings.BLENDER_I18N_ROOT, self.settings.LANGUAGES_FILE), 'w', encoding="utf8") as f:
193 f.write("\n".join(languages_menu_lines))
194 context.window_manager.progress_end()
196 return {'FINISHED'}
199 class UI_OT_i18n_updatetranslation_statistics(Operator):
200 """Create or extend a 'i18n_info.txt' Text datablock"""
201 """(it will contain statistics and checks about current working repository PO files)"""
202 bl_idname = "ui.i18n_updatetranslation_statistics"
203 bl_label = "Update I18n Statistics"
205 report_name = "i18n_info.txt"
207 def execute(self, context):
208 if not hasattr(self, "settings"):
209 self.settings = settings.settings
210 i18n_sett = context.window_manager.i18n_update_settings
212 buff = io.StringIO()
213 lst = [(lng, lng.po_path) for lng in i18n_sett.langs]
215 context.window_manager.progress_begin(0, len(lst))
216 context.window_manager.progress_update(0)
217 for progress, (lng, path) in enumerate(lst):
218 context.window_manager.progress_update(progress + 1)
219 if not lng.use:
220 print("Skipping {} language ({}).".format(lng.name, lng.uid))
221 continue
222 buff.write("Processing {} language ({}, {}).\n".format(lng.name, lng.uid, path))
223 po = utils_i18n.I18nMessages(uid=lng.uid, kind='PO', src=path, settings=self.settings)
224 po.print_info(prefix=" ", output=buff.write)
225 errs = po.check(fix=False)
226 if errs:
227 buff.write(" WARNING! Po contains following errors:\n")
228 buff.write(" " + "\n ".join(errs))
229 buff.write("\n")
230 buff.write("\n\n")
232 text = None
233 if self.report_name not in bpy.data.texts:
234 text = bpy.data.texts.new(self.report_name)
235 else:
236 text = bpy.data.texts[self.report_name]
237 data = text.as_string()
238 data = data + "\n" + buff.getvalue()
239 text.from_string(data)
240 self.report({'INFO'}, "Info written to %s text datablock!" % self.report_name)
241 context.window_manager.progress_end()
243 return {'FINISHED'}
245 def invoke(self, context, event):
246 wm = context.window_manager
247 return wm.invoke_props_dialog(self)
250 classes = (
251 UI_OT_i18n_updatetranslation_work_repo,
252 UI_OT_i18n_cleanuptranslation_work_repo,
253 UI_OT_i18n_updatetranslation_blender_repo,
254 UI_OT_i18n_updatetranslation_statistics,