feat: add module-specific locale support for i18n
Enhance the self-contained module architecture with locale/translation support:
ModuleDefinition changes:
- Add locales_path attribute for module-specific translations
- Add get_locales_dir() helper method
- Include locales in validate_structure() check
i18n module changes (app/utils/i18n.py):
- Add get_module_locale_dirs() to discover module locales
- Update load_translations() to merge module translations with core
- Module translations namespaced under module code (e.g., cms.title)
- Add _deep_merge() helper for nested dictionary merging
- Add _load_json_file() helper for cleaner JSON loading
CMS module locales:
- Add app/modules/cms/locales/ with translations for all 4 languages
- en.json, fr.json, de.json, lb.json with CMS-specific strings
- Covers: pages, page editing, SEO, navigation, publishing, homepage
sections, media library, themes, actions, and messages
Usage in templates:
{{ _("cms.title") }} -> "Content Management" (en)
{{ _("cms.pages.create") }} -> "Créer une page" (fr)
{{ _("cms.publishing.draft") }} -> "Entwurf" (de)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,7 @@ class ModuleDefinition:
|
||||
schemas_path: Path to schemas subpackage (e.g., "app.modules.billing.schemas")
|
||||
templates_path: Path to templates directory (relative to module)
|
||||
exceptions_path: Path to exceptions module (e.g., "app.modules.billing.exceptions")
|
||||
locales_path: Path to locales directory (relative to module, e.g., "locales")
|
||||
is_self_contained: Whether module uses self-contained structure
|
||||
|
||||
Example (traditional thin wrapper):
|
||||
@@ -91,6 +92,7 @@ class ModuleDefinition:
|
||||
schemas_path="app.modules.cms.schemas",
|
||||
templates_path="templates",
|
||||
exceptions_path="app.modules.cms.exceptions",
|
||||
locales_path="locales",
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -120,6 +122,7 @@ class ModuleDefinition:
|
||||
schemas_path: str | None = None
|
||||
templates_path: str | None = None # Relative to module directory
|
||||
exceptions_path: str | None = None
|
||||
locales_path: str | None = None # Relative to module directory, e.g., "locales"
|
||||
|
||||
def get_menu_items(self, frontend_type: FrontendType) -> list[str]:
|
||||
"""Get menu item IDs for a specific frontend type."""
|
||||
@@ -176,6 +179,17 @@ class ModuleDefinition:
|
||||
return None
|
||||
return self.get_module_dir() / self.templates_path
|
||||
|
||||
def get_locales_dir(self) -> Path | None:
|
||||
"""
|
||||
Get the filesystem path to this module's locales directory.
|
||||
|
||||
Returns:
|
||||
Path to locales directory, or None if not configured
|
||||
"""
|
||||
if not self.is_self_contained or not self.locales_path:
|
||||
return None
|
||||
return self.get_module_dir() / self.locales_path
|
||||
|
||||
def get_import_path(self, component: str) -> str | None:
|
||||
"""
|
||||
Get the Python import path for a module component.
|
||||
@@ -221,6 +235,8 @@ class ModuleDefinition:
|
||||
expected_dirs.append("schemas")
|
||||
if self.templates_path:
|
||||
expected_dirs.append(self.templates_path)
|
||||
if self.locales_path:
|
||||
expected_dirs.append(self.locales_path)
|
||||
|
||||
for dir_name in expected_dirs:
|
||||
dir_path = module_dir / dir_name
|
||||
|
||||
Reference in New Issue
Block a user