How do you set up Next.js i18n on a marketing site?
You set up Next.js i18n by defining your locales once, routing every page under a locale segment, pulling localized content from your CMS, and generating hreflang and sitemaps from the same list. Most broken multilingual Next.js sites have these pieces set up separately, by different people, at different times.
This is the build side of our multilingual website guide. It assumes you've already chosen subfolders such as /de/ and /fr/, which is the right default for most startups. The examples follow the App Router, which is what new Next.js marketing sites use in 2026. If you're still deciding whether Next.js is the right platform at all, read when Next.js is the right choice for a marketing website first.
Layer | What it does | Where it lives |
|---|---|---|
Locale config | Lists supported locales and the default | One shared file imported everywhere |
Routing | Serves /de/pricing, /fr/pricing and so on | app/[locale]/ segment and generateStaticParams |
Detection | Suggests or redirects at the root only | Middleware (called proxy in recent Next.js versions) or the library's helper |
UI strings | Buttons, nav, form labels | Message files or a library such as next-intl |
Page content | Headlines, sections, proof, pricing | CMS with field-level or document-level locales |
SEO output | hreflang, canonicals, translated titles, sitemaps | generateMetadata and sitemap.ts |
How does i18n routing in Next.js work in the App Router?
i18n routing in the Next.js App Router works by nesting your pages under a dynamic [locale] folder, so the first part of the URL becomes a parameter every page can read. Unlike the old Pages Router, there's no i18n option in the config file; routing is just a route segment you control.
Define locales once
Create a small module exporting your locales (for example en, de, fr) and the default. Every other piece imports from here, so adding a language means changing one list.
Add the locale segment
Move your pages under app/[locale]/. The root layout reads the locale parameter and sets the html lang attribute, plus dir="rtl" for right-to-left languages.
Generate every locale statically
Export generateStaticParams from the locale layout so every locale is prerendered at build time. Marketing pages should be static or cached, not rendered on every request.
Reject unknown locales
If the locale parameter isn't in your list, call notFound() so /xx/pricing returns a real 404 instead of an empty page.
Decide on the default locale URL
Either prefix every locale (/en/, /de/) or keep the default unprefixed (/pricing, /de/pricing). Unprefixed defaults keep existing English URLs, which matters if you already have rankings.
Handle the root gently
At the root only, middleware can suggest a locale based on the Accept-Language header. Remember the visitor's choice in a cookie, and never redirect someone who clicked a link to a specific locale.
Should you use next-intl or build your own setup?
Use next-intl, or a similar maintained library, when your site has a meaningful number of UI strings, plurals or formatted dates and numbers. A small marketing site whose words mostly come from the CMS can get by with a plain dictionary per locale and the built-in Intl APIs.
Pros
- next-intl handles locale-aware routing helpers, links and middleware for you
- Message formatting covers plurals, dates, numbers and currencies correctly per locale
- Works with Server Components, so strings don't have to ship to the browser
- A maintained library tracks App Router changes so you don't have to
Cons
- Another dependency to update when Next.js releases a major version
- Overkill for sites where nearly all copy lives in the CMS
- Its conventions shape your folder structure and imports
- Message files can duplicate content that editors expect to find in the CMS
Whichever you choose, keep UI strings and page content apart. Buttons and form labels belong in message files that developers own. Headlines, proof and pricing belong in the CMS that marketing owns. Mixing them is how translators end up editing code.
How should the CMS handle locales?
The CMS should store content per locale with a clear fallback, so editors can localize the fields that need it without duplicating whole pages. Most headless CMSs offer field-level localization, document-level localization, or both.
- Field-level localization keeps one document with a value per locale for each translatable field. It's tidy for pages with the same structure in every language, and it's how Payload's localization works.
- Document-level localization creates a separate document per language. It suits content that differs a lot by market, such as localized case studies or region-only pages.
- Fallbacks decide what happens when a translation is missing. For marketing pages, falling back silently to English can publish half-translated pages, so prefer showing the page only when it's complete.
- Localized slugs let /de/preise exist alongside /pricing. Store the slug per locale and build routes from it, then generate redirects when a slug changes.
Our own site runs on Next.js with Payload on Cloudflare Workers, and we model locales in the CMS before writing a single page component. Our guide to hosting Next.js on Cloudflare covers the deployment side. Components also need to flex for longer text and right-to-left scripts, which our guide to multilingual web design covers.
How do you generate hreflang and sitemaps in Next.js?
Generate hreflang by returning alternates.languages from generateMetadata on each page, built from your locale list and that page's localized slugs. Generate the sitemap the same way in sitemap.ts, with an alternates entry for each URL. One source means return tags are always complete.
- In generateMetadata, look up the page's slug in every locale where it exists.
- Return alternates.canonical as the page's own URL in the current locale, so every version is self-canonical.
- Return alternates.languages with one entry per available locale plus x-default, using absolute URLs via metadataBase.
- Translate the title, description and Open Graph fields from the CMS, not from the default locale.
- In sitemap.ts, list every localized URL with its alternates, and skip locales where the page doesn't exist.
If you want the rules behind the output, our explainer on hreflang setup and mistakes covers self-references, return tags and codes. The Next.js documentation at nextjs.org/docs describes the metadata and sitemap APIs.
Hypothetical example: adding French to an English and German Next.js site
For example, suppose a site already serves English at the root and German under /de/, from one locale list, with Payload storing localized fields.
Adding French means: add fr to the locale list; enable fr in the CMS localization config; translate UI strings into a French message file; have editors localize the page fields and French slugs; review the pages in the preview deployment. Because routes, metadata, sitemaps and the language switcher all read the same list, hreflang on every English and German page gains a French entry automatically on the next build.
No component code changes. If a French page isn't ready, it's left out of the build and out of every hreflang set until it is.
What should you check before launching a multilingual Next.js site?
Before launch, check that every locale renders statically, returns the right status codes, outputs complete hreflang, and can be switched without losing the visitor's place. These are the failures we see most on multilingual Next.js builds.
- Every locale URL returns 200, and unknown locales return 404.
- The html lang and dir attributes are correct per page.
- View the source of one page per locale: canonical, hreflang set and translated titles are all present in the HTML, not added by client-side code.
- The language switcher links to the equivalent page, not the home page, and hides locales where the page doesn't exist.
- Preview deployments are not indexable, and production is.
- If you're moving an existing multilingual site to Next.js, every old URL redirects to its new locale URL. Our guide to Next.js internationalization for migrated sites covers that mapping.
