Add a new lib-i18n to make message translation easier (#17336)
				
					
				
			* Add support to better internationalization with a new lib. * Add info about `lib-i18n` in the contributing guide. * Use lib-i18n in M+ as well. * Change properties files to UTF-8. * use the assets/ folder instead of the res/ folder files under assets/ are addressible by name * mangadex: add string invalid_manga_id * M+: Add translations for Vietnamese * m+: Add Vietnamese to list of available languages * mangadex: remove duplicate declarations --------- Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									7cec9eab66
								
							
						
					
					
						commit
						c367e3b126
					
				@ -257,6 +257,16 @@ dependencies {
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### i18n library
 | 
			
		||||
 | 
			
		||||
[`lib-i18n`](https://github.com/tachiyomiorg/tachiyomi-extensions/tree/master/lib/i18n) is a library for handling internationalization in the sources. It allows loading `.properties` files with messages located under the `assets/i18n` folder of each extension, that can be used to translate strings under the source.
 | 
			
		||||
 | 
			
		||||
```gradle
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation(project(':lib-i18n'))
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Additional dependencies
 | 
			
		||||
 | 
			
		||||
If you find yourself needing additional functionality, you can add more dependencies to your `build.gradle` file.
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ android {
 | 
			
		||||
            manifest.srcFile "AndroidManifest.xml"
 | 
			
		||||
            java.srcDirs = ['src']
 | 
			
		||||
            res.srcDirs = ['res']
 | 
			
		||||
            assets.srcDirs = ['assets']
 | 
			
		||||
        }
 | 
			
		||||
        release {
 | 
			
		||||
            manifest.srcFile "AndroidManifest.xml"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								lib/i18n/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/i18n/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    id("com.android.library")
 | 
			
		||||
    kotlin("android")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdk = AndroidConfig.compileSdk
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        minSdk = AndroidConfig.minSdk
 | 
			
		||||
        targetSdk = AndroidConfig.targetSdk
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    compileOnly(libs.kotlin.stdlib)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								lib/i18n/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								lib/i18n/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="eu.kanade.tachiyomi.lib.i18n" />
 | 
			
		||||
							
								
								
									
										83
									
								
								lib/i18n/src/main/java/eu/kanade/tachiyomi/lib/i18n/Intl.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								lib/i18n/src/main/java/eu/kanade/tachiyomi/lib/i18n/Intl.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
package eu.kanade.tachiyomi.lib.i18n
 | 
			
		||||
 | 
			
		||||
import java.io.InputStreamReader
 | 
			
		||||
import java.text.Collator
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
import java.util.PropertyResourceBundle
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A simple wrapper to make internationalization easier to use in sources.
 | 
			
		||||
 *
 | 
			
		||||
 * Message files should be put in the `assets/i18n` folder, with the name
 | 
			
		||||
 * `messages_{iso_639_1}.properties`, where `iso_639_1` should be using
 | 
			
		||||
 * snake case and be in lowercase.
 | 
			
		||||
 *
 | 
			
		||||
 * To edit the strings, use the official JetBrain's
 | 
			
		||||
 * [Resource Bundle Editor plugin](https://plugins.jetbrains.com/plugin/17035-resource-bundle-editor).
 | 
			
		||||
 *
 | 
			
		||||
 * Make sure to configure Android Studio to save Properties files as UTF-8 as well.
 | 
			
		||||
 * You can refer to this [documentation](https://www.jetbrains.com/help/idea/properties-files.html#1cbc434e)
 | 
			
		||||
 * on how to do so.
 | 
			
		||||
 */
 | 
			
		||||
class Intl(
 | 
			
		||||
    private val language: String,
 | 
			
		||||
    private val baseLanguage: String,
 | 
			
		||||
    private val availableLanguages: Set<String>,
 | 
			
		||||
    private val classLoader: ClassLoader,
 | 
			
		||||
    private val createMessageFileName: (String) -> String = { createDefaultMessageFileName(it) }
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    val chosenLanguage: String = when (language) {
 | 
			
		||||
        in availableLanguages -> language
 | 
			
		||||
        else -> baseLanguage
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val locale: Locale = Locale.forLanguageTag(chosenLanguage)
 | 
			
		||||
 | 
			
		||||
    val collator: Collator = Collator.getInstance(locale)
 | 
			
		||||
 | 
			
		||||
    private val baseBundle: PropertyResourceBundle by lazy { createBundle(baseLanguage) }
 | 
			
		||||
 | 
			
		||||
    private val bundle: PropertyResourceBundle by lazy {
 | 
			
		||||
        if (chosenLanguage == baseLanguage) baseBundle else createBundle(chosenLanguage)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the string from the message file. If the [key] is not present
 | 
			
		||||
     * in the current language, the English value will be returned. If the [key]
 | 
			
		||||
     * is also not present in English, the [key] surrounded by brackets will be returned.
 | 
			
		||||
     */
 | 
			
		||||
    operator fun get(key: String): String = when {
 | 
			
		||||
        bundle.containsKey(key) -> bundle.getString(key)
 | 
			
		||||
        baseBundle.containsKey(key) -> baseBundle.getString(key)
 | 
			
		||||
        else -> "[$key]"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun languageDisplayName(localeCode: String): String =
 | 
			
		||||
        Locale.forLanguageTag(localeCode)
 | 
			
		||||
            .getDisplayName(locale)
 | 
			
		||||
            .replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a [PropertyResourceBundle] instance from the language specified.
 | 
			
		||||
     * The expected message file will be loaded from the `res/raw`.
 | 
			
		||||
     *
 | 
			
		||||
     * The [PropertyResourceBundle] is used directly instead of [java.util.ResourceBundle]
 | 
			
		||||
     * because the later has issues with UTF-8 files in Java 8, which would need
 | 
			
		||||
     * the message files to be saved in ISO-8859-1, making the file readability bad.
 | 
			
		||||
     */
 | 
			
		||||
    private fun createBundle(lang: String): PropertyResourceBundle {
 | 
			
		||||
        val fileName = createMessageFileName(lang)
 | 
			
		||||
        val fileContent = classLoader.getResourceAsStream(fileName)
 | 
			
		||||
 | 
			
		||||
        return PropertyResourceBundle(InputStreamReader(fileContent, "UTF-8"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun createDefaultMessageFileName(lang: String): String {
 | 
			
		||||
            val langSnakeCase = lang.replace("-", "_").lowercase()
 | 
			
		||||
 | 
			
		||||
            return "assets/i18n/messages_$langSnakeCase.properties"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										146
									
								
								src/all/mangadex/assets/i18n/messages_en.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/all/mangadex/assets/i18n/messages_en.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,146 @@
 | 
			
		||||
alternative_titles=Alternative titles:
 | 
			
		||||
alternative_titles_in_description=Alternative titles in description
 | 
			
		||||
alternative_titles_in_description_summary=Include a manga's alternative titles at the end of its description
 | 
			
		||||
block_group_by_uuid=Block groups by UUID
 | 
			
		||||
block_group_by_uuid_summary=Chapters from blocked groups will not show up in Latest or Manga feed. Enter as a Comma-separated list of group UUIDs
 | 
			
		||||
block_uploader_by_uuid=Block uploader by UUID
 | 
			
		||||
block_uploader_by_uuid_summary=Chapters from blocked uploaders will not show up in Latest or Manga feed. Enter as a Comma-separated list of uploader UUIDs
 | 
			
		||||
content=Content
 | 
			
		||||
content_gore=Gore
 | 
			
		||||
content_rating=Content rating
 | 
			
		||||
content_rating_erotica=Erotica
 | 
			
		||||
content_rating_genre=Content rating: %s
 | 
			
		||||
content_rating_pornographic=Pornographic
 | 
			
		||||
content_rating_safe=Safe
 | 
			
		||||
content_rating_suggestive=Suggestive
 | 
			
		||||
content_sexual_violence=Sexual violence
 | 
			
		||||
cover_quality=Cover quality
 | 
			
		||||
cover_quality_low=Low
 | 
			
		||||
cover_quality_medium=Medium
 | 
			
		||||
cover_quality_original=Original
 | 
			
		||||
data_saver=Data saver
 | 
			
		||||
data_saver_summary=Enables smaller, more compressed images
 | 
			
		||||
excluded_tags_mode=Excluded tags mode
 | 
			
		||||
filter_original_languages=Filter original languages
 | 
			
		||||
filter_original_languages_summary=Only show content that was originally published in the selected languages in both latest and browse
 | 
			
		||||
format=Format
 | 
			
		||||
format_adaptation=Adaptation
 | 
			
		||||
format_anthology=Anthology
 | 
			
		||||
format_award_winning=Award winning
 | 
			
		||||
format_doujinshi=Doujinshi
 | 
			
		||||
format_fan_colored=Fan colored
 | 
			
		||||
format_full_color=Full color
 | 
			
		||||
format_long_strip=Long strip
 | 
			
		||||
format_official_colored=Official colored
 | 
			
		||||
format_oneshot=Oneshot
 | 
			
		||||
format_user_created=User created
 | 
			
		||||
format_web_comic=Web comic
 | 
			
		||||
format_yonkoma=4-Koma
 | 
			
		||||
genre=Genre
 | 
			
		||||
genre_action=Action
 | 
			
		||||
genre_adventure=Adventure
 | 
			
		||||
genre_boys_love=Boy's Love
 | 
			
		||||
genre_comedy=Comedy
 | 
			
		||||
genre_crime=Crime
 | 
			
		||||
genre_drama=Drama
 | 
			
		||||
genre_fantasy=Fantasy
 | 
			
		||||
genre_girls_love=Girl's Love
 | 
			
		||||
genre_historical=Historical
 | 
			
		||||
genre_horror=Horror
 | 
			
		||||
genre_isekai=Isekai
 | 
			
		||||
genre_magical_girls=Magical girls
 | 
			
		||||
genre_mecha=Mecha
 | 
			
		||||
genre_medical=Medical
 | 
			
		||||
genre_mystery=Mystery
 | 
			
		||||
genre_philosophical=Philosophical
 | 
			
		||||
genre_romance=Romance
 | 
			
		||||
genre_sci_fi=Sci-Fi
 | 
			
		||||
genre_slice_of_life=Slice of life
 | 
			
		||||
genre_sports=Sports
 | 
			
		||||
genre_superhero=Superhero
 | 
			
		||||
genre_thriller=Thriller
 | 
			
		||||
genre_tragedy=Tragedy
 | 
			
		||||
genre_wuxia=Wuxia
 | 
			
		||||
has_available_chapters=Has available chapters
 | 
			
		||||
included_tags_mode=Included tags mode
 | 
			
		||||
invalid_author_id=Not a valid author ID
 | 
			
		||||
invalid_manga_id=Not a valid manga ID
 | 
			
		||||
invalid_group_id=Not a valid group ID
 | 
			
		||||
invalid_uuids=The text contains invalid UUIDs
 | 
			
		||||
migrate_warning=Migrate this entry from MangaDex to MangaDex to update it
 | 
			
		||||
mode_and=And
 | 
			
		||||
mode_or=Or
 | 
			
		||||
no_group=No Group
 | 
			
		||||
no_series_in_list=No series in the list
 | 
			
		||||
original_language=Original language
 | 
			
		||||
original_language_filter_chinese=%s (Manhua)
 | 
			
		||||
original_language_filter_japanese=%s (Manga)
 | 
			
		||||
original_language_filter_korean=%s (Manhwa)
 | 
			
		||||
publication_demographic=Publication demographic
 | 
			
		||||
publication_demographic_josei=Josei
 | 
			
		||||
publication_demographic_none=None
 | 
			
		||||
publication_demographic_seinen=Seinen
 | 
			
		||||
publication_demographic_shoujo=Shoujo
 | 
			
		||||
publication_demographic_shounen=Shounen
 | 
			
		||||
sort=Sort
 | 
			
		||||
sort_alphabetic=Alphabetic
 | 
			
		||||
sort_chapter_uploaded_at=Chapter uploaded at
 | 
			
		||||
sort_content_created_at=Content created at
 | 
			
		||||
sort_content_info_updated_at=Content info updated at
 | 
			
		||||
sort_number_of_follows=Number of follows
 | 
			
		||||
sort_rating=Rating
 | 
			
		||||
sort_relevance=Relevance
 | 
			
		||||
sort_year=Year
 | 
			
		||||
standard_content_rating=Default content rating
 | 
			
		||||
standard_content_rating_summary=Show content with the selected ratings by default
 | 
			
		||||
standard_https_port=Use HTTPS port 443 only
 | 
			
		||||
standard_https_port_summary=Enable to only request image servers that use port 443. This allows users with stricter firewall restrictions to access MangaDex images
 | 
			
		||||
status=Status
 | 
			
		||||
status_cancelled=Cancelled
 | 
			
		||||
status_completed=Completed
 | 
			
		||||
status_hiatus=Hiatus
 | 
			
		||||
status_ongoing=Ongoing
 | 
			
		||||
tags_mode=Tags mode
 | 
			
		||||
theme=Theme
 | 
			
		||||
theme_aliens=Aliens
 | 
			
		||||
theme_animals=Animals
 | 
			
		||||
theme_cooking=Cooking
 | 
			
		||||
theme_crossdressing=Crossdressing
 | 
			
		||||
theme_delinquents=Delinquents
 | 
			
		||||
theme_demons=Demons
 | 
			
		||||
theme_gender_swap=Genderswap
 | 
			
		||||
theme_ghosts=Ghosts
 | 
			
		||||
theme_gyaru=Gyaru
 | 
			
		||||
theme_harem=Harem
 | 
			
		||||
theme_incest=Incest
 | 
			
		||||
theme_loli=Loli
 | 
			
		||||
theme_mafia=Mafia
 | 
			
		||||
theme_magic=Magic
 | 
			
		||||
theme_martial_arts=Martial arts
 | 
			
		||||
theme_military=Military
 | 
			
		||||
theme_monster_girls=Monster girls
 | 
			
		||||
theme_monsters=Monsters
 | 
			
		||||
theme_music=Music
 | 
			
		||||
theme_ninja=Ninja
 | 
			
		||||
theme_office_workers=Office workers
 | 
			
		||||
theme_police=Police
 | 
			
		||||
theme_post_apocalyptic=Post-apocalyptic
 | 
			
		||||
theme_psychological=Psychological
 | 
			
		||||
theme_reincarnation=Reincarnation
 | 
			
		||||
theme_reverse_harem=Reverse harem
 | 
			
		||||
theme_samurai=Samurai
 | 
			
		||||
theme_school_life=School life
 | 
			
		||||
theme_shota=Shota
 | 
			
		||||
theme_supernatural=Supernatural
 | 
			
		||||
theme_survival=Survival
 | 
			
		||||
theme_time_travel=Time travel
 | 
			
		||||
theme_traditional_games=Traditional games
 | 
			
		||||
theme_vampires=Vampires
 | 
			
		||||
theme_video_games=Video games
 | 
			
		||||
theme_villainess=Vilania
 | 
			
		||||
theme_virtual_reality=Virtual reality
 | 
			
		||||
theme_zombies=Zombies
 | 
			
		||||
try_using_first_volume_cover=Attempt to use the first volume cover as cover
 | 
			
		||||
try_using_first_volume_cover_summary=May need to manually refresh entries already in library. Otherwise, clear database to have new covers to show up.
 | 
			
		||||
unable_to_process_chapter_request=Unable to process Chapter request. HTTP code: %d
 | 
			
		||||
uploaded_by=Uploaded by %s
 | 
			
		||||
							
								
								
									
										108
									
								
								src/all/mangadex/assets/i18n/messages_es.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/all/mangadex/assets/i18n/messages_es.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
block_group_by_uuid=Bloquear grupos por UUID
 | 
			
		||||
block_group_by_uuid_summary=Los capítulos de los grupos bloqueados no aparecerán en Recientes o en el Feed de mangas. Introduce una coma para separar la lista de UUIDs
 | 
			
		||||
block_uploader_by_uuid=Bloquear uploader por UUID
 | 
			
		||||
block_uploader_by_uuid_summary=Los capítulos de los uploaders bloqueados no aparecerán en Recientes o en el Feed de mangas. Introduce una coma para separar la lista de UUIDs
 | 
			
		||||
content=Contenido
 | 
			
		||||
content_rating=Clasificación de contenido
 | 
			
		||||
content_rating_erotica=Erótico
 | 
			
		||||
content_rating_genre=Clasificación: %s
 | 
			
		||||
content_rating_pornographic=Pornográfico
 | 
			
		||||
content_rating_safe=Seguro
 | 
			
		||||
content_rating_suggestive=Sugestivo
 | 
			
		||||
content_sexual_violence=Violencia sexual
 | 
			
		||||
cover_quality=Calidad de la portada
 | 
			
		||||
cover_quality_low=Bajo
 | 
			
		||||
cover_quality_medium=Medio
 | 
			
		||||
data_saver=Ahorro de datos
 | 
			
		||||
data_saver_summary=Utiliza imágenes más pequeñas y más comprimidas
 | 
			
		||||
excluded_tags_mode=Modo de etiquetas excluidas
 | 
			
		||||
filter_original_languages=Filtrar por lenguajes
 | 
			
		||||
filter_original_languages_summary=Muestra solo el contenido publicado en los idiomas seleccionados en recientes y en la búsqueda
 | 
			
		||||
format=Formato
 | 
			
		||||
format_adaptation=Adaptación
 | 
			
		||||
format_anthology=Antología
 | 
			
		||||
format_award_winning=Ganador de premio
 | 
			
		||||
format_fan_colored=Coloreado por fans
 | 
			
		||||
format_full_color=Todo a color
 | 
			
		||||
format_long_strip=Tira larga
 | 
			
		||||
format_official_colored=Coloreo oficial
 | 
			
		||||
format_user_created=Creado por usuario
 | 
			
		||||
genre=Genero
 | 
			
		||||
genre_action=Acción
 | 
			
		||||
genre_adventure=Aventura
 | 
			
		||||
genre_comedy=Comedia
 | 
			
		||||
genre_crime=Crimen
 | 
			
		||||
genre_fantasy=Fantasia
 | 
			
		||||
genre_historical=Histórico
 | 
			
		||||
genre_magical_girls=Chicas mágicas
 | 
			
		||||
genre_medical=Medico
 | 
			
		||||
genre_mystery=Misterio
 | 
			
		||||
genre_philosophical=Filosófico
 | 
			
		||||
genre_sci_fi=Ciencia ficción
 | 
			
		||||
genre_slice_of_life=Recuentos de la vida
 | 
			
		||||
genre_sports=Deportes
 | 
			
		||||
genre_superhero=Superhéroes
 | 
			
		||||
genre_tragedy=Tragedia
 | 
			
		||||
has_available_chapters=Tiene capítulos disponibles
 | 
			
		||||
included_tags_mode=Modo de etiquetas incluidas
 | 
			
		||||
invalid_author_id=ID de autor inválida
 | 
			
		||||
invalid_group_id=ID de grupo inválida
 | 
			
		||||
migrate_warning=Migre la entrada MangaDex a MangaDex para actualizarla
 | 
			
		||||
mode_and=Y
 | 
			
		||||
mode_or=O
 | 
			
		||||
no_group=Sin grupo
 | 
			
		||||
no_series_in_list=No hay series en la lista
 | 
			
		||||
original_language=Lenguaje original
 | 
			
		||||
publication_demographic=Demografía
 | 
			
		||||
publication_demographic_none=Ninguna
 | 
			
		||||
sort=Ordenar
 | 
			
		||||
sort_alphabetic=Alfabeticamente
 | 
			
		||||
sort_chapter_uploaded_at=Capítulo subido en
 | 
			
		||||
sort_content_created_at=Contenido creado en
 | 
			
		||||
sort_content_info_updated_at=Información del contenido actualizada en
 | 
			
		||||
sort_number_of_follows=Número de seguidores
 | 
			
		||||
sort_rating=Calificación
 | 
			
		||||
sort_relevance=Relevancia
 | 
			
		||||
sort_year=Año
 | 
			
		||||
standard_content_rating=Clasificación de contenido por defecto
 | 
			
		||||
standard_content_rating_summary=Muestra el contenido con la clasificación de contenido seleccionada por defecto
 | 
			
		||||
standard_https_port=Utilizar el puerto 443 de HTTPS
 | 
			
		||||
standard_https_port_summary=Habilite esta opción solicitar las imágenes a los servidores que usan el puerto 443. Esto permite a los usuarios con restricciones estrictas de firewall acceder a las imagenes en MangaDex
 | 
			
		||||
status=Estado
 | 
			
		||||
status_cancelled=Cancelado
 | 
			
		||||
status_completed=Completado
 | 
			
		||||
status_hiatus=Pausado
 | 
			
		||||
status_ongoing=Publicandose
 | 
			
		||||
tags_mode=Modo de etiquetas
 | 
			
		||||
theme=Tema
 | 
			
		||||
theme_aliens=Alienígenas
 | 
			
		||||
theme_animals=Animales
 | 
			
		||||
theme_cooking=Cocina
 | 
			
		||||
theme_crossdressing=Travestismo
 | 
			
		||||
theme_delinquents=Delincuentes
 | 
			
		||||
theme_demons=Demonios
 | 
			
		||||
theme_gender_swap=Cambio de sexo
 | 
			
		||||
theme_ghosts=Fantasmas
 | 
			
		||||
theme_incest=Incesto
 | 
			
		||||
theme_magic=Magia
 | 
			
		||||
theme_martial_arts=Artes marciales
 | 
			
		||||
theme_military=Militar
 | 
			
		||||
theme_monster_girls=Chicas monstruo
 | 
			
		||||
theme_monsters=Monstruos
 | 
			
		||||
theme_music=Musica
 | 
			
		||||
theme_office_workers=Oficinistas
 | 
			
		||||
theme_police=Policial
 | 
			
		||||
theme_post_apocalyptic=Post-apocalíptico
 | 
			
		||||
theme_psychological=Psicológico
 | 
			
		||||
theme_reincarnation=Reencarnación
 | 
			
		||||
theme_reverse_harem=Harem inverso
 | 
			
		||||
theme_school_life=Vida escolar
 | 
			
		||||
theme_supernatural=Sobrenatural
 | 
			
		||||
theme_survival=Supervivencia
 | 
			
		||||
theme_time_travel=Viaje en el tiempo
 | 
			
		||||
theme_traditional_games=Juegos tradicionales
 | 
			
		||||
theme_vampires=Vampiros
 | 
			
		||||
theme_villainess=Villana
 | 
			
		||||
theme_virtual_reality=Realidad virtual
 | 
			
		||||
unable_to_process_chapter_request=No se ha podido procesar la solicitud del capítulo. Código HTTP: %d
 | 
			
		||||
uploaded_by=Subido por %s
 | 
			
		||||
							
								
								
									
										119
									
								
								src/all/mangadex/assets/i18n/messages_pt_br.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/all/mangadex/assets/i18n/messages_pt_br.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,119 @@
 | 
			
		||||
alternative_titles=Títulos alternativos:
 | 
			
		||||
alternative_titles_in_description=Títulos alternativos na descrição
 | 
			
		||||
alternative_titles_in_description_summary=Inclui os títulos alternativos das séries no final de cada descrição
 | 
			
		||||
block_group_by_uuid=Bloquear grupos por UUID
 | 
			
		||||
block_group_by_uuid_summary=Capítulos de grupos bloqueados não irão aparecer no feed de Recentes ou Mangás. Digite uma lista de UUIDs dos grupos separados por vírgulas
 | 
			
		||||
block_uploader_by_uuid=Bloquear uploaders por UUID
 | 
			
		||||
block_uploader_by_uuid_summary=Capítulos de usuários bloqueados não irão aparecer no feed de Recentes ou Mangás. Digite uma lista de UUIDs dos usuários separados por vírgulas
 | 
			
		||||
content=Conteúdo
 | 
			
		||||
content_rating=Classificação de conteúdo
 | 
			
		||||
content_rating_erotica=Erótico
 | 
			
		||||
content_rating_genre=Classificação: %s
 | 
			
		||||
content_rating_pornographic=Pornográfico
 | 
			
		||||
content_rating_safe=Seguro
 | 
			
		||||
content_rating_suggestive=Sugestivo
 | 
			
		||||
content_sexual_violence=Violência sexual
 | 
			
		||||
cover_quality=Qualidade da capa
 | 
			
		||||
cover_quality_low=Baixa
 | 
			
		||||
cover_quality_medium=Média
 | 
			
		||||
data_saver=Economia de dados
 | 
			
		||||
data_saver_summary=Utiliza imagens menores e mais compactadas
 | 
			
		||||
excluded_tags_mode=Modo de exclusão de tags
 | 
			
		||||
filter_original_languages=Filtrar os idiomas originais
 | 
			
		||||
filter_original_languages_summary=Mostra somente conteúdos que foram publicados originalmente nos idiomas selecionados nas seções de recentes e navegar
 | 
			
		||||
format=Formato
 | 
			
		||||
format_adaptation=Adaptação
 | 
			
		||||
format_anthology=Antologia
 | 
			
		||||
format_award_winning=Premiado
 | 
			
		||||
format_fan_colored=Colorizado por fãs
 | 
			
		||||
format_full_color=Colorido
 | 
			
		||||
format_long_strip=Vertical
 | 
			
		||||
format_official_colored=Colorizado oficialmente
 | 
			
		||||
format_user_created=Criado por usuários
 | 
			
		||||
genre=Gênero
 | 
			
		||||
genre_action=Ação
 | 
			
		||||
genre_adventure=Aventura
 | 
			
		||||
genre_comedy=Comédia
 | 
			
		||||
genre_crime=Crime
 | 
			
		||||
genre_fantasy=Fantasia
 | 
			
		||||
genre_historical=Histórico
 | 
			
		||||
genre_magical_girls=Garotas mágicas
 | 
			
		||||
genre_medical=Médico
 | 
			
		||||
genre_mystery=Mistério
 | 
			
		||||
genre_philosophical=Filosófico
 | 
			
		||||
genre_sci_fi=Ficção científica
 | 
			
		||||
genre_slice_of_life=Cotidiano
 | 
			
		||||
genre_sports=Esportes
 | 
			
		||||
genre_superhero=Super-heroi
 | 
			
		||||
genre_tragedy=Tragédia
 | 
			
		||||
has_available_chapters=Há capítulos disponíveis
 | 
			
		||||
included_tags_mode=Modo de inclusão de tags
 | 
			
		||||
invalid_author_id=ID do autor inválido
 | 
			
		||||
invalid_manga_id=ID do mangá inválido
 | 
			
		||||
invalid_group_id=ID do grupo inválido
 | 
			
		||||
invalid_uuids=O texto contém UUIDs inválidos
 | 
			
		||||
migrate_warning=Migre esta entrada do MangaDex para o MangaDex para atualizar
 | 
			
		||||
mode_and=E
 | 
			
		||||
mode_or=Ou
 | 
			
		||||
no_group=Sem grupo
 | 
			
		||||
no_series_in_list=Sem séries na lista
 | 
			
		||||
original_language=Idioma original
 | 
			
		||||
original_language_filter_japanese=%s (Mangá)
 | 
			
		||||
publication_demographic=Demografia da publicação
 | 
			
		||||
publication_demographic_none=Nenhuma
 | 
			
		||||
sort=Ordenar
 | 
			
		||||
sort_alphabetic=Alfabeticamente
 | 
			
		||||
sort_chapter_uploaded_at=Upload do capítulo
 | 
			
		||||
sort_content_created_at=Criação do conteúdo
 | 
			
		||||
sort_content_info_updated_at=Atualização das informações
 | 
			
		||||
sort_number_of_follows=Número de seguidores
 | 
			
		||||
sort_rating=Nota
 | 
			
		||||
sort_relevance=Relevância
 | 
			
		||||
sort_year=Ano de lançamento
 | 
			
		||||
standard_content_rating=Classificação de conteúdo padrão
 | 
			
		||||
standard_content_rating_summary=Mostra os conteúdos com as classificações selecionadas por padrão
 | 
			
		||||
standard_https_port=Utilizar somente a porta 443 do HTTPS
 | 
			
		||||
standard_https_port_summary=Ative para fazer requisições em somente servidores de imagem que usem a porta 443. Isso permite com que usuários com regras mais restritas de firewall possam acessar as imagens do MangaDex.
 | 
			
		||||
status=Estado
 | 
			
		||||
status_cancelled=Cancelado
 | 
			
		||||
status_completed=Completo
 | 
			
		||||
status_hiatus=Hiato
 | 
			
		||||
status_ongoing=Em andamento
 | 
			
		||||
tags_mode=Modo das tags
 | 
			
		||||
theme=Tema
 | 
			
		||||
theme_aliens=Alienígenas
 | 
			
		||||
theme_animals=Animais
 | 
			
		||||
theme_cooking=Culinária
 | 
			
		||||
theme_delinquents=Delinquentes
 | 
			
		||||
theme_demons=Demônios
 | 
			
		||||
theme_gender_swap=Troca de gêneros
 | 
			
		||||
theme_ghosts=Fantasmas
 | 
			
		||||
theme_harem=Harém
 | 
			
		||||
theme_incest=Incesto
 | 
			
		||||
theme_mafia=Máfia
 | 
			
		||||
theme_magic=Magia
 | 
			
		||||
theme_martial_arts=Artes marciais
 | 
			
		||||
theme_military=Militar
 | 
			
		||||
theme_monster_girls=Garotas monstro
 | 
			
		||||
theme_monsters=Monstros
 | 
			
		||||
theme_music=Musical
 | 
			
		||||
theme_office_workers=Funcionários de escritório
 | 
			
		||||
theme_police=Policial
 | 
			
		||||
theme_post_apocalyptic=Pós-apocalíptico
 | 
			
		||||
theme_psychological=Psicológico
 | 
			
		||||
theme_reincarnation=Reencarnação
 | 
			
		||||
theme_reverse_harem=Harém reverso
 | 
			
		||||
theme_school_life=Vida escolar
 | 
			
		||||
theme_supernatural=Sobrenatural
 | 
			
		||||
theme_survival=Sobrevivência
 | 
			
		||||
theme_time_travel=Viagem no tempo
 | 
			
		||||
theme_traditional_games=Jogos tradicionais
 | 
			
		||||
theme_vampires=Vampiros
 | 
			
		||||
theme_video_games=Videojuegos
 | 
			
		||||
theme_villainess=Villainess
 | 
			
		||||
theme_virtual_reality=Realidade virtual
 | 
			
		||||
theme_zombies=Zumbis
 | 
			
		||||
try_using_first_volume_cover=Tentar usar a capa do primeiro volume como capa
 | 
			
		||||
try_using_first_volume_cover_summary=Pode ser necessário atualizar os itens já adicionados na biblioteca. Alternativamente, limpe o banco de dados para as novas capas aparecerem.
 | 
			
		||||
unable_to_process_chapter_request=Não foi possível processar a requisição do capítulo. Código HTTP: %d
 | 
			
		||||
uploaded_by=Enviado por %s
 | 
			
		||||
							
								
								
									
										138
									
								
								src/all/mangadex/assets/i18n/messages_ru.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/all/mangadex/assets/i18n/messages_ru.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,138 @@
 | 
			
		||||
block_group_by_uuid=Заблокировать группы по UUID
 | 
			
		||||
block_group_by_uuid_summary=Главы от заблокированных групп не будут отображаться в последних обновлениях и в списке глав тайтла. Введите через запятую список UUID групп. 
 | 
			
		||||
block_uploader_by_uuid=Заблокировать загрузчика по UUID
 | 
			
		||||
block_uploader_by_uuid_summary=Главы от заблокированных загрузчиков не будут отображаться в последних обновлениях  и в списке глав тайтла. Введите через запятую список UUID загрузчиков.
 | 
			
		||||
content=Неприемлемый контент
 | 
			
		||||
content_gore=Жестокость
 | 
			
		||||
content_rating=Рейтинг контента
 | 
			
		||||
content_rating_erotica=Эротический
 | 
			
		||||
content_rating_genre=Рейтинг контента: %s
 | 
			
		||||
content_rating_pornographic=Порнографический
 | 
			
		||||
content_rating_safe=Безопасный
 | 
			
		||||
content_rating_suggestive=Намекающий
 | 
			
		||||
content_sexual_violence=Сексуальное насилие
 | 
			
		||||
cover_quality=Качество обложки
 | 
			
		||||
cover_quality_low=Низкое
 | 
			
		||||
cover_quality_medium=Среднее
 | 
			
		||||
cover_quality_original=Оригинальное
 | 
			
		||||
data_saver=Экономия трафика
 | 
			
		||||
data_saver_summary=Использует меньшие по размеру, сжатые изображения
 | 
			
		||||
excluded_tags_mode=Исключая
 | 
			
		||||
filter_original_languages=Фильтр по языку оригинала
 | 
			
		||||
filter_original_languages_summary=Показывать тайтлы которые изначально были выпущены только в выбранных языках в последних обновлениях и при поиске
 | 
			
		||||
format=Формат
 | 
			
		||||
format_adaptation=Адаптация
 | 
			
		||||
format_anthology=Антология
 | 
			
		||||
format_award_winning=Отмеченный наградами
 | 
			
		||||
format_doujinshi=Додзинси
 | 
			
		||||
format_fan_colored=Раскрашенная фанатами
 | 
			
		||||
format_full_color=В цвете
 | 
			
		||||
format_long_strip=Веб
 | 
			
		||||
format_official_colored=Официально раскрашенная
 | 
			
		||||
format_oneshot=Сингл
 | 
			
		||||
format_user_created=Созданная пользователями
 | 
			
		||||
format_web_comic=Веб-комикс
 | 
			
		||||
format_yonkoma=Ёнкома
 | 
			
		||||
genre=Жанр
 | 
			
		||||
genre_action=Боевик
 | 
			
		||||
genre_adventure=Приключения
 | 
			
		||||
genre_boys_love=BL
 | 
			
		||||
genre_comedy=Комедия
 | 
			
		||||
genre_crime=Криминал
 | 
			
		||||
genre_drama=Драма
 | 
			
		||||
genre_fantasy=Фэнтези
 | 
			
		||||
genre_girls_love=GL
 | 
			
		||||
genre_historical=История
 | 
			
		||||
genre_horror=Ужасы
 | 
			
		||||
genre_isekai=Исекай
 | 
			
		||||
genre_magical_girls=Махо-сёдзё
 | 
			
		||||
genre_mecha=Меха
 | 
			
		||||
genre_medical=Медицина
 | 
			
		||||
genre_mystery=Мистика
 | 
			
		||||
genre_philosophical=Философия
 | 
			
		||||
genre_romance=Романтика
 | 
			
		||||
genre_sci_fi=Научная фантастика
 | 
			
		||||
genre_slice_of_life=Повседневность
 | 
			
		||||
genre_sports=Спорт
 | 
			
		||||
genre_superhero=Супергерои
 | 
			
		||||
genre_thriller=Триллер
 | 
			
		||||
genre_tragedy=Трагедия
 | 
			
		||||
genre_wuxia=Культивация
 | 
			
		||||
has_available_chapters=Есть главы
 | 
			
		||||
included_tags_mode=Включая
 | 
			
		||||
invalid_author_id=Недействительный ID автора
 | 
			
		||||
invalid_group_id=Недействительный ID группы
 | 
			
		||||
mode_and=И
 | 
			
		||||
mode_or=Или
 | 
			
		||||
no_group=Нет группы
 | 
			
		||||
no_series_in_list=Лист пуст
 | 
			
		||||
original_language=Язык оригинала
 | 
			
		||||
original_language_filter_chinese=%s (Манхуа)
 | 
			
		||||
original_language_filter_japanese=%s (Манга)
 | 
			
		||||
original_language_filter_korean=%s (Манхва)
 | 
			
		||||
publication_demographic=Целевая аудитория
 | 
			
		||||
publication_demographic_josei=Дзёсэй
 | 
			
		||||
publication_demographic_none=Нет
 | 
			
		||||
publication_demographic_seinen=Сэйнэн
 | 
			
		||||
publication_demographic_shoujo=Сёдзё
 | 
			
		||||
publication_demographic_shounen=Сёнэн
 | 
			
		||||
sort=Сортировать по
 | 
			
		||||
sort_alphabetic=Алфавиту
 | 
			
		||||
sort_chapter_uploaded_at=Загруженной главе
 | 
			
		||||
sort_content_created_at=По дате создания
 | 
			
		||||
sort_content_info_updated_at=По дате обновления
 | 
			
		||||
sort_number_of_follows=Количеству фолловеров
 | 
			
		||||
sort_rating=Популярности
 | 
			
		||||
sort_relevance=Лучшему соответствию
 | 
			
		||||
sort_year=Год
 | 
			
		||||
standard_content_rating=Рейтинг контента по умолчанию
 | 
			
		||||
standard_content_rating_summary=Показывать контент с выбранным рейтингом по умолчанию
 | 
			
		||||
standard_https_port=Использовать только HTTPS порт 443
 | 
			
		||||
standard_https_port_summary=Запрашивает изображения только с серверов которые используют порт 443. Это позволяет пользователям со строгими правилами брандмауэра загружать изображения с MangaDex.
 | 
			
		||||
status=Статус
 | 
			
		||||
status_cancelled=Отменён
 | 
			
		||||
status_completed=Завершён
 | 
			
		||||
status_hiatus=Приостановлен
 | 
			
		||||
status_ongoing=Онгоинг
 | 
			
		||||
tags_mode=Режим поиска
 | 
			
		||||
theme=Теги
 | 
			
		||||
theme_aliens=Инопланетяне
 | 
			
		||||
theme_animals=Животные
 | 
			
		||||
theme_cooking=Животные
 | 
			
		||||
theme_crossdressing=Кроссдрессинг
 | 
			
		||||
theme_delinquents=Хулиганы
 | 
			
		||||
theme_demons=Демоны
 | 
			
		||||
theme_gender_swap=Смена гендера
 | 
			
		||||
theme_ghosts=Призраки
 | 
			
		||||
theme_gyaru=Гяру
 | 
			
		||||
theme_harem=Гарем
 | 
			
		||||
theme_incest=Инцест
 | 
			
		||||
theme_loli=Лоли
 | 
			
		||||
theme_mafia=Мафия
 | 
			
		||||
theme_magic=Магия
 | 
			
		||||
theme_martial_arts=Боевые исскуства
 | 
			
		||||
theme_military=Военные
 | 
			
		||||
theme_monster_girls=Монстродевушки
 | 
			
		||||
theme_monsters=Монстры
 | 
			
		||||
theme_music=Музыка
 | 
			
		||||
theme_ninja=Ниндзя
 | 
			
		||||
theme_office_workers=Офисные работники
 | 
			
		||||
theme_police=Полиция
 | 
			
		||||
theme_post_apocalyptic=Постапокалиптика
 | 
			
		||||
theme_psychological=Психология
 | 
			
		||||
theme_reincarnation=Реинкарнация
 | 
			
		||||
theme_reverse_harem=Обратный гарем
 | 
			
		||||
theme_samurai=Самураи
 | 
			
		||||
theme_school_life=Школа
 | 
			
		||||
theme_shota=Шота
 | 
			
		||||
theme_supernatural=Сверхъестественное
 | 
			
		||||
theme_survival=Выживание
 | 
			
		||||
theme_time_travel=Путешествие во времени
 | 
			
		||||
theme_traditional_games=Путешествие во времени
 | 
			
		||||
theme_vampires=Вампиры
 | 
			
		||||
theme_video_games=Видеоигры
 | 
			
		||||
theme_villainess=Злодейка
 | 
			
		||||
theme_virtual_reality=Виртуальная реальность
 | 
			
		||||
theme_zombies=Зомби
 | 
			
		||||
unable_to_process_chapter_request=Не удалось обработать ссылку на главу. Ошибка: %d
 | 
			
		||||
uploaded_by=Загрузил %s
 | 
			
		||||
@ -6,8 +6,12 @@ ext {
 | 
			
		||||
    extName = 'MangaDex'
 | 
			
		||||
    pkgNameSuffix = 'all.mangadex'
 | 
			
		||||
    extClass = '.MangaDexFactory'
 | 
			
		||||
    extVersionCode = 184
 | 
			
		||||
    extVersionCode = 185
 | 
			
		||||
    isNsfw = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation(project(":lib-i18n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.all.mangadex
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.lib.i18n.Intl
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
import java.util.TimeZone
 | 
			
		||||
@ -50,8 +51,8 @@ object MDConstants {
 | 
			
		||||
        return "${coverQualityPref}_$dexLang"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getCoverQualityPreferenceEntries(intl: MangaDexIntl) =
 | 
			
		||||
        arrayOf(intl.coverQualityOriginal, intl.coverQualityMedium, intl.coverQualityLow)
 | 
			
		||||
    fun getCoverQualityPreferenceEntries(intl: Intl) =
 | 
			
		||||
        arrayOf(intl["cover_quality_original"], intl["cover_quality_medium"], intl["cover_quality_low"])
 | 
			
		||||
 | 
			
		||||
    fun getCoverQualityPreferenceEntryValues() = arrayOf("", ".512.jpg", ".256.jpg")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -213,7 +213,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
            .asObservable()
 | 
			
		||||
            .map { response ->
 | 
			
		||||
                if (response.isSuccessful.not()) {
 | 
			
		||||
                    throw Exception(helper.intl.unableToProcessChapterRequest(response.code))
 | 
			
		||||
                    throw Exception(helper.intl["unable_to_process_chapter_request"].format(response.code))
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                response.parseAs<ChapterDto>().data!!.relationships
 | 
			
		||||
@ -227,7 +227,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
            val mangaId = query.removePrefix(MDConstants.prefixIdSearch)
 | 
			
		||||
 | 
			
		||||
            if (!helper.containsUuid(mangaId)) {
 | 
			
		||||
                throw Exception(helper.intl.invalidMangaId)
 | 
			
		||||
                throw Exception(helper.intl["invalid_manga_id"])
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val url = MDConstants.apiMangaUrl.toHttpUrl().newBuilder()
 | 
			
		||||
@ -249,7 +249,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
                val groupId = query.removePrefix(MDConstants.prefixGrpSearch)
 | 
			
		||||
 | 
			
		||||
                if (!helper.containsUuid(groupId)) {
 | 
			
		||||
                    throw Exception(helper.intl.invalidGroupId)
 | 
			
		||||
                    throw Exception(helper.intl["invalid_group_id"])
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                tempUrl.addQueryParameter("group", groupId)
 | 
			
		||||
@ -259,7 +259,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
                val authorId = query.removePrefix(MDConstants.prefixAuthSearch)
 | 
			
		||||
 | 
			
		||||
                if (!helper.containsUuid(authorId)) {
 | 
			
		||||
                    throw Exception(helper.intl.invalidAuthorId)
 | 
			
		||||
                    throw Exception(helper.intl["invalid_author_id"])
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                tempUrl.addQueryParameter("authorOrArtist", authorId)
 | 
			
		||||
@ -295,7 +295,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
        val amount = listDtoFiltered.count()
 | 
			
		||||
 | 
			
		||||
        if (amount < 1) {
 | 
			
		||||
            throw Exception(helper.intl.noSeriesInList)
 | 
			
		||||
            throw Exception(helper.intl["no_series_in_list"])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val minIndex = (page - 1) * MDConstants.mangaLimit
 | 
			
		||||
@ -384,7 +384,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
     */
 | 
			
		||||
    override fun mangaDetailsRequest(manga: SManga): Request {
 | 
			
		||||
        if (!helper.containsUuid(manga.url.trim())) {
 | 
			
		||||
            throw Exception(helper.intl.migrateWarning)
 | 
			
		||||
            throw Exception(helper.intl["migrate_warning"])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val url = (MDConstants.apiUrl + manga.url).toHttpUrl().newBuilder()
 | 
			
		||||
@ -485,7 +485,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
     */
 | 
			
		||||
    override fun chapterListRequest(manga: SManga): Request {
 | 
			
		||||
        if (!helper.containsUuid(manga.url)) {
 | 
			
		||||
            throw Exception(helper.intl.migrateWarning)
 | 
			
		||||
            throw Exception(helper.intl["migrate_warning"])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return paginatedChapterListRequest(helper.getUUIDFromUrl(manga.url), 0)
 | 
			
		||||
@ -545,7 +545,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
    override fun pageListRequest(chapter: SChapter): Request {
 | 
			
		||||
        if (!helper.containsUuid(chapter.url)) {
 | 
			
		||||
            throw Exception(helper.intl.migrateWarning)
 | 
			
		||||
            throw Exception(helper.intl["migrate_warning"])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val chapterId = chapter.url.substringAfter("/chapter/")
 | 
			
		||||
@ -589,7 +589,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) {
 | 
			
		||||
        val coverQualityPref = ListPreference(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getCoverQualityPreferenceKey(dexLang)
 | 
			
		||||
            title = helper.intl.coverQuality
 | 
			
		||||
            title = helper.intl["cover_quality"]
 | 
			
		||||
            entries = MDConstants.getCoverQualityPreferenceEntries(helper.intl)
 | 
			
		||||
            entryValues = MDConstants.getCoverQualityPreferenceEntryValues()
 | 
			
		||||
            setDefaultValue(MDConstants.getCoverQualityPreferenceDefaultValue())
 | 
			
		||||
@ -608,8 +608,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val tryUsingFirstVolumeCoverPref = SwitchPreferenceCompat(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getTryUsingFirstVolumeCoverPrefKey(dexLang)
 | 
			
		||||
            title = helper.intl.tryUsingFirstVolumeCover
 | 
			
		||||
            summary = helper.intl.tryUsingFirstVolumeCoverSummary
 | 
			
		||||
            title = helper.intl["try_using_first_volume_cover"]
 | 
			
		||||
            summary = helper.intl["try_using_first_volume_cover_summary"]
 | 
			
		||||
            setDefaultValue(MDConstants.tryUsingFirstVolumeCoverDefault)
 | 
			
		||||
 | 
			
		||||
            setOnPreferenceChangeListener { _, newValue ->
 | 
			
		||||
@ -623,8 +623,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val dataSaverPref = SwitchPreferenceCompat(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getDataSaverPreferenceKey(dexLang)
 | 
			
		||||
            title = helper.intl.dataSaver
 | 
			
		||||
            summary = helper.intl.dataSaverSummary
 | 
			
		||||
            title = helper.intl["data_saver"]
 | 
			
		||||
            summary = helper.intl["data_saver_summary"]
 | 
			
		||||
            setDefaultValue(false)
 | 
			
		||||
 | 
			
		||||
            setOnPreferenceChangeListener { _, newValue ->
 | 
			
		||||
@ -638,8 +638,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val standardHttpsPortPref = SwitchPreferenceCompat(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getStandardHttpsPreferenceKey(dexLang)
 | 
			
		||||
            title = helper.intl.standardHttpsPort
 | 
			
		||||
            summary = helper.intl.standardHttpsPortSummary
 | 
			
		||||
            title = helper.intl["standard_https_port"]
 | 
			
		||||
            summary = helper.intl["standard_https_port_summary"]
 | 
			
		||||
            setDefaultValue(false)
 | 
			
		||||
 | 
			
		||||
            setOnPreferenceChangeListener { _, newValue ->
 | 
			
		||||
@ -653,13 +653,13 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val contentRatingPref = MultiSelectListPreference(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getContentRatingPrefKey(dexLang)
 | 
			
		||||
            title = helper.intl.standardContentRating
 | 
			
		||||
            summary = helper.intl.standardContentRatingSummary
 | 
			
		||||
            title = helper.intl["standard_content_rating"]
 | 
			
		||||
            summary = helper.intl["standard_content_rating_summary"]
 | 
			
		||||
            entries = arrayOf(
 | 
			
		||||
                helper.intl.contentRatingSafe,
 | 
			
		||||
                helper.intl.contentRatingSuggestive,
 | 
			
		||||
                helper.intl.contentRatingErotica,
 | 
			
		||||
                helper.intl.contentRatingPornographic,
 | 
			
		||||
                helper.intl["content_rating_safe"],
 | 
			
		||||
                helper.intl["content_rating_suggestive"],
 | 
			
		||||
                helper.intl["content_rating_erotica"],
 | 
			
		||||
                helper.intl["content_rating_pornographic"],
 | 
			
		||||
            )
 | 
			
		||||
            entryValues = arrayOf(
 | 
			
		||||
                MDConstants.contentRatingPrefValSafe,
 | 
			
		||||
@ -680,8 +680,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val originalLanguagePref = MultiSelectListPreference(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getOriginalLanguagePrefKey(dexLang)
 | 
			
		||||
            title = helper.intl.filterOriginalLanguages
 | 
			
		||||
            summary = helper.intl.filterOriginalLanguagesSummary
 | 
			
		||||
            title = helper.intl["filter_original_languages"]
 | 
			
		||||
            summary = helper.intl["filter_original_languages_summary"]
 | 
			
		||||
            entries = arrayOf(
 | 
			
		||||
                helper.intl.languageDisplayName(MangaDexIntl.JAPANESE),
 | 
			
		||||
                helper.intl.languageDisplayName(MangaDexIntl.CHINESE),
 | 
			
		||||
@ -705,8 +705,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val blockedGroupsPref = EditTextPreference(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getBlockedGroupsPrefKey(dexLang)
 | 
			
		||||
            title = helper.intl.blockGroupByUuid
 | 
			
		||||
            summary = helper.intl.blockGroupByUuidSummary
 | 
			
		||||
            title = helper.intl["block_group_by_uuid"]
 | 
			
		||||
            summary = helper.intl["block_group_by_uuid_summary"]
 | 
			
		||||
 | 
			
		||||
            setOnBindEditTextListener(helper::setupEditTextUuidValidator)
 | 
			
		||||
 | 
			
		||||
@ -719,8 +719,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val blockedUploaderPref = EditTextPreference(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getBlockedUploaderPrefKey(dexLang)
 | 
			
		||||
            title = helper.intl.blockUploaderByUuid
 | 
			
		||||
            summary = helper.intl.blockUploaderByUuidSummary
 | 
			
		||||
            title = helper.intl["block_uploader_by_uuid"]
 | 
			
		||||
            summary = helper.intl["block_uploader_by_uuid_summary"]
 | 
			
		||||
 | 
			
		||||
            setOnBindEditTextListener(helper::setupEditTextUuidValidator)
 | 
			
		||||
 | 
			
		||||
@ -733,8 +733,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
 | 
			
		||||
 | 
			
		||||
        val altTitlesInDescPref = SwitchPreferenceCompat(screen.context).apply {
 | 
			
		||||
            key = MDConstants.getAltTitlesInDescPrefKey(dexLang)
 | 
			
		||||
            title = helper.intl.altTitlesInDesc
 | 
			
		||||
            summary = helper.intl.altTitlesInDescSummary
 | 
			
		||||
            title = helper.intl["alternative_titles_in_description"]
 | 
			
		||||
            summary = helper.intl["alternative_titles_in_description_summary"]
 | 
			
		||||
            setDefaultValue(false)
 | 
			
		||||
 | 
			
		||||
            setOnPreferenceChangeListener { _, newValue ->
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import android.content.SharedPreferences
 | 
			
		||||
import eu.kanade.tachiyomi.extension.all.mangadex.dto.ContentRatingDto
 | 
			
		||||
import eu.kanade.tachiyomi.extension.all.mangadex.dto.PublicationDemographicDto
 | 
			
		||||
import eu.kanade.tachiyomi.extension.all.mangadex.dto.StatusDto
 | 
			
		||||
import eu.kanade.tachiyomi.lib.i18n.Intl
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.Filter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.FilterList
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
@ -13,7 +14,7 @@ class MangaDexFilters {
 | 
			
		||||
    internal fun getMDFilterList(
 | 
			
		||||
        preferences: SharedPreferences,
 | 
			
		||||
        dexLang: String,
 | 
			
		||||
        intl: MangaDexIntl,
 | 
			
		||||
        intl: Intl,
 | 
			
		||||
    ): FilterList = FilterList(
 | 
			
		||||
        HasAvailableChaptersFilter(intl),
 | 
			
		||||
        OriginalLanguageList(intl, getOriginalLanguage(preferences, dexLang, intl)),
 | 
			
		||||
@ -22,18 +23,18 @@ class MangaDexFilters {
 | 
			
		||||
        StatusList(intl, getStatus(intl)),
 | 
			
		||||
        SortFilter(intl, getSortables(intl)),
 | 
			
		||||
        TagsFilter(intl, getTagFilters(intl)),
 | 
			
		||||
        TagList(intl.content, getContents(intl)),
 | 
			
		||||
        TagList(intl.format, getFormats(intl)),
 | 
			
		||||
        TagList(intl.genre, getGenres(intl)),
 | 
			
		||||
        TagList(intl.theme, getThemes(intl)),
 | 
			
		||||
        TagList(intl["content"], getContents(intl)),
 | 
			
		||||
        TagList(intl["format"], getFormats(intl)),
 | 
			
		||||
        TagList(intl["genre"], getGenres(intl)),
 | 
			
		||||
        TagList(intl["theme"], getThemes(intl)),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private interface UrlQueryFilter {
 | 
			
		||||
        fun addQueryParameter(url: HttpUrl.Builder, dexLang: String)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class HasAvailableChaptersFilter(intl: MangaDexIntl) :
 | 
			
		||||
        Filter.CheckBox(intl.hasAvailableChapters),
 | 
			
		||||
    private class HasAvailableChaptersFilter(intl: Intl) :
 | 
			
		||||
        Filter.CheckBox(intl["has_available_chapters"]),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -45,8 +46,8 @@ class MangaDexFilters {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class OriginalLanguage(name: String, val isoCode: String) : Filter.CheckBox(name)
 | 
			
		||||
    private class OriginalLanguageList(intl: MangaDexIntl, originalLanguage: List<OriginalLanguage>) :
 | 
			
		||||
        Filter.Group<OriginalLanguage>(intl.originalLanguage, originalLanguage),
 | 
			
		||||
    private class OriginalLanguageList(intl: Intl, originalLanguage: List<OriginalLanguage>) :
 | 
			
		||||
        Filter.Group<OriginalLanguage>(intl["original_language"], originalLanguage),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -69,7 +70,7 @@ class MangaDexFilters {
 | 
			
		||||
    private fun getOriginalLanguage(
 | 
			
		||||
        preferences: SharedPreferences,
 | 
			
		||||
        dexLang: String,
 | 
			
		||||
        intl: MangaDexIntl,
 | 
			
		||||
        intl: Intl,
 | 
			
		||||
    ): List<OriginalLanguage> {
 | 
			
		||||
        val originalLanguages = preferences.getStringSet(
 | 
			
		||||
            MDConstants.getOriginalLanguagePrefKey(dexLang),
 | 
			
		||||
@ -77,18 +78,18 @@ class MangaDexFilters {
 | 
			
		||||
        )!!
 | 
			
		||||
 | 
			
		||||
        return listOf(
 | 
			
		||||
            OriginalLanguage(intl.originalLanguageFilterJapanese, MDConstants.originalLanguagePrefValJapanese)
 | 
			
		||||
            OriginalLanguage(intl["original_language_filter_japanese"], MDConstants.originalLanguagePrefValJapanese)
 | 
			
		||||
                .apply { state = MDConstants.originalLanguagePrefValJapanese in originalLanguages },
 | 
			
		||||
            OriginalLanguage(intl.originalLanguageFilterChinese, MDConstants.originalLanguagePrefValChinese)
 | 
			
		||||
            OriginalLanguage(intl["original_language_filter_chinese"], MDConstants.originalLanguagePrefValChinese)
 | 
			
		||||
                .apply { state = MDConstants.originalLanguagePrefValChinese in originalLanguages },
 | 
			
		||||
            OriginalLanguage(intl.originalLanguageFilterKorean, MDConstants.originalLanguagePrefValKorean)
 | 
			
		||||
            OriginalLanguage(intl["original_language_filter_korean"], MDConstants.originalLanguagePrefValKorean)
 | 
			
		||||
                .apply { state = MDConstants.originalLanguagePrefValKorean in originalLanguages },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class ContentRating(name: String, val value: String) : Filter.CheckBox(name)
 | 
			
		||||
    private class ContentRatingList(intl: MangaDexIntl, contentRating: List<ContentRating>) :
 | 
			
		||||
        Filter.Group<ContentRating>(intl.contentRating, contentRating),
 | 
			
		||||
    private class ContentRatingList(intl: Intl, contentRating: List<ContentRating>) :
 | 
			
		||||
        Filter.Group<ContentRating>(intl["content_rating"], contentRating),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -103,7 +104,7 @@ class MangaDexFilters {
 | 
			
		||||
    private fun getContentRating(
 | 
			
		||||
        preferences: SharedPreferences,
 | 
			
		||||
        dexLang: String,
 | 
			
		||||
        intl: MangaDexIntl,
 | 
			
		||||
        intl: Intl,
 | 
			
		||||
    ): List<ContentRating> {
 | 
			
		||||
        val contentRatings = preferences.getStringSet(
 | 
			
		||||
            MDConstants.getContentRatingPrefKey(dexLang),
 | 
			
		||||
@ -111,24 +112,24 @@ class MangaDexFilters {
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return listOf(
 | 
			
		||||
            ContentRating(intl.contentRatingSafe, ContentRatingDto.SAFE.value).apply {
 | 
			
		||||
            ContentRating(intl["content_rating_safe"], ContentRatingDto.SAFE.value).apply {
 | 
			
		||||
                state = contentRatings?.contains(MDConstants.contentRatingPrefValSafe) ?: true
 | 
			
		||||
            },
 | 
			
		||||
            ContentRating(intl.contentRatingSuggestive, ContentRatingDto.SUGGESTIVE.value).apply {
 | 
			
		||||
            ContentRating(intl["content_rating_suggestive"], ContentRatingDto.SUGGESTIVE.value).apply {
 | 
			
		||||
                state = contentRatings?.contains(MDConstants.contentRatingPrefValSuggestive) ?: true
 | 
			
		||||
            },
 | 
			
		||||
            ContentRating(intl.contentRatingErotica, ContentRatingDto.EROTICA.value).apply {
 | 
			
		||||
            ContentRating(intl["content_rating_erotica"], ContentRatingDto.EROTICA.value).apply {
 | 
			
		||||
                state = contentRatings?.contains(MDConstants.contentRatingPrefValErotica) ?: false
 | 
			
		||||
            },
 | 
			
		||||
            ContentRating(intl.contentRatingPornographic, ContentRatingDto.PORNOGRAPHIC.value).apply {
 | 
			
		||||
            ContentRating(intl["content_rating_pornographic"], ContentRatingDto.PORNOGRAPHIC.value).apply {
 | 
			
		||||
                state = contentRatings?.contains(MDConstants.contentRatingPrefValPornographic) ?: false
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class Demographic(name: String, val value: String) : Filter.CheckBox(name)
 | 
			
		||||
    private class DemographicList(intl: MangaDexIntl, demographics: List<Demographic>) :
 | 
			
		||||
        Filter.Group<Demographic>(intl.publicationDemographic, demographics),
 | 
			
		||||
    private class DemographicList(intl: Intl, demographics: List<Demographic>) :
 | 
			
		||||
        Filter.Group<Demographic>(intl["publication_demographic"], demographics),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -140,17 +141,17 @@ class MangaDexFilters {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getDemographics(intl: MangaDexIntl) = listOf(
 | 
			
		||||
        Demographic(intl.publicationDemographicNone, PublicationDemographicDto.NONE.value),
 | 
			
		||||
        Demographic(intl.publicationDemographicShounen, PublicationDemographicDto.SHOUNEN.value),
 | 
			
		||||
        Demographic(intl.publicationDemographicShoujo, PublicationDemographicDto.SHOUJO.value),
 | 
			
		||||
        Demographic(intl.publicationDemographicSeinen, PublicationDemographicDto.SEINEN.value),
 | 
			
		||||
        Demographic(intl.publicationDemographicJosei, PublicationDemographicDto.JOSEI.value),
 | 
			
		||||
    private fun getDemographics(intl: Intl) = listOf(
 | 
			
		||||
        Demographic(intl["publication_demographic_none"], PublicationDemographicDto.NONE.value),
 | 
			
		||||
        Demographic(intl["publication_demographic_shounen"], PublicationDemographicDto.SHOUNEN.value),
 | 
			
		||||
        Demographic(intl["publication_demographic_shoujo"], PublicationDemographicDto.SHOUJO.value),
 | 
			
		||||
        Demographic(intl["publication_demographic_seinen"], PublicationDemographicDto.SEINEN.value),
 | 
			
		||||
        Demographic(intl["publication_demographic_josei"], PublicationDemographicDto.JOSEI.value),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private class Status(name: String, val value: String) : Filter.CheckBox(name)
 | 
			
		||||
    private class StatusList(intl: MangaDexIntl, status: List<Status>) :
 | 
			
		||||
        Filter.Group<Status>(intl.status, status),
 | 
			
		||||
    private class StatusList(intl: Intl, status: List<Status>) :
 | 
			
		||||
        Filter.Group<Status>(intl["status"], status),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -162,31 +163,31 @@ class MangaDexFilters {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getStatus(intl: MangaDexIntl) = listOf(
 | 
			
		||||
        Status(intl.statusOngoing, StatusDto.ONGOING.value),
 | 
			
		||||
        Status(intl.statusCompleted, StatusDto.COMPLETED.value),
 | 
			
		||||
        Status(intl.statusHiatus, StatusDto.HIATUS.value),
 | 
			
		||||
        Status(intl.statusCancelled, StatusDto.CANCELLED.value),
 | 
			
		||||
    private fun getStatus(intl: Intl) = listOf(
 | 
			
		||||
        Status(intl["status_ongoing"], StatusDto.ONGOING.value),
 | 
			
		||||
        Status(intl["status_completed"], StatusDto.COMPLETED.value),
 | 
			
		||||
        Status(intl["status_hiatus"], StatusDto.HIATUS.value),
 | 
			
		||||
        Status(intl["status_cancelled"], StatusDto.CANCELLED.value),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class Sortable(val title: String, val value: String) {
 | 
			
		||||
        override fun toString(): String = title
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getSortables(intl: MangaDexIntl) = arrayOf(
 | 
			
		||||
        Sortable(intl.sortAlphabetic, "title"),
 | 
			
		||||
        Sortable(intl.sortChapterUploadedAt, "latestUploadedChapter"),
 | 
			
		||||
        Sortable(intl.sortNumberOfFollows, "followedCount"),
 | 
			
		||||
        Sortable(intl.sortContentCreatedAt, "createdAt"),
 | 
			
		||||
        Sortable(intl.sortContentInfoUpdatedAt, "updatedAt"),
 | 
			
		||||
        Sortable(intl.sortRelevance, "relevance"),
 | 
			
		||||
        Sortable(intl.sortYear, "year"),
 | 
			
		||||
        Sortable(intl.sortRating, "rating"),
 | 
			
		||||
    private fun getSortables(intl: Intl) = arrayOf(
 | 
			
		||||
        Sortable(intl["sort_alphabetic"], "title"),
 | 
			
		||||
        Sortable(intl["sort_chapter_uploaded_at"], "latestUploadedChapter"),
 | 
			
		||||
        Sortable(intl["sort_number_of_follows"], "followedCount"),
 | 
			
		||||
        Sortable(intl["sort_content_created_at"], "createdAt"),
 | 
			
		||||
        Sortable(intl["sort_content_info_updated_at"], "updatedAt"),
 | 
			
		||||
        Sortable(intl["sort_relevance"], "relevance"),
 | 
			
		||||
        Sortable(intl["sort_year"], "year"),
 | 
			
		||||
        Sortable(intl["sort_rating"], "rating"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class SortFilter(intl: MangaDexIntl, private val sortables: Array<Sortable>) :
 | 
			
		||||
    class SortFilter(intl: Intl, private val sortables: Array<Sortable>) :
 | 
			
		||||
        Filter.Sort(
 | 
			
		||||
            intl.sort,
 | 
			
		||||
            intl["sort"],
 | 
			
		||||
            sortables.map(Sortable::title).toTypedArray(),
 | 
			
		||||
            Selection(5, false),
 | 
			
		||||
        ),
 | 
			
		||||
@ -222,112 +223,112 @@ class MangaDexFilters {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getContents(intl: MangaDexIntl): List<Tag> {
 | 
			
		||||
    private fun getContents(intl: Intl): List<Tag> {
 | 
			
		||||
        val tags = listOf(
 | 
			
		||||
            Tag("b29d6a3d-1569-4e7a-8caf-7557bc92cd5d", intl.contentGore),
 | 
			
		||||
            Tag("97893a4c-12af-4dac-b6be-0dffb353568e", intl.contentSexualViolence),
 | 
			
		||||
            Tag("b29d6a3d-1569-4e7a-8caf-7557bc92cd5d", intl["content_gore"]),
 | 
			
		||||
            Tag("97893a4c-12af-4dac-b6be-0dffb353568e", intl["content_sexual_violence"]),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return tags.sortIfTranslated(intl)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getFormats(intl: MangaDexIntl): List<Tag> {
 | 
			
		||||
    private fun getFormats(intl: Intl): List<Tag> {
 | 
			
		||||
        val tags = listOf(
 | 
			
		||||
            Tag("b11fda93-8f1d-4bef-b2ed-8803d3733170", intl.formatFourKoma),
 | 
			
		||||
            Tag("f4122d1c-3b44-44d0-9936-ff7502c39ad3", intl.formatAdaptation),
 | 
			
		||||
            Tag("51d83883-4103-437c-b4b1-731cb73d786c", intl.formatAnthology),
 | 
			
		||||
            Tag("0a39b5a1-b235-4886-a747-1d05d216532d", intl.formatAwardWinning),
 | 
			
		||||
            Tag("b13b2a48-c720-44a9-9c77-39c9979373fb", intl.formatDoujinshi),
 | 
			
		||||
            Tag("7b2ce280-79ef-4c09-9b58-12b7c23a9b78", intl.formatFanColored),
 | 
			
		||||
            Tag("f5ba408b-0e7a-484d-8d49-4e9125ac96de", intl.formatFullColor),
 | 
			
		||||
            Tag("3e2b8dae-350e-4ab8-a8ce-016e844b9f0d", intl.formatLongStrip),
 | 
			
		||||
            Tag("320831a8-4026-470b-94f6-8353740e6f04", intl.formatOfficialColored),
 | 
			
		||||
            Tag("0234a31e-a729-4e28-9d6a-3f87c4966b9e", intl.formatOneshot),
 | 
			
		||||
            Tag("891cf039-b895-47f0-9229-bef4c96eccd4", intl.formatUserCreated),
 | 
			
		||||
            Tag("e197df38-d0e7-43b5-9b09-2842d0c326dd", intl.formatWebComic),
 | 
			
		||||
            Tag("b11fda93-8f1d-4bef-b2ed-8803d3733170", intl["format_yonkoma"]),
 | 
			
		||||
            Tag("f4122d1c-3b44-44d0-9936-ff7502c39ad3", intl["format_adaptation"]),
 | 
			
		||||
            Tag("51d83883-4103-437c-b4b1-731cb73d786c", intl["format_anthology"]),
 | 
			
		||||
            Tag("0a39b5a1-b235-4886-a747-1d05d216532d", intl["format_award_winning"]),
 | 
			
		||||
            Tag("b13b2a48-c720-44a9-9c77-39c9979373fb", intl["format_doujinshi"]),
 | 
			
		||||
            Tag("7b2ce280-79ef-4c09-9b58-12b7c23a9b78", intl["format_fan_colored"]),
 | 
			
		||||
            Tag("f5ba408b-0e7a-484d-8d49-4e9125ac96de", intl["format_full_color"]),
 | 
			
		||||
            Tag("3e2b8dae-350e-4ab8-a8ce-016e844b9f0d", intl["format_long_strip"]),
 | 
			
		||||
            Tag("320831a8-4026-470b-94f6-8353740e6f04", intl["format_official_colored"]),
 | 
			
		||||
            Tag("0234a31e-a729-4e28-9d6a-3f87c4966b9e", intl["format_oneshot"]),
 | 
			
		||||
            Tag("891cf039-b895-47f0-9229-bef4c96eccd4", intl["format_user_created"]),
 | 
			
		||||
            Tag("e197df38-d0e7-43b5-9b09-2842d0c326dd", intl["format_web_comic"]),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return tags.sortIfTranslated(intl)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getGenres(intl: MangaDexIntl): List<Tag> {
 | 
			
		||||
    private fun getGenres(intl: Intl): List<Tag> {
 | 
			
		||||
        val tags = listOf(
 | 
			
		||||
            Tag("391b0423-d847-456f-aff0-8b0cfc03066b", intl.genreAction),
 | 
			
		||||
            Tag("87cc87cd-a395-47af-b27a-93258283bbc6", intl.genreAdventure),
 | 
			
		||||
            Tag("5920b825-4181-4a17-beeb-9918b0ff7a30", intl.genreBoysLove),
 | 
			
		||||
            Tag("4d32cc48-9f00-4cca-9b5a-a839f0764984", intl.genreComedy),
 | 
			
		||||
            Tag("5ca48985-9a9d-4bd8-be29-80dc0303db72", intl.genreCrime),
 | 
			
		||||
            Tag("b9af3a63-f058-46de-a9a0-e0c13906197a", intl.genreDrama),
 | 
			
		||||
            Tag("cdc58593-87dd-415e-bbc0-2ec27bf404cc", intl.genreFantasy),
 | 
			
		||||
            Tag("a3c67850-4684-404e-9b7f-c69850ee5da6", intl.genreGirlsLove),
 | 
			
		||||
            Tag("33771934-028e-4cb3-8744-691e866a923e", intl.genreHistorical),
 | 
			
		||||
            Tag("cdad7e68-1419-41dd-bdce-27753074a640", intl.genreHorror),
 | 
			
		||||
            Tag("ace04997-f6bd-436e-b261-779182193d3d", intl.genreIsekai),
 | 
			
		||||
            Tag("81c836c9-914a-4eca-981a-560dad663e73", intl.genreMagicalGirls),
 | 
			
		||||
            Tag("50880a9d-5440-4732-9afb-8f457127e836", intl.genreMecha),
 | 
			
		||||
            Tag("c8cbe35b-1b2b-4a3f-9c37-db84c4514856", intl.genreMedical),
 | 
			
		||||
            Tag("ee968100-4191-4968-93d3-f82d72be7e46", intl.genreMystery),
 | 
			
		||||
            Tag("b1e97889-25b4-4258-b28b-cd7f4d28ea9b", intl.genrePhilosophical),
 | 
			
		||||
            Tag("423e2eae-a7a2-4a8b-ac03-a8351462d71d", intl.genreRomance),
 | 
			
		||||
            Tag("256c8bd9-4904-4360-bf4f-508a76d67183", intl.genreSciFi),
 | 
			
		||||
            Tag("e5301a23-ebd9-49dd-a0cb-2add944c7fe9", intl.genreSliceOfLife),
 | 
			
		||||
            Tag("69964a64-2f90-4d33-beeb-f3ed2875eb4c", intl.genreSports),
 | 
			
		||||
            Tag("7064a261-a137-4d3a-8848-2d385de3a99c", intl.genreSuperhero),
 | 
			
		||||
            Tag("07251805-a27e-4d59-b488-f0bfbec15168", intl.genreThriller),
 | 
			
		||||
            Tag("f8f62932-27da-4fe4-8ee1-6779a8c5edba", intl.genreTragedy),
 | 
			
		||||
            Tag("acc803a4-c95a-4c22-86fc-eb6b582d82a2", intl.genreWuxia),
 | 
			
		||||
            Tag("391b0423-d847-456f-aff0-8b0cfc03066b", intl["genre_action"]),
 | 
			
		||||
            Tag("87cc87cd-a395-47af-b27a-93258283bbc6", intl["genre_adventure"]),
 | 
			
		||||
            Tag("5920b825-4181-4a17-beeb-9918b0ff7a30", intl["genre_boys_love"]),
 | 
			
		||||
            Tag("4d32cc48-9f00-4cca-9b5a-a839f0764984", intl["genre_comedy"]),
 | 
			
		||||
            Tag("5ca48985-9a9d-4bd8-be29-80dc0303db72", intl["genre_crime"]),
 | 
			
		||||
            Tag("b9af3a63-f058-46de-a9a0-e0c13906197a", intl["genre_drama"]),
 | 
			
		||||
            Tag("cdc58593-87dd-415e-bbc0-2ec27bf404cc", intl["genre_fantasy"]),
 | 
			
		||||
            Tag("a3c67850-4684-404e-9b7f-c69850ee5da6", intl["genre_girls_love"]),
 | 
			
		||||
            Tag("33771934-028e-4cb3-8744-691e866a923e", intl["genre_historical"]),
 | 
			
		||||
            Tag("cdad7e68-1419-41dd-bdce-27753074a640", intl["genre_horror"]),
 | 
			
		||||
            Tag("ace04997-f6bd-436e-b261-779182193d3d", intl["genre_isekai"]),
 | 
			
		||||
            Tag("81c836c9-914a-4eca-981a-560dad663e73", intl["genre_magical_girls"]),
 | 
			
		||||
            Tag("50880a9d-5440-4732-9afb-8f457127e836", intl["genre_mecha"]),
 | 
			
		||||
            Tag("c8cbe35b-1b2b-4a3f-9c37-db84c4514856", intl["genre_medical"]),
 | 
			
		||||
            Tag("ee968100-4191-4968-93d3-f82d72be7e46", intl["genre_mystery"]),
 | 
			
		||||
            Tag("b1e97889-25b4-4258-b28b-cd7f4d28ea9b", intl["genre_philosophical"]),
 | 
			
		||||
            Tag("423e2eae-a7a2-4a8b-ac03-a8351462d71d", intl["genre_romance"]),
 | 
			
		||||
            Tag("256c8bd9-4904-4360-bf4f-508a76d67183", intl["genre_sci_fi"]),
 | 
			
		||||
            Tag("e5301a23-ebd9-49dd-a0cb-2add944c7fe9", intl["genre_slice_of_life"]),
 | 
			
		||||
            Tag("69964a64-2f90-4d33-beeb-f3ed2875eb4c", intl["genre_sports"]),
 | 
			
		||||
            Tag("7064a261-a137-4d3a-8848-2d385de3a99c", intl["genre_superhero"]),
 | 
			
		||||
            Tag("07251805-a27e-4d59-b488-f0bfbec15168", intl["genre_thriller"]),
 | 
			
		||||
            Tag("f8f62932-27da-4fe4-8ee1-6779a8c5edba", intl["genre_tragedy"]),
 | 
			
		||||
            Tag("acc803a4-c95a-4c22-86fc-eb6b582d82a2", intl["genre_wuxia"]),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return tags.sortIfTranslated(intl)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getThemes(intl: MangaDexIntl): List<Tag> {
 | 
			
		||||
    private fun getThemes(intl: Intl): List<Tag> {
 | 
			
		||||
        val tags = listOf(
 | 
			
		||||
            Tag("e64f6742-c834-471d-8d72-dd51fc02b835", intl.themeAliens),
 | 
			
		||||
            Tag("3de8c75d-8ee3-48ff-98ee-e20a65c86451", intl.themeAnimals),
 | 
			
		||||
            Tag("ea2bc92d-1c26-4930-9b7c-d5c0dc1b6869", intl.themeCooking),
 | 
			
		||||
            Tag("9ab53f92-3eed-4e9b-903a-917c86035ee3", intl.themeCrossdressing),
 | 
			
		||||
            Tag("da2d50ca-3018-4cc0-ac7a-6b7d472a29ea", intl.themeDelinquents),
 | 
			
		||||
            Tag("39730448-9a5f-48a2-85b0-a70db87b1233", intl.themeDemons),
 | 
			
		||||
            Tag("2bd2e8d0-f146-434a-9b51-fc9ff2c5fe6a", intl.themeGenderSwap),
 | 
			
		||||
            Tag("3bb26d85-09d5-4d2e-880c-c34b974339e9", intl.themeGhosts),
 | 
			
		||||
            Tag("fad12b5e-68ba-460e-b933-9ae8318f5b65", intl.themeGyaru),
 | 
			
		||||
            Tag("aafb99c1-7f60-43fa-b75f-fc9502ce29c7", intl.themeHarem),
 | 
			
		||||
            Tag("5bd0e105-4481-44ca-b6e7-7544da56b1a3", intl.themeIncest),
 | 
			
		||||
            Tag("2d1f5d56-a1e5-4d0d-a961-2193588b08ec", intl.themeLoli),
 | 
			
		||||
            Tag("85daba54-a71c-4554-8a28-9901a8b0afad", intl.themeMafia),
 | 
			
		||||
            Tag("a1f53773-c69a-4ce5-8cab-fffcd90b1565", intl.themeMagic),
 | 
			
		||||
            Tag("799c202e-7daa-44eb-9cf7-8a3c0441531e", intl.themeMartialArts),
 | 
			
		||||
            Tag("ac72833b-c4e9-4878-b9db-6c8a4a99444a", intl.themeMilitary),
 | 
			
		||||
            Tag("dd1f77c5-dea9-4e2b-97ae-224af09caf99", intl.themeMonsterGirls),
 | 
			
		||||
            Tag("36fd93ea-e8b8-445e-b836-358f02b3d33d", intl.themeMonsters),
 | 
			
		||||
            Tag("f42fbf9e-188a-447b-9fdc-f19dc1e4d685", intl.themeMusic),
 | 
			
		||||
            Tag("489dd859-9b61-4c37-af75-5b18e88daafc", intl.themeNinja),
 | 
			
		||||
            Tag("92d6d951-ca5e-429c-ac78-451071cbf064", intl.themeOfficeWorkers),
 | 
			
		||||
            Tag("df33b754-73a3-4c54-80e6-1a74a8058539", intl.themePolice),
 | 
			
		||||
            Tag("9467335a-1b83-4497-9231-765337a00b96", intl.themePostApocalyptic),
 | 
			
		||||
            Tag("3b60b75c-a2d7-4860-ab56-05f391bb889c", intl.themePsychological),
 | 
			
		||||
            Tag("0bc90acb-ccc1-44ca-a34a-b9f3a73259d0", intl.themeReincarnation),
 | 
			
		||||
            Tag("65761a2a-415e-47f3-bef2-a9dababba7a6", intl.themeReverseHarem),
 | 
			
		||||
            Tag("81183756-1453-4c81-aa9e-f6e1b63be016", intl.themeSamurai),
 | 
			
		||||
            Tag("caaa44eb-cd40-4177-b930-79d3ef2afe87", intl.themeSchoolLife),
 | 
			
		||||
            Tag("ddefd648-5140-4e5f-ba18-4eca4071d19b", intl.themeShota),
 | 
			
		||||
            Tag("eabc5b4c-6aff-42f3-b657-3e90cbd00b75", intl.themeSupernatural),
 | 
			
		||||
            Tag("5fff9cde-849c-4d78-aab0-0d52b2ee1d25", intl.themeSurvival),
 | 
			
		||||
            Tag("292e862b-2d17-4062-90a2-0356caa4ae27", intl.themeTimeTravel),
 | 
			
		||||
            Tag("31932a7e-5b8e-49a6-9f12-2afa39dc544c", intl.themeTraditionalGames),
 | 
			
		||||
            Tag("d7d1730f-6eb0-4ba6-9437-602cac38664c", intl.themeVampires),
 | 
			
		||||
            Tag("9438db5a-7e2a-4ac0-b39e-e0d95a34b8a8", intl.themeVideoGames),
 | 
			
		||||
            Tag("d14322ac-4d6f-4e9b-afd9-629d5f4d8a41", intl.themeVillainess),
 | 
			
		||||
            Tag("8c86611e-fab7-4986-9dec-d1a2f44acdd5", intl.themeVirtualReality),
 | 
			
		||||
            Tag("631ef465-9aba-4afb-b0fc-ea10efe274a8", intl.themeZombies),
 | 
			
		||||
            Tag("e64f6742-c834-471d-8d72-dd51fc02b835", intl["theme_aliens"]),
 | 
			
		||||
            Tag("3de8c75d-8ee3-48ff-98ee-e20a65c86451", intl["theme_animals"]),
 | 
			
		||||
            Tag("ea2bc92d-1c26-4930-9b7c-d5c0dc1b6869", intl["theme_cooking"]),
 | 
			
		||||
            Tag("9ab53f92-3eed-4e9b-903a-917c86035ee3", intl["theme_crossdressing"]),
 | 
			
		||||
            Tag("da2d50ca-3018-4cc0-ac7a-6b7d472a29ea", intl["theme_delinquents"]),
 | 
			
		||||
            Tag("39730448-9a5f-48a2-85b0-a70db87b1233", intl["theme_demons"]),
 | 
			
		||||
            Tag("2bd2e8d0-f146-434a-9b51-fc9ff2c5fe6a", intl["theme_gender_swap"]),
 | 
			
		||||
            Tag("3bb26d85-09d5-4d2e-880c-c34b974339e9", intl["theme_ghosts"]),
 | 
			
		||||
            Tag("fad12b5e-68ba-460e-b933-9ae8318f5b65", intl["theme_gyaru"]),
 | 
			
		||||
            Tag("aafb99c1-7f60-43fa-b75f-fc9502ce29c7", intl["theme_harem"]),
 | 
			
		||||
            Tag("5bd0e105-4481-44ca-b6e7-7544da56b1a3", intl["theme_incest"]),
 | 
			
		||||
            Tag("2d1f5d56-a1e5-4d0d-a961-2193588b08ec", intl["theme_loli"]),
 | 
			
		||||
            Tag("85daba54-a71c-4554-8a28-9901a8b0afad", intl["theme_mafia"]),
 | 
			
		||||
            Tag("a1f53773-c69a-4ce5-8cab-fffcd90b1565", intl["theme_magic"]),
 | 
			
		||||
            Tag("799c202e-7daa-44eb-9cf7-8a3c0441531e", intl["theme_martial_arts"]),
 | 
			
		||||
            Tag("ac72833b-c4e9-4878-b9db-6c8a4a99444a", intl["theme_military"]),
 | 
			
		||||
            Tag("dd1f77c5-dea9-4e2b-97ae-224af09caf99", intl["theme_monster_girls"]),
 | 
			
		||||
            Tag("36fd93ea-e8b8-445e-b836-358f02b3d33d", intl["theme_monsters"]),
 | 
			
		||||
            Tag("f42fbf9e-188a-447b-9fdc-f19dc1e4d685", intl["theme_music"]),
 | 
			
		||||
            Tag("489dd859-9b61-4c37-af75-5b18e88daafc", intl["theme_ninja"]),
 | 
			
		||||
            Tag("92d6d951-ca5e-429c-ac78-451071cbf064", intl["theme_office_workers"]),
 | 
			
		||||
            Tag("df33b754-73a3-4c54-80e6-1a74a8058539", intl["theme_police"]),
 | 
			
		||||
            Tag("9467335a-1b83-4497-9231-765337a00b96", intl["theme_post_apocalyptic"]),
 | 
			
		||||
            Tag("3b60b75c-a2d7-4860-ab56-05f391bb889c", intl["theme_psychological"]),
 | 
			
		||||
            Tag("0bc90acb-ccc1-44ca-a34a-b9f3a73259d0", intl["theme_reincarnation"]),
 | 
			
		||||
            Tag("65761a2a-415e-47f3-bef2-a9dababba7a6", intl["theme_reverse_harem"]),
 | 
			
		||||
            Tag("81183756-1453-4c81-aa9e-f6e1b63be016", intl["theme_samurai"]),
 | 
			
		||||
            Tag("caaa44eb-cd40-4177-b930-79d3ef2afe87", intl["theme_school_life"]),
 | 
			
		||||
            Tag("ddefd648-5140-4e5f-ba18-4eca4071d19b", intl["theme_shota"]),
 | 
			
		||||
            Tag("eabc5b4c-6aff-42f3-b657-3e90cbd00b75", intl["theme_supernatural"]),
 | 
			
		||||
            Tag("5fff9cde-849c-4d78-aab0-0d52b2ee1d25", intl["theme_survival"]),
 | 
			
		||||
            Tag("292e862b-2d17-4062-90a2-0356caa4ae27", intl["theme_time_travel"]),
 | 
			
		||||
            Tag("31932a7e-5b8e-49a6-9f12-2afa39dc544c", intl["theme_traditional_games"]),
 | 
			
		||||
            Tag("d7d1730f-6eb0-4ba6-9437-602cac38664c", intl["theme_vampires"]),
 | 
			
		||||
            Tag("9438db5a-7e2a-4ac0-b39e-e0d95a34b8a8", intl["theme_video_games"]),
 | 
			
		||||
            Tag("d14322ac-4d6f-4e9b-afd9-629d5f4d8a41", intl["theme_villainess"]),
 | 
			
		||||
            Tag("8c86611e-fab7-4986-9dec-d1a2f44acdd5", intl["theme_virtual_reality"]),
 | 
			
		||||
            Tag("631ef465-9aba-4afb-b0fc-ea10efe274a8", intl["theme_zombies"]),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return tags.sortIfTranslated(intl)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // to get all tags from dex https://api.mangadex.org/manga/tag
 | 
			
		||||
    internal fun getTags(intl: MangaDexIntl): List<Tag> {
 | 
			
		||||
    internal fun getTags(intl: Intl): List<Tag> {
 | 
			
		||||
        return getContents(intl) + getFormats(intl) + getGenres(intl) + getThemes(intl)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -335,13 +336,13 @@ class MangaDexFilters {
 | 
			
		||||
        override fun toString(): String = title
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getTagModes(intl: MangaDexIntl) = arrayOf(
 | 
			
		||||
        TagMode(intl.modeAnd, "AND"),
 | 
			
		||||
        TagMode(intl.modeOr, "OR"),
 | 
			
		||||
    private fun getTagModes(intl: Intl) = arrayOf(
 | 
			
		||||
        TagMode(intl["mode_and"], "AND"),
 | 
			
		||||
        TagMode(intl["mode_or"], "OR"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private class TagInclusionMode(intl: MangaDexIntl, modes: Array<TagMode>) :
 | 
			
		||||
        Filter.Select<TagMode>(intl.includedTagsMode, modes, 0),
 | 
			
		||||
    private class TagInclusionMode(intl: Intl, modes: Array<TagMode>) :
 | 
			
		||||
        Filter.Select<TagMode>(intl["included_tags_mode"], modes, 0),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -349,8 +350,8 @@ class MangaDexFilters {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class TagExclusionMode(intl: MangaDexIntl, modes: Array<TagMode>) :
 | 
			
		||||
        Filter.Select<TagMode>(intl.excludedTagsMode, modes, 1),
 | 
			
		||||
    private class TagExclusionMode(intl: Intl, modes: Array<TagMode>) :
 | 
			
		||||
        Filter.Select<TagMode>(intl["excluded_tags_mode"], modes, 1),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -358,8 +359,8 @@ class MangaDexFilters {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class TagsFilter(intl: MangaDexIntl, innerFilters: FilterList) :
 | 
			
		||||
        Filter.Group<Filter<*>>(intl.tags, innerFilters),
 | 
			
		||||
    private class TagsFilter(intl: Intl, innerFilters: FilterList) :
 | 
			
		||||
        Filter.Group<Filter<*>>(intl["tags_mode"], innerFilters),
 | 
			
		||||
        UrlQueryFilter {
 | 
			
		||||
 | 
			
		||||
        override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) {
 | 
			
		||||
@ -368,7 +369,7 @@ class MangaDexFilters {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getTagFilters(intl: MangaDexIntl): FilterList = FilterList(
 | 
			
		||||
    private fun getTagFilters(intl: Intl): FilterList = FilterList(
 | 
			
		||||
        TagInclusionMode(intl, getTagModes(intl)),
 | 
			
		||||
        TagExclusionMode(intl, getTagModes(intl)),
 | 
			
		||||
    )
 | 
			
		||||
@ -380,8 +381,8 @@ class MangaDexFilters {
 | 
			
		||||
        return url.build()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun List<Tag>.sortIfTranslated(intl: MangaDexIntl): List<Tag> = apply {
 | 
			
		||||
        if (intl.availableLang == MangaDexIntl.ENGLISH) {
 | 
			
		||||
    private fun List<Tag>.sortIfTranslated(intl: Intl): List<Tag> = apply {
 | 
			
		||||
        if (intl.chosenLanguage == MangaDexIntl.ENGLISH) {
 | 
			
		||||
            return this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ import eu.kanade.tachiyomi.extension.all.mangadex.dto.TagDto
 | 
			
		||||
import eu.kanade.tachiyomi.extension.all.mangadex.dto.UnknownEntity
 | 
			
		||||
import eu.kanade.tachiyomi.extension.all.mangadex.dto.UserAttributes
 | 
			
		||||
import eu.kanade.tachiyomi.extension.all.mangadex.dto.UserDto
 | 
			
		||||
import eu.kanade.tachiyomi.lib.i18n.Intl
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SChapter
 | 
			
		||||
@ -85,7 +86,19 @@ class MangaDexHelper(lang: String) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val intl = MangaDexIntl(lang)
 | 
			
		||||
    val intl = Intl(
 | 
			
		||||
        language = lang,
 | 
			
		||||
        baseLanguage = MangaDexIntl.ENGLISH,
 | 
			
		||||
        availableLanguages = MangaDexIntl.AVAILABLE_LANGS,
 | 
			
		||||
        classLoader = this::class.java.classLoader!!,
 | 
			
		||||
        createMessageFileName = { lang ->
 | 
			
		||||
            when (lang) {
 | 
			
		||||
                MangaDexIntl.SPANISH_LATAM -> Intl.createDefaultMessageFileName(MangaDexIntl.SPANISH)
 | 
			
		||||
                MangaDexIntl.PORTUGUESE -> Intl.createDefaultMessageFileName(MangaDexIntl.BRAZILIAN_PORTUGUESE)
 | 
			
		||||
                else -> Intl.createDefaultMessageFileName(lang)
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the UUID from the url
 | 
			
		||||
@ -298,10 +311,11 @@ class MangaDexHelper(lang: String) {
 | 
			
		||||
        val dexLocale = Locale.forLanguageTag(lang)
 | 
			
		||||
 | 
			
		||||
        val nonGenres = listOfNotNull(
 | 
			
		||||
            attr.publicationDemographic?.let { intl.publicationDemographic(it) },
 | 
			
		||||
            attr.publicationDemographic
 | 
			
		||||
                ?.let { intl["publication_demographic_${it.name.lowercase()}"] },
 | 
			
		||||
            attr.contentRating
 | 
			
		||||
                .takeIf { it != ContentRatingDto.SAFE }
 | 
			
		||||
                ?.let { intl.contentRatingGenre(it) },
 | 
			
		||||
                ?.let { intl["content_rating_genre"].format(intl["content_rating_${it.name.lowercase()}"]) },
 | 
			
		||||
            attr.originalLanguage
 | 
			
		||||
                ?.let { Locale.forLanguageTag(it) }
 | 
			
		||||
                ?.getDisplayName(dexLocale)
 | 
			
		||||
@ -344,7 +358,7 @@ class MangaDexHelper(lang: String) {
 | 
			
		||||
 | 
			
		||||
            if (altTitles.isNotEmpty()) {
 | 
			
		||||
                val altTitlesDesc = altTitles
 | 
			
		||||
                    .joinToString("\n", "${intl.altTitleText}\n") { "• $it" }
 | 
			
		||||
                    .joinToString("\n", "${intl["alternative_titles"]}\n") { "• $it" }
 | 
			
		||||
                desc += (if (desc.isNullOrBlank()) "" else "\n\n") + altTitlesDesc.removeEntitiesAndMarkdown()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -376,9 +390,9 @@ class MangaDexHelper(lang: String) {
 | 
			
		||||
                val users = chapterDataDto.relationships
 | 
			
		||||
                    .filterIsInstance<UserDto>()
 | 
			
		||||
                    .mapNotNull { it.attributes?.username }
 | 
			
		||||
                if (users.isNotEmpty()) intl.uploadedBy(users) else ""
 | 
			
		||||
                if (users.isNotEmpty()) intl["uploaded_by"].format(users.joinToString(" & ")) else ""
 | 
			
		||||
            }
 | 
			
		||||
            .ifEmpty { intl.noGroup } // "No Group" as final resort
 | 
			
		||||
            .ifEmpty { intl["no_group"] } // "No Group" as final resort
 | 
			
		||||
 | 
			
		||||
        val chapterName = mutableListOf<String>()
 | 
			
		||||
        // Build chapter name
 | 
			
		||||
@ -459,7 +473,7 @@ class MangaDexHelper(lang: String) {
 | 
			
		||||
                        .map(String::trim)
 | 
			
		||||
                        .all(::isUuid)
 | 
			
		||||
 | 
			
		||||
                    editText.error = if (!isValid) intl.invalidUuids else null
 | 
			
		||||
                    editText.error = if (!isValid) intl["invalid_uuids"] else null
 | 
			
		||||
                    editText.rootView.findViewById<Button>(android.R.id.button1)
 | 
			
		||||
                        ?.isEnabled = editText.error == null
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								src/all/mangaplus/assets/i18n/messages_en.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/all/mangaplus/assets/i18n/messages_en.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
chapter_expired=The chapter reading period has expired.
 | 
			
		||||
image_quality=Image quality
 | 
			
		||||
image_quality_high=High
 | 
			
		||||
image_quality_low=Low
 | 
			
		||||
image_quality_medium=Medium
 | 
			
		||||
not_available=Title not available in this language.
 | 
			
		||||
split_double_pages=Split double pages
 | 
			
		||||
split_double_pages_summary=Only a few titles supports disabling this setting.
 | 
			
		||||
title_removed=This title was removed from the MANGA Plus catalogue.
 | 
			
		||||
unknown_error=An unknown error happened.
 | 
			
		||||
							
								
								
									
										10
									
								
								src/all/mangaplus/assets/i18n/messages_pt_br.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/all/mangaplus/assets/i18n/messages_pt_br.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
chapter_expired=O período de leitura do capítulo expirou.
 | 
			
		||||
image_quality=Qualidade da imagem
 | 
			
		||||
image_quality_high=Alta
 | 
			
		||||
image_quality_low=Baixa
 | 
			
		||||
image_quality_medium=Média
 | 
			
		||||
not_available=Título não disponível neste idioma.
 | 
			
		||||
split_double_pages=Dividir as páginas duplas
 | 
			
		||||
split_double_pages_summary=Somente poucos títulos suportam a desativação desta configuração.
 | 
			
		||||
title_removed=Este título foi removido do catálogo do MANGA Plus.
 | 
			
		||||
unknown_error=Um erro desconhecido ocorreu.
 | 
			
		||||
							
								
								
									
										10
									
								
								src/all/mangaplus/assets/i18n/messages_vi.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/all/mangaplus/assets/i18n/messages_vi.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
chapter_expired=Đã hết thời gian đọc chương này.
 | 
			
		||||
image_quality=Chất lượng ảnh
 | 
			
		||||
image_quality_high=Cao
 | 
			
		||||
image_quality_low=Thấp
 | 
			
		||||
image_quality_medium=Vừa
 | 
			
		||||
not_available=Truyện không có sẵn ở ngôn ngữ này.
 | 
			
		||||
split_double_pages=Tách trang đôi
 | 
			
		||||
split_double_pages_summary=Chỉ một số truyện hỗ trợ không tách trang đôi.
 | 
			
		||||
title_removed=Truyện này đã bị gỡ khỏi MANGA Plus.
 | 
			
		||||
unknown_error=Đã xảy ra lỗi không xác định.
 | 
			
		||||
@ -6,7 +6,11 @@ ext {
 | 
			
		||||
    extName = 'MANGA Plus by SHUEISHA'
 | 
			
		||||
    pkgNameSuffix = 'all.mangaplus'
 | 
			
		||||
    extClass = '.MangaPlusFactory'
 | 
			
		||||
    extVersionCode = 43
 | 
			
		||||
    extVersionCode = 44
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation(project(":lib-i18n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import android.content.SharedPreferences
 | 
			
		||||
import androidx.preference.ListPreference
 | 
			
		||||
import androidx.preference.PreferenceScreen
 | 
			
		||||
import androidx.preference.SwitchPreferenceCompat
 | 
			
		||||
import eu.kanade.tachiyomi.lib.i18n.Intl
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
 | 
			
		||||
import eu.kanade.tachiyomi.source.ConfigurableSource
 | 
			
		||||
@ -57,7 +58,14 @@ class MangaPlus(
 | 
			
		||||
 | 
			
		||||
    private val json: Json by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val intl by lazy { MangaPlusIntl(langCode) }
 | 
			
		||||
    private val intl by lazy {
 | 
			
		||||
        Intl(
 | 
			
		||||
            language = lang,
 | 
			
		||||
            baseLanguage = "en",
 | 
			
		||||
            availableLanguages = setOf("en", "pt-BR", "vi"),
 | 
			
		||||
            classLoader = this::class.java.classLoader!!,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val preferences: SharedPreferences by lazy {
 | 
			
		||||
        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
 | 
			
		||||
@ -82,7 +90,7 @@ class MangaPlus(
 | 
			
		||||
        val result = response.asMangaPlusResponse()
 | 
			
		||||
 | 
			
		||||
        checkNotNull(result.success) {
 | 
			
		||||
            result.error!!.langPopup(langCode)?.body ?: intl.unknownError
 | 
			
		||||
            result.error!!.langPopup(langCode)?.body ?: intl["unknown_error"]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val titleList = result.success.titleRankingView!!.titles
 | 
			
		||||
@ -105,7 +113,7 @@ class MangaPlus(
 | 
			
		||||
        val result = response.asMangaPlusResponse()
 | 
			
		||||
 | 
			
		||||
        checkNotNull(result.success) {
 | 
			
		||||
            result.error!!.langPopup(langCode)?.body ?: intl.unknownError
 | 
			
		||||
            result.error!!.langPopup(langCode)?.body ?: intl["unknown_error"]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Fetch all titles to get newer thumbnail URLs in the interceptor.
 | 
			
		||||
@ -151,7 +159,7 @@ class MangaPlus(
 | 
			
		||||
        val result = response.asMangaPlusResponse()
 | 
			
		||||
 | 
			
		||||
        checkNotNull(result.success) {
 | 
			
		||||
            result.error!!.langPopup(langCode)?.body ?: intl.unknownError
 | 
			
		||||
            result.error!!.langPopup(langCode)?.body ?: intl["unknown_error"]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (result.success.titleDetailView != null) {
 | 
			
		||||
@ -163,7 +171,7 @@ class MangaPlus(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (result.success.mangaViewer != null) {
 | 
			
		||||
            checkNotNull(result.success.mangaViewer.titleId) { intl.chapterExpired }
 | 
			
		||||
            checkNotNull(result.success.mangaViewer.titleId) { intl["chapter_expired"] }
 | 
			
		||||
 | 
			
		||||
            val titleId = result.success.mangaViewer.titleId
 | 
			
		||||
            val cachedTitle = titleCache?.get(titleId)
 | 
			
		||||
@ -173,7 +181,7 @@ class MangaPlus(
 | 
			
		||||
                val titleResult = client.newCall(titleRequest).execute().asMangaPlusResponse()
 | 
			
		||||
 | 
			
		||||
                checkNotNull(titleResult.success) {
 | 
			
		||||
                    titleResult.error!!.langPopup(langCode)?.body ?: intl.unknownError
 | 
			
		||||
                    titleResult.error!!.langPopup(langCode)?.body ?: intl["unknown_error"]
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                titleResult.success.titleDetailView!!
 | 
			
		||||
@ -222,15 +230,15 @@ class MangaPlus(
 | 
			
		||||
            val error = result.error!!.langPopup(langCode)
 | 
			
		||||
 | 
			
		||||
            when {
 | 
			
		||||
                error?.subject == NOT_FOUND_SUBJECT -> intl.titleRemoved
 | 
			
		||||
                error?.subject == NOT_FOUND_SUBJECT -> intl["title_removed"]
 | 
			
		||||
                !error?.body.isNullOrEmpty() -> error!!.body
 | 
			
		||||
                else -> intl.unknownError
 | 
			
		||||
                else -> intl["unknown_error"]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val titleDetails = result.success.titleDetailView!!
 | 
			
		||||
            .takeIf { it.title.language == langCode }
 | 
			
		||||
            ?: throw Exception(intl.notAvailable)
 | 
			
		||||
            ?: throw Exception(intl["not_available"])
 | 
			
		||||
 | 
			
		||||
        return titleDetails.toSManga()
 | 
			
		||||
    }
 | 
			
		||||
@ -244,9 +252,9 @@ class MangaPlus(
 | 
			
		||||
            val error = result.error!!.langPopup(langCode)
 | 
			
		||||
 | 
			
		||||
            when {
 | 
			
		||||
                error?.subject == NOT_FOUND_SUBJECT -> intl.titleRemoved
 | 
			
		||||
                error?.subject == NOT_FOUND_SUBJECT -> intl["title_removed"]
 | 
			
		||||
                !error?.body.isNullOrEmpty() -> error!!.body
 | 
			
		||||
                else -> intl.unknownError
 | 
			
		||||
                else -> intl["unknown_error"]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -290,9 +298,9 @@ class MangaPlus(
 | 
			
		||||
            val error = result.error!!.langPopup(langCode)
 | 
			
		||||
 | 
			
		||||
            when {
 | 
			
		||||
                error?.subject == NOT_FOUND_SUBJECT -> intl.chapterExpired
 | 
			
		||||
                error?.subject == NOT_FOUND_SUBJECT -> intl["chapter_expired"]
 | 
			
		||||
                !error?.body.isNullOrEmpty() -> error!!.body
 | 
			
		||||
                else -> intl.unknownError
 | 
			
		||||
                else -> intl["unknown_error"]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -322,8 +330,12 @@ class MangaPlus(
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) {
 | 
			
		||||
        val qualityPref = ListPreference(screen.context).apply {
 | 
			
		||||
            key = "${QUALITY_PREF_KEY}_$lang"
 | 
			
		||||
            title = intl.imageQuality
 | 
			
		||||
            entries = arrayOf(intl.imageQualityLow, intl.imageQualityMedium, intl.imageQualityHigh)
 | 
			
		||||
            title = intl["image_quality"]
 | 
			
		||||
            entries = arrayOf(
 | 
			
		||||
                intl["image_quality_low"],
 | 
			
		||||
                intl["image_quality_medium"],
 | 
			
		||||
                intl["image_quality_high"],
 | 
			
		||||
            )
 | 
			
		||||
            entryValues = QUALITY_PREF_ENTRY_VALUES
 | 
			
		||||
            setDefaultValue(QUALITY_PREF_DEFAULT_VALUE)
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
@ -331,8 +343,8 @@ class MangaPlus(
 | 
			
		||||
 | 
			
		||||
        val splitPref = SwitchPreferenceCompat(screen.context).apply {
 | 
			
		||||
            key = "${SPLIT_PREF_KEY}_$lang"
 | 
			
		||||
            title = intl.splitDoublePages
 | 
			
		||||
            summary = intl.splitDoublePagesSummary
 | 
			
		||||
            title = intl["split_double_pages"]
 | 
			
		||||
            summary = intl["split_double_pages_summary"]
 | 
			
		||||
            setDefaultValue(SPLIT_PREF_DEFAULT_VALUE)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,54 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.all.mangaplus
 | 
			
		||||
 | 
			
		||||
class MangaPlusIntl(lang: Language) {
 | 
			
		||||
 | 
			
		||||
    val imageQuality: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Qualidade da imagem"
 | 
			
		||||
        else -> "Image quality"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val imageQualityLow: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Baixa"
 | 
			
		||||
        else -> "Low"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val imageQualityMedium: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Média"
 | 
			
		||||
        else -> "Medium"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val imageQualityHigh: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Alta"
 | 
			
		||||
        else -> "High"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val splitDoublePages: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Dividir as páginas duplas"
 | 
			
		||||
        else -> "Split double pages"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val splitDoublePagesSummary: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Somente poucos títulos suportam a desativação desta configuração."
 | 
			
		||||
        else -> "Only a few titles supports disabling this setting."
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val chapterExpired: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "O período de leitura do capítulo expirou."
 | 
			
		||||
        else -> "The chapter reading period has expired."
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val notAvailable: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Título não disponível neste idioma."
 | 
			
		||||
        else -> "Title not available in this language."
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val unknownError: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Um erro desconhecido ocorreu."
 | 
			
		||||
        else -> "An unknown error happened."
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val titleRemoved: String = when (lang) {
 | 
			
		||||
        Language.PORTUGUESE_BR -> "Este título foi removido do catálogo do MANGA Plus."
 | 
			
		||||
        else -> "This title was removed from the MANGA Plus catalogue."
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user