How to implement in app localization with Jetpack Compose? I mean I don't want the user to change their device language, instead, let them change only the app language. How to achieve that? All of the resources I found talks about changing the device language.
The recommended way to handle app internationalization is using the "Per-app language" feature.
You can find more info here: https://www.youtube.com/watch?v=DUKnNWwcNvo
[Previous answer]
This is what I did based on this answer here.
In your application class, do the following:
class MyApp : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(LocaleHelper.setLocale(base, myLang))
}
companion object {
var myLang = "en"
}
}
I'm saving the language in myLang
variable, but in practice you will save in Shared Preferences.
In onAttachBaseContext
the setLocale
is called (it's declared below).
Then, in your activity you will do the same:
class MainActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(
LocaleHelper.setLocale(newBase, MyApp.myLang)
)
}
}
The object below will set the language in MyApp.myLang
and update the Context
object.
object LocaleHelper {
fun setLocale(context: Context, language: String): Context? {
MyApp.myLang = language
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return updateResources(context, language);
}
return updateResourcesLegacy(context, language);
}
@TargetApi(Build.VERSION_CODES.N)
private fun updateResources(context: Context, language: String): Context? {
val locale = Locale(language)
Locale.setDefault(locale)
val configuration = context.resources.configuration
configuration.setLocale(locale)
configuration.setLayoutDirection(locale)
return context.createConfigurationContext(configuration)
}
private fun updateResourcesLegacy(context: Context, language: String): Context {
val locale = Locale(language)
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
return context
}
}
Finally, in your composable, you can do the following:
@Composable
fun TestLanguage() {
val context = LocalContext.current
Text(text = stringResource(id = R.string.activity_java_text))
Button(onClick = {
LocaleHelper.setLocale(context, "pt")
(context as? Activity)?.recreate()
}) {
Text(text = stringResource(id = R.string.btn_ok))
}
}
Notice the recreate
method is called to recreate the current activity and apply the language change.
A new API was introduced in 2022 for adding per-app language preferences. Upgrading to androidx.appcompat:appcompat:1.6.0
latest version (>beta01) brings up two new methods: setApplicationLocales()
and getApplicationLocales()
More details about setup and guidelines can be found here: https://developer.android.com/guide/topics/resources/app-languages
It is backwards compatible with older versions of Android, not just Android 13. It allows quick in app language setting. Beaware that it seems to restart the activity, at least in the current version.
Simplified sample code of a language picker which triggers a dialogue:
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable { openDialog.value = SettingsDialogueStates.PICK_LANGUAGE }
) {
Row(
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Language,
contentDescription = "Language choice icon"
)
Text(
text = "Application language",
modifier = Modifier.padding(8.dp)
)
}
Text(
text = when (AppCompatDelegate.getApplicationLocales().toLanguageTags()) {
"he" -> "Hebrew"
"en-US", "en" -> "English"
else -> "System default"
}
)
}
And just the dialogue:
Dialog(
onDismissRequest = {
openDialog.value = SettingsDialogueStates.HIDDEN
}
) {
Card(
shape = RoundedCornerShape(10.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
) {
Text(
text = "Choose language",
style = MaterialTheme.typography.headlineLarge
)
OutlinedButton(
colors = ButtonDefaults.outlinedButtonColors(containerColor = Color.Transparent),
border = BorderStroke(0.dp, Color.Transparent),
modifier = Modifier.fillMaxWidth(),
onClick = {
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.create(
Locale("en")
)
)
openDialog.value = SettingsDialogueStates.HIDDEN
}
) {
Text(text = "English")
}
OutlinedButton(
colors = ButtonDefaults.outlinedButtonColors(containerColor = Color.Transparent),
border = BorderStroke(0.dp, Color.Transparent),
modifier = Modifier.fillMaxWidth(),
onClick = {
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.create(
Locale("he")
)
)
openDialog.value = SettingsDialogueStates.HIDDEN
}
) {
Text(text = "Hebrew")
}
OutlinedButton(
colors = ButtonDefaults.outlinedButtonColors(containerColor = Color.Transparent),
border = BorderStroke(0.dp, Color.Transparent),
modifier = Modifier.fillMaxWidth(),
onClick = {
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.getEmptyLocaleList()
)
openDialog.value = SettingsDialogueStates.HIDDEN
}
) {
Text(text = "System default")
}
}
}
}
The recommended way to handle app internationalization is using the "Per-app language" feature.
You can find more info here: https://www.youtube.com/watch?v=DUKnNWwcNvo
[Previous answer]
This is what I did based on this answer here.
In your application class, do the following:
class MyApp : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(LocaleHelper.setLocale(base, myLang))
}
companion object {
var myLang = "en"
}
}
I'm saving the language in myLang
variable, but in practice you will save in Shared Preferences.
In onAttachBaseContext
the setLocale
is called (it's declared below).
Then, in your activity you will do the same:
class MainActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(
LocaleHelper.setLocale(newBase, MyApp.myLang)
)
}
}
The object below will set the language in MyApp.myLang
and update the Context
object.
object LocaleHelper {
fun setLocale(context: Context, language: String): Context? {
MyApp.myLang = language
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return updateResources(context, language);
}
return updateResourcesLegacy(context, language);
}
@TargetApi(Build.VERSION_CODES.N)
private fun updateResources(context: Context, language: String): Context? {
val locale = Locale(language)
Locale.setDefault(locale)
val configuration = context.resources.configuration
configuration.setLocale(locale)
configuration.setLayoutDirection(locale)
return context.createConfigurationContext(configuration)
}
private fun updateResourcesLegacy(context: Context, language: String): Context {
val locale = Locale(language)
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
return context
}
}
Finally, in your composable, you can do the following:
@Composable
fun TestLanguage() {
val context = LocalContext.current
Text(text = stringResource(id = R.string.activity_java_text))
Button(onClick = {
LocaleHelper.setLocale(context, "pt")
(context as? Activity)?.recreate()
}) {
Text(text = stringResource(id = R.string.btn_ok))
}
}
Notice the recreate
method is called to recreate the current activity and apply the language change.
© 2022 - 2024 — McMap. All rights reserved.