Support translation off the app(for the most part)

This commit is contained in:
Syer10
2021-06-19 17:24:39 -04:00
parent 2a505f6d96
commit 2bb8e12543
39 changed files with 646 additions and 174 deletions

View File

@@ -42,6 +42,11 @@ dependencies {
// Json // Json
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1")
// Xml
val xmlutilVersion = "0.82.0"
implementation("io.github.pdvrieze.xmlutil:core-jvm:$xmlutilVersion")
implementation("io.github.pdvrieze.xmlutil:serialization-jvm:$xmlutilVersion")
// Dependency Injection // Dependency Injection
val toothpickVersion = "3.1.0" val toothpickVersion = "3.1.0"
implementation("com.github.stephanenicolas.toothpick:ktp:$toothpickVersion") implementation("com.github.stephanenicolas.toothpick:ktp:$toothpickVersion")

View File

@@ -24,6 +24,8 @@ import ca.gosyer.data.server.interactions.ExtensionInteractionHandler
import ca.gosyer.data.server.interactions.LibraryInteractionHandler import ca.gosyer.data.server.interactions.LibraryInteractionHandler
import ca.gosyer.data.server.interactions.MangaInteractionHandler import ca.gosyer.data.server.interactions.MangaInteractionHandler
import ca.gosyer.data.server.interactions.SourceInteractionHandler import ca.gosyer.data.server.interactions.SourceInteractionHandler
import ca.gosyer.data.translation.ResourceProvider
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.data.ui.UiPreferences import ca.gosyer.data.ui.UiPreferences
import toothpick.ktp.binding.bind import toothpick.ktp.binding.bind
import toothpick.ktp.binding.module import toothpick.ktp.binding.module
@@ -60,6 +62,10 @@ val DataModule = module {
.toProvider(HttpProvider::class) .toProvider(HttpProvider::class)
.providesSingleton() .providesSingleton()
bind<XmlResourceBundle>()
.toProvider(ResourceProvider::class)
.providesSingleton()
bind<BackupInteractionHandler>() bind<BackupInteractionHandler>()
.toClass<BackupInteractionHandler>() .toClass<BackupInteractionHandler>()
bind<CategoryInteractionHandler>() bind<CategoryInteractionHandler>()

View File

@@ -10,8 +10,8 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
enum class Direction(val res: String) { enum class Direction(val res: String) {
Down("Down"), Down("dir_down"),
Left("RTL"), Left("dir_rtl"),
Right("LTR"), Right("dir_ltr"),
Up("Up") Up("dir_up")
} }

View File

@@ -10,10 +10,10 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
enum class ImageScale(val res: String) { enum class ImageScale(val res: String) {
FitScreen("Fit Screen"), FitScreen("scale_fit_screen"),
Stretch("Strech"), Stretch("scale_stretch"),
FitWidth("Fit Width"), FitWidth("scale_fit_width"),
FitHeight("Fit Height"), FitHeight("scale_fit_height"),
OriginalSize("Original Size"), OriginalSize("scale_original"),
SmartFit("Smart Fit"), SmartFit("scale_smart"),
} }

View File

@@ -10,8 +10,8 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
enum class NavigationMode(val res: String) { enum class NavigationMode(val res: String) {
LNavigation("L shaped"), LNavigation("nav_l_shaped"),
KindlishNavigation("Kindle-ish"), KindlishNavigation("nav_kindle_ish"),
EdgeNavigation("Edge"), EdgeNavigation("nav_edge"),
RightAndLeftNavigation("Right and Left"), RightAndLeftNavigation("nav_left_right"),
} }

View File

@@ -0,0 +1,30 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data.translation
import ca.gosyer.data.ui.UiPreferences
import java.util.Locale
import javax.inject.Inject
import javax.inject.Provider
class ResourceProvider @Inject constructor(
val uiPreferences: UiPreferences
) : Provider<XmlResourceBundle> {
override fun get(): XmlResourceBundle {
val languagePref = uiPreferences.language()
return if (languagePref.isSet()) {
languagePref.get().let {
val locale: Locale? = Locale.forLanguageTag(it)
if (locale != null) {
XmlResourceBundle.forLocale(locale)
} else {
XmlResourceBundle.forTag(it)
}
}
} else XmlResourceBundle.forLocale(Locale.getDefault())
}
}

View File

@@ -0,0 +1,115 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data.translation
import ca.gosyer.data.translation.xml.Resources
import nl.adaptivity.xmlutil.StAXReader
import nl.adaptivity.xmlutil.serialization.XML
import java.io.InputStream
import java.io.Reader
import java.nio.charset.Charset
import java.util.Collections
import java.util.Enumeration
import java.util.Locale
import java.util.ResourceBundle
class XmlResourceBundle internal constructor(internal val lookup: Map<String, Any>) : ResourceBundle() {
constructor(stream: InputStream, charset: Charset = Charsets.UTF_8) : this(
stream.reader(charset)
)
constructor(reader: Reader) : this(
format.decodeFromReader<Resources>(
StAXReader(reader)
).values.associate { it.name to it.value }
)
public override fun handleGetObject(key: String): Any? {
return lookup[key]
}
override fun getKeys(): Enumeration<String> {
return Collections.enumeration(keySet())
}
override fun handleKeySet(): Set<String> {
return lookup.keys
}
operator fun plus(other: XmlResourceBundle): XmlResourceBundle {
return XmlResourceBundle(lookup + other.lookup)
}
fun getStringA(key: String): String {
return getString(key).replace("\\n", "\n")
}
fun getString(key: String, vararg replacements: String): String {
var string = getStringA(key)
replacements.forEachIndexed { index, s ->
string = string.replace(
"%" + (index + 1).toString() + '$' + "s",
s
)
}
return string
}
companion object {
private val format by lazy {
XML {
autoPolymorphic = true
indentString = "\t"
}
}
fun forTag(tag: String): XmlResourceBundle {
val classLoader = this::class.java.classLoader
val rootBundle = classLoader.getResourceAsStream("values/values/strings.xml")!!
.use { XmlResourceBundle(it) }
val languageBundle = classLoader.getResourceAsStream("values/values-${tag.substringBefore('-')}/strings.xml")
?.use { XmlResourceBundle(it) }
val languageTagBundle = if (tag.contains('-')) {
classLoader.getResourceAsStream("values/values-$tag/strings.xml")
?.use { XmlResourceBundle(it) }
} else null
var resultBundle = rootBundle
if (languageBundle != null) {
resultBundle += languageBundle
}
if (languageTagBundle != null) {
resultBundle += languageTagBundle
}
return resultBundle
}
fun forLocale(locale: Locale): XmlResourceBundle {
val classLoader = this::class.java.classLoader
val rootBundle = classLoader.getResourceAsStream("values/values/strings.xml")!!
.use { XmlResourceBundle(it) }
val languageBundle = classLoader.getResourceAsStream("values/values-${locale.language}/strings.xml")
?.use { XmlResourceBundle(it) }
val languageTagBundle = classLoader.getResourceAsStream("values/values-${locale.toLanguageTag()}/strings.xml")
?.use { XmlResourceBundle(it) }
var resultBundle = rootBundle
if (languageBundle != null) {
resultBundle += languageBundle
}
if (languageTagBundle != null) {
resultBundle += languageTagBundle
}
return resultBundle
}
}
}

View File

@@ -0,0 +1,14 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data.translation.xml
import kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlSerialName
@Serializable
@XmlSerialName("resources", "", "")
data class Resources(val values: List<XmlString>)

View File

@@ -0,0 +1,21 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data.translation.xml
import kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlElement
import nl.adaptivity.xmlutil.serialization.XmlSerialName
import nl.adaptivity.xmlutil.serialization.XmlValue
@Serializable
@XmlSerialName("string", "", "")
data class XmlString(
@XmlElement(false)
val name: String,
@XmlValue(true)
val value: String
)

View File

@@ -21,6 +21,7 @@ import androidx.compose.material.OutlinedButton
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.Key
@@ -28,6 +29,9 @@ import androidx.compose.ui.input.key.KeysSet
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ca.gosyer.common.di.AppScope
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.ui.base.resources.LocalResources
import ca.gosyer.ui.base.theme.AppTheme import ca.gosyer.ui.base.theme.AppTheme
import ca.gosyer.util.lang.launchUI import ca.gosyer.util.lang.launchUI
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@@ -77,28 +81,34 @@ fun WindowDialog(
window.keyboard.setShortcut(it.key) { it.shortcut(window) } window.keyboard.setShortcut(it.key) { it.shortcut(window) }
} }
window.show { val resources = AppScope.getInstance<XmlResourceBundle>()
AppTheme {
Surface {
Box(modifier = Modifier.fillMaxSize()) {
Row(
content = row,
modifier = Modifier.fillMaxWidth()
)
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.End,
modifier = Modifier.height(70.dp)
.align(Alignment.BottomEnd)
) {
if (showNegativeButton) {
OutlinedButton(onNegativeButton.plusClose(), modifier = Modifier.padding(end = 8.dp, bottom = 8.dp)) {
Text(negativeButtonText)
}
}
OutlinedButton(onPositiveButton.plusClose(), modifier = Modifier.padding(end = 8.dp, bottom = 8.dp)) { window.show {
Text(positiveButtonText) CompositionLocalProvider(
LocalResources provides resources
) {
AppTheme {
Surface {
Box(modifier = Modifier.fillMaxSize()) {
Row(
content = row,
modifier = Modifier.fillMaxWidth()
)
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.End,
modifier = Modifier.height(70.dp)
.align(Alignment.BottomEnd)
) {
if (showNegativeButton) {
OutlinedButton(onNegativeButton.plusClose(), modifier = Modifier.padding(end = 8.dp, bottom = 8.dp)) {
Text(negativeButtonText)
}
}
OutlinedButton(onPositiveButton.plusClose(), modifier = Modifier.padding(end = 8.dp, bottom = 8.dp)) {
Text(positiveButtonText)
}
} }
} }
} }
@@ -139,14 +149,20 @@ fun WindowDialog(
window.keyboard.setShortcut(it.key) { it.shortcut(window) } window.keyboard.setShortcut(it.key) { it.shortcut(window) }
} }
val resources = AppScope.getInstance<XmlResourceBundle>()
window.show { window.show {
AppTheme { CompositionLocalProvider(
Surface { LocalResources provides resources
Column( ) {
modifier = Modifier.fillMaxSize() AppTheme {
) { Surface {
content(window) Column(
buttons(window) modifier = Modifier.fillMaxSize()
) {
content(window)
buttons(window)
}
} }
} }
} }

View File

@@ -0,0 +1,28 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.ui.base.resources
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import ca.gosyer.data.translation.XmlResourceBundle
val LocalResources: ProvidableCompositionLocal<XmlResourceBundle> =
compositionLocalOf { throw IllegalStateException("resources have not been not initialized") }
@Composable
fun stringResource(key: String): String {
val resources = LocalResources.current
return remember(key) { resources.getStringA(key) }
}
@Composable
fun stringResource(key: String, vararg replacements: String): String {
val resources = LocalResources.current
return remember(key, replacements) { resources.getString(key, *replacements) }
}

View File

@@ -12,6 +12,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import ca.gosyer.ui.base.WindowDialog import ca.gosyer.ui.base.WindowDialog
import ca.gosyer.ui.base.resources.stringResource
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
fun openRenameDialog( fun openRenameDialog(
@@ -52,7 +53,7 @@ fun openDeleteDialog(
}, },
negativeButtonText = "No" negativeButtonText = "No"
) { ) {
Text("Do you wish to delete the category ${category.name}?") Text(stringResource("categories_delete_confirm", category.name))
} }
} }

View File

@@ -43,6 +43,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ca.gosyer.BuildConfig import ca.gosyer.BuildConfig
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.util.compose.ThemedWindow import ca.gosyer.util.compose.ThemedWindow
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
@@ -103,7 +104,7 @@ fun CategoriesMenu(notifyFinished: (() -> Unit)? = null, windowEvents: WindowEve
} }
} }
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
text = { Text(text = "Add") }, text = { Text(text = stringResource("action_add")) },
icon = { Icon(imageVector = Icons.Default.Add, contentDescription = null) }, icon = { Icon(imageVector = Icons.Default.Add, contentDescription = null) },
modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp), modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp),
onClick = { onClick = {

View File

@@ -45,6 +45,7 @@ import ca.gosyer.data.models.Chapter
import ca.gosyer.ui.base.components.ActionIcon import ca.gosyer.ui.base.components.ActionIcon
import ca.gosyer.ui.base.components.DropdownIconButton import ca.gosyer.ui.base.components.DropdownIconButton
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.util.compose.ThemedWindow import ca.gosyer.util.compose.ThemedWindow
@@ -62,16 +63,16 @@ fun DownloadsMenu() {
Surface { Surface {
Column { Column {
Toolbar( Toolbar(
"Downloads", stringResource("location_downloads"),
closable = false, closable = false,
actions = { actions = {
val downloadStatus by vm.downloaderStatus.collectAsState() val downloadStatus by vm.downloaderStatus.collectAsState()
if (downloadStatus == DownloaderStatus.Started) { if (downloadStatus == DownloaderStatus.Started) {
ActionIcon(onClick = vm::pause, "Pause", Icons.Default.Pause) ActionIcon(onClick = vm::pause, stringResource("action_pause"), Icons.Default.Pause)
} else { } else {
ActionIcon(onClick = vm::start, "Continue", Icons.Default.PlayArrow) ActionIcon(onClick = vm::start, stringResource("action_continue"), Icons.Default.PlayArrow)
} }
ActionIcon(onClick = vm::clear, "Clear queue", Icons.Default.ClearAll) ActionIcon(onClick = vm::clear, stringResource("action_clear_queue"), Icons.Default.ClearAll)
} }
) )
LazyColumn(Modifier.fillMaxSize()) { LazyColumn(Modifier.fillMaxSize()) {
@@ -137,10 +138,10 @@ private fun downloadsItem(
chapter.mangaId to chapter.chapterIndex, chapter.mangaId to chapter.chapterIndex,
{ {
DropdownMenuItem(onClick = { onDownloadCancel(chapter.chapter) }) { DropdownMenuItem(onClick = { onDownloadCancel(chapter.chapter) }) {
Text("Cancel") Text(stringResource("action_cancel"))
} }
DropdownMenuItem(onClick = { onMoveDownloadToBottom(chapter.chapter) }) { DropdownMenuItem(onClick = { onMoveDownloadToBottom(chapter.chapter) }) {
Text("Move to bottom") Text(stringResource("action_move_to_bottom"))
} }
} }
) { ) {

View File

@@ -52,6 +52,7 @@ import ca.gosyer.ui.base.components.ActionIcon
import ca.gosyer.ui.base.components.KtorImage import ca.gosyer.ui.base.components.KtorImage
import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.components.LoadingScreen
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.util.compose.ThemedWindow import ca.gosyer.util.compose.ThemedWindow
import ca.gosyer.util.compose.persistentLazyListState import ca.gosyer.util.compose.persistentLazyListState
@@ -82,7 +83,7 @@ fun ExtensionsMenu() {
LazyColumn(Modifier.fillMaxSize().padding(end = 12.dp), state) { LazyColumn(Modifier.fillMaxSize().padding(end = 12.dp), state) {
item { item {
Toolbar( Toolbar(
"Extensions", stringResource("location_extensions"),
closable = false, closable = false,
searchText = search, searchText = search,
search = { search = {
@@ -96,7 +97,7 @@ fun ExtensionsMenu() {
vm.setEnabledLanguages(enabledLangs.value) vm.setEnabledLanguages(enabledLangs.value)
} }
}, },
"Enabled languages", stringResource("enabled_languages"),
Icons.Default.Translate Icons.Default.Translate
) )
} }
@@ -167,9 +168,9 @@ fun ExtensionItem(
) { ) {
Text( Text(
when { when {
extension.hasUpdate -> "Update" extension.hasUpdate -> stringResource("action_update")
extension.installed -> "Uninstall" extension.installed -> stringResource("action_uninstall")
else -> "Install" else -> stringResource("action_install")
} }
) )
} }
@@ -178,6 +179,7 @@ fun ExtensionItem(
fun LanguageDialog(enabledLangsFlow: MutableStateFlow<Set<String>>, availableLangs: List<String>, setLangs: () -> Unit) { fun LanguageDialog(enabledLangsFlow: MutableStateFlow<Set<String>>, availableLangs: List<String>, setLangs: () -> Unit) {
WindowDialog(BuildConfig.NAME, onPositiveButton = setLangs) { WindowDialog(BuildConfig.NAME, onPositiveButton = setLangs) {
val locale = Locale.getDefault()
val enabledLangs by enabledLangsFlow.collectAsState() val enabledLangs by enabledLangsFlow.collectAsState()
val state = rememberLazyListState() val state = rememberLazyListState()
Box { Box {
@@ -185,7 +187,7 @@ fun LanguageDialog(enabledLangsFlow: MutableStateFlow<Set<String>>, availableLan
items(availableLangs) { lang -> items(availableLangs) { lang ->
Row { Row {
val langName = remember(lang) { val langName = remember(lang) {
Locale.forLanguageTag(lang)?.displayName ?: lang Locale.forLanguageTag(lang)?.getDisplayName(locale) ?: lang
} }
Text(langName) Text(langName)
Switch( Switch(

View File

@@ -12,6 +12,7 @@ import ca.gosyer.data.models.Manga
import ca.gosyer.data.server.ServerPreferences import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.interactions.CategoryInteractionHandler import ca.gosyer.data.server.interactions.CategoryInteractionHandler
import ca.gosyer.data.server.interactions.LibraryInteractionHandler import ca.gosyer.data.server.interactions.LibraryInteractionHandler
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.ui.base.vm.ViewModel import ca.gosyer.ui.base.vm.ViewModel
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
@@ -33,6 +34,7 @@ private fun LibraryMap.setManga(order: Int, manga: List<Manga>) {
class LibraryScreenViewModel @Inject constructor( class LibraryScreenViewModel @Inject constructor(
private val libraryHandler: LibraryInteractionHandler, private val libraryHandler: LibraryInteractionHandler,
private val categoryHandler: CategoryInteractionHandler, private val categoryHandler: CategoryInteractionHandler,
private val resources: XmlResourceBundle,
libraryPreferences: LibraryPreferences, libraryPreferences: LibraryPreferences,
serverPreferences: ServerPreferences, serverPreferences: ServerPreferences,
) : ViewModel() { ) : ViewModel() {
@@ -49,6 +51,8 @@ class LibraryScreenViewModel @Inject constructor(
private val _isLoading = MutableStateFlow(true) private val _isLoading = MutableStateFlow(true)
val isLoading = _isLoading.asStateFlow() val isLoading = _isLoading.asStateFlow()
private val defaultCategory = Category(0, 0, resources.getStringA("default_category"), true)
init { init {
getLibrary() getLibrary()
} }
@@ -85,8 +89,4 @@ class LibraryScreenViewModel @Inject constructor(
fun getLibraryForCategoryIndex(index: Int): StateFlow<List<Manga>> { fun getLibraryForCategoryIndex(index: Int): StateFlow<List<Manga>> {
return library.mangaMap.getManga(index).asStateFlow() return library.mangaMap.getManga(index).asStateFlow()
} }
companion object {
val defaultCategory = Category(0, 0, "Default", true)
}
} }

View File

@@ -50,6 +50,7 @@ import androidx.compose.ui.unit.sp
import ca.gosyer.BuildConfig import ca.gosyer.BuildConfig
import ca.gosyer.data.ui.model.StartScreen import ca.gosyer.data.ui.model.StartScreen
import ca.gosyer.ui.base.components.combinedMouseClickable import ca.gosyer.ui.base.components.combinedMouseClickable
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.downloads.DownloadsMenu import ca.gosyer.ui.downloads.DownloadsMenu
import ca.gosyer.ui.downloads.DownloadsMenuViewModel import ca.gosyer.ui.downloads.DownloadsMenuViewModel
@@ -125,7 +126,7 @@ fun SideMenu(backStack: BackStack<Route>) {
fun SideMenuItem(topLevelMenu: TopLevelMenus, backStack: BackStack<Route>) { fun SideMenuItem(topLevelMenu: TopLevelMenus, backStack: BackStack<Route>) {
MainMenuItem( MainMenuItem(
backStack.elements.first() == topLevelMenu.menu, backStack.elements.first() == topLevelMenu.menu,
topLevelMenu.text, stringResource(topLevelMenu.textKey),
topLevelMenu.menu, topLevelMenu.menu,
topLevelMenu.selectedIcon, topLevelMenu.selectedIcon,
topLevelMenu.unselectedIcon, topLevelMenu.unselectedIcon,
@@ -233,7 +234,7 @@ fun DownloadsExtraInfo() {
val list by vm.downloadQueue.collectAsState() val list by vm.downloadQueue.collectAsState()
if (list.isNotEmpty()) { if (list.isNotEmpty()) {
Text( Text(
"${list.size} remaining", stringResource("downloads_remaining", list.size.toString()),
style = MaterialTheme.typography.body2, style = MaterialTheme.typography.body2,
color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled) color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled)
) )
@@ -241,7 +242,7 @@ fun DownloadsExtraInfo() {
} }
enum class TopLevelMenus( enum class TopLevelMenus(
val text: String, val textKey: String,
val unselectedIcon: ImageVector, val unselectedIcon: ImageVector,
val selectedIcon: ImageVector, val selectedIcon: ImageVector,
val menu: Route, val menu: Route,
@@ -249,11 +250,11 @@ enum class TopLevelMenus(
val openInNewWindow: () -> Unit = {}, val openInNewWindow: () -> Unit = {},
val extraInfo: (@Composable () -> Unit)? = null val extraInfo: (@Composable () -> Unit)? = null
) { ) {
Library("Library", Icons.Outlined.Book, Icons.Filled.Book, Route.Library, true, ::openLibraryMenu), Library("location_library", Icons.Outlined.Book, Icons.Filled.Book, Route.Library, true, ::openLibraryMenu),
Sources("Sources", Icons.Outlined.Explore, Icons.Filled.Explore, Route.Sources, true, ::openSourcesMenu), Sources("location_sources", Icons.Outlined.Explore, Icons.Filled.Explore, Route.Sources, true, ::openSourcesMenu),
Extensions("Extensions", Icons.Outlined.Store, Icons.Filled.Store, Route.Extensions, true, ::openExtensionsMenu), Extensions("location_extensions", Icons.Outlined.Store, Icons.Filled.Store, Route.Extensions, true, ::openExtensionsMenu),
Downloads("Downloads", Icons.Outlined.Download, Icons.Filled.Download, Route.Downloads, false, extraInfo = { DownloadsExtraInfo() }), Downloads("location_downloads", Icons.Outlined.Download, Icons.Filled.Download, Route.Downloads, false, extraInfo = { DownloadsExtraInfo() }),
Settings("Settings", Icons.Outlined.Settings, Icons.Filled.Settings, Route.Settings, false) Settings("location_settings", Icons.Outlined.Settings, Icons.Filled.Settings, Route.Settings, false)
} }
sealed class Route { sealed class Route {

View File

@@ -16,10 +16,13 @@ import ca.gosyer.BuildConfig
import ca.gosyer.data.DataModule import ca.gosyer.data.DataModule
import ca.gosyer.data.server.ServerService import ca.gosyer.data.server.ServerService
import ca.gosyer.data.server.ServerService.ServerResult import ca.gosyer.data.server.ServerService.ServerResult
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.data.ui.UiPreferences import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.ui.model.ThemeMode import ca.gosyer.data.ui.model.ThemeMode
import ca.gosyer.data.ui.model.WindowSettings import ca.gosyer.data.ui.model.WindowSettings
import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.components.LoadingScreen
import ca.gosyer.ui.base.resources.LocalResources
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.theme.AppTheme import ca.gosyer.ui.base.theme.AppTheme
import ca.gosyer.util.lang.launchUI import ca.gosyer.util.lang.launchUI
import ca.gosyer.util.lang.withUIContext import ca.gosyer.util.lang.withUIContext
@@ -66,8 +69,10 @@ fun main() {
) )
val serverService = scope.getInstance<ServerService>() val serverService = scope.getInstance<ServerService>()
val uiPreferences = scope.getInstance<UiPreferences>()
val resources = scope.getInstance<XmlResourceBundle>()
scope.getInstance<UiPreferences>().themeMode() uiPreferences.themeMode()
.getAsFlow { .getAsFlow {
if (System.getProperty("os.name").startsWith("Mac") && System.getProperty("os.arch") == "aarch64") { if (System.getProperty("os.name").startsWith("Mac") && System.getProperty("os.arch") == "aarch64") {
return@getAsFlow return@getAsFlow
@@ -89,7 +94,7 @@ fun main() {
} }
.launchIn(GlobalScope) .launchIn(GlobalScope)
val windowSettings = scope.getInstance<UiPreferences>().window() val windowSettings = uiPreferences.window()
val ( val (
offset, offset,
size, size,
@@ -129,7 +134,8 @@ fun main() {
window.show { window.show {
AppTheme { AppTheme {
CompositionLocalProvider( CompositionLocalProvider(
LocalBackPressHandler provides backPressHandler LocalBackPressHandler provides backPressHandler,
LocalResources provides resources
) { ) {
val initialized by serverService.initialized.collectAsState() val initialized by serverService.initialized.collectAsState()
when (initialized) { when (initialized) {
@@ -139,12 +145,14 @@ fun main() {
ServerResult.STARTING, ServerResult.FAILED, ServerResult.NO_TACHIDESK_JAR -> { ServerResult.STARTING, ServerResult.FAILED, ServerResult.NO_TACHIDESK_JAR -> {
LoadingScreen( LoadingScreen(
initialized == ServerResult.STARTING, initialized == ServerResult.STARTING,
errorMessage = if (initialized == ServerResult.NO_TACHIDESK_JAR) { errorMessage = stringResource(
"Tachidesk jar does not exist, run Tachidesk yourself" if (initialized == ServerResult.NO_TACHIDESK_JAR) {
} else { "tachidesk_doesnt_exist"
"Unable to start server" } else {
}, "unable_to_start_server"
retryMessage = "Start anyway", }
),
retryMessage = stringResource("action_start_anyway"),
retry = serverService::startAnyway retry = serverService::startAnyway
) )
} }

View File

@@ -47,6 +47,7 @@ import ca.gosyer.data.download.model.DownloadChapter
import ca.gosyer.data.download.model.DownloadState import ca.gosyer.data.download.model.DownloadState
import ca.gosyer.ui.base.components.DropdownIconButton import ca.gosyer.ui.base.components.DropdownIconButton
import ca.gosyer.ui.base.components.combinedMouseClickable import ca.gosyer.ui.base.components.combinedMouseClickable
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.util.compose.contextMenu import ca.gosyer.util.compose.contextMenu
import java.time.Instant import java.time.Instant
@@ -108,7 +109,7 @@ fun ChapterItem(
if (length > 0) append("") if (length > 0) append("")
append( append(
AnnotatedString( AnnotatedString(
"Page " + (chapter.lastPageRead + 1).toString(), stringResource("page_progress", (chapter.lastPageRead + 1).toString()),
SpanStyle(color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled)) SpanStyle(color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled))
) )
) )
@@ -179,7 +180,7 @@ private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: ()
downloadChapter?.mangaId to downloadChapter?.chapterIndex, downloadChapter?.mangaId to downloadChapter?.chapterIndex,
{ {
DropdownMenuItem(onClick = onClick) { DropdownMenuItem(onClick = onClick) {
Text("Cancel") Text(stringResource("action_cancel"))
} }
} }
) { ) {
@@ -247,7 +248,7 @@ private fun DownloadedIconButton(chapter: Pair<Long, Int?>, onClick: () -> Unit)
chapter, chapter,
{ {
DropdownMenuItem(onClick = onClick) { DropdownMenuItem(onClick = onClick) {
Text("Delete") Text(stringResource("action_delete"))
} }
} }
) { ) {

View File

@@ -45,6 +45,7 @@ import ca.gosyer.ui.base.components.KtorImage
import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.components.LoadingScreen
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.components.mangaAspectRatio import ca.gosyer.ui.base.components.mangaAspectRatio
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import ca.gosyer.ui.reader.openReaderMenu import ca.gosyer.ui.reader.openReaderMenu
@@ -69,15 +70,15 @@ fun MangaMenu(mangaId: Long, backStack: BackStack<Route>? = null) {
val dateTimeFormatter by vm.dateTimeFormatter.collectAsState() val dateTimeFormatter by vm.dateTimeFormatter.collectAsState()
Column(Modifier.background(MaterialTheme.colors.background)) { Column(Modifier.background(MaterialTheme.colors.background)) {
Toolbar("Manga", backStack, backStack != null) Toolbar(stringResource("location_manga"), backStack, backStack != null)
Surface(Modifier.height(40.dp).fillMaxWidth()) { Surface(Modifier.height(40.dp).fillMaxWidth()) {
Row(horizontalArrangement = Arrangement.SpaceBetween) { Row(horizontalArrangement = Arrangement.SpaceBetween) {
Button(onClick = vm::toggleFavorite) { Button(onClick = vm::toggleFavorite) {
Text(if (manga?.inLibrary == true) "UnFavorite" else "Favorite") Text(stringResource(if (manga?.inLibrary == true) "action_remove_favorite" else "action_favorite"))
} }
Button(onClick = vm::refreshManga, enabled = !vm.isRefreshing.collectAsState().value) { Button(onClick = vm::refreshManga, enabled = !vm.isRefreshing.collectAsState().value) {
Text("Refresh manga") Text(stringResource("action_refresh_manga"))
} }
} }
} }

View File

@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -32,12 +33,15 @@ import ca.gosyer.common.di.AppScope
import ca.gosyer.data.reader.model.Direction import ca.gosyer.data.reader.model.Direction
import ca.gosyer.data.reader.model.ImageScale import ca.gosyer.data.reader.model.ImageScale
import ca.gosyer.data.reader.model.NavigationMode import ca.gosyer.data.reader.model.NavigationMode
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.data.ui.UiPreferences import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.ui.model.WindowSettings import ca.gosyer.data.ui.model.WindowSettings
import ca.gosyer.ui.base.KeyboardShortcut import ca.gosyer.ui.base.KeyboardShortcut
import ca.gosyer.ui.base.components.ErrorScreen import ca.gosyer.ui.base.components.ErrorScreen
import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.components.LoadingScreen
import ca.gosyer.ui.base.components.mangaAspectRatio import ca.gosyer.ui.base.components.mangaAspectRatio
import ca.gosyer.ui.base.resources.LocalResources
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.theme.AppTheme import ca.gosyer.ui.base.theme.AppTheme
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.reader.model.Navigation import ca.gosyer.ui.reader.model.Navigation
@@ -63,6 +67,8 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
maximized maximized
) = windowSettings.get().get() ) = windowSettings.get().get()
val resources = AppScope.getInstance<XmlResourceBundle>()
launchUI { launchUI {
val window = AppWindow( val window = AppWindow(
"TachideskJUI - Reader", "TachideskJUI - Reader",
@@ -94,12 +100,16 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
} }
window.show { window.show {
AppTheme { CompositionLocalProvider(
ReaderMenu(chapterIndex, mangaId, setHotkeys) { LocalResources provides resources
val onClose = window.events.onClose ) {
window.events.onClose = { AppTheme {
it() ReaderMenu(chapterIndex, mangaId, setHotkeys) {
onClose?.invoke() val onClose = window.events.onClose
window.events.onClose = {
it()
onClose?.invoke()
}
} }
} }
} }
@@ -196,7 +206,7 @@ fun ReaderMenu(chapterIndex: Int, mangaId: Long, setHotkeys: (List<KeyboardShort
) )
} }
} else { } else {
ErrorScreen("No pages found") ErrorScreen(stringResource("no_pages_found"))
} }
} }
} }
@@ -243,15 +253,15 @@ fun ChapterSeperator(
Column { Column {
when { when {
previousChapter == null && nextChapter != null -> { previousChapter == null && nextChapter != null -> {
Text("There is no previous chapter") Text(stringResource("no_previous_chapter"))
} }
previousChapter != null && nextChapter != null -> { previousChapter != null && nextChapter != null -> {
Text("Previous:\n ${previousChapter.chapter.name}") Text(stringResource("previous_chapter", previousChapter.chapter.name))
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
Text("Next:\n ${nextChapter.chapter.name}") Text(stringResource("next_chapter", nextChapter.chapter.name))
} }
previousChapter != null && nextChapter == null -> { previousChapter != null && nextChapter == null -> {
Text("There is no next chapter") Text(stringResource("no_next_chapter"))
} }
} }
} }

View File

@@ -10,13 +10,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import com.github.zsoltk.compose.router.BackStack import com.github.zsoltk.compose.router.BackStack
@Composable @Composable
fun SettingsAdvancedScreen(navController: BackStack<Route>) { fun SettingsAdvancedScreen(navController: BackStack<Route>) {
Column { Column {
Toolbar("Advanced Settings", navController, true) Toolbar(stringResource("settings_advanced_screen"), navController, true)
LazyColumn { LazyColumn {
} }
} }

View File

@@ -34,6 +34,7 @@ import ca.gosyer.data.ui.model.ThemeMode
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.prefs.ChoicePreference import ca.gosyer.ui.base.prefs.ChoicePreference
import ca.gosyer.ui.base.prefs.ColorPreference import ca.gosyer.ui.base.prefs.ColorPreference
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.theme.AppColorsPreferenceState import ca.gosyer.ui.base.theme.AppColorsPreferenceState
import ca.gosyer.ui.base.theme.Theme import ca.gosyer.ui.base.theme.Theme
import ca.gosyer.ui.base.theme.asStateFlow import ca.gosyer.ui.base.theme.asStateFlow
@@ -73,22 +74,22 @@ fun SettingsAppearance(navController: BackStack<Route>) {
} }
Column { Column {
Toolbar("Appearance Settings", navController, true) Toolbar(stringResource("settings_appearance_screen"), navController, true)
LazyColumn { LazyColumn {
item { item {
ChoicePreference( ChoicePreference(
preference = vm.themeMode, preference = vm.themeMode,
choices = mapOf( choices = mapOf(
// ThemeMode.System to R.string.follow_system_settings, // ThemeMode.System to R.string.follow_system_settings,
ThemeMode.Light to "Light", ThemeMode.Light to stringResource("theme_light"),
ThemeMode.Dark to "Dark" ThemeMode.Dark to stringResource("theme_Dark")
), ),
title = "Theme" title = stringResource("theme")
) )
} }
item { item {
Text( Text(
"Preset themes", stringResource("preset_themes"),
modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 4.dp) modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 4.dp)
) )
LazyRow(modifier = Modifier.padding(horizontal = 8.dp)) { LazyRow(modifier = Modifier.padding(horizontal = 8.dp)) {
@@ -107,16 +108,16 @@ fun SettingsAppearance(navController: BackStack<Route>) {
item { item {
ColorPreference( ColorPreference(
preference = activeColors.primaryStateFlow, preference = activeColors.primaryStateFlow,
title = "Color primary", title = stringResource("color_primary"),
subtitle = "Displayed most frequently across your app", subtitle = stringResource("color_primary_sub"),
unsetColor = MaterialTheme.colors.primary unsetColor = MaterialTheme.colors.primary
) )
} }
item { item {
ColorPreference( ColorPreference(
preference = activeColors.secondaryStateFlow, preference = activeColors.secondaryStateFlow,
title = "Color secondary", title = stringResource("color_secondary"),
subtitle = "Accents select parts of the UI", subtitle = stringResource("color_secondary_sub"),
unsetColor = MaterialTheme.colors.secondary unsetColor = MaterialTheme.colors.secondary
) )
} }
@@ -152,7 +153,7 @@ private fun ThemeItem(
.weight(1f) .weight(1f)
.padding(6.dp) .padding(6.dp)
) { ) {
Text("Text", fontSize = 11.sp) Text(stringResource("theme_text"), fontSize = 11.sp)
Button( Button(
onClick = {}, onClick = {},
enabled = false, enabled = false,

View File

@@ -26,6 +26,7 @@ import androidx.compose.ui.unit.min
import ca.gosyer.data.server.interactions.BackupInteractionHandler import ca.gosyer.data.server.interactions.BackupInteractionHandler
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.prefs.PreferenceRow import ca.gosyer.ui.base.prefs.PreferenceRow
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.ViewModel import ca.gosyer.ui.base.vm.ViewModel
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
@@ -134,12 +135,12 @@ fun SettingsBackupScreen(navController: BackStack<Route>) {
val creatingProgress by vm.creatingProgress.collectAsState() val creatingProgress by vm.creatingProgress.collectAsState()
val creatingStatus by vm.creatingStatus.collectAsState() val creatingStatus by vm.creatingStatus.collectAsState()
Column { Column {
Toolbar("Backup Settings", navController, true) Toolbar(stringResource("settings_backup_screen"), navController, true)
LazyColumn { LazyColumn {
item { item {
PreferenceFile( PreferenceFile(
"Restore Backup", stringResource("backup_restore"),
"Restore a backup into Tachidesk", stringResource("backup_restore_sub"),
restoring, restoring,
restoringProgress, restoringProgress,
restoreStatus restoreStatus
@@ -149,8 +150,8 @@ fun SettingsBackupScreen(navController: BackStack<Route>) {
} }
} }
PreferenceFile( PreferenceFile(
"Create Backup", stringResource("backup_create"),
"Create a backup from Tachidesk", stringResource("backup_create_sub"),
creating, creating,
creatingProgress, creatingProgress,
creatingStatus creatingStatus

View File

@@ -10,13 +10,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import com.github.zsoltk.compose.router.BackStack import com.github.zsoltk.compose.router.BackStack
@Composable @Composable
fun SettingsBrowseScreen(navController: BackStack<Route>) { fun SettingsBrowseScreen(navController: BackStack<Route>) {
Column { Column {
Toolbar("Browse Settings", navController, true) Toolbar(stringResource("settings_browse_screen"), navController, true)
LazyColumn { LazyColumn {
} }
} }

View File

@@ -10,13 +10,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import com.github.zsoltk.compose.router.BackStack import com.github.zsoltk.compose.router.BackStack
@Composable @Composable
fun SettingsDownloadsScreen(navController: BackStack<Route>) { fun SettingsDownloadsScreen(navController: BackStack<Route>) {
Column { Column {
Toolbar("Download Settings", navController, true) Toolbar(stringResource("settings_download_screen"), navController, true)
LazyColumn { LazyColumn {
} }
} }

View File

@@ -10,11 +10,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Divider import androidx.compose.material.Divider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.data.ui.UiPreferences import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.ui.model.StartScreen import ca.gosyer.data.ui.model.StartScreen
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.prefs.ChoicePreference import ca.gosyer.ui.base.prefs.ChoicePreference
import ca.gosyer.ui.base.prefs.SwitchPreference import ca.gosyer.ui.base.prefs.SwitchPreference
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.ViewModel import ca.gosyer.ui.base.vm.ViewModel
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
@@ -28,7 +30,8 @@ import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
class SettingsGeneralViewModel @Inject constructor( class SettingsGeneralViewModel @Inject constructor(
uiPreferences: UiPreferences private val resources: XmlResourceBundle,
uiPreferences: UiPreferences,
) : ViewModel() { ) : ViewModel() {
val startScreen = uiPreferences.startScreen().asStateFlow() val startScreen = uiPreferences.startScreen().asStateFlow()
@@ -37,22 +40,26 @@ class SettingsGeneralViewModel @Inject constructor(
val dateFormat = uiPreferences.dateFormat().asStateFlow() val dateFormat = uiPreferences.dateFormat().asStateFlow()
private val now: Instant = Instant.now() private val now: Instant = Instant.now()
private val currentLocale = Locale.getDefault()
fun getLocalePair(locale: String): Pair<String, String> {
return locale to Locale.forLanguageTag(locale).getDisplayName(currentLocale)
}
@Composable @Composable
fun getLanguageChoices(): Map<String, String> { fun getLanguageChoices(): Map<String, String> {
val currentLocaleDisplayName = val currentLocaleDisplayName = currentLocale.getDisplayName(currentLocale).capitalize(currentLocale)
Locale.getDefault().let { locale ->
locale.getDisplayName(locale).capitalize()
}
return mapOf( return mapOf(
"" to "System Default ($currentLocaleDisplayName)" "" to resources.getString("language_system_default", currentLocaleDisplayName),
getLocalePair("en-CA")
) )
} }
@Composable @Composable
fun getDateChoices(): Map<String, String> { fun getDateChoices(): Map<String, String> {
return mapOf( return mapOf(
"" to "System Default", "" to resources.getStringA("date_system_default"),
"MM/dd/yy" to "MM/dd/yy", "MM/dd/yy" to "MM/dd/yy",
"dd/MM/yy" to "dd/MM/yy", "dd/MM/yy" to "dd/MM/yy",
"yyyy-MM-dd" to "yyyy-MM-dd" "yyyy-MM-dd" to "yyyy-MM-dd"
@@ -73,21 +80,21 @@ class SettingsGeneralViewModel @Inject constructor(
fun SettingsGeneralScreen(navController: BackStack<Route>) { fun SettingsGeneralScreen(navController: BackStack<Route>) {
val vm = viewModel<SettingsGeneralViewModel>() val vm = viewModel<SettingsGeneralViewModel>()
Column { Column {
Toolbar("General Settings", navController, true) Toolbar(stringResource("settings_general_screen"), navController, true)
LazyColumn { LazyColumn {
item { item {
ChoicePreference( ChoicePreference(
preference = vm.startScreen, preference = vm.startScreen,
title = "Start Screen", title = stringResource("start_screen"),
choices = mapOf( choices = mapOf(
StartScreen.Library to "Library", StartScreen.Library to stringResource("location_library"),
StartScreen.Sources to "Sources", StartScreen.Sources to stringResource("location_sources"),
StartScreen.Extensions to "Extensions", StartScreen.Extensions to stringResource("location_extensions"),
) )
) )
} }
item { item {
SwitchPreference(preference = vm.confirmExit, title = "Confirm Exit") SwitchPreference(preference = vm.confirmExit, title = stringResource("confirm_exit"))
} }
item { item {
Divider() Divider()
@@ -95,14 +102,14 @@ fun SettingsGeneralScreen(navController: BackStack<Route>) {
item { item {
ChoicePreference( ChoicePreference(
preference = vm.language, preference = vm.language,
title = "Language", title = stringResource("language"),
choices = vm.getLanguageChoices(), choices = vm.getLanguageChoices(),
) )
} }
item { item {
ChoicePreference( ChoicePreference(
preference = vm.dateFormat, preference = vm.dateFormat,
title = "Date Format", title = stringResource("date_format"),
choices = vm.getDateChoices() choices = vm.getDateChoices()
) )
} }

View File

@@ -15,6 +15,7 @@ import ca.gosyer.data.server.interactions.CategoryInteractionHandler
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.prefs.PreferenceRow import ca.gosyer.ui.base.prefs.PreferenceRow
import ca.gosyer.ui.base.prefs.SwitchPreference import ca.gosyer.ui.base.prefs.SwitchPreference
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.ViewModel import ca.gosyer.ui.base.vm.ViewModel
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.categories.openCategoriesMenu import ca.gosyer.ui.categories.openCategoriesMenu
@@ -50,14 +51,14 @@ fun SettingsLibraryScreen(navController: BackStack<Route>) {
val vm = viewModel<SettingsLibraryViewModel>() val vm = viewModel<SettingsLibraryViewModel>()
Column { Column {
Toolbar("Library Settings", navController, true) Toolbar(stringResource("settings_library_screen"), navController, true)
LazyColumn { LazyColumn {
item { item {
SwitchPreference(preference = vm.showAllCategory, title = "Show all category") SwitchPreference(preference = vm.showAllCategory, title = stringResource("show_all_category"))
} }
item { item {
PreferenceRow( PreferenceRow(
"Categories", stringResource("location_categories"),
onClick = { openCategoriesMenu(vm::refreshCategoryCount) }, onClick = { openCategoriesMenu(vm::refreshCategoryCount) },
subtitle = vm.categories.collectAsState().value.toString() subtitle = vm.categories.collectAsState().value.toString()
) )

View File

@@ -10,13 +10,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import com.github.zsoltk.compose.router.BackStack import com.github.zsoltk.compose.router.BackStack
@Composable @Composable
fun SettingsParentalControlsScreen(navController: BackStack<Route>) { fun SettingsParentalControlsScreen(navController: BackStack<Route>) {
Column { Column {
Toolbar("Parental Control Settings", navController, true) Toolbar(stringResource("settings_parental_control_screen"), navController, true)
LazyColumn { LazyColumn {
} }
} }

View File

@@ -18,12 +18,14 @@ import ca.gosyer.data.reader.ReaderPreferences
import ca.gosyer.data.reader.model.Direction import ca.gosyer.data.reader.model.Direction
import ca.gosyer.data.reader.model.ImageScale import ca.gosyer.data.reader.model.ImageScale
import ca.gosyer.data.reader.model.NavigationMode import ca.gosyer.data.reader.model.NavigationMode
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.prefs.ChoicePreference import ca.gosyer.ui.base.prefs.ChoicePreference
import ca.gosyer.ui.base.prefs.ExpandablePreference import ca.gosyer.ui.base.prefs.ExpandablePreference
import ca.gosyer.ui.base.prefs.PreferenceMutableStateFlow import ca.gosyer.ui.base.prefs.PreferenceMutableStateFlow
import ca.gosyer.ui.base.prefs.SwitchPreference import ca.gosyer.ui.base.prefs.SwitchPreference
import ca.gosyer.ui.base.prefs.asStateIn import ca.gosyer.ui.base.prefs.asStateIn
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.ViewModel import ca.gosyer.ui.base.vm.ViewModel
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
@@ -36,6 +38,7 @@ import kotlinx.coroutines.flow.onEach
import javax.inject.Inject import javax.inject.Inject
class SettingsReaderViewModel @Inject constructor( class SettingsReaderViewModel @Inject constructor(
private val resources: XmlResourceBundle,
readerPreferences: ReaderPreferences readerPreferences: ReaderPreferences
) : ViewModel() { ) : ViewModel() {
val modes = readerPreferences.modes().asStateFlow() val modes = readerPreferences.modes().asStateFlow()
@@ -56,10 +59,10 @@ class SettingsReaderViewModel @Inject constructor(
}.launchIn(scope) }.launchIn(scope)
} }
fun getDirectionChoices() = Direction.values().associate { it to it.res } fun getDirectionChoices() = Direction.values().associate { it to resources.getStringA(it.res) }
fun getPaddingChoices() = mapOf( fun getPaddingChoices() = mapOf(
0 to "None", 0 to resources.getStringA("page_padding_none"),
8 to "8 Dp", 8 to "8 Dp",
16 to "16 Dp", 16 to "16 Dp",
32 to "32 Dp" 32 to "32 Dp"
@@ -67,23 +70,23 @@ class SettingsReaderViewModel @Inject constructor(
fun getMaxSizeChoices(direction: Direction) = if (direction == Direction.Right || direction == Direction.Left) { fun getMaxSizeChoices(direction: Direction) = if (direction == Direction.Right || direction == Direction.Left) {
mapOf( mapOf(
0 to "Unrestricted", 0 to resources.getStringA("max_size_unrestricted"),
700 to "700 Dp", 700 to "700 Dp",
900 to "900 Dp", 900 to "900 Dp",
1100 to "1100 Dp" 1100 to "1100 Dp"
) )
} else { } else {
mapOf( mapOf(
0 to "Unrestricted", 0 to resources.getStringA("max_size_unrestricted"),
500 to "500 Dp", 500 to "500 Dp",
700 to "700 Dp", 700 to "700 Dp",
900 to "900 Dp" 900 to "900 Dp"
) )
} }
fun getImageScaleChoices() = ImageScale.values().associate { it to it.res } fun getImageScaleChoices() = ImageScale.values().associate { it to resources.getStringA(it.res) }
fun getNavigationModeChoices() = NavigationMode.values().associate { it to it.res } fun getNavigationModeChoices() = NavigationMode.values().associate { it to resources.getStringA(it.res) }
} }
data class ReaderModePreference( data class ReaderModePreference(
@@ -118,13 +121,13 @@ fun SettingsReaderScreen(navController: BackStack<Route>) {
val vm = viewModel<SettingsReaderViewModel>() val vm = viewModel<SettingsReaderViewModel>()
val modeSettings by vm.modeSettings.collectAsState() val modeSettings by vm.modeSettings.collectAsState()
Column { Column {
Toolbar("Reader Settings", navController, true) Toolbar(stringResource("settings_reader"), navController, true)
LazyColumn { LazyColumn {
item { item {
ChoicePreference( ChoicePreference(
vm.selectedMode, vm.selectedMode,
vm.modes.collectAsState().value.associateWith { it }, vm.modes.collectAsState().value.associateWith { it },
"Reader Mode" stringResource("reader_mode")
) )
} }
item { item {
@@ -136,13 +139,13 @@ fun SettingsReaderScreen(navController: BackStack<Route>) {
ChoicePreference( ChoicePreference(
it.direction, it.direction,
vm.getDirectionChoices(), vm.getDirectionChoices(),
"Direction", stringResource("direction"),
enabled = !it.defaultMode enabled = !it.defaultMode
) )
SwitchPreference( SwitchPreference(
it.continuous, it.continuous,
"Continuous", stringResource("continuous"),
"If the reader is a pager or a scrolling window", stringResource("continuous_sub"),
enabled = !it.defaultMode enabled = !it.defaultMode
) )
val continuous by it.continuous.collectAsState() val continuous by it.continuous.collectAsState()
@@ -150,13 +153,13 @@ fun SettingsReaderScreen(navController: BackStack<Route>) {
ChoicePreference( ChoicePreference(
it.padding, it.padding,
vm.getPaddingChoices(), vm.getPaddingChoices(),
"Page Padding" stringResource("page_padding")
) )
val direction by it.direction.collectAsState() val direction by it.direction.collectAsState()
val (title, subtitle) = if (direction == Direction.Up || direction == Direction.Down) { val (title, subtitle) = if (direction == Direction.Up || direction == Direction.Down) {
"Force fit width" to "When the window's width is over the images width, scale the image to the window" stringResource("force_fit_width") to stringResource("force_fit_width_sub")
} else { } else {
"Force fit height" to "When the window's height is over the images height, scale the image to the window" stringResource("force_fit_height") to stringResource("force_fit_height_sub")
} }
SwitchPreference( SwitchPreference(
it.fitSize, it.fitSize,
@@ -165,9 +168,9 @@ fun SettingsReaderScreen(navController: BackStack<Route>) {
) )
val maxSize by it.maxSize.collectAsState() val maxSize by it.maxSize.collectAsState()
val (maxSizeTitle, maxSizeSubtitle) = if (direction == Direction.Up || direction == Direction.Down) { val (maxSizeTitle, maxSizeSubtitle) = if (direction == Direction.Up || direction == Direction.Down) {
"Max width" to "Width to restrict a image from going over, currently $maxSize" + "dp. Works with the above setting to scale images up but restrict them to a certain amount" stringResource("max_width") to stringResource("max_width_sub", maxSize.toString())
} else { } else {
"Max height" to "Height to restrict a image from going over, currently $maxSize" + "dp. Works with the above setting to scale images up but restrict them to a certain amount" stringResource("max_height") to stringResource("max_height_sub", maxSize.toString())
} }
ChoicePreference( ChoicePreference(
it.maxSize, it.maxSize,
@@ -179,13 +182,13 @@ fun SettingsReaderScreen(navController: BackStack<Route>) {
ChoicePreference( ChoicePreference(
it.imageScale, it.imageScale,
vm.getImageScaleChoices(), vm.getImageScaleChoices(),
"Image Scale" stringResource("image_scale")
) )
} }
ChoicePreference( ChoicePreference(
it.navigationMode, it.navigationMode,
vm.getNavigationModeChoices(), vm.getNavigationModeChoices(),
"Navigation mode" stringResource("navigation_mode")
) )
} }
} }

View File

@@ -20,94 +20,95 @@ import androidx.compose.material.icons.filled.Tune
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.prefs.PreferenceRow import ca.gosyer.ui.base.prefs.PreferenceRow
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import com.github.zsoltk.compose.router.BackStack import com.github.zsoltk.compose.router.BackStack
@Composable @Composable
fun SettingsScreen(navController: BackStack<Route>) { fun SettingsScreen(navController: BackStack<Route>) {
Column { Column {
Toolbar("Settings", closable = false) Toolbar(stringResource("location_settings"), closable = false)
LazyColumn { LazyColumn {
item { item {
PreferenceRow( PreferenceRow(
title = "General", title = stringResource("settings_general"),
icon = Icons.Default.Tune, icon = Icons.Default.Tune,
onClick = { navController.push(Route.SettingsGeneral) } onClick = { navController.push(Route.SettingsGeneral) }
) )
} }
item { item {
PreferenceRow( PreferenceRow(
title = "Appearance", title = stringResource("settings_appearance"),
icon = Icons.Default.Palette, icon = Icons.Default.Palette,
onClick = { navController.push(Route.SettingsAppearance) } onClick = { navController.push(Route.SettingsAppearance) }
) )
} }
item { item {
PreferenceRow( PreferenceRow(
title = "Server", title = stringResource("settings_server"),
icon = Icons.Default.Computer, icon = Icons.Default.Computer,
onClick = { navController.push(Route.SettingsServer) } onClick = { navController.push(Route.SettingsServer) }
) )
} }
item { item {
PreferenceRow( PreferenceRow(
title = "Library", title = stringResource("settings_library"),
icon = Icons.Default.CollectionsBookmark, icon = Icons.Default.CollectionsBookmark,
onClick = { navController.push(Route.SettingsLibrary) } onClick = { navController.push(Route.SettingsLibrary) }
) )
} }
item { item {
PreferenceRow( PreferenceRow(
title = "Reader", title = stringResource("settings_reader"),
icon = Icons.Default.ChromeReaderMode, icon = Icons.Default.ChromeReaderMode,
onClick = { navController.push(Route.SettingsReader) } onClick = { navController.push(Route.SettingsReader) }
) )
} }
/*item { /*item {
Pref( Pref(
title = "Downloads", title = stringResource("settings_download"),
icon = Icons.Default.GetApp, icon = Icons.Default.GetApp,
onClick = { navController.push(Route.SettingsDownloads) } onClick = { navController.push(Route.SettingsDownloads) }
) )
} }
item { item {
Pref( Pref(
title = "Tracking", title = stringResource("settings_tracking"),
icon = Icons.Default.Sync, icon = Icons.Default.Sync,
onClick = { navController.push(Route.SettingsTracking) } onClick = { navController.push(Route.SettingsTracking) }
) )
}*/ }*/
item { item {
PreferenceRow( PreferenceRow(
title = "Browse", title = stringResource("settings_browse"),
icon = Icons.Default.Explore, icon = Icons.Default.Explore,
onClick = { navController.push(Route.SettingsBrowse) } onClick = { navController.push(Route.SettingsBrowse) }
) )
} }
item { item {
PreferenceRow( PreferenceRow(
title = "Backup", title = stringResource("settings_backup"),
icon = Icons.Default.Backup, icon = Icons.Default.Backup,
onClick = { navController.push(Route.SettingsBackup) } onClick = { navController.push(Route.SettingsBackup) }
) )
} }
/*item { /*item {
Pref( Pref(
title = "Security", title = stringResource("settings_security"),
icon = Icons.Default.Security, icon = Icons.Default.Security,
onClick = { navController.push(Route.SettingsSecurity) } onClick = { navController.push(Route.SettingsSecurity) }
) )
} }
item { item {
Pref( Pref(
title = "Parental Controls", title = stringResource("settings_parental_controls"),
icon = Icons.Default.PeopleOutline, icon = Icons.Default.PeopleOutline,
onClick = { navController.push(Route.SettingsParentalControls) } onClick = { navController.push(Route.SettingsParentalControls) }
) )
}*/ }*/
item { item {
PreferenceRow( PreferenceRow(
title = "Advanced", title = stringResource("settings_advanced"),
icon = Icons.Default.Code, icon = Icons.Default.Code,
onClick = { navController.push(Route.SettingsAdvanced) } onClick = { navController.push(Route.SettingsAdvanced) }
) )

View File

@@ -10,13 +10,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import com.github.zsoltk.compose.router.BackStack import com.github.zsoltk.compose.router.BackStack
@Composable @Composable
fun SettingsSecurityScreen(navController: BackStack<Route>) { fun SettingsSecurityScreen(navController: BackStack<Route>) {
Column { Column {
Toolbar("Security Settings", navController, true) Toolbar(stringResource("settings_security_screen"), navController, true)
LazyColumn { LazyColumn {
} }
} }

View File

@@ -16,6 +16,7 @@ import ca.gosyer.ui.base.prefs.EditTextPreference
import ca.gosyer.ui.base.prefs.SwitchPreference import ca.gosyer.ui.base.prefs.SwitchPreference
import ca.gosyer.ui.base.prefs.asStateIn import ca.gosyer.ui.base.prefs.asStateIn
import ca.gosyer.ui.base.prefs.asStringStateIn import ca.gosyer.ui.base.prefs.asStringStateIn
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.ViewModel import ca.gosyer.ui.base.vm.ViewModel
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
@@ -34,14 +35,14 @@ class SettingsServerViewModel @Inject constructor(
fun SettingsServerScreen(navController: BackStack<Route>) { fun SettingsServerScreen(navController: BackStack<Route>) {
val vm = viewModel<SettingsServerViewModel>() val vm = viewModel<SettingsServerViewModel>()
Column { Column {
Toolbar("Server Settings", navController, true) Toolbar(stringResource("settings_server_screen"), navController, true)
SwitchPreference(preference = vm.host, title = "Host server inside TachideskJUI") SwitchPreference(preference = vm.host, title = stringResource("host_server"))
LazyColumn { LazyColumn {
item { item {
EditTextPreference(vm.server, "Server Url", subtitle = vm.server.collectAsState().value) EditTextPreference(vm.server, stringResource("server_url"), subtitle = vm.server.collectAsState().value)
} }
item { item {
EditTextPreference(vm.port, "Server Port", subtitle = vm.port.collectAsState().value) EditTextPreference(vm.port, stringResource("server_port"), subtitle = vm.port.collectAsState().value)
} }
} }
} }

View File

@@ -10,13 +10,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.main.Route import ca.gosyer.ui.main.Route
import com.github.zsoltk.compose.router.BackStack import com.github.zsoltk.compose.router.BackStack
@Composable @Composable
fun SettingsTrackingScreen(navController: BackStack<Route>) { fun SettingsTrackingScreen(navController: BackStack<Route>) {
Column { Column {
Toolbar("Tracking Settings", navController, true) Toolbar(stringResource("settings_tracking_screen"), navController, true)
LazyColumn { LazyColumn {
} }
} }

View File

@@ -33,6 +33,7 @@ import ca.gosyer.data.models.Source
import ca.gosyer.ui.base.components.KtorImage import ca.gosyer.ui.base.components.KtorImage
import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.components.Toolbar
import ca.gosyer.ui.base.components.combinedMouseClickable import ca.gosyer.ui.base.components.combinedMouseClickable
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.ui.manga.openMangaMenu import ca.gosyer.ui.manga.openMangaMenu
import ca.gosyer.ui.sources.components.SourceHomeScreen import ca.gosyer.ui.sources.components.SourceHomeScreen
@@ -75,7 +76,7 @@ fun SourcesMenu(bundle: Bundle, onMangaClick: (Long) -> Unit) {
Surface { Surface {
Column { Column {
Toolbar( Toolbar(
selectedSourceTab?.name ?: "Sources", selectedSourceTab?.name ?: stringResource("location_sources"),
closable = selectedSourceTab != null, closable = selectedSourceTab != null,
onClose = { onClose = {
selectedSourceTab?.let { vm.closeTab(it) } selectedSourceTab?.let { vm.closeTab(it) }
@@ -110,14 +111,14 @@ fun SourcesMenu(bundle: Bundle, onMangaClick: (Long) -> Unit) {
shape = RoundedCornerShape(4.dp), shape = RoundedCornerShape(4.dp),
elevation = 4.dp elevation = 4.dp
) { ) {
Text(source?.name ?: "Home", modifier = Modifier.padding(10.dp)) Text(source?.name ?: stringResource("sources_home"), modifier = Modifier.padding(10.dp))
} }
} }
) { ) {
if (source != null) { if (source != null) {
KtorImage(source.iconUrl(serverUrl), imageModifier = modifier) KtorImage(source.iconUrl(serverUrl), imageModifier = modifier)
} else { } else {
Icon(Icons.Default.Home, "Home", modifier = modifier) Icon(Icons.Default.Home, stringResource("sources_home"), modifier = modifier)
} }
} }
} }

View File

@@ -26,6 +26,7 @@ import ca.gosyer.data.models.Manga
import ca.gosyer.data.models.Source import ca.gosyer.data.models.Source
import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.components.LoadingScreen
import ca.gosyer.ui.base.components.MangaGridItem import ca.gosyer.ui.base.components.MangaGridItem
import ca.gosyer.ui.base.resources.stringResource
import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.base.vm.viewModel
import ca.gosyer.util.compose.persistentLazyListState import ca.gosyer.util.compose.persistentLazyListState
import com.github.zsoltk.compose.savedinstancestate.Bundle import com.github.zsoltk.compose.savedinstancestate.Bundle
@@ -97,7 +98,7 @@ private fun MangaTable(
enabled = !isLoading, enabled = !isLoading,
modifier = Modifier.align(Alignment.TopEnd) modifier = Modifier.align(Alignment.TopEnd)
) { ) {
Text(text = if (isLatest) "To Browse" else "To Latest") Text(text = stringResource(if (isLatest) "move_to_browse" else "move_to_latest"))
} }
} }
} }

View File

@@ -9,9 +9,13 @@ package ca.gosyer.util.compose
import androidx.compose.desktop.Window import androidx.compose.desktop.Window
import androidx.compose.desktop.WindowEvents import androidx.compose.desktop.WindowEvents
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.window.v1.MenuBar import androidx.compose.ui.window.v1.MenuBar
import ca.gosyer.common.di.AppScope
import ca.gosyer.data.translation.XmlResourceBundle
import ca.gosyer.ui.base.resources.LocalResources
import ca.gosyer.ui.base.theme.AppTheme import ca.gosyer.ui.base.theme.AppTheme
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
@@ -28,9 +32,14 @@ fun ThemedWindow(
onDismissRequest: (() -> Unit)? = null, onDismissRequest: (() -> Unit)? = null,
content: @Composable () -> Unit = { } content: @Composable () -> Unit = { }
) { ) {
val resources = AppScope.getInstance<XmlResourceBundle>()
Window(title, size, location, centered, icon, menuBar, undecorated, resizable, events, onDismissRequest) { Window(title, size, location, centered, icon, menuBar, undecorated, resizable, events, onDismissRequest) {
AppTheme { CompositionLocalProvider(
content() LocalResources provides resources
) {
AppTheme {
content()
}
} }
} }
} }

View File

@@ -0,0 +1,12 @@
<!--
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<resources>
<string name="color_primary">Colour primary</string>
<string name="color_secondary">Colour secondary</string>
<string name="action_favorite">Favourite</string>
<string name="action_remove_favorite">UnFavourite</string>
</resources>

View File

@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<resources>
<string name="app_name">TachideskJUI</string>
<!-- Main Menu -->
<string name="tachidesk_doesnt_exist">Tachidesk jar does not exist, run Tachidesk yourself</string>
<string name="unable_to_start_server">Unable to start server</string>
<!-- Actions -->
<string name="action_yes">Yes</string>
<string name="action_no">No</string>
<string name="action_cancel">Cancel</string>
<string name="action_delete">Delete</string>
<string name="action_start_anyway">Start anyway</string>
<string name="action_rename">Rename</string>
<string name="action_create">Create</string>
<string name="action_add">Add</string>
<string name="action_pause">Pause</string>
<string name="action_continue">Continue</string>
<string name="action_clear_queue">Clear queue</string>
<string name="action_move_to_bottom">Move to bottom</string>
<string name="action_install">Install</string>
<string name="action_uninstall">Uninstall</string>
<string name="action_update">Update</string>
<string name="action_toggle_read">Toggle read</string>
<string name="action_mark_previous_read">Mark previous as read</string>
<string name="action_toggle_bookmarked">Toggle bookmarked</string>
<string name="action_favorite">Favorite</string>
<string name="action_remove_favorite">UnFavorite</string>
<string name="action_refresh_manga">Refresh manga</string>
<!-- Locations -->
<string name="location_library">Library</string>
<string name="location_sources">Sources</string>
<string name="location_extensions">Extensions</string>
<string name="location_downloads">Downloads</string>
<string name="location_settings">Settings</string>
<string name="location_categories">Categories</string>
<string name="location_manga">Manga</string>
<!-- Categories Menu -->
<string name="categories_delete_confirm">Do you wish to delete the category %1$s?</string>
<!-- Downloads Menu -->
<string name="downloads_remaining">%1$s remaining</string>
<!-- Extensions Menu -->
<string name="enabled_languages">Enabled languages</string>
<!-- Library Menu -->
<string name="default_category">Default</string>
<!-- Manga Menu -->
<string name="page_progress">Page %1$s</string>
<!-- Sources Menu -->
<string name="sources_home">Home</string>
<string name="move_to_browse">To Browse</string>
<string name="move_to_latest">To Latest</string>
<!-- Reader Menu -->
<string name="no_pages_found">No pages found</string>
<string name="no_previous_chapter">There is no previous chapter</string>
<string name="previous_chapter">Previous:\n %1$s</string>
<string name="next_chapter">Next:\n %1$s</string>
<string name="no_next_chapter">There is no next chapter</string>
<!-- Settings-->
<string name="settings_advanced">Advanced</string>
<string name="settings_appearance">Appearance</string>
<string name="settings_backup">Backup</string>
<string name="settings_browse">Browse</string>
<string name="settings_download">Download</string>
<string name="settings_general">General</string>
<string name="settings_library">Library</string>
<string name="settings_parental_controls">Parental Controls</string>
<string name="settings_reader">Reader</string>
<string name="settings_security">Security</string>
<string name="settings_server">Server</string>
<string name="settings_tracking">Tracking</string>
<!-- Settings Menu Titles-->
<string name="settings_advanced_screen">Advanced Settings</string>
<string name="settings_appearance_screen">Appearance Settings</string>
<string name="settings_backup_screen">Backup Settings</string>
<string name="settings_browse_screen">Browse Settings</string>
<string name="settings_download_screen">Download Settings</string>
<string name="settings_general_screen">General Settings</string>
<string name="settings_library_screen">Library Settings</string>
<string name="settings_parental_control_screen">Parental Control Settings</string>
<string name="settings_reader_screen">Reader Settings</string>
<string name="settings_security_screen">Security Settings</string>
<string name="settings_server_screen">Server Settings</string>
<string name="settings_tracking_screen">Tracking Settings</string>
<!-- Appearance Settings -->
<string name="theme">Theme</string>
<string name="theme_light">Light</string>
<string name="theme_Dark">Light</string>
<string name="theme_text">Text</string>
<string name="preset_themes">Preset themes</string>
<string name="color_primary">Color primary</string>
<string name="color_primary_sub">Displayed most frequently across your app</string>
<string name="color_secondary">Color secondary</string>
<string name="color_secondary_sub">Accents select parts of the UI</string>
<!-- Backup Settings -->
<string name="backup_restore">Restore Backup</string>
<string name="backup_restore_sub">Restore a backup into Tachidesk</string>
<string name="backup_create">Create Backup</string>
<string name="backup_create_sub">Create a backup from Tachidesk</string>
<!-- General Settings -->
<string name="start_screen">Start Screen</string>
<string name="confirm_exit">Confirm Exit</string>
<string name="language">Language</string>
<string name="language_system_default">System Default (%1$s)</string>
<string name="date_format">Date Format</string>
<string name="date_system_default">System Default</string>
<!-- Library Settings -->
<string name="show_all_category">Show all category</string>
<!-- Reader Settings -->
<string name="reader_mode">Reader Mode</string>
<string name="direction">Direction</string>
<string name="dir_down">Down</string>
<string name="dir_rtl">RTL</string>
<string name="dir_ltr">LTR</string>
<string name="dir_up">Up</string>
<string name="continuous">Continuous</string>
<string name="continuous_sub">If the reader is a pager or a scrolling window</string>
<string name="page_padding">Page Padding</string>
<string name="page_padding_none">None</string>
<string name="force_fit_width">Force fit width</string>
<string name="force_fit_width_sub">When the window's width is over the images width, scale the image to the window</string>
<string name="force_fit_height">Force fit height</string>
<string name="force_fit_height_sub">When the window's height is over the images height, scale the image to the window</string>
<string name="max_width">Max width</string>
<string name="max_width_sub">Width to restrict a image from going over, currently %1$sdp. Works with the above setting to scale images up but restrict them to a certain amount</string>
<string name="max_height">Max height</string>
<string name="max_height_sub">Height to restrict a image from going over, currently %1$sdp. Works with the above setting to scale images up but restrict them to a certain amount</string>
<string name="max_size_unrestricted">Unrestricted</string>
<string name="image_scale">Image Scale</string>
<string name="scale_fit_screen">Fit Screen</string>
<string name="scale_stretch">Stretch</string>
<string name="scale_fit_width">Fit Width</string>
<string name="scale_fit_height">Fit Height</string>
<string name="scale_original">Original Size</string>
<string name="scale_smart">Smart Fit</string>
<string name="navigation_mode">Navigation mode</string>
<string name="nav_l_shaped">L shaped</string>
<string name="nav_kindle_ish">Kindle-ish</string>
<string name="nav_edge">Edge</string>
<string name="nav_left_right">Right and Left</string>
<!-- Server Settings -->
<string name="host_server">Host server inside TachideskJUI</string>
<string name="server_url">Server Url</string>
<string name="server_port">Server Port</string>
</resources>