Routing

astro-intl provee un sistema de routing que te permite definir rutas URL traducidas para cada locale. Esto permite URLs localizadas como /es/sobre-nosotros en lugar de /es/about.

1. Define tus rutas

Agrega un mapa de routes a tu configuración de routing. Cada clave es un nombre de ruta, y cada valor mapea locales a sus templates de URL. Usa [param] para segmentos dinámicos.

src/i18n/routing.ts
export const routing = {
  locales: ["en", "es"],
  defaultLocale: "en",
  routes: {
    home:  { en: "/",                      es: "/" },
    about: { en: "/about",                 es: "/sobre-nosotros" },
    blog:  { en: "/blog/[slug]",           es: "/blog/[slug]" },
    shop:  { en: "/shop/[category]/[id]",  es: "/tienda/[category]/[id]" },
  },
} as const;

2a. Con Middleware (recomendado)

Cuando usas createIntlMiddleware, pasa tus rutas en la config. El middleware automáticamente reescribirá las URLs traducidas a sus rutas canónicas del filesystem — sin archivos de página extra.

Pasa las rutas al middleware:

src/middleware.ts
import "@/i18n/request";
import { createIntlMiddleware } from "astro-intl/middleware";
import { routing } from "@/i18n/routing";

export const onRequest = createIntlMiddleware(routing);

Tu filesystem solo necesita las rutas del locale por defecto:

File structure
src/pages/
└── [lang]/
    ├── index.astro        ← /en/  and  /es/
    ├── about.astro        ← /en/about  and  /es/sobre-nosotros
    ├── blog/
    │   └── [slug].astro   ← /en/blog/my-post  and  /es/blog/mi-post
    └── shop/
        └── [category]/
            └── [id].astro ← /en/shop/clothing/42  and  /es/tienda/clothing/42
Cómo funciona Cuando un usuario visita /es/sobre-nosotros, el middleware lo matchea contra el mapa de routes, encuentra el template canónico (/about) y reescribe la petición a /es/about — que mapea a tu archivo [lang]/about.astro. Sin páginas duplicadas.

2b. Sin Middleware

Si prefieres no usar middleware, aún puedes usar path() y switchLocalePath() para generación de URLs. Configura las rutas via las opciones de la integración.

astro.config.mjs
import { defineConfig } from "astro/config";
import astroIntl from "astro-intl";

export default defineConfig({
  integrations: [
    astroIntl({
      defaultLocale: "en",
      locales: ["en", "es"],
      routes: {
        about: { en: "/about", es: "/sobre-nosotros" },
        blog:  { en: "/blog/[slug]", es: "/blog/[slug]" },
      },
    }),
  ],
});
Sin middleware, no hay rewrites automáticos. Necesitas crear archivos de página para cada ruta traducida. Lo más simple es crear páginas wrapper que re-exporten la canónica.

Ejemplo: Routing nativo de Astro para rutas traducidas

Crea un archivo de página para cada URL traducida. Estas páginas "wrapper" simplemente re-exportan todo desde la página canónica (locale por defecto) — cero duplicación.

Tu página canónica (rutas del locale por defecto):

src/pages/[lang]/about.astro
---
import Layout from "@/layouts/Layout.astro";
import { getTranslations } from "astro-intl";

const t = getTranslations("about");

export function getStaticPaths() {
  return [
    { params: { lang: "en" } },
    { params: { lang: "es" } },
  ];
}
---

<Layout title={t("title")}>
  <h1>{t("heading")}</h1>
  <p>{t("description")}</p>
</Layout>

Crea la ruta traducida como un wrapper ligero:

src/pages/[lang]/sobre-nosotros.astro
---
// Re-export everything from the canonical page
export { default } from "./about.astro";
export { getStaticPaths } from "./about.astro";
---

Estructura de archivos resultante:

