androidlocalizationviewmodel

android `application.resources.getString` doesn't provide proper localized value


I need to set value of an UI element from string resource. It's a localized application. I thought it's going to be pretty straight forward:

textField.postValue(getApplication<Application>().resources.getString(R.string.text))

When I select the language, I set that into a SharedPreferences, and on attachBaseContext, I set that language as locale:

private fun Context.setAppLocale(language: String): Context {
        val locale = Locale(language)
        Locale.setDefault(locale)
        val config = resources.configuration
        config.setLocale(locale)
        config.setLayoutDirection(locale)
        return createConfigurationContext(config)
}

override fun attachBaseContext(newBase: Context) {
        // read from sharedpref
        ...
        super.attachBaseContext(ContextWrapper(newBase.setAppLocale(language)))
}

And after selecting the language I simply restart the application.

How I'm restarting the application:

                editor.apply()
                finish()
                startActivity(intent)

All the texts in my application are being updated, except for those I set from android-viewmodel using getString.

Can anyone help me out here, to get the proper localized values for my application within viewmodel?


Solution

  • All the texts in my application are being updated, except for those I set from android-viewmodel using getString.

    You can't use the Application context to get a localized String, this will always return the default String value; regardless from where this is called; ViewModel or somewhere else.

    To get a localized String, you have to call the resources object on the activity (base) context.

    The best practice is to avoid dealing with objects that have a lifecycle in ViewModels. ViewModels not intended to get resources using Context. That should be done on the View instead; obviously because you need that to update an existing UI/view; so, the UI (activity/fragment) that is associated to the ViewModel is the right place to do that.

    If there is a need to do that in the ViewModel, then you'd pass the baseContext from the View as function parameter, and not to be stored in the ViewModel.

    This question already discussed that, you'd have a look.

    Something like:

    val textField: LiveData<...>
    
    updateTextField(context: Context) {
        textField.postValue(context.resources.getString(R.string.text))
    }