# I18N.md — Internationalisation FR/EN, côté backend (session 2026-07-24)

Partie backend du système de traduction FR/EN. Vue d'ensemble complète
(front, routing, sélecteur de langue, comment traduire une page) :
`uluana/claude/I18N.md` — à lire en premier si vous découvrez le sujet.

## Ce qui existe côté backend

| Pièce | Fichier | Rôle |
|---|---|---|
| Préférence de langue du compte | `sql/2026_07_24_125_users_lang.sql`, `users.lang` | `VARCHAR(5) DEFAULT 'fr'`. Lu/écrit par `api/account.php`. |
| Contenu i18n des landing pages | `sql/2026_07_24_126_landing_themes_i18n.sql`, colonnes `landing_themes.*_en` | Traduction du contenu marketing (titre/accroche/probleme/destination/cta_label/raw_html), nullable, avec repli sur la colonne française. |
| Bake multilingue | `api/_landing_bake.php` | Génère `landing_pages/landings_baked/{slug}.html` (français, inchangé) **et** `landing_pages/landings_baked/en/{slug}.html` (anglais, nouveau). |
| Fragment de contenu localisé | `api/landing-themes.php` (`accueil-actuel`) | Accepte `?lang=en`, utilisé par la landing SPA interne (`elems/pages/landing/landing.js`). |

## `users.lang`

Colonne simple, pas un ENUM (`VARCHAR(5)`) — accepte n'importe quel code de
langue sans migration future. `api/account.php` :
- **GET** `/api/users/{id}/account` renvoie `lang` dans la réponse.
- **PUT** : accepte `{ lang: 'en' }`, **whitelisté** en dur
  (`in_array($data['lang'], ['fr', 'en'], true)`) plutôt que passé par le
  tableau `$editable` générique — ajouter une langue nécessite d'étendre
  cette whitelist (voir "Ajouter une langue" plus bas).

Cette colonne est une **préférence de compte** (utile pour un futur email ou
un repli si l'URL visitée n'a pas de préfixe), mais **l'URL prime en
navigation** côté front — voir `I18n.detectInitialLocale()` dans le doc
front. Elle n'est pas (encore) branchée sur les emails transactionnels.

## Landing pages — bake multilingue

### Colonnes `_en` sur `landing_themes`

`titre_en`, `accroche_en`, `probleme_en`, `destination_en`, `cta_label_en`,
`raw_html_en` — toutes nullable. Fonction `landing_theme_field($theme,
$field, $locale)` dans `_landing_bake.php` : si `$locale !== 'fr'` et que
`{$field}_en` est renseigné, l'utilise ; sinon repli sur `$theme[$field]`
(français). **Aucune UI admin pour éditer ces colonnes aujourd'hui** — à
faire s'il faut un jour permettre la rédaction du contenu anglais depuis
`admin-landing-themes` (actuellement uniquement via SQL/API directe).

### `landing_bake_theme()`

Bake désormais **deux fichiers** par thématique à chaque appel (édition
admin, migration, détection de changement de template) :
- `landings_baked/{slug}.html` — français, comportement inchangé, c'est
  toujours cette version qui alimente `landing_themes.html_content` en base.
- `landings_baked/en/{slug}.html` — anglais, généré par
  `landing_bake_theme_locale()`, **pas de colonne BDD dédiée** (uniquement le
  fichier statique — pas besoin de traçabilité BDD pour du contenu qui n'est
  pour l'instant qu'un repli automatique sur le français).

La liste des langues bakées est `I18N_LANDING_LOCALES` (constante en haut de
`_landing_bake.php`, actuellement `['fr', 'en']`) — à tenir en miroir de
`I18n.SUPPORTED_LOCALES` côté front.

`landing_render_body()`/`landing_render_document()` prennent un paramètre
`$locale` optionnel (défaut `'fr'`) — tous les appels existants sans ce
paramètre continuent de fonctionner à l'identique (rétrocompatible).

Les 3 labels fixes du gabarit ("Aujourd'hui", "Ce que ça change", note du
CTA) sont traduits en dur dans `landing_render_body()` (tableau `$labels`) —
ce ne sont pas des données BDD, donc pas concernés par le repli
français/`_en`.

### `.htaccess` (front)

Règles dupliquées pour `/en/landing/{slug}` et `/en/` (racine, si une
thématique est désignée Accueil) — voir le fichier, section "i18n : préfixe
/en/". Le `.gitignore` front ignore aussi
`landing_pages/landings_baked/*/*.html` (les sous-dossiers de langue).

## Ajouter une langue (ex. espagnol) — côté backend

1. `I18N_LANDING_LOCALES` dans `_landing_bake.php` : ajouter `'es'`.
2. `api/account.php` : ajouter `'es'` à la whitelist du PUT.
3. Migration SQL : ajouter les colonnes `titre_es`, `accroche_es`,
   `probleme_es`, `destination_es`, `cta_label_es`, `raw_html_es` sur
   `landing_themes` (même modèle que `2026_07_24_126_landing_themes_i18n.sql`),
   + forcer un re-bake global (`UPDATE landing_themes SET html_content = NULL;`).
4. `.htaccess` (front) : dupliquer les deux règles `^en/...` en `^es/...`.
5. `landing_render_body()` : ajouter l'entrée `'es'` dans le tableau
   `$labels` (labels fixes du gabarit).
6. Aucune migration requise pour `users.lang` (colonne libre, pas un ENUM).

## Hors scope

Voir `uluana/claude/I18N.md` section "Hors scope" — en résumé : contenu des
124 cartes du catalogue, emails transactionnels, PDF générés. Le contenu
anglais des landing pages a la plomberie prête mais reste à rédiger (repli
français en attendant).
