Laravel multilingüe: rutas traducidas bien hechas
Fuente en texto plano, para humanos con prisa y para máquinas en general.
La mayoría de las aplicaciones Laravel multilingües se construyen igual: una tabla de rutas en inglés con un prefijo de locale atornillado delante. Funciona, y deja a las versiones que no son inglés sin poder posicionar por nada que alguien busque de verdad.
Por qué el slug tiene que estar traducido
Un prospecto neerlandés que busca precios busca tarieven. La URL es una señal de relevancia real, aunque modesta, y más importante: es lo que aparece en los resultados, se comparte en mensajes y lo leen los asistentes. /nl/pricing anuncia la traducción de un sitio inglés. /nl/tarieven anuncia una página en neerlandés.
Lo mismo vale para toda la ruta. Este sitio corre /pricing, /nl/tarieven y /es/precios; /ai-development, /nl/ai-development y /es/desarrollo-ia. El mercado neerlandés usa el término inglés para esa, así que traducirla sería peor.
Ese último detalle es el punto de verdad: la elección de slug es una decisión de keyword por idioma, no una tarea de traducción. No se puede automatizar, y es la razón de que los slugs vivan en archivos de traducción, donde una persona puede fijarlos uno a uno.
Cómo construirlo
Pon los slugs en un archivo de traducción por locale, una entrada por página:
// lang/nl/routes.php
return [
'pricing' => 'tarieven',
'about' => 'over',
'ai-development' => 'ai-development',
];
Luego registra las rutas una sola vez, resolviendo cada URI a través del traductor, dentro del mecanismo de rutas localizadas que uses. Cada locale obtiene su propia ruta con su propio path y un nombre prefijado por locale, de modo que route('pricing') da la URL del locale actual y los enlaces son correctos en todas partes sin un solo condicional en una plantilla.
Dos reglas hacen que esto aguante con el tiempo. No renombres nunca un slug que ya posiciona. No hay ventaja, y heredas una redirección que hay que mantener para siempre. Y guarda la tabla de redirecciones de URLs antiguas como datos en un solo array, no como una pila de closures sueltos, para que un test pueda afirmarla entera.
Los errores de hreflang que importan
Aquí es donde más se rompen los sitios multilingües, y los fallos son silenciosos.
El conjunto debe ser recíproco y referenciarse a sí mismo. La página de cada locale lista todos los locales, incluido el suyo. Si la página en neerlandés apunta a inglés y español pero la inglesa no apunta de vuelta al neerlandés, los buscadores descartan la relación.
x-default no es opcional. Le dice al motor qué servir a alguien a quien no le encaja ninguno de tus locales. Apúntalo al idioma de mayor alcance, normalmente el inglés.
Los canonicals se quedan dentro del locale. El canonical de la página en neerlandés es la URL en neerlandés. Un canonical que apunta desde la página neerlandesa a la inglesa es una instrucción de sacar la página neerlandesa del índice, y es un accidente notablemente frecuente.
Lista solo los locales que existen. Si un artículo de blog tiene versión inglesa y no neerlandesa, no emitas un alternate en neerlandés. Por eso las traducciones necesitan un identificador compartido (el nombre de archivo, o una clave) independiente del slug traducido, para que el sistema sepa si una traducción existe en vez de asumirlo.
No redirijas nunca a un crawler por Accept-Language
Redirigir automáticamente a los visitantes de primera visita al idioma de su navegador es bueno para las personas y desastroso si también aplica a los bots. Un crawler que llega con una IP europea y sin preferencia de idioma acaba rebotado a un locale, y los demás quedan sin rastrear y sin indexar.
Tres reglas lo mantienen seguro: exime por completo a los user agents de crawlers conocidos; redirige solo una primera visita, condicionada a una cookie, para que quien eligió un idioma lo conserve; y usa siempre una redirección temporal, nunca una permanente, porque el destino depende del visitante.
Testear lo que no se ve
Dos tests pillan la mayor parte.
Afirma que cada ruta se resuelve en cada locale, devuelve 200 y renderiza exactamente un h1. Impúlalo desde la config de locales para que añadir un idioma extienda la cobertura sola. Si añadir un locale exige un cambio de código en cualquier sitio que no sea una entrada de config y un directorio de idioma, la arquitectura está mal y este test es lo que te lo dice.
Luego afirma los conjuntos hreflang: para cada página en cada locale, el conjunto contiene cada locale, se contiene a sí mismo, incluye x-default, y cada URL del conjunto se resuelve. La reciprocidad es exactamente el tipo de propiedad tediosa de comprobar a mano y trivial de comprobar en un bucle.
La versión corta
Slugs traducidos desde archivos de traducción. Un solo registro de rutas para todos los locales. Hreflang recíproco y autorreferenciado con x-default. Canonicals dentro del locale. Bots exentos de las redirecciones de idioma. Tests que iteran la lista de locales en vez de dejarla fija.
Haz eso y añadir un cuarto idioma es un directorio y una entrada de config, que es la única definición razonable de terminado aquí.
Preguntas que también surgen
¿Subdirectorios, subdominios o dominios separados?
¿El idioma por defecto debería tener prefijo?
¿Basta la traducción automática?
¿Cómo sabemos que un locale está roto?
Quién escribe esto
Alexander (Sander) van Hooff
Alexander (Sander) van Hooff construye aplicaciones web desde 2015 y hoy dedica la mayor parte de su tiempo a llevar funciones de IA a productos que ya tienen usuarios. Vuewer es su estudio, dirigido desde la provincia de Alicante para clientes de toda Europa.
Trabaja con VuewerLecturas relacionadas
Poner en verde los Core Web Vitals de una aplicación real
Qué mueve de verdad LCP, INP y CLS en una aplicación con una década de código detrás, en el orden que da más mejora con menos riesgo.
Por qué seguimos eligiendo Laravel en 2026
Un argumento honesto a favor de un framework de diez años: qué hace Laravel mejor que las alternativas, qué hace peor, y los casos en los que elegiríamos otra cosa.
SEO, AEO y GEO: qué ha cambiado de verdad
Tres siglas, un cambio: los buscadores ahora responden en vez de enlazar. Qué cambia eso sobre cómo hay que escribir las páginas, y qué no cambia en absoluto.