From 30e70f8b1c3149018f849b100d8c9dbd87f0fbf3 Mon Sep 17 00:00:00 2001 From: AmirHossein Abdolmotallebi Date: Mon, 27 Jan 2025 08:56:14 +0330 Subject: [PATCH] set system language as default language (#386) --- .../pages/settings/SettingsComponent.kt | 28 ++++++++---- .../desktop/storage/AppSettingsStorage.kt | 17 +++++-- .../localizationmanager/LanguageManager.kt | 44 +++++++++++++++---- .../localizationmanager/LanguageStorage.kt | 5 ++- 4 files changed, 70 insertions(+), 24 deletions(-) diff --git a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/settings/SettingsComponent.kt b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/settings/SettingsComponent.kt index af7f801..6be2cbe 100644 --- a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/settings/SettingsComponent.kt +++ b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/pages/settings/SettingsComponent.kt @@ -318,26 +318,36 @@ fun themeConfig( fun languageConfig( languageManager: LanguageManager, scope: CoroutineScope, -): EnumConfigurable { - val currentLanguageName = languageManager.selectedLanguage - val allLanguages = languageManager.languageList +): EnumConfigurable { + val currentLanguageName = languageManager.selectedLanguageInStorage + val allLanguages = languageManager.languageList.value return EnumConfigurable( title = Res.string.settings_language.asStringSource(), description = "".asStringSource(), backedBy = createMutableStateFlowFromStateFlow( - flow = currentLanguageName.mapStateFlow { l -> - allLanguages.value.find { - it.toLocaleString() == l - } ?: LanguageManager.DefaultLanguageInfo + flow = currentLanguageName.mapStateFlow { language -> + language?.let { + allLanguages.find { + it.toLocaleString() == language + } + } }, updater = { languageInfo -> languageManager.selectLanguage(languageInfo) }, scope = scope, ), - possibleValues = allLanguages.value, + possibleValues = listOf(null).plus(allLanguages), describe = { - it.nativeName.asStringSource() + val isAuto = it == null + val language = it ?: languageManager.systemLanguageOrDefault + val languageName = language.nativeName + if (isAuto) { + // always use english here! + "System ($languageName)".asStringSource() + } else { + languageName.asStringSource() + } }, ) } diff --git a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/storage/AppSettingsStorage.kt b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/storage/AppSettingsStorage.kt index a919227..ef1875b 100644 --- a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/storage/AppSettingsStorage.kt +++ b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/storage/AppSettingsStorage.kt @@ -14,7 +14,7 @@ import java.io.File @Serializable data class AppSettingsModel( val theme: String = "dark", - val language: String = "en", + val language: String? = null, val uiScale: Float? = null, val mergeTopBarWithTitleBar: Boolean = false, val threadCount: Int = 8, @@ -95,7 +95,7 @@ data class AppSettingsModel( override fun set(source: MapConfig, focus: AppSettingsModel): MapConfig { return source.apply { put(Keys.theme, focus.theme) - put(Keys.language, focus.language) + putNullable(Keys.language, focus.language) putNullable(Keys.uiScale, focus.uiScale) put(Keys.mergeTopBarWithTitleBar, focus.mergeTopBarWithTitleBar) put(Keys.threadCount, focus.threadCount) @@ -127,6 +127,15 @@ private val uiScaleLens: Lens s.copy(uiScale = f) } ) +private val languageLens: Lens + get() = Lens( + get = { + it.language + }, + set = { s, f -> + s.copy(language = f) + } + ) class AppSettingsStorage( settings: DataStore, @@ -134,7 +143,7 @@ class AppSettingsStorage( ConfigBaseSettingsByMapConfig(settings, AppSettingsModel.ConfigLens), LanguageStorage { var theme = from(AppSettingsModel.theme) - override val selectedLanguage = from(AppSettingsModel.language) + override val selectedLanguage = from(languageLens) var uiScale = from(uiScaleLens) var mergeTopBarWithTitleBar = from(AppSettingsModel.mergeTopBarWithTitleBar) val threadCount = from(AppSettingsModel.threadCount) @@ -152,4 +161,4 @@ class AppSettingsStorage( val browserIntegrationPort = from(AppSettingsModel.browserIntegrationPort) val trackDeletedFilesOnDisk = from(AppSettingsModel.trackDeletedFilesOnDisk) val useBitsForSpeed = from(AppSettingsModel.useBitsForSpeed) -} \ No newline at end of file +} diff --git a/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageManager.kt b/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageManager.kt index bbdeb66..ca23f71 100644 --- a/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageManager.kt +++ b/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageManager.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable import ir.amirab.util.compose.contants.FILE_PROTOCOL import ir.amirab.util.compose.contants.RESOURCE_PROTOCOL import ir.amirab.util.flow.mapStateFlow +import ir.amirab.util.flow.mapTwoWayStateFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import okio.FileSystem @@ -11,14 +12,24 @@ import okio.Path.Companion.toPath import okio.buffer import java.io.InputStream import java.net.URI -import java.util.Properties +import java.util.* class LanguageManager( private val storage: LanguageStorage, ) { private val _languageList: MutableStateFlow> = MutableStateFlow(emptyList()) val languageList = _languageList.asStateFlow() - val selectedLanguage = storage.selectedLanguage + val systemLanguageOrDefault: LanguageInfo by lazy { + getSystemLanguageIfWeCanUse() + } + val selectedLanguageInStorage = storage.selectedLanguage + val selectedLanguage = storage.selectedLanguage.mapStateFlow { + it ?: systemLanguageOrDefault.toLocaleString() + } + + // val selectedLanguageInfo = selectedLanguage.mapStateFlow { +// bestLanguageInfo(it) +// } val isRtl = selectedLanguage.mapStateFlow { selectedLanguage -> rtlLanguages.any { selectedLanguage.startsWith(it) } } @@ -28,11 +39,11 @@ class LanguageManager( instance = this } - fun selectLanguage(languageInfo: LanguageInfo) { + fun selectLanguage(languageInfo: LanguageInfo?) { // ensure that language info is in the list! // val languageInfo = languageList.value.find { it == languageInfo } // selectedLanguage.value = (languageInfo ?: DefaultLanguageInfo).toLocaleString() - selectedLanguage.value = languageInfo.toLocaleString() + selectedLanguageInStorage.value = languageInfo?.toLocaleString() } fun getMessage(key: String): String { @@ -42,7 +53,7 @@ class LanguageManager( } private fun getRequestedLanguage(): String { - return selectedLanguage.value + return selectedLanguage.value ?: systemLanguageOrDefault.toLocaleString() } @Volatile @@ -74,6 +85,10 @@ class LanguageManager( } } + /** + * Find the best language info for the given locale. + * the returned language is guaranteed to be available. (at least [DefaultLanguageInfo]) + */ private fun bestLanguageInfo(locale: String): LanguageInfo { return languageList.value.find { it.toLocaleString() == locale @@ -122,6 +137,11 @@ class LanguageManager( } } + private fun getSystemLanguageIfWeCanUse(): LanguageInfo { + val systemLocale = getSystemLocale().toString() + return bestLanguageInfo(systemLocale) + } + companion object { lateinit var instance: LanguageManager private const val LOCALES_PATH = "/com/abdownloadmanager/resources/locales" @@ -130,10 +150,8 @@ class LanguageManager( languageCode = "en", countryCode = "US", ) - LanguageInfo( - locale = locale, - nativeName = "English", - path = URI("$RESOURCE_PROTOCOL:$LOCALES_PATH/${locale}.properties") + locale.toLanguageInfo( + path = "$RESOURCE_PROTOCOL:$LOCALES_PATH/${locale}.properties", ) } @@ -204,3 +222,11 @@ data class LanguageInfo( return locale.toString() } } + +private fun getSystemLocale(): MyLocale { + val javaSystemLocale = Locale.getDefault(Locale.Category.DISPLAY) + return MyLocale( + languageCode = javaSystemLocale.language, + countryCode = javaSystemLocale.country, + ) +} diff --git a/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageStorage.kt b/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageStorage.kt index c1dd15d..509fa9e 100644 --- a/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageStorage.kt +++ b/shared/compose-utils/src/main/kotlin/ir/amirab/util/compose/localizationmanager/LanguageStorage.kt @@ -3,5 +3,6 @@ package ir.amirab.util.compose.localizationmanager import kotlinx.coroutines.flow.MutableStateFlow interface LanguageStorage { - val selectedLanguage: MutableStateFlow -} \ No newline at end of file + // null means auto + val selectedLanguage: MutableStateFlow +}