|
1 | 1 | --- |
2 | 2 | title: Internationalization |
3 | 3 | description: Add support for multiple languages with internationalized routing and localized content. |
| 4 | +related: |
| 5 | + description: Related API references and conventions. |
| 6 | + links: |
| 7 | + - app/api-reference/functions/next-root-params |
4 | 8 | --- |
5 | 9 |
|
6 | 10 | Next.js enables you to configure the routing and rendering of content to support multiple languages. Making your site adaptive to different locales includes translated content (localization) and internationalized routes. |
@@ -178,6 +182,75 @@ export default async function Page({ params }) { |
178 | 182 |
|
179 | 183 | Because all layouts and pages in the `app/` directory default to [Server Components](/docs/app/getting-started/server-and-client-components), we do not need to worry about the size of the translation files affecting our client-side JavaScript bundle size. This code will **only run on the server**, and only the resulting HTML will be sent to the browser. |
180 | 184 |
|
| 185 | +## Sharing the locale across your app |
| 186 | + |
| 187 | +The locale is often needed beyond the page that receives it, such as in shared data-fetching utilities or deeply nested components. Instead of prop drilling `lang` through each layer, we can read it directly with [`next/root-params`](/docs/app/api-reference/functions/next-root-params). |
| 188 | + |
| 189 | +`next/root-params` exports a getter for each dynamic segment above the root layout. Since every route is nested under `app/[lang]`, `lang` is a root parameter, and any Server Component or server-side utility can call its getter. We can move the locale lookup into `getDictionary`, so callers no longer pass `lang`: |
| 190 | + |
| 191 | +```ts filename="app/[lang]/dictionaries.ts" highlight={1,2,15,16,17} switcher |
| 192 | +import { lang } from 'next/root-params' |
| 193 | +import { notFound } from 'next/navigation' |
| 194 | + |
| 195 | +const dictionaries = { |
| 196 | + en: () => import('./dictionaries/en.json').then((module) => module.default), |
| 197 | + nl: () => import('./dictionaries/nl.json').then((module) => module.default), |
| 198 | +} |
| 199 | + |
| 200 | +export type Locale = keyof typeof dictionaries |
| 201 | + |
| 202 | +export const hasLocale = (locale: string): locale is Locale => |
| 203 | + locale in dictionaries |
| 204 | + |
| 205 | +export const getDictionary = async () => { |
| 206 | + const locale = await lang() |
| 207 | + if (!hasLocale(locale)) notFound() |
| 208 | + return dictionaries[locale]() |
| 209 | +} |
| 210 | +``` |
| 211 | + |
| 212 | +```js filename="app/[lang]/dictionaries.js" highlight={1,2,12,13,14} switcher |
| 213 | +import { lang } from 'next/root-params' |
| 214 | +import { notFound } from 'next/navigation' |
| 215 | + |
| 216 | +const dictionaries = { |
| 217 | + en: () => import('./dictionaries/en.json').then((module) => module.default), |
| 218 | + nl: () => import('./dictionaries/nl.json').then((module) => module.default), |
| 219 | +} |
| 220 | + |
| 221 | +export const hasLocale = (locale) => locale in dictionaries |
| 222 | + |
| 223 | +export const getDictionary = async () => { |
| 224 | + const locale = await lang() |
| 225 | + if (!hasLocale(locale)) notFound() |
| 226 | + return dictionaries[locale]() |
| 227 | +} |
| 228 | +``` |
| 229 | + |
| 230 | +> **Good to know:** Files that import from `next/root-params` do not need `import 'server-only'`. The import already fails at build time if used in a Client Component. |
| 231 | +
|
| 232 | +Pages and components then call `getDictionary()` with no arguments, since the locale is resolved internally: |
| 233 | + |
| 234 | +```tsx filename="app/[lang]/page.tsx" highlight={4} switcher |
| 235 | +import { getDictionary } from './dictionaries' |
| 236 | + |
| 237 | +export default async function Page() { |
| 238 | + const dict = await getDictionary() |
| 239 | + return <button>{dict.products.cart}</button> // Add to Cart |
| 240 | +} |
| 241 | +``` |
| 242 | + |
| 243 | +```jsx filename="app/[lang]/page.js" highlight={4} switcher |
| 244 | +import { getDictionary } from './dictionaries' |
| 245 | + |
| 246 | +export default async function Page() { |
| 247 | + const dict = await getDictionary() |
| 248 | + return <button>{dict.products.cart}</button> // Add to Cart |
| 249 | +} |
| 250 | +``` |
| 251 | + |
| 252 | +> **Good to know:** Root parameter getters run in Server Components and server-side utilities, but not in Client Components, Server Actions, or Route Handlers. See [`next/root-params`](/docs/app/api-reference/functions/next-root-params) for the full API and its behavior with caching. |
| 253 | +
|
181 | 254 | ## Static Rendering |
182 | 255 |
|
183 | 256 | To generate static routes for a given set of locales, we can use `generateStaticParams` with any page or layout. This can be global, for example, in the root layout: |
|
0 commit comments