Conserver le choix de langue d'un utilisateur

Utiliser un cookie pour mémoriser une sélection

Problème

Un utilisateur sélectionne manuellement 'Français' sur un site. Lorsqu'il ferme son navigateur et saisit ultérieurement l'adresse principale du site (par exemple, example.com), l'application revient à sa langue par défaut (par exemple, l'anglais) ou à la langue détectée automatiquement. Cet échec de mémorisation de son choix oblige l'utilisateur à retrouver le sélecteur de langue et à resélectionner sa langue à chaque nouvelle session.

Solution

Lorsqu'un utilisateur sélectionne une langue, stockez ce choix dans un cookie. Dans le middleware, lorsqu'un utilisateur visite le chemin racine (/), vérifiez ce cookie avant de consulter l'en-tête Accept-Language. Si un cookie valide est trouvé, redirigez l'utilisateur vers la racine de la langue choisie (par exemple, /fr), remplaçant ainsi toute langue par défaut du navigateur.

Étapes

1. Définir la configuration de langue

Créez un fichier de configuration central pour stocker vos langues prises en charge, une langue par défaut et le nom du cookie que vous utiliserez pour stocker la préférence de l'utilisateur.

// i18n.config.ts

export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const localeCookieName = 'NEXT_LOCALE';

2. Installer un analyseur de langue

Vous avez toujours besoin d'un analyseur pour l'en-tête Accept-Language, qui sera utilisé comme solution de repli si aucun cookie n'est défini.

npm install accept-language-parser

3. Créer le middleware

Créez un fichier middleware.ts à la racine de votre projet. Ce middleware vérifiera d'abord le cookie NEXT_LOCALE. S'il n'est pas trouvé, il se rabattra sur la vérification de l'en-tête Accept-Language. Cette logique ne s'appliquera qu'aux requêtes pour le chemin racine (/).

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import parser from 'accept-language-parser';
import {
  locales,
  defaultLocale,
  localeCookieName,
} from './i18n.config';

function getPreferredLocale(request: NextRequest) {
  // 1. Vérifier le cookie
  const cookie = request.cookies.get(localeCookieName);
  if (cookie) {
    const locale = cookie.value;
    if (locales.includes(locale)) {
      return locale;
    }
  }

  // 2. Vérifier l'en-tête Accept-Language
  const acceptLang = request.headers.get('Accept-Language');
  if (acceptLang) {
    const bestMatch = parser.pick(locales, acceptLang, {
      loose: true,
    });
    if (bestMatch) {
      return bestMatch;
    }
  }

  // 3. Retourner la valeur par défaut
  return defaultLocale;
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 1. Vérifier si la requête concerne le chemin racine
  if (pathname === '/') {
    // Obtenir la langue préférée de l'utilisateur (du cookie ou de l'en-tête)
    const bestLocale = getPreferredLocale(request);

    // Rediriger vers le chemin de la langue la mieux adaptée
    request.nextUrl.pathname = `/${bestLocale}`;
    return NextResponse.redirect(request.nextUrl);
  }

  // 2. Pour tous les autres chemins, continuer normalement
  return NextResponse.next();
}

export const config = {
  matcher: [
    // Nous voulons exécuter ceci uniquement sur le chemin racine pour l'instant
    '/',
    // Nous devons également faire correspondre les chemins non-racine pour les laisser passer
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

La logique de ce middleware privilégie désormais correctement le choix explicite de l'utilisateur (le cookie) par rapport à sa préférence implicite (l'en-tête du navigateur) lorsqu'il visite la racine du site.