Skip to main content

Setup

To configure Android SDK integration, you need to:

  • Upload your localization files to Crowdin. If you have existing translations, you can upload them as well. You can use one of the following options:
  • Set up a distribution in Crowdin.
  • Set up SDK and enable Over-The-Air Content Delivery feature in your project.

Distribution is a CDN vault that mirrors your project's translated content and is required for Android app integration.

info
  • By default, translation download happens asynchronously after app launch. Downloaded translations are applied after the next app launch or activity re-render. Otherwise, previously cached translations are used (or bundled local translations if a cache does not exist).
  • The CDN feature does not update your localization files. If you want to add new translations to localization files, update them separately.
  • Once the SDK receives translations, they are stored on the device as application files for future sessions to minimize requests. Storage time can be configured using the withUpdateInterval option.
  • CDN caches released translations for up to 1 hour, so newly released translations in Crowdin may be returned with a delay.
  • To display a string, Crowdin will try to find it in the dynamic strings (from the CDN) and use the bundled version as a fallback. In other words, only the newly provided strings will be overridden and the bundled version will be used for the rest.

To integrate the SDK with your application, follow the step-by-step instructions:

Context wrapping​

Inject Crowdin translations into the Context by adding the override method to your BaseActivity class. If you have already migrated to AppCompat 1.2.0+ version, use this method:

override fun getDelegate() = BaseContextWrappingDelegate(super.getDelegate())

For AppCompat 1.1.0 and lower use this:

override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(Crowdin.wrapContext(newBase))
}
info

If you don't have the BaseActivity class, add the above code to all of your activities.

Configuring Crowdin SDK​

Enable Over-The-Air Content Delivery in your project so that the application can pull translations from the CDN vault. Add the following code to the App/Application class:

override fun onCreate() {
super.onCreate()
Crowdin.init(applicationContext,
CrowdinConfig.Builder()
.withDistributionHash(your_distribution_hash)
.withOrganizationName(organization_name) // required for Crowdin Enterprise
.withNetworkType(network_type) // optional
.withUpdateInterval(interval_in_seconds) // optional
.build())
}

Change locale programmatically​

Crowdin works with the current locale. If you want to change the locale programmatically, use the language plus country format: Locale("en", "US").

Example of language change in App.kt/Application.java:

/**
* Should be overridden in case you want to change locale programmatically.
* For a custom language, set your application locale with language and country/region constraints.
* This should match with `Locale code:` for your custom language in Crowdin.
*
* language - [a-zA-Z]{2,8}
* country/region - [a-zA-Z]{2} | [0-9]{3}
*
* Example: "aa-BB"
*/
override fun attachBaseContext(newBase: Context) {
languagePreferences = LanguagePreferences(newBase)
super.attachBaseContext(
ContextWrapper(newBase.updateLocale(languagePreferences.getLanguageCode()))
)
}
caution

Make sure you've added this code to the App.kt/Application.java class.

Config options​

Config optionDescriptionExample
withDistributionHashDistribution HashwithDistributionHash("7a0c1...7uo3b")
withNetworkTypeNetwork type to be used for translations downloadAcceptable values are:
- NetworkType.ALL (default)
- NetworkType.CELLULAR
- NetworkType.WIFI
withUpdateIntervalTranslations update interval in seconds. The minimum and default value is 15 minutes (900 seconds). Visit the Cache page for more detailswithUpdateInterval(900)

Tips and tricks​

1. To translate menu items you need to update your onCreateOptionsMenu method:

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflateWithCrowdin(R.menu.activity_menu, menu, resources)
return true
}

2. In case you have custom views that use TypedArray and styleable attributes, you will need to use the following approach:

val textId = typedArray.getResourceId(R.styleable.sample_item, 0)
textView.setText(textId)

instead of typedArray.getString(R.styleable.sample_item).

3. Activity title defined via AndroidManifest won't be translated:

<activity
android:name=".activities.SampleActivity"
android:label="@string/title"/>

You can simply update your toolbar inside of activity or fragment:

toolbar.setTitle(R.string.title);

4. In case your project already overrides attachBaseContext:

super.attachBaseContext(Crowdin.wrapContext(SomeLib.wrap(newBase)));

5. You can receive callbacks about data changes in two ways:

a) During initialization using LoadingStateListener:

Crowdin.init(applicationContext,
crowdinConfig,
loadingStateListener = object : LoadingStateListener {
override fun onDataChanged() {
// Called when new translation data is updated
}

override fun onFailure(throwable: Throwable) {
// Handle failures
}
})

b) By registering/unregistering an observer:

override fun onCreate(savedInstanceState: Bundle?) {
Crowdin.registerDataLoadingObserver(this)
}

It has a callback method onDataChanged() that can be used to invalidate your UI (TextView, Menu, etc.). It will use downloaded resources automatically.

override fun onDataChanged() {
invalidateOptionsMenu()
Crowdin.updateMenuItemsText(R.menu.activity_main_drawer, navigationView.menu, resources)
toolbarMain.title = getString(R.string.category)
}

Otherwise, new resources are applied when the activity is restarted.

6. In case you have a custom TextView subclass with a string specified in XML, make sure its class name contains TextView (e.g., PlaceholderTextView). Otherwise, the SDK will skip this view during the inflating process and it won't be translated.

Limitations​

  1. Plurals are supported from Android SDK version 24.
  2. TabItem text added via XML won't be updated. There is a workaround: you can store TabItem titles in a string array and add tabs dynamically.
  3. PreferenceScreen defined via XML is not supported.

See also​