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

@@ -2,7 +2,7 @@
{# 3-Step Signup Wizard: Plan → Account → Payment #}
{% extends "platform/base.html" %}
{% block title %}Start Your Free Trial - Orion{% endblock %}
{% block title %}{{ _("cms.platform.signup.page_title") }} - {{ platform.name if platform else 'Orion' }}{% endblock %}
{% block extra_head %}
{# Stripe.js for payment #}
@@ -16,7 +16,7 @@
{# Progress Steps #}
<div class="mb-12">
<div class="flex items-center justify-between">
<template x-for="(stepName, index) in ['Select Plan', 'Account', 'Payment']" :key="index">
<template x-for="(stepName, index) in ['{{ _("cms.platform.signup.step_plan") }}', '{{ _("cms.platform.signup.step_account") }}', '{{ _("cms.platform.signup.step_payment") }}']" :key="index">
<div class="flex items-center" :class="index < 2 ? 'flex-1' : ''">
<div class="flex items-center justify-center w-10 h-10 rounded-full font-semibold transition-colors"
:class="currentStep > index + 1 ? 'bg-green-500 text-white' : currentStep === index + 1 ? 'bg-indigo-600 text-white' : 'bg-gray-200 dark:bg-gray-700 text-gray-500'">
@@ -50,11 +50,11 @@
STEP 1: SELECT PLAN
=============================================================== #}
<div x-show="currentStep === 1" class="p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6">Choose Your Plan</h2>
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6">{{ _("cms.platform.signup.choose_plan") }}</h2>
{# Billing Toggle #}
<div class="flex items-center justify-center mb-8 space-x-4">
<span :class="!isAnnual ? 'font-semibold text-gray-900 dark:text-white' : 'text-gray-500'">Monthly</span>
<span :class="!isAnnual ? 'font-semibold text-gray-900 dark:text-white' : 'text-gray-500'">{{ _("cms.platform.pricing.monthly") }}</span>
<button @click="isAnnual = !isAnnual"
class="relative w-12 h-6 rounded-full transition-colors"
:class="isAnnual ? 'bg-indigo-600' : 'bg-gray-300'">
@@ -62,7 +62,7 @@
:class="isAnnual ? 'translate-x-6' : ''"></span>
</button>
<span :class="isAnnual ? 'font-semibold text-gray-900 dark:text-white' : 'text-gray-500'">
Annual <span class="text-green-600 text-xs">Save 17%</span>
{{ _("cms.platform.pricing.annual") }} <span class="text-green-600 text-xs">{{ _("cms.platform.signup.save_percent", percent=17) }}</span>
</span>
</div>
@@ -80,17 +80,17 @@
<div>
<h3 class="font-semibold text-gray-900 dark:text-white">{{ tier.name }}</h3>
<p class="text-sm text-gray-500">
{% if tier.orders_per_month %}{{ tier.orders_per_month }} orders/mo{% else %}Unlimited{% endif %}
{% if tier.orders_per_month %}{{ tier.orders_per_month }} {{ _("cms.platform.signup.orders_per_month") }}{% else %}{{ _("cms.platform.signup.unlimited") }}{% endif %}
&bull;
{% if tier.team_members %}{{ tier.team_members }} user{% if tier.team_members > 1 %}s{% endif %}{% else %}Unlimited{% endif %}
{% if tier.team_members %}{{ tier.team_members }} {{ _("cms.platform.signup.team_members", count=tier.team_members) }}{% else %}{{ _("cms.platform.signup.unlimited") }}{% endif %}
</p>
</div>
<div class="text-right">
<template x-if="!isAnnual">
<span class="text-xl font-bold text-gray-900 dark:text-white">{{ tier.price_monthly|int }}&euro;/mo</span>
<span class="text-xl font-bold text-gray-900 dark:text-white">{{ tier.price_monthly|int }}&euro;{{ _("cms.platform.signup.per_month_short") }}</span>
</template>
<template x-if="isAnnual">
<span class="text-xl font-bold text-gray-900 dark:text-white">{{ ((tier.price_annual or tier.price_monthly * 12) / 12)|round(0)|int }}&euro;/mo</span>
<span class="text-xl font-bold text-gray-900 dark:text-white">{{ ((tier.price_annual or tier.price_monthly * 12) / 12)|round(0)|int }}&euro;{{ _("cms.platform.signup.per_month_short") }}</span>
</template>
</div>
</div>
@@ -103,15 +103,15 @@
{# Free Trial Note #}
<div class="mt-6 p-4 bg-green-50 dark:bg-green-900/20 rounded-xl">
<p class="text-sm text-green-800 dark:text-green-300">
<strong>{{ trial_days }}-day free trial.</strong>
We'll collect your payment info, but you won't be charged until the trial ends.
<strong>{{ trial_days }}-{{ _("cms.platform.signup.trial_info_days") }}</strong>
{{ _("cms.platform.signup.trial_info") }}
</p>
</div>
<button @click="startSignup()"
:disabled="!selectedTier || loading"
class="mt-8 w-full py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-xl transition-colors disabled:opacity-50">
Continue
{{ _("cms.platform.signup.continue") }}
</button>
</div>
@@ -119,23 +119,23 @@
STEP 2: CREATE ACCOUNT
=============================================================== #}
<div x-show="currentStep === 2" class="p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Create Your Account</h2>
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">{{ _("cms.platform.signup.create_account") }}</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
<span class="text-red-500">*</span> Required fields
<span class="text-red-500">*</span> {{ _("cms.platform.signup.required_fields") }}
</p>
<div class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
First Name <span class="text-red-500">*</span>
{{ _("cms.platform.signup.first_name") }} <span class="text-red-500">*</span>
</label>
<input type="text" x-model="account.firstName" required
class="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white"/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Last Name <span class="text-red-500">*</span>
{{ _("cms.platform.signup.last_name") }} <span class="text-red-500">*</span>
</label>
<input type="text" x-model="account.lastName" required
class="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white"/>
@@ -144,7 +144,7 @@
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Business Name <span class="text-red-500">*</span>
{{ _("cms.platform.signup.merchant_name") }} <span class="text-red-500">*</span>
</label>
<input type="text" x-model="account.merchantName" required
class="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white"/>
@@ -152,7 +152,7 @@
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Email <span class="text-red-500">*</span>
{{ _("cms.platform.signup.email") }} <span class="text-red-500">*</span>
</label>
<input type="email" x-model="account.email" required
class="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white"/>
@@ -160,11 +160,11 @@
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Password <span class="text-red-500">*</span>
{{ _("cms.platform.signup.password") }} <span class="text-red-500">*</span>
</label>
<input type="password" x-model="account.password" required minlength="8"
class="w-full px-4 py-3 rounded-xl border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white"/>
<p class="text-xs text-gray-500 mt-1">Minimum 8 characters</p>
<p class="text-xs text-gray-500 mt-1">{{ _("cms.platform.signup.password_hint") }}</p>
</div>
<template x-if="accountError">
@@ -177,12 +177,12 @@
<div class="mt-8 flex gap-4">
<button @click="currentStep = 1"
class="flex-1 py-3 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 font-semibold rounded-xl">
Back
{{ _("cms.platform.signup.back") }}
</button>
<button @click="createAccount()"
:disabled="loading || !isAccountValid()"
class="flex-1 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-xl disabled:opacity-50">
Continue to Payment
{{ _("cms.platform.signup.continue_payment") }}
</button>
</div>
</div>
@@ -191,8 +191,8 @@
STEP 3: PAYMENT
=============================================================== #}
<div x-show="currentStep === 3" class="p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Add Payment Method</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">You won't be charged until your {{ trial_days }}-day trial ends.</p>
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">{{ _("cms.platform.signup.add_payment") }}</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">{{ _("cms.platform.signup.no_charge_note", trial_days=trial_days) }}</p>
{# Stripe Card Element #}
<div id="card-element" class="p-4 border border-gray-300 dark:border-gray-600 rounded-xl bg-gray-50 dark:bg-gray-900"></div>
@@ -201,16 +201,16 @@
<div class="mt-8 flex gap-4">
<button @click="currentStep = 2"
class="flex-1 py-3 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 font-semibold rounded-xl">
Back
{{ _("cms.platform.signup.back") }}
</button>
<button @click="submitPayment()"
:disabled="loading || paymentProcessing"
class="flex-1 py-3 bg-green-600 hover:bg-green-700 text-white font-semibold rounded-xl disabled:opacity-50">
<template x-if="paymentProcessing">
<span>Processing...</span>
<span>{{ _("cms.platform.signup.processing") }}</span>
</template>
<template x-if="!paymentProcessing">
<span>Start Free Trial</span>
<span>{{ _("cms.platform.signup.start_trial") }}</span>
</template>
</button>
</div>
@@ -224,6 +224,13 @@
{% block extra_scripts %}
<script>
function signupWizard() {
const MSGS = {
failedStart: '{{ _("cms.platform.signup.error_start") }}',
failedAccount: '{{ _("cms.platform.signup.error_account") }}',
paymentNotConfigured: '{{ _("cms.platform.signup.error_payment_config") }}',
paymentFailed: '{{ _("cms.platform.signup.error_payment") }}',
};
return {
currentStep: 1,
loading: false,
@@ -286,11 +293,11 @@ function signupWizard() {
this.sessionId = data.session_id;
this.currentStep = 2;
} else {
alert(data.detail || 'Failed to start signup');
alert(data.detail || MSGS.failedStart);
}
} catch (error) {
console.error('Error:', error);
alert('Failed to start signup. Please try again.');
alert(MSGS.failedStart);
} finally {
this.loading = false;
}
@@ -326,11 +333,11 @@ function signupWizard() {
if (response.ok) {
this.currentStep = 3;
} else {
this.accountError = data.detail || 'Failed to create account';
this.accountError = data.detail || MSGS.failedAccount;
}
} catch (error) {
console.error('Error:', error);
this.accountError = 'Failed to create account. Please try again.';
this.accountError = MSGS.failedAccount;
} finally {
this.loading = false;
}
@@ -379,7 +386,7 @@ function signupWizard() {
async submitPayment() {
if (!this.stripe || !this.clientSecret) {
alert('Payment not configured. Please contact support.');
alert(MSGS.paymentNotConfigured);
return;
}
@@ -416,11 +423,11 @@ function signupWizard() {
}
window.location.href = '/signup/success?store_code=' + data.store_code;
} else {
alert(data.detail || 'Failed to complete signup');
alert(data.detail || MSGS.paymentFailed);
}
} catch (error) {
console.error('Payment error:', error);
alert('Payment failed. Please try again.');
alert(MSGS.paymentFailed);
} finally {
this.paymentProcessing = false;
}