feat(i18n): add multilingual platform descriptions and HostWizard demo data
Some checks failed
Some checks failed
- Add description_translations JSON column to Platform model + migration - Add language tabs to platform admin edit form for multilingual descriptions - Update API schemas to include description_translations in request/response - Translate pricing section UI labels via _t() macro (monthly/annual/CTA/etc.) - Add Luxembourgish (lb) support to all platforms (OMS, Main, Loyalty, Hosting) - Seed description_translations, contact emails, and social links for all platforms - Add LuxWeb Agency demo merchant with hosting stores, team, and content pages - Fix language code typo: lu → lb in platform-edit.js availableLanguages - Fix store content pages to use correct primary platform instead of hardcoded OMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -214,45 +214,103 @@ def create_default_platforms(db: Session) -> list[Platform]:
|
||||
"code": "oms",
|
||||
"name": "OMS",
|
||||
"description": "Order Management System for multi-store e-commerce",
|
||||
"description_translations": {
|
||||
"fr": "Système de gestion des commandes pour le commerce multi-boutiques",
|
||||
"de": "Bestellverwaltungssystem für Multi-Store-E-Commerce",
|
||||
"en": "Order Management System for multi-store e-commerce",
|
||||
"lb": "Bestellverwaltungssystem fir Multi-Store-E-Commerce",
|
||||
},
|
||||
"domain": "omsflow.lu",
|
||||
"path_prefix": "oms",
|
||||
"default_language": "fr",
|
||||
"supported_languages": ["fr", "de", "en"],
|
||||
"settings": {},
|
||||
"supported_languages": ["fr", "de", "en", "lb"],
|
||||
"settings": {
|
||||
"contact_email": "info@omsflow.lu",
|
||||
"support_email": "support@omsflow.lu",
|
||||
},
|
||||
"theme_config": {},
|
||||
},
|
||||
{
|
||||
"code": "main",
|
||||
"name": "Wizard",
|
||||
"description": "Main marketing site showcasing all Orion platforms",
|
||||
"description_translations": {
|
||||
"fr": "Site marketing principal présentant toutes les plateformes Orion",
|
||||
"de": "Zentrale Marketingseite für alle Orion-Plattformen",
|
||||
"en": "Main marketing site showcasing all Orion platforms",
|
||||
"lb": "Haaptmarketingsäit fir all Orion-Plattformen",
|
||||
},
|
||||
"domain": "wizard.lu",
|
||||
"path_prefix": None,
|
||||
"default_language": "fr",
|
||||
"supported_languages": ["fr", "de", "en"],
|
||||
"settings": {"is_marketing_site": True},
|
||||
"theme_config": {"primary_color": "#2563EB", "secondary_color": "#3B82F6"},
|
||||
"supported_languages": ["fr", "de", "en", "lb"],
|
||||
"settings": {
|
||||
"is_marketing_site": True,
|
||||
"contact_email": "info@wizard.lu",
|
||||
"support_email": "support@wizard.lu",
|
||||
},
|
||||
"theme_config": {
|
||||
"primary_color": "#2563EB",
|
||||
"secondary_color": "#3B82F6",
|
||||
"social": {
|
||||
"facebook": "https://facebook.com/wizard.lu",
|
||||
"linkedin": "https://linkedin.com/company/wizard-lu",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"code": "loyalty",
|
||||
"name": "Loyalty",
|
||||
"description": "Customer loyalty program platform for Luxembourg businesses",
|
||||
"description_translations": {
|
||||
"fr": "Plateforme de programme de fidélité pour les entreprises luxembourgeoises",
|
||||
"de": "Kundenbindungsprogramm-Plattform für luxemburgische Unternehmen",
|
||||
"en": "Customer loyalty program platform for Luxembourg businesses",
|
||||
"lb": "Clientsfidélitéitsprogramm-Plattform fir lëtzebuerger Betriber",
|
||||
},
|
||||
"domain": "rewardflow.lu",
|
||||
"path_prefix": "loyalty",
|
||||
"default_language": "fr",
|
||||
"supported_languages": ["fr", "de", "en"],
|
||||
"settings": {"features": ["points", "rewards", "tiers", "analytics"]},
|
||||
"theme_config": {"primary_color": "#8B5CF6", "secondary_color": "#A78BFA"},
|
||||
"supported_languages": ["fr", "de", "en", "lb"],
|
||||
"settings": {
|
||||
"features": ["points", "rewards", "tiers", "analytics"],
|
||||
"contact_email": "info@rewardflow.lu",
|
||||
"support_email": "support@rewardflow.lu",
|
||||
},
|
||||
"theme_config": {
|
||||
"primary_color": "#8B5CF6",
|
||||
"secondary_color": "#A78BFA",
|
||||
},
|
||||
},
|
||||
{
|
||||
"code": "hosting",
|
||||
"name": "HostWizard",
|
||||
"description": "Web hosting, domains, email, and website building for Luxembourg businesses",
|
||||
"description_translations": {
|
||||
"fr": "Hébergement web, domaines, e-mail et création de sites pour les entreprises luxembourgeoises",
|
||||
"de": "Webhosting, Domains, E-Mail und Website-Erstellung für luxemburgische Unternehmen",
|
||||
"en": "Web hosting, domains, email, and website building for Luxembourg businesses",
|
||||
"lb": "Webhosting, Domainer, E-Mail a Websäit-Erstellung fir lëtzebuerger Betriber",
|
||||
},
|
||||
"domain": "hostwizard.lu",
|
||||
"path_prefix": "hosting",
|
||||
"default_language": "fr",
|
||||
"supported_languages": ["fr", "de", "en", "lb"],
|
||||
"settings": {"features": ["hosting", "domains", "email", "ssl", "poc_sites"]},
|
||||
"theme_config": {"primary_color": "#0D9488", "secondary_color": "#14B8A6"},
|
||||
"settings": {
|
||||
"features": ["hosting", "domains", "email", "ssl", "poc_sites"],
|
||||
"contact_email": "info@hostwizard.lu",
|
||||
"support_email": "support@hostwizard.lu",
|
||||
"sales_email": "sales@hostwizard.lu",
|
||||
},
|
||||
"theme_config": {
|
||||
"primary_color": "#0D9488",
|
||||
"secondary_color": "#14B8A6",
|
||||
"social": {
|
||||
"facebook": "https://facebook.com/hostwizard.lu",
|
||||
"linkedin": "https://linkedin.com/company/hostwizard",
|
||||
"instagram": "https://instagram.com/hostwizard.lu",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -271,6 +329,7 @@ def create_default_platforms(db: Session) -> list[Platform]:
|
||||
code=pdef["code"],
|
||||
name=pdef["name"],
|
||||
description=pdef["description"],
|
||||
description_translations=pdef.get("description_translations"),
|
||||
domain=pdef["domain"],
|
||||
path_prefix=pdef["path_prefix"],
|
||||
default_language=pdef["default_language"],
|
||||
|
||||
@@ -140,6 +140,19 @@ DEMO_COMPANIES = [
|
||||
"business_address": "789 Library Lane, Esch-sur-Alzette, L-9012, Luxembourg",
|
||||
"tax_number": "LU34567890",
|
||||
},
|
||||
{
|
||||
"name": "LuxWeb Agency S.à r.l.",
|
||||
"description": "Web design and hosting agency serving Luxembourg businesses",
|
||||
"owner_email": "marc.owner@luxweb.lu",
|
||||
"owner_password": "password123", # noqa: SEC001
|
||||
"owner_first_name": "Marc",
|
||||
"owner_last_name": "Weber",
|
||||
"contact_email": "info@luxweb.lu",
|
||||
"contact_phone": "+352 456 789 012",
|
||||
"website": "https://www.luxweb.lu",
|
||||
"business_address": "12 Rue du Web, Differdange, L-4501, Luxembourg",
|
||||
"tax_number": "LU45678901",
|
||||
},
|
||||
]
|
||||
|
||||
# Demo store configurations (linked to merchants by index)
|
||||
@@ -212,6 +225,26 @@ DEMO_STORES = [
|
||||
"theme_preset": "modern",
|
||||
"custom_domain": None,
|
||||
},
|
||||
# LuxWeb Agency stores (hosting platform)
|
||||
{
|
||||
"merchant_index": 3, # LuxWeb Agency
|
||||
"store_code": "LUXWEBSITES",
|
||||
"name": "LuxWeb Sites",
|
||||
"subdomain": "luxweb",
|
||||
"description": "Professional websites for Luxembourg businesses",
|
||||
"theme_preset": "modern",
|
||||
"custom_domain": "luxweb.lu",
|
||||
"custom_domain_platform": "hosting",
|
||||
},
|
||||
{
|
||||
"merchant_index": 3, # LuxWeb Agency
|
||||
"store_code": "LUXWEBHOSTING",
|
||||
"name": "LuxWeb Hosting",
|
||||
"subdomain": "luxwebhosting",
|
||||
"description": "Web hosting, domains, and email services",
|
||||
"theme_preset": "modern",
|
||||
"custom_domain": None,
|
||||
},
|
||||
]
|
||||
|
||||
# Demo subscriptions (linked to merchants by index)
|
||||
@@ -223,6 +256,8 @@ DEMO_SUBSCRIPTIONS = [
|
||||
{"merchant_index": 1, "platform_code": "loyalty", "tier_code": "essential", "trial_days": 14},
|
||||
# BookWorld: OMS (business, active)
|
||||
{"merchant_index": 2, "platform_code": "oms", "tier_code": "business", "trial_days": 0},
|
||||
# LuxWeb Agency: Hosting (professional, active)
|
||||
{"merchant_index": 3, "platform_code": "hosting", "tier_code": "professional", "trial_days": 0},
|
||||
]
|
||||
|
||||
# Demo team members (linked to merchants by index, assigned to stores by store_code)
|
||||
@@ -276,6 +311,25 @@ DEMO_TEAM_MEMBERS = [
|
||||
"role": "manager",
|
||||
"store_codes": ["BOOKSTORE", "BOOKDIGITAL"],
|
||||
},
|
||||
# LuxWeb Agency team
|
||||
{
|
||||
"merchant_index": 3,
|
||||
"email": "sophie.dev@luxweb.lu",
|
||||
"password": "password123", # noqa: SEC001
|
||||
"first_name": "Sophie",
|
||||
"last_name": "Developer",
|
||||
"role": "manager",
|
||||
"store_codes": ["LUXWEBSITES", "LUXWEBHOSTING"],
|
||||
},
|
||||
{
|
||||
"merchant_index": 3,
|
||||
"email": "tom.support@luxweb.lu",
|
||||
"password": "password123", # noqa: SEC001
|
||||
"first_name": "Tom",
|
||||
"last_name": "Support",
|
||||
"role": "support",
|
||||
"store_codes": ["LUXWEBHOSTING"],
|
||||
},
|
||||
]
|
||||
|
||||
# Theme presets
|
||||
@@ -397,6 +451,69 @@ STORE_CONTENT_PAGES = {
|
||||
"show_in_footer": True,
|
||||
},
|
||||
],
|
||||
"LUXWEBSITES": [
|
||||
{
|
||||
"slug": "about",
|
||||
"title": "About LuxWeb Sites",
|
||||
"content": """
|
||||
<div class="prose-content">
|
||||
<h2>Welcome to LuxWeb Sites</h2>
|
||||
<p>Professional web design and development for Luxembourg businesses.
|
||||
We create modern, responsive websites that help your business grow online.</p>
|
||||
|
||||
<h3>Our Services</h3>
|
||||
<ul>
|
||||
<li><strong>Website Design:</strong> Custom designs tailored to your brand</li>
|
||||
<li><strong>E-commerce:</strong> Online shops with secure payment integration</li>
|
||||
<li><strong>Multilingual Sites:</strong> FR, DE, EN, and LB support built-in</li>
|
||||
<li><strong>SEO Optimization:</strong> Get found on Google Luxembourg</li>
|
||||
<li><strong>Maintenance:</strong> Ongoing updates and technical support</li>
|
||||
</ul>
|
||||
|
||||
<h3>Why LuxWeb?</h3>
|
||||
<p>Based in Luxembourg, we understand the local market. All our sites are GDPR compliant
|
||||
and optimized for the multilingual Luxembourg audience.</p>
|
||||
|
||||
<h3>Contact Us</h3>
|
||||
<p>12 Rue du Web, Differdange<br>
|
||||
Email: info@luxweb.lu | Phone: +352 456 789 012</p>
|
||||
</div>
|
||||
""",
|
||||
"meta_description": "LuxWeb Sites - Professional web design for Luxembourg businesses",
|
||||
"show_in_header": True,
|
||||
"show_in_footer": True,
|
||||
},
|
||||
{
|
||||
"slug": "contact",
|
||||
"title": "Contact LuxWeb",
|
||||
"content": """
|
||||
<div class="prose-content">
|
||||
<h2>Get in Touch</h2>
|
||||
|
||||
<h3>Request a Quote</h3>
|
||||
<p>Tell us about your project and we'll get back to you within 24 hours.</p>
|
||||
<ul>
|
||||
<li><strong>Email:</strong> info@luxweb.lu</li>
|
||||
<li><strong>Phone:</strong> +352 456 789 012</li>
|
||||
<li><strong>WhatsApp:</strong> +352 456 789 012</li>
|
||||
</ul>
|
||||
|
||||
<h3>Technical Support</h3>
|
||||
<p>Existing clients can reach our support team:</p>
|
||||
<ul>
|
||||
<li><strong>Email:</strong> support@luxweb.lu</li>
|
||||
<li><strong>Hours:</strong> Monday-Friday, 8am-6pm CET</li>
|
||||
</ul>
|
||||
|
||||
<h3>Office</h3>
|
||||
<p>12 Rue du Web<br>Differdange, L-4501<br>Luxembourg</p>
|
||||
</div>
|
||||
""",
|
||||
"meta_description": "Contact LuxWeb for web design, hosting, and domain services in Luxembourg",
|
||||
"show_in_header": True,
|
||||
"show_in_footer": True,
|
||||
},
|
||||
],
|
||||
"BOOKSTORE": [
|
||||
{
|
||||
"slug": "about",
|
||||
@@ -1157,13 +1274,22 @@ def create_demo_store_content_pages(db: Session, stores: list[Store]) -> int:
|
||||
"""
|
||||
created_count = 0
|
||||
|
||||
# Get the OMS platform ID (stores are registered on OMS)
|
||||
# Build store→primary platform lookup
|
||||
from app.modules.tenancy.models import Platform
|
||||
|
||||
store_primary_platform: dict[int, int] = {}
|
||||
sp_rows = db.execute(
|
||||
select(StorePlatform.store_id, StorePlatform.platform_id)
|
||||
.where(StorePlatform.is_primary == True) # noqa: E712
|
||||
).all()
|
||||
for store_id, platform_id in sp_rows:
|
||||
store_primary_platform[store_id] = platform_id
|
||||
|
||||
# Fallback: OMS platform ID
|
||||
oms_platform = db.execute(
|
||||
select(Platform).where(Platform.code == "oms")
|
||||
).scalar_one_or_none()
|
||||
default_platform_id = oms_platform.id if oms_platform else 1
|
||||
fallback_platform_id = oms_platform.id if oms_platform else 1
|
||||
|
||||
for store in stores:
|
||||
store_pages = STORE_CONTENT_PAGES.get(store.store_code, [])
|
||||
@@ -1171,6 +1297,8 @@ def create_demo_store_content_pages(db: Session, stores: list[Store]) -> int:
|
||||
if not store_pages:
|
||||
continue
|
||||
|
||||
platform_id = store_primary_platform.get(store.id, fallback_platform_id)
|
||||
|
||||
for page_data in store_pages:
|
||||
# Check if this store page already exists
|
||||
existing = db.execute(
|
||||
@@ -1185,7 +1313,7 @@ def create_demo_store_content_pages(db: Session, stores: list[Store]) -> int:
|
||||
|
||||
# Create store content page override
|
||||
page = ContentPage(
|
||||
platform_id=default_platform_id,
|
||||
platform_id=platform_id,
|
||||
store_id=store.id,
|
||||
slug=page_data["slug"],
|
||||
title=page_data["title"],
|
||||
|
||||
Reference in New Issue
Block a user