feat: in-app display language switcher (Settings → Appearance) (#604)

Add a Language selector to Settings → Appearance that overrides the app
display language independent of the device locale. Persists a BCP-47 code
in ServerStoreState.appLanguage; MainActivity.attachBaseContext applies it
via a ContextWrapper (works on minSdk 26, no AppCompat dependency needed
since the Activity is a plain ComponentActivity). Selecting a language
recreates the activity so resources re-resolve immediately.

Includes System default / English / 한국어 options, plus a unit test for
the locale-code resolver.

Note: the new string keys (settings_item_language, language_system,
language_english, language_korean) should be backfilled into values-ko
once PR #602 lands.

Co-authored-by: hy4ri <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andhy4ri authored and GitHub committed 2026-07-15 23:57:39 +03:00
1 parent 4314bddaae
commit b3765bd106
9 files changed
+200

No files matched your search

@@ -1,5 +1,6 @@
package com.m57.hermescontrol
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
@@ -16,11 +17,24 @@ import androidx.compose.ui.Modifier
import com.m57.hermescontrol.data.local.AuthManager
import com.m57.hermescontrol.notification.NotificationReplyReceiver
import com.m57.hermescontrol.theme.HermesControlTheme
import com.m57.hermescontrol.util.LocaleContextWrapper
class MainActivity : ComponentActivity() {
// Observable state so both onCreate and onNewIntent updates flow to Compose
private var notificationSessionId by mutableStateOf<String?>(null)
/**
* Apply the user-selected display language before any view is inflated.
* Reads the persisted code from [AuthManager]; an uninitialized store
* (shouldn't happen here, but guarded) falls back to the device locale.
*/
override fun attachBaseContext(base: Context) {
val code =
runCatching { AuthManager.getAppLanguage() }
.getOrDefault(LocaleContextWrapper.SYSTEM_LANGUAGE)
super.attachBaseContext(LocaleContextWrapper.wrapWithCode(base, code))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)