Persistir la elección de idioma de un usuario
Usar una cookie para recordar una selección
Problema
Un usuario selecciona manualmente 'francés' en un sitio. Cuando cierra su navegador y más tarde escribe la dirección principal del sitio (por ejemplo, example.com), la aplicación vuelve a su idioma predeterminado (por ejemplo, inglés) o detectado automáticamente. Este fallo al recordar su elección obliga al usuario a encontrar el selector de idioma y volver a seleccionar su idioma cada vez que inicia una nueva sesión.
Solución
Cuando un usuario selecciona un idioma, almacena esa elección en una cookie. En el middleware, cuando un usuario visita la ruta raíz (/), verifica esta cookie antes de verificar el encabezado Accept-Language. Si se encuentra una cookie válida, redirige al usuario a la raíz de su idioma elegido (por ejemplo, /fr), anulando cualquier valor predeterminado del navegador.
Pasos
1. Definir la configuración de idioma
Crea un archivo de configuración central para almacenar tus idiomas compatibles, un idioma predeterminado y el nombre de la cookie que usarás para almacenar la preferencia del usuario.
// i18n.config.ts
export const locales = ['en', 'es', 'fr'];
export const defaultLocale = 'en';
export const localeCookieName = 'NEXT_LOCALE';
2. Instalar un analizador de idioma
Aún necesitas un analizador para el encabezado Accept-Language, que se usará como alternativa si no hay ninguna cookie configurada.
npm install accept-language-parser
3. Crear el middleware
Crea un archivo middleware.ts en la raíz de tu proyecto. Este middleware verificará primero la cookie NEXT_LOCALE. Si no se encuentra, recurrirá a verificar el encabezado Accept-Language. Esta lógica solo se aplicará a las solicitudes de la ruta raíz (/).
// 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. Check for the cookie
const cookie = request.cookies.get(localeCookieName);
if (cookie) {
const locale = cookie.value;
if (locales.includes(locale)) {
return locale;
}
}
// 2. Check the Accept-Language header
const acceptLang = request.headers.get('Accept-Language');
if (acceptLang) {
const bestMatch = parser.pick(locales, acceptLang, {
loose: true,
});
if (bestMatch) {
return bestMatch;
}
}
// 3. Return the default
return defaultLocale;
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 1. Check if the request is for the root path
if (pathname === '/') {
// Get the user's preferred language (from cookie or header)
const bestLocale = getPreferredLocale(request);
// Redirect to the best-matched language path
request.nextUrl.pathname = `/${bestLocale}`;
return NextResponse.redirect(request.nextUrl);
}
// 2. For all other paths, continue as normal
return NextResponse.next();
}
export const config = {
matcher: [
// We only want to run this on the root path for now
'/',
// We also need to match non-root paths to let them pass
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
La lógica de este middleware ahora prioriza correctamente la elección explícita del usuario (la cookie) sobre su preferencia implícita (el encabezado del navegador) cuando visita la raíz del sitio.