mindroot 9.3.0__py3-none-any.whl → 9.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mindroot/coreplugins/admin/__init__.py +3 -1
- mindroot/coreplugins/admin/agent_router.py +250 -7
- mindroot/coreplugins/admin/asset_manager.py +164 -0
- mindroot/coreplugins/admin/command_router.py +236 -1
- mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
- mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
- mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
- mindroot/coreplugins/admin/mcp_routes.py +216 -0
- mindroot/coreplugins/admin/mod.py +62 -0
- mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
- mindroot/coreplugins/admin/persona_handler.py +15 -6
- mindroot/coreplugins/admin/persona_router.py +158 -2
- mindroot/coreplugins/admin/plugin_manager.py +105 -9
- mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
- mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
- mindroot/coreplugins/admin/plugin_routes.py +114 -0
- mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
- mindroot/coreplugins/admin/router.py +116 -15
- mindroot/coreplugins/admin/service_models.py +1 -1
- mindroot/coreplugins/admin/settings_router.py +1 -0
- mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
- mindroot/coreplugins/admin/static/css/dark.css +1 -0
- mindroot/coreplugins/admin/static/css/default.css +4 -0
- mindroot/coreplugins/admin/static/js/about-info.js +367 -0
- mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
- mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
- mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
- mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
- mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
- mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
- mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
- mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
- mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
- mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
- mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
- mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
- mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
- mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
- mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
- mindroot/coreplugins/admin/static/js/registry-shared-services.js +903 -0
- mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
- mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
- mindroot/coreplugins/admin/static/logo.png +0 -0
- mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
- mindroot/coreplugins/agent/Assistant/agent.json +27 -11
- mindroot/coreplugins/agent/agent.py +2 -2
- mindroot/coreplugins/agent/command_parser.py +25 -10
- mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
- mindroot/coreplugins/chat/__init__.py +4 -1
- mindroot/coreplugins/chat/router.py +132 -20
- mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
- mindroot/coreplugins/chat/services.py +31 -1
- mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
- mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
- mindroot/coreplugins/chat/static/css/dark.css +24 -3
- mindroot/coreplugins/chat/static/css/default.css +24 -3
- mindroot/coreplugins/chat/static/css/main.css +1 -0
- mindroot/coreplugins/chat/static/js/action.js +137 -60
- mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
- mindroot/coreplugins/chat/static/js/chat.js +59 -16
- mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
- mindroot/coreplugins/chat/static/js/chatform.js +2 -2
- mindroot/coreplugins/chat/static/site.webmanifest +1 -1
- mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
- mindroot/coreplugins/chat/widget_manager.py +139 -0
- mindroot/coreplugins/chat/widget_routes.py +287 -0
- mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
- mindroot/coreplugins/email/__init__.py +2 -0
- mindroot/coreplugins/email/email_provider.py +2 -2
- mindroot/coreplugins/email/mod.py +100 -0
- mindroot/coreplugins/email/services.py +5 -3
- mindroot/coreplugins/email/smtp_handler.py +9 -3
- mindroot/coreplugins/email/test_email_service.py +75 -0
- mindroot/coreplugins/env_manager/mod.py +61 -25
- mindroot/coreplugins/home/router.py +37 -2
- mindroot/coreplugins/home/static/imgs/logo.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
- mindroot/coreplugins/home/templates/home.jinja2 +15 -6
- mindroot/coreplugins/index/indices/default/index.json +39 -6
- mindroot/coreplugins/jwt_auth/middleware.py +47 -2
- mindroot/coreplugins/jwt_auth/mod.py +40 -17
- mindroot/coreplugins/l8n/__init__.py +6 -0
- mindroot/coreplugins/l8n/debug_loader.py +85 -0
- mindroot/coreplugins/l8n/debug_middleware.py +74 -0
- mindroot/coreplugins/l8n/l8n_constants.py +19 -0
- mindroot/coreplugins/l8n/language_detection.py +183 -0
- mindroot/coreplugins/l8n/middleware.py +151 -0
- mindroot/coreplugins/l8n/mod.py +277 -0
- mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
- mindroot/coreplugins/l8n/test_enhanced.py +298 -0
- mindroot/coreplugins/l8n/test_l8n.py +95 -0
- mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
- mindroot/coreplugins/l8n/test_middleware.py +272 -0
- mindroot/coreplugins/l8n/utils.py +232 -0
- mindroot/coreplugins/mcp_/__init__.py +14 -0
- mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
- mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
- mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
- mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
- mindroot/coreplugins/mcp_/mod.py +367 -0
- mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
- mindroot/coreplugins/mcp_/server_installer.py +79 -0
- mindroot/coreplugins/mcp_/setup.py +26 -0
- mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
- mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
- mindroot/coreplugins/persona/mod.py +12 -7
- mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
- mindroot/coreplugins/subscriptions/__init__.py +1 -0
- mindroot/coreplugins/subscriptions/mod.py +14 -3
- mindroot/coreplugins/subscriptions/router.py +3 -0
- mindroot/coreplugins/user_service/__init__.py +1 -2
- mindroot/coreplugins/user_service/admin_init.py +1 -0
- mindroot/coreplugins/user_service/email_service.py +72 -17
- mindroot/coreplugins/user_service/mod.py +10 -2
- mindroot/coreplugins/user_service/router.py +2 -0
- mindroot/lib/auth/api_key.py +28 -0
- mindroot/lib/cli/plugins.py +94 -0
- mindroot/lib/plugins/default_plugin_manifest.json +20 -0
- mindroot/lib/plugins/installation.py +5 -5
- mindroot/lib/plugins/l8n_static_handler.py +225 -0
- mindroot/lib/plugins/loader.py +33 -3
- mindroot/lib/plugins/loader_with_l8n.py +281 -0
- mindroot/lib/plugins/manifest.py +236 -24
- mindroot/lib/providers/commands.py +3 -1
- mindroot/lib/route_decorators.py +5 -5
- mindroot/lib/templates.py +183 -11
- mindroot/lib/utils/merge_arrays.py +1 -1
- mindroot/migrate.py +39 -20
- mindroot/registry/data_access.py +1 -1
- mindroot/server.py +42 -13
- mindroot/server_missing_normal_args.py +197 -0
- mindroot/server_prev.py +173 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/METADATA +7 -2
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/RECORD +143 -113
- mindroot/coreplugins/admin/plugin_manager_backup.py +0 -615
- mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
- mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
- mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
- mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
- mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
- mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
- mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
- mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
- mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
- mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
- mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
- mindroot/coreplugins/index/default.json +0 -76
- mindroot/coreplugins/user_service/file_trigger_service.py +0 -12
- mindroot/coreplugins/user_service/hooks.py +0 -23
- /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/WHEEL +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/entry_points.txt +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/licenses/LICENSE +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
# Try to import from lib first (for same instance), fallback to mindroot.lib
|
|
7
|
+
try:
|
|
8
|
+
from lib.providers.commands import command, command_manager
|
|
9
|
+
print("l8n: Using lib.providers.commands (same instance)")
|
|
10
|
+
except ImportError:
|
|
11
|
+
from mindroot.lib.providers.commands import command, command_manager
|
|
12
|
+
print("l8n: Using mindroot.lib.providers.commands (fallback)")
|
|
13
|
+
|
|
14
|
+
from .utils import extract_plugin_root, get_localized_file_path, load_plugin_translations, get_plugin_translations_path
|
|
15
|
+
from mindroot.lib.utils.debug import debug_box
|
|
16
|
+
|
|
17
|
+
debug_box("l8n: Top of mod.py")
|
|
18
|
+
|
|
19
|
+
from .l8n_constants import *
|
|
20
|
+
|
|
21
|
+
def save_plugin_translations(plugin_path: str, translations: dict):
|
|
22
|
+
"""Save translations for a specific plugin to disk."""
|
|
23
|
+
translations_file = get_plugin_translations_path(plugin_path)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Ensure directory exists
|
|
27
|
+
translations_file.parent.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
with open(translations_file, 'w', encoding='utf-8') as f:
|
|
29
|
+
json.dump(translations, f, indent=2, ensure_ascii=False)
|
|
30
|
+
return True
|
|
31
|
+
except Exception as e:
|
|
32
|
+
print(f"Warning: Could not save translations to {translations_file}: {e}")
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
debug_box("l8n: defining command")
|
|
37
|
+
|
|
38
|
+
# Debug: Check if command_manager has functions
|
|
39
|
+
debug_box(f"l8n: command_manager has {len(command_manager.functions)} functions before registration")
|
|
40
|
+
debug_box(f"l8n: command_manager instance ID: {id(command_manager)}")
|
|
41
|
+
|
|
42
|
+
@command()
|
|
43
|
+
async def write_localized_file(original_path: str, content: str, context=None):
|
|
44
|
+
"""
|
|
45
|
+
Write a localized version of a file with static placeholders.
|
|
46
|
+
|
|
47
|
+
This command creates a localized version of a template or source file
|
|
48
|
+
with __TRANSLATE_key__ placeholders that will be replaced with actual
|
|
49
|
+
translations when the file is loaded.
|
|
50
|
+
|
|
51
|
+
PLACEHOLDER FORMAT RULES:
|
|
52
|
+
- Always use the exact format: __TRANSLATE_key_name__
|
|
53
|
+
- Must start with __TRANSLATE_ and end with __
|
|
54
|
+
- Use lowercase letters, numbers, and underscores only for the key name
|
|
55
|
+
- Use descriptive, hierarchical key names like section_element or buttons_save
|
|
56
|
+
- NO spaces, hyphens, or special characters in key names
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
original_path: Absolute path to the original file
|
|
60
|
+
content: File content with __TRANSLATE_key__ placeholders
|
|
61
|
+
context: Command context (optional)
|
|
62
|
+
|
|
63
|
+
Examples:
|
|
64
|
+
await write_localized_file(
|
|
65
|
+
"/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2",
|
|
66
|
+
"<h1>__TRANSLATE_chat_title__</h1><button>__TRANSLATE_buttons_send__</button>"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
await write_localized_file(
|
|
70
|
+
"/some/path/src/my_plugin/templates/dashboard.jinja2",
|
|
71
|
+
"<div>__TRANSLATE_dashboard_welcome__</div>"
|
|
72
|
+
)
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
localized_path = get_localized_file_path(original_path)
|
|
76
|
+
|
|
77
|
+
# Create directory if it doesn't exist
|
|
78
|
+
localized_path.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
|
|
80
|
+
# Write the content
|
|
81
|
+
with open(localized_path, 'w', encoding='utf-8') as f:
|
|
82
|
+
f.write(content)
|
|
83
|
+
|
|
84
|
+
return f"Localized file written to: {localized_path}"
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
return f"Error writing localized file: {str(e)}"
|
|
88
|
+
|
|
89
|
+
@command()
|
|
90
|
+
async def append_localized_file(original_path: str, content: str, context=None):
|
|
91
|
+
"""
|
|
92
|
+
Append content to an existing localized file.
|
|
93
|
+
|
|
94
|
+
Use this for large files that need to be built incrementally.
|
|
95
|
+
Follow the same placeholder format rules as write_localized_file.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
original_path: Absolute path to the original file
|
|
99
|
+
content: Content to append with __TRANSLATE_key__ placeholders
|
|
100
|
+
context: Command context (optional)
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
# First write the beginning
|
|
104
|
+
await write_localized_file(
|
|
105
|
+
"/path/to/large_template.jinja2",
|
|
106
|
+
"<html><head><title>__TRANSLATE_page_title__</title></head>"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Then append more sections
|
|
110
|
+
await append_localized_file(
|
|
111
|
+
"/path/to/large_template.jinja2",
|
|
112
|
+
"<body><h1>__TRANSLATE_main_heading__</h1></body></html>"
|
|
113
|
+
)
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
localized_path = get_localized_file_path(original_path)
|
|
117
|
+
|
|
118
|
+
# Create directory if it doesn't exist
|
|
119
|
+
localized_path.parent.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
|
|
121
|
+
# Append the content
|
|
122
|
+
with open(localized_path, 'a', encoding='utf-8') as f:
|
|
123
|
+
f.write(content)
|
|
124
|
+
|
|
125
|
+
return f"Content appended to: {localized_path}"
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return f"Error appending to localized file: {str(e)}"
|
|
129
|
+
|
|
130
|
+
@command()
|
|
131
|
+
async def set_translations(original_path: str, language: str, translations: dict, context=None):
|
|
132
|
+
"""
|
|
133
|
+
Set translations for a specific language and plugin.
|
|
134
|
+
|
|
135
|
+
This command stores the translation mappings that will be used to replace
|
|
136
|
+
__TRANSLATE_key__ placeholders in localized files. Translations are stored
|
|
137
|
+
per plugin based on the provided file path.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
original_path: Absolute path to a file in the plugin (used to identify the plugin)
|
|
141
|
+
language: Language code (e.g., 'en', 'es', 'fr', 'de')
|
|
142
|
+
translations: Dictionary mapping translation keys to translated text
|
|
143
|
+
context: Command context (optional)
|
|
144
|
+
|
|
145
|
+
Examples:
|
|
146
|
+
await set_translations(
|
|
147
|
+
"/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2",
|
|
148
|
+
'es',
|
|
149
|
+
{
|
|
150
|
+
'chat_title': 'Interfaz de Chat',
|
|
151
|
+
'buttons_send': 'Enviar Mensaje',
|
|
152
|
+
'nav_home': 'Inicio',
|
|
153
|
+
'error_connection_failed': 'Error de conexión'
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
await set_translations(
|
|
158
|
+
"/some/path/src/my_plugin/templates/dashboard.jinja2",
|
|
159
|
+
'fr',
|
|
160
|
+
{
|
|
161
|
+
'dashboard_welcome': 'Bienvenue au Tableau de Bord',
|
|
162
|
+
'buttons_save': 'Enregistrer',
|
|
163
|
+
'nav_home': 'Accueil'
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
if not isinstance(translations, dict):
|
|
169
|
+
return "Error: translations must be a dictionary"
|
|
170
|
+
|
|
171
|
+
# Get the plugin-specific translations path
|
|
172
|
+
plugin_key = str(get_plugin_translations_path(original_path))
|
|
173
|
+
|
|
174
|
+
# Load existing translations for this plugin
|
|
175
|
+
plugin_translations = load_plugin_translations(original_path)
|
|
176
|
+
|
|
177
|
+
# Validate translation keys (should match placeholder format)
|
|
178
|
+
invalid_keys = []
|
|
179
|
+
for key in translations.keys():
|
|
180
|
+
if not re.match(r'^[a-z0-9_]+$', key):
|
|
181
|
+
invalid_keys.append(key)
|
|
182
|
+
|
|
183
|
+
if invalid_keys:
|
|
184
|
+
return f"Error: Invalid translation keys (use lowercase, numbers, underscores only): {invalid_keys}"
|
|
185
|
+
|
|
186
|
+
# Update translations for this language
|
|
187
|
+
if language not in plugin_translations:
|
|
188
|
+
plugin_translations[language] = {}
|
|
189
|
+
|
|
190
|
+
plugin_translations[language].update(translations)
|
|
191
|
+
|
|
192
|
+
# Save translations to disk
|
|
193
|
+
if save_plugin_translations(original_path, plugin_translations):
|
|
194
|
+
# Update cache
|
|
195
|
+
TRANSLATIONS[plugin_key] = plugin_translations
|
|
196
|
+
return f"Set {len(translations)} translations for language '{language}' in {Path(plugin_key).parent.name} plugin"
|
|
197
|
+
else:
|
|
198
|
+
return f"Error: Could not save translations"
|
|
199
|
+
|
|
200
|
+
except Exception as e:
|
|
201
|
+
return f"Error setting translations: {str(e)}"
|
|
202
|
+
|
|
203
|
+
@command()
|
|
204
|
+
async def get_translations(original_path: str = None, language: str = None, context=None):
|
|
205
|
+
"""
|
|
206
|
+
Get translations for a specific plugin and language.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
original_path: Absolute path to a file in the plugin (optional)
|
|
210
|
+
If not provided, returns all cached translations
|
|
211
|
+
language: Language code to get translations for (optional)
|
|
212
|
+
context: Command context (optional)
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Dictionary of translations
|
|
216
|
+
|
|
217
|
+
Examples:
|
|
218
|
+
# Get all translations for a plugin
|
|
219
|
+
translations = await get_translations(
|
|
220
|
+
"/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Get Spanish translations for a plugin
|
|
224
|
+
spanish = await get_translations(
|
|
225
|
+
"/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2",
|
|
226
|
+
'es'
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Get all cached translations
|
|
230
|
+
all_cached = await get_translations()
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
if original_path:
|
|
234
|
+
# Load translations for specific plugin
|
|
235
|
+
plugin_translations = load_plugin_translations(original_path)
|
|
236
|
+
|
|
237
|
+
# Update cache
|
|
238
|
+
plugin_key = str(get_plugin_translations_path(original_path))
|
|
239
|
+
TRANSLATIONS[plugin_key] = plugin_translations
|
|
240
|
+
|
|
241
|
+
if language:
|
|
242
|
+
return plugin_translations.get(language, {})
|
|
243
|
+
else:
|
|
244
|
+
return plugin_translations
|
|
245
|
+
else:
|
|
246
|
+
# Return all cached translations
|
|
247
|
+
return TRANSLATIONS
|
|
248
|
+
|
|
249
|
+
except Exception as e:
|
|
250
|
+
return f"Error getting translations: {str(e)}"
|
|
251
|
+
|
|
252
|
+
@command()
|
|
253
|
+
async def list_localized_files(context=None):
|
|
254
|
+
"""
|
|
255
|
+
List all localized files that have been created.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
List of paths to localized files
|
|
259
|
+
"""
|
|
260
|
+
try:
|
|
261
|
+
localized_files = []
|
|
262
|
+
|
|
263
|
+
if LOCALIZED_FILES_DIR.exists():
|
|
264
|
+
for file_path in LOCALIZED_FILES_DIR.rglob("*.i18n.*"):
|
|
265
|
+
localized_files.append(str(file_path.relative_to(LOCALIZED_FILES_DIR)))
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
"count": len(localized_files),
|
|
269
|
+
"files": sorted(localized_files)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
except Exception as e:
|
|
273
|
+
return f"Error listing localized files: {str(e)}"
|
|
274
|
+
|
|
275
|
+
debug_box(f"l8n: command_manager has {len(command_manager.functions)} functions after registration")
|
|
276
|
+
|
|
277
|
+
debug_box("l8n: End of mod.py")
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
to_delete = """
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
# Import the translation replacement function
|
|
8
|
+
from .mod import replace_placeholders, LOCALIZED_FILES_DIR
|
|
9
|
+
|
|
10
|
+
# Import the enhanced language detection
|
|
11
|
+
from .language_detection import get_fallback_language
|
|
12
|
+
from .middleware import get_request_language
|
|
13
|
+
|
|
14
|
+
# Store original Jinja2 loader methods
|
|
15
|
+
_original_get_source = None
|
|
16
|
+
_original_loader_get_source = None
|
|
17
|
+
|
|
18
|
+
def get_current_language() -> str:
|
|
19
|
+
"""
|
|
20
|
+
Get the current language for the request using enhanced detection.
|
|
21
|
+
"""
|
|
22
|
+
return get_fallback_language(get_request_language())
|
|
23
|
+
|
|
24
|
+
def find_localized_template(template_name: str) -> Optional[Path]:
|
|
25
|
+
"""
|
|
26
|
+
Find a localized version of a template.
|
|
27
|
+
|
|
28
|
+
Uses relative path matching to find templates regardless of installation directory.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
template_name: Original template name/path
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Path to localized template if found, None otherwise
|
|
35
|
+
"""
|
|
36
|
+
if not LOCALIZED_FILES_DIR.exists():
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
# Convert template name to potential localized paths
|
|
40
|
+
template_path = Path(template_name)
|
|
41
|
+
stem = template_path.stem
|
|
42
|
+
suffix = template_path.suffix
|
|
43
|
+
|
|
44
|
+
# Create the .i18n version filename
|
|
45
|
+
localized_filename = f"{stem}.i18n{suffix}"
|
|
46
|
+
|
|
47
|
+
# Search strategies in order of preference:
|
|
48
|
+
search_strategies = [
|
|
49
|
+
# 1. Exact filename match anywhere in localized_files
|
|
50
|
+
lambda: list(LOCALIZED_FILES_DIR.rglob(localized_filename)),
|
|
51
|
+
|
|
52
|
+
# 2. Match last 2 path components
|
|
53
|
+
lambda: _find_by_path_components(template_path, localized_filename, 2),
|
|
54
|
+
|
|
55
|
+
# 3. Match last 3 path components
|
|
56
|
+
lambda: _find_by_path_components(template_path, localized_filename, 3),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
for strategy in search_strategies:
|
|
60
|
+
matches = strategy()
|
|
61
|
+
if matches:
|
|
62
|
+
# Return the first match
|
|
63
|
+
return matches[0]
|
|
64
|
+
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
def _find_by_path_components(original_path: Path, localized_filename: str, num_components: int) -> list:
|
|
68
|
+
"""
|
|
69
|
+
Find localized templates by matching the last N path components.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
original_path: Original template path
|
|
73
|
+
localized_filename: Localized filename to search for
|
|
74
|
+
num_components: Number of path components to match
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
List of matching paths
|
|
78
|
+
"""
|
|
79
|
+
if len(original_path.parts) < num_components:
|
|
80
|
+
return []
|
|
81
|
+
|
|
82
|
+
# Get the last N-1 components (excluding filename) + localized filename
|
|
83
|
+
target_components = original_path.parts[-(num_components-1):]
|
|
84
|
+
target_path_pattern = '/'.join(target_components[:-1]) + '/' + localized_filename
|
|
85
|
+
|
|
86
|
+
matches = []
|
|
87
|
+
for candidate in LOCALIZED_FILES_DIR.rglob(localized_filename):
|
|
88
|
+
candidate_relative = candidate.relative_to(LOCALIZED_FILES_DIR)
|
|
89
|
+
if str(candidate_relative).endswith(target_path_pattern):
|
|
90
|
+
matches.append(candidate)
|
|
91
|
+
|
|
92
|
+
return matches
|
|
93
|
+
|
|
94
|
+
def patched_get_source(self, environment, template):
|
|
95
|
+
"""
|
|
96
|
+
Patched version of Jinja2 loader's get_source method.
|
|
97
|
+
|
|
98
|
+
This intercepts template loading and:
|
|
99
|
+
1. Checks for localized versions
|
|
100
|
+
2. Replaces placeholders with translations
|
|
101
|
+
3. Falls back to original templates if no localized version exists
|
|
102
|
+
"""
|
|
103
|
+
# First, try to find a localized version
|
|
104
|
+
localized_path = find_localized_template(template)
|
|
105
|
+
|
|
106
|
+
if localized_path and localized_path.exists():
|
|
107
|
+
try:
|
|
108
|
+
# Read the localized template
|
|
109
|
+
with open(localized_path, 'r', encoding='utf-8') as f:
|
|
110
|
+
source = f.read()
|
|
111
|
+
|
|
112
|
+
# Replace placeholders with translations
|
|
113
|
+
current_language = get_current_language()
|
|
114
|
+
translated_source = replace_placeholders(source, current_language, str(localized_path))
|
|
115
|
+
|
|
116
|
+
# Return the translated source
|
|
117
|
+
# Note: We use the original template name for caching purposes
|
|
118
|
+
return translated_source, str(localized_path), lambda: True
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
# If there's an error reading the localized template, fall back to original
|
|
122
|
+
print(f"Error loading localized template {localized_path}: {e}")
|
|
123
|
+
|
|
124
|
+
# Fall back to original template loading
|
|
125
|
+
return _original_get_source(self, environment, template)
|
|
126
|
+
|
|
127
|
+
def install_monkey_patch():
|
|
128
|
+
"""
|
|
129
|
+
Install the monkey patch for Jinja2 template loading.
|
|
130
|
+
|
|
131
|
+
This patches the FileSystemLoader.get_source method to intercept
|
|
132
|
+
template loading and provide localized versions when available.
|
|
133
|
+
"""
|
|
134
|
+
global _original_get_source
|
|
135
|
+
return
|
|
136
|
+
try:
|
|
137
|
+
from jinja2 import FileSystemLoader
|
|
138
|
+
|
|
139
|
+
# Store the original method
|
|
140
|
+
if _original_get_source is None:
|
|
141
|
+
_original_get_source = FileSystemLoader.get_source
|
|
142
|
+
|
|
143
|
+
# Install the patch
|
|
144
|
+
FileSystemLoader.get_source = patched_get_source
|
|
145
|
+
|
|
146
|
+
print("L8n monkey patch installed successfully")
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
except ImportError:
|
|
150
|
+
print("Warning: Could not import Jinja2 for monkey patching")
|
|
151
|
+
return False
|
|
152
|
+
except Exception as e:
|
|
153
|
+
print(f"Error installing l8n monkey patch: {e}")
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
def uninstall_monkey_patch():
|
|
157
|
+
"""
|
|
158
|
+
Remove the monkey patch and restore original Jinja2 behavior.
|
|
159
|
+
"""
|
|
160
|
+
global _original_get_source
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
from jinja2 import FileSystemLoader
|
|
164
|
+
|
|
165
|
+
if _original_get_source is not None:
|
|
166
|
+
FileSystemLoader.get_source = _original_get_source
|
|
167
|
+
print("L8n monkey patch removed successfully")
|
|
168
|
+
return True
|
|
169
|
+
else:
|
|
170
|
+
print("No monkey patch to remove")
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
except ImportError:
|
|
174
|
+
print("Warning: Could not import Jinja2 for monkey patch removal")
|
|
175
|
+
return False
|
|
176
|
+
except Exception as e:
|
|
177
|
+
print(f"Error removing l8n monkey patch: {e}")
|
|
178
|
+
return False
|
|
179
|
+
|
|
180
|
+
# Auto-install the monkey patch when this module is imported
|
|
181
|
+
# Prevent double installation by checking if already installed
|
|
182
|
+
if __name__ != '__main__' and not hasattr(install_monkey_patch, '_auto_installed'):
|
|
183
|
+
install_monkey_patch()
|
|
184
|
+
install_monkey_patch._auto_installed = True
|
|
185
|
+
|
|
186
|
+
"""
|