Search code examples
androidkotlinandroid-jetpack-composeandroid-jetpack

Recompose an fragment/activity in jetpack after changing the language


I have a code for changing the language of an APP. When I'm using this code, the language does change but not fully. Some parts of the page I'm in is still having the old language. For e.g., if I'm using English, Portugese and Spanish and when I change from English to Spanish, some parts are of the page are not changing. I have only two activities and rest of the them are fragments. And below is the code I'm using for the app to switch languages.

But when I go out of the page and come back again, the whole page is translated.

fun updateLocale(context: Context, language: String) {
            val locale = when (language) {
                "Portuguese" -> Locale("pt")
                "Spanish" -> Locale("es")
                else -> Locale("en")
            }
            Locale.setDefault(locale)
            val config = Configuration()
            config.setLocale(locale)

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context.createConfigurationContext(config)
            } else {
                @Suppress("DEPRECATION")
                context.resources.updateConfiguration(config, context.resources.displayMetrics)
            }
        }

How do make this function workable? Is there any additional code that has to be added for this to work?

This is how I'm using the code in my activity Utils.updateLocale(this, App.instance.language)

I tried detaching and attaching the fragment. Recreated activities and still did not work.


Solution

  • When you change the language at runtime, some parts of your UI may continue to show old strings because they were composed (or “inflated”) with the previous configuration. In Jetpack Compose (or even in traditional fragments), the UI won’t automatically update if only the locale is changed in the resources. Instead, you have to “force” a recomposition (or even a full recreation) of your composables so that they pick up the new language from resources.

    1. If you're using compose for your UI, you can save the state that tracks the current language and use a key to force a recomposition when it changes, as so:

      @Composable
      fun MainScreen(currentLanguage: String) {
         key(currentLanguage) {
            Column(modifier = Modifier.fillMaxSize()) {
               Text(text = stringResource(id = R.string.hello))
            }
         }
      }
      
    2. Another Compose-friendly approach is to provide the current configuration (or locale) via a CompositionLocal.

      // saving the state in your fragment/activity 
      private var currentLocale by mutableStateOf(Locale.getDefault())
      // setting the composition local provider
      setContent {
          CompositionLocalProvider(LocalAppLocale provides currentLocale) {
              MainScreen()
          }
      }
      
      val LocalAppLocale = compositionLocalOf { Locale.getDefault() }
      
      @Composable
      fun MainScreen() {
          // if the configuration changes, the UI will recompose.
          val locale = LocalAppLocale.current
          Column {
             Text(text = stringResource(id = R.string.hello))
          }
      }
      
    3. Another approach — more common with traditional Android Views is to recreate the activity when the language changes.