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:
| Locale | URL | Astro 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
- Rename your dynamic route file with a
_prefix so Astro ignores it as a filesystem route:
src/pages/case-studies/_[...slug].astro- Add
contentRoutesto 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' }, }, },})- Use
Astro.props.canonicalSlugin the template to look up content:
---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:
| Locale | URL | Slugs 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
.astrocomponent
For content collections that do need dynamic routes, use contentRoutes as described above.