コンテンツにスキップ

Translated Routes

このコンテンツはまだ日本語訳がありません。

astro-i18n-next generates locale-specific routes at build time. The default locale has no URL prefix, while all other locales are prefixed.

How it works

Given a page src/pages/about.astro with:

---
export const slugs = { en: 'about', es: 'sobre' };
---

The integration generates these routes:

LocaleURLAstro entrypoint
en (default)/about/src/pages/about.astro
es/es/sobre/src/pages/about.astro

Both routes render the same component — the locale is available via Astro.locals.locale.

Using localePath()

localePath translates a canonical (default-locale) path to the correct locale-specific URL:

import { localePath } from 'virtual:i18n';
localePath('en', '/about/'); // "/about/"
localePath('es', '/about/'); // "/es/sobre/"
localePath('es', '/saunas/model-165/'); // "/es/saunas/modelo-165/"

It handles both page slugs and content slugs, and works with nested paths.

Nested routes

For nested pages like src/pages/saunas/index.astro, only the final slug segment is translated. The directory structure stays the same:

/saunas/ → /es/saunas/ (directory not translated)
/saunas/model-165/ → /es/saunas/modelo-165/ (content slug translated)

Content collection routes

For content collections (blog posts, case studies, etc.) that use dynamic [...slug] routes, the integration provides contentRoutes to generate per-locale virtual entrypoints. This prevents cross-locale slug pollution — where Spanish slugs appear under English paths and vice versa.

Setup

  1. Rename your dynamic route file with a _ prefix so Astro ignores it as a filesystem route:
src/pages/case-studies/_[...slug].astro
  1. Add contentRoutes to your config:
createI18n({
// ...
contentRoutes: {
'case-studies': {
template: 'src/pages/case-studies/_[...slug].astro',
contentDir: 'src/content/case-studies',
prefixes: { en: 'case-studies', es: 'estudios-de-caso' },
},
},
})
  1. Use Astro.props.canonicalSlug in the template to look up content:
src/pages/case-studies/_[...slug].astro
---
import { getEntry } from 'astro:content';
import { localized } from 'virtual:i18n';
const { canonicalSlug } = Astro.props;
const locale = Astro.locals.locale;
const entry = await getEntry('case-studies', canonicalSlug);
const title = localized(entry.data.title, locale);
---
<h1>{title}</h1>

How it works

The integration generates a virtual entrypoint for each locale, each with its own getStaticPaths() that returns only that locale’s slugs:

LocaleURLSlugs returned
en (default)/case-studies/[...slug]English slugs only
es/es/estudios-de-caso/[...slug]Spanish slugs only

This ensures:

  • /case-studies/annex-ai-platform/ exists (EN)
  • /es/estudios-de-caso/annex-plataforma-ia/ exists (ES)
  • /case-studies/annex-plataforma-ia/ does NOT exist (no cross-locale pollution)

No dynamic routes needed for static pages

Unlike other i18n approaches, you don’t need [...slug].astro catch-all routes for static pages. The integration uses Astro’s injectRoute() API to create concrete routes for every locale at build time. This means:

  • Full static prerendering
  • No runtime route matching
  • Each route maps to a known .astro component

For content collections that do need dynamic routes, use contentRoutes as described above.