New languages (#318)
* Added Hindi language support * Added Simplified Chinese support and implemented search in language selection dialog * Added support for Italian, Polish, Vietnamese, and Brazilian Portuguese languages * Added Belarusian language support and fixed activity recreation on locale change
This commit is contained in:
parent
056485a140
commit
70c272baa7
26 changed files with 7931 additions and 301 deletions
|
|
@ -47,6 +47,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
|
@ -71,6 +72,7 @@ import androidx.compose.material.icons.filled.Info
|
|||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.PhoneAndroid
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.VerifiedUser
|
||||
import androidx.compose.material.icons.outlined.AccountCircle
|
||||
|
|
@ -2183,31 +2185,78 @@ fun CreateAppThemeDialog(
|
|||
|
||||
@Composable
|
||||
fun LanguageSelectionDialog(onDismiss: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val currentLocales = AppCompatDelegate.getApplicationLocales()
|
||||
val currentTag = if (!currentLocales.isEmpty) currentLocales.get(0)?.toLanguageTag() else null
|
||||
var languageSearchQuery by remember { mutableStateOf("") }
|
||||
val languageRows = appLanguageSelectionOptions
|
||||
.map { language -> language to stringResource(language.labelRes) }
|
||||
.filter { (language, label) ->
|
||||
language.matchesLanguageSearch(label = label, query = languageSearchQuery)
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.options_language)) },
|
||||
text = {
|
||||
Column {
|
||||
appLanguageSelectionOptions.forEach { language ->
|
||||
Row(
|
||||
androidx.compose.material3.OutlinedTextField(
|
||||
value = languageSearchQuery,
|
||||
onValueChange = { languageSearchQuery = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
label = { Text(stringResource(R.string.action_search)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Search, contentDescription = null)
|
||||
},
|
||||
trailingIcon = {
|
||||
if (languageSearchQuery.isNotBlank()) {
|
||||
IconButton(onClick = { languageSearchQuery = "" }) {
|
||||
Icon(
|
||||
Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.action_clear)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
if (languageRows.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(R.string.search_no_results_simple),
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
} else {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
val locales = language.tag?.let { tag ->
|
||||
LocaleListCompat.forLanguageTags(tag)
|
||||
} ?: LocaleListCompat.getEmptyLocaleList()
|
||||
AppCompatDelegate.setApplicationLocales(locales)
|
||||
onDismiss()
|
||||
}
|
||||
.padding(vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
.heightIn(max = 360.dp)
|
||||
) {
|
||||
RadioButton(selected = currentTag == language.tag, onClick = null)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(stringResource(language.labelRes))
|
||||
items(
|
||||
items = languageRows,
|
||||
key = { (language, _) -> language.tag ?: "system" }
|
||||
) { (language, label) ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
val locales = language.tag?.let { tag ->
|
||||
LocaleListCompat.forLanguageTags(tag)
|
||||
} ?: LocaleListCompat.getEmptyLocaleList()
|
||||
AppCompatDelegate.setApplicationLocales(locales)
|
||||
onDismiss()
|
||||
context.findActivity()?.recreate()
|
||||
}
|
||||
.padding(vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(selected = currentTag == language.tag, onClick = null)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(label)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,99 @@
|
|||
package com.aryan.reader
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import java.text.Normalizer
|
||||
import java.util.Locale
|
||||
|
||||
data class AppLanguageOption(
|
||||
val tag: String?,
|
||||
@StringRes val labelRes: Int
|
||||
@StringRes val labelRes: Int,
|
||||
val searchAliases: List<String> = emptyList()
|
||||
)
|
||||
|
||||
val systemAppLanguageOption = AppLanguageOption(null, R.string.language_system_default)
|
||||
val systemAppLanguageOption = AppLanguageOption(
|
||||
tag = null,
|
||||
labelRes = R.string.language_system_default,
|
||||
searchAliases = listOf("system", "default", "device", "automatic")
|
||||
)
|
||||
|
||||
val supportedAppLanguageOptions = listOf(
|
||||
AppLanguageOption("en", R.string.language_english),
|
||||
AppLanguageOption("ar", R.string.language_arabic),
|
||||
AppLanguageOption("de", R.string.language_german),
|
||||
AppLanguageOption("tr", R.string.language_turkish),
|
||||
AppLanguageOption("fr", R.string.language_french),
|
||||
AppLanguageOption("ru", R.string.language_russian),
|
||||
AppLanguageOption("es", R.string.language_spanish)
|
||||
AppLanguageOption("en", R.string.language_english, listOf("english")),
|
||||
AppLanguageOption("ar", R.string.language_arabic, listOf("arabic", "arabi")),
|
||||
AppLanguageOption("de", R.string.language_german, listOf("german", "deutsch")),
|
||||
AppLanguageOption("tr", R.string.language_turkish, listOf("turkish", "turkce", "turkçe")),
|
||||
AppLanguageOption("fr", R.string.language_french, listOf("french", "francais", "français")),
|
||||
AppLanguageOption("ru", R.string.language_russian, listOf("russian", "russkiy", "русский")),
|
||||
AppLanguageOption("be", R.string.language_belarusian, listOf("belarusian", "belarus", "belaruskaya")),
|
||||
AppLanguageOption("es", R.string.language_spanish, listOf("spanish", "espanol", "español")),
|
||||
AppLanguageOption(
|
||||
"pt-BR",
|
||||
R.string.language_portuguese_brazilian,
|
||||
listOf(
|
||||
"portuguese",
|
||||
"brazilian portuguese",
|
||||
"portugues",
|
||||
"português",
|
||||
"portugues brasileiro",
|
||||
"português brasileiro",
|
||||
"brasil",
|
||||
"brazil",
|
||||
"pt-br"
|
||||
)
|
||||
),
|
||||
AppLanguageOption("it", R.string.language_italian, listOf("italian", "italiano", "italia", "italy")),
|
||||
AppLanguageOption("pl", R.string.language_polish, listOf("polish", "polski", "polska")),
|
||||
AppLanguageOption(
|
||||
"vi",
|
||||
R.string.language_vietnamese,
|
||||
listOf("vietnamese", "vietnam", "tieng viet", "tiếng việt")
|
||||
),
|
||||
AppLanguageOption("hi", R.string.language_hindi, listOf("hindi", "devanagari", "हिंदी", "हिन्दी")),
|
||||
AppLanguageOption(
|
||||
tag = "zh-CN",
|
||||
labelRes = R.string.language_chinese_simplified,
|
||||
searchAliases = listOf(
|
||||
"chinese",
|
||||
"simplified chinese",
|
||||
"mandarin",
|
||||
"zhongwen",
|
||||
"jian ti zhong wen",
|
||||
"zh-hans",
|
||||
"zh-cn",
|
||||
"中文",
|
||||
"简体中文",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val appLanguageSelectionOptions = listOf(systemAppLanguageOption) + supportedAppLanguageOptions
|
||||
|
||||
fun AppLanguageOption.matchesLanguageSearch(label: String, query: String): Boolean {
|
||||
val searchTokens = query.normalizedLanguageSearchTokens()
|
||||
if (searchTokens.isEmpty()) return true
|
||||
|
||||
val searchableText = buildString {
|
||||
append(label)
|
||||
append(' ')
|
||||
append(tag.orEmpty())
|
||||
append(' ')
|
||||
append(searchAliases.joinToString(" "))
|
||||
}.normalizedLanguageSearchText()
|
||||
|
||||
return searchTokens.all { token -> token in searchableText }
|
||||
}
|
||||
|
||||
private fun String.normalizedLanguageSearchTokens(): List<String> =
|
||||
normalizedLanguageSearchText()
|
||||
.split(' ')
|
||||
.filter { it.isNotBlank() }
|
||||
|
||||
private fun String.normalizedLanguageSearchText(): String =
|
||||
Normalizer.normalize(this, Normalizer.Form.NFD)
|
||||
.replace("\\p{Mn}+".toRegex(), "")
|
||||
.lowercase(Locale.ROOT)
|
||||
.replace("[^\\p{L}\\p{N}]+".toRegex(), " ")
|
||||
.trim()
|
||||
|
||||
val AddBooksSource.labelRes: Int
|
||||
@StringRes get() = when (this) {
|
||||
AddBooksSource.UNSHELVED -> R.string.add_books_source_unshelved
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue