Skip to main content
Encore serves translated copy to each user in their language. The SDKs send the device or browser language for you, Encore resolves it against the languages enabled for your app, and the sheet and its offers come back in that language. Copy you pass per call is the one exception: Encore shows it exactly as you wrote it. Read this page when your app ships in more than one language, when a user reports seeing English in a translated app, or when you need to know what a 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:
  • pt is translated as Brazilian Portuguese, so users in Portugal see Brazilian spelling.
  • zh is 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

Call setLanguage() 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.
To go back to the device language, for example when the user picks “System default” in your picker, call 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, or fil. Longer language codes and old tags such as i-klingon are rejected.
  • Its parts are separated by hyphens only. pt-BR is accepted, pt_BR is not.
  • Case does not matter, so PT-br is the same as pt-BR.
  • The tag has no surrounding spaces or trailing newline. ' en' and 'en\n' are rejected.
  • A language name is not a tag. english is rejected; use en.
  • Extended language forms are not accepted, because Encore resolves a tag to its language and region and would pick the wrong language. zh-yue-HK is rejected; pass the preferred form yue-HK.
Every SDK checks the same rule, so a tag accepted on one platform is accepted on all of them. After the check, a withdrawn ISO 639 code is replaced by its current one: 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 perk on /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, as categoryDisplayName and categoryGroupDisplayName (see Offer search)
  • The SDK’s own button and status words (see SDK strings)
Offer creative text (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.
When the English of an offer’s copy changes, its old translations stop serving, and the new English shows until the new translations are approved. The Encore team can also correct any string by hand. Once approved, a hand correction always wins over the machine translation. A field without a translation yet falls back to its source copy on its own. One untranslated field never blanks the sheet or pulls the rest of it back into English.

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.
Reference: 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 sets lang 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. 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:
  • strings maps a string id to its translated text.
  • plurals maps a string id to its plural forms from the Unicode Common Locale Data Repository (CLDR) (zero, one, two, few, many, other), where other is always present and ${count} stands for the number. Every SDK writes the number with ASCII digits (0 to 9) and no thousands separator, in every language, so 1200 stays 1200.
Each map holds only the ids that have an approved translation in the served language, and neither is sent for English. The SDK uses its built-in English for any id that is missing, one id at a time, so a partly translated language never blanks a button. iOS SDK 2.3.0, Android SDK 2.3.0, and the web SDK release that includes language support read these maps. React Native and Flutter read them from the release that pins native 2.3.0. Older SDKs ignore them and show English.