diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt index de92127fb..9a027977a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt @@ -286,7 +286,7 @@ class BackupRestoreService : Service() { backupManager.restoreSavedSearches(savedSearchesJson) restoreProgress += 1 - showRestoreProgress(restoreProgress, restoreAmount, getString(R.string.eh_saved_searches)) + showRestoreProgress(restoreProgress, restoreAmount, getString(R.string.saved_searches)) } // SY <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt index 3e46605f2..b0fb30d37 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt @@ -98,7 +98,7 @@ open class SourceManager(private val context: Context) { XLog.d("[EXH] Delegating source: %s -> %s!", sourceQName, delegate.newSourceClass.qualifiedName) val enhancedSource = EnhancedHttpSource( source, - delegate.newSourceClass.constructors.find { it.parameters.size == 1 }!!.call(source) + delegate.newSourceClass.constructors.find { it.parameters.size == 2 }!!.call(source, context) ) val map = listOf(DelegatedSource(enhancedSource.originalSource.name, enhancedSource.originalSource.id, enhancedSource.originalSource::class.qualifiedName ?: delegate.originalSourceQualifiedClassName, (enhancedSource.enhancedSource as DelegatedHttpSource)::class, delegate.factory)).associateBy { it.originalSourceQualifiedClassName } currentDelegatedSources.plusAssign(map) @@ -132,12 +132,12 @@ open class SourceManager(private val context: Context) { if (prefs.enableExhentai().get()) { exSrcs += EHentai(EXH_SOURCE_ID, true, context) } - exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, PervEdenLang.en) - exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, PervEdenLang.it) + exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, PervEdenLang.en, context) + exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, PervEdenLang.it, context) exSrcs += NHentai(context) - exSrcs += Hitomi() - exSrcs += EightMuses() - exSrcs += HBrowse() + exSrcs += Hitomi(context) + exSrcs += EightMuses(context) + exSrcs += HBrowse(context) return exSrcs } // SY <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index 7f2f7f0c9..275024f90 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -271,7 +271,7 @@ class EHentai( // Support direct URL importing override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = - urlImportFetchSearchManga(query) { + urlImportFetchSearchManga(context, query) { searchMangaRequestObservable(page, query, filters).flatMap { client.newCall(it).asObservableSuccess() }.map { response -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt index 1ffd8ee05..4dcd148de 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.all +import android.content.Context import android.net.Uri import android.os.Build import com.github.salomonbrys.kotson.array @@ -41,7 +42,7 @@ import uy.kohesive.injekt.injectLazy /** * Man, I hate this source :( */ -class Hitomi : HttpSource(), LewdSource, UrlImportableSource { +class Hitomi(val context: Context) : HttpSource(), LewdSource, UrlImportableSource { private val prefs: PreferencesHelper by injectLazy() override val id = HITOMI_SOURCE_ID @@ -185,7 +186,7 @@ class Hitomi : HttpSource(), LewdSource, UrlImpo override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException() override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - return urlImportFetchSearchManga(query) { + return urlImportFetchSearchManga(context, query) { val splitQuery = query.split(" ") val positive = splitQuery.filter { !it.startsWith('-') }.toMutableList() diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt index dd7c3d192..6d4dab3e8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.all +import android.content.Context import android.net.Uri import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.source.ConfigurableSource @@ -11,7 +12,7 @@ import exh.source.DelegatedHttpSource import exh.util.urlImportFetchSearchManga import rx.Observable -class MangaDex(delegate: HttpSource) : +class MangaDex(delegate: HttpSource, val context: Context) : DelegatedHttpSource(delegate), ConfigurableSource, UrlImportableSource { @@ -19,7 +20,7 @@ class MangaDex(delegate: HttpSource) : override val matchingHosts: List = listOf("mangadex.org", "www.mangadex.org") override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable = - urlImportFetchSearchManga(query) { + urlImportFetchSearchManga(context, query) { super.fetchSearchManga(page, query, filters) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt index 5f1193334..622f266c9 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt @@ -37,7 +37,7 @@ import rx.Observable * NHentai source */ -class NHentai(context: Context) : HttpSource(), LewdSource, UrlImportableSource { +class NHentai(val context: Context) : HttpSource(), LewdSource, UrlImportableSource { override val metaClass = NHentaiSearchMetadata::class override fun fetchPopularManga(page: Int): Observable { @@ -57,7 +57,7 @@ class NHentai(context: Context) : HttpSource(), LewdSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt index b36096064..76eddc386 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.all +import android.content.Context import android.net.Uri import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess @@ -33,7 +34,7 @@ import org.jsoup.nodes.TextNode import rx.Observable // TODO Transform into delegated source -class PervEden(override val id: Long, val pvLang: PervEdenLang) : +class PervEden(override val id: Long, val pvLang: PervEdenLang, val context: Context) : ParsedHttpSource(), LewdSource, UrlImportableSource { @@ -64,7 +65,7 @@ class PervEden(override val id: Long, val pvLang: PervEdenLang) : // Support direct URL importing override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = - urlImportFetchSearchManga(query) { + urlImportFetchSearchManga(context, query) { super.fetchSearchManga(page, query, filters) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/EightMuses.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/EightMuses.kt index 5a389e093..2dcc694f0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/EightMuses.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/EightMuses.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.english +import android.content.Context import android.net.Uri import com.kizitonwose.time.hours import eu.kanade.tachiyomi.network.GET @@ -41,7 +42,7 @@ import rx.schedulers.Schedulers typealias SiteMap = NakedTrie -class EightMuses : +class EightMuses(val context: Context) : HttpSource(), LewdSource, UrlImportableSource { @@ -177,7 +178,7 @@ class EightMuses : override fun fetchPopularManga(page: Int) = fetchListing(popularMangaRequest(page), false) // TODO Dig override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - return urlImportFetchSearchManga(query) { + return urlImportFetchSearchManga(context, query) { fetchListing(searchMangaRequest(page, query, filters), false) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt index 414747474..262a35d20 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.english +import android.content.Context import android.net.Uri import com.github.salomonbrys.kotson.array import com.github.salomonbrys.kotson.string @@ -43,7 +44,7 @@ import org.jsoup.nodes.Element import rx.Observable import rx.schedulers.Schedulers -class HBrowse : HttpSource(), LewdSource, UrlImportableSource { +class HBrowse(val context: Context) : HttpSource(), LewdSource, UrlImportableSource { /** * An ISO 639-1 compliant language code (two letters in lower case). */ @@ -110,7 +111,7 @@ class HBrowse : HttpSource(), LewdSource, UrlIm * @param filters the list of filters to apply. */ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - return urlImportFetchSearchManga(query) { + return urlImportFetchSearchManga(context, query) { fetchSearchMangaInternal(page, query, filters) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt index c624fc6e6..a0e6e6ff9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.english +import android.content.Context import android.net.Uri import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.model.FilterList @@ -19,7 +20,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.jsoup.nodes.Document import rx.Observable -class HentaiCafe(delegate: HttpSource) : +class HentaiCafe(delegate: HttpSource, val context: Context) : DelegatedHttpSource(delegate), LewdSource, UrlImportableSource { @@ -34,7 +35,7 @@ class HentaiCafe(delegate: HttpSource) : // Support direct URL importing override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = - urlImportFetchSearchManga(query) { + urlImportFetchSearchManga(context, query) { super.fetchSearchManga(page, query, filters) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Pururin.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Pururin.kt index 4b6d609e7..0bab14afd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Pururin.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Pururin.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.english +import android.content.Context import android.net.Uri import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.model.FilterList @@ -18,7 +19,7 @@ import exh.util.urlImportFetchSearchManga import org.jsoup.nodes.Document import rx.Observable -class Pururin(delegate: HttpSource) : +class Pururin(delegate: HttpSource, val context: Context) : DelegatedHttpSource(delegate), LewdSource, UrlImportableSource { @@ -38,7 +39,7 @@ class Pururin(delegate: HttpSource) : "$baseUrl/gallery/$trimmedIdQuery/-" } else query - return urlImportFetchSearchManga(newQuery) { + return urlImportFetchSearchManga(context, newQuery) { super.fetchSearchManga(page, query, filters) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt index b61a0fe2b..97cbc415c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.source.online.english +import android.content.Context import android.net.Uri import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.model.FilterList @@ -19,7 +20,7 @@ import java.util.Locale import org.jsoup.nodes.Document import rx.Observable -class Tsumino(delegate: HttpSource) : +class Tsumino(delegate: HttpSource, val context: Context) : DelegatedHttpSource(delegate), LewdSource, UrlImportableSource { @@ -28,7 +29,7 @@ class Tsumino(delegate: HttpSource) : // Support direct URL importing override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = - urlImportFetchSearchManga(query) { + urlImportFetchSearchManga(context, query) { super.fetchSearchManga(page, query, filters) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt index 15a9bdfb0..5adc02f0b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt @@ -64,7 +64,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import reactivecircus.flowbinding.appcompat.QueryTextEvent import reactivecircus.flowbinding.appcompat.queryTextEvents -import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy /** @@ -193,7 +192,7 @@ open class BrowseSourceController(bundle: Bundle) : if (presenter.sourceFilters.isEmpty()) { // SY --> - actionFab?.text = activity!!.getString(R.string.eh_saved_searches) + actionFab?.text = activity!!.getString(R.string.saved_searches) // SY <-- } @@ -218,8 +217,8 @@ open class BrowseSourceController(bundle: Bundle) : onSaveClicked = { filterSheet?.context?.let { MaterialDialog(it) - .title(text = "Save current search query?") - .input("My search name", hintRes = null) { _, searchName -> + .title(R.string.save_search) + .input(hintRes = R.string.save_search_hint) { _, searchName -> val oldSavedSearches = presenter.loadSearches() if (searchName.isNotBlank() && oldSavedSearches.size < MAX_SAVED_SEARCHES @@ -248,8 +247,8 @@ open class BrowseSourceController(bundle: Bundle) : if (search == null) { filterSheet?.context?.let { MaterialDialog(it) - .title(text = "Failed to load saved searches!") - .message(text = "An error occurred while loading your saved searches.") + .title(R.string.save_search_failed_to_load) + .message(R.string.save_search_failed_to_load_message) .cancelable(true) .cancelOnTouchOutside(true) .show() @@ -275,8 +274,8 @@ open class BrowseSourceController(bundle: Bundle) : if (search == null || search.name != name) { filterSheet?.context?.let { MaterialDialog(it) - .title(text = "Failed to delete saved search!") - .message(text = "An error occurred while deleting the search.") + .title(R.string.save_search_failed_to_delete) + .message(R.string.save_search_failed_to_delete_message) .cancelable(true) .cancelOnTouchOutside(true) .show() @@ -286,10 +285,10 @@ open class BrowseSourceController(bundle: Bundle) : filterSheet?.context?.let { MaterialDialog(it) - .title(text = "Delete saved search query?") - .message(text = "Are you sure you wish to delete your saved search query: '${search.name}'?") + .title(R.string.save_search_delete) + .message(text = it.getString(R.string.save_search_delete_message, search.name)) .positiveButton(R.string.action_cancel) - .negativeButton(text = "Confirm") { + .negativeButton(android.R.string.yes) { val newSearches = savedSearches.filterIndexed { index, _ -> index != indexToDelete } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index f14037792..ae671baa3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -696,7 +696,7 @@ class LibraryController( private fun showSyncProgressDialog() { favSyncDialog?.dismiss() favSyncDialog = buildDialog() - ?.title(text = "Favorites syncing") + ?.title(R.string.favorites_syncing) ?.cancelable(false) // ?.progress(true, 0) favSyncDialog?.show() @@ -723,15 +723,15 @@ class LibraryController( favSyncDialog?.dismiss() favSyncDialog = buildDialog() - ?.title(text = "Favorites sync error") - ?.message(text = status.message + " Sync will not start until the gallery is in only one category.") + ?.title(R.string.favorites_sync_error) + ?.message(text = activity!!.getString(R.string.favorites_sync_bad_library_state, status.message)) ?.cancelable(false) - ?.positiveButton(text = "Show gallery") { + ?.positiveButton(R.string.show_gallery) { openManga(status.manga) - presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle()) + presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle(activity!!)) } ?.negativeButton(android.R.string.ok) { - presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle()) + presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle(activity!!)) } favSyncDialog?.show() } @@ -740,11 +740,11 @@ class LibraryController( favSyncDialog?.dismiss() favSyncDialog = buildDialog() - ?.title(text = "Favorites sync error") - ?.message(text = "An error occurred during the sync process: ${status.message}") + ?.title(R.string.favorites_sync_error) + ?.message(text = activity!!.getString(R.string.favorites_sync_error_string, status.message)) ?.cancelable(false) ?.positiveButton(android.R.string.ok) { - presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle()) + presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle(activity!!)) } favSyncDialog?.show() } @@ -753,11 +753,11 @@ class LibraryController( favSyncDialog?.dismiss() favSyncDialog = buildDialog() - ?.title(text = "Favorites sync complete with errors") - ?.message(text = "Errors occurred during the sync process that were ignored:\n${status.message}") + ?.title(R.string.favorites_sync_done_errors) + ?.message(text = activity!!.getString(R.string.favorites_sync_done_errors_message, status.message)) ?.cancelable(false) ?.positiveButton(android.R.string.ok) { - presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle()) + presenter.favoritesSync.status.onNext(FavoritesSyncStatus.Idle(activity!!)) } favSyncDialog?.show() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 421dc79a7..eb303c329 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -548,7 +548,7 @@ class MangaController : startActivityForResult( Intent.createChooser( intent, - resources?.getString(R.string.select_cover_image) + resources?.getString(R.string.action_edit_cover) ), REQUEST_EDIT_MANGA_COVER ) @@ -750,7 +750,7 @@ class MangaController : presenter.editCoverWithStream(uri) } } catch (error: IOException) { - activity.toast(R.string.failed_to_update_cover) + activity.toast(R.string.notification_cover_update_failed) Timber.e(error) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index ac278287d..d1294c0f1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -133,7 +133,7 @@ class MangaInfoHeaderAdapter( binding.btnSmartSearch.clicks() .onEach { controller.openSmartSearch() } .launchIn(scope) - binding.btnSmartSearch.setTooltip(R.string.eh_merge_with_another_source) + binding.btnSmartSearch.setTooltip(R.string.merge_with_another_source) } // SY <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index f99d8ac8d..1bc14ed80 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -146,12 +146,12 @@ class SettingsAdvancedController : SettingsController() { // --> EXH preferenceCategory { - title = "Developer tools" + titleRes = R.string.developer_tools isPersistent = false switchPreference { - title = "Enable integrated hentai features" - summary = "This is a experimental feature that will disable all hentai features if toggled off" + titleRes = R.string.toggle_hentai_features + summaryRes = R.string.toggle_hentai_features_summary key = Keys.eh_is_hentai_enabled defaultValue = true @@ -212,35 +212,35 @@ class SettingsAdvancedController : SettingsController() { } switchPreference { - title = "Enable delegated sources" + titleRes = R.string.toggle_delegated_sources key = Keys.eh_delegateSources defaultValue = true - summary = "Apply ${context.getString(R.string.app_name)} enhancements to the following sources if they are installed: ${DELEGATED_SOURCES.values.map { it.sourceName }.distinct().joinToString()}" + summary = context.getString(R.string.toggle_delegated_sources_summary, context.getString(R.string.app_name), DELEGATED_SOURCES.values.map { it.sourceName }.distinct().joinToString()) } intListPreference { key = Keys.eh_logLevel - title = "Log level" + titleRes = R.string.log_level entries = EHLogLevel.values().map { - "${it.name.toLowerCase().capitalize()} (${it.description})" + "${context.getString(it.nameRes)} (${context.getString(it.description)})" }.toTypedArray() entryValues = EHLogLevel.values().mapIndexed { index, _ -> "$index" }.toTypedArray() defaultValue = "0" - summary = "Changing this can impact app performance. Force-restart app after changing. Current value: %s" + summaryRes = R.string.log_level_summary } switchPreference { - title = "Enable source blacklist" + titleRes = R.string.enable_source_blacklist key = Keys.eh_enableSourceBlacklist defaultValue = true - summary = "Hide extensions/sources that are incompatible with ${context.getString(R.string.app_name)}. Force-restart app after changing." + summary = context.getString(R.string.enable_source_blacklist_summary, context.getString(R.string.app_name)) } preference { - title = "Open debug menu" - summary = HtmlCompat.fromHtml("DO NOT TOUCH THIS MENU UNLESS YOU KNOW WHAT YOU ARE DOING! IT CAN CORRUPT YOUR LIBRARY!", HtmlCompat.FROM_HTML_MODE_LEGACY) + titleRes = R.string.open_debug_menu + summary = HtmlCompat.fromHtml(context.getString(R.string.open_debug_menu_summary), HtmlCompat.FROM_HTML_MODE_LEGACY) onClick { router.pushController(SettingsDebugController().withFadeTransaction()) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt index 6d68daf40..7ba211340 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt @@ -33,6 +33,7 @@ import eu.kanade.tachiyomi.util.preference.preference import eu.kanade.tachiyomi.util.preference.preferenceCategory import eu.kanade.tachiyomi.util.preference.summaryRes import eu.kanade.tachiyomi.util.preference.switchPreference +import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.system.toast import exh.EH_SOURCE_ID import exh.EXH_SOURCE_ID @@ -143,14 +144,14 @@ class SettingsEhController : SettingsController() { } override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { - title = "E-Hentai" + titleRes = R.string.pref_category_eh preferenceCategory { - title = "E-Hentai Website Account Settings" + titleRes = R.string.ehentai_prefs_account_settings switchPreference { - title = "Enable ExHentai" - summaryOff = "Requires login" + titleRes = R.string.enable_exhentai + summaryOff = context.getString(R.string.requires_login) key = PreferenceKeys.eh_enableExHentai isPersistent = false defaultValue = false @@ -178,33 +179,23 @@ class SettingsEhController : SettingsController() { } intListPreference { - title = "Use Hentai@Home Network" + titleRes = R.string.use_hentai_at_home key = PreferenceKeys.eh_enable_hah - if (preferences.eh_hathPerksCookies().get().isBlank()) { - summary = "Do you wish to load images through the Hentai@Home Network, if available? Disabling this option will reduce the amount of pages you are able to view\nOptions:\n - Any client (Recommended)\n - Default port clients only (Can be slower. Enable if behind firewall/proxy that blocks outgoing non-standard ports.)" - entries = arrayOf( - "Any client (Recommended)", - "Default port clients only" - ) - entryValues = arrayOf("0", "1") - } else { - summary = "Do you wish to load images through the Hentai@Home Network, if available? Disabling this option will reduce the amount of pages you are able to view\nOptions:\n - Any client (Recommended)\n - Default port clients only (Can be slower. Enable if behind firewall/proxy that blocks outgoing non-standard ports.)\n - No (Donator only. You will not be able to browse as many pages, enable only if having severe problems.)" - entries = arrayOf( - "Any client (Recommended)", - "Default port clients only", - "No(will select Default port clients only if you are not a donator)" - ) - entryValues = arrayOf("0", "1", "2") - } + summaryRes = R.string.use_hentai_at_home_summary + entriesRes = arrayOf( + R.string.use_hentai_at_home_option_1, + R.string.use_hentai_at_home_option_2 + ) + entryValues = arrayOf("0", "1") onChange { preferences.useHentaiAtHome().reconfigure() } }.dependency = PreferenceKeys.eh_enableExHentai switchPreference { - title = "Show Japanese titles in search results" - summaryOn = "Currently showing Japanese titles in search results. Clear the chapter cache after changing this (in the Advanced section)" - summaryOff = "Currently showing English/Romanized titles in search results. Clear the chapter cache after changing this (in the Advanced section)" + titleRes = R.string.show_japanese_titles + summaryOn = context.getString(R.string.show_japanese_titles_option_1) + summaryOff = context.getString(R.string.show_japanese_titles_option_2) key = "use_jp_title" defaultValue = false @@ -212,9 +203,9 @@ class SettingsEhController : SettingsController() { }.dependency = PreferenceKeys.eh_enableExHentai switchPreference { - title = "Use original images" - summaryOn = "Currently using original images" - summaryOff = "Currently using resampled images" + titleRes = R.string.use_original_images + summaryOn = context.getString(R.string.use_original_images_on) + summaryOff = context.getString(R.string.use_original_images_off) key = PreferenceKeys.eh_useOrigImages defaultValue = false @@ -222,28 +213,28 @@ class SettingsEhController : SettingsController() { }.dependency = PreferenceKeys.eh_enableExHentai preference { - title = "Watched Tags" - summary = "Opens a webview to your E/ExHentai watched tags page" + titleRes = R.string.watched_tags + summaryRes = R.string.watched_tags_summary onClick { val intent = if (preferences.enableExhentai().get()) { - WebViewActivity.newIntent(activity!!, url = "https://exhentai.org/mytags", title = "ExHentai Watched Tags") + WebViewActivity.newIntent(activity!!, url = "https://exhentai.org/mytags", title = context.getString(R.string.watched_tags_exh)) } else { - WebViewActivity.newIntent(activity!!, url = "https://e-hentai.org/mytags", title = "E-Hentai Watched Tags") + WebViewActivity.newIntent(activity!!, url = "https://e-hentai.org/mytags", title = context.getString(R.string.watched_tags_eh)) } startActivity(intent) } }.dependency = PreferenceKeys.eh_enableExHentai preference { - title = "Tag Filtering Threshold" + titleRes = R.string.tag_filtering_threshold key = PreferenceKeys.eh_tag_filtering_value defaultValue = 0 - summary = "You can soft filter tags by adding them to the \"My Tags\" E/ExHentai page with a negative weight. If a gallery has tags that add up to weight below this value, it is filtered from view. This threshold can be set between -9999 and 0. Currently: ${preferences.ehTagFilterValue().get()}" + summaryRes = R.string.tag_filtering_threshhold_summary onClick { MaterialDialog(activity!!) - .title(text = "Tag Filtering Threshold") + .title(R.string.tag_filtering_threshold) .input( inputType = InputType.TYPE_NUMBER_FLAG_SIGNED, waitForPositiveButton = false, @@ -255,14 +246,13 @@ class SettingsEhController : SettingsController() { if (value != null && value in -9999..0) { inputField.error = null } else { - inputField.error = "Must be between -9999 and 0!" + inputField.error = context.getString(R.string.tag_filtering_threshhold_error) } dialog.setActionButtonEnabled(WhichButton.POSITIVE, value != null && value in -9999..0) } .positiveButton(android.R.string.ok) { val value = it.getInputField().text.toString().toInt() preferences.ehTagFilterValue().set(value) - summary = "You can soft filter tags by adding them to the \"My Tags\" E/ExHentai page with a negative weight. If a gallery has tags that add up to weight below this value, it is filtered from view. This threshold can be set between 0 and -9999. Currently: $value" preferences.ehTagFilterValue().reconfigure() } .show() @@ -270,15 +260,15 @@ class SettingsEhController : SettingsController() { }.dependency = PreferenceKeys.eh_enableExHentai preference { - title = "Tag Watching Threshold" + titleRes = R.string.tag_watching_threshhold key = PreferenceKeys.eh_tag_watching_value defaultValue = 0 - summary = "Recently uploaded galleries will be included on the watched screen if it has at least one watched tag with positive weight, and the sum of weights on its watched tags add up to this value or higher. This threshold can be set between 0 and 9999. Currently: ${preferences.ehTagWatchingValue().get()}" + summaryRes = R.string.tag_watching_threshhold_summary onClick { MaterialDialog(activity!!) - .title(text = "Tag Watching Threshold") + .title(R.string.tag_watching_threshhold) .input( inputType = InputType.TYPE_NUMBER_FLAG_SIGNED, maxLength = 4, @@ -291,14 +281,13 @@ class SettingsEhController : SettingsController() { if (value != null && value in 0..9999) { inputField.error = null } else { - inputField.error = "Must be between 0 and 9999!" + inputField.error = context.getString(R.string.tag_watching_threshhold_error) } dialog.setActionButtonEnabled(WhichButton.POSITIVE, value != null && value in 0..9999) } .positiveButton(android.R.string.ok) { val value = it.getInputField().text.toString().toInt() preferences.ehTagWatchingValue().set(value) - summary = "Recently uploaded galleries will be included on the watched screen if it has at least one watched tag with positive weight, and the sum of weights on its watched tags add up to this value or higher. This threshold can be set between 0 and 9999. Currently: $value" preferences.ehTagWatchingValue().reconfigure() } .show() @@ -306,13 +295,13 @@ class SettingsEhController : SettingsController() { }.dependency = PreferenceKeys.eh_enableExHentai preference { - title = "Language Filtering" - summary = "If you wish to hide galleries in certain languages from the gallery list and searches, select them in the dialog that will popup.\nNote that matching galleries will never appear regardless of your search query.\nTldr checkmarked = exclude" + titleRes = R.string.language_filtering + summaryRes = R.string.language_filtering_summary onClick { MaterialDialog(activity!!) - .title(text = "Language Filtering") - .message(text = "If you wish to hide galleries in certain languages from the gallery list and searches, select them in the dialog that will popup.\nNote that matching galleries will never appear regardless of your search query.\nTldr checkmarked = exclude") + .title(R.string.language_filtering) + .message(R.string.language_filtering_summary) .customView(R.layout.eh_dialog_languages, scrollable = true) .positiveButton(android.R.string.ok) { val customView = it.view.contentLayout.customView!! @@ -444,13 +433,13 @@ class SettingsEhController : SettingsController() { }.dependency = PreferenceKeys.eh_enableExHentai preference { - title = "Front Page Categories" - summary = "What categories would you like to show by default on the front page and in searches? They can still be enabled by enabling their filters" + titleRes = R.string.frong_page_categories + summaryRes = R.string.fromt_page_categories_summary onClick { MaterialDialog(activity!!) - .title(text = "Front Page Categories") - .message(text = "What categories would you like to show by default on the front page and in searches? They can still be enabled by enabling their filters") + .title(R.string.frong_page_categories) + .message(R.string.fromt_page_categories_summary) .customView(R.layout.eh_dialog_categories, scrollable = true) .positiveButton { val customView = it.view.contentLayout.customView!! @@ -497,22 +486,22 @@ class SettingsEhController : SettingsController() { switchPreference { defaultValue = false key = PreferenceKeys.eh_watched_list_default_state - title = "Watched List Filter Default State" - summary = "When browsing ExHentai/E-Hentai should the watched list filter be enabled by default" - } + titleRes = R.string.watched_list_default + summaryRes = R.string.watched_list_state_summary + }.dependency = PreferenceKeys.eh_enableExHentai listPreference { defaultValue = "auto" key = PreferenceKeys.eh_ehentai_quality - summary = "The quality of the downloaded images" - title = "Image quality" - entries = arrayOf( - "Auto", - "2400x", - "1600x", - "1280x", - "980x", - "780x" + summaryRes = R.string.eh_image_quality_summary + titleRes = R.string.eh_image_quality + entriesRes = arrayOf( + R.string.eh_image_quality_auto, + R.string.eh_image_quality_2400, + R.string.eh_image_quality_1600, + R.string.eh_image_quality_1280, + R.string.eh_image_quality_980, + R.string.eh_image_quality_780 ) entryValues = arrayOf( "auto", @@ -528,18 +517,18 @@ class SettingsEhController : SettingsController() { } preferenceCategory { - title = "Favorites sync" + titleRes = R.string.favorites_sync switchPreference { - title = "Disable favorites uploading" - summary = "Favorites are only downloaded from ExHentai. Any changes to favorites in the app will not be uploaded. Prevents accidental loss of favorites on ExHentai. Note that removals will still be downloaded (if you remove a favorites on ExHentai, it will be removed in the app as well)." + titleRes = R.string.disable_favorites_uploading + summaryRes = R.string.disable_favorites_uploading_summary key = PreferenceKeys.eh_readOnlySync defaultValue = false } preference { - title = "Show favorites sync notes" - summary = "Show some information regarding the favorites sync feature" + titleRes = R.string.show_favorite_sync_notes + summaryRes = R.string.show_favorite_sync_notes_summary onClick { activity?.let { @@ -549,21 +538,21 @@ class SettingsEhController : SettingsController() { } switchPreference { - title = "Ignore sync errors when possible" - summary = "Do not abort immediately when encountering errors during the sync process. Errors will still be displayed when the sync is complete. Can cause loss of favorites in some cases. Useful when syncing large libraries." + titleRes = R.string.ignore_sync_errors + summaryRes = R.string.ignore_sync_errors_summary key = PreferenceKeys.eh_lenientSync defaultValue = false } preference { - title = "Force sync state reset" - summary = "Performs a full resynchronization on the next sync. Removals will not be synced. All favorites in the app will be re-uploaded to ExHentai and all favorites on ExHentai will be re-downloaded into the app. Useful for repairing sync after sync has been interrupted." + titleRes = R.string.force_sync_state_reset + summaryRes = R.string.force_sync_state_reset_summary onClick { activity?.let { activity -> MaterialDialog(activity) - .title(R.string.eh_force_sync_reset_title) - .message(R.string.eh_force_sync_reset_message) + .title(R.string.favorites_sync_reset) + .message(R.string.favorites_sync_reset_message) .positiveButton(android.R.string.yes) { LocalFavoritesStorage().apply { getRealm().use { @@ -572,7 +561,7 @@ class SettingsEhController : SettingsController() { } } } - activity.toast("Sync state reset", Toast.LENGTH_LONG) + activity.toast(context.getString(R.string.sync_state_reset), Toast.LENGTH_LONG) } .negativeButton(android.R.string.no) .cancelable(false) @@ -583,20 +572,20 @@ class SettingsEhController : SettingsController() { } preferenceCategory { - title = "Gallery update checker" + titleRes = R.string.gallery_update_checker intListPreference { key = PreferenceKeys.eh_autoUpdateFrequency - title = "Time between update batches" - entries = arrayOf( - "Never update galleries", - "1 hour", - "2 hours", - "3 hours", - "6 hours", - "12 hours", - "24 hours", - "48 hours" + titleRes = R.string.time_between_batches + entriesRes = arrayOf( + R.string.time_between_batches_never, + R.string.time_between_batches_1_hour, + R.string.time_between_batches_2_hours, + R.string.time_between_batches_3_hours, + R.string.time_between_batches_6_hours, + R.string.time_between_batches_12_hours, + R.string.time_between_batches_24_hours, + R.string.time_between_batches_48_hours ) entryValues = arrayOf("0", "1", "2", "3", "6", "12", "24", "48") defaultValue = "0" @@ -604,11 +593,9 @@ class SettingsEhController : SettingsController() { preferences.eh_autoUpdateFrequency().asFlow() .onEach { newVal -> summary = if (newVal == 0) { - "${context.getString(R.string.app_name)} will currently never check galleries in your library for updates." + context.getString(R.string.time_between_batches_summary_1, context.getString(R.string.app_name)) } else { - "${context.getString(R.string.app_name)} checks/updates galleries in batches. " + - "This means it will wait $newVal hour(s), check ${EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION} galleries," + - " wait $newVal hour(s), check ${EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION} and so on..." + context.getString(R.string.time_between_batches_summary_2, context.getString(R.string.app_name), newVal, EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION) } } .launchIn(scope) @@ -622,7 +609,7 @@ class SettingsEhController : SettingsController() { multiSelectListPreference { key = PreferenceKeys.eh_autoUpdateRestrictions - title = "Auto update restrictions" + titleRes = R.string.auto_update_restrictions entriesRes = arrayOf(R.string.wifi, R.string.charging) entryValues = arrayOf("wifi", "ac") summaryRes = R.string.pref_library_update_restriction_summary @@ -639,11 +626,11 @@ class SettingsEhController : SettingsController() { } preference { - title = "Show updater statistics" + titleRes = R.string.show_updater_statistics onClick { val progress = MaterialDialog(context) - .message(R.string.eh_show_update_statistics_dialog) + .message(R.string.gallery_updater_statistics_collection) .cancelable(false) progress.show() @@ -655,8 +642,8 @@ class SettingsEhController : SettingsController() { } val statsText = if (stats != null) { - "The updater last ran ${Humanize.naturalTime(Date(stats.startTime))}, and checked ${stats.updateCount} out of the ${stats.possibleUpdates} galleries that were ready for checking." - } else "The updater has not ran yet." + context.getString(R.string.gallery_updater_stats_text, Humanize.naturalTime(Date(stats.startTime)), stats.updateCount, stats.possibleUpdates) + } else context.getString(R.string.gallery_updater_not_ran_yet) val allMeta = db.getFavoriteMangaWithMetadata().await().filter { it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID @@ -672,26 +659,24 @@ class SettingsEhController : SettingsController() { }.count() } - """ - $statsText - - Galleries that were checked in the last: - - hour: ${metaInRelativeDuration(1.hours)} - - 6 hours: ${metaInRelativeDuration(6.hours)} - - 12 hours: ${metaInRelativeDuration(12.hours)} - - day: ${metaInRelativeDuration(1.days)} - - 2 days: ${metaInRelativeDuration(2.days)} - - week: ${metaInRelativeDuration(7.days)} - - month: ${metaInRelativeDuration(30.days)} - - year: ${metaInRelativeDuration(365.days)} - """.trimIndent() + statsText + "\n\n" + context.getString( + R.string.gallery_updater_stats_time, + metaInRelativeDuration(1.hours), + metaInRelativeDuration(6.hours), + metaInRelativeDuration(12.hours), + metaInRelativeDuration(1.days), + metaInRelativeDuration(2.days), + metaInRelativeDuration(7.days), + metaInRelativeDuration(30.days), + metaInRelativeDuration(365.days) + ) } finally { progress.dismiss() } withContext(Dispatchers.Main) { MaterialDialog(context) - .title(text = "Gallery updater statistics") + .title(R.string.gallery_updater_statistics) .message(text = updateInfo) .positiveButton(android.R.string.ok) .show() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index c6caab60f..1fa860840 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.util.preference.onChange import eu.kanade.tachiyomi.util.preference.onClick import eu.kanade.tachiyomi.util.preference.preference import eu.kanade.tachiyomi.util.preference.preferenceCategory +import eu.kanade.tachiyomi.util.preference.summaryRes import eu.kanade.tachiyomi.util.preference.switchPreference import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.system.LocaleHelper @@ -252,19 +253,18 @@ class SettingsGeneralController : SettingsController() { } // --> EXH preferenceCategory { - titleRes = R.string.eh_settings_category + titleRes = R.string.pref_category_fork switchPreference { key = Keys.eh_expandFilters - title = "Expand all search filters by default" + titleRes = R.string.toggle_expand_search_filters defaultValue = false } switchPreference { key = Keys.eh_autoSolveCaptchas - title = "Automatically solve captcha" - summary = - "Use HIGHLY EXPERIMENTAL automatic ReCAPTCHA solver. Will be grayed out if unsupported by your device." + titleRes = R.string.auto_solve_captchas + summaryRes = R.string.auto_solve_captchas_summary defaultValue = false } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt index ea6dd3e25..7d8900175 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt @@ -1,9 +1,12 @@ package eu.kanade.tachiyomi.ui.setting import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferenceKeys import eu.kanade.tachiyomi.util.preference.defaultValue +import eu.kanade.tachiyomi.util.preference.summaryRes import eu.kanade.tachiyomi.util.preference.switchPreference +import eu.kanade.tachiyomi.util.preference.titleRes /** * hitomi.la Settings fragment @@ -11,11 +14,11 @@ import eu.kanade.tachiyomi.util.preference.switchPreference class SettingsHlController : SettingsController() { override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { - title = "hitomi.la" + titleRes = R.string.pref_category_hl switchPreference { - title = "Use high-quality thumbnails" - summary = "May slow down search results" + titleRes = R.string.high_quality_thumbnails + summaryRes = R.string.high_quality_thumbnails_summary key = PreferenceKeys.eh_hl_useHighQualityThumbs defaultValue = false } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt index d00280075..c09962cda 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt @@ -219,11 +219,11 @@ class SettingsLibraryController : SettingsController() { .isNotEmpty() ) { preferenceCategory { - title = "Migration" + titleRes = R.string.migration switchPreference { key = Keys.skipPreMigration - titleRes = R.string.pref_skip_pre_migration + titleRes = R.string.skip_pre_migration summaryRes = R.string.pref_skip_pre_migration_summary defaultValue = false } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt index 2e7d27ba0..3da614bba 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt @@ -1,9 +1,12 @@ package eu.kanade.tachiyomi.ui.setting import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferenceKeys import eu.kanade.tachiyomi.util.preference.defaultValue +import eu.kanade.tachiyomi.util.preference.summaryRes import eu.kanade.tachiyomi.util.preference.switchPreference +import eu.kanade.tachiyomi.util.preference.titleRes /** * nhentai Settings fragment @@ -11,11 +14,11 @@ import eu.kanade.tachiyomi.util.preference.switchPreference class SettingsNhController : SettingsController() { override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { - title = "nhentai" + titleRes = R.string.pref_category_nh switchPreference { - title = "Use high-quality thumbnails" - summary = "May slow down search results" + titleRes = R.string.high_quality_thumbnails + summaryRes = R.string.high_quality_thumbnails_summary key = PreferenceKeys.eh_nh_useHighQualityThumbs defaultValue = false } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt index dae948e30..7b23c5cb1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt @@ -140,34 +140,31 @@ class SettingsReaderController : SettingsController() { // EXH --> preferenceCategory { - titleRes = R.string.eh_settings_category + titleRes = R.string.pref_category_fork intListPreference { key = Keys.eh_readerThreads - title = "Download threads" + titleRes = R.string.download_threads entries = arrayOf("1", "2", "3", "4", "5") entryValues = entries defaultValue = "2" - summary = - "Higher values can speed up image downloading significantly, but can also trigger bans. Recommended value is 2 or 3. Current value is: %s" + summaryRes = R.string.download_threads_summary } switchPreference { key = Keys.eh_aggressivePageLoading - title = "Aggressively load pages" - summary = - "Slowly download the entire gallery while reading instead of just loading the pages you are viewing." + titleRes = R.string.aggressively_load_pages + summaryRes = R.string.aggressively_load_pages_summary defaultValue = false } switchPreference { key = Keys.eh_readerInstantRetry - title = "Skip queue on retry" - summary = - "Normally, pressing the retry button on a failed download will wait until the downloader has finished downloading the last page before beginning to re-download the failed page. Enabling this will force the downloader to begin re-downloading the failed page as soon as you press the retry button." + titleRes = R.string.skip_queue_on_retry + summaryRes = R.string.skip_queue_on_retry_summary defaultValue = true } intListPreference { key = Keys.eh_preload_size - title = "Reader Preload amount" + titleRes = R.string.reader_preload_amount entryValues = arrayOf( "1", "2", @@ -180,25 +177,24 @@ class SettingsReaderController : SettingsController() { "14", "16" ) - entries = arrayOf( - "1 Page", - "2 Pages", - "3 Pages", - "4 Pages", - "6 Pages", - "8 Pages", - "10 Pages", - "12 Pages", - "14 Pages", - "16 Pages" + entriesRes = arrayOf( + R.string.reader_preload_amount_1_page, + R.string.reader_preload_amount_2_pages, + R.string.reader_preload_amount_3_pages, + R.string.reader_preload_amount_4_pages, + R.string.reader_preload_amount_6_pages, + R.string.reader_preload_amount_8_pages, + R.string.reader_preload_amount_10_pages, + R.string.reader_preload_amount_12_pages, + R.string.reader_preload_amount_14_pages, + R.string.reader_preload_amount_16_pages ) defaultValue = "4" - summary = - "The amount of pages to preload when reading. Higher values will result in a smoother reading experience, at the cost of higher cache usage, it is recommended to increase the amount of cache you allocate when using larger values" + summaryRes = R.string.reader_preload_amount_summary } listPreference { key = Keys.eh_cacheSize - title = "Reader cache size" + titleRes = R.string.reader_cache_size entryValues = arrayOf( "50", "75", @@ -236,18 +232,17 @@ class SettingsReaderController : SettingsController() { "5 GB" ) defaultValue = "75" - summary = - "The amount of images to save on device while reading. Higher values will result in a smoother reading experience, at the cost of higher disk space usage" + summaryRes = R.string.reader_cache_size_summary } switchPreference { key = Keys.eh_preserveReadingPosition - title = "Preserve reading position on read manga" + titleRes = R.string.preserve_reading_position defaultValue = false } switchPreference { key = Keys.eh_use_auto_webtoon - title = "Auto Webtoon Mode" - summary = "Use auto webtoon mode for manga that are detected to likely use the long strip format" + titleRes = R.string.auto_webtoon_mode + summaryRes = R.string.auto_webtoon_mode_summary defaultValue = true } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/AutoCompleteAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/AutoCompleteAdapter.kt index e343cbad3..9553a3fb4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/AutoCompleteAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/AutoCompleteAdapter.kt @@ -4,7 +4,6 @@ import android.content.Context import android.widget.ArrayAdapter import android.widget.Filter import android.widget.Filterable -import timber.log.Timber class AutoCompleteAdapter(context: Context, resource: Int, var objects: List, val excludePrefix: String?) : ArrayAdapter(context, resource, objects), @@ -35,8 +34,6 @@ class AutoCompleteAdapter(context: Context, resource: Int, var objects: List Unit = {} ): GalleryAddEvent { - XLog.d("Importing gallery (url: %s, fav: %s, forceSource: %s)...", url, fav, forceSource) + XLog.d(context.getString(R.string.gallery_adder_importing_gallery, url, fav.toString(), forceSource)) try { val uri = Uri.parse(url) @@ -31,10 +34,10 @@ class GalleryAdder { val source = if (forceSource != null) { try { if (forceSource.matchesUri(uri)) forceSource - else return GalleryAddEvent.Fail.UnknownType(url) + else return GalleryAddEvent.Fail.UnknownType(url, context) } catch (e: Exception) { - XLog.e("Source URI match check error!", e) - return GalleryAddEvent.Fail.UnknownType(url) + XLog.e(context.getString(R.string.gallery_adder_source_uri_must_match), e) + return GalleryAddEvent.Fail.UnknownType(url, context) } } else { sourceManager.getVisibleCatalogueSources() @@ -43,7 +46,7 @@ class GalleryAdder { try { it.matchesUri(uri) } catch (e: Exception) { - XLog.e("Source URI match check error!", e) + XLog.e(context.getString(R.string.gallery_adder_source_uri_must_match), e) false } } ?: sourceManager.getDelegatedCatalogueSources() @@ -52,27 +55,27 @@ class GalleryAdder { try { it.matchesUri(uri) } catch (e: Exception) { - XLog.e("Source URI match check error!", e) + XLog.e(context.getString(R.string.gallery_adder_source_uri_must_match), e) false } - } ?: return GalleryAddEvent.Fail.UnknownType(url) + } ?: return GalleryAddEvent.Fail.UnknownType(url, context) } // Map URL to manga URL val realUrl = try { source.mapUrlToMangaUrl(uri) } catch (e: Exception) { - XLog.e("Source URI map-to-manga error!", e) + XLog.e(context.getString(R.string.gallery_adder_uri_map_to_manga_error), e) null - } ?: return GalleryAddEvent.Fail.UnknownType(url) + } ?: return GalleryAddEvent.Fail.UnknownType(url, context) // Clean URL val cleanedUrl = try { source.cleanMangaUrl(realUrl) } catch (e: Exception) { - XLog.e("Source URI clean error!", e) + XLog.e(context.getString(R.string.gallery_adder_uri_clean_error), e) null - } ?: return GalleryAddEvent.Fail.UnknownType(url) + } ?: return GalleryAddEvent.Fail.UnknownType(url, context) // Use manga in DB if possible, otherwise, make a new manga val manga = db.getManga(cleanedUrl, source.id).executeAsBlocking() @@ -112,16 +115,16 @@ class GalleryAdder { syncChaptersWithSource(db, it, manga, source) }.toBlocking().first() } catch (e: Exception) { - XLog.w("Failed to update chapters for gallery: ${manga.title}!", e) - return GalleryAddEvent.Fail.Error(url, "Failed to update chapters for gallery: $url") + XLog.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e) + return GalleryAddEvent.Fail.Error(url, context.getString(R.string.gallery_adder_chapter_fetch_error, url)) } - return GalleryAddEvent.Success(url, manga) + return GalleryAddEvent.Success(url, manga, context) } catch (e: Exception) { - XLog.w("Could not add gallery (url: $url)!", e) + XLog.w(context.getString(R.string.gallery_adder_could_not_add_gallery, url), e) if (e is EHentai.GalleryNotFoundException) { - return GalleryAddEvent.Fail.NotFound(url) + return GalleryAddEvent.Fail.NotFound(url, context) } return GalleryAddEvent.Fail.Error( @@ -139,15 +142,16 @@ sealed class GalleryAddEvent { class Success( override val galleryUrl: String, - val manga: Manga + val manga: Manga, + val context: Context ) : GalleryAddEvent() { override val galleryTitle = manga.title - override val logMessage = "Added gallery: $galleryTitle" + override val logMessage = context.getString(R.string.batch_add_success_log_message, galleryTitle) } sealed class Fail : GalleryAddEvent() { - class UnknownType(override val galleryUrl: String) : Fail() { - override val logMessage = "Unknown gallery type for gallery: $galleryUrl" + class UnknownType(override val galleryUrl: String, val context: Context) : Fail() { + override val logMessage = context.getString(R.string.batch_add_unknown_type_log_message, galleryUrl) } open class Error( @@ -155,7 +159,7 @@ sealed class GalleryAddEvent { override val logMessage: String ) : Fail() - class NotFound(galleryUrl: String) : - Error(galleryUrl, "Gallery does not exist: $galleryUrl") + class NotFound(galleryUrl: String, context: Context) : + Error(galleryUrl, context.getString(R.string.batch_add_not_exist_log_message, galleryUrl)) } } diff --git a/app/src/main/java/exh/favorites/FavoritesIntroDialog.kt b/app/src/main/java/exh/favorites/FavoritesIntroDialog.kt index 1889e63cc..6c46fe829 100644 --- a/app/src/main/java/exh/favorites/FavoritesIntroDialog.kt +++ b/app/src/main/java/exh/favorites/FavoritesIntroDialog.kt @@ -3,6 +3,7 @@ package exh.favorites import android.content.Context import androidx.core.text.HtmlCompat import com.afollestad.materialdialogs.MaterialDialog +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import uy.kohesive.injekt.injectLazy @@ -10,26 +11,11 @@ class FavoritesIntroDialog { private val prefs: PreferencesHelper by injectLazy() fun show(context: Context) = MaterialDialog(context) - .title(text = "IMPORTANT FAVORITES SYNC NOTES") - .message(text = HtmlCompat.fromHtml(FAVORITES_INTRO_TEXT, HtmlCompat.FROM_HTML_MODE_LEGACY)) + .title(R.string.favorites_sync_notes) + .message(text = HtmlCompat.fromHtml(context.getString(R.string.favorites_sync_notes_message), HtmlCompat.FROM_HTML_MODE_LEGACY)) .positiveButton(android.R.string.ok) { prefs.eh_showSyncIntro().set(false) } .cancelable(false) .show() - - private val FAVORITES_INTRO_TEXT = - """ - 1. Changes to category names in the app are NOT synced! Please change the category names on ExHentai instead. The category names will be copied from the ExHentai servers every sync. -

