93 lines
2.9 KiB
Kotlin
93 lines
2.9 KiB
Kotlin
package com.m57.hermescontrol.util
|
|
|
|
import android.content.Context
|
|
import android.content.ContextWrapper
|
|
import android.os.Build
|
|
import java.util.Locale
|
|
|
|
/**
|
|
* Static helpers for applying a user-selected in-app display language.
|
|
*
|
|
* Hermes Mobile's [com.m57.hermescontrol.MainActivity] extends the plain
|
|
* `ComponentActivity` (not `AppCompatActivity`), so we apply the locale through
|
|
* `attachBaseContext` + [ContextWrapper.wrap] rather than relying on
|
|
* AppCompatDelegate. Works on minSdk 26.
|
|
*/
|
|
object LocaleContextWrapper {
|
|
const val SYSTEM_LANGUAGE = "system"
|
|
|
|
/**
|
|
* Resolve a language code ("system", "en", "ko", …) into a [Locale].
|
|
*
|
|
* "system" returns the device's default locale (so passing it through
|
|
* [wrap] is effectively a no-op). Codes containing a region separator
|
|
* ("zh-rCN", "pt-BR") are split into language + country.
|
|
*/
|
|
fun localeForCode(code: String): Locale =
|
|
when {
|
|
code.isEmpty() || code == SYSTEM_LANGUAGE -> {
|
|
Locale.getDefault()
|
|
}
|
|
|
|
code.contains("-r", ignoreCase = true) -> {
|
|
val parts = code.split("-r", ignoreCase = true)
|
|
localeFor(parts[0], parts.getOrElse(1) { "" })
|
|
}
|
|
|
|
code.contains("-") -> {
|
|
val parts = code.split("-")
|
|
localeFor(parts[0], parts.getOrElse(1) { "" })
|
|
}
|
|
|
|
code.contains("_") -> {
|
|
val parts = code.split("_")
|
|
localeFor(parts[0], parts.getOrElse(1) { "" })
|
|
}
|
|
|
|
else -> {
|
|
Locale.forLanguageTag(code)
|
|
}
|
|
}
|
|
|
|
private fun localeFor(
|
|
language: String,
|
|
region: String,
|
|
): Locale =
|
|
if (region.isEmpty()) {
|
|
Locale.forLanguageTag(language)
|
|
} else {
|
|
Locale.forLanguageTag("$language-$region")
|
|
}
|
|
|
|
/**
|
|
* Wrap [base] so the configuration carries [locale].
|
|
*
|
|
* On API 33+ we use [Context.createConfigurationContext] which is the only
|
|
* reliable path once the deprecated `Configuration.locale` / `setLocale`
|
|
* APIs were removed from the public surface.
|
|
*/
|
|
fun wrap(
|
|
base: Context,
|
|
locale: Locale,
|
|
): Context {
|
|
val config = base.resources.configuration
|
|
val newConfig =
|
|
android.content.res.Configuration(config).apply {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
setLocales(android.os.LocaleList(locale))
|
|
} else {
|
|
@Suppress("DEPRECATION")
|
|
setLocale(locale)
|
|
}
|
|
setLayoutDirection(locale)
|
|
}
|
|
return base.createConfigurationContext(newConfig)
|
|
}
|
|
|
|
/** Convenience: wrap [base] using a stored language code. */
|
|
fun wrapWithCode(
|
|
base: Context,
|
|
code: String,
|
|
): Context = wrap(base, localeForCode(code))
|
|
}
|