Manejo de códigos de idioma no compatibles

Problema

Una aplicación utiliza la ruta de la URL (por ejemplo, /en/, /fr/) para determinar el idioma, pero los usuarios pueden escribir manualmente cualquier valor, como /xx/about. Cuando este valor no coincide con un idioma compatible, la aplicación puede fallar, mostrar un error genérico o mostrar contenido sin traducir, sin lograr guiar al usuario de vuelta a una experiencia válida.

Solución

Utiliza un middleware para interceptar todas las solicitudes entrantes. Este middleware validará el código de idioma de la URL contra una lista definitiva de idiomas compatibles. Si el código no es compatible, la solicitud se reescribirá a una página "No encontrada" antes de que llegue a la lógica de la aplicación.

Pasos

1. Define tus idiomas compatibles

Crea un archivo de configuración central para almacenar tu lista de códigos de idioma válidos (locales). Esto hace que la lista sea reutilizable para tu middleware y otras partes de tu aplicación.

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

2. Crea el archivo de middleware

Crea un nuevo archivo llamado middleware.ts en la raíz de tu proyecto (o en tu directorio src/). Next.js detectará automáticamente este archivo y lo ejecutará en las solicitudes.

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

export function middleware(request: NextRequest) {
  // Logic will go here in the next step
}

3. Añade la lógica de validación

Dentro de la función middleware, obtén el pathname de la solicitud. Necesitamos verificar el primer segmento de la ruta (por ejemplo, en en /en/about) y ver si es un idioma válido y compatible.

// 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. Handle the root path separately
  // (This will be handled by the next recipe: "Detecting a user's
  // preferred language"). For now, we just let it pass.
  if (pathname === '/') {
    return NextResponse.next();
  }

  // 2. Extract the language code from the path
  const langCode = pathname.split('/')[1];

  // 3. Check if the language code is in our list
  if (locales.includes(langCode)) {
    // Language is valid, continue to the requested page
    return NextResponse.next();
  }

  // 4. If the language is not valid, rewrite to a 404 page
  // This keeps the invalid URL in the browser bar
  const url = request.nextUrl.clone();
  url.pathname = `/404`; // Assumes you have an app/404.tsx file
  return NextResponse.rewrite(url);
}

Esta lógica verifica cada solicitud. Si la URL es /fr/about, langCode es fr, se encuentra en locales y la solicitud continúa. Si la URL es /xx/about, langCode es xx, no se encuentra y se muestra al usuario la página 404 sin que la aplicación intente procesar la solicitud no válida.

4. Configurar el matcher del middleware

Para hacer tu middleware más eficiente, debes indicarle en qué rutas debe ejecutarse. Queremos que se ejecute en las solicitudes de página pero que omita los archivos estáticos y las rutas de API.

Añade un objeto config al final de tu archivo middleware.ts.

// middleware.ts

// ... (the middleware function from above)

export const config = {
  matcher: [
    // Skip all paths that start with:
    // - api (API routes)
    // - _next/static (static files)
    // - _next/image (image optimization files)
    // - favicon.ico (favicon file)
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

Esta expresión regular indica al middleware que se ejecute en todas las rutas excepto en aquellas que típicamente son para recursos estáticos o llamadas a API. Esto evita validaciones innecesarias en cada solicitud de imagen, fuente o datos.