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 Distribution in Crowdin.
- Set up SDK and enable Over-The-Air Content Delivery feature in your project.
Distribution is a CDN vault that mirrors the translated content of your project and is required for integration with Android app.
- By default, the translation downloading happens asynchronously after launching the app. The downloaded translations will be used after the next launch of the app or Activity re-render. Otherwise, the previously cached translations will be used (or local translations if a cache does not exist).
- The CDN feature does not update the localization files. if you want to add new translations to the localization files you need to do it yourself.
- Once SDK receives the translations, it's stored on the device as application files for further sessions to minimize requests the next time the app starts. Storage time can be configured using
withUpdateInterval
option. - CDN caches all the translation in release for up to 1 hour and even when new translations are released in Crowdin, CDN may return it 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 register/unregister observer for data changes by adding this lines:
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.
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.PreferenceScreen
defined via XML are not supported.