From 01f8363e0795ede81636b02784812a249ad22b9f Mon Sep 17 00:00:00 2001 From: Yusarina Date: Wed, 19 Jun 2024 02:27:07 +0100 Subject: [PATCH] Start of Translations - This is the start of the translations system, does not yet work as I can't seem to get it to swap languages, I too tired today to fix it but it's a start. - It also adds properties registration as well. --- __init__.py | 27 ++++++------- core/properties.py | 15 ++++++++ core/register.py | 13 +++++++ functions/translations.py | 64 +++++++++++++++++++++++++++++++ resources/translations/en_US.json | 9 +++++ resources/translations/ja_JP.json | 8 ++++ ui/__init__.py | 4 -- ui/settings.py | 19 +++++++++ 8 files changed, 140 insertions(+), 19 deletions(-) create mode 100644 core/properties.py create mode 100644 functions/translations.py create mode 100644 resources/translations/en_US.json create mode 100644 resources/translations/ja_JP.json create mode 100644 ui/settings.py diff --git a/__init__.py b/__init__.py index 4f93020..a25163f 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,3 @@ - - if "bpy" not in locals(): import bpy from . import ui @@ -7,28 +5,27 @@ if "bpy" not in locals(): from . import functions from .core import register from .core.register import __bl_ordered_classes + from .core import properties else: import importlib importlib.reload(ui) importlib.reload(core) importlib.reload(functions) - + importlib.reload(properties) def register(): print("Registering Avatar Toolkit") - # Order the classes before registration + properties.register() core.register.order_classes() - # Register the UI classes - - # Iterate over the classes to register and register them - for cls in __bl_ordered_classes: - print("registering" + str(cls)) - bpy.utils.register_class(cls) + core.register.register_properties() + for cls in core.register.__bl_ordered_classes: + print("registering " + str(cls)) + bpy.utils.register_class(cls) + def unregister(): print("Unregistering Avatar Toolkit") - # Unregister the UI classes - - # Iterate over the classes to unregister in reverse order and unregister them - for cls in reversed(list(__bl_ordered_classes)): + for cls in reversed(core.register.__bl_ordered_classes): bpy.utils.unregister_class(cls) - print("unregistering" + str(cls)) + print("unregistering " + str(cls)) + core.register.unregister_properties() + properties.unregister() diff --git a/core/properties.py b/core/properties.py new file mode 100644 index 0000000..1e4f319 --- /dev/null +++ b/core/properties.py @@ -0,0 +1,15 @@ +import bpy +from ..functions.translations import t, get_languages_list, update_ui +from ..core.register import register_property +from typing import Tuple + +def register() -> None: + register_property((bpy.types.Scene, "language", bpy.props.EnumProperty( + name=t("Settings.language.label"), + description=t("Settings.language.desc"), + items=get_languages_list, + update=update_ui + ))) + +def unregister() -> None: + pass diff --git a/core/register.py b/core/register.py index 0d91a8a..c7dc5dc 100644 --- a/core/register.py +++ b/core/register.py @@ -5,6 +5,8 @@ import typing __bl_classes = [] # List to store the ordered classes for registration __bl_ordered_classes = [] +# List to store props to register +__bl_props = [] def register_wrap(cls): # Check if the class has a 'bl_rna' attribute (indicating it's a Blender class) @@ -13,6 +15,17 @@ def register_wrap(cls): __bl_classes.append(cls) return cls +# Register all properties +def register_property(prop): + __bl_props.append(prop) + +def register_properties(): + for prop in __bl_props: + setattr(prop[0], prop[1], prop[2]) + +def unregister_properties(): + for prop in reversed(__bl_props): + delattr(prop[0], prop[1]) #- @989onan had to add this from Cats. This is extremely important else you will be screamed at by register order issues! # Find order to register to solve dependencies diff --git a/functions/translations.py b/functions/translations.py new file mode 100644 index 0000000..4f84a1d --- /dev/null +++ b/functions/translations.py @@ -0,0 +1,64 @@ +import os +import json +import bpy +from bpy.app.translations import locale +from ..core.register import register_wrap +from typing import Dict, List, Tuple + +main_dir: str = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +resources_dir: str = os.path.join(main_dir, "resources") +translations_dir: str = os.path.join(resources_dir, "translations") + +dictionary: Dict[str, str] = dict() +languages: List[str] = [] +verbose: bool = True + +def load_translations() -> None: + global dictionary, languages + + dictionary = dict() + languages = ["auto"] + + language: str = bpy.context.preferences.view.language + + for i in os.listdir(translations_dir): + languages.append(i.split(".")[0]) + + translation_file: str = os.path.join(translations_dir, language + ".json") + if os.path.exists(translation_file): + with open(translation_file, 'r', encoding='utf-8') as file: + dictionary = json.load(file)["messages"] + else: + custom_language: str = language.split("_")[0] + custom_translation_file: str = os.path.join(translations_dir, custom_language + ".json") + if os.path.exists(custom_translation_file): + with open(custom_translation_file, 'r', encoding='utf-8') as file: + dictionary = json.load(file)["messages"] + else: + print(f"Translation file not found for language: {language}") + default_file: str = os.path.join(translations_dir, "en_US.json") + if os.path.exists(default_file): + with open(default_file, 'r', encoding='utf-8') as file: + dictionary = json.load(file)["messages"] + else: + print("Default translation file 'en_US.json' not found.") + +def t(phrase: str, *args, **kwargs) -> str: + output: str = dictionary.get(phrase) + if output is None: + if verbose: + print('Warning: Unknown phrase: ' + phrase) + return phrase + return output.format(*args, **kwargs) + +def get_languages_list(self, context) -> List[Tuple[str, str, str]]: + choices: List[Tuple[str, str, str]] = [] + for language in languages: + choices.append((language, language, language)) + return choices + +def update_ui(self, context) -> None: + load_translations() + bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) + +load_translations() diff --git a/resources/translations/en_US.json b/resources/translations/en_US.json new file mode 100644 index 0000000..ed97a90 --- /dev/null +++ b/resources/translations/en_US.json @@ -0,0 +1,9 @@ +{ + "messages": { + "Settings.label": "Settings", + "Settings.language.label": "Language", + "Settings.language.desc": "Select the language for the addon's UI" + } + } + + \ No newline at end of file diff --git a/resources/translations/ja_JP.json b/resources/translations/ja_JP.json new file mode 100644 index 0000000..1c53c39 --- /dev/null +++ b/resources/translations/ja_JP.json @@ -0,0 +1,8 @@ +{ + "messages": { + "Settings.label": "Settings Ja Test", + "Settings.language.label": "Language Ja Test", + "Settings.language.desc": "Select the language for the addon's UI Ja Test" + } + } + \ No newline at end of file diff --git a/ui/__init__.py b/ui/__init__.py index cc0041c..7db3379 100644 --- a/ui/__init__.py +++ b/ui/__init__.py @@ -13,7 +13,3 @@ else: for module_name in [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]: print("reloading " +module_name) exec("importlib.reload("+module_name+")") - - - - diff --git a/ui/settings.py b/ui/settings.py new file mode 100644 index 0000000..bd298ed --- /dev/null +++ b/ui/settings.py @@ -0,0 +1,19 @@ +import bpy +from ..core.register import register_wrap +from .panel import AvatarToolkitPanel +from ..functions.translations import t + +@register_wrap +class AvatarToolkitSettingsPanel(bpy.types.Panel): + bl_label = t("Settings.label") + bl_idname = "OBJECT_PT_avatar_toolkit_settings" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Avatar Toolkit" + bl_parent_id = "OBJECT_PT_avatar_toolkit" + + def draw(self, context): + layout = self.layout + props = context.scene + + layout.prop(props, "language")