Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8.5 KiB
Language & Internationalization (i18n) Implementation
Overview
This document describes the implementation of multi-language support for the Orion platform. The system supports four languages (English, French, German, Luxembourgish) with flexible configuration at store, user, and customer levels.
Supported Languages
| Code | Language | Native Name | Flag |
|---|---|---|---|
en |
English | English | GB |
fr |
French | Francais | FR |
de |
German | Deutsch | DE |
lb |
Luxembourgish | Letzebuerg | LU |
Default Language: French (fr) - reflecting the Luxembourg market context.
Database Changes
Migration: fcfdc02d5138_add_language_settings_to_store_user_customer
Stores Table
New columns added to stores:
ALTER TABLE stores ADD COLUMN default_language VARCHAR(5) NOT NULL DEFAULT 'fr';
ALTER TABLE stores ADD COLUMN dashboard_language VARCHAR(5) NOT NULL DEFAULT 'fr';
ALTER TABLE stores ADD COLUMN storefront_language VARCHAR(5) NOT NULL DEFAULT 'fr';
ALTER TABLE stores ADD COLUMN storefront_languages JSON NOT NULL DEFAULT '["fr", "de", "en"]';
| Column | Type | Description |
|---|---|---|
default_language |
VARCHAR(5) | Fallback language for content when translation unavailable |
dashboard_language |
VARCHAR(5) | Default UI language for store dashboard |
storefront_language |
VARCHAR(5) | Default language for customer-facing shop |
storefront_languages |
JSON | Array of enabled languages for storefront selector |
Users Table
ALTER TABLE users ADD COLUMN preferred_language VARCHAR(5) NULL;
| Column | Type | Description |
|---|---|---|
preferred_language |
VARCHAR(5) | User's preferred dashboard language (overrides store setting) |
Customers Table
ALTER TABLE customers ADD COLUMN preferred_language VARCHAR(5) NULL;
| Column | Type | Description |
|---|---|---|
preferred_language |
VARCHAR(5) | Customer's preferred shop language (overrides store setting) |
Architecture
Language Resolution Flow
Store Dashboard
User preferred_language
|
v (if not set)
Store dashboard_language
|
v (if not set)
System DEFAULT_LANGUAGE (fr)
Storefront
Customer preferred_language
|
v (if not set)
Session/Cookie language
|
v (if not set)
Store storefront_language
|
v (if not set)
Browser Accept-Language header
|
v (if not supported)
System DEFAULT_LANGUAGE (fr)
Files Created/Modified
New Files
| File | Description |
|---|---|
app/utils/i18n.py |
Core i18n utilities, translation loading, language resolution |
middleware/language.py |
Language detection middleware |
app/api/v1/shared/language.py |
Language API endpoints |
app/templates/shared/macros/language_selector.html |
UI components for language selection |
static/locales/en.json |
English translations |
static/locales/fr.json |
French translations |
static/locales/de.json |
German translations |
static/locales/lb.json |
Luxembourgish translations |
Modified Files
| File | Changes |
|---|---|
models/database/store.py |
Added language settings columns |
models/database/user.py |
Added preferred_language column |
models/database/customer.py |
Added preferred_language column |
models/schema/store.py |
Added language fields to Pydantic schemas |
models/schema/auth.py |
Added preferred_language to user schemas |
models/schema/customer.py |
Added preferred_language to customer schemas |
main.py |
Registered LanguageMiddleware |
app/api/main.py |
Registered language API router |
API Endpoints
Language API (/api/v1/language)
| Endpoint | Method | Description |
|---|---|---|
/api/v1/language/set |
POST | Set language preference (cookie) |
/api/v1/language/current |
GET | Get current language info |
/api/v1/language/list |
GET | List all available languages |
/api/v1/language/clear |
DELETE | Clear language preference |
Request/Response Examples
Set Language
POST /api/v1/language/set
Content-Type: application/json
{
"language": "de"
}
Response:
{
"success": true,
"language": "de",
"message": "Language set to Deutsch"
}
Translation Files
Translation files are stored in static/locales/{lang}.json with the following structure:
{
"common": {
"save": "Save",
"cancel": "Cancel"
},
"auth": {
"login": "Login",
"logout": "Logout"
},
"nav": {
"dashboard": "Dashboard",
"products": "Products"
}
}
Key Naming Convention
- Use dot notation for nested keys:
common.save,auth.login - Use snake_case for key names:
product_name,order_status - Group by feature/section:
products.add_product,orders.confirm_order
Template Integration
Using Translations in Jinja2
{# Import the translation function #}
{% from 'shared/macros/language_selector.html' import language_selector %}
{# Use translation function #}
{{ _('common.save') }}
{# With interpolation #}
{{ _('welcome.message', name=user.name) }}
{# Language selector component #}
{{ language_selector(
current_language=request.state.language,
enabled_languages=store.storefront_languages
) }}
Request State Variables
The LanguageMiddleware sets these on request.state:
| Variable | Type | Description |
|---|---|---|
language |
str | Resolved language code |
language_info |
dict | Additional info (source, cookie value, browser value) |
Middleware Order
The LanguageMiddleware must run after ContextMiddleware (to know the context type) and before ThemeContextMiddleware.
Execution order (request flow):
- LoggingMiddleware
- StoreContextMiddleware
- ContextMiddleware
- LanguageMiddleware <-- Detects language
- ThemeContextMiddleware
- FastAPI Router
UI Components
Full Language Selector
{{ language_selector(
current_language='fr',
enabled_languages=['fr', 'de', 'en'],
position='right',
show_label=true
) }}
Compact Selector (Flag Only)
{{ language_selector_compact(
current_language='fr',
enabled_languages=['fr', 'de', 'en']
) }}
Language Settings Form
For store settings pages:
{{ language_settings_form(
current_settings={
'default_language': store.default_language,
'dashboard_language': store.dashboard_language,
'storefront_language': store.storefront_language,
'storefront_languages': store.storefront_languages
}
) }}
Flag Icons
The language selector uses flag-icons CSS library. Ensure this is included in your base template:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css">
Usage: <span class="fi fi-fr"></span> for French flag.
Testing
Manual Testing Checklist
- Language cookie is set when selecting language
- Page reloads with correct language after selection
- Store dashboard respects user's preferred_language
- Storefront respects customer's preferred_language
- Browser language detection works (clear cookie, use browser with different language)
- Fallback to default language works for unsupported languages
- Language selector shows only enabled languages on storefront
API Testing
# Set language
curl -X POST http://localhost:8000/api/v1/language/set \
-H "Content-Type: application/json" \
-d '{"language": "de"}'
# Get current language
curl http://localhost:8000/api/v1/language/current
# List languages
curl http://localhost:8000/api/v1/language/list
Future Enhancements
-
Admin Language Support: Currently admin is English-only. The system is designed to easily add admin language support later.
-
Translation Management UI: Add a UI for stores to manage their own translations (product descriptions, category names, etc.).
-
RTL Language Support: The
is_rtl_language()function is ready for future RTL language support (Arabic, Hebrew, etc.). -
Auto-Translation: Integration with translation APIs for automatic content translation.
Rollback
To rollback this migration:
alembic downgrade -1
This will remove:
default_language,dashboard_language,storefront_language,storefront_languagesfromstorespreferred_languagefromuserspreferred_languagefromcustomers