Files
orion/docs/development/migration/language-i18n-implementation.md
Samir Boulahtit e9253fbd84 refactor: rename Wizamart to Orion across entire codebase
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>
2026-02-14 16:46:56 +01:00

320 lines
8.5 KiB
Markdown

# 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`:
```sql
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
```sql
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
```sql
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
```http
POST /api/v1/language/set
Content-Type: application/json
{
"language": "de"
}
```
Response:
```json
{
"success": true,
"language": "de",
"message": "Language set to Deutsch"
}
```
## Translation Files
Translation files are stored in `static/locales/{lang}.json` with the following structure:
```json
{
"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
```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):
1. LoggingMiddleware
2. StoreContextMiddleware
3. ContextMiddleware
4. **LanguageMiddleware** <-- Detects language
5. ThemeContextMiddleware
6. FastAPI Router
## UI Components
### Full Language Selector
```jinja2
{{ language_selector(
current_language='fr',
enabled_languages=['fr', 'de', 'en'],
position='right',
show_label=true
) }}
```
### Compact Selector (Flag Only)
```jinja2
{{ language_selector_compact(
current_language='fr',
enabled_languages=['fr', 'de', 'en']
) }}
```
### Language Settings Form
For store settings pages:
```jinja2
{{ 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](https://flagicons.lipis.dev/) CSS library. Ensure this is included in your base template:
```html
<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
```bash
# 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
1. **Admin Language Support**: Currently admin is English-only. The system is designed to easily add admin language support later.
2. **Translation Management UI**: Add a UI for stores to manage their own translations (product descriptions, category names, etc.).
3. **RTL Language Support**: The `is_rtl_language()` function is ready for future RTL language support (Arabic, Hebrew, etc.).
4. **Auto-Translation**: Integration with translation APIs for automatic content translation.
## Rollback
To rollback this migration:
```bash
alembic downgrade -1
```
This will remove:
- `default_language`, `dashboard_language`, `storefront_language`, `storefront_languages` from `stores`
- `preferred_language` from `users`
- `preferred_language` from `customers`