Source code for mindroot.coreplugins.l8n.mod

import os
import re
import json
from pathlib import Path

# Try to import from lib first (for same instance), fallback to mindroot.lib
try:
    from lib.providers.commands import command, command_manager
    print("l8n: Using lib.providers.commands (same instance)")
except ImportError:
    from mindroot.lib.providers.commands import command, command_manager
    print("l8n: Using mindroot.lib.providers.commands (fallback)")

from .utils import extract_plugin_root, get_localized_file_path, load_plugin_translations, get_plugin_translations_path
from mindroot.lib.utils.debug import debug_box

debug_box("l8n: Top of mod.py")

from .l8n_constants import *

[docs] def save_plugin_translations(plugin_path: str, translations: dict): """Save translations for a specific plugin to disk.""" translations_file = get_plugin_translations_path(plugin_path) try: # Ensure directory exists translations_file.parent.mkdir(parents=True, exist_ok=True) with open(translations_file, 'w', encoding='utf-8') as f: json.dump(translations, f, indent=2, ensure_ascii=False) return True except Exception as e: print(f"Warning: Could not save translations to {translations_file}: {e}") return False
debug_box("l8n: defining command") # Debug: Check if command_manager has functions debug_box(f"l8n: command_manager has {len(command_manager.functions)} functions before registration") debug_box(f"l8n: command_manager instance ID: {id(command_manager)}")
[docs] @command() async def write_localized_file(original_path: str, content: str, context=None): """ Write a localized version of a file with static placeholders. This command creates a localized version of a template or source file with __TRANSLATE_key__ placeholders that will be replaced with actual translations when the file is loaded. PLACEHOLDER FORMAT RULES: - Always use the exact format: __TRANSLATE_key_name__ - Must start with __TRANSLATE_ and end with __ - Use lowercase letters, numbers, and underscores only for the key name - Use descriptive, hierarchical key names like section_element or buttons_save - NO spaces, hyphens, or special characters in key names Args: original_path: Absolute path to the original file content: File content with __TRANSLATE_key__ placeholders context: Command context (optional) Examples: await write_localized_file( "/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2", "<h1>__TRANSLATE_chat_title__</h1><button>__TRANSLATE_buttons_send__</button>" ) await write_localized_file( "/some/path/src/my_plugin/templates/dashboard.jinja2", "<div>__TRANSLATE_dashboard_welcome__</div>" ) """ try: localized_path = get_localized_file_path(original_path) # Create directory if it doesn't exist localized_path.parent.mkdir(parents=True, exist_ok=True) # Write the content with open(localized_path, 'w', encoding='utf-8') as f: f.write(content) return f"Localized file written to: {localized_path}" except Exception as e: return f"Error writing localized file: {str(e)}"
[docs] @command() async def append_localized_file(original_path: str, content: str, context=None): """ Append content to an existing localized file. Use this for large files that need to be built incrementally. Follow the same placeholder format rules as write_localized_file. Args: original_path: Absolute path to the original file content: Content to append with __TRANSLATE_key__ placeholders context: Command context (optional) Example: # First write the beginning await write_localized_file( "/path/to/large_template.jinja2", "<html><head><title>__TRANSLATE_page_title__</title></head>" ) # Then append more sections await append_localized_file( "/path/to/large_template.jinja2", "<body><h1>__TRANSLATE_main_heading__</h1></body></html>" ) """ try: localized_path = get_localized_file_path(original_path) # Create directory if it doesn't exist localized_path.parent.mkdir(parents=True, exist_ok=True) # Append the content with open(localized_path, 'a', encoding='utf-8') as f: f.write(content) return f"Content appended to: {localized_path}" except Exception as e: return f"Error appending to localized file: {str(e)}"
[docs] @command() async def set_translations(original_path: str, language: str, translations: dict, context=None): """ Set translations for a specific language and plugin. This command stores the translation mappings that will be used to replace __TRANSLATE_key__ placeholders in localized files. Translations are stored per plugin based on the provided file path. Args: original_path: Absolute path to a file in the plugin (used to identify the plugin) language: Language code (e.g., 'en', 'es', 'fr', 'de') translations: Dictionary mapping translation keys to translated text context: Command context (optional) Examples: await set_translations( "/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2", 'es', { 'chat_title': 'Interfaz de Chat', 'buttons_send': 'Enviar Mensaje', 'nav_home': 'Inicio', 'error_connection_failed': 'Error de conexión' } ) await set_translations( "/some/path/src/my_plugin/templates/dashboard.jinja2", 'fr', { 'dashboard_welcome': 'Bienvenue au Tableau de Bord', 'buttons_save': 'Enregistrer', 'nav_home': 'Accueil' } ) """ try: if not isinstance(translations, dict): return "Error: translations must be a dictionary" # Get the plugin-specific translations path plugin_key = str(get_plugin_translations_path(original_path)) # Load existing translations for this plugin plugin_translations = load_plugin_translations(original_path) # Validate translation keys (should match placeholder format) invalid_keys = [] for key in translations.keys(): if not re.match(r'^[a-z0-9_]+$', key): invalid_keys.append(key) if invalid_keys: return f"Error: Invalid translation keys (use lowercase, numbers, underscores only): {invalid_keys}" # Update translations for this language if language not in plugin_translations: plugin_translations[language] = {} plugin_translations[language].update(translations) # Save translations to disk if save_plugin_translations(original_path, plugin_translations): # Update cache TRANSLATIONS[plugin_key] = plugin_translations return f"Set {len(translations)} translations for language '{language}' in {Path(plugin_key).parent.name} plugin" else: return f"Error: Could not save translations" except Exception as e: return f"Error setting translations: {str(e)}"
[docs] @command() async def get_translations(original_path: str = None, language: str = None, context=None): """ Get translations for a specific plugin and language. Args: original_path: Absolute path to a file in the plugin (optional) If not provided, returns all cached translations language: Language code to get translations for (optional) context: Command context (optional) Returns: Dictionary of translations Examples: # Get all translations for a plugin translations = await get_translations( "/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2" ) # Get Spanish translations for a plugin spanish = await get_translations( "/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2", 'es' ) # Get all cached translations all_cached = await get_translations() """ try: if original_path: # Load translations for specific plugin plugin_translations = load_plugin_translations(original_path) # Update cache plugin_key = str(get_plugin_translations_path(original_path)) TRANSLATIONS[plugin_key] = plugin_translations if language: return plugin_translations.get(language, {}) else: return plugin_translations else: # Return all cached translations return TRANSLATIONS except Exception as e: return f"Error getting translations: {str(e)}"
[docs] @command() async def list_localized_files(context=None): """ List all localized files that have been created. Returns: List of paths to localized files """ try: localized_files = [] if LOCALIZED_FILES_DIR.exists(): for file_path in LOCALIZED_FILES_DIR.rglob("*.i18n.*"): localized_files.append(str(file_path.relative_to(LOCALIZED_FILES_DIR))) return { "count": len(localized_files), "files": sorted(localized_files) } except Exception as e: return f"Error listing localized files: {str(e)}"
debug_box(f"l8n: command_manager has {len(command_manager.functions)} functions after registration") debug_box("l8n: End of mod.py")