1 # SPDX-FileCopyrightText: 2013-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
7 importlib
.reload(settings
)
8 importlib
.reload(utils_i18n
)
9 importlib
.reload(utils_languages_menu
)
12 from bpy
.types
import Operator
13 from bpy
.props
import (
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
29 # Operators ###################################################################
31 def i18n_updatetranslation_work_repo_callback(pot
, lng
, settings
):
34 if os
.path
.isfile(lng
['po_path']):
35 po
= utils_i18n
.I18nMessages(uid
=lng
['uid'], kind
='PO', src
=lng
['po_path'], settings
=settings
)
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 Repo"
48 use_skip_pot_gen
: BoolProperty(
50 description
="Skip POT file generation",
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!
73 os
.path
.join(os
.path
.dirname(utils_i18n
.__file
__), "bl_extract_messages.py"),
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()
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
,
93 [dict(lng
.items()) for lng
in i18n_sett
.langs
],
94 (self
.settings
,) * num_langs
,
96 context
.window_manager
.progress_update(progress
+ 2)
97 context
.window_manager
.progress_end()
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
):
107 print("Skipping {} language ({}).".format(lng
['name'], lng
['uid']))
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 Repo"
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
,
138 context
.window_manager
.progress_update(progress
+ 1)
140 context
.window_manager
.progress_end()
145 def i18n_updatetranslation_blender_repo_callback(lng
, settings
):
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
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
:
160 po
.write(kind
="PO_COMPACT", dest
=lng
['po_path_blender'])
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 Repo"
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
,
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()
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
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)
220 print("Skipping {} language ({}).".format(lng
.name
, lng
.uid
))
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)
227 buff
.write(" WARNING! Po contains following errors:\n")
228 buff
.write(" " + "\n ".join(errs
))
233 if self
.report_name
not in bpy
.data
.texts
:
234 text
= bpy
.data
.texts
.new(self
.report_name
)
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()
245 def invoke(self
, context
, event
):
246 wm
= context
.window_manager
247 return wm
.invoke_props_dialog(self
)
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
,