---
title: "Internationalisation"
description: "L'API est traduite côté serveur : les champs errors[].detail et toute chaîne destinée à l'affichage sont rendus dans la locale active (en plus du meta.code…"
---

L'API est traduite **côté serveur** : les champs `errors[].detail` et toute chaîne destinée à l'affichage sont rendus dans la locale active (en plus du `meta.code` toujours présent et stable, qui reste la source de vérité programmatique pour le client).

## Locales supportées

- `fr-FR` — locale par défaut applicative
- `en-US`

Catalogues sous `resources/lang/<locale>/` (`auth.php`, `users.php`, `validation.php`, `errors.php`, `mails.php`). Format ICU MessageFormat (interpolation `{name}`, pluriels `{count, plural, =1{...} other{...}}`).

## Résolution de la locale active

Le [`LocaleResolverMiddleware`](../src/Http/Middleware/LocaleResolverMiddleware.php) pose `i18n.locale` sur la requête et renseigne le header `Content-Language` sur la réponse. Ordre de priorité (première règle qui matche) :

1. Setting `locale` de l'utilisateur authentifié (lu via `UserSettingService`). Le middleware tourne **après** `AuthenticationMiddleware` sur les routes protégées.
2. Négociation du header `Accept-Language` contre la liste des locales supportées (q-values respectés, fallback langue-only `fr` → `fr-FR`).
3. Locale applicative par défaut (`fr-FR`).

## Fallback de clés (B.c)

Si une clé manque dans la locale active, le `Translator` la cherche dans la locale **`fr-FR`** (locale historique, présumée complète à 100%). Si elle manque aussi là-bas, la clé brute est renvoyée et un warning est loggé.

## Emails

Les emails transactionnels (`email_confirmation`, `password_reset`, …) sont **toujours** rendus dans la locale du **destinataire** (lue sur son setting `locale`), jamais celle de l'acteur qui déclenche l'envoi. Voir [`ConfirmationEmailSender`](../src/Domain/Auth/ConfirmationEmailSender.php) et [`PasswordResetEmailSender`](../src/Domain/Auth/PasswordResetEmailSender.php).

## Réglage utilisateur `locale`

Le setting [`locale`](#patch-apiusersmesettings) est **restreint** aux locales effectivement supportées (= dossiers présents sous `resources/lang/`). Choisir `fr-FR` ou `en-US` actuellement ; toute autre valeur est rejetée avec `setting.invalidLocale`. Cette restriction est volontaire : accepter `ja-JP` produirait une UI en français (fallback) — autant le refuser à la source.

---

## `GET /api/i18n/locales`

Liste les locales **effectivement supportées** par l'application (présence d'un catalogue sous `resources/lang/`), enrichies avec les libellés (anglais et natif) issus de la table SQL `locale`. Différent de la liste des 377 entrées BCP-47 que contient cette même table.

- **Auth** : aucune
- **Action** : [ListLocalesAction](../src/Http/Action/Api/I18n/ListLocalesAction.php)
- **Réponse `200`** :

```json
{
  "jsonapi": { "version": "1.1" },
  "data": [
    {
      "type": "locales",
      "id":   "en-US",
      "attributes": {
        "name":       "English",
        "nativeName": "English",
        "isDefault":  false
      }
    },
    {
      "type": "locales",
      "id":   "fr-FR",
      "attributes": {
        "name":       "French",
        "nativeName": "français",
        "isDefault":  true
      }
    }
  ],
  "meta": { "count": 2, "default": "fr-FR" }
}
```

- **Notes** :
  - Tri alphabétique par `id`.
  - `isDefault` flag la locale applicative par défaut (`fr-FR`).
  - Header de réponse `Content-Language` reflète la locale active de la requête courante.
  - Ajout d'une nouvelle locale = créer le dossier `resources/lang/<tag>/` avec les 5 catalogues (`auth.php`, `users.php`, `validation.php`, `errors.php`, `mails.php`). Vérifier ensuite que la ligne `locale.id = '<tag>'` existe en base.

---
