Dans ce guide, nous allons voir comment configurer l'internationalisation de votre application Next.js en utilisant Next-intl sans avoir recours au système de routage i18n intégré. Cette approche est particulièrement utile dans les cas suivants :

  1. Vous souhaitez fournir un moyen pour les utilisateurs de changer de langue sans changer d'URL
  2. Votre application ne supporte qu'une seule langue à la fois

Structure du projet

Voici la structure de fichiers que nous allons mettre en place :

├── messages
│   ├── en.json
│   ├── fr.json
│   └── ...
├── next.config.ts
├── src
│   ├── i18n
│   │   ├── config.ts
│   │   ├── request.ts
│   │   └── service
│   │       └── locale.ts
│   └── app
│       ├── layout.tsx
│       └── page.tsx

Étape 1 : Configuration des locales disponibles

Commençons par créer le fichier src/i18n/config.ts pour définir les langues supportées par notre application :

export type Locale = (typeof locales)[number];

export const locales = ['fr', 'en'] as const;
export const defaultLocale: Locale = 'fr';

Ce fichier définit les langues disponibles dans notre application (français et anglais), avec le français comme langue par défaut.

Étape 2 : Création du service de gestion des locales

Créons ensuite le fichier src/i18n/service/locale.ts pour gérer la détection et la configuration de la langue :

'use server';

import {cookies} from 'next/headers';
import { defaultLocale, Locale } from '../config';

// Nom du cookie qui stockera la locale choisie par l'utilisateur
const COOKIE_NAME = 'NEXT_LOCALE';

// Function pour récupérer la locale actuelle de l'utilisateur
export async function getUserLocale() {
  return (await cookies()).get(COOKIE_NAME)?.value ?? 
    defaultLocale;
}

// Function pour définir la locale de l'utilisateur
export async function setUserLocale(locale: Locale) {
  (await cookies()).set(COOKIE_NAME, locale)
}

Ce service utilise les cookies pour stocker et récupérer la langue choisie par l'utilisateur. Si aucune langue n'est définie, il utilise la langue par défaut.

Étape 3 : Configuration des requêtes internationalisées

Créons maintenant le fichier src/i18n/request.ts qui sera utilisé pour configurer les requêtes avec la bonne locale :

import {getRequestConfig} from 'next-intl/server';
import { getUserLocale } from './service/locale';

export default getRequestConfig(async () => {
  const locale = await getUserLocale();
  
  return {
    locale,
    messages: (await import(`../../messages/${locale}.json`)).default,
    timeZone: 'Europe/Paris'
  };
});

Cette configuration permet de charger dynamiquement les traductions correspondant à la langue de l'utilisateur.

Étape 4 : Configuration du layout principal

Maintenant, configurons le layout principal de notre application (app/layout.tsx) pour intégrer Next-intl :

import { cn } from "@/src/lib/utils";

import { GeistMono } from "geist/font/mono";
import { GeistSans } from "geist/font/sans";
import type { Metadata } from "next";
import { Space_Grotesk } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/src/components/theme-provider";
import { NextIntlClientProvider } from "next-intl";
import { PropsWithChildren } from "react";
import { LayoutParams } from "@/src/types/next";
import { getLocale, getMessages } from "next-intl/server";

export default async function RootLayout({ children }: PropsWithChildren<LayoutParams>) {
  const locale = await getLocale();
  const messages = await getMessages();
  
  return (
    <>
      <html lang={locale} className="h-full" suppressHydrationWarning>
        <body
          suppressHydrationWarning
          className={cn(
            "bg-background h-full font-sans antialiased",
            GeistMono.variable,
            GeistSans.variable,
            CaptionFont.variable,
          )}
        >
          <ThemeProvider
            attribute="class"
            defaultTheme="system"
            enableSystem
            disableTransitionOnChange
          >
            <NextIntlClientProvider locale={locale} messages={messages}>
              {children}
            </NextIntlClientProvider>
          </ThemeProvider>
        </body>
      </html>
    </>
  );
}

Ce layout configure l'application pour utiliser Next-intl et applique la langue détectée au niveau de l'attribut lang de l'élément HTML.

Étape 5 : Création des fichiers de traduction

Créez vos fichiers de traduction dans le dossier messages. Par exemple :

messages/fr.json :

{
  "title": "Bonjour monde",
  "welcome": "Bienvenue sur notre site"
}

messages/en.json :

{
  "title": "Hello world",
  "welcome": "Welcome to our website"
}

Étape 6 : Utilisation des traductions dans vos composants

Pour utiliser les traductions dans vos composants, importez les hooks de Next-intl :

'use client';

import { useTranslations } from 'next-intl';

export default function HomePage() {
  const t = useTranslations();
  
  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('welcome')}</p>
    </div>
  );
}

Étape 7 : Ajout d'un sélecteur de langue (optionnel)

Vous pouvez ajouter un sélecteur de langue pour permettre aux utilisateurs de changer de langue :

'use client';

import { useTranslations } from 'next-intl';
import { useRouter } from 'next/navigation';
import { setUserLocale } from '@/src/i18n/service/locale';
import { locales } from '@/src/i18n/config';

export default function LanguageSwitcher() {
  const t = useTranslations();
  const router = useRouter();
  
  const handleLanguageChange = async (locale: string) => {
    await setUserLocale(locale as any);
    router.refresh();
  };
  
  return (
    <div>
      <select onChange={(e) => handleLanguageChange(e.target.value)}>
        {locales.map((locale) => (
          <option key={locale} value={locale}>
            {t(`languages.${locale}`)}
          </option>
        ))}
      </select>
    </div>
  );
}

Conclusion

Vous avez maintenant configuré avec succès l'internationalisation de votre application Next.js en utilisant Next-intl sans le routage i18n intégré. Cette approche vous permet de:

  1. Gérer les traductions de manière efficace
  2. Permettre aux utilisateurs de changer de langue sans changer d'URL
  3. Détecter automatiquement la langue de l'utilisateur via un cookie

N'oubliez pas d'ajouter de nouvelles traductions au fur et à mesure que votre application évolue. Vous pouvez également créer des dossiers spécifiques pour chaque locale si vous avez besoin d'organiser vos traductions par section ou fonctionnalité.