Chargement des traductions

Utilisation d'un provider pour gérer les messages

Problème

Coder en dur du texte, comme 'Hello World', directement dans les composants d'une application couple le contenu avec le code. Pour afficher une langue différente, les développeurs doivent dupliquer le composant ou ajouter une logique if/else, rendant la traduction non évolutive et nécessitant une modification complète du code pour chaque nouveau texte.

Solution

Utilisez le IntlProvider de react-intl pour fournir les traductions. Chargez les messages de traduction sur le serveur dans le layout racine, transmettez-les à un composant provider côté client, puis consommez-les dans d'autres composants clients en utilisant le hook useIntl.

Étapes

1. Installer react-intl

Tout d'abord, ajoutez react-intl comme dépendance à votre projet.

npm install react-intl

2. Créer des fichiers de traduction plats

Créez un dossier dictionaries. À l'intérieur, ajoutez un fichier JSON pour chaque langue. react-intl fonctionne mieux avec une structure clé-valeur plate.

// dictionaries/en.json
{
  "home.title": "Home Page",
  "home.welcome": "Hello, welcome to our site!",
  "about.title": "About Us"
}
// dictionaries/es.json
{
  "home.title": "Página de Inicio",
  "home.welcome": "¡Hola, bienvenido a nuestro sitio!",
  "about.title": "Sobre Nosotros"
}

3. Créer une fonction pour charger les dictionnaires

Créez une fonction helper pour charger le fichier de dictionnaire correct sur le serveur en fonction du paramètre lang.

// app/get-dictionary.ts
import 'server-only';

// Define the type for our flat message object
type Messages = Record<string, string>;

const dictionaries: { [key: string]: () => Promise<Messages> } = {
  en: () => import('@/dictionaries/en.json').then((module) => module.default),
  es: () => import('@/dictionaries/es.json').then((module) => module.default),
  // fr: () => import('@/dictionaries/fr.json').then((module) => module.default),
};

export const getDictionary = async (lang: string) => {
  const load = dictionaries[lang];
  if (load) {
    return load();
  }
  // Fallback to English
  return dictionaries.en();
};

4. Créer un provider côté client

IntlProvider est un composant client qui utilise le Context de React. Nous devons créer un wrapper pour celui-ci qui peut accepter les messages chargés depuis le serveur.

// app/components/IntlClientProvider.tsx
'use client';

import { IntlProvider } from 'react-intl';

type Props = {
  children: React.ReactNode;
  locale: string;
  messages: Record<string, string>; // Flat messages object
};

export default function IntlClientProvider({
  children,
  locale,
  messages,
}: Props) {
  return (
    <IntlProvider messages={messages} locale={locale} defaultLocale="en">
      {children}
    </IntlProvider>
  );
}

5. Mettre à jour le layout racine

Modifiez votre app/[lang]/layout.tsx pour en faire un composant async. Il chargera les messages et les transmettra au IntlClientProvider.

// app/[lang]/layout.tsx
import { getDictionary } from '@/app/get-dictionary';
import IntlClientProvider from '@/app/components/IntlClientProvider';

export async function generateStaticParams() {
  return [{ lang: 'en' }, { lang: 'es' }];
}

export default async function RootLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Load messages on the server
  const messages = await getDictionary(params.lang);

  return (
    <html lang={params.lang}>
      <body>
        {/* Pass messages to the client provider */}
        <IntlClientProvider locale={params.lang} messages={messages}>
          {children}
        </IntlClientProvider>
      </body>
    </html>
  );
}

6. Utiliser les traductions dans un composant client

Vous pouvez maintenant utiliser le hook useIntl dans n'importe quel composant client. Les composants serveur ne peuvent pas utiliser ce hook.

Créez un nouveau composant client pour afficher le texte traduit :

// app/components/HomePageContent.tsx
'use client';

import { useIntl } from 'react-intl';

export default function HomePageContent() {
  const intl = useIntl();

  return (
    <div>
      <h1>{intl.formatMessage({ id: 'home.title' })}</h1>
      <p>{intl.formatMessage({ id: 'home.welcome' })}</p>
    </div>
  );
}

7. Ajouter le composant à votre page

Enfin, ajoutez votre nouveau composant client à votre page.

// app/[lang]/page.tsx
import HomePageContent from '@/app/components/HomePageContent';

export default function Home() {
  // This page is a Server Component
  return (
    <div>
      {/* It renders the Client Component */}
      <HomePageContent />
    </div>
  );
}