Internationalisation and locale routing
Internationalisation affects URLs, metadata, formatting, caching, and layout. Decide the URL model before translating the first page.
Give each language a stable URL
A common App Router structure uses a dynamic locale segment such as app/[lang]. That makes language part of the URL, which helps links remain shareable and lets search engines understand each version as a distinct page.
Choose the supported locale list centrally. Reject unknown values with notFound instead of rendering a partly translated fallback under a URL that looks valid.
const locales = ['en', 'id', 'nl'] as const
export function generateStaticParams() {
return locales.map((lang) => ({ lang }))
}
export default async function Layout({ children, params }) {
const { lang } = await params
if (!locales.includes(lang)) notFound()
return <html lang={lang}><body>{children}</body></html>
}Detection should help, not trap the visitor
The Accept-Language header can suggest a first language, but it should not override an explicit locale already present in the URL. Once a visitor chooses a language, remember the choice and keep the language switcher available.
Avoid redirect loops between a platform-level redirect and application locale detection. Pick one layer to own the initial decision and test requests with and without locale cookies.
Translate messages, but format values with locale-aware APIs
Dates, numbers, currencies, and plural rules should not be assembled with string concatenation. Intl.DateTimeFormat, Intl.NumberFormat, and Intl.PluralRules encode conventions that are easy to miss.
Keep raw values in data and format them near the presentation layer. A stored date should not become 09/10/2026 until the application knows which locale is reading it.
Metadata needs the same translation care as the page
Generate a translated title and description for every locale. Add alternate language URLs so search engines can connect equivalent pages, and make the canonical URL point to the current language version rather than always pointing to the default language.
Open Graph images may need translated text too. If one image is shared across languages, avoid embedding copy that contradicts the page title.
Design for text that changes shape
Translations are rarely the same length. Navigation labels grow, buttons wrap, and headings may need different line breaks. Test with long realistic strings rather than a made-up Lorem ipsum paragraph.
If right-to-left languages are supported, use logical CSS properties such as margin-inline and inset-inline. Check icons that imply direction, because an arrow that means next may need to flip while a familiar media-control icon may not.
Can you explain “Internationalisation and locale routing” without reaching for the API reference?
Describe the main trade-off, one failure mode, and the decision you would make in a real project.