Android App-Specific Language Change Programmatically Using Kotlin

Arul mani
4 min readOct 29, 2020

--

Hi there,

By default, Android will try to load resources based on the System language that is set on the user’s phone. Therefore, if a Tamil language user, Kavi, with her Android set to the Tamil language opened our android application on her phone, she’d see an app localized to her own language.

But what if another user wants to use the Tamil language for his android application on an Android that has its default language set to English?

To deal with this, we will have to programmatically update the locale of our android application to override the default locale set in the user’s system. Here we gonna do ask the user to choose the language and switch the language in application only.

Let’s start with creating new resource files for the Tamil language using Android Studio’s resource file wizard.

Firstly, right-click on the res folder and choose “New -> Android resource file”: It will show prompt like this type file name as strings.xml

Here select the language,

It will generate XML for you then you can put sample string like below.

English default look like

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="title_greatings">Hello</string>
//... other strings
</resources>

Newly added Langauge Tamil look like

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="title_greatings">வணக்கம்</string>
//... other strings
</resources>

That's it we configured strings locale.

Photo by bruce mars on Unsplash

Now let’s create a ContextUtils utility class to hold our locale updating methods. Place this within a utils package on the android application, as follows:

class ContextUtils(base: Context) : ContextWrapper(base) {

companion object {

fun updateLocale(c: Context, localeToSwitchTo: Locale): ContextWrapper {
var context = c
val resources: Resources = context.resources
val configuration: Configuration = resources.configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val localeList = LocaleList(localeToSwitchTo)
LocaleList.setDefault(localeList)
configuration.setLocales(localeList)
} else {
configuration.locale = localeToSwitchTo
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
context = context.createConfigurationContext(configuration)
} else {
resources.updateConfiguration(configuration, resources.displayMetrics)
}
return ContextUtils(context)
}
}
}

So here,

  1. Configuration.locale the field was deprecated in API level 24 (Nougat). This was put in place by Android API developers to make coders move to the use of getters and setters instead of directly accessing variables.
  2. This was marked as the preferred way of setting up locales (instead of using the direct accessor or setLocale(java.util.Locale)) starting from API level 24.
  3. Before API level 24, developers could directly access the configuration.locale field to change it as they pleased.

Then we can use this method to apply locale changes.

Create BaseActivity which is extends AppCompatActivity and it must be inherited by other activities.

We going to use attachBaseContext override method to update locale configuration to ACTIVITY so it will reflect on all other activities which are extended by.

Q: What is the use of AttachBaseContext?

A: The attachBaseContext function of the ContextWrapper class is making sure the context is attached only once. ContextThemeWrapper applies theme from application or Activity which is defined as android:theme in the AndroidManifest.xml file. Since both Application and Service do not need theme, they inherit it directly from ContextWrapper. During the activity creation, application and service are initiated, a new ContextImpl object is created each time and it implements functions in Context.

open class BaseActivity: AppCompatActivity() {

override fun attachBaseContext(newBase: Context) {
// get chosen language from shread preference
val localeToSwitchTo = PreferenceManager(newBase).getAppLanguage()
val localeUpdatedContext: ContextWrapper = ContextUtils.updateLocale(newBase, localeToSwitchTo) super.attachBaseContext(localeUpdatedContext)
}

}

Example:

LanguageChooseActivity → MainActvity

class LanguageActivity: AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_language)
tamil_bt.setOnClickListener {
PreferenceManager(this).updateLanguage("ta")
startActivity(Intent(this, MainActivity::class.java))
finish()
}

english_bt.setOnClickListener {
PreferenceManager(this).updateLanguage("en")
startActivity(Intent(this, MainActivity::class.java))
finish()
}
}
}

Here we store inside SharedPreference a string that contains language code like for tamil →“ta” , English →”en”, Hindi →”hi” etc…

This code used to identify the Language using Locale("language-code")

After applying in base activity wherever the strings are used automatically translated.

Below is the sample activity that extends BaseActivity().

class MainActivity : BaseActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}

}

Just extends BaseActivity is enough and see the magic. ;) Hope user won't get lost in application.

Photo by David Kovalenko on Unsplash

References:

https://ericyang505.github.io/android/Context.html

Bones knowledge

Tamil is the oldest language on earth At present, the number of speakers of Tamil language is around 7.7 crores.

https://www.britannica.com/topic/Tamil-language

--

--