Nami Paywalls in Different Languages

Localize paywalls in your app using the Nami platform.

Nami supports localizing your paywalls so they will work with different languages that your customers use.

To get started with localization, there are a few key steps.

  1. Add Control Center localizations for your:
    1. Product SKUs
    2. Legal clickwrap language
    3. Paywall buttons
    4. Text to speech accessibility hints
  2. Set a language for your paywall
  3. Configure the SDK for the correct language

Everything else works exactly the same! Let's dig into each one of these tasks in more detail.

📘

Paywall Localization Required SDK versions

Support for multiple languages on Nami paywalls requires the following minimum SDK versions.

  • Android v2.0.0
  • Apple v2.8.0
  • Flutter v1.0.0
  • React Native v2.0.0
  • Xamarin v1.3.0

Control Center Setup

App Default Language

Each App has a Default Language. You set this at creation time in the App creation modal. You may change this later in the App Settings screen or the settings screen on the Apps tab.

📘

Note that the default language for any of the app or paywall elements described below cannot be deleted.

If you wish to delete a translation for any of these elements, you must first switch the default language for your app.

App Language Configuration Options

There are 3 main parts of your App to configure for language support.

  1. The legal clickwrap language for your paywalls.
  2. The text for buttons on your paywalls, including Restore Purchase, Close, and Sign In.
  3. Hints for text to speech accessibility support.

You will find the button text and text to speech accessibility hints in the. App Settings > Languages tab. Learn how to set up accessibility hints here: Configuring your Paywalls for Accessibility.

The legal clickwrap text can be found in the App Settings > Legal tab. See this guide to setting up your legal clickwrap: Setting up Legal Text for your Apps.

On both of these screens, you may add a new language by clicking the + Add Language button and selecting the new language you wish to add from the dropdown.

Product SKU Languages

Product SKUs have 2 text fields you may need to localize for them to display correctly on your paywall. These are the Display Text and Sub Display Text that is displayed beneath the purchase button.

Note that each paywall must have an associated language for the SKU in order for the paywall to display correctly in your app.

👍

SKU Smart Text Variables Do Not Need to be Translated

SKU Smart Text variables are automatically translated in the SDK.

For example, ${sku.price} / ${sku.duration} will work correctly in all languages.

Set a Language on your Paywall

Each Paywall must have a language set at creation time in the + Add Paywall modal.

🚧

The paywall language cannot be changed once it has been set. You will need to delete and recreate the paywall if you wish to change the language.

Configure the SDK for Different Languages

Nami supports the following standard language codes which are a subset of the ISO 639 standard.

📘

Language codes with a hyphen switch to an underscore in the SDKs.

For example es-mx would be es_mx.

Note that these may differ from the language codes used on any particular store platform such as the Apple App Store or the Google Play Store. See the table below for how specific Store Platforms handle language codes.

The logic you implement to determine what language each user will see on their device is completely up to you. We generally recommend using the device language, but you could also optionally use the store code, currency or any other part of the locale to determine the language you wish to display.

Set the Paywall Language by the Device Language

Below is an example of setting the language on the device where we only have paywalls in English and Spanish.

📘

Note that if you pass in a language code to the NamiConfiguration object that does not match any paywalls created in your app, the SDK will receive and display the paywalls for the default_language set for your app.

👍

Nami Best Practice

To avoid any unexpected behavior, we recommend explicitly setting the language for all use cases including a default case. This ensures that you know exactly what language paywall is being shown to your users in all cases.

let deviceLanguage: NamiLanguageCode
if (Locale.current.languageCode == "es") {
  deviceLanguage = NamiLanguageCodes.es
} else {
  // default paywall language to be shown to all other users
  deviceLanguage = NamiLanguageCodes.en
}

let namiConfig : NamiConfiguration = NamiConfiguration( appPlatformID: "YOUR_APP_PLATFORM_ID" )
namiConfig.logLevel = .warn
namiConfig.namiLanguageCode = deviceLanguage
Nami.configure(namiConfig: namiConfig)
let deviceLocale =
      Platform.OS === 'ios'
        ? NativeModules.SettingsManager.settings.AppleLocale ||
          NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13
        : NativeModules.I18nManager.localeIdentifier;
    
let deviceLanguage = deviceLocale.split("_")[0];

var paywallLanguage = "";
if (deviceLanguage === "es") {
  paywallLanguage = "es";
} else {
  // default paywall language to be shown to all other users
  paywallLanguage = "en";
}

let configDict = {
  'appPlatformID-apple': 'YOUR_APPLE_PLATFORM_ID',
  'appPlatformID-google': 'YOUR_GOOGLE_PLATFORM_ID',
  logLevel: 'WARN',
  namiLanguageCode: paywallLanguage
};

NativeModules.NamiBridge.configure(configDict);

In addition to the language selection in the configuration, be sure to also select the correct paywall when you call the raisePaywall method.

👍

Nami Best Practice

Nami paywalls are fixed to one language. We recommend appending the language code to the Developer Paywall ID so you can easily select the correct one.

Example Developer Paywall IDs:
primary_paywall_en
primary_paywall_es

// deviceLanguage defined in previous code snippet
let paywall_name = "my_paywall_" + deviceLanguage

// assumes paywalls are named:
//   my_paywall_en
//   my_paywall_es
NamiPaywallManager.preparePaywallForDisplay(developerPaywallID: paywall_name, backgroundImageRequired: true, imageFetchTimeout: 2.0) { (success, error) in
  if success {
    NamiPaywallManager.raisePaywall(developerPaywallID: paywall_name, fromVC: self)
  } else if let error = error {
    print("Could not raise paywall, error was \(error.localizedDescription).")
  }
}
const onPreparePaywallFinished = (paywallId, result) => {
  if (result.success == true) {
    console.log('prepare paywall success')
    NativeModules.NamiPaywallManagerBridge.raisePaywallByPaywallDeveloperId(paywallId);
  } else {
    console.log("error is " + result.errorMessage );
  }
  preparePaywallListenSubscriber?.remove();
}

const raisePaywallAction = (paywallId) => {
  if (
    eventEmitter?._subscriber?._subscriptionsForType?.PreparePaywallFinished == null
  ) {
    preparePaywallListenSubscriber = eventEmitter.addListener('PreparePaywallFinished', onPreparePaywallFinished);
  }
  NativeModules.NamiPaywallManagerBridge.preparePaywallForDisplayByDeveloperPaywallId(paywallId, true, 2);
};

...

<Button title="Subscribe" onPress={() => subscribeAction(paywallId)} />

🚧

Note it is important here not to attach the actual language from the locale as that would require that you have created paywalls for every possible language.

Be sure to only use the languages that you have created paywalls for in the Nami Control Center.

📘

SKU Prices

Note that the prices on in-app purchase products on the Apple App Store and Google Play Store are determined by the settings on the device and cannot be affected by the Nami platform.

Data Received in the SDK

The SDK will only receive the Paywalls that have the same language as the one requested in the NamiConfiguration method.

If you try to call raisePaywall with a Developer Paywall ID that does not match the requested language, you will get an error that no paywall was found for that Developer Paywall ID.