> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://contentful.com/developers/docs/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/_mcp/server. # Locale handling in the Optimization SDK Suite > Understand how to keep the application Contentful locale separate from the SDK Experience/event locale across Web, React Web, Next.js, Node, React Native, iOS, and Android applications. ## Overview Use this document to keep the application Contentful locale separate from the SDK Experience/event locale across Web, React Web, Next.js, Node, React Native, iOS, and Android applications. For app-owned content fetching and entry resolution, the SDKs do not resolve Contentful locales, create Contentful Delivery API clients, or infer browser, device, or request locales. Applications choose their own locale from routing, i18n, native state, or request logic and pass it to manual Contentful calls or SDK-managed entry fetching. Preview and debug tooling is separate: preview-panel APIs can use Contentful clients or pre-fetched entries to load Optimization definitions, but they do not choose or fetch locales for application content. For entry replacement mechanics, see [Entry optimization and variant resolution](/personalization/optimization-sdk/entry-personalization-and-variant-resolution/). For package setup, use the relevant integration guide or package README. ## Runtime locale surfaces Each runtime exposes the SDK Experience/event locale through its own API surface: | Runtime | Locale API surface | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Web** | `new ContentfulOptimization({ locale })`, `optimization.locale`, `optimization.states.locale`, `optimization.setLocale(locale)`, and `` | | **React Web** | ``, provider-owned ``, and `useOptimization()` access to `sdk.locale`, `sdk.states.locale`, and `sdk.setLocale(locale)` | | **Next.js** | App Router `/app-router/server` `bindNextjsAppRouterServerOptimization({ locale })` request components and advanced `createRequestHandoff({ locale })`; `/app-router/client` `bindNextjsAppRouterClientOptimization({ locale })`; Pages Router `/pages-router/server` `createRequestHandoff(context, { locale })`; lower-level server `configureNextjsServerOptimization({ locale })` and `getNextjsServerOptimizationData(sdk, { locale })`; edge `createEdgeRequestHandoff({ locale })`; and manual client `OptimizationRoot locale` | | **Node** | `new ContentfulOptimization({ locale })` for a default, `optimization.forRequest({ locale })` for request scope, and `experienceOptions.locale` as an advanced pass-through when request `locale` is absent | | **React Native** | ``, provider-owned ``, `ContentfulOptimization.initialize({ locale })`, `sdk.locale`, and `sdk.setLocale(locale)` | | **iOS** | `OptimizationConfig(locale:)`, `OptimizationRoot(config:)`, `OptimizationClient.locale`, and `OptimizationClient.setLocale(_:)` | | **Android** | `OptimizationConfig(locale = ...)`, Compose `OptimizationRoot(config = ...)`, XML Views `OptimizationManager.initialize(config = ...)`, `OptimizationClient.locale`, and `OptimizationClient.setLocale(locale)` | ## The locale channels Locale handling in an optimized application has two application-facing channels. | Channel | Owned by | Used for | | ----------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Application Contentful locale | Application router, i18n layer, request logic, or native app state | Contentful Delivery API (CDA) and Contentful Preview API (CPA) `locale` query values, UI language, route language, cache keys, and content refetching | | SDK Experience/event locale | Optimization SDK configuration or request options | Experience API `locale` query values and default event `context.locale` | The same string is often used for both channels, but the SDK treats them as separate inputs. The SDK does not know which Contentful locales are enabled in a space and does not validate that an SDK locale is supported by Contentful. ## Application Contentful locale Choose an `appLocale` with application-owned logic, then pass it to CDA or CPA calls. JavaScript managed fetching uses the application-owned `contentful.js` client from `contentful: { client }`; the SDK does not create clients, discover Contentful locales, infer browser or request locales, or own locale policy. SDK-managed Contentful fetch, JavaScript runtimes (TypeScript): ```ts const appLocale = getAppLocale() const optimization = new ContentfulOptimization({ spaceId, contentful: { client: contentfulClient, defaultQuery: { locale: appLocale }, }, locale: appLocale, }) const entry = await optimization.fetchContentfulEntry(entryId, { locale: appLocale, }) ``` Per-call `entryQuery` or `query` values override `contentful.defaultQuery`. If no Contentful query locale is provided, managed fetching falls back to the SDK `locale` before calling `contentful.js getEntry()`. Request-bound Node clients use `forRequest({ locale })` as that fallback. Use a concrete locale such as `en-US`; do not use `withAllLocales` or `locale=*` for entries that the SDK will resolve. Manual Contentful fetch, JavaScript runtimes (TypeScript): ```ts const entry = await contentfulClient.getEntry(entryId, { include: 10, locale: appLocale, }) ``` Pass the same `appLocale` anywhere Contentful content is fetched: browser data loaders, React hooks, server routes, React Native services, and native app content clients. If the app omits `locale`, Contentful uses the space default locale. Use the same `appLocale` in cache keys when localized content can differ. JavaScript runtimes (TypeScript): ```ts const cacheKey = `${appLocale}:${entryId}` ``` ## SDK Experience and event locale Stateful SDKs accept top-level `locale` as the default SDK Experience/event locale: Web runtime (TypeScript): ```ts const appLocale = getAppLocale() const optimization = new ContentfulOptimization({ spaceId, locale: appLocale, }) ``` That value initializes the SDK locale state, sets the default Experience API request locale, and provides the default event context locale. If `locale` is omitted, the Experience API locale query is omitted by default. Event payloads still include `context.locale: 'en-US'` when neither an SDK locale nor an event payload locale is available, because the event schemas require a locale. The Experience API locale is not a Contentful CDA locale. It can localize Experience API profile fields, such as `location.city` and `location.country` values used by MergeTags, and the SDK also copies it into the default event `context.locale`. It does not localize Contentful entries or resolve Contentful locale fallbacks. The low-level Experience API client still supports its own default and per-request `locale` options. Use those when calling the low-level API client directly or when an advanced stateless request needs a pass-through. ## Stateful SDKs Web, React Web, React Native, iOS, and Android keep live locale state: * JavaScript SDKs expose `optimization.locale`, `optimization.states.locale`, and `optimization.setLocale(locale)`. * Web custom elements apply `` to the SDK instance that the element creates and owns. If the element reuses an assigned `sdk` property or the global `window.contentfulOptimization` instance, update that SDK instance with `setLocale()`. * React Web and React Native providers update provider-owned SDK instances when their `locale` prop changes. * iOS exposes `OptimizationClient.locale` and `OptimizationClient.setLocale(_:)`. * Android exposes `OptimizationClient.locale` and `OptimizationClient.setLocale(locale)`. On iOS and Android, call `setLocale` only after the client has initialized; set the initial locale through `OptimizationConfig` before mounting or initializing. `setLocale(locale)` validates and normalizes the SDK Experience/event locale. It does not refetch Contentful content, update routes, or clear application caches. JavaScript managed fetching can use the SDK locale only as the fallback `getEntry()` query locale when neither `contentful.defaultQuery` nor a per-call query provides one. Application code must refetch Contentful entries with its chosen Contentful locale. Web or React Web client runtime (TypeScript): ```ts const nextLocale = getAppLocaleFromRoute() optimization.setLocale(nextLocale) const entry = await contentfulClient.getEntry(entryId, { include: 10, locale: nextLocale, }) ``` ## Next.js adapter Next.js composes the stateless Node SDK on the server with the React Web SDK on the client. App Router uses explicit runtime paths. Bind Server Components with `bindNextjsAppRouterServerOptimization({ locale })` from `/app-router/server`; use the nested `request` components for private request rendering. Bind Client Components separately with `bindNextjsAppRouterClientOptimization({ locale })` from `/app-router/client` when they need the bound browser surface. Router-neutral browser hooks and per-entry controls remain under `/client`. Next.js App Router binding module (TypeScript): ```ts import { bindNextjsAppRouterServerOptimization } from '@contentful/optimization-nextjs/app-router/server' export const optimization = bindNextjsAppRouterServerOptimization({ spaceId, locale: defaultLocale, consent: { server: consent }, }) ``` The request family uses the server binding `locale` when it creates its private-request handoff. A separate client binding applies its configured locale through React Web. Keep the values aligned when server and browser runtimes represent the same application locale. Keep lower-level/manual surfaces for direct request control. The bound App Router `createRequestHandoff({ locale })` can override the binding locale for advanced orchestration. `configureNextjsServerOptimization({ locale })` sets the server SDK default locale, `getNextjsServerOptimizationData(sdk, { locale })` binds an App Router Server Component request, the Pages Router server binding's `createRequestHandoff(context, { locale })` binds a Pages Router `getServerSideProps` request with explicit `locale` taking precedence over `context.locale`, `createEdgeRequestHandoff({ locale })` binds an edge request, and manual client `OptimizationRoot locale` follows the React Web behavior. Next.js manual server runtime (TypeScript): ```ts const optimization = configureNextjsServerOptimization({ spaceId, locale: defaultLocale, }) const { data } = await getNextjsServerOptimizationData(optimization, { consent, cookies, headers, locale: appLocale, }) export const proxy = createNextjsOptimizationContextHandler() ``` Use the request-scoped manual `locale` path when a manual route can serve different locales. A module-level `configureNextjsServerOptimization({ locale })` value is a default for the server SDK runtime, not the current request locale. Manual Server Components pass `headers()` to `getNextjsServerOptimizationData()` so the SDK can derive page context from the request URL captured by the Next.js proxy or middleware helper. Locale selection is separate from browser state handoff. When the browser provider has the server/static/edge data at its boundary, pass it with the `handoff` prop on `OptimizationRoot`. The App Router request root or provider receives its request handoff from the SDK-owned initializer. Top-level App Router static, public-permutation, analytics-only, and advanced manual paths pass an explicit handoff. With Pages Router, `pages/_app.tsx` receives `pageProps.contentfulOptimization.handoff` and passes it to the bound root. Keep `defaults` for configuration or default state such as consent policy, not for server-returned profile, selected optimizations, or changes. ## Node and stateless SDKs Node and other stateless environments can set a constructor `locale`, but that value is a default for the SDK instance. Bind request-specific locale with `forRequest({ locale })`, which is the promoted request-scoped path for localized Experience API responses and default event context. Node server runtime (TypeScript): ```ts const appLocale = getAppLocale(request) const requestUserAgent = getRequestUserAgent(request) const requestOptimization = optimization.forRequest({ consent: true, locale: appLocale, eventContext: { page: getPageContext(request), userAgent: requestUserAgent ?? 'server', }, }) const [entry, data] = await Promise.all([ contentfulClient.getEntry(entryId, { include: 10, locale: appLocale }), requestOptimization.page(), ]) ``` `forRequest({ locale })` sets the request-bound Experience API locale and default event context locale. If both `locale` and `experienceOptions.locale` are supplied, `locale` wins. Use `experienceOptions.locale` only as an advanced low-level pass-through when `locale` is not supplied. When a Node SDK is configured with `contentful: { client }`, root `fetchOptimizedEntry(entryId)` needs explicit `selectedOptimizations` for personalized results. A request-bound `forRequest()` client uses the latest accepted Experience response selections by default when `fetchOptimizedEntry(entryId)` omits `selectedOptimizations`. It also uses the request `locale` as the managed Contentful query locale when neither `contentful.defaultQuery` nor the per-call query sets `locale`. ## Entry resolution and localized Contentful content Entry resolution expects one localized view of a baseline entry and linked optimization entries. Pass direct single-locale field values to the runtime-specific entry resolution surface: * Core, Web, and Node `fetchContentfulEntry()` and `fetchOptimizedEntry()` for JavaScript SDK-managed fetching through an app-owned `contentful.js` client. * Web and Node `resolveOptimizedEntry()` for manual baseline entries. * React Web `OptimizedEntry` and `useOptimizedEntry()` with either `baselineEntry` or managed `entryId` plus optional `entryQuery`. * Web Component `ctfl-optimized-entry` with either `baselineEntry` or managed `entry-id`/`entryId` plus optional `entryQuery`. * Next.js request `OptimizedEntry` from `bindNextjsAppRouterServerOptimization()` for private App Router Server Component rendering, top-level server `OptimizedEntry` for explicit request-free paths, and bound client `OptimizedEntry` from `bindNextjsAppRouterClientOptimization()` for App Router Client Components. Use `bindNextjsPagesRouterOptimization()` for Pages Router client rendering. Lower-level server flows can use `resolveOptimizedEntry()` or managed `fetchOptimizedEntry()`, then pass manual `baselineEntry` and `resolvedData` props or the managed result to `ServerOptimizedEntry` when server-rendered tracking attributes are needed. * React Native `OptimizedEntry` and `useOptimizedEntry()` with either `baselineEntry` or managed `entryId` plus optional `entryQuery`; `useEntryResolver()` remains manual-only. * iOS `OptimizationClient.resolveOptimizedEntry(baseline:selectedOptimizations:)` and SwiftUI `OptimizedEntry(entry:)`. * Android `OptimizationClient.resolveOptimizedEntry(...)`, Compose `OptimizedEntry(entry = ...)`, and XML Views `OptimizedEntryView.setEntry(...)`. Do not pass all-locale CDA responses from `withAllLocales` or `locale=*`. The SDK does not mutate application Contentful clients or infer when a content refetch is needed. When route or language state changes, the application must update SDK locale state, refetch Contentful content with the app locale, clear SDK-managed Contentful entry cache entries when those cached CDA results are no longer valid, and invalidate app caches as needed. ## Application responsibilities Applications own: * Choosing the application Contentful locale from routes, request context, i18n state, or native app state. * Passing the Contentful locale to manual CDA and CPA requests or SDK-managed `contentful.js` fetching. * Passing the SDK Experience/event locale through top-level SDK `locale`, provider `locale`, Next.js App Router server or client binding `locale`, advanced App Router `createRequestHandoff({ locale, ... })`, Pages Router `createRequestHandoff(context, { locale, ... })`, lower-level Next.js `getNextjsServerOptimizationData(sdk, { locale, ... })`, edge `createEdgeRequestHandoff({ locale })`, native config `locale`, native `setLocale`, or Node `forRequest({ locale })`. * Keeping localized content cache keys distinct. * Refetching Contentful content after locale changes. * Ensuring the Contentful locale is supported by the target Contentful environment. ## Related documentation * [Entry optimization and variant resolution](/personalization/optimization-sdk/entry-personalization-and-variant-resolution/) * [Integrate the Web SDK in a web app](/personalization/optimization-sdk/integrate-the-web-sdk-in-a-web-app/) * [Integrate the React Web SDK in a React app](/personalization/optimization-sdk/integrate-the-react-web-sdk-in-a-react-app/) * [Integrate the Optimization Next.js SDK in a Next.js App Router app](/personalization/optimization-sdk/integrate-the-optimization-sdk-in-a-nextjs-app-router-app/) * [Integrate the Optimization Next.js SDK in a Next.js Pages Router app](/personalization/optimization-sdk/integrate-the-optimization-sdk-in-a-nextjs-pages-router-app/) * [Integrate the Optimization Node SDK in a Node app](/personalization/optimization-sdk/integrate-the-node-sdk-into-a-node-app/) * [Integrate the Optimization React Native SDK in a React Native app](/personalization/optimization-sdk/integrate-the-react-native-sdk-in-a-react-native-app/) * [React Native SDK interaction tracking mechanics](/personalization/optimization-sdk/react-native-sdk-interaction-tracking-mechanics/) * [Integrate the Optimization iOS SDK in a SwiftUI app](/personalization/optimization-sdk/integrate-the-optimization-ios-sdk-in-a-swiftui-app/) * [Integrate the Optimization iOS SDK in a UIKit app](/personalization/optimization-sdk/integrate-the-optimization-ios-sdk-in-a-uikit-app/) * [iOS SDK runtime and interaction mechanics](/personalization/optimization-sdk/ios-sdk-runtime-and-interaction-mechanics/) * [Integrate the Optimization Android SDK in a Jetpack Compose app](/personalization/optimization-sdk/integrate-the-optimization-android-sdk-in-a-compose-app/) * [Integrate the Optimization Android SDK in an Android Views app](/personalization/optimization-sdk/integrate-the-optimization-android-sdk-in-a-views-app/) * [Android SDK runtime and interaction mechanics](/personalization/optimization-sdk/android-sdk-runtime-and-interaction-mechanics/) > Understand how to keep the application Contentful locale separate from the SDK Experience/event locale across Web, React Web, Next.js, Node, React Native, iOS, and Android applications.