refactor: language model
This commit is contained in:
parent
96c3917169
commit
3f0cd812a5
12 changed files with 112 additions and 89 deletions
|
|
@ -7,28 +7,28 @@
|
||||||
package ua.acclorite.book_story.core.data
|
package ua.acclorite.book_story.core.data
|
||||||
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import ua.acclorite.book_story.core.model.Language
|
import ua.acclorite.book_story.core.language.Language
|
||||||
|
|
||||||
object CoreData {
|
object CoreData {
|
||||||
val defaultLanguage = Language.fromCode(code = "en")
|
val defaultLanguage = Language.fromLanguageTag(languageTag = "en") // English
|
||||||
val languages = listOf(
|
val languages = listOf(
|
||||||
Language.fromCode(code = "en"),
|
Language.fromLanguageTag(languageTag = "en"), // English
|
||||||
Language.fromCode(code = "uk"),
|
Language.fromLanguageTag(languageTag = "uk"), // Ukrainian
|
||||||
Language.fromCode(code = "de"),
|
Language.fromLanguageTag(languageTag = "de"), // German
|
||||||
Language.fromCode(code = "ar"),
|
Language.fromLanguageTag(languageTag = "ar"), // Arabic
|
||||||
Language.fromCode(code = "es"),
|
Language.fromLanguageTag(languageTag = "es"), // Spanish
|
||||||
Language.fromCode(code = "tr"),
|
Language.fromLanguageTag(languageTag = "tr"), // Turkish
|
||||||
Language.fromCode(code = "fr"),
|
Language.fromLanguageTag(languageTag = "fr"), // French
|
||||||
Language.fromCode(code = "pl"),
|
Language.fromLanguageTag(languageTag = "pl"), // Polish
|
||||||
Language.fromCode(code = "it"),
|
Language.fromLanguageTag(languageTag = "it"), // Italian
|
||||||
Language.fromCode(code = "zh"),
|
Language.fromLanguageTag(languageTag = "zh-CN"), // Chinese (China)
|
||||||
Language.fromCode(code = "hi"),
|
Language.fromLanguageTag(languageTag = "hi"), // Hindi
|
||||||
Language.fromCode(code = "pt", displayName = "pt-BR"),
|
Language.fromLanguageTag(languageTag = "pt-BR"), // Portuguese (Brazil)
|
||||||
Language.fromCode(code = "ta"),
|
Language.fromLanguageTag(languageTag = "ta"), // Tamil
|
||||||
Language.fromCode(code = "ml"),
|
Language.fromLanguageTag(languageTag = "ml"), // Malayalam
|
||||||
Language.fromCode(code = "cs"),
|
Language.fromLanguageTag(languageTag = "cs"), // Czech
|
||||||
Language.fromCode(code = "ro"),
|
Language.fromLanguageTag(languageTag = "ro"), // Romanian
|
||||||
Language.fromCode(code = "be"),
|
Language.fromLanguageTag(languageTag = "be"), // Belarusian
|
||||||
Language.fromCode(code = "ja"),
|
Language.fromLanguageTag(languageTag = "ja"), // Japanese
|
||||||
).sortedBy { it.displayName }.toPersistentList()
|
).sortedBy { it.displayName }.toPersistentList()
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Book's Story — free and open-source Material You eBook reader.
|
||||||
|
* Copyright (C) 2024-2025 Acclorite
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.acclorite.book_story.core.language
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class Language(
|
||||||
|
val locale: Locale,
|
||||||
|
val displayName: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun getDisplayName(locale: Locale): String {
|
||||||
|
return locale
|
||||||
|
.getDisplayName(locale)
|
||||||
|
.replaceFirstChar { it.uppercaseChar() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromLanguageTag(languageTag: String): Language {
|
||||||
|
val locale = Locale.forLanguageTag(languageTag)
|
||||||
|
return Language(
|
||||||
|
locale = locale,
|
||||||
|
displayName = getDisplayName(locale)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Book's Story — free and open-source Material You eBook reader.
|
||||||
|
* Copyright (C) 2024-2025 Acclorite
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.acclorite.book_story.core.language
|
||||||
|
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
object LanguageUtils {
|
||||||
|
fun findLanguage(
|
||||||
|
languages: List<Language>,
|
||||||
|
locale: Locale,
|
||||||
|
defaultLanguage: Language
|
||||||
|
): Language {
|
||||||
|
// Searching for exact locale match
|
||||||
|
languages.find { language ->
|
||||||
|
language.locale == locale
|
||||||
|
}?.let { return it }
|
||||||
|
|
||||||
|
// Searching for localLe language match
|
||||||
|
languages.find { language ->
|
||||||
|
language.locale.language == locale.language
|
||||||
|
}?.let { return it }
|
||||||
|
|
||||||
|
return defaultLanguage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Book's Story — free and open-source Material You eBook reader.
|
|
||||||
* Copyright (C) 2024-2025 Acclorite
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ua.acclorite.book_story.core.model
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
data class Language(
|
|
||||||
val languageCode: LanguageCode,
|
|
||||||
val displayName: String
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
fun fromCode(code: String, displayName: String = code): Language {
|
|
||||||
return Language(
|
|
||||||
languageCode = LanguageCode(code = code),
|
|
||||||
displayName = LanguageCode.getDisplayName(code = displayName)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Book's Story — free and open-source Material You eBook reader.
|
|
||||||
* Copyright (C) 2024-2025 Acclorite
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ua.acclorite.book_story.core.model
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@JvmInline
|
|
||||||
value class LanguageCode(val code: String) {
|
|
||||||
companion object {
|
|
||||||
fun getDisplayName(code: String): String {
|
|
||||||
val target = Locale.forLanguageTag(code)
|
|
||||||
val displayName = target.getDisplayName(target)
|
|
||||||
return displayName.replaceFirstChar { it.uppercaseChar() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -20,8 +20,9 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ua.acclorite.book_story.core.data.CoreData
|
import ua.acclorite.book_story.core.data.CoreData
|
||||||
|
import ua.acclorite.book_story.core.language.Language
|
||||||
|
import ua.acclorite.book_story.core.language.LanguageUtils
|
||||||
import ua.acclorite.book_story.core.log.logI
|
import ua.acclorite.book_story.core.log.logI
|
||||||
import ua.acclorite.book_story.core.model.Language
|
|
||||||
import ua.acclorite.book_story.data.local.data_store.DataStore
|
import ua.acclorite.book_story.data.local.data_store.DataStore
|
||||||
import ua.acclorite.book_story.data.settings.model.Setting
|
import ua.acclorite.book_story.data.settings.model.Setting
|
||||||
import ua.acclorite.book_story.presentation.browse.model.BrowseLayout
|
import ua.acclorite.book_story.presentation.browse.model.BrowseLayout
|
||||||
|
|
@ -63,17 +64,21 @@ class SettingsManager @Inject constructor(
|
||||||
|
|
||||||
/* ------ Settings --------------------------- */
|
/* ------ Settings --------------------------- */
|
||||||
/* ------ General ---------------------------- */
|
/* ------ General ---------------------------- */
|
||||||
val language =
|
val language = setting<Language, String>(
|
||||||
setting<Language, String>(
|
|
||||||
key = stringPreferencesKey("language"),
|
key = stringPreferencesKey("language"),
|
||||||
default = Locale.getDefault().language.take(2).let { defaultLocale ->
|
default = LanguageUtils.findLanguage(
|
||||||
CoreData.languages.find { locale -> locale.languageCode.code == defaultLocale }
|
languages = CoreData.languages,
|
||||||
?: CoreData.defaultLanguage
|
locale = Locale.getDefault(),
|
||||||
},
|
defaultLanguage = CoreData.defaultLanguage
|
||||||
serialize = { it.languageCode.code },
|
),
|
||||||
deserialize = { code ->
|
serialize = { it.locale.toLanguageTag() },
|
||||||
CoreData.languages.find { locale -> locale.languageCode.code == code }
|
deserialize = { languageTag ->
|
||||||
?: CoreData.defaultLanguage
|
val locale = Locale.forLanguageTag(languageTag)
|
||||||
|
LanguageUtils.findLanguage(
|
||||||
|
languages = CoreData.languages,
|
||||||
|
locale = locale,
|
||||||
|
defaultLanguage = CoreData.defaultLanguage
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
val theme = setting<Theme, String>(
|
val theme = setting<Theme, String>(
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.update
|
||||||
@Stable
|
@Stable
|
||||||
class Setting<T, P>(
|
class Setting<T, P>(
|
||||||
val key: Preferences.Key<P>,
|
val key: Preferences.Key<P>,
|
||||||
default: T,
|
private val default: T,
|
||||||
private val setSetting: (P) -> Unit,
|
private val setSetting: (P) -> Unit,
|
||||||
private val serialize: (T) -> P,
|
private val serialize: (T) -> P,
|
||||||
private val deserialize: (P) -> T
|
private val deserialize: (P) -> T
|
||||||
|
|
@ -33,7 +33,10 @@ class Setting<T, P>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init(value: P?) {
|
fun init(value: P?) {
|
||||||
if (value == null) return
|
if (value == null) {
|
||||||
|
setSetting(serialize(default))
|
||||||
|
return
|
||||||
|
}
|
||||||
_value.update { deserialize(value) }
|
_value.update { deserialize(value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ package ua.acclorite.book_story.domain.use_case.settings
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
import ua.acclorite.book_story.core.model.Language
|
import ua.acclorite.book_story.core.language.Language
|
||||||
import ua.acclorite.book_story.data.settings.SettingsManager
|
import ua.acclorite.book_story.data.settings.SettingsManager
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ class UpdateLanguageUseCase @Inject constructor(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
operator fun invoke(language: Language) {
|
operator fun invoke(language: Language) {
|
||||||
val appLocale = LocaleListCompat.forLanguageTags(language.languageCode.code)
|
val appLocale = LocaleListCompat.forLanguageTags(language.locale.toLanguageTag())
|
||||||
AppCompatDelegate.setApplicationLocales(appLocale)
|
AppCompatDelegate.setApplicationLocales(appLocale)
|
||||||
settings.language.update(language)
|
settings.language.update(language)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import android.net.Uri
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import ua.acclorite.book_story.core.ID
|
import ua.acclorite.book_story.core.ID
|
||||||
import ua.acclorite.book_story.core.model.Language
|
import ua.acclorite.book_story.core.language.Language
|
||||||
import ua.acclorite.book_story.domain.model.library.Category
|
import ua.acclorite.book_story.domain.model.library.Category
|
||||||
import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder
|
import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import ua.acclorite.book_story.core.model.Language
|
import ua.acclorite.book_story.core.language.Language
|
||||||
import ua.acclorite.book_story.presentation.navigator.StackEvent
|
import ua.acclorite.book_story.presentation.navigator.StackEvent
|
||||||
import ua.acclorite.book_story.presentation.start.StartScreen
|
import ua.acclorite.book_story.presentation.start.StartScreen
|
||||||
import ua.acclorite.book_story.ui.common.model.ListItem
|
import ua.acclorite.book_story.ui.common.model.ListItem
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
package ua.acclorite.book_story.ui.start
|
package ua.acclorite.book_story.ui.start
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import ua.acclorite.book_story.core.model.Language
|
import ua.acclorite.book_story.core.language.Language
|
||||||
import ua.acclorite.book_story.presentation.navigator.StackEvent
|
import ua.acclorite.book_story.presentation.navigator.StackEvent
|
||||||
import ua.acclorite.book_story.presentation.start.StartScreen
|
import ua.acclorite.book_story.presentation.start.StartScreen
|
||||||
import ua.acclorite.book_story.ui.common.model.ListItem
|
import ua.acclorite.book_story.ui.common.model.ListItem
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.core.model.Language
|
import ua.acclorite.book_story.core.language.Language
|
||||||
import ua.acclorite.book_story.ui.common.components.dialog.SelectableDialogItem
|
import ua.acclorite.book_story.ui.common.components.dialog.SelectableDialogItem
|
||||||
import ua.acclorite.book_story.ui.common.model.ListItem
|
import ua.acclorite.book_story.ui.common.model.ListItem
|
||||||
import ua.acclorite.book_story.ui.settings.components.SettingsSubcategoryTitle
|
import ua.acclorite.book_story.ui.settings.components.SettingsSubcategoryTitle
|
||||||
|
|
@ -35,7 +35,7 @@ fun LazyListScope.StartSettingsLayoutGeneral(
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
items(languages, key = { it.item }) { item ->
|
items(languages) { item ->
|
||||||
SelectableDialogItem(
|
SelectableDialogItem(
|
||||||
selected = item.selected,
|
selected = item.selected,
|
||||||
title = item.title,
|
title = item.title,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue