locale field in a response means.
How Encore picks the language
Every request resolves to one served language. Encore takes the first candidate that is enabled for your app:
A candidate is used only when your app has that language enabled. One that is not enabled is skipped, not rejected. Encore walks the device’s languages in order and serves the first one your app has enabled. A device set to French, then Spanish, on an app with only English and Spanish enabled gets Spanish. The app’s default language is served only when none of the user’s languages is enabled. English is always enabled, so a device that lists English always has a match.
Encore serves by language, not by region. It accepts any BCP-47 tag and keeps only the language part, so
es-MX, es-ES, and es all serve es. Send whichever form you already have. Two consequences worth knowing:
ptis translated as Brazilian Portuguese, so users in Portugal see Brazilian spelling.zhis translated as Simplified Chinese, so users in Taiwan and Hong Kong see Simplified characters.
What the SDKs send for you
You do not have to do anything for a user to get their own language.Override the language
CallsetLanguage() when your app has its own language picker, or when the user’s app language differs from their device language. It outranks the device language on every request the SDK makes. The SDK keeps the language on the device, across launches, until you clear it. Encore never stores it on its servers.
clearLanguage(). It keeps the user and every other attribute.
The language is a preference of the device, like appearance, not user state. reset() and logout leave it in place, and only clearLanguage() removes it.
setLanguage() and clearLanguage() are new in iOS SDK 2.3.0, Android SDK 2.3.0, and the web SDK release that includes language support. React Native and Flutter add them in the release that pins native 2.3.0. On the web SDK, call setLanguage() after configure(); configure() takes no language. Reference: setLanguage() on iOS, Android, React Native, Flutter, and web.
Language tags
setLanguage() takes a language tag in the BCP-47 syntax (RFC 5646) and checks it before anything is sent. The rule is narrower than the RFC on purpose, and the same on every SDK:
- The tag starts with a language code of two or three letters, for example
en,pt, orfil. Longer language codes and old tags such asi-klingonare rejected. - Its parts are separated by hyphens only.
pt-BRis accepted,pt_BRis not. - Case does not matter, so
PT-bris the same aspt-BR. - The tag has no surrounding spaces or trailing newline.
' en'and'en\n'are rejected. - A language name is not a tag.
englishis rejected; useen. - Extended language forms are not accepted, because Encore resolves a tag to its language and region and would pick the wrong language.
zh-yue-HKis rejected; pass the preferred formyue-HK.
iw becomes he, in becomes id, ji becomes yi, and jw becomes jv. Every SDK and Encore’s own resolver do this, so setLanguage("iw") serves Hebrew everywhere. Encore’s resolver also serves nb (Norwegian Bokmål) as no.
Every SDK also normalizes the case of an accepted tag, following RFC 5646: the language is lowercase, a region is uppercase, a script is title case, and everything else is lowercase. PT-br becomes pt-BR, and en-US-X-Twain becomes en-US-x-twain.
The language you set stays on the device. It is never sent as a user attribute or in an identify() call, and Encore’s analytics record only the language that was served.
What happens to a tag that fails the check depends on the SDK:
The language user attribute is deprecated
Earlier releases set the language through the language field of setUserAttributes(). That field still works: from the releases in the preceding paragraph, setting it calls setLanguage() for you and logs a deprecation warning once. It is removed at the next major release, so move to setLanguage(). A language your app saved through the attribute on an earlier release carries over the first time the new SDK starts, so users keep their language when they update. A language set through the attribute is stored as the same override, so it also survives reset().
Because some apps pass a locale identifier, the deprecated attribute still accepts one and turns it into a tag before forwarding it: surrounding spaces are trimmed, anything from @ on is dropped, and _ becomes -, so de_DE@currency=EUR becomes de-DE and pt_BR becomes pt-BR. The first-launch carry-over does the same, so no user loses their language on upgrade. setLanguage() itself is strict and accepts none of these forms; see Language tags.
What Encore translates
Encore translates the copy it serves from its own side. Once a language is enabled for your app, these arrive translated:- The headline and subheadline you set for your app in the Encore dashboard
- Use-case copy: the reward headline and subheadline on Reward Users
- The copy inside each sheet variant, including its button labels
- Campaign copy: each offer’s
perkon/offers/feed,/offers/message,/offers/catalog,/offers/search, and/creators/links, and its display properties on the responses that carry them (/offers/feed,/offers/catalog, and/offers/search) - Category names on
/offers/search, ascategoryDisplayNameandcategoryGroupDisplayName(see Offer search) - The SDK’s own button and status words (see SDK strings)
title, subtitle, description, ctaText, tooltipText, and the redemption instructions) is no longer translated. Creative text that already has translations keeps serving them. New creative text is shown in its source copy, and the campaign copy around it is translated.
Encore machine-translates this copy and checks each result automatically. What happens next depends on where the copy is shown:
- Your app’s own copy (the dashboard headline and subheadline, and use-case copy) goes live once it passes the checks.
- Offer copy is shared by every app that runs the offer, so a person at Encore approves each machine translation before it goes live. This includes a perk Encore set for your app alone.
- Sheet-variant copy, SDK strings, and category names are also shared by every app, so a person at Encore approves each translation before it goes live.
What stays yours
Copy you pass per call is never translated. That means.headline() and .subheadline() on the placement builder in the iOS, Android, React Native, and Flutter SDKs, and the headline and subheadline placement options in the Web SDK. Encore shows the string exactly as you passed it, so localize it the way your app already localizes its own strings.
placement() on iOS.
The per-call value still outranks the Encore-resolved copy. If you pass an English headline to a Spanish user, the headline stays English while the rest of the sheet is Spanish. Pass a per-call headline only when you can supply it in the user’s language, and otherwise let the translated copy through. The full order is in Where the copy comes from.
Enabling languages
Encore enables languages for each app. To add one, contact admin@encorekit.com with the app and the languages you want. Encore can translate a language for your app before enabling it, so the copy can be read first, but no user is served a language until it is enabled. Until then, every user of that app gets the default language.- English is always enabled and cannot be turned off.
- A new language can be enabled only once the SDK’s own strings are approved in it, so a sheet never mixes translated copy with English buttons. Languages already enabled for your app keep serving.
Check what was served
/config, /offers/search, and /offers/catalog report the language they served in a locale field. It is always language-only, for example es. Compare it with the language you asked for to see whether a request fell back. /offers/feed, /offers/message, and /creators/links translate their copy the same way but return no locale.
In every row, a language is used only when your app has it enabled; otherwise the next source decides.
?language= on /config works the same way as setLanguage(): region codes are stripped and it outranks Accept-Language. The SDKs add it for you after setLanguage(). The legacy /ui-config endpoint accepts the same parameter.
To see what a device receives, request the config with a language hint and read locale:
"es" means Spanish is enabled and was served. Your default language, usually "en", means Spanish is not enabled for the app yet.
Right-to-left languages
When the served language is Arabic, Hebrew, Persian, or Urdu, the sheet lays itself out right to left. Text aligns to the start edge, arrows and chevrons point the other way, and swipes on carousels and the slide-to-claim control follow reading order. On iOS, Android, and web the direction follows the served language, not the host app, so English copy shown to a user with an Arabic device stays left to right. The Web SDK also setslang and dir on the dialog, so screen readers use the right voice.
Offline and first launch
The iOS and Android SDKs cache the last config they received, stamped with the language it was fetched for. A cached copy is only reused for the same language. After a language change the SDK fetches the copy again in the new language. If that copy cannot arrive in time, for example offline, and there is no cached copy for the language, the sheet renders the copy shipped inside the SDK. That built-in copy is English.Offer search
POST /offers/search is the call the Web SDK makes to fill its sheet. From the September 2026 backend release, each offer in its response carries two display names in the served language:
category and categoryGroup stay English on every response, so group and filter on them and show the display names. Both display names fall back to English when a translation is missing.
SDK strings
Encore also translates the SDK’s own words: button labels such as “Claim”, status lines on the confirmation screens, and accessibility labels. You do nothing to get them. From the September 2026 backend release, the/config response carries them under ui.values in two maps:
stringsmaps a string id to its translated text.pluralsmaps a string id to its plural forms from the Unicode Common Locale Data Repository (CLDR) (zero,one,two,few,many,other), whereotheris always present and${count}stands for the number. Every SDK writes the number with ASCII digits (0to9) and no thousands separator, in every language, so1200stays1200.
Related
- Where the copy comes from: the priority order for headline and subheadline
setLanguage(): iOS, Android, React Native, Flutter, WebclearLanguage(): iOS, Android, React Native, Flutter, Web- POST /offers/catalog: the
languagebody field