Documentation

Internationalisation

next-intl v4, ajout d'une langue, helpers LocalizedText et conventions.

Le boilerplate utilise next-intl v4 avec fr et en actifs par défaut. Le CLI propose aussi es / de / pt / it à la génération.

Structure

messages/
  fr.json
  en.json
i18n/
  routing.ts        // locales, defaultLocale
  request.ts        // chargement messages
proxy.ts            // matcher next-intl
app/[locale]/...    // toutes les pages user-facing

Les pages api/, maintenance/, r/[code]/, u/[username]/ sont hors [locale] — elles n'ont pas besoin de traduction.

Ajouter une langue

  1. Ajoute le code locale dans i18n/routing.ts :
    export const locales = ["fr", "en", "es"] as const;
  2. Crée messages/es.json avec toutes les clés (copie depuis fr.json, traduis).
  3. Ajoute le bouton dans components/shared/locale-switcher.tsx.
  4. Pour les contenus DB (Plan, SiteConfig, Coupon), ajoute simplement la clé es aux objets LocalizedText existants — pickLocalized fait le reste avec son fallback.

LocalizedText pour les données DB

Plans, annonces, messages de maintenance utilisent ce pattern :

export type LocalizedText = Record<string, string>;

Helper :

import { pickLocalized } from "@/lib/site-config";

const text = pickLocalized(plan.name, locale);
// fallback: locale → fr → en → première dispo

Pour Plan.features (array par locale) :

const features =
  plan.features[locale]?.length > 0
    ? plan.features[locale]
    : plan.features.fr ?? plan.features.en ?? [];

Ou utilise directement localizePlan(plan, locale) qui retourne un objet aplati prêt à rendre.

Conventions hooks

// ✅ Top du composant
"use client";
import { useTranslations } from "next-intl";

export function MyForm() {
  const t = useTranslations("auth");
  return <Button>{t("login")}</Button>;
}
// ❌ Inline — interdit
<Button>{useTranslations("auth")("login")}</Button>

// ❌ Dans un handler async — interdit
const onClick = async () => {
  toast.error(useTranslations("common")("error"));
};

// ✅ Stocké au top, utilisé dans le handler
const tCommon = useTranslations("common");
const onClick = async () => {
  toast.error(tCommon("error"));
};

Côté server (async), c'est getTranslations :

import { getTranslations } from "next-intl/server";

export default async function Page() {
  const t = await getTranslations("dashboard");
  return <h1>{t("title")}</h1>;
}

Sections de clés

Les fichiers messages/*.json sont organisés par feature/page :

{
  "auth": { "login": "...", "register": "..." },
  "nav": { ... },
  "dashboard": { ... },
  "settings": { ... },
  "landing": { ... },
  "footer": { ... },
  "errors": { ... }
}

Pas de string en dur dans le JSX — règle stricte du projet.

Locale du user

User.locale (DB) prime quand le user est connecté. Sinon, next-intl détecte via Accept-Language ou cookie.

Pour changer la locale d'un user : PATCH /api/user/profile { locale }.

Allez plus loin

On this page