All pages use catch-all routing: [...slug].astro. The params key must match the filename bracket word exactly (e.g., [...home].astro → params: { home: path }).
Every page follows this skeleton:
---
import GlobalLayout from '@layouts/GlobalLayout.astro';
import Navigation from '@layouts/Navigation.astro';
import { loadTranslations } from '@utils/translate';
export async function getStaticPaths() {
await loadTranslations() // MUST be first — before any getTranslation() call
const pages = await getPageBodyData("content-type", "&populate[...]")
// OR: const pages = await getStrapi("content-type?locale=all&populate=*")
let pathArray = [];
pages.forEach((page) => {
const locale = page.attributes.locale
const path = getLocaleSlug(locale) + "/slug/" + page.attributes.SEO.canonicalURL
pathArray.push({
params: { paramName: path },
props: { SEO, locale, body, hrefLangObject, canonical: path }
});
});
return pathArray;
}
---
<GlobalLayout SEO={SEO} locale={locale} canonical={canonical} navbarLangLinks={hrefLangObject}>
<body>
<Navigation locale={locale} navbarLangLinks={hrefLangObject} navColor="white">
<main>...</main>
</Navigation>
</body>
</GlobalLayout>
getPageBodyData() or getStrapi("...?locale=all") — internally fetches en/fr/de in parallel. Locale comes from page.attributes.locale.getAllBlogPosts() then languageToLocale(post.attributes.Language) to derive locale. Filter by Language field, not locale.See architecture.md § i18n Handling for the complete breakdown.
<link rel="alternate" hreflang>.singleHrefLang prop on GlobalLayout). Other locales link back to blog index.| Import | From |
|---|---|
getPageBodyData (default export) | @utils/strapi |
getStrapi, getAllBlogPosts, getFormByTarget | @utils/strapi |
loadTranslations, getTranslation | @utils/translate |
getLocaleSlug, buildHrefLangObject, languageToLocale | @utils/helper |
generateBreadcrumbSchema, generateFAQSchema, etc. | @utils/schema |
GlobalLayout | @layouts/GlobalLayout.astro |
Navigation | @layouts/Navigation.astro |
/ (root, empty string from getLocaleSlug)/fr/deloadTranslations() must be called in getStaticPaths() before any getTranslation() usage.getFormByTarget(target, locale) not form IDs — IDs are unstable after Strapi v5 migration.architecture.md § Page Routing.