- 2. The favorite categories on ExHentai correspond to the first 10 categories in the app (excluding the 'Default' category). Galleries in other categories will NOT be synced! -

- 3. ENSURE YOU HAVE A STABLE INTERNET CONNECTION WHEN SYNC IS IN PROGRESS! If the internet disconnects while the app is syncing, your favorites may be left in a partially-synced state. -

- 4. Keep the app open while favorites are syncing. Android will close apps that are in the background sometimes and that could be bad if it happens while the app is syncing. -

- 5. Do NOT put favorites in multiple categories (the app supports this). This can confuse the sync algorithm as ExHentai only allows each favorite to be in one category. -

- This dialog will only popup once. You can read these notes again by going to 'Settings > E-Hentai > Show favorites sync notes'. - """.trimIndent() } diff --git a/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt b/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt index d5b758866..0ae1f4630 100644 --- a/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt +++ b/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt @@ -4,6 +4,7 @@ import android.content.Context import android.net.wifi.WifiManager import android.os.PowerManager import com.elvishew.xlog.XLog +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Manga @@ -52,7 +53,7 @@ class FavoritesSyncHelper(val context: Context) { private val logger = XLog.tag("EHFavSync").build() - val status = BehaviorSubject.create(FavoritesSyncStatus.Idle()) + val status = BehaviorSubject.create(FavoritesSyncStatus.Idle(context)) @Synchronized fun runSync() { @@ -60,7 +61,7 @@ class FavoritesSyncHelper(val context: Context) { return } - status.onNext(FavoritesSyncStatus.Initializing()) + status.onNext(FavoritesSyncStatus.Initializing(context)) thread { beginSync() } } @@ -68,12 +69,12 @@ class FavoritesSyncHelper(val context: Context) { private fun beginSync() { // Check if logged in if (!prefs.enableExhentai().get()) { - status.onNext(FavoritesSyncStatus.Error("Please log in!")) + status.onNext(FavoritesSyncStatus.Error(context.getString(R.string.please_login))) return } // Validate library state - status.onNext(FavoritesSyncStatus.Processing("Verifying local library")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)) val libraryManga = db.getLibraryMangas().executeAsBlocking() val seenManga = HashSet(libraryManga.size) libraryManga.forEach { @@ -83,9 +84,9 @@ class FavoritesSyncHelper(val context: Context) { val inCategories = db.getCategoriesForManga(it).executeAsBlocking() status.onNext( FavoritesSyncStatus.BadLibraryState - .MangaInMultipleCategories(it, inCategories) + .MangaInMultipleCategories(it, inCategories, context) ) - logger.w("Manga %s is in multiple categories!", it.id) + logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, it.id)) return } else { seenManga += it.id!! @@ -94,11 +95,11 @@ class FavoritesSyncHelper(val context: Context) { // Download remote favorites val favorites = try { - status.onNext(FavoritesSyncStatus.Processing("Downloading favorites from remote server")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_downloading), context = context)) exh.fetchFavorites() } catch (e: Exception) { - status.onNext(FavoritesSyncStatus.Error("Failed to fetch favorites from remote server!")) - logger.e("Could not fetch favorites!", e) + status.onNext(FavoritesSyncStatus.Error(context.getString(R.string.favorites_sync_failed_to_featch))) + logger.e(context.getString(R.string.favorites_sync_could_not_fetch), e) return } @@ -127,17 +128,17 @@ class FavoritesSyncHelper(val context: Context) { storage.getRealm().use { realm -> realm.trans { db.inTransaction { - status.onNext(FavoritesSyncStatus.Processing("Calculating remote changes")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_remote_changes), context = context)) val remoteChanges = storage.getChangedRemoteEntries(realm, favorites.first) val localChanges = if (prefs.eh_readOnlySync().get()) { null // Do not build local changes if they are not going to be applied } else { - status.onNext(FavoritesSyncStatus.Processing("Calculating local changes")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_local_changes), context = context)) storage.getChangedDbEntries(realm) } // Apply remote categories - status.onNext(FavoritesSyncStatus.Processing("Updating category names")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_syncing_category_names), context = context)) applyRemoteCategories(favorites.second) // Apply change sets @@ -146,7 +147,7 @@ class FavoritesSyncHelper(val context: Context) { applyChangeSetToRemote(errorList, localChanges) } - status.onNext(FavoritesSyncStatus.Processing("Cleaning up")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_cleaning_up), context = context)) storage.snapshotEntries(realm) } } @@ -154,15 +155,15 @@ class FavoritesSyncHelper(val context: Context) { val theContext = context launchUI { - theContext.toast("Sync complete!") + theContext.toast(context.getString(R.string.favorites_sync_complete)) } } catch (e: IgnoredException) { // Do not display error as this error has already been reported - logger.w("Ignoring exception!", e) + logger.w(context.getString(R.string.favorites_sync_ignoring_exception), e) return } catch (e: Exception) { - status.onNext(FavoritesSyncStatus.Error("Unknown error: ${e.message}")) - logger.e("Sync error!", e) + status.onNext(FavoritesSyncStatus.Error(context.getString(R.string.favorites_sync_unknown_error, e.message))) + logger.e(context.getString(R.string.favorites_sync_sync_error), e) return } finally { // Release wake + wifi locks @@ -180,7 +181,7 @@ class FavoritesSyncHelper(val context: Context) { } if (errorList.isEmpty()) { - status.onNext(FavoritesSyncStatus.Idle()) + status.onNext(FavoritesSyncStatus.Idle(context)) } else { status.onNext(FavoritesSyncStatus.CompleteWithErrors(errorList)) } @@ -268,7 +269,7 @@ class FavoritesSyncHelper(val context: Context) { break } } catch (e: Exception) { - logger.w("Sync network error!", e) + logger.w(context.getString(R.string.favorites_sync_network_error), e) } } @@ -278,7 +279,7 @@ class FavoritesSyncHelper(val context: Context) { private fun applyChangeSetToRemote(errorList: MutableList, changeSet: ChangeSet) { // Apply removals if (changeSet.removed.isNotEmpty()) { - status.onNext(FavoritesSyncStatus.Processing("Removing ${changeSet.removed.size} galleries from remote server")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_removing_galleries, changeSet.removed.size), context = context)) val formBody = FormBody.Builder() .add("ddact", "delete") @@ -295,7 +296,7 @@ class FavoritesSyncHelper(val context: Context) { .build() if (!explicitlyRetryExhRequest(10, request)) { - val errorString = "Unable to delete galleries from the remote servers!" + val errorString = context.getString(R.string.favorites_sync_unable_to_delete) if (prefs.eh_lenientSync().get()) { errorList += errorString @@ -311,8 +312,9 @@ class FavoritesSyncHelper(val context: Context) { changeSet.added.forEachIndexed { index, it -> status.onNext( FavoritesSyncStatus.Processing( - "Adding gallery ${index + 1} of ${changeSet.added.size} to remote server", - needWarnThrottle() + context.getString(R.string.favorites_sync_adding_to_remote, index + 1, changeSet.added.size), + needWarnThrottle(), + context ) ) @@ -327,7 +329,7 @@ class FavoritesSyncHelper(val context: Context) { // Apply removals changeSet.removed.forEachIndexed { index, it -> - status.onNext(FavoritesSyncStatus.Processing("Removing gallery ${index + 1} of ${changeSet.removed.size} from local library")) + status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_remove_from_local, index + 1, changeSet.removed.size), context = context)) val url = it.getUrl() // Consider both EX and EH sources @@ -359,8 +361,9 @@ class FavoritesSyncHelper(val context: Context) { changeSet.added.forEachIndexed { index, it -> status.onNext( FavoritesSyncStatus.Processing( - "Adding gallery ${index + 1} of ${changeSet.added.size} to local library", - needWarnThrottle() + context.getString(R.string.favorites_sync_add_to_local, index + 1, changeSet.added.size), + needWarnThrottle(), + context ) ) @@ -368,6 +371,7 @@ class FavoritesSyncHelper(val context: Context) { // Import using gallery adder val result = galleryAdder.addGallery( + context, "${exh.baseUrl}${it.getUrl()}", true, exh, @@ -376,14 +380,14 @@ class FavoritesSyncHelper(val context: Context) { if (result is GalleryAddEvent.Fail) { if (result is GalleryAddEvent.Fail.NotFound) { - XLog.e("Remote gallery does not exist, skipping: %s!", it.getUrl()) + XLog.e(context.getString(R.string.favorites_sync_remote_not_exist, it.getUrl())) // Skip this gallery, it no longer exists return@forEachIndexed } - val errorString = "Failed to add gallery to local database: " + when (result) { - is GalleryAddEvent.Fail.Error -> "'${it.title}' ${result.logMessage}" - is GalleryAddEvent.Fail.UnknownType -> "'${it.title}' (${result.galleryUrl}) is not a valid gallery!" + val errorString = context.getString(R.string.favorites_sync_failed_to_add_to_local) + when (result) { + is GalleryAddEvent.Fail.Error -> context.getString(R.string.favorites_sync_failed_to_add_to_local_error, it.title, result.logMessage) + is GalleryAddEvent.Fail.UnknownType -> context.getString(R.string.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl) } if (prefs.eh_lenientSync().get()) { @@ -418,20 +422,22 @@ class FavoritesSyncHelper(val context: Context) { } } +// TODO String resources sealed class FavoritesSyncStatus(val message: String) { class Error(message: String) : FavoritesSyncStatus(message) - class Idle : FavoritesSyncStatus("Waiting for sync to start") + class Idle(context: Context) : FavoritesSyncStatus(context.getString(R.string.favorites_sync_waiting_for_start)) sealed class BadLibraryState(message: String) : FavoritesSyncStatus(message) { class MangaInMultipleCategories( val manga: Manga, - val categories: List + val categories: List, + context: Context ) : - BadLibraryState("The gallery: ${manga.title} is in more than one category (${categories.joinToString { it.name }})!") + BadLibraryState(context.getString(R.string.favorites_sync_manga_in_multiple_categories, manga.title, categories.joinToString { it.name })) } - class Initializing : FavoritesSyncStatus("Initializing sync") - class Processing(message: String, isThrottle: Boolean = false) : FavoritesSyncStatus( + class Initializing(context: Context) : FavoritesSyncStatus(context.getString(R.string.favorites_sync_initializing)) + class Processing(message: String, isThrottle: Boolean = false, context: Context) : FavoritesSyncStatus( if (isThrottle) { - "$message\n\nSync is currently throttling (to avoid being banned from ExHentai) and may take a long time to complete." + context.getString(R.string.favorites_sync_processing_throttle, message) } else { message } diff --git a/app/src/main/java/exh/log/EHLogLevel.kt b/app/src/main/java/exh/log/EHLogLevel.kt index cc8ce7a64..76269ac40 100644 --- a/app/src/main/java/exh/log/EHLogLevel.kt +++ b/app/src/main/java/exh/log/EHLogLevel.kt @@ -1,13 +1,15 @@ package exh.log import android.content.Context +import androidx.annotation.StringRes import androidx.preference.PreferenceManager +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferenceKeys -enum class EHLogLevel(val description: String) { - MINIMAL("critical errors only"), - EXTRA("log everything"), - EXTREME("network inspection mode"); +enum class EHLogLevel(@StringRes val nameRes: Int, @StringRes val description: Int) { + MINIMAL(R.string.log_minimal, R.string.log_minimal_desc), + EXTRA(R.string.log_extra, R.string.log_extra_desc), + EXTREME(R.string.log_extreme, R.string.log_extreme_desc); companion object { private var curLogLevel: Int? = null diff --git a/app/src/main/java/exh/uconfig/ConfiguringDialogController.kt b/app/src/main/java/exh/uconfig/ConfiguringDialogController.kt index 5bc9a8f6b..5777fbc67 100644 --- a/app/src/main/java/exh/uconfig/ConfiguringDialogController.kt +++ b/app/src/main/java/exh/uconfig/ConfiguringDialogController.kt @@ -4,6 +4,7 @@ import android.app.Dialog import android.os.Bundle import android.view.View import com.afollestad.materialdialogs.MaterialDialog +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.system.toast @@ -17,16 +18,16 @@ class ConfiguringDialogController : DialogController() { if (savedViewState == null) { thread { try { - EHConfigurator().configureAll() + EHConfigurator(activity!!).configureAll() launchUI { - activity?.toast("Settings successfully uploaded!") + activity?.toast(activity?.getString(R.string.eh_settings_successfully_uploaded)) } } catch (e: Exception) { activity?.let { it.runOnUiThread { MaterialDialog(it) - .title(text = "Configuration failed!") - .message(text = "An error occurred during the configuration process: " + e.message) + .title(R.string.eh_settings_configuration_failed) + .message(text = it.getString(R.string.eh_settings_configuration_failed_message, e.message)) .positiveButton(android.R.string.ok) .show() } @@ -40,8 +41,8 @@ class ConfiguringDialogController : DialogController() { } return MaterialDialog(activity!!) - .title(text = "Uploading settings to server") - .message(text = "Please wait, this may take some time...") + .title(R.string.eh_settings_uploading_to_server) + .message(R.string.eh_settings_uploading_to_server_message) .cancelable(false) .also { materialDialog = it diff --git a/app/src/main/java/exh/uconfig/EHConfigurator.kt b/app/src/main/java/exh/uconfig/EHConfigurator.kt index c5ec98e25..ae4e5b18d 100644 --- a/app/src/main/java/exh/uconfig/EHConfigurator.kt +++ b/app/src/main/java/exh/uconfig/EHConfigurator.kt @@ -1,5 +1,7 @@ package exh.uconfig +import android.content.Context +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.all.EHentai @@ -13,7 +15,7 @@ import okhttp3.Request import timber.log.Timber import uy.kohesive.injekt.injectLazy -class EHConfigurator { +class EHConfigurator(val context: Context) { private val prefs: PreferencesHelper by injectLazy() private val sources: SourceManager by injectLazy() @@ -104,7 +106,7 @@ class EHConfigurator { // No profile slots left :( if (availableProfiles.isEmpty()) { - throw IllegalStateException("You are out of profile slots on ${source.name}, please delete a profile!") + throw IllegalStateException(context.getString(R.string.eh_settings_out_of_slots_error, source.name)) } // Create profile in available slot diff --git a/app/src/main/java/exh/uconfig/WarnConfigureDialogController.kt b/app/src/main/java/exh/uconfig/WarnConfigureDialogController.kt index 8dcbd9ab4..370de906b 100644 --- a/app/src/main/java/exh/uconfig/WarnConfigureDialogController.kt +++ b/app/src/main/java/exh/uconfig/WarnConfigureDialogController.kt @@ -4,6 +4,7 @@ import android.app.Dialog import android.os.Bundle import com.afollestad.materialdialogs.MaterialDialog import com.bluelinelabs.conductor.Router +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.controller.DialogController import uy.kohesive.injekt.Injekt @@ -14,15 +15,8 @@ class WarnConfigureDialogController : DialogController() { private val prefs: PreferencesHelper by injectLazy() override fun onCreateDialog(savedViewState: Bundle?): Dialog { return MaterialDialog(activity!!) - .title(text = "Settings profile note") - .message( - text = - """ - The app will now add a new settings profile on E-Hentai and ExHentai to optimize app performance. Please ensure that you have less than three profiles on both sites. - - If you have no idea what settings profiles are, then it probably doesn't matter, just hit 'OK'. - """.trimIndent() - ) + .title(R.string.settings_profile_note) + .message(R.string.settings_profile_note_message) .positiveButton(android.R.string.ok) { prefs.eh_showSettingsUploadWarning().set(false) ConfiguringDialogController().showDialog(router) diff --git a/app/src/main/java/exh/ui/batchadd/BatchAddController.kt b/app/src/main/java/exh/ui/batchadd/BatchAddController.kt index fae01e6e2..d61eea9fb 100755 --- a/app/src/main/java/exh/ui/batchadd/BatchAddController.kt +++ b/app/src/main/java/exh/ui/batchadd/BatchAddController.kt @@ -5,6 +5,7 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import com.afollestad.materialdialogs.MaterialDialog +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.databinding.EhFragmentBatchAddBinding import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.util.lang.combineLatest @@ -26,7 +27,7 @@ class BatchAddController : NucleusController materialDialog.dismiss() } .cancelable(true) .cancelOnTouchOutside(true) diff --git a/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt b/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt index 06d84dd7b..acc87b9fc 100644 --- a/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt +++ b/app/src/main/java/exh/ui/batchadd/BatchAddPresenter.kt @@ -1,7 +1,9 @@ package exh.ui.batchadd +import android.content.Context import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.ReplayRelay +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import exh.GalleryAddEvent @@ -20,7 +22,7 @@ class BatchAddPresenter : BasePresenter() { var eventRelay: ReplayRelay? = null val currentlyAddingRelay = BehaviorRelay.create(STATE_IDLE)!! - fun addGalleries(galleries: String) { + fun addGalleries(context: Context, galleries: String) { eventRelay = ReplayRelay.create() val regex = """[0-9]*?\.[a-z0-9]*?:""".toRegex() @@ -53,7 +55,7 @@ class BatchAddPresenter : BasePresenter() { val failed = mutableListOf() splitGalleries.forEachIndexed { i, s -> - val result = galleryAdder.addGallery(s, true) + val result = galleryAdder.addGallery(context, s, true) if (result is GalleryAddEvent.Success) { succeeded.add(s) } else { @@ -63,15 +65,15 @@ class BatchAddPresenter : BasePresenter() { eventRelay?.call( ( when (result) { - is GalleryAddEvent.Success -> "[OK]" - is GalleryAddEvent.Fail -> "[ERROR]" + is GalleryAddEvent.Success -> context.getString(R.string.batch_add_ok) + is GalleryAddEvent.Fail -> context.getString(R.string.batch_add_error) } ) + " " + result.logMessage ) } // Show report - val summary = "\nSummary:\nAdded: ${succeeded.size} gallerie(s)\nFailed: ${failed.size} gallerie(s)" + val summary = context.getString(R.string.batch_add_summary, succeeded.size, failed.size) eventRelay?.call(summary) } } diff --git a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt index 6082b4281..f8418b692 100644 --- a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt +++ b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt @@ -15,6 +15,7 @@ import com.afollestad.materialdialogs.MaterialDialog import com.github.salomonbrys.kotson.get import com.github.salomonbrys.kotson.string import com.google.gson.JsonParser +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.asObservableSuccess @@ -180,8 +181,8 @@ class BrowserActionActivity : AppCompatActivity() { runOnUiThread { webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null) MaterialDialog(this) - .title(text = "Captcha solve failure") - .message(text = "Failed to auto-solve the captcha!") + .title(R.string.captcha_solve_failure) + .message(R.string.captcha_solve_failure_message) .cancelable(true) .cancelOnTouchOutside(true) .positiveButton(android.R.string.ok) diff --git a/app/src/main/java/exh/ui/intercept/InterceptActivity.kt b/app/src/main/java/exh/ui/intercept/InterceptActivity.kt index e92533256..3c7cb4da6 100755 --- a/app/src/main/java/exh/ui/intercept/InterceptActivity.kt +++ b/app/src/main/java/exh/ui/intercept/InterceptActivity.kt @@ -61,7 +61,7 @@ class InterceptActivity : BaseActivity() { when (it) { is InterceptResult.Success -> { binding.interceptProgress.gone() - binding.interceptStatus.text = "Launching app..." + binding.interceptStatus.setText(R.string.launching_app) onBackPressed() startActivity( Intent(this, MainActivity::class.java) @@ -72,10 +72,10 @@ class InterceptActivity : BaseActivity() { } is InterceptResult.Failure -> { binding.interceptProgress.gone() - binding.interceptStatus.text = "Error: ${it.reason}" + binding.interceptStatus.text = this.getString(R.string.error_with_reason, it.reason) MaterialDialog(this) - .title(text = "Error") - .message(text = "Could not open this gallery:\n\n${it.reason}") + .title(R.string.chapter_error) + .message(text = this.getString(R.string.could_not_open_gallery, it.reason)) .cancelable(true) .cancelOnTouchOutside(true) .positiveButton(android.R.string.ok) @@ -104,13 +104,13 @@ class InterceptActivity : BaseActivity() { // Load gallery async thread { - val result = galleryAdder.addGallery(gallery) + val result = galleryAdder.addGallery(this, gallery) status.onNext( when (result) { is GalleryAddEvent.Success -> result.manga.id?.let { InterceptResult.Success(it) - } ?: InterceptResult.Failure("Manga ID is null!") + } ?: InterceptResult.Failure(this.getString(R.string.manga_id_is_null)) is GalleryAddEvent.Fail -> InterceptResult.Failure(result.logMessage) } ) diff --git a/app/src/main/java/exh/util/SearchOverride.kt b/app/src/main/java/exh/util/SearchOverride.kt index ca4593729..ab0f16d53 100644 --- a/app/src/main/java/exh/util/SearchOverride.kt +++ b/app/src/main/java/exh/util/SearchOverride.kt @@ -1,5 +1,6 @@ package exh.util +import android.content.Context import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.online.UrlImportableSource import exh.GalleryAddEvent @@ -13,11 +14,11 @@ private val galleryAdder by lazy { /** * A version of fetchSearchManga that supports URL importing */ -fun UrlImportableSource.urlImportFetchSearchManga(query: String, fail: () -> Observable) = +fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable) = when { query.startsWith("http://") || query.startsWith("https://") -> { Observable.fromCallable { - val res = galleryAdder.addGallery(query, false, this) + val res = galleryAdder.addGallery(context, query, false, this) MangasPage( ( if (res is GalleryAddEvent.Success) { diff --git a/app/src/main/res/layout/manga_info_header.xml b/app/src/main/res/layout/manga_info_header.xml index 9376c956c..d708e5ff8 100644 --- a/app/src/main/res/layout/manga_info_header.xml +++ b/app/src/main/res/layout/manga_info_header.xml @@ -192,7 +192,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" - android:contentDescription="@string/eh_merge_with_another_source" + android:contentDescription="@string/merge_with_another_source" android:visibility="gone" app:icon="@drawable/eh_ic_find_replace_white_24dp" tools:visibility="visible" /> @@ -289,7 +289,7 @@ android:layout_marginTop="4dp" android:layout_marginEnd="16dp" android:layout_marginBottom="4dp" - android:text="@string/eh_merge_with_another_source" + android:text="@string/merge_with_another_source" android:visibility="gone" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/reader_settings_sheet.xml b/app/src/main/res/layout/reader_settings_sheet.xml index f7874ce7b..dcc7432fb 100644 --- a/app/src/main/res/layout/reader_settings_sheet.xml +++ b/app/src/main/res/layout/reader_settings_sheet.xml @@ -194,7 +194,7 @@ android:id="@+id/auto_webtoon_mode" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/eh_auto_webtoon_mode" + android:text="@string/auto_webtoon_mode" android:textColor="?android:attr/textColorSecondary" app:layout_constraintTop_toBottomOf="@id/always_show_chapter_transition" /> diff --git a/app/src/main/res/layout/source_filter_sheet_saved_searches.xml b/app/src/main/res/layout/source_filter_sheet_saved_searches.xml index 6cd0254ba..9f7eb9dc9 100644 --- a/app/src/main/res/layout/source_filter_sheet_saved_searches.xml +++ b/app/src/main/res/layout/source_filter_sheet_saved_searches.xml @@ -14,7 +14,7 @@ android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:visibility="visible" - android:text="@string/eh_saved_searches" /> + android:text="@string/saved_searches" /> diff --git a/app/src/main/res/values/strings_extra.xml b/app/src/main/res/values/strings_extra.xml deleted file mode 100644 index 0ec0767b3..000000000 --- a/app/src/main/res/values/strings_extra.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Reorder - Alpha. (descending) - Hide title - Show title - - - First checked - Drag & Drop - Enabled - Don\'t migrate - Search manually - Migrate now - Copy now - - - - Tracked - Lewd - - - All Sources - - - Skip pre-migration - Use last saved pre-migration preferences - and sources to mass migrate - - - Redundant - This extension is redundant and will not be used inside this version of Tachiyomi. - - - For %1$d titles - - - - Select sources - Select none - Source migration - Migration - Skip pre-migration - To show this screen again, go to Settings -> Library. - Select a source to migrate from - Search title + keywords of title - Data to include in migration - Search parameter (e.g. language:english) - Include extra search parameter when searching - Use source with the most chapters (slower) - Use first source with alternative - Skip this step next time - Search parameter (e.g. language:english) - To show this screen again, go to Settings -> Library. - Latest: %1$s - migrating to - Don\'t migrate - Search manually - Migrate now - Copy now - - - Migrate %1$s - Copy - Match pinned sources - Match enabled sources - No chapters found, this manga cannot be used for - migration - No Alternatives Found - Stop migrating? - - Migrate %1$d%2$s manga? - Migrate %1$d%2$s manga? - - - Copy %1$d%2$s manga? - Copy %1$d%2$s manga? - - (skipping %1$d) - - %d manga migrated - %d manga migrated - - - - Login - E-Hentai - nhentai - hitomi.la - Batch Add - Fork Settings - Sync favorites - Find in another source - Autoscroll - Retry all - Boost page - Merge with current - Merge With Another - Autoscroll help - Automatically scroll to the next page in the specified interval. Interval is specified in seconds. - Retry all help - Re-add all failed pages to the download queue. - Boost page help - Normally the downloader can only download a specific amount of pages at the same time. This means you can be waiting for a page to download but the downloader will not start downloading the page until it has a free download slot. Pressing \'Boost page\' will force the downloader to begin downloading the current page, regardless of whether or not there is an available slot. - Are you sure? - Resetting the sync state can cause your next sync to be extremely slow. - Collecting statistics… - Saved Searches - Example:\n\nhttp://e-hentai.org/g/12345/1a2b3c4e\nhttp://g.e-hentai.org/g/67890/6f7g8h9i\nhttp://exhentai.org/g/13579/1a3b5c7e\nhttps://exhentai.org/g/24680/2f4g6h8i\n\nIt also supports E-H Visited exported data\n - Enter the galleries to add (separated by a new line): - Add Galleries - Adding galleries… - Finish - Auto Webtoon Mode Detection - Reading webtoon style - Loading gallery… - Watch - Unwatch - Latest tab position - Do you want the latest tab to be the first tab in browse? This will make it the default tab when opening browse, not recommended if your on data or a metered network - Too many watched sources, cannot add more then 5 - You don\'t have any watched sources, go to the sources tab and long press a source to watch it - Display language code next to name - No source categories available - Invalid category name - Add Tag - The cover will be updated when you exit manga info edit - Select cover image - Failed to update cover - - - See Recommendations - - - - Manhwa - Manhua - Comic - Webtoon - - - Reset Tags - Reset Cover - - \ No newline at end of file diff --git a/app/src/main/res/values/strings_sy.xml b/app/src/main/res/values/strings_sy.xml new file mode 100644 index 000000000..386a4420a --- /dev/null +++ b/app/src/main/res/values/strings_sy.xml @@ -0,0 +1,341 @@ + + + + + Reorder + Alpha. (descending) + + + First checked + Drag & Drop + Enabled + Don\'t migrate + Search manually + Migrate now + Copy now + + + Manhwa + Manhua + Comic + Webtoon + + + Captcha solve failure + Failed to auto-solve the captcha! + Please log in! + + + + All Sources + E-Hentai + nhentai + hitomi.la + Fork Settings + + + E-Hentai Website Account Settings + Enable ExHentai + Requires login + Use Hentai@Home Network + Do you wish to load images through the Hentai@Home Network, if available? Disabling this option will reduce the amount of pages you are able to view\nOptions:\n- Any client (Recommended)\n- Default port clients only (Can be slower. Enable if behind firewall/proxy that blocks outgoing non-standard ports.) + Any client (Recommended) + Default port clients only + Show Japanese titles in search results + Currently showing Japanese titles in search results. Clear the chapter cache after changing this (in the Advanced section) + Currently showing English/Romanized titles in search results. Clear the chapter cache after changing this (in the Advanced section) + Use original images + Currently using original images + Currently using resampled images + Watched Tags + Opens a webview to your E/ExHentai watched tags page + ExHentai Watched Tags + E-Hentai Watched Tags + Tag Filtering Threshold + Must be between -9999 and 0! + You can soft filter tags by adding them to the "My Tags" E/ExHentai page with a negative weight. If a gallery has tags that add up to weight below this value, it is filtered from view. This threshold can be set between -9999 and 0. Currently: %s + Tag Watching Threshold + Must be between 0 and 9999! + Recently uploaded galleries will be included on the watched screen if it has at least one watched tag with positive weight, and the sum of weights on its watched tags add up to this value or higher. This threshold can be set between 0 and 9999. Currently: %s + Language Filtering + If you wish to hide galleries in certain languages from the gallery list and searches, select them in the dialog that will popup.\nNote that matching galleries will never appear regardless of your search query.\nTldr checkmarked = exclude + Front Page Categories + What categories would you like to show by default on the front page and in searches? They can still be enabled by enabling their filters + Watched List Filter Default State + When browsing ExHentai/E-Hentai should the watched list filter be enabled by default + The quality of the downloaded images + Image quality + Auto + 2400x + 1600x + 1280x + 980x + 780x + Favorites sync + Disable favorites uploading + Favorites are only downloaded from ExHentai. Any changes to favorites in the app will not be uploaded. Prevents accidental loss of favorites on ExHentai. Note that removals will still be downloaded (if you remove a favorites on ExHentai, it will be removed in the app as well). + Show favorites sync notes + Show some information regarding the favorites sync feature + Ignore sync errors when possible + Do not abort immediately when encountering errors during the sync process. Errors will still be displayed when the sync is complete. Can cause loss of favorites in some cases. Useful when syncing large libraries. + Force sync state reset + Performs a full resynchronization on the next sync. Removals will not be synced. All favorites in the app will be re-uploaded to ExHentai and all favorites on ExHentai will be re-downloaded into the app. Useful for repairing sync after sync has been interrupted. + Sync state reset + Gallery update checker + Auto update restrictions + Time between update batches + Never update galleries + 1 hour + 2 hours + 3 hours + 6 hours + 12 hours + 24 hours + 48 hours + %1$s will currently never check galleries in your library for updates. + %1$s checks/updates galleries in batches. This means it will wait %2$d hour(s), check %3$d galleries, wait %2$d hour(s), check %3$d and so on… + Show updater statistics + Collecting statistics… + Gallery updater statistics + The updater last ran %1$s, and checked %2$d out of the %3$d galleries that were ready for checking. + The updater has not ran yet. + \nGalleries that were checked in the last:\n- hour: %1$d\n- 6 hours: %2$d\n- 12 hours: %3$d\n- day: %4$d\n- 2 days: %5$d\n- week: %6$d\n- month: %7$d\n- year: %8$d + + + Settings profile note + The app will now add a new settings profile on E-Hentai and ExHentai to optimize app performance. Please ensure that you have less than three profiles on both sites.\n\nIf you have no idea what settings profiles are, then it probably doesn\'t matter, just hit \'OK\'. + Settings successfully uploaded! + Configuration failed! + An error occurred during the configuration process: %1$s + Uploading settings to server + Please wait, this may take some time… + You are out of profile slots on %1$s, please delete a profile! + + + Developer tools + Enable integrated hentai features + This is a experimental feature that will disable all hentai features if toggled off + Enable delegated sources + Apply %1$s enhancements to the following sources if they are installed: %2$s + Log level + Changing this can impact app performance. Force-restart app after changing. Current value: %s + Enable source blacklist + Hide extensions/sources that are incompatible with %1$s. Force-restart app after changing. + Open debug menu + IT CAN CORRUPT YOUR LIBRARY!]]> + + + Minimal + Extra + Extreme + critical errors only + log everything + network inspection mode + + + Expand all search filters by default + Automatically solve captcha + Use HIGHLY EXPERIMENTAL automatic ReCAPTCHA solver. Will be grayed out if unsupported by your device. + + + Use last saved pre-migration preferences and sources to mass migrate + + + Use high-quality thumbnails + May slow down search results + + + Download threads + Higher values can speed up image downloading significantly, but can also trigger bans. Recommended value is 2 or 3. Current value is: %s + Aggressively load pages + Slowly download the entire gallery while reading instead of just loading the pages you are viewing. + Skip queue on retry + Normally, pressing the retry button on a failed download will wait until the downloader has finished downloading the last page before beginning to re-download the failed page. Enabling this will force the downloader to begin re-downloading the failed page as soon as you press the retry button. + Reader Preload amount + 1 Page + 2 Pages + 3 Pages + 4 Pages + 6 Pages + 8 Pages + 10 Pages + 12 Pages + 14 Pages + 16 Pages + The amount of pages to preload when reading. Higher values will result in a smoother reading experience, at the cost of higher cache usage, it is recommended to increase the amount of cache you allocate when using larger values + Reader cache size + The amount of images to save on device while reading. Higher values will result in a smoother reading experience, at the cost of higher disk space usage + Preserve reading position on read manga + Auto Webtoon Mode + Use auto webtoon mode for manga that are detected to likely use the long strip format + + + + Autoscroll + Retry all + Boost page + Autoscroll help + Automatically scroll to the next page in the specified interval. Interval is specified in seconds. + Retry all help + Re-add all failed pages to the download queue. + Boost page help + Normally the downloader can only download a specific amount of pages at the same time. This means you can be waiting for a page to download but the downloader will not start downloading the page until it has a free download slot. Pressing \'Boost page\' will force the downloader to begin downloading the current page, regardless of whether or not there is an available slot. + + + Reading webtoon style + + + + See Recommendations + Merge With Another + + + Reset Tags + Reset Cover + Add Tag + The cover will be updated when you exit manga info edit + + + + Saved Searches + Save current search query? + My search name + Failed to load saved searches! + An error occurred while loading your saved searches. + Failed to delete saved search! + An error occurred while deleting the search. + Delete saved search query? + Are you sure you wish to delete your saved search query: \'%1$s\'? + + + No source categories available + Invalid category name + + + Watch + Unwatch + Too many watched sources, cannot add more then 5 + You don\'t have any watched sources, go to the sources tab and long press a source to watch it + Display language code next to name + Latest tab position + Do you want the latest tab to be the first tab in browse? This will make it the default tab when opening browse, not recommended if your on data or a metered network + + + Redundant + This extension is redundant and will not be used inside this version of Tachiyomi. + + + Select sources + Select none + Source migration + Migration + Skip pre-migration + To show this screen again, go to Settings -> Library. + Select a source to migrate from + Search title + keywords of title + Data to include in migration + Include extra search parameter when searching + Use source with the most chapters (slower) + Use first source with alternative + Skip this step next time + Search parameter (e.g. language:english) + Latest: %1$s + migrating to + Match pinned sources + Match enabled sources + No chapters found, this manga cannot be used for + migration + No Alternatives Found + Stop migrating? + + Migrate %1$d%2$s manga? + Migrate %1$d%2$s manga? + + + Copy %1$d%2$s manga? + Copy %1$d%2$s manga? + + (skipping %1$d) + + %d manga migrated + %d manga migrated + + + + + Tracked + Lewd + + + Sync favorites + Favorites sync error + Show Gallery + %1$s Sync will not start until the gallery is in only one category. + Favorites syncing + An error occurred during the sync process: %1$s + Favorites sync complete with errors + Errors occurred during the sync process that were ignored:\n%1$s + Verifying local library + Manga %1$d is in multiple categories! + Downloading favorites from remote server + Failed to fetch favorites from remote server! + Could not fetch favorites! + Calculating remote changes + Calculating local changes + Updating category names + Cleaning up + Sync complete! + Ignoring exception! + Sync error! + Unknown error: %1$s + Sync network error! + Removing %1$d galleries from remote server + Unable to delete galleries from the remote servers! + Adding gallery %1$d of %2$d to remote server + Removing gallery %1$d of %2$d from local library + Adding gallery %1$d of %2$d to local library + Remote gallery does not exist, skipping: %1$s! + Failed to add gallery to local database: + \'%1$s\' %2$s + \'%1$s\' (%2$s) is not a valid gallery! + Waiting for sync to start + The gallery: %1$s is in more than one category (%2$s)! + Initializing sync + %1$s\n\nSync is currently throttling (to avoid being banned from ExHentai) and may take a long time to complete. + IMPORTANT FAVORITES SYNC NOTES + NOT synced! Please change the category names on ExHentai instead. The category names will be copied from the ExHentai servers every sync.

