Source code for mindroot.lib.plugins.l8n_static_handler

import os
import re
from pathlib import Path
from fastapi import Request, Response
from fastapi.staticfiles import StaticFiles
from starlette.responses import FileResponse
from starlette.types import Scope, Receive, Send
import sys
import traceback
try:
    from mindroot.coreplugins.l8n.utils import replace_placeholders, extract_plugin_root, get_localized_file_path, load_plugin_translations
    from mindroot.coreplugins.l8n.middleware import get_request_language
    from mindroot.coreplugins.l8n.language_detection import get_fallback_language
    L8N_AVAILABLE = True
except ImportError as e:
    trace = traceback.format_exc()
    L8N_AVAILABLE = False
    sys.exit(1)

[docs] class TranslatedStaticFiles(StaticFiles): """Custom StaticFiles handler that applies l8n translations to JavaScript files.""" def __init__(self, *, directory: str, plugin_name: str=None, **kwargs): super().__init__(directory=directory, **kwargs) self.plugin_name = plugin_name self.directory_path = Path(directory)
[docs] def get_current_language(self, request: Request) -> str: """Get the current language for the request.""" if not L8N_AVAILABLE: return 'en' try: if hasattr(request.state, 'language'): return request.state.language lang = get_request_language() return get_fallback_language(lang) if lang else 'en' except Exception as e: return 'en'
[docs] def should_translate_file(self, file_path: Path) -> bool: """Check if a file should be translated.""" if not L8N_AVAILABLE: return False return file_path.suffix.lower() in ['.js', '.mjs']
[docs] def apply_translations_to_js(self, content: str, language: str, file_path: str) -> str: """Apply translations to JavaScript content. This looks for __TRANSLATE_key__ placeholders in JS files and replaces them with translated strings. If translations are missing, returns None to signal that the original file should be served instead. """ if not L8N_AVAILABLE or not content: return content try: translated_content = replace_placeholders(content, language, file_path) if translated_content is None: return None return translated_content except Exception as e: return None
[docs] async def get_response(self, path: str, scope: Scope) -> Response: """Override to add translation support for JavaScript files.""" try: full_path = self.directory_path / path if full_path.exists() and self.should_translate_file(full_path): request = Request(scope) current_language = self.get_current_language(request) localized_path = get_localized_file_path(str(full_path)) if localized_path.exists(): with open(localized_path, 'r', encoding='utf-8') as f: content = f.read() translated_content = self.apply_translations_to_js(content, current_language, str(localized_path)) if translated_content is not None: return Response(content=translated_content, media_type='application/javascript', headers={'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0'}) return await super().get_response(path, scope) except Exception as e: trace = traceback.format_exc() return await super().get_response(path, scope)
[docs] def mount_translated_static_files(app, plugin_name: str, category: str): """Mount plugin static files with translation support. Args: app (FastAPI): The FastAPI application instance plugin_name (str): Name of the plugin category (str): Plugin category ('core' or 'installed') """ from .paths import get_plugin_path plugin_dir = get_plugin_path(plugin_name) if not plugin_dir: return dir_name = os.path.basename(plugin_dir) if category != 'core': static_path = os.path.join(plugin_dir, 'src', dir_name, 'static') if not os.path.exists(static_path): static_path = os.path.join(plugin_dir, 'static') else: static_path = os.path.join(plugin_dir, 'static') if os.path.exists(static_path): app.mount(f'/{dir_name}/static', TranslatedStaticFiles(directory=static_path, plugin_name=plugin_name), name=f'/{dir_name}/static')
[docs] def create_js_translation_object(translations: dict) -> str: """Create a JavaScript object containing translations. This can be injected into JS files to provide client-side translation support. Args: translations: Dictionary of translation key-value pairs Returns: JavaScript code defining a translation object """ import json js_translations = json.dumps(translations, ensure_ascii=False) return f'\n// Auto-generated translation object\nwindow.MINDROOT_TRANSLATIONS = {js_translations};\n\n// Helper function to get translations\nwindow.translate = function(key, fallback) {{\n return window.MINDROOT_TRANSLATIONS[key] || fallback || key;\n}};\n\n// Alias for shorter usage\nwindow.t = window.translate;\n'
[docs] def inject_translations_into_js(content: str, translations: dict) -> str: """Inject translation object into JavaScript content. This prepends translation definitions to JS files. Args: content: Original JavaScript content translations: Translation dictionary Returns: JavaScript content with translations injected """ if not translations: return content translation_js = create_js_translation_object(translations) return translation_js + '\n\n' + content