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 by adding the override method to the BaseActivity class to inject Crowdin translations into the Context. 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 the default value is 15 minutes. 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 uses TypedArray and stylable 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 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 with string specified in xml, make sure you follow this naming convention PlaceholderTextView otherwise SDK will skip this view during 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 workaround: you can store tabItem titles in your string-array and add tabs dynamically.
  3. PreferenceScreen defined via XML are not supported.

See also