Detectando el idioma preferido del usuario
Redirigiendo a los nuevos visitantes a su idioma más probable
Problema
Cuando un usuario visita por primera vez la raíz de una aplicación (por ejemplo, /), se muestra un idioma predeterminado, como el inglés. Esto crea fricción inmediata para los usuarios que hablan otros idiomas, obligándolos a encontrar manualmente un selector de idioma, aunque su navegador ya comunica su preferencia.
Solución
Utilizar middleware para interceptar las solicitudes a la ruta raíz (/). Verificar el encabezado HTTP Accept-Language del usuario para encontrar su idioma preferido. Si ese idioma es compatible con la aplicación, redirigir al usuario a la raíz de ese idioma (por ejemplo, /fr). Si no, redirigirlos a un idioma predeterminado (por ejemplo, /en).
Pasos
1. Instalar un analizador de idiomas
El encabezado Accept-Language puede ser complejo (por ejemplo, fr-CH, fr;q=0.9, en;q=0.8). Una pequeña biblioteca ayuda a analizar este encabezado y encontrar la mejor coincidencia de nuestra lista de idiomas compatibles.
Ejecuta este comando en tu terminal:
npm install accept-language-parser
2. Definir tus idiomas y el predeterminado
Crea un archivo de configuración central para almacenar tu lista de idiomas compatibles y definir un valor predeterminado. Este valor predeterminado se utilizará si las preferencias del navegador del usuario no coinciden con ningún idioma que admitas.
// i18n.config.ts
export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
3. Crear el middleware
Crea un archivo middleware.ts en la raíz de tu proyecto. Este archivo se ejecutará en las solicitudes entrantes, permitiéndote verificar la ruta y los encabezados.
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import parser from 'accept-language-parser';
import { locales, defaultLocale } from './i18n.config';
// Función auxiliar para encontrar la mejor coincidencia de idioma
function getBestLocale(acceptLangHeader: string | null) {
if (!acceptLangHeader) {
return defaultLocale;
}
// Utiliza el analizador para encontrar el mejor idioma compatible
const bestMatch = parser.pick(locales, acceptLangHeader, {
loose: true,
});
return bestMatch || defaultLocale;
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 1. Verifica si la solicitud es para la ruta raíz
if (pathname === '/') {
// Obtiene el idioma preferido del usuario
const acceptLang = request.headers.get('Accept-Language');
const bestLocale = getBestLocale(acceptLang);
// Redirige a la ruta del idioma mejor coincidente
request.nextUrl.pathname = `/${bestLocale}`;
return NextResponse.redirect(request.nextUrl);
}
// 2. Para todas las demás rutas, continúa normalmente
return NextResponse.next();
}
export const config = {
matcher: [
// Omite todas las rutas que comienzan con:
// - api (rutas API)
// - _next/static (archivos estáticos)
// - _next/image (archivos de optimización de imágenes)
// - favicon.ico (archivo favicon)
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
Este código solo ejecuta lógica para la ruta raíz (/). Si un usuario visita /, verifica su encabezado Accept-Language, encuentra la mejor coincidencia (por ejemplo, es), y los redirige a /es. Todas las demás solicitudes, como /en/about, son ignoradas por esta lógica y pasan a través.