Gestion des codes de langue non pris en charge

Problème

Une application utilise le chemin URL (par exemple, /en/, /fr/) pour déterminer la langue, mais les utilisateurs peuvent saisir manuellement n'importe quelle valeur, comme /xx/about. Lorsque cette valeur ne correspond pas à une langue prise en charge, l'application peut planter, afficher une erreur générique ou afficher un contenu non traduit, sans guider l'utilisateur vers une expérience valide.

Solution

Utilisez un middleware pour intercepter toutes les requêtes entrantes. Ce middleware validera le code de langue de l'URL par rapport à une liste définitive des langues prises en charge. Si le code n'est pas pris en charge, la requête sera réécrite vers une page "Not Found" avant qu'elle n'atteigne la logique de l'application.

Étapes

1. Définir vos langues prises en charge

Créez un fichier de configuration central pour stocker votre liste de codes de langue valides (locales). Cela rend la liste réutilisable pour votre middleware et d'autres parties de votre application.

// i18n.config.ts
export const locales = ['en', 'es', 'fr'];

2. Créer le fichier middleware

Créez un nouveau fichier nommé middleware.ts à la racine de votre projet (ou dans votre répertoire src/). Next.js détectera automatiquement ce fichier et l'exécutera sur les requêtes.

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { locales } from './i18n.config';

export function middleware(request: NextRequest) {
  // La logique sera ajoutée ici à l'étape suivante
}

3. Ajouter la logique de validation

Dans la fonction middleware, récupérez le pathname de la requête. Nous devons vérifier le premier segment du chemin (par exemple, en dans /en/about) et voir s'il s'agit d'une langue valide et prise en charge.

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { locales } from './i18n.config';

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

  // 1. Traiter le chemin racine séparément
  // (Cela sera géré par la prochaine recette : "Détecter la langue
  // préférée d'un utilisateur"). Pour l'instant, nous le laissons passer.
  if (pathname === '/') {
    return NextResponse.next();
  }

  // 2. Extraire le code de langue du chemin
  const langCode = pathname.split('/')[1];

  // 3. Vérifier si le code de langue est dans notre liste
  if (locales.includes(langCode)) {
    // La langue est valide, continuer vers la page demandée
    return NextResponse.next();
  }

  // 4. Si la langue n'est pas valide, réécrire vers une page 404
  // Cela conserve l'URL invalide dans la barre du navigateur
  const url = request.nextUrl.clone();
  url.pathname = `/404`; // Suppose que vous avez un fichier app/404.tsx
  return NextResponse.rewrite(url);
}

Cette logique vérifie chaque requête. Si l'URL est /fr/about, langCode est fr, il est trouvé dans locales, et la requête continue. Si l'URL est /xx/about, langCode est xx, il n'est pas trouvé, et l'utilisateur voit la page 404 sans que l'application n'essaie de traiter la requête invalide.

4. Configurer le matcher du middleware

Pour rendre votre middleware plus efficace, vous devriez lui indiquer quels chemins il doit traiter. Nous voulons qu'il s'exécute sur les requêtes de pages mais qu'il ignore les fichiers statiques et les routes API.

Ajoutez un objet config à la fin de votre fichier middleware.ts.

// middleware.ts

// ... (la fonction middleware ci-dessus)

export const config = {
  matcher: [
    // Ignorer tous les chemins qui commencent par :
    // - api (routes API)
    // - _next/static (fichiers statiques)
    // - _next/image (fichiers d'optimisation d'images)
    // - favicon.ico (fichier favicon)
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

Cette expression régulière indique au middleware de s'exécuter sur tous les chemins sauf ceux qui sont typiquement destinés aux ressources statiques ou aux appels API. Cela évite des validations inutiles sur chaque image, police ou requête de données.