fix(storefront): i18n sweep + locale-aware reset-password and welcome email
Some checks failed
CI / ruff (push) Successful in 19s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

Test 5 (storefront password reset + customer dashboard) surfaced five
issues that all traced back to missing i18n plumbing:

- Forgot-password email arrived in EN regardless of storefront locale —
  handler now prefers request.state.language over customer.preferred_language,
  and loyalty self-enrollment backfills preferred_language for new + returning
  customers so future locale-sensitive flows hit the right language without
  being told twice.
- reset-password.html rendered "undefined" icon boxes because $icon magic
  wasn't loaded in the standalone page — replaced with inline SVGs matching
  the forgot-password.html convention.
- reset-password.html was hardcoded English: added lang attr, full _()
  sweep (22 new auth.* keys × 4 locales), language selector, and JS
  validation strings exposed via tojson.
- "Continue shopping" CTA renamed to "Back to Home" (auth.back_to_home,
  4 locales) on login + forgot + reset — loyalty storefronts have no
  catalog to continue to, mirroring the earlier enroll-success rename.
- /account dashboard, profile, addresses were hardcoded English in the
  body (menu was FR because base layout uses _()). New customers.storefront
  .pages.{dashboard,profile,addresses}.* namespace (~80 keys × 4 locales),
  templates updated, Alpine JS strings injected via window.__*I18n.

18 files, 18 changed; arch validation: 126 warnings before = 126 after,
mkdocs --strict clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 23:06:11 +02:00
parent f04cbb8ca2
commit 10a99f98fe
18 changed files with 722 additions and 162 deletions

View File

@@ -94,6 +94,7 @@ def self_enroll(
customer_name=data.customer_name,
customer_phone=data.customer_phone,
customer_birthday=data.customer_birthday,
customer_language=getattr(request.state, "language", None),
)
logger.info(f"Self-enrollment for customer {customer_id} at store {store.subdomain}")

View File

@@ -188,6 +188,7 @@ class CardService:
customer_name: str | None = None,
customer_phone: str | None = None,
customer_birthday: date | None = None,
customer_language: str | None = None,
) -> int:
"""
Resolve a customer ID from either a direct ID or email lookup.
@@ -224,11 +225,18 @@ class CardService:
customer = customer_service.get_customer_by_email(db, store_id, email)
if customer:
# Backfill birthday on existing customer if they didn't have
# one before — keeps the enrollment form useful for returning
# customers who never previously provided a birthday.
# Backfill birthday + preferred_language on existing customer
# if they were missing — keeps the enrollment form useful for
# returning customers and lets transactional emails (welcome,
# password reset) hit the right locale.
dirty = False
if customer_birthday and not customer.birth_date:
customer.birth_date = customer_birthday
dirty = True
if customer_language and not customer.preferred_language:
customer.preferred_language = customer_language
dirty = True
if dirty:
db.flush()
return customer.id
@@ -250,8 +258,17 @@ class CardService:
.first()
)
if existing_cardholder:
dirty = False
if customer_birthday and not existing_cardholder.birth_date:
existing_cardholder.birth_date = customer_birthday
dirty = True
if (
customer_language
and not existing_cardholder.preferred_language
):
existing_cardholder.preferred_language = customer_language
dirty = True
if dirty:
db.flush()
return existing_cardholder.id
@@ -272,6 +289,7 @@ class CardService:
last_name=last_name,
phone=customer_phone,
birth_date=customer_birthday,
preferred_language=customer_language,
)
logger.info(
f"Created customer {customer.id} ({email}) "