9

I want to change language programmatically in Jetpack Compose. I've read quite some posts and watch videos but still can't find the way to do it. (The post and video are in Android view system.)

How to change language in kotlin (locale)
https://www.youtube.com/watch?v=xxPzi2h0Vvc

I want my app works like below image. After clicking the language, the whole app will change the language. Below code is the clickable's part. What should I do in this clickable part and MainActivity.kt?

@Composable
fun LanguageScreen(
    navController: NavController,
) {
    val context = LocalContext.current
    val langList = arrayOf("English", "繁體中文", "简体中文", "日本語")
    var items by remember {
        mutableStateOf(
            langList.map {
                LanguageItem(
                    title = it,
                    isSelected = false
                )
            }
        )
    }
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
    ) {
        items(items.size) { i ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable {

                        items = items.mapIndexed { j, item ->
                            if (i == j) {
                                item.copy(isSelected = true)
                            } else item.copy(isSelected = false)
                        }

                        if (i == 0) {
                            setLocaleLang("", context)
                        } else if (i == 1) {
                            setLocaleLang("zh-rTW", context)
                        } else if (i == 2) {
                            setLocaleLang("zh-rCN", context)
                        } else {
                            setLocaleLang("ja", context)
                        }

                    }
                    .padding(16.dp),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(text = items[i].title, fontSize = 20.sp)
                if (items[i].isSelected) {
                    Icon(
                        imageVector = Icons.Default.Check,
                        contentDescription = "Selected",
                        tint = Color.Blue,
                        modifier = Modifier.size(24.dp)
                    )
                }
            }
            Spacer(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(1.dp)
                    .background(Color.LightGray)
            )
        }
    }
}


fun setLocaleLang(lang: String, context: Context) {
    val locale = Locale(lang)
    Locale.setDefault(locale)
    val resources = context.resources
    val configuration = resources.configuration
    configuration.setLocale(locale)
    resources.updateConfiguration(configuration, resources.displayMetrics)

    val editor = context.getSharedPreferences("Settings", Context.MODE_PRIVATE).edit()
    editor.putString("My_Lang", lang)
    editor.apply()
}

fun loadLocale(context: Context) {
    val sharedPreferences = context.getSharedPreferences("Settings", Activity.MODE_PRIVATE)
    val language = sharedPreferences.getString("My_Lang", "")
    setLocaleLang(language!!, context)
}

MainActivity.kt

class MainActivity : ComponentActivity() {
    @ExperimentalFoundationApi
    override fun onCreate(savedInstanceState: Bundle?) {

        loadLocale(this)

        super.onCreate(savedInstanceState)
        setContent {
            SpanishTravelTheme {

Image: https://i.stack.imgur.com/y5kcO.png

Justin
  • 91
  • 2
  • 6
  • Please remove the unnecessary part from the clickable lambda. It confuses people. – Richard Onslow Roper Nov 07 '21 at 16:18
  • PS: I don't think there is any difference for Compose. You do it the exact same way you did in the previous system. – Richard Onslow Roper Nov 07 '21 at 16:50
  • Per-app language feature was just added to the latest Android API 33, that is still on Developer preview. See my answer at https://stackoverflow.com/a/71151685/5038317 – Heitor Paceli Feb 17 '22 at 02:25
  • Does this answer your question? [Change app language programmatically in Android](https://stackoverflow.com/questions/2900023/change-app-language-programmatically-in-android) – Heitor Paceli Feb 17 '22 at 02:26

2 Answers2

1

Try this

 val context = LocalContext.current
 Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable {
                           val locale = Locale(language) //Here I assume you have access to the language you want
        Locale.setDefault(locale)
        val resources = context.getResources()
        val configuration = resources.getConfiguration()
        configuration.locale = locale
        resources.updateConfiguration(configuration, resources.getDisplayMetrics())
                    }
                    .padding(16.dp),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            )
Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
  • I think you were missing the `LocalContext.current`. – Richard Onslow Roper Nov 07 '21 at 16:55
  • I add your suggestion and LocalContext.current. However the language is still not changed. I think I need to recreate the activity or add something to MainActivity.kt. But I don't know how to do this part specifically. – Justin Nov 08 '21 at 15:02
  • I see another post use `recreate()` under the code and in onCreate. I can't find the way to use it in Jetpack Compose. What do you suggest? – Justin Nov 08 '21 at 15:05
  • Check if the language changes when you pause the activity, then resume. To do this, just press the home button on the activity, then open it back from the recents menu – Richard Onslow Roper Nov 08 '21 at 17:31
  • I update my code as above. It's still not working. How should I find the problem? Thank you – Justin Nov 09 '21 at 16:22
1

You can try this

  1. Create a helper object

LocaleUtils.kt

object LocaleUtils {

// [AppPrefs] is sharedpreferences or datastore
fun setLocale(c: Context, pref: AppPrefs) = updateResources(c, pref.language ?: "en") //use locale codes

private fun updateResources(context: Context, language: String) {
    context.resources.apply {
        val locale = Locale(language)
        val config = Configuration(configuration)

        context.createConfigurationContext(configuration)
        Locale.setDefault(locale)
        config.setLocale(locale)
        context.resources.updateConfiguration(config, displayMetrics)
    }
  }
}
  1. Call setLocale inside setContent to change the language at runtime.
    setContent {
        LocaleUtils.setLocale(LocalContext.current, viewModel.pref)
  1. To change the App language
    fun changeAppLanguage(languageISO: String) {
        sharedPrefs.edit().putString(LANGUAGE_KEY, languageISO).apply()
    }

Please remember to use language ISO 639-1 Code

here's a list of Locale codes https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/localization

UPDATE:

now you can just use AppCompatDelegate from androidx.appcompat

Example:

AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags("en"))
emAd H
  • 29
  • 1
  • 5
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 13 '22 at 06:35
  • 2
    `updateConfiguration` is deprecated – Melom Nov 09 '22 at 13:59