Skip to content

Instantly share code, notes, and snippets.

@ElianFabian
Last active September 20, 2024 17:03
Show Gist options
  • Select an option

  • Save ElianFabian/4e5edb10d7b36935a2f2695227ad8e60 to your computer and use it in GitHub Desktop.

Select an option

Save ElianFabian/4e5edb10d7b36935a2f2695227ad8e60 to your computer and use it in GitHub Desktop.
ContextWrapper to propperly update the locale of your application. The default implementation takes the first supported locale, otherwise takes the default locale.. Gist to generate the supported and default locale: Gist to generate the supported locales: https://gist.github.com/ElianFabian/e94071f348998c24bedaa959ddb8df40
import android.annotation.SuppressLint
import android.app.Application
import android.content.ComponentCallbacks
import android.content.Context
import android.content.ContextWrapper
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.LocaleList
import androidx.core.os.ConfigurationCompat
import androidx.core.os.LocaleListCompat
import java.util.Locale
fun Application.createSyncLocaleContextWrapper(baseContext: Context): ContextWrapper {
val localeContextWrapper = LocaleContextWrapper(baseContext)
registerComponentCallbacks(object : ComponentCallbacks {
override fun onConfigurationChanged(newConfig: Configuration) {
localeContextWrapper.setLocaleList(newConfig.getLocaleList())
}
override fun onLowMemory() = Unit
})
return localeContextWrapper
}
class LocaleContextWrapper(
application: Application,
baseContext: Context,
private val createUpdatedLocaleContext: (oldContext: Context, newConfig: Configuration) -> Context,
) : ContextWrapper(
createUpdatedLocaleContext(baseContext, baseContext.resources.configuration)
) {
init {
application.registerComponentCallbacks(object : ComponentCallbacks {
override fun onConfigurationChanged(newConfig: Configuration) {
_baseContext = createUpdatedLocaleContext(_baseContext, newConfig)
}
override fun onLowMemory() = Unit
})
}
private var _baseContext = super.getBaseContext()
override fun getResources(): Resources = _baseContext.resources
override fun getTheme(): Resources.Theme = _baseContext.theme
override fun getBaseContext(): Context = _baseContext
override fun setTheme(resid: Int) {
_baseContext.setTheme(resid)
}
}
@Suppress("DEPRECATION")
@SuppressLint("ObsoleteSdkInt")
private fun createContextWithFirstSupportedUserLocaleOrForceDefault(
context: Context,
configuration: Configuration,
defaultLanguage: String,
supportedLanguages: List<String>,
): Context {
val userSupportedLocales = ConfigurationCompat
.getLocales(configuration)
.getMatches(supportedLanguages)
val newLocales = userSupportedLocales.ifEmpty {
listOf(Locale(defaultLanguage))
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val newLocaleList = LocaleList(*newLocales.toTypedArray())
LocaleList.setDefault(newLocaleList)
configuration.setLocales(newLocaleList)
}
else {
val newLocale = newLocales.first()
Locale.setDefault(newLocale)
configuration.locale = newLocale
}
return context.createConfigurationContext(configuration)
}
private fun LocaleListCompat.getMatches(supportedLocales: List<String>): List<Locale> {
val currentSupportedLocales = supportedLocales.toMutableList()
return buildList {
while (currentSupportedLocales.isNotEmpty()) {
val match = getFirstMatch(currentSupportedLocales.toTypedArray()) ?: continue
add(match)
currentSupportedLocales.removeAll { supportedLocale ->
match.toString().startsWith(supportedLocale)
}
}
}
}
import android.annotation.SuppressLint
import android.os.Build
import android.os.LocaleList
import androidx.core.os.LocaleListCompat
import java.util.Locale
fun LocaleListCompat.getMatches(supportedLocales: List<String>): List<Locale> {
val currentSupportedLocales = supportedLocales.toMutableList()
return buildList {
while (currentSupportedLocales.isNotEmpty()) {
val match = getFirstMatch(currentSupportedLocales.toTypedArray()) ?: continue
add(match)
currentSupportedLocales.removeAll { supportedLocale ->
match.toString().startsWith(supportedLocale)
}
}
}
}
fun LocaleList.toLocales(): List<Locale> {
return buildList {
for (i in 0 until size()) {
add(get(i))
}
}
}
@Suppress("DEPRECATION")
@SuppressLint("ObsoleteSdkInt")
fun Configuration.getLocaleList(): List<Locale> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
locales.toLocales()
}
else listOf(locale)
}
import android.app.Application
import android.content.Context
class YourApplication : Application() {
override fun attachBaseContext(base: Context?) {
if (base == null) {
return super.attachBaseContext(null)
}
val localeContext = createSyncLocaleContextWrapper(base)
super.attachBaseContext(localeContext)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment