Cómo crear un componente de selector de idioma en Next.js (Pages Router) v16
Cambia de idioma mientras permaneces en la misma página
Problema
Los usuarios esperan que los selectores de idioma preserven su ubicación actual. Cuando alguien ve una página de producto en inglés y cambia a español, quiere ver esa misma página de producto en español, no ser redirigido a la página de inicio. Romper esta expectativa crea fricción y obliga a los usuarios a navegar de vuelta a donde estaban, degradando la experiencia y potencialmente causando que abandonen la tarea por completo.
Muchas implementaciones de selectores de idioma fallan porque tratan la selección de idioma como una simple navegación en lugar de como una transformación de la vista actual. Sin acceso a la información de enrutamiento de la página actual, un selector solo puede enlazar a destinos fijos, perdiendo el contexto del usuario en el proceso.
Solución
Crea un selector de idioma que lea el pathname y los parámetros de consulta de la ruta actual desde el router, luego genera enlaces para cada idioma soportado que preserven esta información de enrutamiento mientras cambian solo el locale. Al pasar el pathname y query a la API de navegación junto con el locale de destino, el selector asegura que los usuarios permanezcan en la página equivalente en el nuevo idioma.
Pasos
1. Crea un componente de selector de idioma que lea la ruta actual
El objeto router proporciona las propiedades pathname, asPath, query, locale y locales que contienen toda la información necesaria para construir enlaces conscientes del locale.
import { useRouter } from "next/router";
import Link from "next/link";
export default function LanguageSwitcher() {
const router = useRouter();
const { locale, locales, pathname, asPath, query } = router;
return (
<nav>
{locales?.map((loc) => (
<Link key={loc} href={{ pathname, query }} as={asPath} locale={loc}>
{loc.toUpperCase()}
</Link>
))}
</nav>
);
}
El componente Link acepta una prop locale para hacer la transición a un locale diferente del actualmente activo. Pasar pathname y query como un objeto a href preserva toda la información de enrutamiento incluyendo los valores de consulta de rutas dinámicas.
2. Aplica estilo a la configuración regional activa para proporcionar retroalimentación visual
Resalta el idioma actual para que los usuarios sepan qué configuración regional están viendo.
import { useRouter } from "next/router";
import Link from "next/link";
export default function LanguageSwitcher() {
const router = useRouter();
const { locale, locales, pathname, asPath, query } = router;
return (
<nav>
{locales?.map((loc) => {
const isActive = loc === locale;
return (
<Link
key={loc}
href={{ pathname, query }}
as={asPath}
locale={loc}
style={{
fontWeight: isActive ? "bold" : "normal",
textDecoration: isActive ? "none" : "underline",
marginRight: "1rem",
}}
>
{loc.toUpperCase()}
</Link>
);
})}
</nav>
);
}
Comparar cada configuración regional con el valor actual de locale identifica el idioma activo y aplica un estilo distintivo para diferenciarlo de las alternativas disponibles.
3. Añade etiquetas accesibles usando react-intl
Reemplaza los códigos de configuración regional con nombres de idiomas legibles para mejorar la usabilidad.
import { useRouter } from "next/router";
import { useIntl } from "react-intl";
import Link from "next/link";
const localeNames: Record<string, string> = {
en: "English",
es: "Español",
fr: "Français",
de: "Deutsch",
};
export default function LanguageSwitcher() {
const router = useRouter();
const intl = useIntl();
const { locale, locales, pathname, asPath, query } = router;
return (
<nav
aria-label={intl.formatMessage({
id: "languageSwitcher.label",
defaultMessage: "Select language",
})}
>
{locales?.map((loc) => {
const isActive = loc === locale;
return (
<Link
key={loc}
href={{ pathname, query }}
as={asPath}
locale={loc}
aria-current={isActive ? "true" : undefined}
style={{
fontWeight: isActive ? "bold" : "normal",
textDecoration: isActive ? "none" : "underline",
marginRight: "1rem",
}}
>
{localeNames[loc] || loc}
</Link>
);
})}
</nav>
);
}
El hook useIntl proporciona acceso a funciones de formato para traducir etiquetas de la interfaz. Los atributos aria-label y aria-current mejoran la accesibilidad para usuarios de lectores de pantalla.