2. The favorite categories on ExHentai correspond to the first 10 categories in the app (excluding the \'Default\' category). Galleries in other categories will NOT be synced!

3. ENSURE YOU HAVE A STABLE INTERNET CONNECTION WHEN SYNC IS IN PROGRESS! If the internet disconnects while the app is syncing, your favorites may be left in a partially-synced state.

4. Keep the app open while favorites are syncing. Android will close apps that are in the background sometimes and that could be bad if it happens while the app is syncing.

5. Do NOT put favorites in multiple categories (the app supports this). This can confuse the sync algorithm as ExHentai only allows each favorite to be in one category.

This dialog will only popup once. You can read these notes again by going to \'Settings > E-Hentai > Show favorites sync notes\'.]]>
+ Are you sure? + Resetting the sync state can cause your next sync to be extremely slow. + + + + Batch Add + Example:\n\nhttp://e-hentai.org/g/12345/1a2b3c4e\nhttp://g.e-hentai.org/g/67890/6f7g8h9i\nhttp://exhentai.org/g/13579/1a3b5c7e\nhttps://exhentai.org/g/24680/2f4g6h8i\n\nIt also supports E-H Visited exported data\n + Enter the galleries to add (separated by a new line): + Add Galleries + Adding galleries… + Finish + No galleries to add! + You must specify at least one gallery to add! + Batch add + [OK] + [ERROR] + \nSummary:\nAdded: %1$d gallerie(s)\nFailed: %2$d gallerie(s) + Added gallery: %1$s + Unknown gallery type for gallery: %1$s + Gallery does not exist: %1$s + Importing gallery (url: %1$s, fav: %2$s, forceSource: %3$s)… + Source URI match check error! + Source URI map-to-manga error! + Source URI clean error! + Failed to update chapters for gallery: %1$s! + Could not add gallery (url: %1$s)! + + + Launching app… + Error: %1$s + Could not open this gallery:\n\n%1$s + Manga ID is null! + Loading gallery… + +
\ No newline at end of file