Use this document to keep the application Contentful locale separate from the SDK Experience/event locale. The SDKs do not resolve Contentful locales, wrap CDA 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 each system that needs it.
For entry replacement mechanics, see Entry personalization and variant resolution. For package setup, use the relevant integration guide or package README.
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 | CDA/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.
Applications fetch Contentful entries directly. Choose an appLocale with application-owned logic,
then pass it to CDA or CPA calls:
const appLocale = getAppLocale()
const entry = await contentfulClient.getEntry(entryId, {
include: 10,
locale: appLocale,
})
Do this 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:
const cacheKey = `${appLocale}:${entryId}`
Stateful SDKs accept top-level locale as the default SDK Experience/event locale:
const appLocale = getAppLocale()
const optimization = new ContentfulOptimization({
clientId,
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 can still use the event-builder fallback required by the event
schemas.
The low-level Experience API client still supports its own default and per-request locale options.
Use those only when calling the low-level API client directly or when an advanced stateless request
needs a pass-through.
Web, React Web, React Native, iOS, and Android keep live locale state:
optimization.locale, optimization.states.locale, and
optimization.setLocale(locale).locale prop changes.client.locale, native state locale, and setLocale(...).setLocale(locale) validates and normalizes the SDK Experience/event locale. It does not refetch
Contentful content, update routes, or clear application caches. Application code must refetch
Contentful entries with its chosen Contentful locale.
const nextLocale = getAppLocaleFromRoute()
optimization.setLocale(nextLocale)
const entry = await contentfulClient.getEntry(entryId, {
include: 10,
locale: nextLocale,
})
Node and other stateless environments bind locale per request with forRequest({ locale }):
const appLocale = getAppLocale(request)
const requestOptimization = optimization.forRequest({
consent: true,
locale: appLocale,
eventContext: {
page: getPageContext(request),
userAgent: request.headers.get('user-agent') ?? '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.
Entry resolution expects one localized view of a baseline entry and linked optimization entries.
Pass direct single-locale field values to resolveOptimizedEntry(), OptimizedEntry,
useEntryResolver(), or native entry helpers. 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 should update SDK locale state, refetch Contentful content with the app locale, and invalidate app caches as needed.
Applications own:
locale, provider locale, native
config locale, or Node forRequest({ locale }).