File structure
src/pages/[lang]/
├── about.astro              ← Canonical page (all the logic)
├── sobre-nosotros.astro     ← Wrapper (2 lines, re-exports about.astro)
├── blog/
│   └── [slug].astro         ← Same URL structure, no wrapper needed
└── shop/
    └── [category]/
        └── [id].astro       ← Canonical for /shop/:category/:id

src/pages/[lang]/tienda/
└── [category]/
    └── [id].astro           ← Wrapper (re-exports ../shop/[category]/[id].astro)
Cada archivo wrapper son solo 2 líneas. La página canónica contiene toda la lógica. Cuando Astro construye /es/sobre-nosotros, renderiza el mismo componente que /es/about — la URL traducida funciona sin middleware.

3. Generando URLs con path()

Usa path(routeKey, options?) para generar URLs localizadas. Escoge el template correcto para el locale destino y sustituye cualquier param.

src/pages/[lang]/index.astro
---
import { path } from "astro-intl/routing";
---

<!-- Simple route -->
<a href={path("about")}>About</a>
<!-- Current locale "en" → /en/about -->
<!-- Current locale "es" → /es/sobre-nosotros -->

<!-- Explicit locale -->
<a href={path("about", { locale: "es" })}>Sobre nosotros</a>
<!-- → /es/sobre-nosotros -->

<!-- With params -->
<a href={path("blog", { params: { slug: "hello-world" } })}>Read post</a>
<!-- → /en/blog/hello-world -->

<!-- Multiple params -->
<a href={path("shop", { locale: "es", params: { category: "ropa", id: "42" } })}>
  Ver producto
</a>
<!-- → /es/tienda/ropa/42 -->

4. Cambiando locale con switchLocalePath()

Usa switchLocalePath(currentPath, nextLocale) para convertir la URL actual a su equivalente en otro locale. Matchea la ruta contra los templates, extrae los params y reconstruye la URL con el template del locale destino.

src/components/LanguageSelector.astro
---
import { switchLocalePath } from "astro-intl/routing";

const currentPath = Astro.url.pathname;
---

<nav>
  <a href={switchLocalePath(currentPath, "en")}>English</a>
  <a href={switchLocalePath(currentPath, "es")}>Español</a>
</nav>

<!-- On /en/about         → switches to /es/sobre-nosotros -->
<!-- On /es/tienda/ropa/42 → switches to /en/shop/ropa/42 -->
<!-- On /en/unknown/page   → falls back to /es/unknown/page -->
Si la ruta actual no matchea ningún template de ruta, switchLocalePath hace fallback a simplemente intercambiar el prefijo del locale.

Parámetros Dinámicos

Los templates de rutas soportan segmentos dinámicos con sintaxis [param]. Puedes tener múltiples params por ruta — se extraen y sustituyen automáticamente.

Route with multiple params
import { path } from "astro-intl/routing";

// Route defined as:
// shop: { en: "/shop/[category]/[id]", es: "/tienda/[category]/[id]" }

const url = path("shop", {
  locale: "es",
  params: { category: "electronics", id: "123" },
});
// → /es/tienda/electronics/123

Rutas Fallback (Astro 6.1+)

A partir de Astro 6.1, la integración detecta automáticamente las fallbackRoutes del hook astro:routes:resolved. Cuando configuras i18n.fallbackType: 'rewrite' en tu config de Astro, Astro genera rutas fallback para locales que no tienen su propio contenido. astro-intl las recopila y las hace disponibles via getFallbackRoutes().

Consulta las rutas fallback en tiempo de ejecución:

src/pages/[lang]/index.astro
---
import { getFallbackRoutes } from "astro-intl";

const fallbacks = getFallbackRoutes();
// [
//   { pattern: "/fr/about", pathname: "/fr/about/", locale: "fr" },
//   { pattern: "/fr/blog/[...slug]", locale: "fr" },
// ]

// Example: check if the current page has a fallback for a locale
const hasFrenchFallback = fallbacks.some(fb => fb.locale === "fr");
---
En Astro < 6.1, getFallbackRoutes() retorna un array vacío — sin errores, totalmente compatible hacia atrás.