feat(i18n): complete post-launch i18n phases 5-8
Some checks failed
CI / dependency-scanning (push) Successful in 28s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped
CI / ruff (push) Successful in 12s
CI / pytest (push) Failing after 47m21s
CI / validate (push) Successful in 25s

- Phase 5: Translate homepage-modern.html (~90 new locale keys, all
  hardcoded strings replaced with _() calls for dashboard mock,
  features, pricing tiers, testimonial sections)
- Phase 6: Translate homepage-minimal.html (17 new locale keys for
  fallback content, features, and CTA sections)
- Phase 7: Add multi-language page.title/content support with
  title_translations and content_translations JSON columns, Alembic
  migration cms_002, translated title/content resolution in templates,
  and seed script updates with tt() helper
- Phase 8: Complete lb.json audit — fill 6 missing keys (messages,
  confirmations), also backfill same keys in fr.json and de.json

All 4 locale files now have 340 keys with full parity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 05:50:06 +01:00
parent 05c53e1865
commit b8aa484653
16 changed files with 965 additions and 235 deletions

View File

@@ -118,6 +118,21 @@ class ContentPage(Base):
comment="Structured homepage sections (hero, features, pricing, cta) with i18n",
)
# Multi-language title and content (JSON dicts keyed by language code)
# e.g. {"en": "About Us", "fr": "À propos", "de": "Über uns", "lb": "Iwwer eis"}
title_translations = Column(
JSON,
nullable=True,
default=None,
comment="Language-keyed title dict for multi-language support",
)
content_translations = Column(
JSON,
nullable=True,
default=None,
comment="Language-keyed content dict for multi-language support",
)
# SEO
meta_description = Column(String(300), nullable=True)
meta_keywords = Column(String(300), nullable=True)
@@ -195,6 +210,26 @@ class ContentPage(Base):
return "store_default"
return "store_override"
def get_translated_title(self, lang: str, default_lang: str = "fr") -> str:
"""Get title in the given language, falling back to default_lang then self.title."""
if self.title_translations:
return (
self.title_translations.get(lang)
or self.title_translations.get(default_lang)
or self.title
)
return self.title
def get_translated_content(self, lang: str, default_lang: str = "fr") -> str:
"""Get content in the given language, falling back to default_lang then self.content."""
if self.content_translations:
return (
self.content_translations.get(lang)
or self.content_translations.get(default_lang)
or self.content
)
return self.content
def to_dict(self):
"""Convert to dictionary for API responses."""
return {
@@ -206,7 +241,9 @@ class ContentPage(Base):
"store_name": self.store.name if self.store else None,
"slug": self.slug,
"title": self.title,
"title_translations": self.title_translations,
"content": self.content,
"content_translations": self.content_translations,
"content_format": self.content_format,
"template": self.template,
"sections": self.sections,