mindroot 9.3.0__py3-none-any.whl → 9.5.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.
Files changed (183) hide show
  1. mindroot/coreplugins/admin/__init__.py +3 -1
  2. mindroot/coreplugins/admin/agent_router.py +250 -7
  3. mindroot/coreplugins/admin/asset_manager.py +164 -0
  4. mindroot/coreplugins/admin/command_router.py +236 -1
  5. mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
  6. mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
  7. mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
  8. mindroot/coreplugins/admin/mcp_routes.py +216 -0
  9. mindroot/coreplugins/admin/mod.py +62 -0
  10. mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
  11. mindroot/coreplugins/admin/persona_handler.py +15 -6
  12. mindroot/coreplugins/admin/persona_router.py +158 -2
  13. mindroot/coreplugins/admin/plugin_manager.py +63 -0
  14. mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
  15. mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
  16. mindroot/coreplugins/admin/plugin_routes.py +114 -0
  17. mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
  18. mindroot/coreplugins/admin/router.py +116 -15
  19. mindroot/coreplugins/admin/service_models.py +1 -1
  20. mindroot/coreplugins/admin/settings_router.py +1 -0
  21. mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
  22. mindroot/coreplugins/admin/static/css/dark.css +1 -0
  23. mindroot/coreplugins/admin/static/css/default.css +4 -0
  24. mindroot/coreplugins/admin/static/js/about-info.js +367 -0
  25. mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
  26. mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
  27. mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
  28. mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
  29. mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
  30. mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
  31. mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
  32. mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
  33. mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
  34. mindroot/coreplugins/admin/static/js/registry-manager-old.js +385 -0
  35. mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
  36. mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
  37. mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
  38. mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
  39. mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
  40. mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
  41. mindroot/coreplugins/admin/static/js/registry-shared-services.js +857 -0
  42. mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
  43. mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
  44. mindroot/coreplugins/admin/static/logo.png +0 -0
  45. mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
  46. mindroot/coreplugins/agent/Assistant/agent.json +27 -11
  47. mindroot/coreplugins/agent/agent.py +2 -2
  48. mindroot/coreplugins/agent/command_parser.py +25 -10
  49. mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
  50. mindroot/coreplugins/chat/__init__.py +4 -1
  51. mindroot/coreplugins/chat/router.py +132 -20
  52. mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
  53. mindroot/coreplugins/chat/services.py +31 -1
  54. mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
  55. mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
  56. mindroot/coreplugins/chat/static/css/dark.css +24 -3
  57. mindroot/coreplugins/chat/static/css/default.css +24 -3
  58. mindroot/coreplugins/chat/static/css/main.css +1 -0
  59. mindroot/coreplugins/chat/static/js/action.js +137 -60
  60. mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
  61. mindroot/coreplugins/chat/static/js/chat.js +59 -16
  62. mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
  63. mindroot/coreplugins/chat/static/js/chatform.js +2 -2
  64. mindroot/coreplugins/chat/static/site.webmanifest +1 -1
  65. mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
  66. mindroot/coreplugins/chat/widget_manager.py +139 -0
  67. mindroot/coreplugins/chat/widget_routes.py +287 -0
  68. mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
  69. mindroot/coreplugins/email/__init__.py +2 -0
  70. mindroot/coreplugins/email/email_provider.py +2 -2
  71. mindroot/coreplugins/email/mod.py +100 -0
  72. mindroot/coreplugins/email/services.py +5 -3
  73. mindroot/coreplugins/email/smtp_handler.py +9 -3
  74. mindroot/coreplugins/email/test_email_service.py +75 -0
  75. mindroot/coreplugins/env_manager/mod.py +61 -25
  76. mindroot/coreplugins/home/router.py +37 -2
  77. mindroot/coreplugins/home/static/imgs/logo.png +0 -0
  78. mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
  79. mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
  80. mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
  81. mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
  82. mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
  83. mindroot/coreplugins/home/templates/home.jinja2 +15 -6
  84. mindroot/coreplugins/index/indices/default/index.json +6 -6
  85. mindroot/coreplugins/jwt_auth/middleware.py +47 -2
  86. mindroot/coreplugins/jwt_auth/mod.py +40 -17
  87. mindroot/coreplugins/l8n/__init__.py +6 -0
  88. mindroot/coreplugins/l8n/debug_loader.py +85 -0
  89. mindroot/coreplugins/l8n/debug_middleware.py +74 -0
  90. mindroot/coreplugins/l8n/l8n_constants.py +19 -0
  91. mindroot/coreplugins/l8n/language_detection.py +183 -0
  92. mindroot/coreplugins/l8n/middleware.py +151 -0
  93. mindroot/coreplugins/l8n/mod.py +277 -0
  94. mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
  95. mindroot/coreplugins/l8n/test_enhanced.py +298 -0
  96. mindroot/coreplugins/l8n/test_l8n.py +95 -0
  97. mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
  98. mindroot/coreplugins/l8n/test_middleware.py +272 -0
  99. mindroot/coreplugins/l8n/utils.py +232 -0
  100. mindroot/coreplugins/mcp_/__init__.py +14 -0
  101. mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
  102. mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
  103. mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
  104. mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
  105. mindroot/coreplugins/mcp_/mod.py +367 -0
  106. mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
  107. mindroot/coreplugins/mcp_/server_installer.py +79 -0
  108. mindroot/coreplugins/mcp_/setup.py +26 -0
  109. mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
  110. mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
  111. mindroot/coreplugins/persona/mod.py +12 -7
  112. mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
  113. mindroot/coreplugins/subscriptions/__init__.py +1 -0
  114. mindroot/coreplugins/subscriptions/mod.py +14 -3
  115. mindroot/coreplugins/subscriptions/router.py +3 -0
  116. mindroot/coreplugins/user_service/__init__.py +1 -2
  117. mindroot/coreplugins/user_service/admin_init.py +1 -0
  118. mindroot/coreplugins/user_service/email_service.py +72 -17
  119. mindroot/coreplugins/user_service/mod.py +10 -2
  120. mindroot/coreplugins/user_service/router.py +2 -0
  121. mindroot/lib/auth/api_key.py +28 -0
  122. mindroot/lib/cli/plugins.py +94 -0
  123. mindroot/lib/plugins/default_plugin_manifest.json +20 -0
  124. mindroot/lib/plugins/installation.py +5 -5
  125. mindroot/lib/plugins/l8n_static_handler.py +225 -0
  126. mindroot/lib/plugins/loader.py +33 -3
  127. mindroot/lib/plugins/loader_with_l8n.py +281 -0
  128. mindroot/lib/plugins/manifest.py +236 -24
  129. mindroot/lib/providers/commands.py +3 -1
  130. mindroot/lib/route_decorators.py +5 -5
  131. mindroot/lib/templates.py +183 -11
  132. mindroot/lib/utils/merge_arrays.py +1 -1
  133. mindroot/migrate.py +39 -20
  134. mindroot/registry/data_access.py +1 -1
  135. mindroot/server.py +42 -13
  136. mindroot/server_missing_normal_args.py +197 -0
  137. mindroot/server_prev.py +173 -0
  138. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/METADATA +7 -2
  139. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/RECORD +144 -112
  140. mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
  141. mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
  142. mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
  143. mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
  144. mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
  145. mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
  146. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
  147. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  148. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  149. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  150. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  151. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  152. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
  153. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  154. mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
  155. mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
  156. mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
  157. mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
  158. mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
  159. mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
  160. mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
  161. mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
  162. mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
  163. mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
  164. mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
  165. mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
  166. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
  167. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  168. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  169. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  170. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  171. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  172. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
  173. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  174. mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
  175. mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
  176. mindroot/coreplugins/index/default.json +0 -76
  177. mindroot/coreplugins/user_service/file_trigger_service.py +0 -12
  178. mindroot/coreplugins/user_service/hooks.py +0 -23
  179. /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
  180. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/WHEEL +0 -0
  181. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/entry_points.txt +0 -0
  182. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/licenses/LICENSE +0 -0
  183. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/top_level.txt +0 -0
