Cómo construir un componente de selector de idioma en React Router v7
Cambiar de idioma mientras permaneces en la misma página
Problema
Los usuarios esperan que los selectores de idioma preserven su contexto actual. Al navegar por una página de producto, un artículo de ayuda o la configuración de la cuenta, cambiar de inglés a español debería mostrar esa misma página en español. En cambio, muchas implementaciones tratan la selección de idioma como un evento de navegación que redirige a los usuarios a la página de inicio en el nuevo idioma, obligándolos a navegar de nuevo hasta donde estaban. Esto interrumpe el flujo del usuario y genera frustración, especialmente en aplicaciones con mucho contenido donde los usuarios pueden estar en niveles profundos de la jerarquía de navegación.
La causa principal es que los selectores de idioma a menudo utilizan URLs de destino codificadas en lugar de construir URLs dinámicamente basadas en la página actual. Sin leer y transformar la estructura de URL actual, el selector no puede mantener la posición del usuario en la aplicación durante los cambios de idioma.
Solución
Construir un componente selector de idioma que lea la URL actual y extraiga tanto el parámetro de localización activo como los segmentos de ruta restantes. Para cada idioma soportado, generar una nueva URL reemplazando solo el segmento de localización mientras se mantienen intactos todos los demás segmentos de ruta y parámetros de consulta. Renderizar estas URLs como enlaces para que los usuarios puedan cambiar de idioma sin perder su ubicación en la aplicación.
Este enfoque trata la localización como un parámetro reemplazable en la estructura de URL en lugar de un destino de navegación, asegurando que el cambio de /en/products/shoes a /es/products/shoes preserve el contexto del usuario.
Pasos
1. Crear una función auxiliar para construir URLs con reconocimiento de localización
Define una función que tome la ruta actual y una localización objetivo, luego construye una nueva ruta reemplazando el segmento de localización.
export function buildLocalePath(
currentPath: string,
newLocale: string,
): string {
const segments = currentPath.split("/").filter(Boolean);
if (segments.length === 0) {
return `/${newLocale}`;
}
segments[0] = newLocale;
return `/${segments.join("/")}`;
}
Esta función divide la ruta en segmentos, reemplaza el primer segmento con la localización objetivo, y reconstruye la ruta. Maneja casos extremos como la ruta raíz y asegura que la localización sea siempre el primer segmento.
2. Define tus locales soportados
Crea un objeto de configuración que enumere todos los idiomas que tu aplicación soporta.
export const locales = [
{ code: "en", label: "English" },
{ code: "es", label: "Español" },
{ code: "fr", label: "Français" },
{ code: "de", label: "Deutsch" },
];
Esta configuración sirve como fuente de verdad para los idiomas que se mostrarán en el selector y proporciona etiquetas amigables para cada locale.
3. Construye el componente del selector de idiomas
Crea un componente que lea la ubicación actual, determine el locale activo y renderice enlaces para todos los demás idiomas soportados.
import { Link, useLocation, useParams } from "react-router";
import { locales, buildLocalePath } from "./i18n-config";
export function LanguageSwitcher() {
const location = useLocation();
const params = useParams();
const currentLocale = params.locale || "en";
return (
<nav aria-label="Language switcher">
<ul>
{locales.map((locale) => {
const isActive = locale.code === currentLocale;
const newPath = buildLocalePath(location.pathname, locale.code);
return (
<li key={locale.code}>
{isActive ? (
<span aria-current="true">{locale.label}</span>
) : (
<Link to={newPath}>{locale.label}</Link>
)}
</li>
);
})}
</ul>
</nav>
);
}
El componente utiliza useLocation para acceder a la ruta actual y useParams para extraer el locale activo de la URL. Para cada locale soportado, genera una nueva ruta utilizando la función auxiliar y renderiza un enlace o un elemento no interactivo para el idioma actual.
4. Preserva los parámetros de consulta y fragmentos hash
Extiende la función auxiliar para mantener las cadenas de consulta y los fragmentos de URL al cambiar de idioma.
export function buildLocalePath(
currentPath: string,
search: string,
hash: string,
newLocale: string,
): string {
const segments = currentPath.split("/").filter(Boolean);
if (segments.length === 0) {
return `/${newLocale}${search}${hash}`;
}
segments[0] = newLocale;
return `/${segments.join("/")}${search}${hash}`;
}
Esta versión actualizada acepta las propiedades search y hash del objeto location y las añade a la ruta generada, asegurando que los filtros, parámetros de ordenación y enlaces de anclaje sobrevivan al cambio de idioma.
5. Actualizar el componente para usar el helper mejorado
Modifica el switcher para pasar la información completa de ubicación a la función helper.
import { Link, useLocation, useParams } from "react-router";
import { locales, buildLocalePath } from "./i18n-config";
export function LanguageSwitcher() {
const location = useLocation();
const params = useParams();
const currentLocale = params.locale || "en";
return (
<nav aria-label="Language switcher">
<ul>
{locales.map((locale) => {
const isActive = locale.code === currentLocale;
const newPath = buildLocalePath(
location.pathname,
location.search,
location.hash,
locale.code,
);
return (
<li key={locale.code}>
{isActive ? (
<span aria-current="true">{locale.label}</span>
) : (
<Link to={newPath}>{locale.label}</Link>
)}
</li>
);
})}
</ul>
</nav>
);
}
El componente ahora pasa location.search y location.hash al helper, asegurando que URLs como /en/products?category=shoes#reviews se conviertan en /es/products?category=shoes#reviews cuando se cambia al español.