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.
- 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
withUpdateIntervaloption. - 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:
- Kotlin
- Java
override fun getDelegate() = BaseContextWrappingDelegate(super.getDelegate())
@NonNull
@Override
public AppCompatDelegate getDelegate() {
return new BaseContextWrappingDelegate(super.getDelegate());
}
For AppCompat 1.1.0 and lower use this:
- Kotlin
- Java
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(Crowdin.wrapContext(newBase))
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(Crowdin.wrapContext(newBase));
}
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:
- Kotlin
- Java
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())
}
@Override
protected void onCreate() {
super.onCreate();
Crowdin.init(this,
new 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:
- Kotlin
- 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()))
)
}
/**
* 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
protected void attachBaseContext(Context newBase) {
languagePreferences = new LanguagePreferences(newBase);
super.attachBaseContext(new ContextWrapper(newBase) {
@Override
public Context getApplicationContext() {
return this;
}
@Override
public Resources getResources() {
Configuration configuration = getBaseContext().getResources().getConfiguration();
configuration.setLocale(new Locale(languagePreferences.getLanguageCode()));
Context updatedContext = getBaseContext().createConfigurationContext(configuration);
return updatedContext.getResources();
}
});
}
Make sure you've added this code to the App.kt/Application.java class.
Config options
| Config option | Description | Example |
|---|---|---|
withDistributionHash | Distribution Hash | withDistributionHash("7a0c1...7uo3b") |
withNetworkType | Network type to be used for translations download | Acceptable values are: - NetworkType.ALL (default)- NetworkType.CELLULAR- NetworkType.WIFI |
withUpdateInterval | Translations update interval in seconds. The minimum and the default value is 15 minutes. Visit the Cache page for more details | withUpdateInterval(900) |
Tips and tricks
1. To translate menu items you need to update your onCreateOptionsMenu method:
- Kotlin
- Java
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflateWithCrowdin(R.menu.activity_menu, menu, resources)
return true
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
ExtentionsKt.inflateWithCrowdin(getMenuInflater(), R.menu.your_menu, menu, getResources());
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:
- Kotlin
- Java
Crowdin.init(applicationContext,
crowdinConfig,
loadingStateListener = object : LoadingStateListener {
override fun onDataChanged() {
// Called when new translation data is updated
}
override fun onFailure(throwable: Throwable) {
// Handle failures
}
})
Crowdin.init(this,
crowdinConfig,
new LoadingStateListener() {
@Override
public void onDataChanged() {
// Called when new translation data is updated
}
@Override
public void 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
- Plurals are supported from Android SDK version 24.
TabItemtext added via xml won't be updated. There is workaround: you can store tabItem titles in your string-array and add tabs dynamically.PreferenceScreendefined via XML are not supported.