diff --git a/__init__.py b/__init__.py index eed4b2c..f49f0c7 100644 --- a/__init__.py +++ b/__init__.py @@ -6,12 +6,14 @@ if "bpy" not in locals(): from .core import register from .core.register import __bl_ordered_classes from .core import properties + from .core import addon_preferences else: import importlib importlib.reload(ui) importlib.reload(core) importlib.reload(functions) importlib.reload(properties) + importlib.reload(addon_preferences) def register(): print("Registering Avatar Toolkit") @@ -26,18 +28,16 @@ def register(): # Register the properties core.register.register_properties() # Register the UI classes - for cls in core.register.__bl_ordered_classes: - print("registering " + str(cls)) - bpy.utils.register_class(cls) - - + for cls in __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(core.register.__bl_ordered_classes): + for cls in reversed(list(__bl_ordered_classes)): bpy.utils.unregister_class(cls) print("unregistering " + str(cls)) core.register.unregister_properties() diff --git a/core/addon_preferences.py b/core/addon_preferences.py new file mode 100644 index 0000000..7949f1d --- /dev/null +++ b/core/addon_preferences.py @@ -0,0 +1,43 @@ +import bpy +import os +import json +from bpy.types import AddonPreferences +from typing import Any, Dict + +# Get the directory of the current file +PREFERENCES_DIR = os.path.dirname(os.path.abspath(__file__)) +PREFERENCES_FILE = os.path.join(PREFERENCES_DIR, "preferences.json") + +def save_preference(key: str, value: Any) -> None: + """Save a single preference to the JSON file.""" + prefs = load_preferences() + prefs[key] = value + with open(PREFERENCES_FILE, 'w') as f: + json.dump(prefs, f, indent=4) + +def load_preferences() -> Dict[str, Any]: + """Load all preferences from the JSON file.""" + if os.path.exists(PREFERENCES_FILE): + with open(PREFERENCES_FILE, 'r') as f: + return json.load(f) + return {} + +def get_preference(key: str, default: Any = None) -> Any: + """Get a single preference from the JSON file.""" + prefs = load_preferences() + return prefs.get(key, default) + +class AvatarToolkitPreferences(AddonPreferences): + bl_idname = __package__.rsplit('.', 1)[0] + + def draw(self, context): + layout = self.layout + layout.label(text="Preferences are managed internally.") + # You can add more UI elements here if needed + +def get_addon_preferences(context): + return context.preferences.addons[AvatarToolkitPreferences.bl_idname].preferences + +# Initialize preferences if the file doesn't exist +if not os.path.exists(PREFERENCES_FILE): + save_preference("language", 0) # Set default language to 0 (auto) \ No newline at end of file diff --git a/core/properties.py b/core/properties.py index 13ef631..9bf108d 100644 --- a/core/properties.py +++ b/core/properties.py @@ -1,16 +1,18 @@ import bpy -from ..functions.translations import t, get_languages_list, update_ui -from ..core.register import register_property -from typing import Tuple +from ..functions.translations import t, get_languages_list, update_language +from ..core.addon_preferences import get_preference def register(): - register_property((bpy.types.Scene, "avatar_toolkit_language", bpy.props.EnumProperty( - name=t("Settings.language.label"), - description=t("Settings.language.desc"), + default_language = get_preference("language", 0) + + bpy.types.Scene.avatar_toolkit_language = bpy.props.EnumProperty( + name=t("Settings.language.label", "Language"), + description=t("Settings.language.desc", "Select the language for the addon"), items=get_languages_list, - default=0, - update=update_ui - ))) + default=default_language, + update=update_language + ) def unregister(): - pass + if hasattr(bpy.types.Scene, "avatar_toolkit_language"): + del bpy.types.Scene.avatar_toolkit_language \ No newline at end of file diff --git a/core/register.py b/core/register.py index c7dc5dc..449050e 100644 --- a/core/register.py +++ b/core/register.py @@ -1,5 +1,5 @@ import bpy -import typing +from typing import List, Type # List to store the classes to register __bl_classes = [] @@ -21,8 +21,11 @@ def register_property(prop): def register_properties(): for prop in __bl_props: - setattr(prop[0], prop[1], prop[2]) - + if isinstance(prop[2], bpy.props._PropertyDeferred): + setattr(prop[0], prop[1], prop[2]) + else: + prop() + def unregister_properties(): for prop in reversed(__bl_props): delattr(prop[0], prop[1]) diff --git a/functions/translations.py b/functions/translations.py index af928fa..c60e886 100644 --- a/functions/translations.py +++ b/functions/translations.py @@ -2,20 +2,24 @@ import os import json import bpy from bpy.app.translations import locale -from ..core.register import register_wrap from typing import Dict, List, Tuple +from ..core.addon_preferences import save_preference, get_preference -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") +# Use __file__ to get the current file's directory +current_dir = os.path.dirname(os.path.abspath(__file__)) +main_dir = os.path.dirname(current_dir) +resources_dir = os.path.join(main_dir, "resources") +translations_dir = os.path.join(resources_dir, "translations") dictionary: Dict[str, str] = dict() languages: List[str] = [] verbose: bool = True -def load_translations() -> None: +def load_translations() -> bool: global dictionary, languages + old_dictionary = dictionary.copy() + dictionary = dict() languages = ["auto"] @@ -25,68 +29,67 @@ def load_translations() -> None: if lang != "auto": languages.append(lang) - # Check if the context and scene are available - if hasattr(bpy.context, "scene"): - # Check if the property exists before trying to access it - if hasattr(bpy.context.scene, "avatar_toolkit_language"): - language_index = bpy.context.scene.avatar_toolkit_language - if isinstance(language_index, str): - language_index = int(language_index) - if language_index == 0: # "auto" - language = bpy.context.preferences.view.language - else: - language = languages[language_index] - else: - language = bpy.context.preferences.view.language + language_index = get_preference("language", 0) + print(f"Loading translations for language index: {language_index}") # Debug print + + if language_index == 0: # "auto" + language = bpy.context.preferences.view.language else: - # Set a default language if the context or scene is not available - language = "en_US" + try: + language = languages[language_index] + except IndexError: + language = bpy.context.preferences.view.language + + print(f"Selected language: {language}") # Debug print 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"] + print(f"Loaded translations: {dictionary}") # Debug print 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"] + print(f"Loaded custom translations: {dictionary}") # Debug print 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"] + print(f"Loaded default translations: {dictionary}") # Debug print else: print("Default translation file 'en_US.json' not found.") -def t(phrase: str, *args, **kwargs) -> str: + return dictionary != old_dictionary + +def t(phrase: str, default: str = None) -> str: output: str = dictionary.get(phrase) if output is None: if verbose: - print('Warning: Unknown phrase: ' + phrase) - return phrase - return output.format(*args, **kwargs) + print(f'Warning: Unknown phrase: {phrase}') + return default if default is not None else phrase + print(f"Translating '{phrase}' to '{output}'") # Debug print + return output + +def get_language_display_name(lang: str) -> str: + if lang == "auto": + return t("Language.auto", "Automatic") + return t(f"Language.{lang}", lang) def get_languages_list(self, context) -> List[Tuple[str, str, str]]: - return [(str(i), lang, f"Use {lang} language") for i, lang in enumerate(languages)] + return [(str(i), get_language_display_name(lang), f"Use {lang} language") for i, lang in enumerate(languages)] -def refresh_translations(): +def update_language(self, context): + print(f"Updating language to: {self.avatar_toolkit_language}") # Debug print + save_preference("language", int(self.avatar_toolkit_language)) load_translations() - # Force a full UI update - for window in bpy.context.window_manager.windows: - for area in window.screen.areas: - area.tag_redraw() - -def update_ui(self, context): - refresh_translations() - # Force Blender to redraw all UI elements - for screen in bpy.data.screens: - for area in screen.areas: - area.tag_redraw() - # Update the Scene to trigger a full UI refresh - bpy.context.scene.update_tag() + # Reload the addon + bpy.ops.script.reload() # Initial load of translations +print("Performing initial load of translations") # Debug print load_translations() diff --git a/resources/translations/en_US.json b/resources/translations/en_US.json index 49b9b23..c9f60cc 100644 --- a/resources/translations/en_US.json +++ b/resources/translations/en_US.json @@ -1,5 +1,8 @@ { "messages": { + "Language.auto": "Automatic", + "Language.en_US": "English", + "Language.ja_JP": "日本語", "Quick_Access.label": "Quick Access", "Quick_Access.import_export.label": "Import/Export", "Quick_Access.options": "Quick Access Options", diff --git a/resources/translations/ja_JP.json b/resources/translations/ja_JP.json index 263544b..0fb43c5 100644 --- a/resources/translations/ja_JP.json +++ b/resources/translations/ja_JP.json @@ -1,5 +1,8 @@ { "messages": { + "Language.auto": "Automatic", + "Language.en_US": "English", + "Language.ja_JP": "日本語", "Quick_Access.label": "クイックアクセス", "Quick_Access.import_export.label": "インポート/エクスポート", "Quick_Access.options": "クイックアクセスオプション",