chore: merge upstream

Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
KaiserBh 2024-01-07 10:04:34 +11:00
commit 95b294ebd6
No known key found for this signature in database
GPG Key ID: 14D73B142042BBA9
130 changed files with 1977 additions and 1323 deletions

View File

@ -3,7 +3,7 @@
I acknowledge that:
- I have updated:
- To the latest version of the app (stable is v0.14.7)
- To the latest version of the app (stable is v0.15.0)
- All extensions
- I have gone through the FAQ (https://tachiyomi.org/docs/faq/general) and troubleshooting guide (https://tachiyomi.org/docs/guides/troubleshooting/)
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions

View File

@ -53,7 +53,7 @@ body:
label: Tachiyomi version
description: You can find your Tachiyomi version in **More → About**.
placeholder: |
Example: "0.14.7"
Example: "0.15.0"
validations:
required: true
@ -98,7 +98,7 @@ body:
required: true
- label: I have gone through the [FAQ](https://tachiyomi.org/docs/faq/general) and [troubleshooting guide](https://tachiyomi.org/docs/guides/troubleshooting/).
required: true
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
- label: I have updated the app to version **[0.15.0](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
required: true
- label: I have updated all installed extensions.
required: true

View File

@ -33,7 +33,7 @@ body:
required: true
- label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
required: true
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
- label: I have updated the app to version **[0.15.0](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true

View File

@ -22,8 +22,8 @@ android {
defaultConfig {
applicationId = "eu.kanade.tachiyomi"
versionCode = 113
versionName = "0.14.7"
versionCode = 115
versionName = "0.15.0"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")

View File

@ -11,8 +11,11 @@ import eu.kanade.domain.manga.interactor.GetExcludedScanlators
import eu.kanade.domain.manga.interactor.SetExcludedScanlators
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.source.interactor.CreateSourceRepo
import eu.kanade.domain.source.interactor.DeleteSourceRepo
import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
import eu.kanade.domain.source.interactor.GetSourceRepos
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.interactor.ToggleLanguage
@ -167,5 +170,9 @@ class DomainModule : InjektModule {
addFactory { ToggleLanguage(get()) }
addFactory { ToggleSource(get()) }
addFactory { ToggleSourcePin(get()) }
addFactory { CreateSourceRepo(get()) }
addFactory { DeleteSourceRepo(get()) }
addFactory { GetSourceRepos(get()) }
}
}

View File

@ -0,0 +1,26 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.preference.plusAssign
class CreateSourceRepo(private val preferences: SourcePreferences) {
fun await(name: String): Result {
// Do not allow invalid formats
if (!name.matches(repoRegex) || name.startsWith(OFFICIAL_REPO_BASE_URL)) {
return Result.InvalidUrl
}
preferences.extensionRepos() += name.substringBeforeLast("/index.min.json")
return Result.Success
}
sealed interface Result {
data object InvalidUrl : Result
data object Success : Result
}
}
const val OFFICIAL_REPO_BASE_URL = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo"
private val repoRegex = """^https://.*/index\.min\.json$""".toRegex()

View File

@ -0,0 +1,11 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.preference.minusAssign
class DeleteSourceRepo(private val preferences: SourcePreferences) {
fun await(repo: String) {
preferences.extensionRepos() -= repo
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class GetSourceRepos(private val preferences: SourcePreferences) {
fun subscribe(): Flow<List<String>> {
return preferences.extensionRepos().changes()
.map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) }
}
}

View File

@ -38,6 +38,8 @@ class SourcePreferences(
SetMigrateSorting.Direction.ASCENDING,
)
fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet())
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
fun trustedSignatures() = preferenceStore.getStringSet(Preference.appStateKey("trusted_signatures"), emptySet())

View File

@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.AlertDialog
@ -38,6 +37,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
@ -67,7 +67,6 @@ fun ExtensionDetailsScreen(
state: ExtensionDetailsScreenModel.State,
onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickWhatsNew: () -> Unit,
onClickReadme: () -> Unit,
onClickEnableAll: () -> Unit,
onClickDisableAll: () -> Unit,
onClickClearCookies: () -> Unit,
@ -91,13 +90,6 @@ fun ExtensionDetailsScreen(
onClick = onClickWhatsNew,
),
)
add(
AppBar.Action(
title = stringResource(MR.strings.action_faq_and_guides),
icon = Icons.AutoMirrored.Outlined.HelpOutline,
onClick = onClickReadme,
),
)
}
addAll(
listOf(
@ -125,7 +117,7 @@ fun ExtensionDetailsScreen(
) { paddingValues ->
if (state.extension == null) {
EmptyScreen(
stringRes = MR.strings.empty_screen,
MR.strings.empty_screen,
modifier = Modifier.padding(paddingValues),
)
return@Scaffold
@ -158,6 +150,21 @@ private fun ExtensionDetails(
contentPadding = contentPadding,
) {
when {
extension.isRepoSource ->
item {
val uriHandler = LocalUriHandler.current
WarningBanner(
MR.strings.repo_extension_message,
modifier = Modifier.clickable {
extension.repoUrl ?: return@clickable
uriHandler.openUri(
extension.repoUrl
.replace("https://raw.githubusercontent.com", "https://github.com")
.removeSuffix("/repo/"),
)
},
)
}
extension.isUnofficial ->
item {
WarningBanner(MR.strings.unofficial_extension_message)

View File

@ -28,6 +28,7 @@ import androidx.compose.ui.focus.focusRequester
import eu.kanade.core.preference.asToggleableState
import eu.kanade.presentation.category.visualName
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import tachiyomi.core.preference.CheckboxState
import tachiyomi.domain.category.model.Category
@ -40,12 +41,12 @@ import kotlin.time.Duration.Companion.seconds
fun CategoryCreateDialog(
onDismissRequest: () -> Unit,
onCreate: (String) -> Unit,
categories: ImmutableList<Category>,
categories: ImmutableList<String>,
) {
var name by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val nameAlreadyExists = remember(name) { categories.anyWithName(name) }
val nameAlreadyExists = remember(name) { categories.contains(name) }
AlertDialog(
onDismissRequest = onDismissRequest,
@ -70,10 +71,13 @@ fun CategoryCreateDialog(
},
text = {
OutlinedTextField(
modifier = Modifier.focusRequester(focusRequester),
modifier = Modifier
.focusRequester(focusRequester),
value = name,
onValueChange = { name = it },
label = { Text(text = stringResource(MR.strings.name)) },
label = {
Text(text = stringResource(MR.strings.name))
},
supportingText = {
val msgRes = if (name.isNotEmpty() && nameAlreadyExists) {
MR.strings.error_category_exists
@ -99,14 +103,14 @@ fun CategoryCreateDialog(
fun CategoryRenameDialog(
onDismissRequest: () -> Unit,
onRename: (String) -> Unit,
categories: ImmutableList<Category>,
category: Category,
categories: ImmutableList<String>,
category: String,
) {
var name by remember { mutableStateOf(category.name) }
var name by remember { mutableStateOf(category) }
var valueHasChanged by remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() }
val nameAlreadyExists = remember(name) { categories.anyWithName(name) }
val nameAlreadyExists = remember(name) { categories.contains(name) }
AlertDialog(
onDismissRequest = onDismissRequest,
@ -163,7 +167,7 @@ fun CategoryRenameDialog(
fun CategoryDeleteDialog(
onDismissRequest: () -> Unit,
onDelete: () -> Unit,
category: Category,
category: String,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
@ -184,7 +188,7 @@ fun CategoryDeleteDialog(
Text(text = stringResource(MR.strings.delete_category))
},
text = {
Text(text = stringResource(MR.strings.delete_category_confirmation, category.name))
Text(text = stringResource(MR.strings.delete_category_confirmation, category))
},
)
}
@ -220,7 +224,7 @@ fun CategorySortAlphabeticallyDialog(
@Composable
fun ChangeCategoryDialog(
initialSelection: List<CheckboxState<Category>>,
initialSelection: ImmutableList<CheckboxState<Category>>,
onDismissRequest: () -> Unit,
onEditCategories: () -> Unit,
onConfirm: (List<Long>, List<Long>) -> Unit,
@ -292,7 +296,7 @@ fun ChangeCategoryDialog(
if (index != -1) {
val mutableList = selection.toMutableList()
mutableList[index] = it.next()
selection = mutableList.toList()
selection = mutableList.toList().toImmutableList()
}
}
Row(
@ -326,7 +330,3 @@ fun ChangeCategoryDialog(
},
)
}
private fun List<Category>.anyWithName(name: String): Boolean {
return any { name == it.name }
}

View File

@ -49,7 +49,7 @@ fun CategoryListItem(
),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = "")
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null)
Text(
text = category.name,
modifier = Modifier
@ -61,13 +61,13 @@ fun CategoryListItem(
onClick = { onMoveUp(category) },
enabled = canMoveUp,
) {
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = "")
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = null)
}
IconButton(
onClick = { onMoveDown(category) },
enabled = canMoveDown,
) {
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = null)
}
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = onRename) {

View File

@ -78,12 +78,13 @@ import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.source.local.isLocal
import java.time.Instant
@Composable
fun MangaScreen(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
nextUpdate: Instant?,
isTabletUi: Boolean,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
@ -138,7 +139,7 @@ fun MangaScreen(
MangaScreenSmallImpl(
state = state,
snackbarHostState = snackbarHostState,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
onBackClicked = onBackClicked,
@ -175,7 +176,7 @@ fun MangaScreen(
snackbarHostState = snackbarHostState,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
onBackClicked = onBackClicked,
onChapterClicked = onChapterClicked,
onDownloadChapter = onDownloadChapter,
@ -211,7 +212,7 @@ fun MangaScreen(
private fun MangaScreenSmallImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
nextUpdate: Instant?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit,
@ -272,10 +273,7 @@ private fun MangaScreenSmallImpl(
onBackClicked()
}
}
BackHandler(
enabled = isAnySelected,
onBack = { onAllChapterSelected(false) },
)
BackHandler(onBack = internalOnBackPressed)
Scaffold(
topBar = {
@ -402,7 +400,7 @@ private fun MangaScreenSmallImpl(
MangaActionRow(
favorite = state.manga.favorite,
trackingCount = state.trackingCount,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
isUserIntervalMode = state.manga.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,
@ -462,7 +460,7 @@ private fun MangaScreenSmallImpl(
fun MangaScreenLargeImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
fetchInterval: Int?,
nextUpdate: Instant?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit,
@ -529,10 +527,7 @@ fun MangaScreenLargeImpl(
onBackClicked()
}
}
BackHandler(
enabled = isAnySelected,
onBack = { onAllChapterSelected(false) },
)
BackHandler(onBack = internalOnBackPressed)
Scaffold(
topBar = {
@ -641,7 +636,7 @@ fun MangaScreenLargeImpl(
MangaActionRow(
favorite = state.manga.favorite,
trackingCount = state.trackingCount,
fetchInterval = fetchInterval,
nextUpdate = nextUpdate,
isUserIntervalMode = state.manga.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,

View File

@ -3,10 +3,6 @@ package eu.kanade.presentation.manga.components
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.os.Build
import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
@ -29,18 +25,15 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
@ -55,13 +48,11 @@ import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.manga.EditCoverAction
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
import kotlinx.collections.immutable.persistentListOf
import soup.compose.material.motion.MotionConstants
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clickableNoIndication
import kotlin.coroutines.cancellation.CancellationException
@Composable
fun MangaCoverDialog(
@ -160,32 +151,10 @@ fun MangaCoverDialog(
val statusBarPaddingPx = with(LocalDensity.current) { contentPadding.calculateTopPadding().roundToPx() }
val bottomPaddingPx = with(LocalDensity.current) { contentPadding.calculateBottomPadding().roundToPx() }
var scale by remember { mutableFloatStateOf(1f) }
PredictiveBackHandler { progress ->
try {
progress.collect { backEvent ->
scale = lerp(1f, 0.8f, LinearOutSlowInEasing.transform(backEvent.progress))
}
onDismissRequest()
} catch (e: CancellationException) {
animate(
initialValue = scale,
targetValue = 1f,
animationSpec = tween(durationMillis = MotionConstants.DefaultMotionDuration),
) { value, _ ->
scale = value
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.clickableNoIndication(onClick = onDismissRequest)
.graphicsLayer {
scaleX = scale
scaleY = scale
},
.clickableNoIndication(onClick = onDismissRequest),
) {
AndroidView(
factory = {

View File

@ -2,8 +2,11 @@ package eu.kanade.presentation.manga.components
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@ -16,10 +19,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.util.system.isDevFlavor
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import kotlinx.collections.immutable.toImmutableList
import tachiyomi.domain.manga.interactor.FetchInterval
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.WheelTextPicker
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import java.time.Instant
@ -59,37 +65,50 @@ fun DeleteChaptersDialog(
@Composable
fun SetIntervalDialog(
interval: Int,
nextUpdate: Long,
nextUpdate: Instant?,
onDismissRequest: () -> Unit,
onValueChanged: (Int) -> Unit,
onValueChanged: ((Int) -> Unit)? = null,
) {
var selectedInterval by rememberSaveable { mutableIntStateOf(if (interval < 0) -interval else 0) }
val nextUpdateDays = remember(nextUpdate) {
return@remember if (nextUpdate != null) {
val now = Instant.now()
val nextUpdateInstant = Instant.ofEpochMilli(nextUpdate)
now.until(nextUpdateInstant, ChronoUnit.DAYS)
now.until(nextUpdate, ChronoUnit.DAYS).toInt().coerceAtLeast(0)
} else {
null
}
}
AlertDialog(
onDismissRequest = onDismissRequest,
title = { Text(stringResource(MR.strings.manga_modify_calculated_interval_title)) },
title = { Text(stringResource(MR.strings.pref_library_update_smart_update)) },
text = {
Column {
if (nextUpdateDays >= 0) {
if (nextUpdateDays != null && nextUpdateDays >= 0) {
Text(
stringResource(
MR.strings.manga_interval_expected_update,
pluralStringResource(
MR.plurals.day,
count = nextUpdateDays.toInt(),
count = nextUpdateDays,
nextUpdateDays,
),
pluralStringResource(
MR.plurals.day,
count = interval,
interval,
),
),
)
Spacer(Modifier.height(MaterialTheme.padding.small))
}
// TODO: selecting "1" then doesn't allow for future changes unless defaulting first?
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
Text(stringResource(MR.strings.manga_interval_custom_amount))
BoxWithConstraints(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
@ -112,6 +131,7 @@ fun SetIntervalDialog(
)
}
}
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
@ -120,7 +140,7 @@ fun SetIntervalDialog(
},
confirmButton = {
TextButton(onClick = {
onValueChanged(selectedInterval)
onValueChanged?.invoke(selectedInterval)
onDismissRequest()
}) {
Text(text = stringResource(MR.strings.action_ok))

View File

@ -86,7 +86,8 @@ import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clickableNoIndication
import tachiyomi.presentation.core.util.secondaryItemAlpha
import kotlin.math.absoluteValue
import java.time.Instant
import java.time.temporal.ChronoUnit
import kotlin.math.roundToInt
private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE))
@ -165,7 +166,7 @@ fun MangaInfoBox(
fun MangaActionRow(
favorite: Boolean,
trackingCount: Int,
fetchInterval: Int?,
nextUpdate: Instant?,
isUserIntervalMode: Boolean,
onAddToLibraryClicked: () -> Unit,
onWebViewClicked: (() -> Unit)?,
@ -177,6 +178,16 @@ fun MangaActionRow(
) {
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
// TODO: show something better when using custom interval
val nextUpdateDays = remember(nextUpdate) {
return@remember if (nextUpdate != null) {
val now = Instant.now()
now.until(nextUpdate, ChronoUnit.DAYS).toInt().coerceAtLeast(0)
} else {
null
}
}
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
MangaActionButton(
title = if (favorite) {
@ -189,18 +200,20 @@ fun MangaActionRow(
onClick = onAddToLibraryClicked,
onLongClick = onEditCategory,
)
if (onEditIntervalClicked != null && fetchInterval != null) {
MangaActionButton(
title = pluralStringResource(
title = if (nextUpdateDays != null) {
pluralStringResource(
MR.plurals.day,
count = fetchInterval.absoluteValue,
fetchInterval.absoluteValue,
),
count = nextUpdateDays,
nextUpdateDays,
)
} else {
stringResource(MR.strings.not_applicable)
},
icon = Icons.Default.HourglassEmpty,
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
onClick = onEditIntervalClicked,
onClick = { onEditIntervalClicked?.invoke() },
)
}
MangaActionButton(
title = if (trackingCount == 0) {
stringResource(MR.strings.manga_tracking_tab)

View File

@ -17,7 +17,7 @@ import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.tooling.preview.PreviewLightDark
import com.halilibo.richtext.markdown.Markdown
import com.halilibo.richtext.ui.RichTextStyle
import com.halilibo.richtext.ui.material3.Material3RichText
import com.halilibo.richtext.ui.material3.RichText
import com.halilibo.richtext.ui.string.RichTextStringStyle
import eu.kanade.presentation.theme.TachiyomiTheme
import tachiyomi.i18n.MR
@ -42,7 +42,7 @@ fun NewUpdateScreen(
rejectText = stringResource(MR.strings.action_not_now),
onRejectClick = onRejectUpdate,
) {
Material3RichText(
RichText(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = MaterialTheme.padding.large),

View File

@ -41,6 +41,7 @@ internal class GuidesStep(
}
HorizontalDivider(
modifier = Modifier.padding(vertical = 8.dp),
color = MaterialTheme.colorScheme.onPrimaryContainer,
)

View File

@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -14,6 +15,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.more.settings.screen.SettingsDataScreen
import eu.kanade.tachiyomi.util.system.toast
@ -38,6 +40,8 @@ internal class StorageStep : OnboardingStep {
@Composable
override fun Content() {
val context = LocalContext.current
val handler = LocalUriHandler.current
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
Column(
@ -64,6 +68,19 @@ internal class StorageStep : OnboardingStep {
) {
Text(stringResource(MR.strings.onboarding_storage_action_select))
}
HorizontalDivider(
modifier = Modifier.padding(vertical = 8.dp),
color = MaterialTheme.colorScheme.onPrimaryContainer,
)
Text(stringResource(MR.strings.onboarding_storage_help_info, stringResource(MR.strings.app_name)))
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { handler.openUri("https://tachiyomi.org/docs/faq/storage") },
) {
Text(stringResource(MR.strings.onboarding_storage_help_action))
}
}
LaunchedEffect(Unit) {

View File

@ -1,34 +1,26 @@
package eu.kanade.presentation.more.settings.screen
import android.app.Activity
import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.core.app.ActivityCompat
import androidx.core.os.LocaleListCompat
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.appearance.AppLanguageScreen
import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableMap
import org.xmlpull.v1.XmlPullParser
import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
@ -107,11 +99,8 @@ object SettingsAppearanceScreen : SearchableSettings {
uiPreferences: UiPreferences,
): Preference.PreferenceGroup {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val langs = remember { getLangs(context) }
var currentLanguage by remember {
mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "")
}
val now = remember { Instant.now().toEpochMilli() }
val dateFormat by uiPreferences.dateFormat().collectAsState()
@ -119,26 +108,12 @@ object SettingsAppearanceScreen : SearchableSettings {
UiPreferences.dateFormat(dateFormat).format(now)
}
LaunchedEffect(currentLanguage) {
val locale = if (currentLanguage.isEmpty()) {
LocaleListCompat.getEmptyLocaleList()
} else {
LocaleListCompat.forLanguageTags(currentLanguage)
}
AppCompatDelegate.setApplicationLocales(locale)
}
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_display),
preferenceItems = persistentListOf(
Preference.PreferenceItem.BasicListPreference(
value = currentLanguage,
Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_app_language),
entries = langs,
onValueChanged = { newValue ->
currentLanguage = newValue
true
},
onClick = { navigator.push(AppLanguageScreen()) },
),
Preference.PreferenceItem.ListPreference(
pref = uiPreferences.tabletUiMode(),
@ -173,30 +148,6 @@ object SettingsAppearanceScreen : SearchableSettings {
),
)
}
private fun getLangs(context: Context): ImmutableMap<String, String> {
val langs = mutableListOf<Pair<String, String>>()
val parser = context.resources.getXml(R.xml.locales_config)
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && parser.name == "locale") {
for (i in 0..<parser.attributeCount) {
if (parser.getAttributeName(i) == "name") {
val langTag = parser.getAttributeValue(i)
val displayName = LocaleHelper.getDisplayName(langTag)
if (displayName.isNotEmpty()) {
langs.add(Pair(langTag, displayName))
}
}
}
}
eventType = parser.next()
}
langs.sortBy { it.second }
langs.add(0, Pair("", context.stringResource(MR.strings.label_default)))
return langs.toMap().toImmutableMap()
}
}
private val DateFormats = listOf(

View File

@ -2,16 +2,22 @@ package eu.kanade.presentation.more.settings.screen
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.fragment.app.FragmentActivity
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -24,7 +30,11 @@ object SettingsBrowseScreen : SearchableSettings {
@Composable
override fun getPreferences(): List<Preference> {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
val reposCount by sourcePreferences.extensionRepos().collectAsState()
return listOf(
Preference.PreferenceGroup(
title = stringResource(MR.strings.label_sources),
@ -33,6 +43,13 @@ object SettingsBrowseScreen : SearchableSettings {
pref = sourcePreferences.hideInLibraryItems(),
title = stringResource(MR.strings.pref_hide_in_library_items),
),
Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.label_extension_repos),
subtitle = pluralStringResource(MR.plurals.num_repos, reposCount.size, reposCount.size),
onClick = {
navigator.push(ExtensionReposScreen())
},
),
),
),
Preference.PreferenceGroup(

View File

@ -198,7 +198,7 @@ object SettingsLibraryScreen : SearchableSettings {
),
Preference.PreferenceItem.MultiSelectListPreference(
pref = libraryPreferences.autoUpdateMangaRestrictions(),
title = stringResource(MR.strings.pref_library_update_manga_restriction),
title = stringResource(MR.strings.pref_library_update_smart_update),
entries = persistentMapOf(
MANGA_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read),
MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started),

View File

@ -0,0 +1,128 @@
package eu.kanade.presentation.more.settings.screen.appearance
import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.core.os.LocaleListCompat
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import org.xmlpull.v1.XmlPullParser
import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import java.util.Locale
class AppLanguageScreen : Screen() {
@Composable
override fun Content() {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val langs = remember { getLangs(context) }
var currentLanguage by remember {
mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "")
}
LaunchedEffect(currentLanguage) {
val locale = if (currentLanguage.isEmpty()) {
LocaleListCompat.getEmptyLocaleList()
} else {
LocaleListCompat.forLanguageTags(currentLanguage)
}
AppCompatDelegate.setApplicationLocales(locale)
}
Scaffold(
topBar = { scrollBehavior ->
AppBar(
title = stringResource(MR.strings.pref_app_language),
navigateUp = navigator::pop,
scrollBehavior = scrollBehavior,
)
},
) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding),
) {
items(langs) {
ListItem(
modifier = Modifier.clickable {
currentLanguage = it.langTag
},
headlineContent = { Text(it.displayName) },
supportingContent = {
it.localizedDisplayName?.let {
Text(it)
}
},
trailingContent = {
if (currentLanguage == it.langTag) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
)
}
},
)
}
}
}
}
private fun getLangs(context: Context): ImmutableList<Language> {
val langs = mutableListOf<Language>()
val parser = context.resources.getXml(R.xml.locales_config)
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && parser.name == "locale") {
for (i in 0..<parser.attributeCount) {
if (parser.getAttributeName(i) == "name") {
val langTag = parser.getAttributeValue(i)
val displayName = LocaleHelper.getDisplayName(langTag)
if (displayName.isNotEmpty()) {
langs.add(Language(langTag, displayName, Locale.forLanguageTag(langTag).displayName))
}
}
}
}
eventType = parser.next()
}
langs.sortBy { it.displayName }
langs.add(0, Language("", context.stringResource(MR.strings.label_default), null))
return langs.toImmutableList()
}
private data class Language(
val langTag: String,
val displayName: String,
val localizedDisplayName: String?,
)
}

View File

@ -0,0 +1,69 @@
package eu.kanade.presentation.more.settings.screen.browse
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoCreateDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoDeleteDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionReposScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
import tachiyomi.presentation.core.screens.LoadingScreen
class ExtensionReposScreen : Screen() {
@Composable
override fun Content() {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { ExtensionReposScreenModel() }
val state by screenModel.state.collectAsState()
if (state is RepoScreenState.Loading) {
LoadingScreen()
return
}
val successState = state as RepoScreenState.Success
ExtensionReposScreen(
state = successState,
onClickCreate = { screenModel.showDialog(RepoDialog.Create) },
onClickDelete = { screenModel.showDialog(RepoDialog.Delete(it)) },
navigateUp = navigator::pop,
)
when (val dialog = successState.dialog) {
null -> {}
RepoDialog.Create -> {
ExtensionRepoCreateDialog(
onDismissRequest = screenModel::dismissDialog,
onCreate = { screenModel.createRepo(it) },
categories = successState.repos,
)
}
is RepoDialog.Delete -> {
ExtensionRepoDeleteDialog(
onDismissRequest = screenModel::dismissDialog,
onDelete = { screenModel.deleteRepo(dialog.repo) },
repo = dialog.repo,
)
}
}
LaunchedEffect(Unit) {
screenModel.events.collectLatest { event ->
if (event is RepoEvent.LocalizedMessage) {
context.toast(event.stringRes)
}
}
}
}
}

View File

@ -0,0 +1,111 @@
package eu.kanade.presentation.more.settings.screen.browse
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.source.interactor.CreateSourceRepo
import eu.kanade.domain.source.interactor.DeleteSourceRepo
import eu.kanade.domain.source.interactor.GetSourceRepos
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import tachiyomi.core.util.lang.launchIO
import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ExtensionReposScreenModel(
private val getSourceRepos: GetSourceRepos = Injekt.get(),
private val createSourceRepo: CreateSourceRepo = Injekt.get(),
private val deleteSourceRepo: DeleteSourceRepo = Injekt.get(),
) : StateScreenModel<RepoScreenState>(RepoScreenState.Loading) {
private val _events: Channel<RepoEvent> = Channel(Int.MAX_VALUE)
val events = _events.receiveAsFlow()
init {
screenModelScope.launchIO {
getSourceRepos.subscribe()
.collectLatest { repos ->
mutableState.update {
RepoScreenState.Success(
repos = repos.toImmutableList(),
)
}
}
}
}
/**
* Creates and adds a new repo to the database.
*
* @param name The name of the repo to create.
*/
fun createRepo(name: String) {
screenModelScope.launchIO {
when (createSourceRepo.await(name)) {
is CreateSourceRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl)
else -> {}
}
}
}
/**
* Deletes the given repo from the database.
*
* @param repo The repo to delete.
*/
fun deleteRepo(repo: String) {
screenModelScope.launchIO {
deleteSourceRepo.await(repo)
}
}
fun showDialog(dialog: RepoDialog) {
mutableState.update {
when (it) {
RepoScreenState.Loading -> it
is RepoScreenState.Success -> it.copy(dialog = dialog)
}
}
}
fun dismissDialog() {
mutableState.update {
when (it) {
RepoScreenState.Loading -> it
is RepoScreenState.Success -> it.copy(dialog = null)
}
}
}
}
sealed class RepoEvent {
sealed class LocalizedMessage(val stringRes: StringResource) : RepoEvent()
data object InvalidUrl : LocalizedMessage(MR.strings.invalid_repo_name)
}
sealed class RepoDialog {
data object Create : RepoDialog()
data class Delete(val repo: String) : RepoDialog()
}
sealed class RepoScreenState {
@Immutable
data object Loading : RepoScreenState()
@Immutable
data class Success(
val repos: ImmutableList<String>,
val dialog: RepoDialog? = null,
) : RepoScreenState() {
val isEmpty: Boolean
get() = repos.isEmpty()
}
}

View File

@ -0,0 +1,81 @@
package eu.kanade.presentation.more.settings.screen.browse.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Label
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import kotlinx.collections.immutable.ImmutableList
import tachiyomi.presentation.core.components.material.padding
@Composable
fun ExtensionReposContent(
repos: ImmutableList<String>,
lazyListState: LazyListState,
paddingValues: PaddingValues,
onClickDelete: (String) -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
modifier = modifier,
) {
items(repos) { repo ->
ExtensionRepoListItem(
modifier = Modifier.animateItemPlacement(),
repo = repo,
onDelete = { onClickDelete(repo) },
)
}
}
}
@Composable
private fun ExtensionRepoListItem(
repo: String,
onDelete: () -> Unit,
modifier: Modifier = Modifier,
) {
ElevatedCard(
modifier = modifier,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
start = MaterialTheme.padding.medium,
top = MaterialTheme.padding.medium,
end = MaterialTheme.padding.medium,
),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null)
Text(text = repo, modifier = Modifier.padding(start = MaterialTheme.padding.medium))
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
) {
IconButton(onClick = onDelete) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = null)
}
}
}
}

View File

@ -0,0 +1,117 @@
package eu.kanade.presentation.more.settings.screen.browse.components
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.delay
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import kotlin.time.Duration.Companion.seconds
@Composable
fun ExtensionRepoCreateDialog(
onDismissRequest: () -> Unit,
onCreate: (String) -> Unit,
categories: ImmutableList<String>,
) {
var name by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val nameAlreadyExists = remember(name) { categories.contains(name) }
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(
enabled = name.isNotEmpty() && !nameAlreadyExists,
onClick = {
onCreate(name)
onDismissRequest()
},
) {
Text(text = stringResource(MR.strings.action_add))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(MR.strings.action_cancel))
}
},
title = {
Text(text = stringResource(MR.strings.action_add_repo))
},
text = {
Column {
Text(text = stringResource(MR.strings.action_add_repo_message))
OutlinedTextField(
modifier = Modifier
.focusRequester(focusRequester),
value = name,
onValueChange = { name = it },
label = {
Text(text = stringResource(MR.strings.label_add_repo_input))
},
supportingText = {
val msgRes = if (name.isNotEmpty() && nameAlreadyExists) {
MR.strings.error_repo_exists
} else {
MR.strings.information_required_plain
}
Text(text = stringResource(msgRes))
},
isError = name.isNotEmpty() && nameAlreadyExists,
singleLine = true,
)
}
},
)
LaunchedEffect(focusRequester) {
// TODO: https://issuetracker.google.com/issues/204502668
delay(0.1.seconds)
focusRequester.requestFocus()
}
}
@Composable
fun ExtensionRepoDeleteDialog(
onDismissRequest: () -> Unit,
onDelete: () -> Unit,
repo: String,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = {
onDelete()
onDismissRequest()
}) {
Text(text = stringResource(MR.strings.action_ok))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(MR.strings.action_cancel))
}
},
title = {
Text(text = stringResource(MR.strings.action_delete_repo))
},
text = {
Text(text = stringResource(MR.strings.delete_repo_confirmation, repo))
},
)
}

View File

@ -0,0 +1,61 @@
@file:JvmName("ExtensionReposScreenKt")
package eu.kanade.presentation.more.settings.screen.browse.components
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.more.settings.screen.browse.RepoScreenState
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.util.plus
@Composable
fun ExtensionReposScreen(
state: RepoScreenState.Success,
onClickCreate: () -> Unit,
onClickDelete: (String) -> Unit,
navigateUp: () -> Unit,
) {
val lazyListState = rememberLazyListState()
Scaffold(
topBar = { scrollBehavior ->
AppBar(
navigateUp = navigateUp,
title = stringResource(MR.strings.label_extension_repos),
scrollBehavior = scrollBehavior,
)
},
floatingActionButton = {
CategoryFloatingActionButton(
lazyListState = lazyListState,
onCreate = onClickCreate,
)
},
) { paddingValues ->
if (state.isEmpty) {
EmptyScreen(
MR.strings.information_empty_repos,
modifier = Modifier.padding(paddingValues),
)
return@Scaffold
}
ExtensionReposContent(
repos = state.repos,
lazyListState = lazyListState,
paddingValues = paddingValues + topSmallPaddingValues +
PaddingValues(horizontal = MaterialTheme.padding.medium),
onClickDelete = onClickDelete,
)
}
}

View File

@ -1,54 +1,13 @@
package eu.kanade.presentation.util
import android.annotation.SuppressLint
import androidx.activity.BackEventCompat
import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.tween
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlurEffect
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.util.lerp
import androidx.compose.ui.zIndex
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.ScreenModelStore
import cafe.adriel.voyager.core.screen.Screen
@ -57,25 +16,14 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey
import cafe.adriel.voyager.core.stack.StackEvent
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.ScreenTransitionContent
import eu.kanade.tachiyomi.util.view.getWindowRadius
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import soup.compose.material.motion.MotionConstants
import soup.compose.material.motion.animation.materialSharedAxisX
import soup.compose.material.motion.animation.rememberSlideDistance
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.PI
import kotlin.math.sin
/**
* For invoking back press to the parent activity
@ -109,299 +57,17 @@ interface AssistContentScreen {
}
@Composable
fun DefaultNavigatorScreenTransition(
navigator: Navigator,
modifier: Modifier = Modifier,
) {
val scope = rememberCoroutineScope()
val view = LocalView.current
val handler = remember {
OnBackHandler(
scope = scope,
windowCornerRadius = view.getWindowRadius(),
onBackPressed = navigator::pop,
)
}
PredictiveBackHandler(enabled = navigator.canPop) { progress ->
progress
.onStart { handler.reset() }
.onCompletion { e ->
if (e == null) {
handler.onBackConfirmed()
} else {
handler.onBackCancelled()
}
}
.collect(handler::onBackEvent)
}
Box(modifier = modifier.onSizeChanged { handler.updateContainerSize(it.toSize()) }) {
val currentSceneEntry = navigator.lastItem
val showPrev by remember {
derivedStateOf { handler.scale < 1f || handler.translationY != 0f }
}
val visibleItems = remember(currentSceneEntry, showPrev) {
if (showPrev) {
val prevSceneEntry = navigator.items.getOrNull(navigator.size - 2)
listOfNotNull(currentSceneEntry, prevSceneEntry)
} else {
listOfNotNull(currentSceneEntry)
}
}
fun DefaultNavigatorScreenTransition(navigator: Navigator) {
val slideDistance = rememberSlideDistance()
val screenContent = remember {
movableContentOf<Screen> { screen ->
navigator.saveableState("transition", screen) {
screen.Content()
}
}
}
visibleItems.forEachIndexed { index, backStackEntry ->
val isPrev = index == 1 && visibleItems.size > 1
if (!isPrev) {
AnimatedContent(
targetState = backStackEntry,
transitionSpec = {
val forward = navigator.lastEvent != StackEvent.Pop
if (!forward && !handler.isReady) {
// Pop screen without animation when predictive back is in use
EnterTransition.None togetherWith ExitTransition.None
} else {
ScreenTransition(
navigator = navigator,
transition = {
materialSharedAxisX(
forward = forward,
forward = navigator.lastEvent != StackEvent.Pop,
slideDistance = slideDistance,
)
}
},
modifier = Modifier
.zIndex(1f)
.graphicsLayer {
this.alpha = handler.alpha
this.transformOrigin = TransformOrigin(
pivotFractionX = if (handler.swipeEdge == BackEventCompat.EDGE_LEFT) 0.8f else 0.2f,
pivotFractionY = 0.5f,
)
this.scaleX = handler.scale
this.scaleY = handler.scale
this.translationY = handler.translationY
this.clip = true
this.shape = if (showPrev) {
RoundedCornerShape(handler.windowCornerRadius.toFloat())
} else {
RectangleShape
}
}
.then(
if (showPrev) {
Modifier.pointerInput(Unit) {
// Animated content should not be interactive
}
} else {
Modifier
},
),
content = {
if (visibleItems.size == 2 && visibleItems.getOrNull(1) == it) {
// Avoid drawing previous screen
return@AnimatedContent
}
screenContent(it)
},
)
} else {
Box(
modifier = Modifier
.zIndex(0f)
.drawWithCache {
val bounds = Rect(Offset.Zero, size)
val matrix = ColorMatrix().apply {
// Reduce saturation and brightness
setToSaturation(lerp(1f, 0.95f, handler.alpha))
set(0, 4, lerp(0f, -25f, handler.alpha))
set(1, 4, lerp(0f, -25f, handler.alpha))
set(2, 4, lerp(0f, -25f, handler.alpha))
}
val paint = Paint().apply { colorFilter = ColorFilter.colorMatrix(matrix) }
onDrawWithContent {
drawIntoCanvas {
it.saveLayer(bounds, paint)
drawContent()
it.restore()
}
}
}
.graphicsLayer {
val blurRadius = 5.dp.toPx() * handler.alpha
renderEffect = if (blurRadius > 0f) {
BlurEffect(blurRadius, blurRadius)
} else {
null
}
}
.pointerInput(Unit) {
// bg content should not be interactive
},
content = { screenContent(backStackEntry) },
)
}
}
LaunchedEffect(currentSceneEntry) {
// Reset *after* the screen is popped successfully
// so that the correct transition is applied
handler.setReady()
}
}
}
@Stable
private class OnBackHandler(
private val scope: CoroutineScope,
val windowCornerRadius: Int,
private val onBackPressed: () -> Unit,
) {
var isReady = true
private set
var alpha by mutableFloatStateOf(1f)
private set
var scale by mutableFloatStateOf(1f)
private set
var translationY by mutableFloatStateOf(0f)
private set
var swipeEdge by mutableIntStateOf(BackEventCompat.EDGE_LEFT)
private set
private var containerSize = Size.Zero
private var startPointY = Float.NaN
var isPredictiveBack by mutableStateOf(false)
private set
private var animationJob: Job? = null
set(value) {
isReady = false
field = value
}
fun updateContainerSize(size: Size) {
containerSize = size
}
fun setReady() {
reset()
animationJob?.cancel()
animationJob = null
isReady = true
isPredictiveBack = false
}
fun reset() {
startPointY = Float.NaN
}
fun onBackEvent(backEvent: BackEventCompat) {
if (!isReady) return
isPredictiveBack = true
swipeEdge = backEvent.swipeEdge
val progress = LinearOutSlowInEasing.transform(backEvent.progress)
scale = lerp(1f, 0.85f, progress)
if (startPointY.isNaN()) {
startPointY = backEvent.touchY
}
val deltaYRatio = (backEvent.touchY - startPointY) / containerSize.height
val translateYDistance = containerSize.height / 20
translationY = sin(deltaYRatio * PI * 0.5).toFloat() * translateYDistance * progress
}
fun onBackConfirmed() {
if (!isReady) return
if (isPredictiveBack) {
// Continue predictive animation and pop the screen
val animationSpec = tween<Float>(
durationMillis = MotionConstants.DefaultMotionDuration,
easing = FastOutSlowInEasing,
)
animationJob = scope.launch {
try {
listOf(
async {
animate(
initialValue = alpha,
targetValue = 0f,
animationSpec = animationSpec,
) { value, _ ->
alpha = value
}
},
async {
animate(
initialValue = scale,
targetValue = scale - 0.05f,
animationSpec = animationSpec,
) { value, _ ->
scale = value
}
},
).awaitAll()
} catch (e: CancellationException) {
// no-op
} finally {
onBackPressed()
alpha = 1f
translationY = 0f
scale = 1f
}
}
} else {
// Pop right away and use default transition
onBackPressed()
}
}
fun onBackCancelled() {
// Reset states
isPredictiveBack = false
animationJob = scope.launch {
listOf(
async {
animate(
initialValue = scale,
targetValue = 1f,
) { value, _ ->
scale = value
}
},
async {
animate(
initialValue = alpha,
targetValue = 1f,
) { value, _ ->
alpha = value
}
},
async {
animate(
initialValue = translationY,
targetValue = 0f,
) { value, _ ->
translationY = value
}
},
).awaitAll()
isReady = true
}
}
}
@Composable

View File

@ -405,6 +405,11 @@ object Migrations {
// Deleting old download cache index files, but might as well clear it all out
context.cacheDir.deleteRecursively()
}
if (oldVersion < 114) {
sourcePreferences.extensionRepos().getAndSet {
it.map { "https://raw.githubusercontent.com/$it/repo" }.toSet()
}
}
return true
}

View File

@ -20,6 +20,7 @@ import com.google.api.services.drive.DriveScopes
import com.google.api.services.drive.model.File
import eu.kanade.tachiyomi.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@ -32,6 +33,7 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.time.Instant
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
@ -57,12 +59,50 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
private val remoteFileName = "tachiyomi_sync_data.gz"
private val lockFileName = "tachiyomi_sync.lock"
private val googleDriveService = GoogleDriveService(context)
override suspend fun beforeSync() = googleDriveService.refreshToken()
override suspend fun beforeSync() {
googleDriveService.refreshToken()
val drive = googleDriveService.driveService ?: throw Exception("Google Drive service not initialized")
var backoff = 2000L // Start with 2 seconds
while (true) {
val lockFiles = findLockFile(drive) // Fetch the current list of lock files
when {
lockFiles.isEmpty() -> {
// No lock file exists, try to create a new one
createLockFile(drive)
}
lockFiles.size == 1 -> {
// Exactly one lock file exists
val lockFile = lockFiles.first()
val createdTime = Instant.parse(lockFile.createdTime.toString())
val ageMinutes = java.time.Duration.between(createdTime, Instant.now()).toMinutes()
if (ageMinutes <= 3) {
// Lock file is new and presumably held by this process, break the loop to proceed
break
} else {
// Lock file is old, delete and attempt to create a new one
deleteLockFile(drive)
createLockFile(drive)
}
}
else -> {
// More than one lock file exists, likely due to a race condition
delay(backoff) // Apply backoff strategy
backoff = (backoff * 2).coerceAtMost(32000L) // Max backoff of 32 seconds
}
}
// The loop continues until it can confirm that there's exactly one new lock file.
}
}
override suspend fun pullSyncData(): SyncData? {
val drive = googleDriveService.googleDriveService
val drive = googleDriveService.driveService
// Check if the Google Drive service is initialized
if (drive == null) {
@ -89,27 +129,11 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
override suspend fun pushSyncData(syncData: SyncData) {
val jsonData = json.encodeToString(syncData)
val drive = googleDriveService.driveService ?: throw Exception(context.getString(R.string.google_drive_not_signed_in))
val drive = googleDriveService.googleDriveService
// Check if the Google Drive service is initialized
if (drive == null) {
logcat(LogPriority.ERROR) { "Google Drive service not initialized" }
throw Exception(context.getString(R.string.google_drive_not_signed_in))
}
// delete file if exists
val fileList = getFileList(drive)
if (fileList.isNotEmpty()) {
drive.files().delete(fileList[0].id).execute()
}
val fileMetadata = File()
fileMetadata.name = remoteFileName
fileMetadata.mimeType = "application/gzip"
val byteArrayOutputStream = ByteArrayOutputStream()
withContext(Dispatchers.IO) {
val gzipOutputStream = GZIPOutputStream(byteArrayOutputStream)
gzipOutputStream.write(jsonData.toByteArray(Charsets.UTF_8))
@ -117,24 +141,100 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
}
val byteArrayContent = ByteArrayContent("application/octet-stream", byteArrayOutputStream.toByteArray())
try {
if (fileList.isNotEmpty()) {
// File exists, so update it
val fileId = fileList[0].id
drive.files().update(fileId, null, byteArrayContent).execute()
logcat(LogPriority.DEBUG) { "Updated existing sync data file in Google Drive with file ID: $fileId" }
} else {
// File doesn't exist, so create it
val fileMetadata = File().apply {
name = remoteFileName
mimeType = "application/gzip"
}
val uploadedFile = drive.files().create(fileMetadata, byteArrayContent)
.setFields("id")
.execute()
logcat(LogPriority.DEBUG) { "Created sync data file in Google Drive with file ID: ${uploadedFile.id}" }
logcat(
LogPriority.DEBUG,
) { "Created new sync data file in Google Drive with file ID: ${uploadedFile.id}" }
}
// Data has been successfully pushed or updated, delete the lock file
deleteLockFile(drive)
} catch (e: Exception) {
logcat(LogPriority.ERROR) { "Failed to push or update sync data: ${e.message}" }
}
}
private fun getFileList(drive: Drive): MutableList<File> {
try {
// Search for the existing file by name
val query = "mimeType='application/gzip' and trashed = false and name = '$remoteFileName'"
val fileList = drive.files().list().setQ(query).execute().files
Log.d("GoogleDrive", "File list: $fileList")
return fileList
} catch (e: Exception) {
Log.e("GoogleDrive", "Error no sync data found: ${e.message}")
return mutableListOf()
}
}
private fun createLockFile(drive: Drive) {
try {
val fileMetadata = File()
fileMetadata.name = lockFileName
fileMetadata.mimeType = "text/plain"
// Create an empty content to upload as the lock file
val emptyContent = ByteArrayContent.fromString("text/plain", "")
val file = drive.files().create(fileMetadata, emptyContent)
.setFields("id, name, createdTime")
.execute()
Log.d("GoogleDrive", "Created lock file with ID: ${file.id}")
} catch (e: Exception) {
Log.e("GoogleDrive", "Error creating lock file: ${e.message}")
}
}
private fun findLockFile(drive: Drive): MutableList<File> {
try {
val query = "mimeType='text/plain' and trashed = false and name = '$lockFileName'"
val fileList = drive.files().list().setQ(query).setFields("files(id, name, createdTime)").execute().files
Log.d("GoogleDrive", "Lock file search result: $fileList")
return fileList
} catch (e: Exception) {
Log.e("GoogleDrive", "Error finding lock file: ${e.message}")
return mutableListOf()
}
}
private fun deleteLockFile(drive: Drive) {
try {
val lockFiles = findLockFile(drive)
if (lockFiles.isNotEmpty()) {
for (file in lockFiles) {
drive.files().delete(file.id).execute()
Log.d("GoogleDrive", "Deleted lock file with ID: ${file.id}")
}
} else {
Log.d("GoogleDrive", "No lock file found to delete.")
}
} catch (e: Exception) {
Log.e("GoogleDrive", "Error deleting lock file: ${e.message}")
throw Exception(context.getString(R.string.error_deleting_google_drive_lock_file))
}
}
suspend fun deleteSyncDataFromGoogleDrive(): DeleteSyncDataStatus {
val drive = googleDriveService.googleDriveService
val drive = googleDriveService.driveService
if (drive == null) {
logcat(LogPriority.ERROR) { "Google Drive service not initialized" }
@ -160,7 +260,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
}
class GoogleDriveService(private val context: Context) {
var googleDriveService: Drive? = null
var driveService: Drive? = null
companion object {
const val REDIRECT_URI = "eu.kanade.google.oauth:/oauth2redirect"
}
@ -179,7 +279,7 @@ class GoogleDriveService(private val context: Context) {
val refreshToken = syncPreferences.googleDriveRefreshToken().get()
if (accessToken == "" || refreshToken == "") {
googleDriveService = null
driveService = null
return
}
@ -296,7 +396,7 @@ class GoogleDriveService(private val context: Context) {
credential.accessToken = accessToken
credential.refreshToken = refreshToken
googleDriveService = Drive.Builder(
driveService = Drive.Builder(
NetHttpTransport(),
jsonFactory,
credential,

View File

@ -3,7 +3,11 @@ package eu.kanade.tachiyomi.data.sync.service
import android.content.Context
import eu.kanade.tachiyomi.data.sync.SyncNotifier
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.PATCH
import eu.kanade.tachiyomi.network.POST
import kotlinx.coroutines.delay
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import logcat.LogPriority
@ -22,6 +26,108 @@ class SyncYomiSyncService(
syncPreferences: SyncPreferences,
private val notifier: SyncNotifier,
) : SyncService(context, json, syncPreferences) {
@Serializable
enum class SyncStatus {
@SerialName("pending")
Pending,
@SerialName("syncing")
Syncing,
@SerialName("success")
Success,
}
@Serializable
data class LockFile(
@SerialName("id")
val id: Int?,
@SerialName("user_api_key")
val userApiKey: String?,
@SerialName("acquired_by")
val acquiredBy: String?,
@SerialName("last_synced")
val lastSynced: String?,
@SerialName("status")
val status: SyncStatus,
@SerialName("acquired_at")
val acquiredAt: String?,
@SerialName("expires_at")
val expiresAt: String?,
)
@Serializable
data class LockfileCreateRequest(
@SerialName("acquired_by")
val acquiredBy: String,
)
@Serializable
data class LockfilePatchRequest(
@SerialName("user_api_key")
val userApiKey: String,
@SerialName("acquired_by")
val acquiredBy: String,
)
override suspend fun beforeSync() {
val host = syncPreferences.syncHost().get()
val apiKey = syncPreferences.syncAPIKey().get()
val lockFileApi = "$host/api/sync/lock"
val deviceId = syncPreferences.uniqueDeviceID()
val client = OkHttpClient()
val headers = Headers.Builder().add("X-API-Token", apiKey).build()
val json = Json { ignoreUnknownKeys = true }
val createLockfileRequest = LockfileCreateRequest(deviceId)
val createLockfileJson = json.encodeToString(createLockfileRequest)
val patchRequest = LockfilePatchRequest(apiKey, deviceId)
val patchJson = json.encodeToString(patchRequest)
val lockFileRequest = GET(
url = lockFileApi,
headers = headers,
)
val lockFileCreate = POST(
url = lockFileApi,
headers = headers,
body = createLockfileJson.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()),
)
val lockFileUpdate = PATCH(
url = lockFileApi,
headers = headers,
body = patchJson.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()),
)
// create lock file first
client.newCall(lockFileCreate).execute()
// update lock file acquired_by
client.newCall(lockFileUpdate).execute()
var backoff = 2000L // Start with 2 seconds
val maxBackoff = 32000L // Maximum backoff time e.g., 32 seconds
var lockFile: LockFile
do {
val response = client.newCall(lockFileRequest).execute()
val responseBody = response.body.string()
lockFile = json.decodeFromString<LockFile>(responseBody)
logcat(LogPriority.DEBUG) { "SyncYomi lock file status: ${lockFile.status}" }
if (lockFile.status != SyncStatus.Success) {
logcat(LogPriority.DEBUG) { "Lock file not ready, retrying in $backoff ms..." }
delay(backoff)
backoff = (backoff * 2).coerceAtMost(maxBackoff)
}
} while (lockFile.status != SyncStatus.Success)
// update lock file acquired_by
client.newCall(lockFileUpdate).execute()
}
override suspend fun pullSyncData(): SyncData? {
val host = syncPreferences.syncHost().get()
val apiKey = syncPreferences.syncAPIKey().get()

View File

@ -28,6 +28,7 @@ import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML
import tachiyomi.core.storage.AndroidStorageFolderProvider
import tachiyomi.core.storage.UniFileTempFileManager
import tachiyomi.data.AndroidDatabaseHandler
import tachiyomi.data.Database
import tachiyomi.data.DatabaseHandler
@ -112,6 +113,8 @@ class AppModule(val app: Application) : InjektModule {
ProtoBuf
}
addSingletonFactory { UniFileTempFileManager(app) }
addSingletonFactory { ChapterCache(app, get()) }
addSingletonFactory { CoverCache(app) }

View File

@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.extension
import android.content.Context
import android.graphics.drawable.Drawable
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
import eu.kanade.tachiyomi.extension.api.ExtensionApi
import eu.kanade.tachiyomi.extension.api.ExtensionUpdateNotifier
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep
@ -49,7 +49,7 @@ class ExtensionManager(
/**
* API where all the available extensions can be found.
*/
private val api = ExtensionGithubApi()
private val api = ExtensionApi()
/**
* The installer which installs, updates and uninstalls the extensions.
@ -258,7 +258,6 @@ class ExtensionManager(
val untrustedSignatures = _untrustedExtensionsFlow.value.map { it.signatureHash }.toSet()
if (signature !in untrustedSignatures) return
ExtensionLoader.trustedSignatures += signature
preferences.trustedSignatures() += signature
val nowTrustedExtensions = _untrustedExtensionsFlow.value.filter { it.signatureHash == signature }

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.extension.api
import android.content.Context
import eu.kanade.domain.source.interactor.OFFICIAL_REPO_BASE_URL
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.LoadResult
@ -20,10 +22,11 @@ import uy.kohesive.injekt.injectLazy
import java.time.Instant
import kotlin.time.Duration.Companion.days
internal class ExtensionGithubApi {
internal class ExtensionApi {
private val networkService: NetworkHelper by injectLazy()
private val preferenceStore: PreferenceStore by injectLazy()
private val sourcePreferences: SourcePreferences by injectLazy()
private val extensionManager: ExtensionManager by injectLazy()
private val json: Json by injectLazy()
@ -31,39 +34,16 @@ internal class ExtensionGithubApi {
preferenceStore.getLong(Preference.appStateKey("last_ext_check"), 0)
}
private var requiresFallbackSource = false
suspend fun findExtensions(): List<Extension.Available> {
return withIOContext {
val githubResponse = if (requiresFallbackSource) {
null
} else {
try {
networkService.client
.newCall(GET("${REPO_URL_PREFIX}index.min.json"))
.awaitSuccess()
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Failed to get extensions from GitHub" }
requiresFallbackSource = true
null
}
}
val response = githubResponse ?: run {
networkService.client
.newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json"))
.awaitSuccess()
}
val extensions = with(json) {
response
.parseAs<List<ExtensionJsonObject>>()
.toExtensions()
val extensions = buildList {
addAll(getExtensions(OFFICIAL_REPO_BASE_URL, true))
sourcePreferences.extensionRepos().get().map { addAll(getExtensions(it, false)) }
}
// Sanity check - a small number of extensions probably means something broke
// with the repo generator
if (extensions.size < 100) {
if (extensions.size < 50) {
throw Exception()
}
@ -71,6 +51,26 @@ internal class ExtensionGithubApi {
}
}
private suspend fun getExtensions(
repoBaseUrl: String,
isOfficialRepo: Boolean,
): List<Extension.Available> {
return try {
val response = networkService.client
.newCall(GET("$repoBaseUrl/index.min.json"))
.awaitSuccess()
with(json) {
response
.parseAs<List<ExtensionJsonObject>>()
.toExtensions(repoBaseUrl, isRepoSource = !isOfficialRepo)
}
} catch (e: Throwable) {
logcat(LogPriority.ERROR, e) { "Failed to get extensions from $repoBaseUrl" }
emptyList()
}
}
suspend fun checkForUpdates(
context: Context,
fromAvailableExtensionList: Boolean = false,
@ -111,7 +111,10 @@ internal class ExtensionGithubApi {
return extensionsWithUpdate
}
private fun List<ExtensionJsonObject>.toExtensions(): List<Extension.Available> {
private fun List<ExtensionJsonObject>.toExtensions(
repoUrl: String,
isRepoSource: Boolean,
): List<Extension.Available> {
return this
.filter {
val libVersion = it.extractLibVersion()
@ -126,25 +129,17 @@ internal class ExtensionGithubApi {
libVersion = it.extractLibVersion(),
lang = it.lang,
isNsfw = it.nsfw == 1,
hasReadme = it.hasReadme == 1,
hasChangelog = it.hasChangelog == 1,
sources = it.sources?.map(extensionSourceMapper).orEmpty(),
apkName = it.apk,
iconUrl = "${getUrlPrefix()}icon/${it.pkg}.png",
iconUrl = "$repoUrl/icon/${it.pkg}.png",
repoUrl = repoUrl,
isRepoSource = isRepoSource,
)
}
}
fun getApkUrl(extension: Extension.Available): String {
return "${getUrlPrefix()}apk/${extension.apkName}"
}
private fun getUrlPrefix(): String {
return if (requiresFallbackSource) {
FALLBACK_REPO_URL_PREFIX
} else {
REPO_URL_PREFIX
}
return "${extension.repoUrl}/apk/${extension.apkName}"
}
private fun ExtensionJsonObject.extractLibVersion(): Double {
@ -152,9 +147,6 @@ internal class ExtensionGithubApi {
}
}
private const val REPO_URL_PREFIX = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/"
private const val FALLBACK_REPO_URL_PREFIX = "https://gcore.jsdelivr.net/gh/tachiyomiorg/tachiyomi-extensions@repo/"
@Serializable
private data class ExtensionJsonObject(
val name: String,
@ -164,8 +156,6 @@ private data class ExtensionJsonObject(
val code: Long,
val version: String,
val nsfw: Int,
val hasReadme: Int = 0,
val hasChangelog: Int = 0,
val sources: List<ExtensionSourceJsonObject>?,
)

View File

@ -13,8 +13,6 @@ sealed class Extension {
abstract val libVersion: Double
abstract val lang: String?
abstract val isNsfw: Boolean
abstract val hasReadme: Boolean
abstract val hasChangelog: Boolean
data class Installed(
override val name: String,
@ -24,8 +22,6 @@ sealed class Extension {
override val libVersion: Double,
override val lang: String,
override val isNsfw: Boolean,
override val hasReadme: Boolean,
override val hasChangelog: Boolean,
val pkgFactory: String?,
val sources: List<Source>,
val icon: Drawable?,
@ -33,6 +29,8 @@ sealed class Extension {
val isObsolete: Boolean = false,
val isUnofficial: Boolean = false,
val isShared: Boolean,
val repoUrl: String? = null,
val isRepoSource: Boolean = false,
) : Extension()
data class Available(
@ -43,11 +41,11 @@ sealed class Extension {
override val libVersion: Double,
override val lang: String,
override val isNsfw: Boolean,
override val hasReadme: Boolean,
override val hasChangelog: Boolean,
val sources: List<Source>,
val apkName: String,
val iconUrl: String,
val repoUrl: String,
val isRepoSource: Boolean,
) : Extension() {
data class Source(
@ -75,7 +73,5 @@ sealed class Extension {
val signatureHash: String,
override val lang: String? = null,
override val isNsfw: Boolean = false,
override val hasReadme: Boolean = false,
override val hasChangelog: Boolean = false,
) : Extension()
}

View File

@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.util.lang.Hash
import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo
import eu.kanade.tachiyomi.util.system.isDevFlavor
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
@ -62,11 +63,6 @@ internal object ExtensionLoader {
// inorichi's key
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
/**
* List of the trusted signatures.
*/
var trustedSignatures = mutableSetOf(officialSignature) + preferences.trustedSignatures().get()
private const val PRIVATE_EXTENSION_EXTENSION = "ext"
private fun getPrivateExtensionDir(context: Context) = File(context.filesDir, "exts")
@ -123,6 +119,12 @@ internal object ExtensionLoader {
* @param context The application context.
*/
fun loadExtensions(context: Context): List<LoadResult> {
// Always make users trust unknown extensions on cold starts in non-dev builds
// due to inherent security risks
if (!isDevFlavor) {
preferences.trustedSignatures().delete()
}
val pkgManager = context.packageManager
val installedPkgs = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@ -329,8 +331,6 @@ internal object ExtensionLoader {
libVersion = libVersion,
lang = lang,
isNsfw = isNsfw,
hasReadme = hasReadme,
hasChangelog = hasChangelog,
sources = sources,
pkgFactory = appInfo.metaData.getString(METADATA_SOURCE_FACTORY),
isUnofficial = !isOfficiallySigned(signatures),
@ -394,6 +394,11 @@ internal object ExtensionLoader {
}
private fun hasTrustedSignature(signatures: List<String>): Boolean {
if (officialSignature in signatures) {
return true
}
val trustedSignatures = preferences.trustedSignatures().get()
return trustedSignatures.any { signatures.contains(it) }
}

View File

@ -37,7 +37,6 @@ data class ExtensionDetailsScreen(
state = state,
onClickSourcePreferences = { navigator.push(SourcePreferencesScreen(it)) },
onClickWhatsNew = { uriHandler.openUri(screenModel.getChangelogUrl()) },
onClickReadme = { uriHandler.openUri(screenModel.getReadmeUrl()) },
onClickEnableAll = { screenModel.toggleSources(true) },
onClickDisableAll = { screenModel.toggleSources(false) },
onClickClearCookies = screenModel::clearCookies,

View File

@ -31,8 +31,6 @@ import uy.kohesive.injekt.api.get
private const val URL_EXTENSION_COMMITS =
"https://github.com/tachiyomiorg/tachiyomi-extensions/commits/master"
private const val URL_EXTENSION_BLOB =
"https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master"
class ExtensionDetailsScreenModel(
pkgName: String,
@ -93,26 +91,11 @@ class ExtensionDetailsScreenModel(
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
val pkgFactory = extension.pkgFactory
if (extension.hasChangelog) {
return createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/CHANGELOG.md")
}
// Falling back on GitHub commit history because there is no explicit changelog in extension
return createUrl(URL_EXTENSION_COMMITS, pkgName, pkgFactory)
}
fun getReadmeUrl(): String {
val extension = state.value.extension ?: return ""
if (!extension.hasReadme) {
return "https://tachiyomi.org/docs/faq/browse/extensions"
}
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
val pkgFactory = extension.pkgFactory
return createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/README.md")
}
fun clearCookies() {
val extension = state.value.extension ?: return

View File

@ -24,6 +24,8 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
@ -265,7 +267,10 @@ class BrowseSourceScreenModel(
else -> {
val preselectedIds = getCategories.await(manga.id).map { it.id }
setDialog(
Dialog.ChangeMangaCategory(manga, categories.mapAsCheckboxState { it.id in preselectedIds }),
Dialog.ChangeMangaCategory(
manga,
categories.mapAsCheckboxState { it.id in preselectedIds }.toImmutableList(),
),
)
}
}
@ -338,7 +343,7 @@ class BrowseSourceScreenModel(
data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
data class ChangeMangaCategory(
val manga: Manga,
val initialSelection: List<CheckboxState.State<Category>>,
val initialSelection: ImmutableList<CheckboxState.State<Category>>,
) : Dialog
data class Migrate(val newManga: Manga) : Dialog
}

View File

@ -5,6 +5,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.util.fastMap
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@ -15,6 +16,7 @@ import eu.kanade.presentation.category.components.CategoryRenameDialog
import eu.kanade.presentation.category.components.CategorySortAlphabeticallyDialog
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.collectLatest
import tachiyomi.presentation.core.screens.LoadingScreen
@ -52,22 +54,22 @@ class CategoryScreen : Screen() {
CategoryCreateDialog(
onDismissRequest = screenModel::dismissDialog,
onCreate = screenModel::createCategory,
categories = successState.categories,
categories = successState.categories.fastMap { it.name }.toImmutableList(),
)
}
is CategoryDialog.Rename -> {
CategoryRenameDialog(
onDismissRequest = screenModel::dismissDialog,
onRename = { screenModel.renameCategory(dialog.category, it) },
categories = successState.categories,
category = dialog.category,
categories = successState.categories.fastMap { it.name }.toImmutableList(),
category = dialog.category.name,
)
}
is CategoryDialog.Delete -> {
CategoryDeleteDialog(
onDismissRequest = screenModel::dismissDialog,
onDelete = { screenModel.deleteCategory(dialog.category.id) },
category = dialog.category,
category = dialog.category.name,
)
}
is CategoryDialog.SortAlphabetically -> {

View File

@ -1,11 +1,8 @@
package eu.kanade.tachiyomi.ui.home
import androidx.activity.compose.PredictiveBackHandler
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.togetherWith
@ -26,20 +23,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.lerp
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
@ -59,7 +49,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import soup.compose.material.motion.MotionConstants
import soup.compose.material.motion.animation.materialFadeThroughIn
import soup.compose.material.motion.animation.materialFadeThroughOut
import tachiyomi.domain.library.service.LibraryPreferences
@ -70,7 +59,6 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.pluralStringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.coroutines.cancellation.CancellationException
object HomeScreen : Screen() {
@ -92,8 +80,6 @@ object HomeScreen : Screen() {
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
var scale by remember { mutableFloatStateOf(1f) }
TabNavigator(
tab = LibraryTab,
key = TabNavigatorKey,
@ -132,11 +118,6 @@ object HomeScreen : Screen() {
) { contentPadding ->
Box(
modifier = Modifier
.graphicsLayer {
scaleX = scale
scaleY = scale
transformOrigin = TransformOrigin(0.5f, 1f)
}
.padding(contentPadding)
.consumeWindowInsets(contentPadding),
) {
@ -157,30 +138,10 @@ object HomeScreen : Screen() {
}
val goToLibraryTab = { tabNavigator.current = LibraryTab }
var handlingBack by remember { mutableStateOf(false) }
PredictiveBackHandler(enabled = handlingBack || tabNavigator.current != LibraryTab) { progress ->
handlingBack = true
val currentTab = tabNavigator.current
try {
progress.collect { backEvent ->
scale = lerp(1f, 0.92f, LinearOutSlowInEasing.transform(backEvent.progress))
tabNavigator.current = if (backEvent.progress > 0.25f) tabs[0] else currentTab
}
goToLibraryTab()
} catch (e: CancellationException) {
tabNavigator.current = currentTab
} finally {
animate(
initialValue = scale,
targetValue = 1f,
animationSpec = tween(durationMillis = MotionConstants.DefaultMotionDuration),
) { value, _ ->
scale = value
}
handlingBack = false
}
}
BackHandler(
enabled = tabNavigator.current != LibraryTab,
onBack = goToLibraryTab,
)
LaunchedEffect(Unit) {
launch {

View File

@ -28,9 +28,11 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.getNextUnread
import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.mutate
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
@ -661,13 +663,15 @@ class LibraryScreenModel(
val common = getCommonCategories(mangaList)
// Get indexes of the mix categories to preselect.
val mix = getMixCategories(mangaList)
val preselected = categories.map {
val preselected = categories
.map {
when (it) {
in common -> CheckboxState.State.Checked(it)
in mix -> CheckboxState.TriState.Exclude(it)
else -> CheckboxState.State.None(it)
}
}
.toImmutableList()
mutableState.update { it.copy(dialog = Dialog.ChangeCategory(mangaList, preselected)) }
}
}
@ -683,7 +687,10 @@ class LibraryScreenModel(
sealed interface Dialog {
data object SettingsSheet : Dialog
data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class ChangeCategory(
val manga: List<Manga>,
val initialSelection: ImmutableList<CheckboxState<Category>>,
) : Dialog
data class DeleteManga(val manga: List<Manga>) : Dialog
}

View File

@ -11,6 +11,7 @@ import android.os.Bundle
import android.view.View
import androidx.activity.ComponentActivity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets
@ -64,7 +65,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
import eu.kanade.tachiyomi.extension.api.ExtensionApi
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen
@ -222,13 +223,14 @@ class MainActivity : BaseActivity() {
contentWindowInsets = scaffoldInsets,
) { contentPadding ->
// Consume insets already used by app state banners
// Shows current screen
DefaultNavigatorScreenTransition(
navigator = navigator,
Box(
modifier = Modifier
.padding(contentPadding)
.consumeWindowInsets(contentPadding),
)
) {
// Shows current screen
DefaultNavigatorScreenTransition(navigator = navigator)
}
}
// Pop source-related screens when incognito mode is turned off
@ -335,7 +337,7 @@ class MainActivity : BaseActivity() {
// Extensions updates
LaunchedEffect(Unit) {
try {
ExtensionGithubApi().checkForUpdates(context)
ExtensionApi().checkForUpdates(context)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
}

View File

@ -104,7 +104,7 @@ class MangaScreen(
MangaScreen(
state = successState,
snackbarHostState = screenModel.snackbarHostState,
fetchInterval = successState.manga.fetchInterval,
nextUpdate = successState.manga.expectedNextUpdate,
isTabletUi = isTabletUi(),
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
@ -146,7 +146,7 @@ class MangaScreen(
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
onEditFetchIntervalClicked = screenModel::showSetFetchIntervalDialog.takeIf {
screenModel.isUpdateIntervalEnabled && successState.manga.favorite
successState.manga.favorite
},
onMigrateClicked = {
navigator.push(MigrateSearchScreen(successState.manga.id))
@ -243,9 +243,10 @@ class MangaScreen(
is MangaScreenModel.Dialog.SetFetchInterval -> {
SetIntervalDialog(
interval = dialog.manga.fetchInterval,
nextUpdate = dialog.manga.nextUpdate,
nextUpdate = dialog.manga.expectedNextUpdate,
onDismissRequest = onDismissRequest,
onValueChanged = { screenModel.setFetchInterval(dialog.manga, it) },
onValueChanged = { interval: Int -> screenModel.setFetchInterval(dialog.manga, interval) }
.takeIf { screenModel.isUpdateIntervalEnabled },
)
}
}

View File

@ -36,6 +36,8 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.chapter.getNextUnread
import eu.kanade.tachiyomi.util.removeCovers
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.catch
@ -360,7 +362,7 @@ class MangaScreenModel(
successState.copy(
dialog = Dialog.ChangeCategory(
manga = manga,
initialSelection = categories.mapAsCheckboxState { it.id in selection },
initialSelection = categories.mapAsCheckboxState { it.id in selection }.toImmutableList(),
),
)
}
@ -992,7 +994,10 @@ class MangaScreenModel(
// Track sheet - end
sealed interface Dialog {
data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class ChangeCategory(
val manga: Manga,
val initialSelection: ImmutableList<CheckboxState<Category>>,
) : Dialog
data class DeleteChapters(val chapters: List<Chapter>) : Dialog
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
data class SetFetchInterval(val manga: Manga) : Dialog

View File

@ -56,6 +56,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import logcat.LogPriority
import tachiyomi.core.preference.toggle
import tachiyomi.core.storage.UniFileTempFileManager
import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.lang.withIOContext
@ -87,6 +88,7 @@ class ReaderViewModel @JvmOverloads constructor(
private val sourceManager: SourceManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val downloadProvider: DownloadProvider = Injekt.get(),
private val tempFileManager: UniFileTempFileManager = Injekt.get(),
private val imageSaver: ImageSaver = Injekt.get(),
preferences: BasePreferences = Injekt.get(),
val readerPreferences: ReaderPreferences = Injekt.get(),
@ -272,7 +274,7 @@ class ReaderViewModel @JvmOverloads constructor(
val context = Injekt.get<Application>()
val source = sourceManager.getOrStub(manga.source)
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source)
loader = ChapterLoader(context, downloadManager, downloadProvider, tempFileManager, manga, source)
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
Result.success(true)
@ -922,6 +924,7 @@ class ReaderViewModel @JvmOverloads constructor(
private fun deletePendingChapters() {
viewModelScope.launchNonCancellable {
downloadManager.deletePendingChapters()
tempFileManager.deleteTempFiles()
}
}

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import tachiyomi.core.i18n.stringResource
import tachiyomi.core.storage.toTempFile
import tachiyomi.core.storage.UniFileTempFileManager
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.manga.model.Manga
@ -24,6 +24,7 @@ class ChapterLoader(
private val context: Context,
private val downloadManager: DownloadManager,
private val downloadProvider: DownloadProvider,
private val tempFileManager: UniFileTempFileManager,
private val manga: Manga,
private val source: Source,
) {
@ -85,17 +86,24 @@ class ChapterLoader(
skipCache = true,
)
return when {
isDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager, downloadProvider)
isDownloaded -> DownloadPageLoader(
chapter,
manga,
source,
downloadManager,
downloadProvider,
tempFileManager,
)
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
when (format) {
is Format.Directory -> DirectoryPageLoader(format.file)
is Format.Zip -> ZipPageLoader(format.file.toTempFile(context))
is Format.Zip -> ZipPageLoader(tempFileManager.createTempFile(format.file))
is Format.Rar -> try {
RarPageLoader(format.file.toTempFile(context))
RarPageLoader(tempFileManager.createTempFile(format.file))
} catch (e: UnsupportedRarV5Exception) {
error(context.stringResource(MR.strings.loader_rar5_error))
}
is Format.Epub -> EpubPageLoader(format.file.toTempFile(context))
is Format.Epub -> EpubPageLoader(tempFileManager.createTempFile(format.file))
}
}
source is HttpSource -> HttpPageLoader(chapter, source)

View File

@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import tachiyomi.core.storage.toTempFile
import tachiyomi.core.storage.UniFileTempFileManager
import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.injectLazy
@ -23,6 +23,7 @@ internal class DownloadPageLoader(
private val source: Source,
private val downloadManager: DownloadManager,
private val downloadProvider: DownloadProvider,
private val tempFileManager: UniFileTempFileManager,
) : PageLoader() {
private val context: Application by injectLazy()
@ -46,8 +47,8 @@ internal class DownloadPageLoader(
zipPageLoader?.recycle()
}
private suspend fun getPagesFromArchive(chapterPath: UniFile): List<ReaderPage> {
val loader = ZipPageLoader(chapterPath.toTempFile(context)).also { zipPageLoader = it }
private suspend fun getPagesFromArchive(file: UniFile): List<ReaderPage> {
val loader = ZipPageLoader(tempFileManager.createTempFile(file)).also { zipPageLoader = it }
return loader.getPages()
}

View File

@ -10,6 +10,7 @@ import java.io.File
import java.io.InputStream
import java.io.PipedInputStream
import java.io.PipedOutputStream
import java.util.concurrent.Executors
/**
* Loader used to load a chapter from a .rar or .cbr file.
@ -20,13 +21,18 @@ internal class RarPageLoader(file: File) : PageLoader() {
override var isLocal: Boolean = true
/**
* Pool for copying compressed files to an input stream.
*/
private val pool = Executors.newFixedThreadPool(1)
override suspend fun getPages(): List<ReaderPage> {
return rar.fileHeaders.asSequence()
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
.mapIndexed { i, header ->
ReaderPage(i).apply {
stream = { getStream(rar, header) }
stream = { getStream(header) }
status = Page.State.READY
}
}
@ -40,15 +46,16 @@ internal class RarPageLoader(file: File) : PageLoader() {
override fun recycle() {
super.recycle()
rar.close()
pool.shutdown()
}
/**
* Returns an input stream for the given [header].
*/
private fun getStream(rar: Archive, header: FileHeader): InputStream {
private fun getStream(header: FileHeader): InputStream {
val pipeIn = PipedInputStream()
val pipeOut = PipedOutputStream(pipeIn)
synchronized(this) {
pool.execute {
try {
pipeOut.use {
rar.extractFile(header, it)

View File

@ -235,7 +235,7 @@ class PagerPageHolder(
*/
private fun setError() {
progressIndicator.hide()
showErrorLayout(withOpenInWebView = false)
showErrorLayout()
}
override fun onImageLoaded() {
@ -248,8 +248,7 @@ class PagerPageHolder(
*/
override fun onImageLoadError() {
super.onImageLoadError()
progressIndicator.hide()
showErrorLayout(withOpenInWebView = true)
setError()
}
/**
@ -260,23 +259,27 @@ class PagerPageHolder(
viewer.activity.hideMenu()
}
private fun showErrorLayout(withOpenInWebView: Boolean): ReaderErrorBinding {
private fun showErrorLayout(): ReaderErrorBinding {
if (errorLayout == null) {
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), this, true)
errorLayout?.actionRetry?.viewer = viewer
errorLayout?.actionRetry?.setOnClickListener {
page.chapter.pageLoader?.retryPage(page)
}
}
val imageUrl = page.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) {
errorLayout?.actionOpenInWebView?.isVisible = imageUrl != null
if (imageUrl != null) {
if (imageUrl.startsWith("http", true)) {
errorLayout?.actionOpenInWebView?.viewer = viewer
errorLayout?.actionOpenInWebView?.setOnClickListener {
val intent = WebViewActivity.newIntent(context, imageUrl!!)
val intent = WebViewActivity.newIntent(context, imageUrl)
context.startActivity(intent)
}
}
}
errorLayout?.actionOpenInWebView?.isVisible = withOpenInWebView
errorLayout?.root?.isVisible = true
return errorLayout!!
}

View File

@ -80,7 +80,7 @@ class WebtoonPageHolder(
refreshLayoutParams()
frame.onImageLoaded = { onImageDecoded() }
frame.onImageLoadError = { onImageDecodeError() }
frame.onImageLoadError = { setError() }
frame.onScaleChanged = { viewer.activity.hideMenu() }
}
@ -240,7 +240,7 @@ class WebtoonPageHolder(
*/
private fun setError() {
progressContainer.isVisible = false
initErrorLayout(withOpenInWebView = false)
initErrorLayout()
}
/**
@ -251,14 +251,6 @@ class WebtoonPageHolder(
removeErrorLayout()
}
/**
* Called when the image fails to decode.
*/
private fun onImageDecodeError() {
progressContainer.isVisible = false
initErrorLayout(withOpenInWebView = true)
}
/**
* Creates a new progress bar.
*/
@ -278,22 +270,26 @@ class WebtoonPageHolder(
/**
* Initializes a button to retry pages.
*/
private fun initErrorLayout(withOpenInWebView: Boolean): ReaderErrorBinding {
private fun initErrorLayout(): ReaderErrorBinding {
if (errorLayout == null) {
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), frame, true)
errorLayout?.root?.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, (parentHeight * 0.8).toInt())
errorLayout?.actionRetry?.setOnClickListener {
page?.let { it.chapter.pageLoader?.retryPage(it) }
}
}
val imageUrl = page?.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) {
errorLayout?.actionOpenInWebView?.isVisible = imageUrl != null
if (imageUrl != null) {
if (imageUrl.startsWith("http", true)) {
errorLayout?.actionOpenInWebView?.setOnClickListener {
val intent = WebViewActivity.newIntent(context, imageUrl!!)
val intent = WebViewActivity.newIntent(context, imageUrl)
context.startActivity(intent)
}
}
}
errorLayout?.actionOpenInWebView?.isVisible = withOpenInWebView
return errorLayout!!
}

View File

@ -40,7 +40,6 @@ class SettingsScreen(
Destination.Tracking.id -> SettingsTrackingScreen
else -> SettingsMainScreen
},
onBackPressed = null,
content = {
val pop: () -> Unit = {
if (it.canPop) {
@ -62,7 +61,6 @@ class SettingsScreen(
Destination.Tracking.id -> SettingsTrackingScreen
else -> SettingsAppearanceScreen
},
onBackPressed = null,
) {
val insets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)
TwoPanelBox(

View File

@ -4,11 +4,9 @@ package eu.kanade.tachiyomi.util.view
import android.content.res.Resources
import android.graphics.Rect
import android.os.Build
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.RoundedCorner
import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@ -97,22 +95,3 @@ fun View?.isVisibleOnScreen(): Boolean {
Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
return actualPosition.intersect(screen)
}
/**
* Returns window radius (in pixel) applied to this view
*/
fun View.getWindowRadius(): Int {
val rad = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val windowInsets = rootWindowInsets
listOfNotNull(
windowInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT),
windowInsets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT),
windowInsets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT),
windowInsets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT),
)
.minOfOrNull { it.radius }
} else {
null
}
return rad ?: 0
}

View File

@ -128,14 +128,13 @@ fun OkHttpClient.Builder.dohQuad101() = dns(
/*
* Mullvad DoH
* without ad blocking option
* Source : https://mullvad.net/en/help/dns-over-https-and-dns-over-tls/
* Source: https://mullvad.net/en/help/dns-over-https-and-dns-over-tls
*/
fun OkHttpClient.Builder.dohMullvad() = dns(
DnsOverHttps.Builder().client(build())
.url("https://doh.mullvad.net/dns-query".toHttpUrl())
.url(" https://dns.mullvad.net/dns-query".toHttpUrl())
.bootstrapDnsHosts(
InetAddress.getByName("194.242.2.2"),
InetAddress.getByName("193.19.108.2"),
InetAddress.getByName("2a07:e340::2"),
)
.build(),
@ -144,7 +143,7 @@ fun OkHttpClient.Builder.dohMullvad() = dns(
/*
* Control D
* unfiltered option
* Source : https://controld.com/free-dns/?
* Source: https://controld.com/free-dns/?
*/
fun OkHttpClient.Builder.dohControlD() = dns(
DnsOverHttps.Builder().client(build())

View File

@ -19,7 +19,7 @@ class NetworkPreferences(
fun defaultUserAgent(): Preference<String> {
return preferenceStore.getString(
"default_user_agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0",
)
}
}

View File

@ -63,6 +63,19 @@ fun PUT(
.cacheControl(cache)
.build()
}
fun PATCH(
url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
.url(url)
.patch(body)
.headers(headers)
.cacheControl(cache)
.build()
}
fun DELETE(
url: String,

View File

@ -15,7 +15,7 @@ import kotlin.coroutines.resume
object WebViewUtil {
const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
const val MINIMUM_WEBVIEW_VERSION = 114
const val MINIMUM_WEBVIEW_VERSION = 118
/**
* Uses the WebView's user agent string to create something similar to what Chrome on Android

View File

@ -1,11 +1,6 @@
package tachiyomi.core.storage
import android.content.Context
import android.os.Build
import android.os.FileUtils
import com.hippo.unifile.UniFile
import java.io.BufferedOutputStream
import java.io.File
val UniFile.extension: String?
get() = name?.substringAfterLast('.')
@ -15,27 +10,3 @@ val UniFile.nameWithoutExtension: String?
val UniFile.displayablePath: String
get() = filePath ?: uri.toString()
fun UniFile.toTempFile(context: Context): File {
val inputStream = context.contentResolver.openInputStream(uri)!!
val tempFile = File.createTempFile(
nameWithoutExtension.orEmpty().padEnd(3), // Prefix must be 3+ chars
null,
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
FileUtils.copy(inputStream, tempFile.outputStream())
} else {
BufferedOutputStream(tempFile.outputStream()).use { tmpOut ->
inputStream.use { input ->
val buffer = ByteArray(8192)
var count: Int
while (input.read(buffer).also { count = it } > 0) {
tmpOut.write(buffer, 0, count)
}
}
}
}
return tempFile
}

View File

@ -0,0 +1,46 @@
package tachiyomi.core.storage
import android.content.Context
import android.os.Build
import android.os.FileUtils
import com.hippo.unifile.UniFile
import java.io.BufferedOutputStream
import java.io.File
class UniFileTempFileManager(
private val context: Context,
) {
private val dir = File(context.externalCacheDir, "tmp")
fun createTempFile(file: UniFile): File {
dir.mkdirs()
val inputStream = context.contentResolver.openInputStream(file.uri)!!
val tempFile = File.createTempFile(
file.nameWithoutExtension.orEmpty().padEnd(3), // Prefix must be 3+ chars
null,
dir,
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
FileUtils.copy(inputStream, tempFile.outputStream())
} else {
BufferedOutputStream(tempFile.outputStream()).use { tmpOut ->
inputStream.use { input ->
val buffer = ByteArray(8192)
var count: Int
while (input.read(buffer).also { count = it } > 0) {
tmpOut.write(buffer, 0, count)
}
}
}
}
return tempFile
}
fun deleteTempFiles() {
dir.deleteRecursively()
}
}

View File

@ -1,8 +1,10 @@
package tachiyomi.domain.manga.model
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import tachiyomi.core.preference.TriState
import java.io.Serializable
import java.time.Instant
data class Manga(
val id: Long,
@ -29,6 +31,11 @@ data class Manga(
val favoriteModifiedAt: Long?,
) : Serializable {
val expectedNextUpdate: Instant?
get() = nextUpdate
.takeIf { status != SManga.COMPLETED.toLong() }
?.let { Instant.ofEpochMilli(it) }
val sorting: Long
get() = chapterFlags and CHAPTER_SORTING_MASK

View File

@ -2,6 +2,7 @@ package tachiyomi.domain.sync
import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore
import java.util.UUID
class SyncPreferences(
private val preferenceStore: PreferenceStore,
@ -43,6 +44,19 @@ class SyncPreferences(
"",
)
fun uniqueDeviceID(): String {
val uniqueIDPreference = preferenceStore.getString("unique_device_id", "")
// Retrieve the current value of the preference
var uniqueID = uniqueIDPreference.get()
if (uniqueID.isBlank()) {
uniqueID = UUID.randomUUID().toString()
uniqueIDPreference.set(uniqueID)
}
return uniqueID
}
fun syncFlags() = preferenceStore.getInt("sync_flags", Flags.Defaults)
fun isSyncEnabled(): Boolean {

View File

@ -1,5 +1,5 @@
[versions]
agp_version = "8.2.0"
agp_version = "8.2.1"
lifecycle_version = "2.6.2"
paging_version = "3.2.1"

View File

@ -1,10 +1,10 @@
[versions]
aboutlib_version = "10.10.0"
acra = "5.11.3"
leakcanary = "2.12"
leakcanary = "2.13"
moko = "0.23.0"
okhttp_version = "5.0.0-alpha.12"
richtext = "0.17.0"
richtext = "0.20.0"
shizuku_version = "12.2.0"
sqldelight = "2.0.0"
sqlite = "2.4.0"

View File

@ -557,7 +557,6 @@
<string name="library_errors_help">للحصول على المساعده في إصلاح أخطاء تحديث المكتبة، أنظر هنا%1$s</string>
<string name="save_chapter_as_cbz">احفظ كأرشيف CBZ</string>
<string name="privacy_policy">سياسة الخصوصية</string>
<string name="pref_library_update_manga_restriction">تخطي تحديث الإدخالات</string>
<string name="pref_update_only_completely_read">فيها فصول لم تُقرأ</string>
<string name="ext_installer_legacy">قياسي</string>
<string name="pref_auto_clear_chapter_cache">امسح ملفات التخزين المؤقت عند فتح التطبيق</string>
@ -566,7 +565,6 @@
<string name="extension_api_error">فشل الحصول على قائمة الملحقات</string>
<string name="ext_installer_shizuku_unavailable_dialog">ثبِّت «شيزوكو» وشغِّله لتستخدمه مثبِّت إضافات.</string>
<string name="download_queue_size_warning">تحذير: يمكن أن تؤدِّي التنزيلات كبيرة الحجم والعدد إلى إبطاء المصادر، وقد يُحظر Tachiyomi منها بسبب ذلك. اضغط لمعرفة المزيد۔</string>
<string name="action_faq_and_guides">الاسئله الشائعه والأدلة</string>
<string name="action_show_manga">إظهار الدخول</string>
<string name="action_display_cover_only_grid">شبكة بالاغلفة</string>
<string name="skipped_reason_not_started">تُخُطِّيت بسبب عدم وجود فصول قُرئت</string>
@ -575,12 +573,10 @@
<string name="skipped_reason_completed">تُخُطِّيت لأنها تمِّت</string>
<string name="pref_navigate_pan">حرِّك الصور الواسعة</string>
<string name="pref_landscape_zoom">كبِّر الصور العريضة تلقائيًّا</string>
<string name="channel_skipped">متجاوَز</string>
<string name="error_saving_picture">خطأ أثناء حفظ الصورة</string>
<string name="disabled_nav">معطل</string>
<string name="rotation_reverse_portrait">رأسي بالعكس</string>
<string name="notification_update_error">فشل %1$d تحديث أو تحديثات</string>
<string name="notification_update_skipped">تُخُطِّي %1$d تحديث أو تحديثات</string>
<string name="learn_more">اضغط لقراءة المزيد</string>
<string name="update_check_fdroid_migration_info">نسخة جديدة متاحة من الإصدارات الرسمية. انقر لمعرفة كيفية التحويل من إصدارات F-Droid غير الرسمية.</string>
<string name="action_move_to_top_all_for_series">نقل سلسلة الفصول للأعلى</string>
@ -718,7 +714,6 @@
<string name="action_filter_interval_dropped">ربما أُلغيت؟ ٢٠ التماسًا متأخرًا وشهران</string>
<string name="manga_display_interval_title">قدِّر كلَّ</string>
<string name="pref_update_only_in_release_period">ليس ضمن مدة الإصدار المتوقعة</string>
<string name="manga_modify_calculated_interval_title">خصِّص المدة</string>
<string name="skipped_reason_not_in_release_period">تُخُطِّيت بسبب عدم توقع صدور اليوم</string>
<string name="manga_display_modified_interval_title">عيِّن التحديث كلَّ</string>
<string name="intervals_header">المدة</string>

View File

@ -80,4 +80,9 @@
<item quantity="one">Extension update available</item>
<item quantity="other">%d extension updates available</item>
</plurals>
<plurals name="num_repos">
<item quantity="one">%d repo</item>
<item quantity="other">%d repos</item>
</plurals>
</resources>

View File

@ -164,7 +164,6 @@
<string name="action_webview_forward">Forward</string>
<string name="action_webview_refresh">Refresh</string>
<string name="action_start_downloading_now">Start downloading now</string>
<string name="action_faq_and_guides">FAQ and Guides</string>
<string name="action_not_now">Not now</string>
<!-- Operations -->
@ -184,6 +183,8 @@
<string name="onboarding_storage_info">Select a folder where %1$s will store chapter downloads, backups, and more.\n\nA dedicated folder is recommended.\n\nSelected folder: %2$s</string>
<string name="onboarding_storage_action_select">Select a folder</string>
<string name="onboarding_storage_selection_required">A folder must be selected</string>
<string name="onboarding_storage_help_info">Updating from an older version and not sure what to select? Refer to the storage guide for more information.</string>
<string name="onboarding_storage_help_action">Storage guide</string>
<string name="onboarding_permission_install_apps">Install apps permission</string>
<string name="onboarding_permission_install_apps_description">To install source extensions.</string>
<string name="onboarding_permission_notifications">Notification permission</string>
@ -192,7 +193,7 @@
<string name="onboarding_permission_ignore_battery_opts_description">Avoid interruptions to long-running library updates, downloads, and backup restores.</string>
<string name="onboarding_permission_action_grant">Grant</string>
<string name="onboarding_guides_new_user">New to %s? We recommend checking out the getting started guide.</string>
<string name="onboarding_guides_returning_user">Already used %s before?</string>
<string name="onboarding_guides_returning_user">Reinstalling %s?</string>
<!-- Preferences -->
<!-- Subsections -->
@ -281,12 +282,12 @@
<string name="charging">When charging</string>
<string name="restrictions">Restrictions: %s</string>
<string name="pref_library_update_manga_restriction">Skip updating entries</string>
<string name="pref_update_only_completely_read">With unread chapter(s)</string>
<string name="pref_update_only_non_completed">With \"Completed\" status</string>
<string name="pref_update_only_started">That haven\'t been started</string>
<string name="pref_library_update_smart_update">Smart update</string>
<string name="pref_update_only_completely_read">Skip entries with unread chapter(s)</string>
<string name="pref_update_only_non_completed">Skip entries with \"Completed\" status</string>
<string name="pref_update_only_started">Skip unstarted entries</string>
<string name="pref_update_only_in_release_period">Predict next release time</string>
<string name="pref_library_update_show_tab_badge">Show unread count on Updates icon</string>
<string name="pref_update_only_in_release_period">Outside expected release period</string>
<string name="pref_library_update_refresh_metadata">Automatically refresh metadata</string>
<string name="pref_library_update_refresh_metadata_summary">Check for new cover and details when updating library</string>
@ -323,7 +324,7 @@
<string name="ext_uninstall">Uninstall</string>
<string name="ext_app_info">App info</string>
<string name="untrusted_extension">Untrusted extension</string>
<string name="untrusted_extension_message">This extension was signed with an untrusted certificate and wasn\'t activated.\n\nA malicious extension could read any stored login credentials or execute arbitrary code.\n\nBy trusting this certificate you accept these risks.</string>
<string name="untrusted_extension_message">This extension was signed by any unknown author and wasn\'t loaded.\n\nMalicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension\'s certificate, you accept these risks.</string>
<string name="obsolete_extension_message">This extension is no longer available. It may not function properly and can cause issues with the app. Uninstalling it is recommended.</string>
<string name="unofficial_extension_message">This extension is not from the official list.</string>
<string name="extension_api_error">Failed to get extensions list</string>
@ -342,6 +343,18 @@
<string name="ext_installer_shizuku_stopped">Shizuku is not running</string>
<string name="ext_installer_shizuku_unavailable_dialog">Install and start Shizuku to use Shizuku as extension installer.</string>
<!-- Extension repos -->
<string name="label_extension_repos">Extension repos</string>
<string name="information_empty_repos">You have no repos set.</string>
<string name="action_add_repo">Add repo</string>
<string name="label_add_repo_input">Repo URL</string>
<string name="action_add_repo_message">Add additional repos to Tachiyomi. This should be a URL that ends with \"index.min.json\".</string>
<string name="error_repo_exists">This repo already exists!</string>
<string name="action_delete_repo">Delete repo</string>
<string name="invalid_repo_name">Invalid repo URL</string>
<string name="delete_repo_confirmation">Do you wish to delete the repo \"%s\"?</string>
<string name="repo_extension_message">This extension is from an external repo. Tap to view the repo.</string>
<!-- Reader section -->
<string name="pref_fullscreen">Fullscreen</string>
<string name="pref_show_navigation_mode">Show tap zones overlay</string>
@ -566,6 +579,7 @@
<string name="google_drive_login_success">Logged in to Google Drive</string>
<string name="google_drive_login_failed">Failed to log in to Google Drive: %s</string>
<string name="google_drive_not_signed_in">Not signed in to Google Drive</string>
<string name="error_deleting_google_drive_lock_file">Error Deleting Google Drive Lock File</string>
<string name="pref_purge_confirmation_title">Purge confirmation</string>
<string name="pref_purge_confirmation_message">Purging sync data will delete all your sync data from Google Drive. Are you sure you want to continue?</string>
<string name="pref_sync_options">Create sync triggers</string>
@ -714,9 +728,10 @@
<string name="display_mode_chapter">Chapter %1$s</string>
<string name="manga_display_interval_title">Estimate every</string>
<string name="manga_display_modified_interval_title">Set to update every</string>
<string name="manga_interval_header">Next update</string>
<!-- "... around 2 days" -->
<string name="manga_interval_expected_update">Next update expected in around %s</string>
<string name="manga_modify_calculated_interval_title">Customize interval</string>
<string name="manga_interval_expected_update">Next update expected in around %1$s, checking around every %2$s</string>
<string name="manga_interval_custom_amount">Custom update frequency:</string>
<string name="chapter_downloading_progress">Downloading (%1$d/%2$d)</string>
<string name="chapter_error">Error</string>
<string name="chapter_paused">Paused</string>

View File

@ -18,4 +18,10 @@
<item quantity="many">%1$d дзён таму</item>
<item quantity="other">%1$d дзён таму</item>
</plurals>
<plurals name="next_unread_chapters">
<item quantity="one">Наступная непрачытаная глава</item>
<item quantity="few">Наступныя %d непрачытаныя главы</item>
<item quantity="many">Наступныя %d непрачытаных глав</item>
<item quantity="other">Наступныя %d непрачытаных глав</item>
</plurals>
</resources>

View File

@ -327,12 +327,10 @@
<string name="ext_app_info">Аб дадатку</string>
<string name="action_sort_count">Колькасць мангі</string>
<string name="action_display_language_badge">Мова</string>
<string name="action_faq_and_guides">Часта задаваемые пытанні і кіраўніцтва</string>
<string name="network_not_metered">Толькі ў безлемітнай сетцы</string>
<string name="ext_info_language">Мова</string>
<string name="theme_lavender">Лаванда</string>
<string name="pref_app_language">Мова дадатку</string>
<string name="pref_library_update_manga_restriction">Прапусціць абнаўленні</string>
<string name="pref_update_only_completely_read">Ёсць непрачытаныя главы</string>
<string name="ext_installer_shizuku_stopped">Shizuku не працуе</string>
<string name="action_close">Закрыць</string>

View File

@ -556,20 +556,17 @@
<string name="privacy_policy">Политика за поверителност</string>
<string name="pref_update_only_completely_read">С непрочетени глави</string>
<string name="ext_update_all">Обнови всички</string>
<string name="pref_library_update_manga_restriction">Ограничения за актуализиране на библиотеката</string>
<string name="extension_api_error">Неуспешно получаване на списък с разширения</string>
<string name="update_72hour">На всеки 3 дена</string>
<string name="connected_to_wifi">Само през Wi-Fi</string>
<string name="download_queue_size_warning">Предупреждение: големите масови изтегляния могат да доведат до забавяне на източниците и/или блокиране на Tachiyomi. Натиснете тук, за да научите повече.</string>
<string name="action_show_manga">Покажи манга</string>
<string name="action_faq_and_guides">Често задавани въпроси и ръководства</string>
<string name="action_display_cover_only_grid">Решетка само с корици</string>
<string name="delete_category_confirmation">Искате ли да изтриете категорията \"%s\"\?</string>
<string name="internal_error">InternalError: Провери записите за крашове за повече информация</string>
<string name="error_saving_picture">Грешка при запазването на изображението</string>
<string name="update_already_running">Обновява се</string>
<string name="not_installed">Не е инсталиран</string>
<string name="channel_skipped">Пропуснати</string>
<string name="rotation_reverse_portrait">Обърнат портрет</string>
<string name="ext_info_language">Език</string>
<string name="crash_screen_title">Случи се неочаквана грешка</string>
@ -607,7 +604,6 @@
<string name="action_sort_last_manga_update">Последно обновяване</string>
<string name="pref_clear_webview_data">Изчисти данните на WebView</string>
<string name="empty_screen">Това е неудобно</string>
<string name="notification_update_skipped">%1$d пропуснати обновление/я</string>
<string name="cancelled">Отменена</string>
<string name="library_errors_help">За помощ при оправянето на грешки с обновленията, виж %1$s</string>
<string name="theme_tidalwave">Приливна вълна</string>

View File

@ -560,7 +560,6 @@
<string name="privacy_policy">গোপনীয়তা নীতি</string>
<string name="extension_api_error">এক্সটেনশন তালিকা পেতে ব্যার্থ হলো</string>
<string name="skipped_reason_completed">এড়িয়ে যাওয়া হয়েছে কারণ সিরিজটি সম্পূর্ণ</string>
<string name="notification_update_skipped">%1$d আপডেট(গুলি) এড়িয়ে গেছে</string>
<string name="cancelled">বাতিল করা হয়েছে</string>
<string name="on_hiatus">বিরতিতে রয়েছে</string>
<string name="notification_update_error">%1$d আপডেট(গুলি) ব্যর্থ হয়েছে</string>
@ -583,14 +582,11 @@
<string name="on_hold_list">স্থগিত তালিকা</string>
<string name="auto_download_while_reading">পড়ার সময় সয়ংক্রিয়ভাবে ডাউনলোড</string>
<string name="pref_update_only_completely_read">অপঠিত অধ্যায়সহ</string>
<string name="pref_library_update_manga_restriction">শিরোনামসমুহ হালনাগাদ এড়িয়ে যান</string>
<string name="channel_skipped">এড়িয়ে যাওয়া</string>
<string name="pref_update_only_started">যেগুলো শুরু করা হয়নি</string>
<string name="webview_data_deleted">ওয়েবভিউ ডাটা মুছা হয়েছে</string>
<string name="split_tall_images_summary">পঠন কর্মক্ষমতা উন্নত করে</string>
<string name="pref_clear_webview_data">ওয়েবভিইউ ডাটা মুছুন</string>
<string name="multi_lang">বহু</string>
<string name="action_faq_and_guides">FAQ এবং নির্দেশনা</string>
<string name="pref_library_summary">বিভাগসমূহ, সার্বজনীন হালনাগাদ</string>
<string name="pref_app_language">অ্যাপ ভাষা</string>
<string name="pref_appearance_summary">থিম,সময় ও তারিখের ধরন</string>
@ -625,4 +621,27 @@
<string name="action_bar_up_description">নেভিগেট আপ</string>
<string name="label_data_storage">ডাটা অন স্টোরেজ</string>
<string name="action_filter_interval_custom">কাস্টমাইজড আনার ব্যবধান</string>
<string name="onboarding_storage_action_select">ফোল্ডার নির্বাচন করুন</string>
<string name="pref_onboarding_guide">অনবর্ডিং গাইড</string>
<string name="onboarding_action_finish">শুরু করুন</string>
<string name="action_apply">এপ্লাই</string>
<string name="action_filter_interval_passed">চেক করার সময় পার হয়েছে</string>
<string name="onboarding_storage_selection_required">একটি ফোল্ডার অবশ্যই ঠিক করতে হবে</string>
<string name="onboarding_permission_notifications">নোটিফিকেশনের পারমিশন</string>
<string name="action_revert_to_default">সকল সেটিং ডিফল্ট করুন</string>
<string name="onboarding_permission_install_apps">অ্যাপের ইন্সটল করার পারমিশন</string>
<string name="action_sort_category">ক্যাটাগরি ফিল্টার করুন</string>
<string name="onboarding_heading">স্বাগতম</string>
<string name="action_move_to_bottom_all_for_series">নিচের দিকে সরান</string>
<string name="onboarding_action_skip">বাদ দিন</string>
<string name="onboarding_action_next">পরবর্তী</string>
<string name="onboarding_permission_install_apps_description">সোর্স এক্সটেনশন ইনস্টল করার জন্য</string>
<string name="onboarding_description">প্রথমে কিছু ঠিক করে নিন , পরবর্তীতে আপনি চাইলে সেটিং থেকে সবকিছু পরিবর্তন করতে পারবেন।</string>
<string name="internal_error">ইন্টারনাল ইরর: বাকি ইনফরমেশন পেতে ক্যাশ লগ দেখুন</string>
<string name="skipped_reason_not_started">স্কিপ করা হয়েছে কারণ আগে কোন চ্যাপ্টার পড়া হয়নি</string>
<string name="action_sort_tracker_score">ট্রেকার স্কোর</string>
<string name="sort_category_confirmation">আপনি কি বর্ণানুক্রমিকভাবে ফিল্টার করতে চান</string>
<string name="action_ok">ঠিক আছে</string>
<string name="action_sort_next_updated">পরবর্তী আপডেটের সম্ভাব্য সময়</string>
<string name="action_filter_interval_long">প্রতিমাসে রিফ্রেস করুন (২৮ দিন)</string>
</resources>

View File

@ -167,7 +167,7 @@
<string name="creating_backup">Sestà creant la còpia de seguretat</string>
<string name="pref_clear_chapter_cache">Buida la memòria cau de capítols</string>
<string name="used_cache">Ús: %1$s</string>
<string name="cache_deleted">Sha buidat la memòria cau. Shan suprimit %1$d fitxers</string>
<string name="cache_deleted">Sha buidat la memòria cau i shan suprimit %1$d fitxers</string>
<string name="cache_delete_error">Sha produït un error en netejar</string>
<string name="pref_clear_cookies">Esborra les galetes</string>
<string name="cookies_cleared">Shan esborrat les galetes</string>
@ -376,7 +376,7 @@
<string name="label_data">Dades</string>
<string name="backup_restore_missing_sources">Manquen fonts:</string>
<string name="invalid_backup_file_missing_manga">La còpia de seguretat no conté cap element de la biblioteca.</string>
<string name="invalid_backup_file">Fitxer de còpia de seguretat invàlid</string>
<string name="invalid_backup_file">Fitxer de còpia de seguretat invàlid:</string>
<string name="pref_library_update_refresh_metadata_summary">Comprova si hi ha noves portades o detalls en actualitzar la biblioteca</string>
<string name="pref_library_update_refresh_metadata">Refresca les metadades automàticament</string>
<string name="action_display_comfortable_grid">Graella confortable</string>
@ -457,9 +457,7 @@
<string name="pref_dual_page_split">Divideix les pàgines amples</string>
<string name="pref_dual_page_invert_summary">Si la ubicació de les pàgines amples dividides no encaixa amb la direcció de lectura</string>
<string name="pref_dual_page_invert">Inverteix la ubicació de la divisió de pàgines</string>
<string name="backup_restore_content_full">Es restauraran les dades del fitxer de còpia de seguretat.
\n
\nCaldrà que instal·leu les extensions que manquin i que després inicieu la sessió als serveis de seguiment per a utilitzar-los.</string>
<string name="backup_restore_content_full">Caldrà que instal·leu les extensions que manquin i que després inicieu la sessió als serveis de seguiment per a utilitzar-los.</string>
<string name="update_check_eol">Aquesta versió dAndroid ja no està suportada</string>
<string name="error_no_match">No sha trobat cap coincidència</string>
<string name="source_unsupported">La font no està suportada</string>
@ -553,8 +551,6 @@
<string name="cancelled">Cancel·lada</string>
<string name="library_errors_help">Per a obtenir ajuda per a resoldre errors dactualització de la biblioteca, vegeu %1$s</string>
<string name="extension_api_error">No sha pogut obtenir la llista dextensions</string>
<string name="action_faq_and_guides">PMF i guies</string>
<string name="pref_library_update_manga_restriction">Omet les actualitzacions dels elements</string>
<string name="pref_update_only_completely_read">Amb capítols no llegits</string>
<string name="save_chapter_as_cbz">Desa com a arxiu CBZ</string>
<string name="backup_info">També hauríeu de desar còpies de les còpies de seguretat en altres llocs. Les còpies de seguretat poden contenir dades sensibles, incloent-hi les contrasenyes desades; aneu amb compte si les compartiu.</string>
@ -576,9 +572,7 @@
<string name="skipped_reason_not_started">Sha omès perquè no hi ha cap capítol llegit</string>
<string name="skipped_reason_not_caught_up">Sha omès perquè hi ha capítols no llegits</string>
<string name="learn_more">Premeu per a obtenir més informació</string>
<string name="channel_skipped">Omesa</string>
<string name="notification_update_error">Han fallat %1$d actualitzacions</string>
<string name="notification_update_skipped">Shan omès %1$d actualitzacions</string>
<string name="rotation_reverse_portrait">Vertical inversa</string>
<string name="action_move_to_top_all_for_series">Mou la sèrie a dalt de tot</string>
<string name="disabled_nav">Desactivat</string>
@ -721,7 +715,6 @@
<string name="intervals_header">Intervals</string>
<string name="manga_display_interval_title">Estima cada</string>
<string name="manga_display_modified_interval_title">Sactualitzarà cada</string>
<string name="manga_modify_calculated_interval_title">Personalitza linterval</string>
<string name="track_delete_title">Voleu deixar de seguir %s\?</string>
<string name="track_delete_remote_text">Elimina també de %s</string>
<string name="track_delete_text">Se neliminarà el seguiment local.</string>
@ -757,7 +750,7 @@
<string name="last_auto_backup_info">Darrera còpia de seguretat automàtica: %s</string>
<string name="no_scanlators_found">No sha trobat cap scanlator</string>
<string name="scanlator">Scanlator</string>
<string name="pref_flash_page">Centelleig blanc en canviar de pàgina</string>
<string name="pref_flash_page">Centelleig en canviar de pàgina</string>
<string name="pref_storage_usage">Ús de lemmagatzematge</string>
<string name="action_sort_tracker_score">Puntuació del servei de seguiment</string>
<string name="label_data_storage">Dades i emmagatzematge</string>
@ -784,4 +777,16 @@
\nÉs recomanable fer servir una carpeta dedicada.
\n
\nCarpeta seleccionada: %2$s</string>
<string name="onboarding_permission_notifications">Permís de notificacions</string>
<string name="onboarding_permission_install_apps">Permís per a instal·lar aplicacions</string>
<string name="onboarding_permission_ignore_battery_opts_description">Eviteu interrupcions en actualitzacions de la biblioteca, baixades i restauracions de còpies de seguretat que tinguin una llarga durada.</string>
<string name="onboarding_permission_ignore_battery_opts">Ús de la bateria en segon pla</string>
<string name="onboarding_permission_install_apps_description">Per a instal·lar extensions de fonts.</string>
<string name="onboarding_permission_notifications_description">Rebeu notificacions quan hi hagi actualitzacions de la biblioteca i més.</string>
<string name="onboarding_permission_action_grant">Permet</string>
<string name="available_disk_space_info">Disponible: %1$s / Total: %2$s</string>
<string name="manga_interval_expected_update">La pròxima actualització es farà pels volts de %s</string>
<string name="invalid_backup_file_error">Error complet:</string>
<string name="ext_permission_install_apps_warning">Calen permisos per a instal·lar extensions. Premeu aquí per a concedir-les.</string>
<string name="private_settings">Inclou-hi configuració sensible (per exemple, testimonis dautenticació dels serveis de seguiment)</string>
</resources>

View File

@ -144,7 +144,6 @@
<string name="update_weekly">Kada semana</string>
<string name="charging">Sa dihang nag-charge</string>
<string name="restrictions">Mga pagdili: %s</string>
<string name="pref_library_update_manga_restriction">Laktawan ang pag-update sa mga titulo</string>
<string name="pref_update_only_completely_read">Uban sa wala pa mabasa nga (mga) kapitulo</string>
<string name="pref_update_only_started">Wala pa kana nagsugod</string>
<string name="pref_library_update_refresh_metadata">Awtomatikong i-refresh ang metadata</string>
@ -156,7 +155,6 @@
<string name="action_move_to_top">Ibalhin sa ibabaw</string>
<string name="action_move_to_top_all_for_series">Ibalhin ang serye sa ibabaw</string>
<string name="action_show_errors">I-tap aron makita ang mga detalye</string>
<string name="action_faq_and_guides">FAQ ug mga Giya</string>
<string name="action_asc">Pagsaka</string>
<string name="action_share">Ipaambit</string>
<string name="action_open_log">Bukas nga log</string>

View File

@ -390,8 +390,8 @@
<string name="secure_screen_summary">Secure screen skryje obsah aplikace při přepínání aplikací a blokuje tvorbu snímků obrazovky</string>
<string name="secure_screen">Chráněná obrazovka</string>
<string name="lock_with_biometrics">Vyžadovat odemknutí</string>
<string name="theme_dark">Zapnuto</string>
<string name="theme_light">Vypnuto</string>
<string name="theme_dark">Tmavý</string>
<string name="theme_light">Světlý</string>
<string name="pref_category_theme">Vzhled</string>
<string name="action_move_to_top">Přesunout nahoru</string>
<string name="action_desc">Sestupně</string>
@ -558,22 +558,18 @@
<string name="privacy_policy">Zásady ochrany osobních údajů</string>
<string name="clear_database_source_item_count">%1$d neknihovní záznamy v databázi</string>
<string name="pref_true_color_summary">Snižuje pruhování barev, ale může mít vliv na výkon</string>
<string name="pref_library_update_manga_restriction">Přeskočit aktualizování položek</string>
<string name="pref_update_only_completely_read">S nepřečtenými kapitolami</string>
<string name="database_clean">Nic k vyčištění</string>
<string name="save_chapter_as_cbz">Uložit jako CBZ archiv</string>
<string name="library_errors_help">Nápovědu k tomu, jak opravit chyby při aktualizaci knihovny, viz %1$s</string>
<string name="action_faq_and_guides">FAQ a Návody</string>
<string name="publishing_finished">Zveřejnění dokončeno</string>
<string name="cancelled">Zrušeno</string>
<string name="action_show_manga">Zobrazit položku</string>
<string name="action_display_cover_only_grid">Mřížka jen s přebaly</string>
<string name="pref_update_only_started">Které nebyly rozečteny</string>
<string name="notification_update_error">%1$d aktualizace(í) selhalo</string>
<string name="notification_update_skipped">%1$d aktualizace(í) přeskočena(y)</string>
<string name="on_hiatus">Má pauzu</string>
<string name="skipped_reason_not_caught_up">Přeskočeno, protože obsahuje nepřečtené kapitoly</string>
<string name="channel_skipped">Přeskočeno</string>
<string name="action_move_to_top_all_for_series">Přesunout sérii na začátek</string>
<string name="learn_more">Klepnutím se dozvíte více</string>
<string name="skipped_reason_not_started">Přeskočeno, protože nebyly přečteny žádné kapitoly</string>
@ -710,7 +706,6 @@
<string name="pref_chapter_swipe_start">Přejetí prstem doleva</string>
<string name="pref_double_tap_zoom">Přiblížení dvojitým klepnutím</string>
<string name="pref_library_columns_per_row">%d na řádek</string>
<string name="manga_modify_calculated_interval_title">Přizpůsobit interval</string>
<string name="action_filter_interval_passed">Uplynulé kontrolní období</string>
<string name="action_filter_interval_custom">Přizpůsobený interval načítání</string>
<string name="action_filter_interval_long">Načíst měsíčně (28 dní)</string>
@ -761,4 +756,37 @@
<string name="label_data_storage">Data a úložiště</string>
<string name="file_null_uri_error">Nevybrán žádný soubor</string>
<string name="exclude_scanlators">Vynechat překladatele</string>
<string name="onboarding_storage_action_select">Vybrat složku</string>
<string name="onboarding_heading">Vítejte!</string>
<string name="action_menu_overflow_description">Více možností</string>
<string name="onboarding_action_skip">Přeskočit</string>
<string name="onboarding_action_next">Další</string>
<string name="pref_storage_location">Umístění úložiště</string>
<string name="pref_onboarding_guide">Nástupní průvodce</string>
<string name="pref_storage_location_info">Používá se pro automatické zálohování, stahování kapitol a lokální zdroj.</string>
<string name="onboarding_guides_new_user">Jste v %s noví? Doporučujeme se podívat se na průvodce pro začátečníky.</string>
<string name="onboarding_action_finish">Začínáme</string>
<string name="onboarding_storage_selection_required">Složka musí být vybrána</string>
<string name="onboarding_permission_notifications">Povolení oznámení</string>
<string name="onboarding_permission_install_apps">Povolení stahovat aplikace</string>
<string name="available_disk_space_info">Dostupné: %1$s / Celkem: %2$s</string>
<string name="onboarding_guides_returning_user">Už jste použili %s dříve?</string>
<string name="selected">Vybráno</string>
<string name="not_selected">Nevybráno</string>
<string name="onboarding_permission_ignore_battery_opts_description">Vyhněte se přerušením dlouhých aktualizací knihovny, stahování a obnovení záloh.</string>
<string name="onboarding_permission_ignore_battery_opts">Využití baterie na pozadí</string>
<string name="onboarding_permission_install_apps_description">K nainstalování rozšíření zdrojů.</string>
<string name="action_bar_up_description">Navigovat nahoru</string>
<string name="onboarding_description">Pojďme nastavit nějaké věci. Vždy můžete tyto změny později změnit v nastavení.</string>
<string name="action_sort_tracker_score">Skóre sledovače</string>
<string name="no_location_set">Není nastaveno umístění úložiště</string>
<string name="onboarding_permission_notifications_description">Buďte upozorněni na aktualizaci knihovny atd.</string>
<string name="ext_permission_install_apps_warning">K instalaci rozšíření je nutné povolení. Klepněte zde pro udělení povolení.</string>
<string name="private_settings">Zahrnout citlivé nastavení (např. přihlašovací tokeny sledovačů)</string>
<string name="onboarding_permission_action_grant">Udělit</string>
<string name="onboarding_storage_info">Vyberte složku, kde %1$s bude ukládat stahování kapitol, zálohy a další.
\n
\nJe doporučena vyhrazená složka.
\n
\nVybraná složka: %2$s</string>
</resources>

View File

@ -490,7 +490,6 @@
<string name="action_display_cover_only_grid">Йатсӑр сетке</string>
<string name="action_move_to_top_all_for_series">Серилӗхе пуҫламӑша куҫар</string>
<string name="confirm_lock_change">Улшӑнӑва ҫирӗплетме есӗлӗхе ҫирӗплет</string>
<string name="action_faq_and_guides">Кӑтартусем тата ыйту-хурав</string>
<string name="action_show_manga">Ҫырава кӑтарт</string>
<string name="action_display_language_badge">Чӗлхе</string>
<string name="action_search_hint">Шыра…</string>
@ -524,7 +523,6 @@
<string name="pref_browse_summary">Ҫӑл куҫсем, хушмасем, пӗтӗмӗшле шырав</string>
<string name="theme_greenapple">Симӗс пан улми</string>
<string name="theme_yotsuba">Йутсупа</string>
<string name="pref_library_update_manga_restriction">Серилӗхсене ҫӗнетнине сиктер</string>
<string name="network_not_metered">Чараксӑр тетел урлӑ ҫеҫ</string>
<string name="pref_update_only_started">Серилӗхе пуҫламан</string>
<string name="theme_tako">Такку</string>

View File

@ -112,7 +112,6 @@
<string name="action_webview_back">Tilbage</string>
<string name="action_webview_refresh">Genindlæs</string>
<string name="action_start_downloading_now">Start download nu</string>
<string name="action_faq_and_guides">FAQ og guides</string>
<string name="loading">Loader…</string>
<string name="app_not_available">App ikke tilgængelig</string>
<string name="pref_category_general">Generelt</string>
@ -170,7 +169,6 @@
<string name="update_weekly">Ugentligt</string>
<string name="charging">Kun under opladning</string>
<string name="restrictions">Begrænsninger: %s</string>
<string name="pref_library_update_manga_restriction">Spring over opdatering af titler</string>
<string name="pref_update_only_completely_read">Med ulæst kapitler</string>
<string name="pref_update_only_non_completed">Med \"Færdig\" status</string>
<string name="pref_update_only_started">Der ikke er blevet startet</string>
@ -275,7 +273,6 @@
<string name="pref_always_show_chapter_transition">Vis altid kapitelovergang</string>
<string name="action_create">Opret</string>
<string name="pref_restore_backup">Gendan sikkerhedskopi</string>
<string name="channel_skipped">Sprunget over</string>
<string name="used_cache">Brugt: %1$s</string>
<string name="pref_lowest">Laveste</string>
<string name="rotation_type">Rotation</string>

View File

@ -140,7 +140,7 @@
<string name="creating_backup">Datensicherung wird erstellt</string>
<string name="pref_clear_chapter_cache">Kapitel-Zwischenspeicher leeren</string>
<string name="used_cache">Belegt: %1$s</string>
<string name="cache_deleted">Zwischenspeicher geleert. %1$d Dateien wurden gelöscht</string>
<string name="cache_deleted">Zwischenspeicher geleert, %1$d Dateien gelöscht</string>
<string name="cache_delete_error">Fehler während dem Leeren aufgetreten</string>
<string name="pref_clear_cookies">Cookies löschen</string>
<string name="cookies_cleared">Cookies gelöscht</string>
@ -560,13 +560,11 @@
<string name="extension_api_error">Herunterladen der Erweiterungsliste ist fehlgeschlagen</string>
<string name="privacy_policy">Datenschutzbestimmungen</string>
<string name="pref_update_only_completely_read">Mit ungelesenen Kapiteln</string>
<string name="pref_library_update_manga_restriction">Aktualisierung der Einträge überspringen</string>
<string name="library_errors_help">Für Hilfe zum Beheben von Fehlern bei Bibliotheksaktualisierungen, siehe %1$s</string>
<string name="save_chapter_as_cbz">Als CBZ-Archiv speichern</string>
<string name="cancelled">Abgebrochen</string>
<string name="on_hiatus">Unterbrochen</string>
<string name="publishing_finished">Veröffentlichung abgeschlossen</string>
<string name="action_faq_and_guides">Häufige Fragen und Leitfäden</string>
<string name="action_show_manga">Eintrag anzeigen</string>
<string name="action_display_cover_only_grid">Nur-Cover-Kacheln</string>
<string name="pref_landscape_zoom">Bei breiten Bildern automatisch hineinzoomen</string>
@ -576,8 +574,6 @@
<string name="skipped_reason_not_caught_up">Übersprungen, weil es ungelesene Kapitel gibt</string>
<string name="skipped_reason_not_started">Übersprungen, weil keine Kapitel gelesen wurden</string>
<string name="notification_update_error">%1$d Aktualisierung(en) fehlgeschlagen</string>
<string name="notification_update_skipped">%1$d Aktualisierung(en) übersprungen</string>
<string name="channel_skipped">Übersprungen</string>
<string name="learn_more">Antippen, um mehr zu erfahren</string>
<string name="rotation_reverse_portrait">Umgekehrtes Hochformat</string>
<string name="action_move_to_top_all_for_series">Serie nach oben verschieben</string>
@ -716,7 +712,6 @@
<string name="action_filter_interval_long">Monatlich abrufen (28 Tage)</string>
<string name="action_sort_next_updated">Nächste erwartete Aktualisierung</string>
<string name="pref_update_only_in_release_period">Außerhalb des erwarteten Veröffentlichungszeitraums</string>
<string name="manga_modify_calculated_interval_title">Intervall anpassen</string>
<string name="skipped_reason_not_in_release_period">Übersprungen, da heute keine Veröffentlichung erwartet wurde</string>
<string name="manga_display_interval_title">Schätzt alle</string>
<string name="manga_display_modified_interval_title">Aktualisiert alle</string>
@ -752,7 +747,7 @@
<string name="relative_time_span_never">Nie</string>
<string name="pref_flash_page_summ">Reduziert Ghosting auf E-Papier-Displays</string>
<string name="last_auto_backup_info">Zuletzt automatisch gesichert: %s</string>
<string name="pref_flash_page">Bei Umblättern weiß aufleuchten</string>
<string name="pref_flash_page">Bei Umblättern aufleuchten</string>
<string name="label_data_storage">Daten und Speicher</string>
<string name="action_create">Erstellen</string>
<string name="action_apply">Anwenden</string>
@ -784,4 +779,14 @@
\nEin dedizierter Ordner wird empfohlen.
\n
\nAusgewählter Ordner: %2$s</string>
<string name="onboarding_permission_notifications">Berechtigung für Benachrichtigungen</string>
<string name="onboarding_permission_install_apps">Berechtigung zum Installieren von Apps</string>
<string name="available_disk_space_info">Verfügbar: %1$s / Insgesamt: %2$s</string>
<string name="onboarding_permission_ignore_battery_opts_description">Verhindere Unterbrechungen bei lang anhaltenden Bibliotheksaktualisierungen, Downloads und Sicherungswiederherstellungen.</string>
<string name="onboarding_permission_ignore_battery_opts">Akkunutzung im Hintergrund</string>
<string name="onboarding_permission_install_apps_description">Zum Installieren von Quellenerweiterungen.</string>
<string name="onboarding_permission_notifications_description">Erhalte Benachrichtigungen für Bibliotheksaktualisierungen und mehr.</string>
<string name="ext_permission_install_apps_warning">Für das Installieren von Erweiterungen sind Berechtigungen erforderlich. Tippe hier, um sie zu gewähren.</string>
<string name="private_settings">Sensible Einstellungen einbeziehen (z. B. Login-Token für Tracker)</string>
<string name="onboarding_permission_action_grant">Zulassen</string>
</resources>

View File

@ -167,7 +167,7 @@
<string name="creating_backup">Δημιουργία αντιγράφων ασφαλείας</string>
<string name="pref_clear_chapter_cache">Καθάρισμα προσωρινής μνήμης κεφαλαίου</string>
<string name="used_cache">Χρησιμοποιήθηκε: %1$s</string>
<string name="cache_deleted">Η κρυφή μνήμη διαγράφηκε. %1$d αρχεία έχουν διαγραφεί</string>
<string name="cache_deleted">Η προσωρινή μνήμη διαγράφηκε, %1$d αρχεία διαγράφηκαν</string>
<string name="cache_delete_error">Παρουσιάστηκε σφάλμα κατά την εκκαθάριση</string>
<string name="pref_clear_cookies">Διαγραφή cookies</string>
<string name="cookies_cleared">Τα cookies διαγράφηκαν</string>
@ -559,14 +559,12 @@
<string name="database_clean">Τίποτα προς εκκαθάριση</string>
<string name="extension_api_error">Απέτυχε η λήψη λίστας επεκτάσεων</string>
<string name="privacy_policy">Πολιτική απορρήτου</string>
<string name="pref_library_update_manga_restriction">Παράλειψη ενημέρωσης καταχωρήσεων</string>
<string name="pref_update_only_completely_read">Με αδιάβαστο(α) κεφάλαιο(α)</string>
<string name="library_errors_help">Για βοήθεια σχετικά με τον τρόπο διόρθωσης σφαλμάτων ενημέρωσης βιβλιοθήκης, ανατρέξτε στο %1$s</string>
<string name="save_chapter_as_cbz">Αποθήκευση ως αρχείο CBZ</string>
<string name="cancelled">Ακυρώθηκε</string>
<string name="on_hiatus">Σε αναστολή</string>
<string name="publishing_finished">Η έκδοση ολοκληρώθηκε</string>
<string name="action_faq_and_guides">Συχνές ερωτήσεις και οδηγοί</string>
<string name="action_show_manga">Εμφάνιση καταχώρισης</string>
<string name="pref_landscape_zoom">Αυτόματο ζουμ σε ευρείες εικόνες</string>
<string name="action_display_cover_only_grid">Πλέγμα μόνο με εξώφυλλα</string>
@ -577,8 +575,6 @@
<string name="skipped_reason_not_started">Παραλείπεται επειδή δεν έχουν διαβαστεί κεφάλαια</string>
<string name="notification_update_error">%1$d ενημέρωση(ες) απέτυχε(-αν)</string>
<string name="learn_more">Πατήστε για να μάθετε περισσότερα</string>
<string name="channel_skipped">Παραλήφθηκαν</string>
<string name="notification_update_skipped">%1$d ενημέρωση(εις) παραλείφθηκε(-αν)</string>
<string name="rotation_reverse_portrait">Αντίστροφο πορτρέτο</string>
<string name="action_move_to_top_all_for_series">Μετακίνηση σειράς προς τα πάνω</string>
<string name="disabled_nav">Απενεργοποιημένο</string>
@ -721,7 +717,6 @@
<string name="manga_display_modified_interval_title">Ρύθμιση για ενημέρωση κάθε</string>
<string name="action_filter_interval_long">Ανάκτηση μηνιαίως (28 ημέρες)</string>
<string name="action_sort_next_updated">Επόμενη αναμενόμενη ενημέρωση</string>
<string name="manga_modify_calculated_interval_title">Προσαρμογή διαστήματος</string>
<string name="skipped_reason_not_in_release_period">Παραλείφθηκε επειδή δεν αναμενόταν κυκλοφορία σήμερα</string>
<string name="action_ok">Εντάξει</string>
<string name="track_delete_title">Κατάργηση παρακολούθησης %s;</string>
@ -784,4 +779,14 @@
\nΣυνιστάται ένας αποκλειστικός φάκελος.
\n
\nΕπιλεγμένος φάκελος: %2$s</string>
<string name="onboarding_permission_notifications">Άδεια ειδοποιήσεων</string>
<string name="onboarding_permission_install_apps">Άδεια εγκατάστασης εφαρμογών</string>
<string name="onboarding_permission_ignore_battery_opts_description">Αποφύγετε διακοπές σε μακροχρόνιες ενημερώσεις της βιβλιοθήκης, λήψεις και επαναφορές αντιγράφων ασφαλείας.</string>
<string name="onboarding_permission_ignore_battery_opts">Χρήση μπαταρίας στο παρασκήνιο</string>
<string name="onboarding_permission_install_apps_description">Για εγκατάσταση επεκτάσεων πηγών.</string>
<string name="onboarding_permission_notifications_description">Ειδοποιηθείτε για ενημερώσεις της βιβλιοθήκης και άλλων.</string>
<string name="onboarding_permission_action_grant">Παραχώρηση</string>
<string name="available_disk_space_info">Διαθέσιμο: %1$s / Σύνολο: %2$s</string>
<string name="ext_permission_install_apps_warning">Απαιτούνται δικαιώματα για την εγκατάσταση επεκτάσεων. Πατήστε εδώ για παραχώρηση.</string>
<string name="private_settings">Συμπεριλάβετε ευαίσθητες ρυθμίσεις (π.χ. διακριτικά σύνδεσης παρακολούθησης)</string>
</resources>

View File

@ -80,4 +80,9 @@
<item quantity="many">%d días</item>
<item quantity="other">%d días</item>
</plurals>
<plurals name="num_repos">
<item quantity="one">%d repositorio</item>
<item quantity="many">%d repositorios</item>
<item quantity="other">%d repositorios</item>
</plurals>
</resources>

View File

@ -38,7 +38,7 @@
<string name="action_install">Instalar</string>
<string name="loading">Cargando…</string>
<string name="pref_category_general">General</string>
<string name="pref_category_reader">Lector</string>
<string name="pref_category_reader">Visor</string>
<string name="pref_category_downloads">Descargas</string>
<string name="pref_category_tracking">Seguimiento</string>
<string name="pref_category_advanced">Avanzado</string>
@ -54,7 +54,7 @@
<string name="update_48hour">Cada 2 días</string>
<string name="pref_library_update_restriction">Restricciones de actualización automática del dispositivo</string>
<string name="charging">Mientras se carga la batería</string>
<string name="pref_update_only_non_completed">Marcadas como completadas</string>
<string name="pref_update_only_non_completed">Omitir entradas marcadas como \"Completado\"</string>
<string name="pref_auto_update_manga_sync">Actualizar progreso al terminar un capítulo</string>
<string name="pref_fullscreen">Pantalla completa</string>
<string name="pref_page_transitions">Transiciones de página animadas</string>
@ -67,8 +67,8 @@
<string name="white_background">Blanco</string>
<string name="black_background">Negro</string>
<string name="pref_viewer_type">Sentido de lectura normal</string>
<string name="left_to_right_viewer">Por páginas (de izquierda a derecha)</string>
<string name="right_to_left_viewer">Por páginas (de derecha a izquierda)</string>
<string name="left_to_right_viewer">Por páginas, de izquierda a derecha</string>
<string name="right_to_left_viewer">Por páginas, de derecha a izquierda</string>
<string name="vertical_viewer">Por páginas, de arriba abajo</string>
<string name="webtoon_viewer">Tira vertical continua</string>
<string name="pref_image_scale_type">Método de ampliación</string>
@ -92,7 +92,7 @@
<string name="services">Servicios de seguimiento</string>
<string name="pref_clear_chapter_cache">Limpiar la caché de capítulos</string>
<string name="used_cache">Usado: %1$s</string>
<string name="cache_deleted">Se vació la caché. Se han eliminado %1$d archivos</string>
<string name="cache_deleted">Se vació la caché. Se han borrado %1$d archivos</string>
<string name="cache_delete_error">Se produjo un error al limpiar</string>
<string name="pref_clear_cookies">Borrar cookies</string>
<string name="cookies_cleared">Cookies borradas</string>
@ -252,11 +252,11 @@
<string name="transition_no_previous">No hay ningún capítulo anterior</string>
<string name="migrate">Migrar</string>
<string name="copy">Copiar</string>
<string name="untrusted_extension_message">Esta extensión está firmada por una fuente sin certificar y por lo tanto no se ha activado.
<string name="untrusted_extension_message">Esta extensión fue firmada por un autor desconocido y no fue cargada.
\n
\nUna extensión maliciosa puede leer credenciales de inicio guardadas o ejecutar cualquier tipo de código en tu dispositivo.
\nLas extensiones maliciosas pueden leer cualquier credencial de inicio de sesión almacenada o ejecutar código arbitrario.
\n
\nAl confiar en este certificado aceptas estos riesgos.</string>
\nAl confiar en el certificado de esta extensión, aceptas estos riesgos.</string>
<string name="double_tap_anim_speed_0">Sin animación</string>
<string name="manga_added_library">Añadido a biblioteca</string>
<string name="manga_removed_library">Quitado de biblioteca</string>
@ -302,12 +302,12 @@
<string name="action_sort_latest_chapter">Por capítulo más reciente</string>
<string name="action_view_chapters">Ver capítulos</string>
<string name="action_cancel_all">Cancelar todo</string>
<string name="theme_light">Claro</string>
<string name="theme_dark">Oscuro</string>
<string name="theme_system">Sistema</string>
<string name="theme_light">Colores claros</string>
<string name="theme_dark">Colores oscuros</string>
<string name="theme_system">Colores del sistema</string>
<string name="pref_manage_notifications">Gestionar notificaciones</string>
<string name="pref_category_security">Seguridad y privacidad</string>
<string name="lock_with_biometrics">Requiere desbloqueo</string>
<string name="lock_with_biometrics">Desbloqueo biométrico</string>
<string name="lock_when_idle">Bloquear por inactividad</string>
<string name="lock_always">Siempre</string>
<string name="lock_never">Nunca</string>
@ -317,7 +317,7 @@
<string name="information_cloudflare_bypass_failure">Fallo al evitar Cloudflare</string>
<string name="information_webview_outdated">Actualiza la aplicación WebView para mejorar la compatibilidad</string>
<string name="channel_new_chapters">Nuevos capítulos</string>
<string name="secure_screen_summary">Pantalla segura oculta el contenido al cambiar de aplicación y bloquea las capturas de pantalla</string>
<string name="secure_screen_summary">Activando «pantalla segura» ocultas la vista previa del contenido de la ventana al cambiar de aplicación y se bloquean las capturas de pantalla</string>
<string name="pref_category_display">Visualización</string>
<string name="notification_chapters_single">Capítulo %1$s</string>
<string name="notification_chapters_single_and_more">Capítulo %1$s y %2$d más</string>
@ -376,7 +376,7 @@
<string name="label_data">Datos</string>
<string name="backup_restore_missing_sources">Fuentes que faltan:</string>
<string name="invalid_backup_file_missing_manga">La copia de seguridad no contiene ningun elemento; la biblioteca está vacía.</string>
<string name="invalid_backup_file">El archivo de copia de respaldo no es correcto</string>
<string name="invalid_backup_file">El archivo de copia de seguridad es inválido:</string>
<string name="pref_library_update_refresh_metadata_summary">Comprueba si hay una nueva portada, información y descripción al actualizar la biblioteca</string>
<string name="pref_library_update_refresh_metadata">Actualizar automáticamente los metadatos</string>
<string name="action_display_comfortable_grid">Cuadrícula amplia</string>
@ -386,7 +386,7 @@
<string name="page_list_empty_error">No se encontraron páginas</string>
<string name="action_disable_all">Deshabilitar todo</string>
<string name="action_enable_all">Habilitar todo</string>
<string name="pref_show_reading_mode_summary">Mostrar brevemente el modo actual al abrir el lector</string>
<string name="pref_show_reading_mode_summary">Mostrar brevemente el modo actual al abrir el visor</string>
<string name="pref_show_reading_mode">Mostrar modo de lectura</string>
<string name="action_start">Empezar</string>
<string name="loader_not_implemented_error">No se ha encontrado la fuente</string>
@ -453,9 +453,7 @@
<string name="action_order_by_upload_date">Por fecha de subida</string>
<string name="action_filter_tracked">En seguimiento</string>
<string name="right_and_left_nav">Derecha e izquierda</string>
<string name="backup_restore_content_full">Se restaurarán los datos del archivo de respaldo.
\n
\nTendrás que instalar las extensiones que falten e iniciar sesión en los servicios de seguimiento para poder usarlos.</string>
<string name="backup_restore_content_full">Tendrás que instalar las extensiones que falten e iniciar sesión en los servicios de seguimiento para poder usarlos.</string>
<string name="update_check_eol">Esta versión de Android ya no es compatible</string>
<string name="clipboard_copy_error">No se pudo copiar al portapapeles</string>
<string name="pref_dns_over_https">DNS sobre HTTPS (DoH)</string>
@ -470,7 +468,7 @@
<string name="pref_dual_page_invert_summary">Si la partición de la página ancha no coincide con la dirección de lectura</string>
<string name="pref_dual_page_invert">Dividir la página partida en la dirección contraria</string>
<string name="pref_dual_page_split">Partir en dos las páginas anchas</string>
<string name="pref_show_navigation_mode_summary">Muestra una vista previa de las zonas al abrir el lector</string>
<string name="pref_show_navigation_mode_summary">Muestra una vista previa de las zonas al abrir el visor</string>
<string name="pref_show_navigation_mode">Ver superposición con zonas de toque</string>
<string name="exclude">Excluir: %s</string>
<string name="include">Incluir: %s</string>
@ -497,7 +495,7 @@
<string name="pref_app_theme">Tema de la aplicación</string>
<string name="action_start_downloading_now">Empezar a descargar ahora</string>
<string name="cancel_all_for_series">Cancelar todo para esta serie</string>
<string name="action_display_local_badge">Fuente local</string>
<string name="action_display_local_badge">Contenido local</string>
<string name="about_dont_kill_my_app">Algunos fabricantes tienen restricciones adicionales para aplicaciones que detienen los servicios en segundo plano. Esta página web tiene más información sobre cómo solucionarlo.</string>
<string name="information_empty_category_dialog">Todavía no existen categorías.</string>
<string name="error_no_match">No se han encontrado coincidencias</string>
@ -559,26 +557,22 @@
<string name="clear_database_source_item_count">%1$d entradas que no pertenecen a la biblioteca en la base de datos</string>
<string name="extension_api_error">No se pudo descargar el listado de extensiones</string>
<string name="privacy_policy">Política de privacidad</string>
<string name="pref_update_only_completely_read">Con capítulos sin leer</string>
<string name="pref_library_update_manga_restriction">Omitir actualizaciones</string>
<string name="pref_update_only_completely_read">Omitir entradas con capítulos no leídos</string>
<string name="library_errors_help">Si necesitas ayuda para resolver los errores de actualización de la biblioteca mira en %1$s</string>
<string name="save_chapter_as_cbz">Guardar como archivo CBZ</string>
<string name="on_hiatus">En pausa</string>
<string name="publishing_finished">Serie terminada</string>
<string name="cancelled">Cancelada</string>
<string name="action_faq_and_guides">Guías y soluciones</string>
<string name="action_show_manga">Mostrar el elemento</string>
<string name="pref_navigate_pan">Desplazarse por el resto de la página antes de cambiar</string>
<string name="action_display_cover_only_grid">Cuadrícula sólo de portadas</string>
<string name="pref_landscape_zoom">Acercar la vista en horizontal</string>
<string name="pref_update_only_started">Sin empezar</string>
<string name="pref_update_only_started">Omitir entradas sin lectura iniciada</string>
<string name="skipped_reason_completed">Omitido, ya que su publicación ha terminado</string>
<string name="skipped_reason_not_caught_up">Omitido porque hay capítulos sin leer</string>
<string name="skipped_reason_not_started">Omitido porque no hay capítulos leídos</string>
<string name="learn_more">Ver más detalles</string>
<string name="channel_skipped">Omitidas</string>
<string name="notification_update_error">Actualizaciones fallidas: %1$d</string>
<string name="notification_update_skipped">Actualizaciones omitidas: %1$d</string>
<string name="rotation_reverse_portrait">En vertical, al revés</string>
<string name="action_move_to_top_all_for_series">Mover al primer puesto</string>
<string name="disabled_nav">Desactivado</string>
@ -593,12 +587,12 @@
<string name="source_empty_screen">No se ha encontrado ninguna fuente</string>
<string name="action_sort_last_manga_update">Última comprobación de actualizaciones</string>
<string name="action_sort_unread_count">Capítulos restantes</string>
<string name="split_tall_images_summary">Mejora el rendimiento del lector dividiendo páginas descargadas mucho más altas que anchas</string>
<string name="split_tall_images_summary">Mejora el rendimiento del visor, dividiendo páginas descargadas mucho más altas que anchas</string>
<string name="download_notifier_split_page_not_found">No se ha encontrado la página %d al dividir</string>
<string name="download_notifier_split_page_path_not_found">La ruta al archivo de la página %d no se encuentra</string>
<string name="pref_reset_viewer_flags_summary">Restablece el modo de lectura y orientación en toda la biblioteca</string>
<string name="pref_reset_viewer_flags_success">Se han restablecido los ajustes del visor</string>
<string name="pref_reset_viewer_flags">Restablecer los ajustes del lector en cada serie</string>
<string name="pref_reset_viewer_flags">Restablecer los ajustes del visor en cada serie</string>
<string name="pref_reset_viewer_flags_error">No se pudieron restablecer los ajustes del visor</string>
<string name="empty_screen">Houston, tenemos un problema</string>
<string name="ext_info_version">Versión</string>
@ -712,14 +706,13 @@
<string name="pref_library_columns_per_row">%d por fila</string>
<string name="action_filter_interval_late">Tras 10 o más días</string>
<string name="action_sort_next_updated">Próxima actualización prevista</string>
<string name="pref_update_only_in_release_period">Fuera del período de publicación esperado</string>
<string name="pref_update_only_in_release_period">Prever la fecha del próximo lanzamiento</string>
<string name="intervals_header">Intervalos</string>
<string name="action_filter_interval_passed">Ha pasado el período de comprobación</string>
<string name="manga_display_interval_title">Estimar cada</string>
<string name="manga_modify_calculated_interval_title">Personalizar intervalo</string>
<string name="action_filter_interval_dropped">¿Abandonado\? Tras más de 20 días y 2 meses</string>
<string name="manga_display_modified_interval_title">Forzar actualización cada</string>
<string name="skipped_reason_not_in_release_period">No se ha comprobado ninguna actualización hoy al no esperar ningún cambio</string>
<string name="skipped_reason_not_in_release_period">No se ha comprobado ninguna actualización hoy al no prever ninguna</string>
<string name="action_set_interval">Establecer intervalo</string>
<string name="action_filter_interval_custom">Intervalo de descarga personalizado</string>
<string name="action_filter_interval_long">Comprobar de forma mensual (28 días)</string>
@ -759,38 +752,54 @@
<string name="action_apply">Aplicar</string>
<string name="action_revert_to_default">Volver a la configuración predeterminada</string>
<string name="action_create">Crear</string>
<string name="no_scanlators_found">Sin escanducciones</string>
<string name="scanlator">Escanductor</string>
<string name="exclude_scanlators">Excluir escanducciones</string>
<string name="no_scanlators_found">No se ha encontrado ningún equipo de traducción</string>
<string name="scanlator">Equipo de traducción</string>
<string name="exclude_scanlators">Excluir equipo de traducción</string>
<string name="selected">Seleccionado</string>
<string name="not_selected">Sin seleccionar</string>
<string name="action_menu_overflow_description">Más opciones</string>
<string name="action_bar_up_description">Subir un nivel</string>
<string name="pref_storage_location">Ubicación del almacenamiento</string>
<string name="pref_storage_location_info">Se utiliza para las copias de seguridad automáticas, las descargas de capítulos y la fuente local.</string>
<string name="onboarding_storage_action_select">Seleccionar una carpeta</string>
<string name="pref_onboarding_guide">Guía de incorporación</string>
<string name="onboarding_guides_new_user">¿Nuevo en %s? Recomendamos consultar la guía de introducción.</string>
<string name="pref_storage_location_info">Se utiliza para las copias de seguridad automáticas, poder descargar capítulos y abrir los que ya tengas en tu dispositivo.</string>
<string name="onboarding_storage_action_select">Elige una carpeta</string>
<string name="pref_onboarding_guide">Guía para principiantes</string>
<string name="onboarding_guides_new_user">¿Es la primera vez que instalas %s? Te recomendamos leer la guía de introducción.</string>
<string name="onboarding_action_finish">Comenzar</string>
<string name="onboarding_storage_selection_required">Debe seleccionarse una carpeta</string>
<string name="onboarding_heading">Bienvenido!</string>
<string name="onboarding_guides_returning_user">¿Ya usaste %s antes?</string>
<string name="onboarding_storage_selection_required">Tienes que elegir una carpeta</string>
<string name="onboarding_heading">Te damos la bienvenida</string>
<string name="onboarding_guides_returning_user">¿No es la primera vez que instalas %s?</string>
<string name="onboarding_action_skip">Saltar</string>
<string name="onboarding_action_next">Siguiente</string>
<string name="onboarding_description">Vamos a configurar algunas cosas primero. Siempre puedes volver a cambiarlas más tarde en la configuración.</string>
<string name="no_location_set">No se ha establecido una ubicación de almacenamiento</string>
<string name="onboarding_storage_info">Selecciona una carpeta donde %1$s almacenará las descargas de capítulos, copias de seguridad y s.
<string name="onboarding_description">Lo primero de todo es poner las cosas a tu gusto. Siempre puedes volver a cambiarlas más tarde en los ajustes.</string>
<string name="no_location_set">Todavía no has proporcionado una ubicación de almacenamiento</string>
<string name="onboarding_storage_info">Selecciona una carpeta donde %1$s almacenará las descargas de capítulos, copias de seguridad y otras cosas.
\n
\nSe recomienda una carpeta dedicada.
\nTe recomendamos que sea solo para %1$s.
\n
\nCarpeta seleccionada: %2$s</string>
<string name="onboarding_permission_install_apps">Permiso para instalar aplicaciones</string>
<string name="onboarding_permission_type_optional">Opcional</string>
<string name="onboarding_permission_type_required">Requerido</string>
<string name="onboarding_permission_notifications">Permiso de notificación</string>
<string name="onboarding_permission_ignore_battery_opts_description">Evitar interrupciones en las actualizaciones largas de la biblioteca, descargas y restauraciones de copias de seguridad.</string>
<string name="onboarding_permission_ignore_battery_opts_description">Previene cortes y retrasos al procesar tareas en segundo plano que tarden un poco; como al buscar y descargar contenido nuevo, así como al restaurar copias de respaldo.</string>
<string name="onboarding_permission_ignore_battery_opts">Uso de batería en segundo plano</string>
<string name="onboarding_permission_install_apps_description">Para instalar las extensiones de fuentes.</string>
<string name="onboarding_permission_notifications_description">Recibe notificaciones sobre actualizaciones de la biblioteca y más.</string>
<string name="onboarding_permission_action_grant">Permitir</string>
<string name="onboarding_permission_install_apps_description">Para instalar extensiones que te permiten buscar y descargar contenido.</string>
<string name="onboarding_permission_notifications_description">Recibe notificaciones cuando aparece contenido nuevo en tu biblioteca y otras cosas.</string>
<string name="onboarding_permission_action_grant">Conceder</string>
<string name="available_disk_space_info">Disponible: %1$s / Total: %2$s</string>
<string name="ext_permission_install_apps_warning">Toca aquí para conceder los permisos necesarios para instalar extensiones.</string>
<string name="private_settings">Incluir datos privados, como las claves de inicio de sesión en plataformas de seguimiento</string>
<string name="invalid_backup_file_error">Error completo:</string>
<string name="manga_interval_expected_update">Próxima actualización prevista en torno a %1$s, revisando cada %2$s</string>
<string name="repo_extension_message">Esta extensión proviene de un repositorio externo. Toca para ver el repositorio.</string>
<string name="manga_interval_custom_amount">Frecuencia de actualización personalizada:</string>
<string name="error_repo_exists">¡Este repositorio ya existe!</string>
<string name="pref_library_update_smart_update">Actualización inteligente</string>
<string name="invalid_repo_name">URL de repositorio inválida</string>
<string name="action_add_repo_message">Agregar repositorios adicionales a Tachiyomi. La URL debe terminar con \"index.min.json\".</string>
<string name="delete_repo_confirmation">¿Deseas eliminar el repositorio \"%s\"?</string>
<string name="action_delete_repo">Eliminar repositorio</string>
<string name="manga_interval_header">Próxima actualización</string>
<string name="action_add_repo">Agregar repositorio</string>
<string name="information_empty_repos">No tienes repositorios agregados.</string>
<string name="label_extension_repos">Repositorios de extensiones</string>
<string name="label_add_repo_input">URL del repositorio</string>
</resources>

View File

@ -357,7 +357,6 @@
<string name="snack_add_to_library">Manga liburutegira gehitu\?</string>
<string name="file_select_backup">Hautatu babeskopia fitxategia</string>
<string name="notification_cover_update_failed">Ezin izan da azala eguneratu</string>
<string name="pref_library_update_manga_restriction">Saltatu eguneraketak</string>
<string name="action_sort_date_added">Gehiketa data</string>
<string name="action_search">Bilatu</string>
<string name="action_search_settings">Bilatu ezarpenak</string>
@ -566,7 +565,6 @@
<string name="pref_dual_page_invert">Alderantzikatu orrialde bikoitzeko zatiketa</string>
<string name="pref_dual_page_invert_summary">Orrialde bikoitzeko zatiketaren kokapena irakurketa noranzkoarekin bat ez badator</string>
<string name="pref_library_update_restriction">Eguneraketa automatikoentzako gailuaren murrizketak</string>
<string name="action_faq_and_guides">Ohiko galderak eta gidak</string>
<string name="action_show_manga">Manga erakutsi</string>
<string name="skipped_reason_not_caught_up">Saltatu egin da irakurri gabeko kapituluak daudelako</string>
<string name="pref_update_only_started">Kapitulurik irakurri gabe</string>
@ -590,4 +588,21 @@
<string name="action_not_now">Orain ez</string>
<string name="delete_downloaded">Ezabatu deskargatuak</string>
<string name="pref_library_columns_per_row">%d ilara bakoitzeko</string>
<string name="onboarding_storage_action_select">Aukeratu karpeta bat</string>
<string name="action_apply">Ezarri</string>
<string name="onboarding_storage_selection_required">Karpeta bat aukeratu behar da</string>
<string name="onboarding_permission_notifications">Notifikazioetarako baimena</string>
<string name="onboarding_permission_install_apps">Aplikazioak instalatzeko baimena</string>
<string name="action_sort_category">Kategoriak ordenatu</string>
<string name="onboarding_heading">Ongi etorri!</string>
<string name="action_update_category">Kategoria eguneratu</string>
<string name="action_menu_overflow_description">Aukera gehiago</string>
<string name="onboarding_guides_returning_user">%s erabili duzu dagoeneko?</string>
<string name="selected">Aukeratuak</string>
<string name="not_selected">Aukeratu gabeak</string>
<string name="onboarding_action_next">Hurrengoa</string>
<string name="action_bar_up_description">Nabigatu gora</string>
<string name="sort_category_confirmation">Kategoriak alfabetikoki ordenatu nahi?</string>
<string name="action_display_show_continue_reading_button">Jarraitu irakurtzen botoia</string>
<string name="action_ok">OK</string>
</resources>

View File

@ -465,7 +465,6 @@
<string name="pref_navigate_pan">هنگام ضربه زدن تصاویر را عریض کنید</string>
<string name="pref_landscape_zoom">بزرگنمایی تصویر چشم انداز</string>
<string name="update_check_fdroid_migration_info">نسخه جدیدی از نسخه‌های رسمی موجود است. برای یادگیری نحوه انتقال از نسخه‌های غیررسمی اف دروید، ضربه بزنید.</string>
<string name="channel_skipped">رد شده</string>
<string name="action_sort_last_manga_update">بررسی آخرین به روزرسانی</string>
<string name="action_sort_unread_count">تعداد نخوانده‌ها</string>
<string name="skipped_reason_completed">به دلیل کامل‌بودن سری، رد شد</string>
@ -478,7 +477,6 @@
<string name="action_order_by_chapter_number">بر اساس شماره‌ی قسمت</string>
<string name="action_asc">صعودی</string>
<string name="action_display_language_badge">زبان</string>
<string name="action_faq_and_guides">پرسش و پاسخ‌های متداول و راهنمایی‌ها</string>
<string name="action_show_manga">نمایش ورودی</string>
<string name="cancel_all_for_series">لغو همه برای این سری</string>
<string name="pref_dark_theme_pure_black">حالت تاریک مشکی خالص</string>
@ -498,7 +496,6 @@
<string name="pref_update_only_started">که شروع نشده‌اند</string>
<string name="restrictions">محدودیت‌ها: %s</string>
<string name="pref_update_only_completely_read">با قسمت‌(های) خوانده‌نشده</string>
<string name="pref_library_update_manga_restriction">از به‌روزرسانی ورودی‌ها صرف‌نظر کنید</string>
<string name="relative_time_today">امروز</string>
<string name="update_72hour">هر 3 روز</string>
<string name="connected_to_wifi">فقط روی Wi-Fi</string>
@ -615,7 +612,6 @@
<string name="download_queue_size_warning">هشدار: حجم زیاد بارگیری ممکن است باعث اهسته تر شدن سرعت ویا مسدود کردن Tachiyomi از منبع شود. برای اطلاعات بیشتر لمس کنید.</string>
<string name="skipped_reason_not_always_update">به دلیل این که این مجموعه نیازی به به روز رسانی نداشت رد شد</string>
<string name="skipped_reason_not_caught_up">به دلیل وجود چپتر های خوانده نشده رد شد</string>
<string name="notification_update_skipped">%1$d بزور رسانی رد شد</string>
<string name="skipped_reason_not_started">به دلیل این که هیچ چپتری خوانده نشده بود رد شد</string>
<string name="notification_update_error">%1$d بروز رسانی ناموفق</string>
<string name="pref_relative_format_summary">\"%1$s\" به جای \"%2$s\"</string>

View File

@ -520,8 +520,6 @@
<string name="label_warning">Varoitus</string>
<string name="confirm_lock_change">Tunnistaudu vahvistaaksesi muutokset</string>
<string name="action_display_language_badge">Kieli</string>
<string name="action_faq_and_guides">Usein kysytyt kysymykset ja oppaat</string>
<string name="pref_library_update_manga_restriction">Ohita päivitys</string>
<string name="action_move_to_top_all_for_series">Siirrä sarja päällimmäiseksi</string>
<string name="theme_tako">Tako</string>
<string name="update_72hour">Joka 3. Päivä</string>
@ -610,14 +608,12 @@
<string name="pref_verbose_logging">Yksityiskohtainen kirjaaminen</string>
<string name="cover_saved">Kansikuva tallennettu</string>
<string name="error_saving_cover">Virhe tallentaessa kansikuvaa</string>
<string name="notification_update_skipped">%1$d päivitys(tä) ohitettu</string>
<string name="loader_rar5_error">RARv5-muoto ei ole tuettu</string>
<string name="skipped_reason_not_caught_up">Ohitettu, koska sarjassa on luettomia lukuja</string>
<string name="skipped_reason_not_always_update">Ohitettu, koska sarja ei vaadi päivityksiä</string>
<string name="remove_manga">Olet poistamassa \"%s\" kirjastostasi</string>
<string name="update_check_fdroid_migration_info">Uusi versio on saatavilla viralliselta alustalta. Napauta saadaksesi tietää, miten siirtyä pois epävirallisesta F-Droid versiosta.</string>
<string name="pref_verbose_logging_summary">Kirjoittaa yksityiskohtaiset lokit järjestelmälokiin (heikentää sovelluksen suorituskykyä)</string>
<string name="channel_skipped">Ohitettu</string>
<string name="backup_info">Varmuuskopioita kannattaa säilyttää myös muissa paikoissa.</string>
<string name="wish_list">Toivelista</string>
<string name="cant_open_last_read_chapter">Viimeksi luettua lukua ei voitu avata</string>

View File

@ -65,7 +65,7 @@
<string name="action_sort_alpha">Pa-alpabeto</string>
<string name="action_filter_empty">Tanggalin ang pansala</string>
<string name="action_filter_unread">Hindi Nabasa</string>
<string name="action_filter_bookmarked">Tinandaan</string>
<string name="action_filter_bookmarked">Na-bookmark</string>
<string name="action_filter">Pansala</string>
<string name="action_menu">Menu</string>
<string name="action_settings">Mga setting</string>
@ -152,7 +152,7 @@
<string name="black_background">Itim</string>
<string name="gray_background">Abo</string>
<string name="white_background">Puti</string>
<string name="pref_reader_theme">Likurang kulay</string>
<string name="pref_reader_theme">Kulay ng background</string>
<string name="pref_read_with_long_tap">Ipakita ang mga aksyon sa mahabang tap</string>
<string name="pref_read_with_volume_keys_inverted">Baligtarin ang pindutan ng volume</string>
<string name="pref_read_with_volume_keys">Pindutan ng volume</string>
@ -412,7 +412,7 @@
<string name="channel_errors">Mga error</string>
<string name="channel_progress">Takbo</string>
<string name="backup_restore_missing_trackers">Di naka-login sa mga tracker:</string>
<string name="pref_remove_bookmarked_chapters">Burahin din ang may pananda</string>
<string name="pref_remove_bookmarked_chapters">Payagan ang pagtanggal ng mga naka-bookmark na kabanata</string>
<string name="pref_category_delete_chapters">Magbura ng kabanata</string>
<string name="parental_controls_info">Hindi nito naiiwasan ang pagpapakita ng mga NSFW (18+) sa mga di-opisyal o posibleng maling naka-flag na extension sa loob ng app.</string>
<string name="ext_nsfw_warning">Posibleng may NSFW (18+) content ang mga source mula sa extension na ito</string>
@ -451,7 +451,7 @@
<string name="action_asc">Pataas</string>
<string name="action_order_by_chapter_number">Kabanata</string>
<string name="action_order_by_upload_date">Petsa in-upload</string>
<string name="action_filter_tracked">Sinundan</string>
<string name="action_filter_tracked">Sinusubaybayan</string>
<string name="right_and_left_nav">Kaliwa at Kanan</string>
<string name="pref_dual_page_split">Hatiin ang mga malalapad na pahina</string>
<string name="action_display_show_number_of_items">Ipakita ang bilang ng mga item</string>
@ -561,12 +561,10 @@
<string name="privacy_policy">Patakaran sa Pagkapribado</string>
<string name="library_errors_help">Para sa tulong sa pag-aayos ng mga error sa pag-update ng aklatan, tingnan ang %1$s</string>
<string name="pref_update_only_completely_read">May di pa nababasang kabanata</string>
<string name="pref_library_update_manga_restriction">Laktawan ang pag-update</string>
<string name="save_chapter_as_cbz">I-save bilang CBZ archive</string>
<string name="publishing_finished">Tapos na\'ng mailathala</string>
<string name="on_hiatus">Naka-hiatus</string>
<string name="cancelled">Kinansela</string>
<string name="action_faq_and_guides">Mga Madalas Itanong (FAQ) at Gabay</string>
<string name="action_show_manga">Ipakita ang entry</string>
<string name="action_display_cover_only_grid">Pabalat lang</string>
<string name="skipped_reason_completed">Nilaktawan dahil kumpleto na ang serye</string>
@ -576,10 +574,8 @@
<string name="pref_landscape_zoom">Awtomatikong mag-zoom sa mga malalawak na larawan</string>
<string name="pref_navigate_pan">I-pan ang mga malalapad na larawan</string>
<string name="learn_more">Matuto pa</string>
<string name="channel_skipped">Nilaktawan</string>
<string name="notification_update_error">Nabigo ang %1$d (na) update</string>
<string name="rotation_reverse_portrait">Baligtad na patayo</string>
<string name="notification_update_skipped">Nilaktawan ang %1$d (na) update</string>
<string name="action_move_to_top_all_for_series">Ilagay sa taas ang serye</string>
<string name="disabled_nav">Nakasara</string>
<string name="empty_backup_error">Walang entry sa Aklatan na maiba-backup</string>
@ -721,7 +717,6 @@
<string name="manga_display_modified_interval_title">Itakdang i-update bawat</string>
<string name="pref_update_only_in_release_period">Sa labas ng inaasahang release period</string>
<string name="intervals_header">Mga pagitan</string>
<string name="manga_modify_calculated_interval_title">Ayusin ang interval</string>
<string name="skipped_reason_not_in_release_period">Nilaktawan dahil walang inaasahang release ngayong araw</string>
<string name="has_results">May mga resulta</string>
<string name="delete_downloaded">Tanggalin ang na-download</string>
@ -784,4 +779,16 @@
\nInirerekomenda ang isang nakalaang folder.
\n
\nNapiling folder: %2$s</string>
<string name="onboarding_permission_notifications">Pahintulot sa pag-abiso</string>
<string name="onboarding_permission_install_apps">Pahintulot sa pag-install ng mga app</string>
<string name="onboarding_permission_ignore_battery_opts_description">Ma-iwasan ang mga hadlang sa mahahabang pag-update ng aklatan, pag-download, at pag-restore ng mga backup.</string>
<string name="onboarding_permission_ignore_battery_opts">Paggamit ng baterya sa background</string>
<string name="onboarding_permission_install_apps_description">Para mag-install ng mga extension ng source.</string>
<string name="onboarding_permission_notifications_description">Maabisuhan para sa mga update sa aklatan at higit pa.</string>
<string name="onboarding_permission_action_grant">Payagan</string>
<string name="available_disk_space_info">Na magagamit: %1$s / Kabuuan: %2$s</string>
<string name="manga_interval_expected_update">Inaasahan ang susunod na update sa humigit-kumulang %s</string>
<string name="invalid_backup_file_error">Buong error:</string>
<string name="ext_permission_install_apps_warning">Kinakailangan ng permiso para mag-install ng mga extension. I-tap upang mapayagan ito.</string>
<string name="private_settings">Kasali ang mga sensitibong setting (hal., mga tracker login token)</string>
</resources>

View File

@ -114,7 +114,7 @@
<string name="pref_download_new">Télécharger les nouveaux chapitres</string>
<string name="services">Services</string>
<string name="used_cache">Utilisé : %1$s</string>
<string name="cache_deleted">Cache effacé. %1$d fichiers ont été supprimés</string>
<string name="cache_deleted">Cache effacé, %1$d fichiers supprimés</string>
<string name="cache_delete_error">Une erreur est survenue lors de l\'effacement du cache</string>
<string name="pref_clear_cookies">Effacer les cookies</string>
<string name="cookies_cleared">Cookies effacés</string>
@ -301,9 +301,9 @@
<string name="action_sort_latest_chapter">Dernier chapitre</string>
<string name="action_view_chapters">Voir les chapitres</string>
<string name="action_cancel_all">Tout annuler</string>
<string name="theme_light">Désactivé</string>
<string name="theme_dark">Activé</string>
<string name="theme_system">Par défaut du système</string>
<string name="theme_light">Clair</string>
<string name="theme_dark">Sombre</string>
<string name="theme_system">Système</string>
<string name="pref_manage_notifications">Notifications</string>
<string name="pref_category_security">Sécurité et confidentialité</string>
<string name="lock_with_biometrics">Nécessite un déverrouillage</string>
@ -457,9 +457,7 @@
<string name="pref_dual_page_split">Diviser les pages larges</string>
<string name="pref_dual_page_invert_summary">Si l\'emplacement des pages larges divisées ne correspond pas au sens de lecture</string>
<string name="pref_dual_page_invert">Inverser le placement des pages divisées</string>
<string name="backup_restore_content_full">Les données du fichier de sauvegarde seront restaurées.
\n
\nVous devrez installer les extensions manquantes et vous connecter ensuite aux services de suivi pour les utiliser.</string>
<string name="backup_restore_content_full">Vous devrez installer les extensions manquantes et vous connecter ensuite aux services de suivi pour les utiliser.</string>
<string name="nav_zone_prev">Précédent</string>
<string name="pref_dns_over_https">DNS over HTTPS (DoH)</string>
<string name="nav_zone_right">Droite</string>
@ -559,14 +557,12 @@
<string name="database_clean">Rien à effacer</string>
<string name="extension_api_error">Échec de la récupération de la liste des extensions</string>
<string name="privacy_policy">Politique de confidentialité</string>
<string name="pref_library_update_manga_restriction">Ignorer la mise à jour</string>
<string name="pref_update_only_completely_read">Avec des chapitres non lus</string>
<string name="save_chapter_as_cbz">Enregistrer comme archive CBZ</string>
<string name="library_errors_help">Pour savoir comment corriger les erreurs de mise à jour de la bibliothèque, voir %1$s</string>
<string name="on_hiatus">En pause</string>
<string name="publishing_finished">Publication terminée</string>
<string name="cancelled">Annulé</string>
<string name="action_faq_and_guides">FAQ et guides</string>
<string name="action_show_manga">Afficher le titre</string>
<string name="action_display_cover_only_grid">Grille avec seulement la couverture</string>
<string name="pref_navigate_pan">Panoramique des images larges</string>
@ -576,9 +572,7 @@
<string name="skipped_reason_not_started">Sauté car aucun chapitre n\'est lu</string>
<string name="skipped_reason_not_caught_up">Sauté car il y a des chapitres non lus</string>
<string name="learn_more">Appuyez pour en savoir plus</string>
<string name="notification_update_skipped">%1$d mise(s) à jour ignorée(s)</string>
<string name="notification_update_error">Échec de %1$d mise(s) à jour</string>
<string name="channel_skipped">Ignoré</string>
<string name="rotation_reverse_portrait">Portrait inversé</string>
<string name="action_move_to_top_all_for_series">Déplacer la série vers le haut</string>
<string name="disabled_nav">Désactivé</string>
@ -732,7 +726,6 @@
<string name="manga_display_interval_title">Estimer chaque</string>
<string name="exception_http">HTTP %d, consulter le site Web dans WebView</string>
<string name="manga_display_modified_interval_title">Configurer pour mettre à jour tous les</string>
<string name="manga_modify_calculated_interval_title">Personnaliser l\'intervalle</string>
<string name="action_move_to_bottom_all_for_series">Déplacer la série vers le bas</string>
<string name="track_delete_remote_text">Supprimez également de %s</string>
<string name="exception_unknown_host">Impossible de joindre %s</string>
@ -745,11 +738,24 @@
<string name="selected">Sélectionné</string>
<string name="not_selected">Pas sélectionné(e)</string>
<string name="scanlator">Scanlator</string>
<string name="pref_flash_page">Faire l\'écran clignoter sur le changement de page</string>
<string name="pref_flash_page">Flash lors du changement de page</string>
<string name="action_bar_up_description">Naviguer vers le haut</string>
<string name="action_sort_tracker_score">Score du service de suivi</string>
<string name="label_data_storage">Données et stockage</string>
<string name="sort_category_confirmation">Voulez-vous trier les catégories par ordre alphabétique ?</string>
<string name="track_activity_name">Se connecter au service de suivi</string>
<string name="pref_relative_format_summary">« %1$s » au lieu de « %2$s »</string>
<string name="pref_storage_location">Emplacement de stockage</string>
<string name="pref_flash_page_summ">Réduire le ghosting sur les écrans e-ink</string>
<string name="pref_storage_location_info">Utilisé pour les backups automatiques, téléchargements des chapitres et la source locale.</string>
<string name="onboarding_storage_selection_required">Un dossier doit être sélectionné</string>
<string name="available_disk_space_info">Disponible: %1$s / Total: %2$s</string>
<string name="onboarding_heading">Bienvenue!</string>
<string name="onboarding_action_skip">Passer</string>
<string name="pref_storage_usage">Utilisation du stockage</string>
<string name="onboarding_action_next">Suivant</string>
<string name="no_location_set">Aucun emplacement de stockage défini</string>
<string name="ext_permission_install_apps_warning">Des permissions sont nécessaires pour installer des extensions. Appuyer ici pour les accorder.</string>
<string name="onboarding_permission_action_grant">Accorder</string>
<string name="pref_relative_format">Durées relatives</string>
</resources>

View File

@ -418,7 +418,6 @@
<string name="tracking_guide">Guía de seguemento</string>
<string name="on_hiatus">En pausa</string>
<string name="action_order_by_chapter_number">Por número de capítulo</string>
<string name="action_faq_and_guides">Preguntas frecuentes e guías</string>
<string name="theme_tako">Tako</string>
<string name="theme_yotsuba">Yotsuba</string>
<string name="theme_monet">Dinámico</string>
@ -482,7 +481,6 @@
<string name="cancel_all_for_series">Cancelar todo para esta serie</string>
<string name="action_order_by_upload_date">Por data de subida</string>
<string name="action_show_errors">Preme para ver os detalles do erro</string>
<string name="pref_library_update_manga_restriction">Omitir actualizacións</string>
<string name="restrictions">Restricións: %s</string>
<string name="categorized_display_settings">Opcións de ordenación e visualización por categoría</string>
<string name="pref_dual_page_split">Separar as páxinas anchas</string>
@ -629,7 +627,6 @@
<string name="show_chapter_number">Número do capítulo</string>
<string name="download_notifier_split_page_path_not_found">Non se atopou a ruta do ficheiro da páxina %d</string>
<string name="channel_complete">Rematada</string>
<string name="channel_skipped">Omitidas</string>
<string name="channel_new_chapters">Actualizacións de capítulos</string>
<string name="information_webview_required">Tachiyomi necesita WebView</string>
<string name="download_notifier_split_page_not_found">Non se atopou a páxina %d ao separar</string>
@ -686,7 +683,6 @@
<string name="seconds_short">%dseg</string>
<string name="label_tracked_titles">Elementos en seguimento</string>
<string name="label_mean_score">Puntuación media</string>
<string name="notification_update_skipped">Actualizacións omitidas: %1$d</string>
<string name="skipped_reason_not_started">Omitiuse porque non hai capítulos lidos</string>
<string name="notification_chapters_single_and_more">Capítulo %1$s e %2$d máis</string>
<string name="notification_chapters_multiple">Capítulos %1$s</string>

View File

@ -379,9 +379,7 @@
<string name="pref_show_reading_mode">הראה מצב קריאה</string>
<string name="ext_unofficial">בלתי רשמי</string>
<string name="ext_app_info">פרטי האפליקצייה</string>
<string name="pref_library_update_manga_restriction">דלג על עדכוני פריטים</string>
<string name="extension_api_error">נכשל בקבלת רשימת ההרחבות</string>
<string name="action_faq_and_guides">שו\"ת ומדריכים</string>
<string name="ext_nsfw_short">18+</string>
<string name="none">אין</string>
<string name="update_72hour">כל 3 ימים</string>
@ -527,7 +525,6 @@
<string name="action_sort_unread_count">לא נקראו</string>
<string name="categorized_display_settings">הגדרות מיון לכל קטגוריה בנפרד</string>
<string name="source_empty_screen">מקור לא נמצא</string>
<string name="notification_update_skipped">%1$d עדכונים דולגו</string>
<string name="update_check_eol">גרסת האנדרואיד הזאת כבר לא נתמכת</string>
<string name="information_webview_required">WebView נצרך ל-Tachiyomi</string>
<string name="action_display_cover_only_grid">רשת רק של הכריכות</string>
@ -539,7 +536,6 @@
<string name="skipped_reason_not_caught_up">דולג בגלל שיש פרקים שלא נקראו</string>
<string name="skipped_reason_not_started">דולג בגלל שאין פרקים שנקראו</string>
<string name="action_close">סגור</string>
<string name="channel_skipped">דולגו</string>
<string name="pref_true_color_summary">מפחית פסים, אך עשוי להשפיע על הביצועים</string>
<string name="channel_errors">שגיאות</string>
<string name="pref_reset_viewer_flags">אפס את הגדרות מצב הקריאה של כל סדרה בנפרד</string>
@ -588,7 +584,6 @@
<string name="action_filter_interval_custom">קבע מרווח זמן מותאם מראש לטעינה</string>
<string name="action_filter_interval_long">טען חודשית (28 ימים)</string>
<string name="unknown_title">כותרת לא מוכרת</string>
<string name="manga_modify_calculated_interval_title">בחר מרווח זמן מותאם אישית</string>
<string name="download_notifier_downloader_title">מנהל ההורדות</string>
<string name="exception_http">HTTP %d, בדוק באתר ב-WebView</string>
<string name="exception_offline">אין חיבור אינטרנט</string>

View File

@ -558,16 +558,13 @@
<string name="pref_auto_clear_chapter_cache">ऐप लॉन्च पर चैप्टर कैशे साफ़ करें</string>
<string name="database_clean">साफ़ करने के लिए कुछ नहीं है</string>
<string name="pref_update_only_completely_read">अपठित अध्याय हैं</string>
<string name="pref_library_update_manga_restriction">शीर्षक अद्यतन न करें</string>
<string name="save_chapter_as_cbz">CBZ आर्कैव के रूप में सहेजें</string>
<string name="privacy_policy">गोपनीयता नीति</string>
<string name="library_errors_help">लाइब्रेरी अपडेट त्रुटियों को ठीक करने के तरीके पर सहायता के लिए, %1$s देखें</string>
<string name="extension_api_error">एक्सटेंशन सूची प्राप्त करने में विफल</string>
<string name="action_faq_and_guides">सामान्य प्रश्नोत्तर और मार्गदर्शन</string>
<string name="cancelled">रद्द किया गया</string>
<string name="on_hiatus">अंतराल पर है</string>
<string name="publishing_finished">प्रकाशन समाप्त</string>
<string name="channel_skipped">छोड़े गए</string>
<string name="learn_more">अधिक जानकारी के लिए दबाए</string>
<string name="skipped_reason_completed">छोड़े गए क्योंकि श्रृंखला पूरी हो गई है</string>
<string name="skipped_reason_not_caught_up">छोड़ दिया गया क्योंकि वहाँ अपठित अध्याय हैं</string>
@ -578,7 +575,6 @@
<string name="disabled_nav">अक्षम</string>
<string name="rotation_reverse_portrait">रिवर्स पोर्ट्रेट</string>
<string name="notification_update_error">%1$d अपडेट विफल</string>
<string name="notification_update_skipped">%1$d अपडेट छोड़े गए</string>
<string name="pref_navigate_pan">चौड़ी छवियां पैन करें</string>
<string name="pref_landscape_zoom">ज़ूम लैंडस्केप इमेज</string>
<string name="action_move_to_top_all_for_series">श्रृंखला को शीर्ष पर ले जाएं</string>

View File

@ -47,9 +47,9 @@
<string name="pref_category_security">Sigurnost i privatnost</string>
<string name="pref_manage_notifications">Upravljaj obavijestima</string>
<string name="pref_date_format">Format datuma</string>
<string name="theme_system">Slijedi sustav</string>
<string name="theme_dark">Uključeno</string>
<string name="theme_light">Isključeno</string>
<string name="theme_system">Sustav</string>
<string name="theme_dark">Tamna</string>
<string name="theme_light">Svijetla</string>
<string name="pref_category_about">Informacije</string>
<string name="pref_category_advanced">Napredno</string>
<string name="pref_category_tracking">Praćenje</string>
@ -345,7 +345,7 @@
<string name="cookies_cleared">Kolačići su izbrisani</string>
<string name="pref_clear_cookies">Ukloni kolačiće</string>
<string name="cache_delete_error">Došlo je do greške prilikom brisanja</string>
<string name="cache_deleted">Predmemorija je prazna. Broj izbrisanih datoteka: %1$d</string>
<string name="cache_deleted">Predmemorija je izrisana. Broj izbrisanih datoteka: %1$d</string>
<string name="used_cache">Korišteno: %1$s</string>
<string name="pref_clear_chapter_cache">Isprazni predmemoriju poglavlja</string>
<string name="restoring_backup_canceled">Obnavljanje prekinuto</string>
@ -519,7 +519,7 @@
<string name="pref_dark_theme_pure_black">Potpuno crna tamna tema</string>
<string name="theme_strawberrydaiquiri">Strawberry Daiquiri</string>
<string name="pref_category_appearance">Izgled</string>
<string name="getting_started_guide">Početni vodič</string>
<string name="getting_started_guide">Vodič za pokretanje</string>
<string name="confirm_lock_change">Ovjeri za potvrditi promjenu</string>
<string name="label_default">Zadano</string>
<string name="restore_miui_warning">Spremanje sigurnosne kopije i obnavljanje možda neće ispravno raditi, ako MIUI optimizacija nije aktivirana.</string>
@ -553,7 +553,6 @@
<string name="enhanced_tracking_info">Pruža poboljšane značajke za određene izvore. Unosi se automatski prate kada se dodaju u biblioteku.</string>
<string name="action_track">Prati</string>
<string name="privacy_policy">Politika privatnosti</string>
<string name="pref_library_update_manga_restriction">Preskoči ažuriranje unosa</string>
<string name="extension_api_error">Neuspjelo preuzimanje popisa proširenja</string>
<string name="ext_installer_pref">Instalacijski program</string>
<string name="ext_installer_legacy">Stari način</string>
@ -566,7 +565,6 @@
<string name="on_hiatus">Zaustavljeno</string>
<string name="library_errors_help">Za pomoć o tome kako popraviti greške aktualiziranja biblioteke, pogledaj %1$s</string>
<string name="save_chapter_as_cbz">Spremi kao CBZ arhivu</string>
<string name="action_faq_and_guides">ČPP i vodiči</string>
<string name="action_show_manga">Prikaži unos</string>
<string name="action_display_cover_only_grid">Samo naslovnice</string>
<string name="pref_navigate_pan">Panoramski prikaz širokih slika</string>
@ -577,8 +575,6 @@
<string name="skipped_reason_not_caught_up">Preskočeno, jer postoje nepročitana poglavlja</string>
<string name="notification_update_error">Nauspjela aktualiziranja: %1$d</string>
<string name="learn_more">Dodirni za daljnje informacije</string>
<string name="channel_skipped">Preskočeno</string>
<string name="notification_update_skipped">Preskočena aktualiziranja: %1$d</string>
<string name="rotation_reverse_portrait">Preokrenuto uspravno</string>
<string name="action_move_to_top_all_for_series">Pomakni seriju na vrh</string>
<string name="disabled_nav">Deaktivirano</string>
@ -613,7 +609,7 @@
<string name="multi_lang">Višejezičnost</string>
<string name="missing_storage_permission">Dozvole za spremanje nisu odobrena</string>
<string name="theme_tidalwave">Tsunami</string>
<string name="invalid_location">Nevažeća lokacija: %s</string>
<string name="invalid_location">Nevažeće mjesto: %s</string>
<string name="pref_advanced_summary">Zapisnici iznenadnog gašenja aplikacije, optimizacije baterije</string>
<string name="label_started">Započeto</string>
<string name="label_local">Lokalno</string>
@ -714,7 +710,6 @@
<string name="action_filter_interval_custom">Prilagođeni interval dohvaćanja</string>
<string name="action_filter_interval_long">Mjesečno dohvaćanje (28 dana)</string>
<string name="manga_display_interval_title">Procijeni svakih</string>
<string name="manga_modify_calculated_interval_title">Prilagodi interval</string>
<string name="action_sort_next_updated">Sljedeće očekivano aktualiziranje</string>
<string name="manga_display_modified_interval_title">Postavi za aktualiziranje svakih</string>
<string name="action_ok">U redu</string>
@ -768,4 +763,27 @@
<string name="selected">Odabrano</string>
<string name="not_selected">Neodabrano</string>
<string name="action_bar_up_description">Navigiraj prema gore</string>
<string name="onboarding_storage_action_select">Odaberi mapu</string>
<string name="onboarding_action_finish">Započni</string>
<string name="onboarding_storage_selection_required">Moraš odabrati jednu mapu</string>
<string name="onboarding_permission_notifications">Dozvola za obavijesti</string>
<string name="onboarding_permission_install_apps">Dozvola za instaliranje aplikacija</string>
<string name="onboarding_action_skip">Preskoči</string>
<string name="onboarding_action_next">Dalje</string>
<string name="onboarding_permission_install_apps_description">Za instaliranje izvornih proširenja.</string>
<string name="onboarding_permission_notifications_description">Primaj obavijesti o aktualiziranju biblioteke i više.</string>
<string name="onboarding_permission_action_grant">Odobri</string>
<string name="onboarding_heading">Dobro došao, dobro došla!</string>
<string name="pref_onboarding_guide">Vodič za početno pokretanje</string>
<string name="onboarding_guides_new_user">Koristiš %s po prvi puta? Preporučujemo da pogledaš vodič za pokretanje.</string>
<string name="onboarding_guides_returning_user">Već si koristio/la %s?</string>
<string name="onboarding_permission_ignore_battery_opts_description">Izbjegni prekide pri dugotrajnim aktualiziranjima biblioteke, preuzimanjima i obnavljanja sigurnosnih kopija.</string>
<string name="onboarding_permission_ignore_battery_opts">Upotreba baterije u pozadini</string>
<string name="onboarding_description">Hajdemo najprije postaviti neke stvari. Ako želiš možeš ih kasnije promijeniti u postavkama.</string>
<string name="no_location_set">Nije postavljeno mjesto za spremanje podataka</string>
<string name="onboarding_storage_info">Odaberi mapu u koju će %1$s spremati preuzimanja poglavlja, sigurnosne kopije i drugo.
\n
\nPreporučujemo koristiti zasebnu mapu.
\n
\nOdabrana mapa: %2$s</string>
</resources>

View File

@ -419,7 +419,6 @@
<string name="date">Dátum</string>
<string name="clear_history_confirmation">Biztos benne\? Minden előzmény elvész.</string>
<string name="delete_category">Kategória törlése</string>
<string name="action_faq_and_guides">Gyakori kérdések és Útmutatók</string>
<string name="pref_app_language">Alkalmazás nyelve</string>
<string name="unread">Olvasatlan</string>
<string name="set_chapter_settings_as_default">Beállítás alapértelmezettként</string>
@ -435,7 +434,6 @@
<string name="delete_category_confirmation">Törölni akarja a %s kategóriát \?</string>
<string name="internal_error">InternalError: Nézze meg a hibaüzenetet további információért</string>
<string name="network_not_metered">Csak nem-lemért hálózatokon</string>
<string name="pref_library_update_manga_restriction">Frissülő elemek kihagyása</string>
<string name="backup_choice">Mit tartalmazzon a biztonsági mentés\?</string>
<string name="restore_miui_warning">Biztonsági mentés/helyreállítás nem biztos,hogy működik ha a MIUI Optimalizáció ki van kapcsolva.</string>
<string name="restore_in_progress">Helyreállítás folyamatban van</string>
@ -474,7 +472,6 @@
<string name="snack_categories_deleted">Kategóriák törölve</string>
<string name="file_select_cover">Válasszon képet a fedlapnak</string>
<string name="update_check_no_new_updates">Nem található új frissítés</string>
<string name="channel_skipped">Kihagyott</string>
<string name="ext_info_version">Verzió</string>
<string name="ext_info_language">Nyelv</string>
<string name="ext_info_age_rating">Korhatár</string>
@ -512,7 +509,6 @@
<string name="backup_in_progress">Biztonsági mentés már folyamatban van</string>
<string name="not_installed">Nincs telepítve</string>
<string name="download_queue_size_warning">Figyelmeztetés: a tömeges letöltések a források lelassulásához és/vagy a Tachiyomi leállásához vezethetnek. Koppintson további információért.</string>
<string name="notification_update_skipped">%1$d frissítés kihagyva</string>
<string name="learn_more">Koppintson további információért</string>
<string name="notification_cover_update_failed">Nem sikerült a fedlap frissítése</string>
<string name="notification_first_add_to_library">Először adja a mangát a könyvtárhoz</string>
@ -726,7 +722,6 @@
<string name="action_menu_overflow_description">További lehetőségek</string>
<string name="library_sync_complete">Könyvtár szinkronizálása elkészült</string>
<string name="last_auto_backup_info">Utolsó automatikus biztonsági mentés: %s</string>
<string name="manga_modify_calculated_interval_title">Intervallum testreszabása</string>
<string name="selected">Kiválasztott</string>
<string name="no_scanlators_found">Nincs szkennelő-fordító találat</string>
<string name="not_selected">Nem kiválasztott</string>

View File

@ -123,9 +123,9 @@
<string name="disabled">Tidak Aktif</string>
<string name="last_read_chapter">Bab terakhir yang dibaca</string>
<string name="second_to_last">Bab kedua dari terakhir dibaca</string>
<string name="third_to_last">Bab ketiga dari terakhir dibaca</string>
<string name="fourth_to_last">Bab keempat dari terakhir dibaca</string>
<string name="fifth_to_last">Bab kelima dari terakhir dibaca</string>
<string name="third_to_last">Chapter ketiga dari terakhir dibaca</string>
<string name="fourth_to_last">Chapter keempat dari terakhir dibaca</string>
<string name="fifth_to_last">Chapter kelima dari terakhir dibaca</string>
<string name="pref_download_new">Unduh bab baru</string>
<string name="services">Pelacakan</string>
<string name="pref_create_backup">Buat cadangan</string>
@ -140,7 +140,7 @@
<string name="creating_backup">Membuat cadangan</string>
<string name="pref_clear_chapter_cache">Hapus cache bab</string>
<string name="used_cache">Terpakai: %1$s</string>
<string name="cache_deleted">Cache dibersihkan %1$d berkas telah dihapus</string>
<string name="cache_deleted">Cache dibersihkan, %1$d file dihapus</string>
<string name="cache_delete_error">Terjadi kesalahan saat membersihkan</string>
<string name="pref_clear_cookies">Hapus cookies</string>
<string name="cookies_cleared">Cookies dihapus</string>
@ -293,9 +293,9 @@
<string name="action_sort_latest_chapter">Bab terbaru</string>
<string name="action_view_chapters">Lihat bab</string>
<string name="action_cancel_all">Batalkan semua</string>
<string name="theme_light">Mati</string>
<string name="theme_dark">Nyala</string>
<string name="theme_system">Ikuti sistem</string>
<string name="theme_light">Terang</string>
<string name="theme_dark">Gelap</string>
<string name="theme_system">Sistem</string>
<string name="pref_manage_notifications">Kelola notifikasi</string>
<string name="pref_category_security">Keamanan dan privasi</string>
<string name="action_menu">Menu</string>
@ -413,7 +413,7 @@
<string name="ext_nsfw_warning">Sumber dari ekstensi ini mungkin berisi konten DEWASA (18+)</string>
<string name="ext_nsfw_short">18+</string>
<string name="pref_remove_bookmarked_chapters">Hapus bab yang ditandai</string>
<string name="pref_category_delete_chapters">Hapus bab</string>
<string name="pref_category_delete_chapters">Hapus chapter</string>
<string name="parental_controls_info">Hal ini tidak mencegah ekstensi yang tidak resmi atau berpotensi salah ditandai untuk menampilkan konten NSFW (18+) di dalam aplikasi.</string>
<string name="no_chapters_error">Tidak ada bab yang ditemukan</string>
<string name="chapter_settings_updated">Pengaturan bab bawaan diperbarui</string>
@ -458,7 +458,7 @@
<string name="nav_zone_right">Kanan</string>
<string name="nav_zone_left">Kiri</string>
<string name="nav_zone_next">Lanjut</string>
<string name="nav_zone_prev">Sblm</string>
<string name="nav_zone_prev">Sebelum</string>
<string name="automatic_background">Otomatis</string>
<string name="pref_create_folder_per_manga">Menyimpan halaman ke dalam folder terpisah</string>
<string name="pref_create_folder_per_manga_summary">Membuat folder menurut judul entri</string>
@ -509,7 +509,7 @@
<string name="enhanced_tracking_info">Menyediakan fitur yang disempurnakan untuk sumber tertentu. Entri secara otomatis dilacak ketika ditambahkan ke perpustakaan Anda.</string>
<string name="enhanced_services">Pelacak yang ditingkatkan</string>
<string name="pref_dark_theme_pure_black">Mode gelap hitam pekat</string>
<string name="theme_yotsuba">yotsuba</string>
<string name="theme_yotsuba">Yotsuba</string>
<string name="theme_yinyang">Yin dan Yang</string>
<string name="theme_tako">Tako</string>
<string name="theme_strawberrydaiquiri">Stroberi Daiquiri</string>
@ -528,7 +528,7 @@
<string name="getting_started_guide">Panduan awal mulai</string>
<string name="action_track">Lacak</string>
<string name="theme_tealturquoise">Teal &amp; Pirus</string>
<string name="pref_category_appearance">Penampilan</string>
<string name="pref_category_appearance">Tampilan</string>
<string name="confirm_lock_change">Autentikasi untuk mengonfirmasi perubahan</string>
<string name="label_default">Bawaan</string>
<string name="pref_remove_exclude_categories">Kategori yang dikecualikan</string>
@ -558,7 +558,6 @@
<string name="database_clean">Tidak ada yang perlu dibersihkan</string>
<string name="clear_database_source_item_count">%1$d entri non-perpustakaan dalam database</string>
<string name="extension_api_error">Gagal mendapatkan daftar ekstensi</string>
<string name="pref_library_update_manga_restriction">Lewati pembaruan entri</string>
<string name="pref_update_only_completely_read">Dengan bab yang belum dibaca</string>
<string name="privacy_policy">Kebijakan privasi</string>
<string name="publishing_finished">Penerbitan selesai</string>
@ -566,7 +565,6 @@
<string name="on_hiatus">Sedang Hiatus</string>
<string name="library_errors_help">Untuk bantuan cara memperbaiki kesalahan pembaruan pustaka, lihat %1$s</string>
<string name="save_chapter_as_cbz">Simpan sebagai arsip CBZ</string>
<string name="action_faq_and_guides">Tanya Jawab dan Panduan</string>
<string name="action_show_manga">Lihat entri</string>
<string name="action_display_cover_only_grid">Grid sampul saja</string>
<string name="pref_update_only_started">Yang belum dibaca</string>
@ -575,9 +573,7 @@
<string name="skipped_reason_not_started">Dilewati karena tidak ada bab yang dibaca</string>
<string name="pref_landscape_zoom">Secara otomatis melebarkan gambar</string>
<string name="pref_navigate_pan">Geser gambar lebar</string>
<string name="notification_update_skipped">%1$d pembaruan terlewat</string>
<string name="learn_more">Pelajari lebih lanjut</string>
<string name="channel_skipped">Terlewat</string>
<string name="rotation_reverse_portrait">Layar tegak terbalik</string>
<string name="notification_update_error">%1$d pembaruan gagal</string>
<string name="action_move_to_top_all_for_series">Pindahkan seri ke atas</string>
@ -641,10 +637,10 @@
<string name="action_search_hint">Cari…</string>
<string name="pref_browse_summary">Sumber, ekstensi, pencarian global</string>
<string name="pref_reader_summary">Mode membaca, tampilan, navigasi</string>
<string name="pref_tracking_summary">Sinkronisasi kemajuan satu arah, sinkronisasi yang ditingkatkan</string>
<string name="pref_tracking_summary">Sinkronisasi progres searah, sinkronisasi yang ditingkatkan</string>
<string name="crash_screen_description">%s mengalami kesalahan tak terduga. Kami menyarankan Anda membagi log kerusakan di saluran dukungan kami di Discord.</string>
<string name="pref_downloads_summary">Unduh otomatis, unduh di depan</string>
<string name="pref_security_summary">Kunci aplikasi, layar aman</string>
<string name="pref_downloads_summary">Unduh otomatis, unduh terlebih dahulu</string>
<string name="pref_security_summary">Kunci aplikasi, amankan layar</string>
<string name="pref_appearance_summary">Tema, format tanggal &amp; waktu</string>
<string name="pref_library_summary">Kategori, pembaruan global, geser chapter</string>
<string name="pref_backup_summary">Pencadangan manual &amp; otomatis, ruang penyimpanan</string>
@ -695,7 +691,7 @@
<string name="confirm_add_duplicate_manga">Kamu memiliki entri di pustaka dengan nama yang sama.
\n
\nApakah kamu ingin melanjutkan\?</string>
<string name="track_error">%1$seror%2$s</string>
<string name="track_error">%1$s eror: %2$s</string>
<string name="information_required_plain">*dibutuhkan</string>
<string name="pref_hide_in_library_items">Semua catatan tersembunyi sudah ada di perpustakaan</string>
<string name="action_copy_to_clipboard">Salin ke papan klip</string>
@ -714,7 +710,6 @@
<string name="action_filter_interval_custom">Interval pengambilan disesuaikan</string>
<string name="action_filter_interval_long">Ambil bulanan (28 hari)</string>
<string name="action_filter_interval_late">Cek 10+ terlambat</string>
<string name="manga_modify_calculated_interval_title">Sesuaikan Interval</string>
<string name="skipped_reason_not_in_release_period">Dilewati karena tidak ada rilis yang diharapkan hari ini</string>
<string name="action_filter_interval_dropped">Berkurang\? Akhir 20+ dan 2 bulan</string>
<string name="action_filter_interval_passed">Melewati periode pemeriksaan</string>
@ -768,4 +763,27 @@
<string name="action_bar_up_description">Navigasi ke atas</string>
<string name="pref_storage_location">Lokasi penyimpanan</string>
<string name="pref_storage_location_info">Digunakan untuk pencadangan otomatis, pengunduhan bab, dan sumber lokal.</string>
<string name="onboarding_storage_action_select">Pilih direktori</string>
<string name="pref_onboarding_guide">Panduan awal</string>
<string name="onboarding_guides_new_user">Baru mengenal %s? Kami menyarankan Anda membaca panduan awal.</string>
<string name="onboarding_action_finish">Selesai</string>
<string name="onboarding_storage_selection_required">Harap pilih direktori terlebih dahulu</string>
<string name="onboarding_permission_notifications">Izin notifikasi</string>
<string name="onboarding_permission_install_apps">Izin pasang aplikasi</string>
<string name="onboarding_heading">Selamat Datang!</string>
<string name="onboarding_guides_returning_user">Sudah pernah menggunakan %s sebelumnya?</string>
<string name="onboarding_action_skip">Lewati</string>
<string name="onboarding_permission_ignore_battery_opts_description">Hindari gangguan pada pembaruan pustaka, pengunduhan, dan pemulihan cadangan yang berlangsung lama.</string>
<string name="onboarding_action_next">Selanjutnya</string>
<string name="onboarding_permission_ignore_battery_opts">Penggunaan baterai di latar belakang</string>
<string name="onboarding_permission_install_apps_description">Untuk memasang ekstensi.</string>
<string name="onboarding_description">Mari kita atur beberapa hal terlebih dahulu. Anda juga selalu dapat mengubahnya di menu pengaturan nanti.</string>
<string name="no_location_set">Lokasi penyimpanan belum ditentukan</string>
<string name="onboarding_permission_notifications_description">Dapatkan notifikasi untuk pembaruan pustaka dan sebagainya.</string>
<string name="onboarding_permission_action_grant">Izinkan</string>
<string name="onboarding_storage_info">Pilih direktori untuk menyimpan konten dari %1$s, termasuk unduhan chapter, data backup, dan lainnya.
\n
\nSebaiknya menggunakan direktori terpisah.
\n
\nDirektori yang dipilih: %2$s</string>
</resources>

View File

@ -114,7 +114,7 @@
<string name="services">Servizi di tracking</string>
<string name="pref_clear_chapter_cache">Cancella cache capitoli</string>
<string name="used_cache">Usati: %1$s</string>
<string name="cache_deleted">Cache cancellata. %1$d file sono stati cancellati</string>
<string name="cache_deleted">Cache cancellat, %1$d file cancellati</string>
<string name="cache_delete_error">Errore durante la cancellazione</string>
<string name="pref_clear_cookies">Cancella cookie</string>
<string name="cookies_cleared">Cookie cancellati</string>
@ -301,9 +301,9 @@
<string name="action_sort_latest_chapter">Ultimo capitolo</string>
<string name="action_view_chapters">Visualizza capitoli</string>
<string name="action_cancel_all">Annulla tutto</string>
<string name="theme_light">Disattivo</string>
<string name="theme_dark">Attivo</string>
<string name="theme_system">Usa il tema di sistema</string>
<string name="theme_light">Chiaro</string>
<string name="theme_dark">Scuro</string>
<string name="theme_system">Sistema</string>
<string name="pref_manage_notifications">Gestisci le notifiche</string>
<string name="pref_category_security">Sicurezza e privacy</string>
<string name="lock_with_biometrics">Richiedi sblocco</string>
@ -560,13 +560,11 @@
<string name="extension_api_error">Impossibile ottenere l\'elenco estensioni</string>
<string name="privacy_policy">Politica sulla privacy</string>
<string name="pref_update_only_completely_read">Con capitoli non letti</string>
<string name="pref_library_update_manga_restriction">Salta l\'aggiornamento delle voci</string>
<string name="library_errors_help">Per un aiuto su come risolvere gli errori di aggiornamento della libreria vedi %1$s</string>
<string name="save_chapter_as_cbz">Salva come archivio CBZ</string>
<string name="cancelled">Cancellata</string>
<string name="on_hiatus">In pausa</string>
<string name="publishing_finished">Pubblicazione terminata</string>
<string name="action_faq_and_guides">Domande frequenti e guide</string>
<string name="action_show_manga">Mostra voce</string>
<string name="pref_navigate_pan">Scorri le immagini larghe</string>
<string name="pref_landscape_zoom">Ingrandisci automaticamente le immagini larghe</string>
@ -575,9 +573,7 @@
<string name="skipped_reason_completed">Saltato perché la serie è completa</string>
<string name="skipped_reason_not_caught_up">Saltato perché ci sono capitoli non letti</string>
<string name="skipped_reason_not_started">Saltato perché non ci sono capitoli letti</string>
<string name="channel_skipped">Saltato</string>
<string name="notification_update_error">%1$d aggiornamento(i) fallito(i)</string>
<string name="notification_update_skipped">%1$d aggiornamento(i) saltato(i)</string>
<string name="learn_more">Tocca per approfondire</string>
<string name="rotation_reverse_portrait">Verticale inverso</string>
<string name="action_move_to_top_all_for_series">Sposta la serie in cima</string>
@ -714,7 +710,6 @@
<string name="action_filter_interval_long">Recupera mensilmente (28 giorni)</string>
<string name="action_filter_interval_dropped">Abbandonato\? In ritardo tra 20 giorni e 2 mesi</string>
<string name="pref_update_only_in_release_period">Fuori dal periodo di rilascio previsto</string>
<string name="manga_modify_calculated_interval_title">Personalizza intervallo</string>
<string name="intervals_header">Intervalli</string>
<string name="manga_display_interval_title">Stima ogni</string>
<string name="action_filter_interval_custom">Intervallo di recupero personalizzato</string>
@ -750,4 +745,48 @@
<string name="sort_category_confirmation">Vuoi ordinare le categorie alfabeticamente\?</string>
<string name="file_null_uri_error">Il selettore di file ha restituito file all\'app</string>
<string name="label_data_storage">Dati e archiviazione</string>
<string name="onboarding_storage_action_select">Scegli una cartella</string>
<string name="onboarding_action_finish">Iniziamo</string>
<string name="action_apply">Applica</string>
<string name="onboarding_storage_selection_required">Devi selezionare una cartella</string>
<string name="onboarding_permission_notifications">Permessi di notifica</string>
<string name="action_revert_to_default">Reimposta predefiniti</string>
<string name="onboarding_permission_install_apps">Permessi per installare app</string>
<string name="onboarding_heading">Ti diamo il benvenuto!</string>
<string name="action_menu_overflow_description">Altre opzioni</string>
<string name="selected">Selezionato</string>
<string name="not_selected">Non selezionato</string>
<string name="scanlator">Scanlator</string>
<string name="onboarding_action_skip">Salta</string>
<string name="onboarding_action_next">Prossimo</string>
<string name="onboarding_permission_ignore_battery_opts">Uso della batteria in secondo piano</string>
<string name="onboarding_permission_install_apps_description">Per installare le estensioni delle fonti.</string>
<string name="action_bar_up_description">Navigare su</string>
<string name="onboarding_description">Per prima cosa impostiamo alcune cose. Puoi sempre cambiarle successivamente nelle impostazioni.</string>
<string name="onboarding_permission_notifications_description">Ricevi notifiche per aggiornamenti della libreria e altro.</string>
<string name="ext_permission_install_apps_warning">È necessaria l\'autorizzazione per installare le estensioni. Tocca qui per concederla.</string>
<string name="onboarding_storage_info">Selezione una cartella dove %1$s salverà i capitoli scaricati, i backup e altro.
\n
\nÈ raccomandato usare una cartella dedicata.
\n
\nCartella selezionata: %2$s</string>
<string name="pref_storage_location">Posizione di archiviazione</string>
<string name="action_create">Crea</string>
<string name="relative_time_span_never">Mai</string>
<string name="pref_flash_page_summ">Riduce il ghosting sugli schermi e-ink</string>
<string name="pref_onboarding_guide">Guida di benvenuto</string>
<string name="pref_storage_location_info">Usato per i backup automatici, download capitoli e fonti locali.</string>
<string name="onboarding_guides_new_user">Nuovo su %s? Ti consigliamo di dare un\'occhiata alla guida introduttiva.</string>
<string name="available_disk_space_info">Disponibile: %1$s / Totale: %2$s</string>
<string name="last_auto_backup_info">Ultimo backup automatico: %s</string>
<string name="onboarding_guides_returning_user">Hai già usato %s in precedenza?</string>
<string name="no_scanlators_found">Nessuno scanlator trovato</string>
<string name="pref_flash_page">Lampeggia al cambio pagina</string>
<string name="onboarding_permission_ignore_battery_opts_description">Evita interruzioni a lunghi aggiornamenti libreria, download e ripristino di backup.</string>
<string name="pref_storage_usage">Archiviazione utilizzata</string>
<string name="action_sort_tracker_score">Punteggio del tracker</string>
<string name="no_location_set">Cartella di archiviazione non impostata</string>
<string name="private_settings">Includi impostazioni sensibili (es. token di login dei tracker)</string>
<string name="onboarding_permission_action_grant">Consenti</string>
<string name="exclude_scanlators">Escludi scanlator</string>
</resources>

View File

@ -158,11 +158,11 @@
<string name="action_open_in_web_view">WebView で開く</string>
<string name="pref_category_reader">ビューア</string>
<string name="pref_category_tracking">同期</string>
<string name="untrusted_extension_message">この拡張機能は信頼できない証明書でサインされているため、有効にされていません。
<string name="untrusted_extension_message">この拡張機能は不明の作成者によりサインされているため、ロードされていません。
\n
\n悪意のある拡張機能は保存されているすべてのログイン情報を読み取ることや、任意コード実行をすることができます。
\n
\nこれらのリスクを受け入れ、この証明書を信頼しますか</string>
\nこれらのリスクを受け入れ、この拡張機能の証明書を信頼しますか?</string>
<string name="pref_show_page_number">ページ数を表示</string>
<string name="pref_keep_screen_on">画面を常にON</string>
<string name="scale_type_fit_height">高さに合わせる</string>
@ -178,7 +178,7 @@
<string name="restoring_backup">バックアップを復元中</string>
<string name="creating_backup">バックアップを作成中</string>
<string name="used_cache">サイズ: %1$s</string>
<string name="cache_deleted">キャッシュを削除しました。%1$d件のファイルは削除されました</string>
<string name="cache_deleted">キャッシュがクリアされ、%1$d 個のファイルが削除されました</string>
<string name="pref_clear_cookies">Cookiesを削除</string>
<string name="cookies_cleared">Cookiesを削除しました</string>
<string name="clear_database_confirmation">データベースをクリアしてもよろしいですか?ライブラリにない項目の読んだ章と読書進捗はすべて失われます</string>
@ -331,7 +331,7 @@
<string name="restore_duration">%02d分%02d秒</string>
<string name="backup_restore_missing_sources">ソースがありません:</string>
<string name="invalid_backup_file_missing_manga">バックアップにはライブラリの項目が含まれません。</string>
<string name="invalid_backup_file">バックアップファイルは無効です</string>
<string name="invalid_backup_file">無効なバックアップファイル:</string>
<string name="tracking_info">一方同期の外部追跡サービスにある章の読書進捗を更新します。個別の項目の「同期」ボタンで追跡サービスを設定してください。</string>
<string name="pref_webtoon_side_padding">余白</string>
<string name="pref_category_reading">読書中</string>
@ -457,9 +457,7 @@
<string name="pref_dual_page_invert_summary">分割された幅広いページの配置が読む方向と一致しない場合は有効にしてください</string>
<string name="pref_dual_page_invert">分割ページの配置を反転</string>
<string name="pref_dual_page_split">幅広いページの分割</string>
<string name="backup_restore_content_full">バックアップファイルからデータを復旧します。
\n
\n後で手動で必要な拡張機能をインストールし、使用したい追跡サービスにログインしてください。</string>
<string name="backup_restore_content_full">後で手動で必要な拡張機能をインストールし、使用したい追跡サービスにログインしてください。</string>
<string name="nav_zone_right"></string>
<string name="nav_zone_left"></string>
<string name="nav_zone_next">次へ</string>
@ -559,14 +557,12 @@
<string name="database_clean">消去できるものはありませんでした</string>
<string name="extension_api_error">拡張機能リストを取得できませんでした</string>
<string name="privacy_policy">プライバシーポリシー</string>
<string name="pref_library_update_manga_restriction">項目更新のスキップ</string>
<string name="pref_update_only_completely_read">未読の章あり</string>
<string name="library_errors_help">ライブラリ更新エラーの修正については、%1$sをご覧ください</string>
<string name="save_chapter_as_cbz">CBZアーカイブとして保存</string>
<string name="publishing_finished">完結済み</string>
<string name="cancelled">キャンセル済み</string>
<string name="on_hiatus">休載中</string>
<string name="action_faq_and_guides">よくあるご質問とガイド</string>
<string name="action_show_manga">項目を表示</string>
<string name="action_display_cover_only_grid">表紙グリッド</string>
<string name="pref_update_only_started">読み始めていない</string>
@ -575,9 +571,7 @@
<string name="skipped_reason_completed">完結済みの為スキップしました</string>
<string name="skipped_reason_not_started">読了した章がない為スキップしました</string>
<string name="pref_navigate_pan">幅広い画像を左右に移動</string>
<string name="notification_update_skipped">%1$d件の更新がスキップされました</string>
<string name="learn_more">タップでもっと詳しく</string>
<string name="channel_skipped">スキップ済み</string>
<string name="notification_update_error">%1$d件の更新に失敗しました</string>
<string name="rotation_reverse_portrait">縦向き (反転)</string>
<string name="action_move_to_top_all_for_series">シリーズをトップに移動</string>
@ -717,7 +711,6 @@
<string name="pref_update_only_in_release_period">更新予定時間外</string>
<string name="intervals_header">間隔</string>
<string name="skipped_reason_not_in_release_period">今日、連載更新が予想されていないためスキップしました</string>
<string name="manga_modify_calculated_interval_title">間隔を設定</string>
<string name="has_results">結果あり</string>
<string name="track_delete_title">%s の追跡を削除しますか\?</string>
<string name="manga_display_interval_title">毎に評価</string>
@ -787,10 +780,12 @@
<string name="onboarding_permission_notifications">通知の許可</string>
<string name="onboarding_permission_install_apps">アプリのインストールの許可</string>
<string name="onboarding_permission_ignore_battery_opts_description">時間のかかるライブラリ更新、ダウンロードやバックアップの復元などへの中断を防ぎます。</string>
<string name="onboarding_permission_type_optional">任意</string>
<string name="onboarding_permission_ignore_battery_opts">バックグラウンドでのバッテリー使用量</string>
<string name="onboarding_permission_install_apps_description">ソース拡張機能をインストールするために必要です。</string>
<string name="onboarding_permission_notifications_description">ライブラリ更新などの通知を送信します。</string>
<string name="onboarding_permission_type_required">必須</string>
<string name="onboarding_permission_action_grant">許可</string>
<string name="available_disk_space_info">空き領域:%1$s/総計:%2$s</string>
<string name="manga_interval_expected_update">次のアップデートは%sに実行する予定です</string>
<string name="invalid_backup_file_error">詳細なエラー情報:</string>
<string name="ext_permission_install_apps_warning">拡張機能をインストールするためには権限が必要です。タップで許可してください。</string>
</resources>

View File

@ -175,7 +175,6 @@
<string name="pref_app_theme">Tema aplikasi</string>
<string name="theme_monet">Dinamis</string>
<string name="theme_greenapple">Ijo apel</string>
<string name="action_faq_and_guides">Takon jawab lan panduan</string>
<string name="parental_controls_info">Iki ora nyegah ekstensi kang ora resmi utawa salah dilabeli kanggo nampilake konten NSFW (18+) ing aplikasi.</string>
<string name="relative_time_today">Dinten puniki</string>
<string name="update_never">Mati</string>
@ -246,7 +245,6 @@
<string name="lock_with_biometrics">Mbutuhake mbukak kunci</string>
<string name="pref_library_update_refresh_metadata">Nganyari metadata otomatis</string>
<string name="network_not_metered">Jaringan gak kebates tok</string>
<string name="pref_library_update_manga_restriction">Lewati updatean judul</string>
<string name="pref_app_language">Bohoso aplikasi</string>
<string name="untrusted_extension_message">Ekstensi iki ditandatangani nganggo sertifikat sing ora dipercaya lan ora diaktifake.
\n

View File

@ -422,7 +422,6 @@
<string name="pref_app_theme">აპლიკაციის თემა</string>
<string name="label_warning">გაფრთხილება</string>
<string name="confirm_lock_change">აუთენტიკაცია ცვლილების დასადასტურებლად</string>
<string name="action_faq_and_guides">ხდკ და სახელმძღვანელოები</string>
<string name="pref_category_appearance">გარეგნობა</string>
<string name="label_default">ნაგულისხმევი</string>
<string name="action_show_errors">დააჭირე დეტალების სანახავად</string>

View File

@ -159,7 +159,6 @@
<string name="action_webview_forward">Алға</string>
<string name="action_webview_refresh">Жаңарту</string>
<string name="action_start_downloading_now">Жүктеуді қазір бастау</string>
<string name="action_faq_and_guides">ЖҚС пен Нұсқаулықтар</string>
<string name="loading">Жүктелуде…</string>
<string name="app_not_available">Қолданба қолжетімсіз</string>
<string name="pref_category_general">Жалпы</string>
@ -231,7 +230,6 @@
<string name="pref_crop_borders">Кесу жиектері</string>
<string name="extension_api_error">Кеңейтулер тізімін алу сәтсіз өтті</string>
<string name="pref_cutout_short">Мазмұнды кесу аймағында көрсету</string>
<string name="pref_library_update_manga_restriction">Жазбаларды жаңартуды өткізіп жіберу</string>
<string name="pref_update_only_started">Бұл басталған жоқ</string>
<string name="pref_library_update_refresh_metadata_summary">Кітапхананы жаңарту кезінде жаңа мұқаба мен мәліметтерді тексеру</string>
<string name="ext_updates_pending">Жаңартулар күтілуде</string>
@ -518,7 +516,6 @@
<string name="clear_history_confirmation">Сенімдісіз бе\? Бар тарих жоғалады.</string>
<string name="source_empty_screen">Дереккөз табылмады</string>
<string name="notification_update_error">%1$d жаңарту сәтсіз өтті</string>
<string name="notification_update_skipped">%1$d жаңарту өткізіп жіберілді</string>
<string name="notification_cover_update_failed">Мұқаба жаңартылмады</string>
<string name="update_check_notification_update_available">Жаңа нұсқа қолжетімді!</string>
<string name="download_notifier_split_page_not_found">%d бет табылмады, бөлінгенде</string>
@ -589,7 +586,6 @@
<string name="update_check_eol">Бұл Android нұсқасы қолжетімсіз</string>
<string name="download_notifier_unknown_error">Белгісіз қателіктің кесірінен тарау жүктеп алынбады</string>
<string name="download_notifier_split_page_path_not_found">Бет файлының жолы %d табылмады</string>
<string name="channel_skipped">Өткізуп жіберілді</string>
<string name="channel_new_chapters">Тарау жаңартулары</string>
<string name="channel_app_updates">Қолданба жаңартулары</string>
<string name="channel_ext_updates">Кеңейту жаңартулары</string>

View File

@ -5,7 +5,7 @@
<string name="chapters">ភាគ</string>
<string name="label_warning">បំរាម</string>
<string name="action_filter_unread">មិនទាន់អាន</string>
<string name="action_sort_last_read">ម៊េងហ្គាដែលបានអានចុងក្រោយបង្អស់</string>
<string name="action_sort_last_read">អានចុងក្រោយគេ</string>
<string name="action_previous_chapter">ភាគមុន</string>
<string name="track">ការតាមដានការអាន</string>
<string name="label_sources">ប្រភព</string>
@ -22,28 +22,28 @@
<string name="label_extensions">ប្រភពម៊េងហ្គា</string>
<string name="label_help">ជំនួយ</string>
<string name="label_default">លំនាំដើម</string>
<string name="action_filter">ហ្វីលធឺ</string>
<string name="action_filter_bookmarked">បានបញ្ចូលទៅក្នុងបណ្ណាល័យ</string>
<string name="action_filter_tracked">បានដាក់ការតាមដានការអាន</string>
<string name="action_filter_empty">ដកហ្វីលធឺចេញ</string>
<string name="action_sort_count">ចំនួនម៊េងហ្គាទាំងអស់</string>
<string name="action_sort_total">ចំនួនភាគទាំងអស់</string>
<string name="action_sort_chapter_fetch_date">ថ្ងៃខែដែលបានកែប្រែ</string>
<string name="action_sort_date_added">បានបញ្ចូលតាមកាលបរិច្ឆេទ</string>
<string name="action_filter">តម្រង</string>
<string name="action_filter_bookmarked">បានចំណាំ</string>
<string name="action_filter_tracked">បានតាមដានការអាន</string>
<string name="action_filter_empty">ជម្រុះតម្រងចេញ</string>
<string name="action_sort_count">ចំនួនត្លុកសរុប</string>
<string name="action_sort_total">ចំនួនភាគសរុប</string>
<string name="action_sort_chapter_fetch_date">ថ្ងៃខែដែលបានឆែកមើល</string>
<string name="action_sort_date_added">តាមកាលបរិច្ឆេទដែលបានបញ្ចូល</string>
<string name="action_global_search">ស្វែងរកជាសកល</string>
<string name="action_select_inverse">ជ្រើសរើសផ្ទុយ</string>
<string name="action_mark_as_read">បញ្ជាក់ថាបានអាន</string>
<string name="action_download">ទាញយក</string>
<string name="action_bookmark">ចំណាំភាគនេះទុក</string>
<string name="action_rename_category">ប្ដូរឈ្មោះថ្នាក់</string>
<string name="action_move_category">ទទួលយកថ្នាក</string>
<string name="action_edit_cover">ផ្លាស់ប្ដូរក្របម៊េងហ្គា</string>
<string name="action_rename_category">ប្ដូរឈ្មោះថ្នាក់ក្រម</string>
<string name="action_move_category">កំណត</string>
<string name="action_edit_cover">ផ្លាស់ប្ដូរកម្របត្លុក</string>
<string name="action_view_chapters">មើលភាគ</string>
<string name="action_pause">ឈប់បណ្ណោះអាសន្ន</string>
<string name="action_retry">ម្ដងទៀត</string>
<string name="action_remove">លុប</string>
<string name="action_pause">ផ្អាក</string>
<string name="action_retry">ព្យាយាមម្ដងទៀត</string>
<string name="action_remove">ដកចេញ</string>
<string name="action_start">ចាប់ផ្ដើម</string>
<string name="action_resume">បន្ត</string>
<string name="action_resume">បន្តវិញ</string>
<string name="action_display_mode">ម៊ូដនៃការបង្ហាញ</string>
<string name="action_display">ការបង្ហាញ</string>
<string name="action_display_grid">ជ្រុងCompact</string>
@ -73,10 +73,10 @@
<string name="label_extension_info">អំពីប្រភពម៊េងហ្គា</string>
<string name="information_empty_library">បណ្ណាល័យរបស់អ្នកទទេស្អាត</string>
<string name="information_empty_category">អ្នកពុំមានការទម្រៀបម៊េងហ្គាឡើយ។ ចុចប៊ូតុងសញ្ញាបូកដើម្បីទម្រៀបម៊េងហ្គាសម្រាប់បណ្ណាល័យរបស់អ្នក។</string>
<string name="manga">ម៊េងហ្គា</string>
<string name="confirm_lock_change">Authenticateដើម្បីបញ្ចាក់ពីការកែប្រែ</string>
<string name="manga">បណ្តុំក្នុងបណ្ណាល័យ</string>
<string name="confirm_lock_change">ផ្ទៀងផ្ទាត់ភាពត្រឹមត្រូវដើម្បីបញ្ជាក់ការផ្លាស់ប្តូរ</string>
<string name="action_settings">ការកំណត់</string>
<string name="action_menu">កន្លែងកំណត់</string>
<string name="action_menu">មនុយ</string>
<string name="action_mark_previous_as_read">បញ្ចាក់ភាគមុនថាបានអាន</string>
<string name="action_delete">លុប</string>
<string name="action_sort_latest_chapter">ភាគថ្មីបំផុត</string>
@ -86,14 +86,14 @@
<string name="action_mark_as_unread">បញ្ជាក់ថាមិនទាន់បានអាន</string>
<string name="action_update_library">ធ្វើបច្ចុប្បន្នភាពបណ្ណាល័យ</string>
<string name="action_enable_all">បើកទាំងអស់</string>
<string name="action_add_category">ញ្ចូលថ្នាក់</string>
<string name="action_add_category">ន្ថែមថ្នាក់ក្រម</string>
<string name="action_disable_all">បិទទាំងអស់</string>
<string name="action_edit">ផ្លាស់ប្ដូរ</string>
<string name="action_add">បញ្ចូល</string>
<string name="action_edit_categories">កែប្រែថ្នាក់</string>
<string name="action_edit_categories">កែប្រែ</string>
<string name="action_next_chapter">ភាគបន្ទាប់</string>
<string name="action_open_in_browser">បើកនៅក្នុងកម្មវិធីរុករក</string>
<string name="action_show_manga">បង្ហាញម៊េងហ្គា</string>
<string name="action_show_manga">បង្ហាញត្លុក</string>
<string name="action_open_in_web_view">បើកនៅក្នុងWebView</string>
<string name="action_migrate">ប្ដូរ</string>
<string name="action_display_show_number_of_items">បង្ហាញចំនួនលេខនៃធាតុ</string>
@ -109,7 +109,6 @@
<string name="action_restore">restore</string>
<string name="action_webview_back">ត្រឡប់</string>
<string name="action_start_downloading_now">ចាប់ផ្ដើមទាញយកឥឡូវនេះ</string>
<string name="action_faq_and_guides">FAQនិងមគ្គុទ្ទេសក៍</string>
<string name="loading">កំពុងដំណើរការ…</string>
<string name="app_not_available">កម្មវិធីមិនដំណើរការ</string>
<string name="pref_category_appearance">រូបរាង</string>
@ -188,4 +187,29 @@
<string name="information_cloudflare_bypass_failure">បរាជ័យក្នុងការឆ្លងកាត់Cloudflare</string>
<string name="action_search_hint">ស្វែងរក…</string>
<string name="notification_new_chapters">មានភាគថ្មី</string>
<string name="action_copy_to_clipboard">ចម្លងទៅក្ដារតម្បៀតខ្ទាស់</string>
<string name="label_backup">បម្រុងទុក និងស្ដារឡើងវិញ</string>
<string name="delete_downloaded">លុបអ្វីដែលបានទាញហើយ</string>
<string name="label_local">ក្នុងទូរស័ព្ទ</string>
<string name="action_filter_interval_passed">សូមឆែកមើលកំឡុងពេល</string>
<string name="on">បើក</string>
<string name="action_sort_category">តម្រៀបតាមថ្នាក់ក្រម</string>
<string name="action_update_category">ធ្វើបច្ចុប្បន្នភាពថ្នាក់ក្រម</string>
<string name="action_menu_overflow_description">ជម្រើសបន្ថែមទៀត</string>
<string name="off">បិទ</string>
<string name="delete_category_confirmation">តើអ្នកចង់លុបថ្នាក់ក្រម %s ឬ?</string>
<string name="selected">បានជ្រើសយក</string>
<string name="not_selected">មិនបានជ្រើសយក</string>
<string name="scanlator">អ្នកបកប្រែ</string>
<string name="action_bar_up_description">រុករកទៅលើ</string>
<string name="label_data_storage">ទិន្នន័យ និងអង្គផ្ទុក</string>
<string name="action_remove_everything">ដកអ្វីៗទាំងអស់ចេញ</string>
<string name="sort_category_confirmation">តើអ្នកចង់តម្រៀប តាមអក្ខរក្រមទេ?</string>
<string name="action_open_random_manga">បើកត្លុកដោយចៃដន្យ</string>
<string name="label_started">បានចាប់ផ្តើមហើយ</string>
<string name="action_sort_unread_count">ចំនួនមិនទាន់អាន</string>
<string name="action_sort_last_manga_update">ពិនិត្យបច្ចុប្បន្នភាពចុងក្រោយ</string>
<string name="delete_category">លុបថ្នាក់ក្រម</string>
<string name="action_sort_next_updated">ភាគថ្មីរំពឹងទុកនៅ</string>
<string name="action_filter_interval_long">ទាញចូលប្រចាំខែ (28 ថ្ងៃ)</string>
</resources>

View File

@ -385,7 +385,6 @@
<string name="pref_category_library">서재</string>
<string name="add_to_library">서재에 추가</string>
<string name="save_chapter_as_cbz">CBZ 파일로 저장</string>
<string name="pref_library_update_manga_restriction">항목 업데이트 건너뛰기</string>
<string name="secure_screen_summary">화면 보안을 켜면 앱을 전환할 때 내용이 숨겨지며 스크린샷을 찍을 수 없습니다</string>
<string name="in_library">서재에 추가됨</string>
<string name="action_migrate">데이터 이전</string>
@ -431,7 +430,6 @@
<string name="database_clean">지울 것이 없습니다</string>
<string name="learn_more">터치하여 자세히 알아보기</string>
<string name="chapter_settings_updated">기본 회차 설정을 업데이트 했습니다</string>
<string name="channel_skipped">건너뜀</string>
<string name="channel_new_chapters">회차 업데이트</string>
<string name="include">포함: %s</string>
<string name="label_background_activity">백그라운드 활동</string>
@ -474,7 +472,6 @@
<string name="notification_size_warning">대규모 업데이트는 소스에 피해를 입히고 업데이트가 느려지고 배터리 사용량이 증가할 수 있습니다. 탭하여 자세히 알아보기.</string>
<string name="notification_chapters_single">%1$s화</string>
<string name="notification_update_error">%1$d개의 업데이트가 실패했습니다</string>
<string name="notification_update_skipped">%1$d개의 업데이트를 건너 뛰었습니다</string>
<string name="library_errors_help">서재 업데이트 오류를 해결하려면 %1$s를 참조하세요</string>
<string name="skipped_reason_completed">완결된 만화를 건너 뛰었습니다</string>
<string name="skipped_reason_not_started">읽지 않은 만화를 건너 뛰었습니다</string>
@ -562,7 +559,6 @@
<string name="cancel_all_for_series">이 만화의 항목을 모두 취소</string>
<string name="action_webview_forward">앞으로</string>
<string name="pref_create_folder_per_manga">각각의 폴더에 페이지 저장</string>
<string name="action_faq_and_guides">FAQ 및 설명서</string>
<string name="pref_update_only_started">읽지 않음</string>
<string name="action_move_to_top_all_for_series">이 시리즈를 맨 위로 이동</string>
<string name="action_webview_refresh">새로고침</string>
@ -714,7 +710,6 @@
<string name="action_filter_interval_custom">사용자 지정 가져오기 간격</string>
<string name="action_filter_interval_long">매월 가져오기 (28일)</string>
<string name="intervals_header">간격</string>
<string name="manga_modify_calculated_interval_title">간격 설정</string>
<string name="delete_downloaded">다운로드 삭제</string>
<string name="has_results">결과가 있는 것만 보기</string>
<string name="track_delete_text">이렇게 하면 로컬에서 동기화가 제거됩니다.</string>

View File

@ -295,8 +295,6 @@
<string name="ext_installer_shizuku_unavailable_dialog">Instaliuoti ir paleisti Shizuku, kad galėtumėte naudoti Shizuku kaip papildinių instaliuotoją.</string>
<string name="pref_show_navigation_mode">Rodyti lietimui jautrias zonas</string>
<string name="label_warning">Įspėjimas</string>
<string name="action_faq_and_guides">DUK ir vadovai</string>
<string name="pref_library_update_manga_restriction">Praleisti įrašų atnaujinimą</string>
<string name="update_72hour">Kas 3 dienas</string>
<string name="connected_to_wifi">Kai naudojamas Wi-Fi</string>
<string name="ext_updates_pending">Yra naujinių</string>
@ -372,7 +370,6 @@
<string name="migration_dialog_what_to_include">Pasirinkite duomenis, kuriuos norite įtraukti</string>
<string name="copy">Kopijuoti</string>
<string name="notification_new_chapters">Atrasti nauji skyriai</string>
<string name="notification_update_skipped">Praleistas %1$d naujinys (-iai)</string>
<string name="skipped_reason_not_caught_up">Praleista, nes yra neperskaitytų skyrių</string>
<string name="update_check_notification_download_in_progress">Atsiunčiama…</string>
<string name="file_select_cover">Pasirinkti viršelio vaizdą</string>
@ -497,7 +494,6 @@
<string name="download_notifier_split_page_path_not_found">Nepavyko rasti puslapio failo kelio %d</string>
<string name="channel_common">Bendras</string>
<string name="channel_complete">Baigtas</string>
<string name="channel_skipped">Praleistas</string>
<string name="download_notifier_download_paused">Atsisiuntimas pristabdytas</string>
<string name="channel_new_chapters">Skyriaus atnaujinimai</string>
<string name="spen_next_page">Kitas puslapis</string>

View File

@ -204,8 +204,6 @@
<string name="update_never">Izslēgts</string>
<string name="action_display_language_badge">Valoda</string>
<string name="action_sort_chapter_fetch_date">Nodaļas ieneses datums</string>
<string name="pref_library_update_manga_restriction">Izlaist ierakstu atjaunināšanu</string>
<string name="action_faq_and_guides">Biežāk uzdotie jautājumi un rokasgrāmatas</string>
<string name="action_close">Aizvērt</string>
<string name="pref_category_library_update">Globāls atjauninājums</string>
<string name="parental_controls_info">Šis neliedz neoficiāliem vai, iespējams, nepareizi atzīmētiem paplašinājumiem parādīties NSFW (18+) saturu lietotnē.</string>
@ -508,12 +506,10 @@
<string name="viewer">Lasīšanas režīms</string>
<string name="no_next_chapter">Nākošā nodaļa nav atrasta</string>
<string name="empty_screen">Nu, šis ir neveikli</string>
<string name="notification_update_skipped">%1$d atjauninājums(-i) izlaists(-i)</string>
<string name="update_check_open">Atvērt vietnē GitHub</string>
<string name="update_check_eol">Šī Android versija vairs netiek atbalstīta</string>
<string name="update_check_notification_download_in_progress">Lejupielādē…</string>
<string name="channel_progress">Progress</string>
<string name="channel_skipped">Izlaists</string>
<string name="decode_image_error">Attēlu nevarēja ielādēt</string>
<string name="confirm_set_image_as_cover">Vai izmantot šo attēlu kā vāku\?</string>
<string name="pref_category_for_this_series">Priekš šīs sērijas</string>
@ -736,7 +732,6 @@
<string name="action_filter_interval_dropped">Pametāt\? Par vēlu +20 un 2 mēnešus</string>
<string name="pref_chapter_swipe">Vilkt nodaļas</string>
<string name="manga_display_modified_interval_title">Atjaunināt katru</string>
<string name="manga_modify_calculated_interval_title">Pielāgot intervālu</string>
<string name="exception_http">HTTP %d, pārbaudiet vietni iekš WebView</string>
<string name="exception_offline">Nav interneta savienojuma</string>
<string name="exception_unknown_host">Nevarēja sasniegt %s</string>

Some files were not shown because too many files have changed in this diff Show More