Use localizations and locale codes in Unity SDK
Why this is important
Locale codes come into play when Adapty picks the localization for a flow or an onboarding, and when you read a remote config for a custom paywall.
Locale codes are complicated and can vary from platform to platform, so Adapty relies on one internal standard across every platform it supports. Understanding that standard helps you predict which localization a user receives.
Locale code standard at Adapty
For locale codes, Adapty uses a slightly modified BCP 47 standard: every code consists of lowercase subtags, separated by hyphens. Some examples: en (English), pt-br (Portuguese (Brazil)), zh (Simplified Chinese), zh-hant (Traditional Chinese).
Locale code matching
In SDK v4, flows and onboardings match locale codes differently: flows are localized by the SDK on the device, onboardings by the Adapty server.
Flows and Paywall Builder paywalls
A paywall built in the Paywall Builder is delivered as a flow in SDK v4, so the rule below covers both.
The match is exact. The SDK compares the code you pass with the localization codes of the flow character by character: it doesn’t change the case, doesn’t replace underscores (_) with hyphens (-), and doesn’t fall back to the language subtag. For a flow with a pt-br localization, only pt-br matches: pt-BR, pt_BR, and pt-PT all miss.
When the code matches no localization, the flow silently renders in its default locale — the SDK doesn’t return an error and doesn’t log a warning.
When the code matches, Adapty merges the localization with the default one: the strings and assets that the matched localization doesn’t define come from the default localization.
Omitting the locale code is not the same as asking for the flow’s default localization: the SDK substitutes a fixed en. A flow whose default locale is de still renders in en when it has an en localization, and falls back to de only when it doesn’t.
Pass the locale code exactly as it’s configured in the dashboard — lowercase subtags separated by hyphens. Don’t pass a system locale identifier as is: CultureInfo.CurrentCulture.Name returns pt-BR, and it falls back to the default localization. Convert the value in your app before you pass it.
Onboardings
Onboardings are localized on the server, and the server rules tolerate other formats. When you pass a Locale to GetOnboarding:
- The locale string is converted to lowercase and all the underscores (
_) are replaced with hyphens (-) - Adapty looks for the localization with the fully matching locale code
- If no match is found, Adapty takes the substring before the first hyphen (
ptforpt-br) and looks for the matching localization - If no match is found again, Adapty returns the content for the onboarding’s default locale
This way pt_BR, pt-BR, and pt-br all resolve to the same onboarding localization.
Implementing localizations
In SDK v4, you don’t pass a locale code when you fetch a flow — a flow is localized when its view is created.
- Flow Builder and Paywall Builder paywalls: the SDK doesn’t read the device locale, so resolve it in your app and pass it when you create the view. The locale code is optional — omit it and the flow renders in
en, or in its default locale when the flow has noenlocalization. - Custom (remote config) paywalls:
GetFlowreturns every configured localization inflow.RemoteConfigs. Each entry is anAdaptyRemoteConfigwith aLocalecode and aDictionaryof values. Select the entry that matches the user, with your own fallback:
using System.Linq;
using AdaptySDK;
Adapty.GetFlow("YOUR_PLACEMENT_ID", (flow, error) => {
if (error != null) {
// handle the error
return;
}
var config = flow.RemoteConfigs.FirstOrDefault(c => c.Locale == "en")
?? flow.RemoteConfigs.FirstOrDefault();
// read your values from config?.Dictionary
});Adapty stores those Locale codes in the format described in Locale code standard at Adapty. The SDK doesn’t match remote configs against a locale, so which entry to apply is up to your app.
Choose the localization of a flow
To render a flow or paywall with a specific localization, pass the locale code to SetLocale when you create the view:
var parameters = new AdaptyUICreateFlowViewParameters()
.SetLocale("pt-br");
AdaptyUI.CreateFlowView(flow, parameters, (view, error) => {
if (error != null) {
// handle the error
return;
}
// view.Locale — the localization the view was built with
});The view reports the localization it was actually built with in view.Locale: the one you requested when that localization exists, and the flow’s default localization otherwise.
Why this is important
There are a few scenarios when locale codes come into play — for example, when you’re trying to fetch the correct paywall for the current localization of your app.
As locale codes are complicated and can vary from platform to platform, we rely on an internal standard for all the platforms we support. However, because these codes are complicated, it is really important for you to understand what exactly are you sending to our server to get the correct localization, and what happens next — so you will always receive what you expect.
Locale code standard at Adapty
For locale codes, Adapty uses a slightly modified BCP 47 standard: every code consists of lowercase subtags, separated by hyphens. Some examples: en (English), pt-br (Portuguese (Brazil)), zh (Simplified Chinese), zh-hant (Traditional Chinese).
Locale code matching
When Adapty receives a call from the client-side SDK with the locale code and starts looking for a corresponding localization of a paywall, the following happens:
- The incoming locale string is converted to lowercase and all the underscores (
_) are replaced with hyphens (-) - We then look for the localization with the fully matching locale code
- If no match was found, we take the substring before the first hyphen (
ptforpt-br) and look for the matching localization - If no match was found again, we return the content for the paywall’s default locale
This way an iOS device that sent 'pt_BR', an Android device that sent pt-BR, and another device that sent pt-br will get the same result.
Implementing localizations: recommended way
If you’re wondering about localizations, chances are you’re already dealing with the localized string files in your project. If that’s the case, we recommend placing some key-value with the intended Adapty locale code in each of your files for the corresponding localizations. And then extract the value for this key when calling our SDK, like so:
// 1. Modify your localization files (e.g., using Unity's Localization package)
/*
en.json
*/
{
"adapty_paywalls_locale": "en"
}
/*
es.json
*/
{
"adapty_paywalls_locale": "es"
}
/*
pt-BR.json
*/
{
"adapty_paywalls_locale": "pt-br"
}
// 2. Extract and use the locale code
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using AdaptySDK;
public class PaywallManager : MonoBehaviour
{
public async void FetchPaywall()
{
// Get the current locale from Unity's Localization system
var locale = LocalizationSettings.SelectedLocale;
var localeCode = GetAdaptyLocaleCode(locale);
// Pass locale code to Adapty.GetPaywall or Adapty.GetPaywallForDefaultAudience method
Adapty.GetPaywall("placement_id", localeCode, (paywall, error) => {
if (error != null) {
// handle the error
return;
}
// Use the paywall
});
}
private string GetAdaptyLocaleCode(Locale locale)
{
// Convert Unity locale to Adapty format
var localeIdentifier = locale.Identifier.Code;
return localeIdentifier.ToLower().Replace('_', '-');
}
}That way you can ensure you’re in full control of what localization will be retrieved for every user of your app.
Implementing localizations: the other way
You can get similar (but not identical) results without explicitly defining locale codes for every localization. That would mean extracting a locale code from some other objects that your platform provides, like this:
using UnityEngine;
using System.Globalization;
using AdaptySDK;
public class PaywallManager : MonoBehaviour
{
public void FetchPaywall()
{
var localeCode = GetSystemLocaleCode();
// Pass locale code to Adapty.GetPaywall or Adapty.GetPaywallForDefaultAudience method
Adapty.GetPaywall("placement_id", localeCode, (paywall, error) => {
if (error != null) {
// handle the error
return;
}
// Use the paywall
});
}
private string GetSystemLocaleCode()
{
// Get the system's current culture
var culture = CultureInfo.CurrentCulture;
var languageCode = culture.TwoLetterISOLanguageName;
var regionCode = culture.Name.Contains('-') ? culture.Name.Split('-')[1] : null;
if (!string.IsNullOrEmpty(regionCode))
{
return $"{languageCode}-{regionCode.ToLower()}";
}
return languageCode;
}
}Note that we don’t recommend this approach due to few reasons:
- On iOS preferred languages and current locale are not identical. If you want the localization to be picked correctly you’ll have to either rely on Apple’s logic, which works out of the box if you’re using the recommended approach with localized string files, or re-create it.
- It’s hard to predict what exactly will Adapty’s server get. For example, on iOS, it is possible to obtain a locale like
ar_OM@numbers='latn'on a device and send it to our server. And for this call you will get not thear-omlocalization you were looking for, but ratherar, which is likely unexpected.
Should you decide to use this approach anyway — make sure you’ve covered all the relevant use cases.