From 6c78827c7f3552df8e9cb12d832fc528b25f0382 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Tue, 24 Feb 2026 10:26:57 +0100 Subject: [PATCH] feat: add language switching to admin and merchant frontends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add cookie to ADMIN resolution chain (cookie → user_pref → "en") - Add explicit MERCHANT resolution (cookie → user_pref → "fr") - Add language selector dropdown to admin and merchant headers - Add languageSelector() function to merchant init-alpine.js - Add flag-icons CSS and i18n.js setup to merchant base template - Add compact flag-based language selector to both login pages - Make lang attribute dynamic on all base and login templates - Pass current_language to login route template context - Update architecture doc with ADMIN/MERCHANT resolution priorities Co-Authored-By: Claude Opus 4.6 --- app/modules/core/routes/pages/admin.py | 5 +- app/modules/core/routes/pages/merchant.py | 5 +- .../core/static/merchant/js/init-alpine.js | 46 +++++++++++++++++++ .../templates/tenancy/admin/login.html | 25 +++++++++- .../templates/tenancy/merchant/login.html | 25 +++++++++- app/templates/admin/base.html | 2 +- app/templates/admin/partials/header.html | 38 +++++++++++++++ app/templates/merchant/base.html | 18 +++++++- app/templates/merchant/partials/header.html | 38 +++++++++++++++ docs/architecture/language-i18n.md | 18 +++++--- middleware/language.py | 16 +++++-- 11 files changed, 219 insertions(+), 17 deletions(-) diff --git a/app/modules/core/routes/pages/admin.py b/app/modules/core/routes/pages/admin.py index c750fe81..dd8aff34 100644 --- a/app/modules/core/routes/pages/admin.py +++ b/app/modules/core/routes/pages/admin.py @@ -57,7 +57,10 @@ async def admin_login_page( if current_user: return RedirectResponse(url="/admin/dashboard", status_code=302) - return templates.TemplateResponse("tenancy/admin/login.html", {"request": request}) + return templates.TemplateResponse("tenancy/admin/login.html", { + "request": request, + "current_language": getattr(request.state, "language", "en"), + }) @router.get("/select-platform", response_class=HTMLResponse, include_in_schema=False) diff --git a/app/modules/core/routes/pages/merchant.py b/app/modules/core/routes/pages/merchant.py index 930f4b85..5059f752 100644 --- a/app/modules/core/routes/pages/merchant.py +++ b/app/modules/core/routes/pages/merchant.py @@ -67,7 +67,10 @@ async def merchant_login_page( if current_user: return RedirectResponse(url="/merchants/dashboard", status_code=302) - return templates.TemplateResponse("tenancy/merchant/login.html", {"request": request}) + return templates.TemplateResponse("tenancy/merchant/login.html", { + "request": request, + "current_language": getattr(request.state, "language", "fr"), + }) # ============================================================================ diff --git a/app/modules/core/static/merchant/js/init-alpine.js b/app/modules/core/static/merchant/js/init-alpine.js index aa7f4c8a..41a43537 100644 --- a/app/modules/core/static/merchant/js/init-alpine.js +++ b/app/modules/core/static/merchant/js/init-alpine.js @@ -201,3 +201,49 @@ function data() { } }; } + +/** + * Language Selector Component + * Alpine.js component for language switching in merchant portal + */ +function languageSelector(currentLang, enabledLanguages) { + return { + isLangOpen: false, + currentLang: currentLang || 'fr', + languages: enabledLanguages || ['en', 'fr', 'de', 'lb'], + languageNames: { + 'en': 'English', + 'fr': 'Français', + 'de': 'Deutsch', + 'lb': 'Lëtzebuergesch' + }, + languageFlags: { + 'en': 'gb', + 'fr': 'fr', + 'de': 'de', + 'lb': 'lu' + }, + async setLanguage(lang) { + if (lang === this.currentLang) { + this.isLangOpen = false; + return; + } + try { + const response = await fetch('/api/v1/platform/language/set', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ language: lang }) + }); + if (response.ok) { + this.currentLang = lang; + window.location.reload(); + } + } catch (error) { + console.error('Failed to set language:', error); + } + this.isLangOpen = false; + } + }; +} + +window.languageSelector = languageSelector; diff --git a/app/modules/tenancy/templates/tenancy/admin/login.html b/app/modules/tenancy/templates/tenancy/admin/login.html index 64ff69a6..317ea94e 100644 --- a/app/modules/tenancy/templates/tenancy/admin/login.html +++ b/app/modules/tenancy/templates/tenancy/admin/login.html @@ -1,6 +1,6 @@ {# app/templates/admin/login.html #} - + @@ -9,6 +9,12 @@ + + @@ -97,6 +103,23 @@ ← Back to Platform

+ + +
+ + + + +
diff --git a/app/modules/tenancy/templates/tenancy/merchant/login.html b/app/modules/tenancy/templates/tenancy/merchant/login.html index 374ddaa0..1a9cfc91 100644 --- a/app/modules/tenancy/templates/tenancy/merchant/login.html +++ b/app/modules/tenancy/templates/tenancy/merchant/login.html @@ -1,7 +1,7 @@ {# app/modules/tenancy/templates/tenancy/merchant/login.html #} {# Standalone login page - does NOT extend merchant/base.html #} - + @@ -10,6 +10,12 @@ + + @@ -128,6 +134,23 @@

+ + +
+ + + + +
diff --git a/app/templates/admin/base.html b/app/templates/admin/base.html index 3cfefbab..e8077347 100644 --- a/app/templates/admin/base.html +++ b/app/templates/admin/base.html @@ -1,6 +1,6 @@ {# app/templates/admin/base.html #} - + diff --git a/app/templates/admin/partials/header.html b/app/templates/admin/partials/header.html index f253d819..d9648495 100644 --- a/app/templates/admin/partials/header.html +++ b/app/templates/admin/partials/header.html @@ -50,6 +50,44 @@ + +
  • + +
    + +
    +
  • +
  • - + @@ -13,6 +13,13 @@ + + +