forked from bestwpw/tachiyomi-extensions
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a new
lib-i18n
to make message translation easier (#16942)
* 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.
- Loading branch information
1 parent
c4b08d0
commit 4e17c22
Showing
20 changed files
with
910 additions
and
1,303 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="eu.kanade.tachiyomi.lib.i18n" /> |
83 changes: 83 additions & 0 deletions
83
lib/i18n/src/main/java/eu/kanade/tachiyomi/lib/i18n/Intl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 at the `/res/raw` 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 "res/raw/messages_$langSnakeCase.properties" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
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_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 |
Oops, something went wrong.