@@ -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
+ """
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Enhanced test script for the l8n localization plugin with language detection.
4
+ """
5
+
6
+ import asyncio
7
+ import os
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ # Add the current directory to Python path for imports
12
+ sys.path.insert(0, str(Path(__file__).parent))
13
+
14
+ from test_l8n_standalone import (
15
+ write_localized_file,
16
+ set_translations,
17
+ replace_placeholders,
18
+ TRANSLATIONS
19
+ )
20
+
21
+ from language_detection import (
22
+ get_current_language_from_request,
23
+ get_fallback_language,
24
+ set_language_for_request,
25
+ get_supported_languages,
26
+ is_language_supported,
27
+ _parse_accept_language_header
28
+ )
29
+
30
+ async def test_language_detection():
31
+ """Test the enhanced language detection functionality."""
32
+ print("Testing Enhanced Language Detection...\n")
33
+
34
+ # Test 1: Default language detection
35
+ print("1. Testing default language detection...")
36
+ default_lang = get_current_language_from_request()
37
+ print(f" Default language: {default_lang}")
38
+
39
+ # Test 2: Environment variable override
40
+ print("\n2. Testing environment variable override...")
41
+ os.environ['MINDROOT_LANGUAGE'] = 'es'
42
+ env_lang = get_current_language_from_request()
43
+ print(f" Language with MINDROOT_LANGUAGE=es: {env_lang}")
44
+
45
+ # Test 3: Language fallback system
46
+ print("\n3. Testing language fallback system...")
47
+ supported = get_supported_languages()
48
+ print(f" Supported languages: {supported}")
49
+
50
+ test_languages = ['es', 'fr', 'de', 'zh-cn', 'pt-br', 'invalid-lang']
51
+ for lang in test_languages:
52
+ fallback = get_fallback_language(lang)
53
+ supported_check = is_language_supported(lang)
54
+ print(f" {lang} -> {fallback} (supported: {supported_check})")
55
+
56
+ # Test 4: Accept-Language header parsing
57
+ print("\n4. Testing Accept-Language header parsing...")
58
+ test_headers = [
59
+ 'en-US,en;q=0.9,es;q=0.8,fr;q=0.7',
60
+ 'es-ES,es;q=0.9,en;q=0.8',
61
+ 'fr-FR,fr;q=0.9',
62
+ 'de-DE,de;q=0.8,en;q=0.5',
63
+ 'zh-CN,zh;q=0.9,en;q=0.1'
64
+ ]
65
+
66
+ for header in test_headers:
67
+ parsed = _parse_accept_language_header(header)
68
+ print(f" '{header}' -> {parsed}")
69
+
70
+ # Test 5: Set language for request
71
+ print("\n5. Testing set_language_for_request...")
72
+ set_language_for_request('fr')
73
+ current_lang = get_current_language_from_request()
74
+ print(f" After setting to 'fr': {current_lang}")
75
+
76
+ # Reset for next tests
77
+ os.environ.pop('MINDROOT_LANGUAGE', None)
78
+
79
+ print("\n✅ Language detection tests completed!")
80
+
81
+ async def test_integrated_translation():
82
+ """Test the integrated translation system with language detection."""
83
+ print("\nTesting Integrated Translation System...\n")
84
+
85
+ # Set up translations for multiple languages
86
+ print("1. Setting up translations...")
87
+
88
+ # English (default)
89
+ await set_translations('en', {
90
+ 'welcome_title': 'Welcome to MindRoot',
91
+ 'nav_home': 'Home',
92
+ 'nav_settings': 'Settings',
93
+ 'button_save': 'Save',
94
+ 'button_cancel': 'Cancel'
95
+ })
96
+
97
+ # Spanish
98
+ await set_translations('es', {
99
+ 'welcome_title': 'Bienvenido a MindRoot',
100
+ 'nav_home': 'Inicio',
101
+ 'nav_settings': 'Configuración',
102
+ 'button_save': 'Guardar',
103
+ 'button_cancel': 'Cancelar'
104
+ })
105
+
106
+ # French
107
+ await set_translations('fr', {
108
+ 'welcome_title': 'Bienvenue à MindRoot',
109
+ 'nav_home': 'Accueil',
110
+ 'nav_settings': 'Paramètres',
111
+ 'button_save': 'Enregistrer',
112
+ 'button_cancel': 'Annuler'
113
+ })
114
+
115
+ # German
116
+ await set_translations('de', {
117
+ 'welcome_title': 'Willkommen bei MindRoot',
118
+ 'nav_home': 'Startseite',
119
+ 'nav_settings': 'Einstellungen',
120
+ 'button_save': 'Speichern',
121
+ 'button_cancel': 'Abbrechen'
122
+ })
123
+
124
+ print(f" Set up translations for {len(TRANSLATIONS)} languages")
125
+
126
+ # Test template with placeholders
127
+ template_content = """
128
+ <html>
129
+ <head>
130
+ <title>__TRANSLATE_welcome_title__</title>
131
+ </head>
132
+ <body>
133
+ <nav>
134
+ <a href="/">__TRANSLATE_nav_home__</a>
135
+ <a href="/settings">__TRANSLATE_nav_settings__</a>
136
+ </nav>
137
+ <main>
138
+ <h1>__TRANSLATE_welcome_title__</h1>
139
+ <div class="actions">
140
+ <button>__TRANSLATE_button_save__</button>
141
+ <button>__TRANSLATE_button_cancel__</button>
142
+ </div>
143
+ </main>
144
+ </body>
145
+ </html>
146
+ """.strip()
147
+
148
+ # Test translation for each language
149
+ print("\n2. Testing translation for each language...")
150
+
151
+ test_languages = ['en', 'es', 'fr', 'de', 'pt'] # pt should fallback to en
152
+
153
+ for lang in test_languages:
154
+ print(f"\n Language: {lang}")
155
+
156
+ # Set the language
157
+ set_language_for_request(lang)
158
+
159
+ # Get the fallback language
160
+ fallback_lang = get_fallback_language(lang)
161
+ print(f" Fallback: {fallback_lang}")
162
+
163
+ # Translate the template
164
+ translated = replace_placeholders(template_content, fallback_lang)
165
+
166
+ # Show a snippet of the translation
167
+ title_line = [line for line in translated.split('\n') if '<title>' in line][0]
168
+ h1_line = [line for line in translated.split('\n') if '<h1>' in line][0]
169
+
170
+ print(f" Title: {title_line.strip()}")
171
+ print(f" H1: {h1_line.strip()}")
172
+
173
+ print("\n✅ Integrated translation tests completed!")
174
+
175
+ async def test_file_integration():
176
+ """Test creating localized files and using language detection."""
177
+ print("\nTesting File Integration...\n")
178
+
179
+ # Create a localized template file
180
+ print("1. Creating localized template file...")
181
+
182
+ template_path = "/files/mindroot/src/mindroot/coreplugins/admin/templates/admin.jinja2"
183
+ template_content = """
184
+ <!DOCTYPE html>
185
+ <html>
186
+ <head>
187
+ <title>__TRANSLATE_admin_title__</title>
188
+ </head>
189
+ <body>
190
+ <header>
191
+ <h1>__TRANSLATE_admin_header__</h1>
192
+ <nav>
193
+ <a href="/admin/users">__TRANSLATE_nav_users__</a>
194
+ <a href="/admin/settings">__TRANSLATE_nav_settings__</a>
195
+ <a href="/admin/logs">__TRANSLATE_nav_logs__</a>
196
+ </nav>
197
+ </header>
198
+ <main>
199
+ <section>
200
+ <h2>__TRANSLATE_section_overview__</h2>
201
+ <p>__TRANSLATE_overview_description__</p>
202
+ </section>
203
+ <div class="actions">
204
+ <button class="primary">__TRANSLATE_button_create__</button>
205
+ <button class="secondary">__TRANSLATE_button_refresh__</button>
206
+ </div>
207
+ </main>
208
+ </body>
209
+ </html>
210
+ """.strip()
211
+
212
+ result = await write_localized_file(template_path, template_content)
213
+ print(f" {result}")
214
+
215
+ # Set up admin-specific translations
216
+ print("\n2. Setting up admin translations...")
217
+
218
+ admin_translations = {
219
+ 'en': {
220
+ 'admin_title': 'MindRoot Administration',
221
+ 'admin_header': 'Admin Dashboard',
222
+ 'nav_users': 'Users',
223
+ 'nav_settings': 'Settings',
224
+ 'nav_logs': 'Logs',
225
+ 'section_overview': 'System Overview',
226
+ 'overview_description': 'Manage your MindRoot installation from this dashboard.',
227
+ 'button_create': 'Create New',
228
+ 'button_refresh': 'Refresh Data'
229
+ },
230
+ 'es': {
231
+ 'admin_title': 'Administración de MindRoot',
232
+ 'admin_header': 'Panel de Administración',
233
+ 'nav_users': 'Usuarios',
234
+ 'nav_settings': 'Configuración',
235
+ 'nav_logs': 'Registros',
236
+ 'section_overview': 'Resumen del Sistema',
237
+ 'overview_description': 'Gestiona tu instalación de MindRoot desde este panel.',
238
+ 'button_create': 'Crear Nuevo',
239
+ 'button_refresh': 'Actualizar Datos'
240
+ },
241
+ 'fr': {
242
+ 'admin_title': 'Administration MindRoot',
243
+ 'admin_header': 'Tableau de Bord Admin',
244
+ 'nav_users': 'Utilisateurs',
245
+ 'nav_settings': 'Paramètres',
246
+ 'nav_logs': 'Journaux',
247
+ 'section_overview': 'Aperçu du Système',
248
+ 'overview_description': 'Gérez votre installation MindRoot depuis ce tableau de bord.',
249
+ 'button_create': 'Créer Nouveau',
250
+ 'button_refresh': 'Actualiser les Données'
251
+ }
252
+ }
253
+
254
+ for lang, translations in admin_translations.items():
255
+ result = await set_translations(lang, translations)
256
+ print(f" {result}")
257
+
258
+ # Test the complete workflow
259
+ print("\n3. Testing complete localization workflow...")
260
+
261
+ for lang in ['en', 'es', 'fr']:
262
+ print(f"\n Testing {lang.upper()} localization:")
263
+
264
+ # Set language
265
+ set_language_for_request(lang)
266
+ current = get_current_language_from_request()
267
+ fallback = get_fallback_language(current)
268
+
269
+ # Translate template
270
+ translated = replace_placeholders(template_content, fallback)
271
+
272
+ # Extract and show key elements
273
+ lines = translated.split('\n')
274
+ title = next((line for line in lines if '<title>' in line), '').strip()
275
+ header = next((line for line in lines if '<h1>' in line), '').strip()
276
+
277
+ print(f" Language: {current} -> Fallback: {fallback}")
278
+ print(f" {title}")
279
+ print(f" {header}")
280
+
281
+ print("\n✅ File integration tests completed!")
282
+
283
+ async def main():
284
+ """Run all enhanced tests."""
285
+ print("=" * 60)
286
+ print("MindRoot l8n Plugin - Enhanced Testing Suite")
287
+ print("=" * 60)
288
+
289
+ await test_language_detection()
290
+ await test_integrated_translation()
291
+ await test_file_integration()
292
+
293
+ print("\n" + "=" * 60)
294
+ print("✅ All enhanced tests completed successfully!")
295
+ print("=" * 60)
296
+
297
+ if __name__ == "__main__":
298
+ asyncio.run(main())
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple test script for the l8n localization plugin.
4
+
5
+ This script tests the basic functionality of the localization commands
6
+ without requiring the full MindRoot environment.
7
+ """
8
+
9
+ import asyncio
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ # Add the current directory to Python path for imports
14
+ sys.path.insert(0, str(Path(__file__).parent))
15
+
16
+ from mod import (
17
+ write_localized_file,
18
+ append_localized_file,
19
+ set_translations,
20
+ get_translations,
21
+ list_localized_files,
22
+ replace_placeholders
23
+ )
24
+
25
+ async def test_basic_functionality():
26
+ """Test the basic l8n functionality."""
27
+ print("Testing MindRoot l8n Plugin...\n")
28
+
29
+ # Test 1: Create a localized file
30
+ print("1. Testing write_localized_file...")
31
+ test_path = "/files/mindroot/src/mindroot/coreplugins/chat/templates/chat.jinja2"
32
+ test_content = "<h1>__TRANSLATE_chat_title__</h1><button>__TRANSLATE_buttons_send__</button>"
33
+
34
+ result = await write_localized_file(test_path, test_content)
35
+ print(f" Result: {result}")
36
+
37
+ # Test 2: Append to the file
38
+ print("\n2. Testing append_localized_file...")
39
+ append_content = "<p>__TRANSLATE_welcome_message__</p>"
40
+
41
+ result = await append_localized_file(test_path, append_content)
42
+ print(f" Result: {result}")
43
+
44
+ # Test 3: Set translations
45
+ print("\n3. Testing set_translations...")
46
+ spanish_translations = {
47
+ 'chat_title': 'Interfaz de Chat',
48
+ 'buttons_send': 'Enviar Mensaje',
49
+ 'welcome_message': 'Bienvenido al chat'
50
+ }
51
+
52
+ result = await set_translations('es', spanish_translations)
53
+ print(f" Result: {result}")
54
+
55
+ # Test 4: Set more translations
56
+ french_translations = {
57
+ 'chat_title': 'Interface de Chat',
58
+ 'buttons_send': 'Envoyer le Message',
59
+ 'welcome_message': 'Bienvenue dans le chat'
60
+ }
61
+
62
+ result = await set_translations('fr', french_translations)
63
+ print(f" Result: {result}")
64
+
65
+ # Test 5: Get translations
66
+ print("\n4. Testing get_translations...")
67
+ es_translations = await get_translations('es')
68
+ print(f" Spanish translations: {es_translations}")
69
+
70
+ all_translations = await get_translations()
71
+ print(f" All languages: {list(all_translations.keys())}")
72
+
73
+ # Test 6: List localized files
74
+ print("\n5. Testing list_localized_files...")
75
+ files_result = await list_localized_files()
76
+ print(f" Result: {files_result}")
77
+
78
+ # Test 7: Test placeholder replacement
79
+ print("\n6. Testing placeholder replacement...")
80
+ test_template = "<h1>__TRANSLATE_chat_title__</h1><button>__TRANSLATE_buttons_send__</button><p>__TRANSLATE_welcome_message__</p>"
81
+
82
+ spanish_result = replace_placeholders(test_template, 'es')
83
+ print(f" Spanish: {spanish_result}")
84
+
85
+ french_result = replace_placeholders(test_template, 'fr')
86
+ print(f" French: {french_result}")
87
+
88
+ # Test 8: Test with unknown language (should return original)
89
+ unknown_result = replace_placeholders(test_template, 'de')
90
+ print(f" German (unknown): {unknown_result}")
91
+
92
+ print("\n✅ All tests completed!")
93
+
94
+ if __name__ == "__main__":
95
+ asyncio.run(test_basic_functionality())