Import_3ds: Improved distance cue node setup
[blender-addons.git] / ui_translate / update_repo.py
blobcba2f40323abec0d029285ab9e79f3510bdf6f5a
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 "--no_checks",
76 "--settings",
77 self.settings.to_json(),
79 # Not working (UI is not refreshed...).
80 #self.report({'INFO'}, "Extracting messages, this will take some time...")
81 context.window_manager.progress_update(1)
82 ret = subprocess.run(cmmd, env=env)
83 if ret.returncode != 0:
84 self.report({'ERROR'}, "Message extraction process failed!")
85 context.window_manager.progress_end()
86 return {'CANCELLED'}
88 # Now we should have a valid POT file, we have to merge it in all languages po's...
89 with concurrent.futures.ProcessPoolExecutor() as exctr:
90 pot = utils_i18n.I18nMessages(kind='PO', src=self.settings.FILE_NAME_POT, settings=self.settings)
91 num_langs = len(i18n_sett.langs)
92 for progress, _ in enumerate(exctr.map(i18n_updatetranslation_work_repo_callback,
93 (pot,) * num_langs,
94 [dict(lng.items()) for lng in i18n_sett.langs],
95 (self.settings,) * num_langs,
96 chunksize=4)):
97 context.window_manager.progress_update(progress + 2)
98 context.window_manager.progress_end()
99 return {'FINISHED'}
101 def invoke(self, context, event):
102 wm = context.window_manager
103 return wm.invoke_props_dialog(self)
106 def i18n_cleanuptranslation_work_repo_callback(lng, settings):
107 if not lng['use']:
108 print("Skipping {} language ({}).".format(lng['name'], lng['uid']))
109 return
110 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
111 errs = po.check(fix=True)
112 cleanedup_commented = po.clean_commented()
113 po.write(kind="PO", dest=lng['po_path'])
114 print("Processing {} language ({}).\n"
115 "Cleaned up {} commented messages.\n".format(lng['name'], lng['uid'], cleanedup_commented) +
116 ("Errors in this po, solved as best as possible!\n\t" + "\n\t".join(errs) if errs else "") + "\n")
119 class UI_OT_i18n_cleanuptranslation_work_repo(Operator):
120 """Clean up i18n working repository (po files)"""
121 bl_idname = "ui.i18n_cleanuptranslation_work_repo"
122 bl_label = "Clean up I18n Work Repository"
124 def execute(self, context):
125 if not hasattr(self, "settings"):
126 self.settings = settings.settings
127 i18n_sett = context.window_manager.i18n_update_settings
128 # 'DEFAULT' and en_US are always valid, fully-translated "languages"!
129 stats = {"DEFAULT": 1.0, "en_US": 1.0}
131 context.window_manager.progress_begin(0, len(i18n_sett.langs) + 1)
132 context.window_manager.progress_update(0)
133 with concurrent.futures.ProcessPoolExecutor() as exctr:
134 num_langs = len(i18n_sett.langs)
135 for progress, _ in enumerate(exctr.map(i18n_cleanuptranslation_work_repo_callback,
136 [dict(lng.items()) for lng in i18n_sett.langs],
137 (self.settings,) * num_langs,
138 chunksize=4)):
139 context.window_manager.progress_update(progress + 1)
141 context.window_manager.progress_end()
143 return {'FINISHED'}
146 def i18n_updatetranslation_blender_repo_callback(lng, settings):
147 reports = []
148 if lng['uid'] in settings.IMPORT_LANGUAGES_SKIP:
149 reports.append("Skipping {} language ({}), edit settings if you want to enable it.".format(lng['name'], lng['uid']))
150 return lng['uid'], 0.0, reports
151 if not lng['use']:
152 reports.append("Skipping {} language ({}).".format(lng['name'], lng['uid']))
153 return lng['uid'], 0.0, reports
154 po = utils_i18n.I18nMessages(uid=lng['uid'], kind='PO', src=lng['po_path'], settings=settings)
155 errs = po.check(fix=True)
156 reports.append("Processing {} language ({}).\n"
157 "Cleaned up {} commented messages.\n".format(lng['name'], lng['uid'], po.clean_commented()) +
158 ("Errors in this po, solved as best as possible!\n\t" + "\n\t".join(errs) if errs else ""))
159 if lng['uid'] in settings.IMPORT_LANGUAGES_RTL:
160 po.rtl_process()
161 po.write(kind="PO_COMPACT", dest=lng['po_path_blender'])
162 po.update_info()
163 return lng['uid'], po.nbr_trans_msgs / po.nbr_msgs, reports
166 class UI_OT_i18n_updatetranslation_blender_repo(Operator):
167 """Update i18n data (po files) in Blender source code repository"""
168 bl_idname = "ui.i18n_updatetranslation_blender_repo"
169 bl_label = "Update I18n Blender Repository"
171 def execute(self, context):
172 if not hasattr(self, "settings"):
173 self.settings = settings.settings
174 i18n_sett = context.window_manager.i18n_update_settings
175 # 'DEFAULT' and en_US are always valid, fully-translated "languages"!
176 stats = {"DEFAULT": 1.0, "en_US": 1.0}
178 context.window_manager.progress_begin(0, len(i18n_sett.langs) + 1)
179 context.window_manager.progress_update(0)
180 with concurrent.futures.ProcessPoolExecutor() as exctr:
181 num_langs = len(i18n_sett.langs)
182 for progress, (lng_uid, stats_val, reports) in enumerate(exctr.map(i18n_updatetranslation_blender_repo_callback,
183 [dict(lng.items()) for lng in i18n_sett.langs],
184 (self.settings,) * num_langs,
185 chunksize=4)):
186 context.window_manager.progress_update(progress + 1)
187 stats[lng_uid] = stats_val
188 print("".join(reports) + "\n")
190 print("Generating languages' menu...")
191 context.window_manager.progress_update(progress + 2)
192 languages_menu_lines = utils_languages_menu.gen_menu_file(stats, self.settings)
193 with open(os.path.join(self.settings.BLENDER_I18N_ROOT, self.settings.LANGUAGES_FILE), 'w', encoding="utf8") as f:
194 f.write("\n".join(languages_menu_lines))
195 context.window_manager.progress_end()
197 return {'FINISHED'}
200 class UI_OT_i18n_updatetranslation_statistics(Operator):
201 """Create or extend a 'i18n_info.txt' Text datablock"""
202 """(it will contain statistics and checks about current working repository PO files)"""
203 bl_idname = "ui.i18n_updatetranslation_statistics"
204 bl_label = "Update I18n Statistics"
206 report_name = "i18n_info.txt"
208 def execute(self, context):
209 if not hasattr(self, "settings"):
210 self.settings = settings.settings
211 i18n_sett = context.window_manager.i18n_update_settings
213 buff = io.StringIO()
214 lst = [(lng, lng.po_path) for lng in i18n_sett.langs]
216 context.window_manager.progress_begin(0, len(lst))
217 context.window_manager.progress_update(0)
218 for progress, (lng, path) in enumerate(lst):
219 context.window_manager.progress_update(progress + 1)
220 if not lng.use:
221 print("Skipping {} language ({}).".format(lng.name, lng.uid))
222 continue
223 buff.write("Processing {} language ({}, {}).\n".format(lng.name, lng.uid, path))
224 po = utils_i18n.I18nMessages(uid=lng.uid, kind='PO', src=path, settings=self.settings)
225 po.print_info(prefix=" ", output=buff.write)
226 errs = po.check(fix=False)
227 if errs:
228 buff.write(" WARNING! Po contains following errors:\n")
229 buff.write(" " + "\n ".join(errs))
230 buff.write("\n")
231 buff.write("\n\n")
233 text = None
234 if self.report_name not in bpy.data.texts:
235 text = bpy.data.texts.new(self.report_name)
236 else:
237 text = bpy.data.texts[self.report_name]
238 data = text.as_string()
239 data = data + "\n" + buff.getvalue()
240 text.from_string(data)
241 self.report({'INFO'}, "Info written to %s text datablock!" % self.report_name)
242 context.window_manager.progress_end()
244 return {'FINISHED'}
246 def invoke(self, context, event):
247 wm = context.window_manager
248 return wm.invoke_props_dialog(self)
251 classes = (
252 UI_OT_i18n_updatetranslation_work_repo,
253 UI_OT_i18n_cleanuptranslation_work_repo,
254 UI_OT_i18n_updatetranslation_blender_repo,
255 UI_OT_i18n_updatetranslation_statistics,