---
title: Implement Localization
category: CMS Features
order: 1
status: published
summary: Configure languages in React Bricks, translate pages, and fetch localized content in a Next.js project.
estimatedTime: 12 min
keywords: localization i18n languages translations next.js locale localized content hreflang canonical
---
React Bricks stores translations as localized versions of the same page.
In this how-to, you'll set up the editorial workflow in the Dashboard, translate a page, and wire your Next.js project so it fetches the right localized content for each route.
Docs reference:
[Localization](https://docs.reactbricks.com/common-tasks/localization/)
## Configure languages in the Dashboard
Open the React Bricks Dashboard and go to **Settings**.
From there you can:
- add or remove languages, based on your plan limits
- choose the default language
The default language is important because React Bricks uses it as the source content when an editor creates a new translation. If a page has no translation in the default language, React Bricks copies content from the first available language instead.
## Translate a page
Open a page in the React Bricks visual editor.
When your app has more than one language, the editor shows language tabs for the page. Click the tab for the language you want to translate.
If that translation does not exist yet, React Bricks asks whether you want to create it.
When a translation is created:
- content is copied from the default language when available
- each language keeps its own content
- page attributes, slug, SEO fields, meta data, and custom field values are independent for each translation
This means editors can localize not only the visible text, but also language-specific metadata and page-level custom fields.
## Fetch one localized page
When rendering a page, pass the current locale as the `language` argument to `fetchPage`.
```tsx
import { fetchPage } from 'react-bricks/rsc'
import config from '@/react-bricks/config'
export async function getPage(slug: string, locale: string) {
const page = await fetchPage({
slug,
language: locale,
config,
fetchOptions: { next: { revalidate: 3 } },
})
return page
}
```
If you do not pass a `language`, React Bricks returns the page in the default language.
## Fetch localized page lists
Use the same idea when fetching lists of pages.
For server-side or build-time lists, pass `language` in the `fetchPages` options object:
```tsx
import { fetchPages } from 'react-bricks/frontend'
const posts = await fetchPages(process.env.API_KEY!, {
type: 'blog',
language: locale,
sort: '-publishedAt',
})
```
## Build a language switcher
A page returned by `fetchPage` includes a `translations` field with the available translations for that page.
Each translation includes the language, slug, page name, status, edit status, lock state, and scheduled publishing date.
```ts
type Page = {
translations: Translation[]
}
type Translation = {
language: string
slug: string
name: string
status: PageStatus
editStatus: EditStatus
isLocked: boolean
scheduledForPublishingOn: string
}
```
Use `translations` to build links to the matching localized URL.
```tsx
import Link from 'next/link'
type Translation = {
language: string
slug: string
name: string
}
export function LanguageSwitcher({
translations,
}: {
translations: Translation[]
}) {
return (
)
}
```
Adapt the `href` format to your routing strategy. For example, the home page usually needs a special case so `/en` does not become `/en/`.
## Configure Next.js App routing
If your project uses the Next.js App Router, place the locale in the route, for example:
```txt
app/[lang]/[[...slug]]/page.tsx
```
Then read the route parameter and pass it to `fetchPage`:
```tsx
export default async function Page({
params,
}: {
params: Promise<{ lang: string; slug?: string[] }>
}) {
const { lang, slug } = await params
const cleanSlug = slug?.join('/') ?? '/'
const page = await fetchPage({
slug: cleanSlug,
language: lang,
config,
fetchOptions: { next: { revalidate: 3 } },
})
return
}
```
For Next.js App projects, React Bricks provides `createI18nMiddleware` to centralize locale resolution in middleware.
Use it when your project has localized routes and you want middleware to resolve the correct language before the page renders.
In current Next.js App starters, `middleware.ts` is already set up for both i18n and optional A/B Testing:
```ts
import { NextResponse } from 'next/server'
import {
chain,
createI18nMiddleware,
createWithAbTestingMiddleware,
} from 'react-bricks/rsc'
import { i18n } from '@/i18n-config'
import { abTestingEnabled, getConfig } from './react-bricks/getConfig'
const rbConfig = getConfig()
const withAbTestingMiddleware = createWithAbTestingMiddleware({
i18n,
config: rbConfig,
})
const withI18nMiddleware = createI18nMiddleware({ i18n, NextResponse })
const middleware = abTestingEnabled
? chain([withAbTestingMiddleware, withI18nMiddleware])
: withI18nMiddleware
export default middleware
export const config = {
// Matcher ignoring `/_next/` and `/api/`
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|admin|logo.svg|bricks-preview-images|masks|preview).*)',
],
}
```
If your project only uses localization, `withI18nMiddleware` handles the request.
If your project also uses A/B Testing, `chain` runs the A/B Testing middleware first and the i18n middleware after it, so the same request resolves both the active variant and the active locale.
If you maintain an older Next.js Pages Router project, see the [Localization docs](https://docs.reactbricks.com/common-tasks/localization/) for the Pages Router setup.
## Manage canonical and hreflang metadata
Localized pages should expose a canonical URL and `hreflang` alternates, so search engines can understand which translated URLs belong to the same page.
Create an `i18n-config.ts` file in the project root:
```ts
export const i18n = {
siteUrl: process.env.NEXT_PUBLIC_SITE_URL,
defaultLocale: 'en',
locales: ['en', 'it'],
} as const
export type Locale = (typeof i18n)['locales'][number]
```
Then, in `app/[lang]/[[...slug]]/page.tsx`, use the page translations returned by React Bricks to create the alternate language URLs.
```tsx
import type { Metadata } from 'next'
import { getMetadata } from 'react-bricks/rsc'
import { i18n, type Locale } from '@/i18n-config'
function getLocalizedUrl(language: string, slug: string) {
const normalizedSlug = slug === '/' ? '' : slug
const localePrefix = language === i18n.defaultLocale ? '' : `/${language}`
return `${i18n.siteUrl}${localePrefix}/${normalizedSlug}`
}
export async function generateMetadata(props: {
params: Promise<{ lang: Locale; slug?: string[] }>
}): Promise {
const params = await props.params
const cleanSlug = params.slug?.join('/') ?? '/'
const { page } = await getData(cleanSlug, params.lang)
if (!page?.meta) {
return {}
}
const metadata = getMetadata(page)
return {
...metadata,
alternates: {
canonical: getLocalizedUrl(params.lang, page.slug),
languages: page.translations.reduce>(
(acc, translation) => ({
...acc,
[translation.language]: getLocalizedUrl(
translation.language,
translation.slug
),
}),
{}
),
},
}
}
```
With this pattern:
- the default language uses URLs without a locale prefix
- translated pages use URLs with the language prefix
- each translation gets its own canonical URL
- `page.translations` provides the language and slug for each alternate URL
## Check the result
After wiring localization:
- create or translate a page in at least two languages
- visit the localized routes in your app
- confirm each route fetches the correct translation
- check that language switcher links use the translated slugs
- verify localized canonical URLs, hreflang alternates, SEO fields, and custom fields if your page type uses them
## Related docs
- [Localization CMS feature](https://docs.reactbricks.com/cms-features/localization/)
- [Implement Localization](https://docs.reactbricks.com/common-tasks/localization/)
- [fetchPage](https://docs.reactbricks.com/api-reference/utilities/fetch-page/)
- [fetchPages](https://docs.reactbricks.com/api-reference/utilities/fetch-pages/)
- [createI18nMiddleware](https://docs.reactbricks.com/api-reference/utilities/create-i18n-middleware/)