Lesson 28 of 81
There's no config toggle that gives an App Router app multiple languages — you build the routing yourself out of a [locale] segment, a proxy that detects the visitor's language, and a way to read that locale from anywhere without threading it through every layer.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
A utility function imported into a Client Component calls `locale()` from `next/root-params`. What happens?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
The old Pages Router had a built-in i18n object in next.config.js — list your locales, Next.js handled locale-prefixed routing automatically. That config key doesn't exist for the App Router. There's no automatic mode to opt into; internationalized routing is a pattern you build, not a setting you flip.
The pattern has three pieces:
[locale] dynamic segment wrapping the whole app/ tree (app/[locale]/page.tsx, app/[locale]/layout.tsx), so every route is implicitly locale-aware.proxy.ts inspecting the incoming request — usually the Accept-Language header — and redirecting an unprefixed URL like /products to a locale-prefixed one like /en-US/products before any page renders.locale deep inside the tree — in a data-fetching utility three layers below the page, say — without passing it down as a prop through every intermediate component. That's what next/root-params is for.locale here is what the docs call a root parameter: a dynamic segment that appears above the root layout, shared by every route beneath it. Because it's guaranteed present for the whole subtree, next/root-params exposes it as an importable async function you can call from any Server Component or server-side utility — not just the page that matched the URL.
// app/[locale]/layout.tsx — root layout, defines the root parameter
export default async function RootLayout(props: LayoutProps<'/[locale]'>) {
return <html>{props.children}</html>
}
// lib/get-translations.ts — three layers deep, no locale prop in sight
import { locale } from 'next/root-params'
export async function getTranslations() {
const current = await locale() // reads the root segment directly
return import(`@/locales/${current}.json`)
}
// app/[locale]/components/nav.tsx
async function getNavigation() {
'use cache'
const current = await locale()
const res = await fetch(`https://api.example.com/nav?locale=${current}`)
return res.json()
}
getTranslations() never receives locale as an argument — it calls the getter directly. That matters for the cached getNavigation() function too: because Next.js can see exactly which root parameter getter a "use cache" function calls, only locale becomes part of that function's cache key, not every dynamic segment in the route. Pass locale in manually as a plain argument instead, and you lose that precision — the cache key is whatever you constructed it from.
i18n config object. Reaching for a next.config.ts i18n key and finding nothing there isn't a documentation gap — the automatic routing mode simply isn't part of the App Router's model; proxy.ts + a [locale] segment is the actual answer.next/root-params only exposes segments above the root layout. A [slug] segment three folders deep in app/[locale]/blog/[slug]/ is a regular route parameter, read through the params prop — not a root parameter, and next/root-params won't expose it.[post-slug] aren't supported at all, since the export name has to be a valid JS identifier.locale outside a "use cache" function and passing it in as an argument works, but you lose the "only what's actually read becomes part of the key" precision — you're back to manually shaping the cache key yourself."How would you add multi-language support to an App Router app, and how would a deeply nested component read the current locale without every layout in between passing it down?" tests whether a candidate knows the App Router replaced automatic i18n routing with a manual, composable pattern — and whether they've actually used next/root-params rather than reaching for React context as the only tool they know.