drop bpy related translations.py<->.po stuff as we won't be using it

This commit is contained in:
Andrej730
2024-01-19 14:22:58 +05:00
parent d1e2009d78
commit a936d56bf8
+26 -280
View File
@@ -2,6 +2,7 @@ try:
import bpy import bpy
import bl_i18n_utils import bl_i18n_utils
import addon_utils import addon_utils
BPY_IS_LOADED = True BPY_IS_LOADED = True
except ModuleNotFoundError: except ModuleNotFoundError:
BPY_IS_LOADED = False BPY_IS_LOADED = False
@@ -12,8 +13,8 @@ import importlib
import os import os
import re import re
from pathlib import Path from pathlib import Path
from dataclasses import dataclass from dataclasses import dataclass, field
from typing import Dict from typing import Dict, List, Optional
bl_info = { bl_info = {
"name": "BlenderBIM Translations", "name": "BlenderBIM Translations",
@@ -65,9 +66,9 @@ def rearrange_files_for_po_import(po_dir_path: Path, temp_directory: tempfile.Te
class Message: class Message:
msgid: str msgid: str
msgctxt: str | None msgctxt: str | None
sources: list[str] sources: Optional[List[str]] = field(default_factory=list)
# mapping languages to translated strings # mapping languages to translated strings
translations: Dict[str, str] translations: Optional[Dict[str, str]] = field(default_factory=dict)
def blenderbim_strings_parse(addon_directory=None, po_directory=None): def blenderbim_strings_parse(addon_directory=None, po_directory=None):
@@ -121,7 +122,7 @@ def blenderbim_strings_parse(addon_directory=None, po_directory=None):
ctx = None ctx = None
message = matched_dict.get(string) message = matched_dict.get(string)
if message is None: if message is None:
message = Message(string, ctx, []) message = Message(string, ctx)
matched_dict[string] = message matched_dict[string] = message
elif ctx != message.msgctxt and False: elif ctx != message.msgctxt and False:
print( print(
@@ -208,128 +209,8 @@ def update_translations_from_po(po_directory: Path, translations_module: Path):
fo.write("\n".join(ret)) fo.write("\n".join(ret))
def dump_py_messages_monkey_patch(msgs, reports, addons, settings, addons_only=False):
ignore_addon_dirs = ["libs"]
def _get_files(path, ignore_dirs=tuple()):
if not os.path.exists(path):
return []
if os.path.isdir(path):
files = []
for dpath, subdirs, fnames in os.walk(path, topdown=True, followlinks=True):
if Path(dpath) in ignore_dirs:
subdirs.clear() # skip walking through `subdirs`
continue
for fn in fnames:
if not fn.endswith(".py"):
continue
if fn.startswith("_") and fn != "__init__.py":
continue
files.append(os.path.join(dpath, fn))
return files
return [path]
files = []
if not addons_only:
for path in settings.CUSTOM_PY_UI_FILES:
for root in (bpy.utils.resource_path(t) for t in ("USER", "LOCAL", "SYSTEM")):
files += _get_files(os.path.join(root, path))
# Add all given addons.
for mod in addons:
fn = mod.__file__
if os.path.basename(fn) == "__init__.py":
parent_dir = Path(fn).parent
ignore_dirs = [parent_dir / dpath for dpath in ignore_addon_dirs]
files += _get_files(os.path.dirname(fn), ignore_dirs=ignore_dirs)
else:
files.append(fn)
bl_i18n_utils.bl_extract_messages.dump_py_messages_from_files(msgs, reports, sorted(files), settings)
def dump_addon_messages(module_name, do_checks, settings):
import datetime
import addon_utils
import bl_i18n_utils.utils as utils
from bl_i18n_utils.bl_extract_messages import (
_gen_reports,
_gen_check_ctxt,
dump_rna_messages,
_diff_check_ctxt,
dump_py_messages,
dump_addon_bl_info,
print_info,
)
# Enable our addon.
ver = module_name
rev = 0
date = datetime.datetime.now()
pot = utils.I18nMessages.gen_empty_messages(
settings.PARSER_TEMPLATE_ID, ver, rev, date, date.year, settings=settings
)
msgs = pot.msgs
minus_pot = utils.I18nMessages.gen_empty_messages(
settings.PARSER_TEMPLATE_ID, ver, rev, date, date.year, settings=settings
)
minus_msgs = minus_pot.msgs
check_ctxt = _gen_check_ctxt(settings) if do_checks else None
minus_check_ctxt = _gen_check_ctxt(settings) if do_checks else None
# Get strings from RNA, our addon being disabled
print("D")
reports = _gen_reports(check_ctxt)
print("E")
dump_rna_messages(minus_msgs, reports, settings)
print("F")
# Now enable our addon, and re-scan RNA.
addon = utils.enable_addons(addons={module_name})[0]
print("A")
reports["check_ctxt"] = minus_check_ctxt
print("B")
dump_rna_messages(msgs, reports, settings)
print("C")
# and make the diff!
for key in minus_msgs:
if key != settings.PO_HEADER_KEY:
if key in msgs:
del msgs[key]
else:
# This should not happen, but some messages seem to have
# leaked on add-on unregister and register?
print(f"Key not found in msgs: {key}")
if check_ctxt:
_diff_check_ctxt(check_ctxt, minus_check_ctxt)
# and we are done with those!
del minus_pot
del minus_msgs
del minus_check_ctxt
# get strings from UI layout definitions text="..." args
reports["check_ctxt"] = check_ctxt
dump_py_messages(msgs, reports, {addon}, settings, addons_only=True)
# Get strings from the addon's bl_info
dump_addon_bl_info(msgs, reports, addon, settings)
pot.unescape() # Strings gathered in py/C source code may contain escaped chars...
print_info(reports, pot)
print("Finished extracting UI messages!")
return pot
if BPY_IS_LOADED: if BPY_IS_LOADED:
class SetupTranslationUI(bpy.types.Operator): class SetupTranslationUI(bpy.types.Operator):
bl_idname = "bim.setup_translation_ui" bl_idname = "bim.setup_translation_ui"
bl_label = "Setup Translation UI" bl_label = "Setup Translation UI"
@@ -401,140 +282,45 @@ if BPY_IS_LOADED:
f" - {ui_translate_settings.BLENDER_I18N_PO_DIR}\n" f" - {ui_translate_settings.BLENDER_I18N_PO_DIR}\n"
) )
# we monkey patch `bl_i18n_utils.bl_extract_messages.dump_py_messages`
# as it's doesn't support ignoring folders
# and we need it, otherwise translation addon will try to parse strings
# from all BlenderBIM dependencies :O
bl_i18n_utils.bl_extract_messages.dump_py_messages = dump_py_messages_monkey_patch
bl_i18n_utils.bl_extract_messages.dump_addon_messages = dump_addon_messages
# setup selected languages
for lang in i18n_settings.langs:
lang.use = lang.uid == context.preferences.view.language
global TRANSLATION_UI_IS_LOADED global TRANSLATION_UI_IS_LOADED
TRANSLATION_UI_IS_LOADED = True TRANSLATION_UI_IS_LOADED = True
return {"FINISHED"} return {"FINISHED"}
class ParseBlenderBIMStrings(bpy.types.Operator):
class ReloadPyTranslations(bpy.types.Operator): bl_idname = "bim.parse_blenderbim_strings"
bl_idname = "bim.reload_py_translations" bl_label = "Parse BlenderBIM strings To .pot"
bl_label = "Reload Py Translations" bl_description = "Parse strings from BlenderBIM and save to .pot"
bl_description = "Parse strings from Blender objects of the addon to `translations.py`"
bl_options = set()
use_bbim_parser: bpy.props.BoolProperty(
name="Use BlenderBIM Parser", description="As oppose to Blender parser", default=True
)
def execute(self, context):
if self.use_bbim_parser:
blenderbim_strings_parse()
else:
if is_addon_loaded(ADDON_NAME):
# ref: https://projects.blender.org/blender/blender/issues/116579
raise Exception(
f"'{ADDON_NAME}' addon is enabled.\n"
"Need to disable it, restart Blender and start reloading translations again.\n"
"Otherwise some strings to translate might get lost due Blender bug."
)
bpy.ops.ui.i18n_addon_translation_update("INVOKE_DEFAULT", module_name=ADDON_NAME)
self.report({"INFO"}, "Translations py data is saved.")
return {"FINISHED"}
class ConvertTranslationsToPo(bpy.types.Operator):
bl_idname = "bim.convert_translations_to_po"
bl_label = "Convert Translations To .po"
bl_description = (
"Extract current translation strings from translation.py to .po files and saves them to I18n Branches directory"
)
bl_options = set() bl_options = set()
def execute(self, context): def execute(self, context):
temp_po_dir = tempfile.TemporaryDirectory() blenderbim_strings_parse()
branches_dir = get_branches_directory() self.report({"INFO"}, "String were parsed and saved to .pot file.")
bpy.ops.ui.i18n_addon_translation_export(
module_name=ADDON_NAME, directory=temp_po_dir.name, use_export_pot=True
)
# NOTE: we use I18n branches directory
# because it is the directory that later will be used to edit translation from UI.
# I18n directory also has a bit different format then `ui_translate.export/import`,
# every .po file has a parent folder with the same name
# so we rearrange the exported data that way
for file in Path(temp_po_dir.name).iterdir():
branches_subdir = branches_dir / file.stem
branches_subdir.mkdir(exist_ok=True)
file.replace(branches_subdir / file.name)
temp_po_dir.cleanup()
self.report({"INFO"}, f"Translations .po files are saved to {branches_dir}.")
return {"FINISHED"} return {"FINISHED"}
class UpdateTranslationsFromPo(bpy.types.Operator): class UpdateTranslationsFromPo(bpy.types.Operator):
bl_idname = "bim.update_translations_from_po" bl_idname = "bim.update_translations_from_po"
bl_label = "Update Translations From .po" bl_label = "Update Translations From .po"
bl_description = ( bl_description = (
"Load translation strings from po files at I18n Branches\n" "Load translation strings from po files at I18n Branches back to translations.py\n"
"back to translations.py (they also get copied to `locale` directory of the addon)" "Also updates current addon translations in UI"
)
use_bbim_parser: bpy.props.BoolProperty(
name="Use BlenderBIM Parser", description="As oppose to Blender parser", default=True
) )
bl_options = set() bl_options = set()
def execute(self, context): def execute(self, context):
branches_dir = get_branches_directory() branches_dir = get_branches_directory()
if self.use_bbim_parser: update_translations_from_po(branches_dir, get_addon_directory())
update_translations_from_po(branches_dir, get_addon_directory())
else:
temp_po_dir = tempfile.TemporaryDirectory()
rearrange_files_for_po_import(branches_dir, temp_po_dir)
bpy.ops.ui.i18n_addon_translation_import( if is_addon_loaded(ADDON_NAME):
module_name=ADDON_NAME, bpy.app.translations.unregister(ADDON_NAME)
directory=temp_po_dir.name, addon_module = importlib.import_module(ADDON_NAME)
) translations_module = getattr(addon_module, "translations")
importlib.reload(translations_module)
bpy.app.translations.register(ADDON_NAME, translations_module.translations_dict)
# update translations in current Blender session
if is_addon_loaded(ADDON_NAME):
bpy.app.translations.unregister(ADDON_NAME)
addon_module = importlib.import_module(ADDON_NAME)
translations_module = getattr(addon_module, "translations")
importlib.reload(translations_module)
bpy.app.translations.register(ADDON_NAME, translations_module.translations_dict)
self.report({"INFO"}, f"Addon's translation updated from .po in {branches_dir}") self.report({"INFO"}, f"Addon's translation updated from .po in {branches_dir}")
return {"FINISHED"} return {"FINISHED"}
class DisableEnableAddon(bpy.types.Operator):
bl_idname = "bim.disable_enable_addon"
bl_label = "Disable/Enable addon"
bl_description = "Will enable addon if it's disabled, will disable it and restart Blender otherwise"
bl_options = set()
def execute(self, context):
if not is_addon_loaded(ADDON_NAME):
addon_utils.enable("blenderbim", default_set=True)
return {"FINISHED"}
import os
import subprocess
addon_utils.disable("blenderbim", default_set=True)
blender_exe = bpy.app.binary_path
head, tail = os.path.split(blender_exe)
blender_launcher = os.path.join(head, "blender-launcher.exe")
subprocess.run([blender_launcher, "-con", "--python-expr", "import bpy; bpy.ops.wm.recover_last_session()"])
bpy.ops.wm.quit_blender()
return {"FINISHED"}
class BBIM_PT_translations(bpy.types.Panel): class BBIM_PT_translations(bpy.types.Panel):
bl_label = "BlenderBIM Translations" bl_label = "BlenderBIM Translations"
bl_idname = "BBIM_PT_translations" bl_idname = "BBIM_PT_translations"
@@ -545,75 +331,35 @@ if BPY_IS_LOADED:
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.prop(context.preferences.view, "language") layout.prop(context.preferences.view, "language")
layout.prop(context.preferences.filepaths, "i18n_branches_directory")
if not TRANSLATION_UI_IS_LOADED: if not TRANSLATION_UI_IS_LOADED:
layout.operator("bim.setup_translation_ui") layout.operator("bim.setup_translation_ui")
layout.prop(context.preferences.filepaths, "i18n_branches_directory", text="I18n branches directory")
return return
layout.label(text="Developer UI:") layout.label(text="Developer UI:")
layout.operator("bim.reload_py_translations", icon="FILE_REFRESH") layout.operator("bim.parse_blenderbim_strings", icon="FILE_REFRESH")
# blender restart is disabled as we'll parse strings with BBIM parser layout.separator()
addon_enabled = is_addon_loaded(ADDON_NAME)
row = layout.row()
row.operator(
"bim.disable_enable_addon",
icon="QUIT" if addon_enabled else "PLUGIN",
text="Disable Addon And Restart Blender" if addon_enabled else "Enable Addon",
)
row.enabled = False
layout.operator("bim.convert_translations_to_po", icon="EXPORT")
layout.separator(factor=3)
layout.label(text="Translator UI:") layout.label(text="Translator UI:")
layout.prop(context.preferences.filepaths, "i18n_branches_directory")
layout.operator("bim.update_translations_from_po", icon="IMPORT") layout.operator("bim.update_translations_from_po", icon="IMPORT")
classes = ( classes = (
ReloadPyTranslations, ParseBlenderBIMStrings,
ConvertTranslationsToPo,
UpdateTranslationsFromPo, UpdateTranslationsFromPo,
SetupTranslationUI, SetupTranslationUI,
DisableEnableAddon,
BBIM_PT_translations, BBIM_PT_translations,
) )
def register(): def register():
for cls in classes: for cls in classes:
bpy.utils.register_class(cls) bpy.utils.register_class(cls)
def unregister(): def unregister():
for cls in classes: for cls in classes:
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)
def bpy_update_translations_from_po(po_directory: Path, translations_module: Path):
from bl_i18n_utils.settings import I18nSettings
import bl_i18n_utils.utils as utils_i18n
temp_po_dir = tempfile.TemporaryDirectory()
rearrange_files_for_po_import(po_directory, temp_po_dir)
settings = I18nSettings()
trans = utils_i18n.I18n(kind="PY", src=translations_module.as_posix(), settings=settings)
po_files = dict(utils_i18n.get_po_files_from_dir(temp_po_dir.name))
for po_uid, po_filepath in po_files.items():
po_uid = po_uid[0]
msgs = utils_i18n.I18nMessages(uid=po_uid, kind="PO", key=po_uid, src=po_filepath, settings=settings)
if po_uid in trans.trans:
trans.trans[po_uid].merge(msgs, replace=True)
else:
trans.trans[po_uid] = msgs
trans.write(kind="PY")
if __name__ == "__main__": if __name__ == "__main__":
# Example: # Example:
# py src/blenderbim/scripts/bbim_translations.py -i "C:/blenderbim-translations" -o "C:/Blender/4.0/scripts/addons/blenderbim/translations.py" # py src/blenderbim/scripts/bbim_translations.py -i "C:/blenderbim-translations" -o "C:/Blender/4.0/scripts/addons/blenderbim/translations.py"