add "Meet the Translators" page

This commit is contained in:
AmirHossein Abdolmotallebi 2024-11-11 02:45:29 +03:30
parent d0c6ae5806
commit db0ad87734
24 changed files with 735 additions and 147 deletions

View File

@ -200,6 +200,12 @@ buildConfig {
"https://github.com/amir1376/ab-download-manager"
}
)
buildConfigField(
"PROJECT_TRANSLATIONS",
provider {
"https://crowdin.com/project/ab-download-manager"
}
)
buildConfigField(
"INTEGRATION_CHROME_LINK",
provider {

View File

@ -743,6 +743,14 @@ class AppComponent(
showOpenSourceLibraries.update { false }
}
fun openTranslatorsPage() {
showTranslators.update { true }
}
fun closeTranslatorsPage() {
showTranslators.update { false }
}
fun openQueues() {
scope.launch {
showQueuesSlot.value.child?.instance.let {
@ -820,6 +828,7 @@ class AppComponent(
// val updater = UpdateComponent(childContext("updater"))
val showAboutPage = MutableStateFlow(false)
val showOpenSourceLibraries = MutableStateFlow(false)
val showTranslators = MutableStateFlow(false)
val theme = appRepository.theme
// val uiScale = appRepository.uiScale
}

View File

@ -8,6 +8,7 @@ interface BaseConstants{
val packageName:String
val projectWebsite:String
val projectSourceCode:String
val projectTranslations: String
val browserIntegrations:List<BrowserIntegrationModel>
val telegramGroupUrl:String
val telegramChannelUrl:String
@ -17,6 +18,7 @@ object SharedConstants:BaseConstants{
override val appName: String = BuildConfig.APP_NAME
override val packageName: String = BuildConfig.PACKAGE_NAME
override val projectWebsite: String= BuildConfig.PROJECT_WEBSITE
override val projectTranslations: String = BuildConfig.PROJECT_TRANSLATIONS
override val projectSourceCode: String= BuildConfig.PROJECT_SOURCE_CODE
override val browserIntegrations: List<BrowserIntegrationModel> = listOf(
BrowserIntegrationModel(

View File

@ -167,6 +167,12 @@ val openOpenSourceThirdPartyLibraries = simpleAction(
) {
appComponent.openOpenSourceLibraries()
}
val openTranslators = simpleAction(
title = Res.string.meet_the_translators.asStringSource(),
icon = MyIcons.language,
) {
appComponent.openTranslatorsPage()
}
val supportActionGroup = MenuItem.SubMenu(
title = Res.string.support_and_community.asStringSource(),

View File

@ -22,6 +22,9 @@ fun ShowAboutDialog(appComponent: AppComponent) {
},
onRequestShowOpenSourceLibraries = {
appComponent.openOpenSourceLibraries()
},
onRequestShowTranslators = {
appComponent.openTranslatorsPage()
}
)
}
@ -31,19 +34,21 @@ fun ShowAboutDialog(appComponent: AppComponent) {
fun AboutDialog(
onClose: () -> Unit,
onRequestShowOpenSourceLibraries: () -> Unit,
onRequestShowTranslators: () -> Unit,
) {
CustomWindow(
resizable = false,
onRequestToggleMaximize = null,
state = rememberWindowState(
size = DpSize(400.dp, 300.dp)
size = DpSize(400.dp, 330.dp)
),
onCloseRequest = onClose
) {
WindowTitle(myStringResource(Res.string.about))
AboutPage(
close = onClose,
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries,
onRequestShowTranslators = onRequestShowTranslators
)
}
}

View File

@ -40,10 +40,12 @@ import ir.amirab.util.compose.resources.myStringResource
fun AboutPage(
close: () -> Unit,
onRequestShowOpenSourceLibraries: () -> Unit,
onRequestShowTranslators: () -> Unit,
) {
Column(Modifier.padding(16.dp)) {
RenderAppInfo(
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries
onRequestShowOpenSourceLibraries = onRequestShowOpenSourceLibraries,
onRequestShowTranslators = onRequestShowTranslators,
)
Spacer(Modifier.weight(1f))
Row(Modifier.fillMaxWidth().wrapContentWidth(Alignment.End)) {
@ -58,6 +60,7 @@ fun AboutPage(
@Composable
fun RenderAppInfo(
onRequestShowOpenSourceLibraries: () -> Unit,
onRequestShowTranslators: () -> Unit,
) {
Row(
Modifier.fillMaxWidth()
@ -114,6 +117,15 @@ fun RenderAppInfo(
onRequestShowOpenSourceLibraries()
}
)
Spacer(Modifier.height(8.dp))
Text(myStringResource(Res.string.localized_by_translators))
Text(
myStringResource(Res.string.meet_the_translators),
style = LocalTextStyle.current.merge(LinkStyle),
modifier = Modifier.clickable {
onRequestShowTranslators()
}
)
}
}
}

View File

@ -75,7 +75,7 @@ fun AddMultiDownloadTable(
content = it
)
},
wrapItem = { item, content ->
wrapItem = { _, item, content ->
val shape = RoundedCornerShape(12.dp)
WithContentAlpha(1f) {
val isSelected = remember(item, component.selectionList) {

View File

@ -0,0 +1,21 @@
package com.abdownloadmanager.desktop.pages.credits.translators
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
data class LanguageTranslationInfo(
val locale: String,// en,es_ES etc...
val englishName: String,//Persian etc...
val nativeName: String,//فارسی ...
val translators: List<Translator>,
)
typealias TranslatorData = @Serializable Map<String, List<Translator>>
@Serializable
data class Translator(
@SerialName("name")
val name: String,
@SerialName("link")
val link: String,
)

View File

@ -0,0 +1,281 @@
package com.abdownloadmanager.desktop.pages.credits.translators
import androidx.compose.foundation.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.abdownloadmanager.desktop.di.Di
import com.abdownloadmanager.desktop.pages.about.MaybeLinkText
import com.abdownloadmanager.desktop.ui.theme.LocalMyColors
import com.abdownloadmanager.desktop.ui.theme.myColors
import com.abdownloadmanager.desktop.ui.theme.myTextSizes
import com.abdownloadmanager.desktop.ui.widget.ActionButton
import com.abdownloadmanager.desktop.ui.widget.Text
import com.abdownloadmanager.desktop.ui.widget.customtable.Table
import com.abdownloadmanager.desktop.ui.widget.customtable.TableState
import com.abdownloadmanager.desktop.ui.widget.customtable.styled.MyStyledTableHeader
import com.abdownloadmanager.desktop.utils.AppInfo
import com.abdownloadmanager.desktop.utils.div
import com.abdownloadmanager.resources.Res
import com.abdownloadmanager.utils.compose.LocalContentColor
import com.abdownloadmanager.utils.compose.WithContentAlpha
import ir.amirab.util.UrlUtils
import ir.amirab.util.compose.localizationmanager.LanguageNameProvider
import ir.amirab.util.compose.localizationmanager.MyLocale
import ir.amirab.util.compose.resources.myStringResource
import kotlinx.serialization.json.Json
import okio.FileSystem
import okio.Path.Companion.toPath
import okio.buffer
import org.koin.core.component.get
@Composable
internal fun Translators(modifier: Modifier) {
Column(
modifier
) {
TranslatorsTable(
Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp)
.weight(1f)
)
ContributionNotice(
modifier = Modifier,
onUserWantsToContribute = {
UrlUtils.openUrl(AppInfo.translationsUrl)
}
)
}
}
@Composable
private fun ContributionNotice(
modifier: Modifier,
onUserWantsToContribute: () -> Unit,
) {
Column(modifier) {
Spacer(
Modifier
.fillMaxWidth()
.height(1.dp)
.background(myColors.onBackground / 0.15f)
)
Column(
Modifier
.fillMaxWidth()
.background(myColors.surface / 0.5f)
.padding(horizontal = 32.dp)
.padding(vertical = 16.dp),
) {
Text(
myStringResource(Res.string.translators_page_thanks),
modifier = Modifier,
fontSize = myTextSizes.lg,
fontWeight = FontWeight.Bold,
)
Spacer(
Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.height(1.dp)
.background(myColors.surface)
)
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Column(
Modifier.weight(1f)
) {
Text(
myStringResource(Res.string.translators_contribute_title),
fontSize = myTextSizes.lg,
fontWeight = FontWeight.Bold,
)
Spacer(Modifier.height(4.dp))
Text(
myStringResource(Res.string.translators_contribute_description),
fontSize = myTextSizes.base,
color = LocalContentColor.current / 0.75f
)
}
Spacer(Modifier.width(32.dp))
PrimaryMainConfigActionButton(
text = myStringResource(Res.string.contribute),
onClick = onUserWantsToContribute,
modifier = Modifier,
enabled = true,
)
}
}
}
}
@Composable
private fun PrimaryMainConfigActionButton(
text: String,
modifier: Modifier,
enabled: Boolean,
onClick: () -> Unit,
) {
val backgroundColor = Brush.horizontalGradient(
myColors.primaryGradientColors.map {
it / 30
}
)
val borderColor = Brush.horizontalGradient(
myColors.primaryGradientColors
)
val disabledBorderColor = Brush.horizontalGradient(
myColors.primaryGradientColors.map {
it / 50
}
)
ActionButton(
text = text,
modifier = modifier,
enabled = enabled,
onClick = onClick,
backgroundColor = backgroundColor,
disabledBackgroundColor = backgroundColor,
borderColor = borderColor,
disabledBorderColor = disabledBorderColor,
)
}
@Composable
private fun TranslatorsTable(
modifier: Modifier,
) {
val tableState = remember {
TableState(
cells = TranslatorsCells.all()
)
}
val itemHorizontalPadding = 16.dp
Table(
modifier = modifier,
list = rememberLanguageTranslationInfo(),
state = rememberLazyListState(),
tableState = tableState,
wrapHeader = {
MyStyledTableHeader(
itemHorizontalPadding = itemHorizontalPadding,
content = it,
)
},
wrapItem = { index, _, rowContent ->
val interactionSource = remember { MutableInteractionSource() }
Box(
Modifier
.widthIn(getTableSize().visibleWidth)
.hoverable(interactionSource)
.indication(
interactionSource,
LocalIndication.current,
)
.background(
if (index % 2 == 0) Color.Transparent else myColors.surface / 0.35f
)
.padding(vertical = 12.dp, horizontal = itemHorizontalPadding)
) {
rowContent()
}
},
renderCell = { libraryCell, translationInfo ->
when (libraryCell) {
TranslatorsCells.LanguageName -> {
Column {
WithContentAlpha(1f) {
Text(
translationInfo.nativeName,
fontSize = myTextSizes.base,
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
}
Spacer(Modifier.height(4.dp))
WithContentAlpha(0.75f) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
translationInfo.englishName,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
fontSize = myTextSizes.base,
)
Spacer(Modifier.width(4.dp))
Text(
translationInfo.locale,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
fontSize = myTextSizes.base,
color = myColors.primary,
modifier = Modifier
.background(myColors.primary / 10)
.padding(vertical = 0.dp, horizontal = 4.dp)
)
}
}
}
}
TranslatorsCells.Translators -> {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
translationInfo.translators.forEach {
MaybeLinkText(
it.name,
it.link,
)
}
}
}
}
},
)
}
private fun convertLanguageToMyLocale(language: String): MyLocale {
return language.split("-").run {
MyLocale(
languageCode = get(0),
countryCode = getOrNull(1)
)
}
}
@Composable
private fun rememberLanguageTranslationInfo(): List<LanguageTranslationInfo> {
return remember {
val json = Di.get<Json>()
val translatorData = FileSystem.RESOURCES.source(
"/com/abdownloadmanager/resources/credits/translators.json".toPath()
).buffer().readUtf8().let {
json.decodeFromString<TranslatorData>(it)
}
translatorData.map {
val name = LanguageNameProvider.getName(convertLanguageToMyLocale(it.key))
LanguageTranslationInfo(
locale = it.key,
englishName = name.englishName,
nativeName = name.nativeName,
translators = it.value,
)
}
}
}

View File

@ -0,0 +1,32 @@
package com.abdownloadmanager.desktop.pages.credits.translators
import com.abdownloadmanager.desktop.ui.widget.customtable.CellSize
import com.abdownloadmanager.desktop.ui.widget.customtable.SortableCell
import com.abdownloadmanager.desktop.ui.widget.customtable.TableCell
import androidx.compose.ui.unit.dp
import com.abdownloadmanager.resources.Res
import ir.amirab.util.compose.StringSource
import ir.amirab.util.compose.asStringSource
sealed interface TranslatorsCells : TableCell<LanguageTranslationInfo> {
data object LanguageName : TranslatorsCells,
SortableCell<LanguageTranslationInfo> {
override fun sortBy(item: LanguageTranslationInfo): Comparable<*> = item.locale
override val id: String = "language"
override val name: StringSource = Res.string.language.asStringSource()
override val size: CellSize = CellSize.Resizeable(100.dp..1000.dp, 200.dp)
}
data object Translators : TranslatorsCells {
override val id: String = "translators"
override val name: StringSource = Res.string.translators.asStringSource()
override val size: CellSize = CellSize.Resizeable(100.dp..1000.dp, 350.dp)
}
companion object {
fun all() = listOf(
LanguageName,
Translators,
)
}
}

View File

@ -0,0 +1,46 @@
package com.abdownloadmanager.desktop.pages.credits.translators
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.rememberWindowState
import com.abdownloadmanager.desktop.AppComponent
import com.abdownloadmanager.desktop.ui.customwindow.CustomWindow
import com.abdownloadmanager.desktop.ui.customwindow.WindowTitle
import com.abdownloadmanager.resources.Res
import ir.amirab.util.compose.resources.myStringResource
@Composable
fun ShowTranslators(
appComponent: AppComponent,
) {
TranslatorsWindow(
isVisible = appComponent.showTranslators.collectAsState().value,
onRequestClose = {
appComponent.closeTranslatorsPage()
}
)
}
@Composable
private fun TranslatorsWindow(
isVisible: Boolean,
onRequestClose: () -> Unit,
) {
if (!isVisible) return
CustomWindow(
onCloseRequest = onRequestClose,
state = rememberWindowState(
size = DpSize(650.dp, 500.dp)
)
) {
WindowTitle(myStringResource(Res.string.meet_the_translators))
Translators(
modifier = Modifier.fillMaxSize(),
)
}
}

View File

@ -61,7 +61,7 @@ private fun OpenSourceLibraries(
content = it,
)
},
wrapItem = { item, rowContent ->
wrapItem = { _, item, rowContent ->
Box(Modifier
.clickable {
currentDialog = item

View File

@ -563,6 +563,7 @@ class HomeComponent(
+supportActionGroup
separator()
+openOpenSourceThirdPartyLibraries
+openTranslators
+openAboutAction
}
}.filterIsInstance<MenuItem.SubMenu>()

View File

@ -154,7 +154,7 @@ fun DownloadList(
wrapHeader = {
MyStyledTableHeader(itemHorizontalPadding = itemHorizontalPadding, content = it)
},
wrapItem = { item, rowContent ->
wrapItem = { _, item, rowContent ->
val isSelected = selectionList.contains(item.id)
var shouldWaitForSecondClick by remember {
mutableStateOf(false)

View File

@ -130,8 +130,8 @@ fun SingleDownloadPage(singleDownloadComponent: SingleDownloadComponent) {
Spacer(Modifier.size(8.dp))
}
val resizingState = LocalSingleDownloadPageSizing.current
LaunchedEffect(resizingState.resizingPartInfo){
if (resizingState.partInfoHeight<=0.dp){
LaunchedEffect(resizingState.resizingPartInfo) {
if (resizingState.partInfoHeight <= 0.dp) {
setShowPartInfo(false)
}
}
@ -317,7 +317,7 @@ fun ColumnScope.RenderPartInfo(itemState: ProcessingDownloadItemState) {
cells = PartInfoCells.all()
)
},
wrapItem = { _, content ->
wrapItem = { _, _, content ->
WithContentAlpha(1f) {
val interactionSource = remember { MutableInteractionSource() }
val isHovered by interactionSource.collectIsHoveredAsState()
@ -375,7 +375,7 @@ fun ColumnScope.RenderPartInfo(itemState: ProcessingDownloadItemState) {
val singleDownloadPageSizing = LocalSingleDownloadPageSizing.current
val mutableInteractionSource = remember { MutableInteractionSource() }
val isDraggingHandle by mutableInteractionSource.collectIsDraggedAsState()
LaunchedEffect(isDraggingHandle){
LaunchedEffect(isDraggingHandle) {
singleDownloadPageSizing.resizingPartInfo = isDraggingHandle
}
Handle(

View File

@ -29,6 +29,7 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.window.*
import com.abdownloadmanager.desktop.pages.batchdownload.BatchDownloadWindow
import com.abdownloadmanager.desktop.pages.category.ShowCategoryDialogs
import com.abdownloadmanager.desktop.pages.credits.translators.ShowTranslators
import com.abdownloadmanager.desktop.pages.editdownload.EditDownloadWindow
import com.abdownloadmanager.desktop.pages.home.HomeWindow
import com.abdownloadmanager.desktop.pages.settings.ThemeManager
@ -98,6 +99,7 @@ object Ui : KoinComponent {
NewQueueDialog(appComponent)
ShowMessageDialogs(appComponent)
ShowOpenSourceLibraries(appComponent)
ShowTranslators(appComponent)
}
}
}

View File

@ -89,6 +89,7 @@ override val back get() = "/icons/back.svg".asIconSource()
override val appearance: IconSource get() = "/icons/color.svg".asIconSource()
override val downloadEngine: IconSource get() = "/icons/down_speed.svg".asIconSource()
override val network: IconSource get() = "/icons/network.svg".asIconSource()
override val language: IconSource get() = "/icons/language.svg".asIconSource()
override val externalLink: IconSource get() = "/icons/external_link.svg".asIconSource()
}

View File

@ -53,8 +53,8 @@ fun <T, C : TableCell<T>> Table(
renderHeaderCell: @Composable (C) -> Unit = { DefaultRenderHeader(it) },
drawOnEmpty: @Composable BoxScope.() -> Unit = {},
wrapHeader: @Composable TableScope.(rowContent: @Composable () -> Unit) -> Unit = { content -> content() },
wrapItem: @Composable TableScope.(item: T, rowContent: @Composable () -> Unit) -> Unit = { _, content -> content() },
renderCell: @Composable TableScope.(C, T) -> Unit
wrapItem: @Composable TableScope.(index: Int, item: T, rowContent: @Composable () -> Unit) -> Unit = { _, _, content -> content() },
renderCell: @Composable TableScope.(C, T) -> Unit,
) {
val scope = TableScope
@ -151,8 +151,11 @@ fun <T, C : TableCell<T>> Table(
.fillMaxHeight(),
state = state,
) {
items(sortedList, key = key) { item ->
scope.wrapItem(item) {
itemsIndexed(
sortedList,
key = if (key != null) { _, item -> key(item) } else null
) { index, item ->
scope.wrapItem(index, item) {
Row(
verticalAlignment = Alignment.CenterVertically
) {

View File

@ -10,6 +10,7 @@ object AppInfo {
val packageName = SharedConstants.packageName
val website = SharedConstants.projectWebsite
val sourceCode = SharedConstants.projectSourceCode
val translationsUrl = SharedConstants.projectTranslations
val version = AppVersion.get()

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.87 15.07L10.33 12.56L10.36 12.53C12.0547 10.6475 13.3205 8.41951 14.07 6H17V4H10V2H8V4H1V6H12.17C11.5 7.92 10.44 9.75 9 11.35C8.07 10.32 7.3 9.19 6.69 8H4.69C5.42 9.63 6.42 11.17 7.67 12.56L2.58 17.58L4 19L9 14L12.11 17.11L12.87 15.07ZM18.5 10H16.5L12 22H14L15.12 19H19.87L21 22H23L18.5 10ZM15.88 17L17.5 12.67L19.12 17H15.88Z"
fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 473 B

View File

@ -77,5 +77,6 @@ interface IMyIcons {
val downloadEngine: IconSource
val browserIntegration: IconSource
val network: IconSource
val language: IconSource
val externalLink: IconSource
}

View File

@ -4,143 +4,158 @@ import java.util.Locale
interface ILanguageNameProvider {
fun getNativeName(myLocale: MyLocale): String
fun getEnglishName(myLocale: MyLocale): String
fun getName(myLocale: MyLocale): LanguageName
}
data class LanguageName(
val nativeName: String,
val englishName: String,
)
object LanguageNameProvider : ILanguageNameProvider {
private val list = mapOf(
"af" to "Afrikaans",
"ak" to "Akan",
"am" to "አማርኛ",
"ar" to "العربية",
"as" to "অসমীয়া",
"az" to "azərbaycan",
"be" to "беларуская",
"bg" to "български",
"bm" to "bamanakan",
"bn" to "বাংলা",
"bo" to "བོད་སྐད་",
"br" to "brezhoneg",
"bs" to "bosanski",
"ca" to "català",
"cs" to "čeština",
"cy" to "Cymraeg",
"da" to "dansk",
"de" to "Deutsch",
"de_AT" to "Österreichisches Deutsch",
"de_CH" to "Schweizer Hochdeutsch",
"dz" to "རྫོང་ཁ",
"ee" to "eʋegbe",
"el" to "Ελληνικά",
"en" to "English",
"eo" to "esperanto",
"es" to "español",
"et" to "eesti",
"eu" to "euskara",
"fa" to "فارسی",
"ff" to "Pulaar",
"fi" to "suomi",
"fo" to "føroyskt",
"fr" to "français",
"fr_CA" to "français canadien",
"fr_CH" to "français suisse",
"fy" to "West-Frysk",
"ga" to "Gaeilge",
"gd" to "Gàidhlig",
"gl" to "galego",
"gu" to "ગુજરાતી",
"gv" to "Gaelg",
"ha" to "Hausa",
"he" to "עברית",
"hi" to "हिंदी",
"hr" to "hrvatski",
"hu" to "magyar",
"hy" to "հայերեն",
"id" to "Bahasa Indonesia",
"ig" to "Igbo",
"ii" to "ꆈꌠꉙ",
"is" to "íslenska",
"it" to "italiano",
"ja" to "日本語",
"ka" to "ქართული",
"ki" to "Gikuyu",
"kk" to "қазақ тілі",
"kl" to "kalaallisut",
"km" to "ខ្មែរ",
"kn" to "ಕನ್ನಡ",
"ko" to "한국어",
"ks" to "کٲشُر",
"kw" to "kernewek",
"ky" to "кыргызча",
"lb" to "Lëtzebuergesch",
"lg" to "Luganda",
"ln" to "lingála",
"lo" to "ລາວ",
"lt" to "lietuvių",
"lu" to "Tshiluba",
"lv" to "latviešu",
"mg" to "Malagasy",
"mk" to "македонски",
"ml" to "മലയാളം",
"mn" to "монгол",
"mr" to "मराठी",
"ms" to "Bahasa Melayu",
"mt" to "Malti",
"my" to "ဗမာ",
"nb" to "norsk bokmål",
"nd" to "isiNdebele",
"ne" to "नेपाली",
"nl" to "Nederlands",
"nl_BE" to "Vlaams",
"nn" to "nynorsk",
"no" to "norsk",
"om" to "Oromoo",
"or" to "ଓଡ଼ିଆ",
"os" to "ирон",
"pa" to "ਪੰਜਾਬੀ",
"pl" to "polski",
"ps" to "پښتو",
"pt" to "português",
"pt_BR" to "português do Brasil",
"qu" to "Runasimi",
"rm" to "rumantsch",
"rn" to "Ikirundi",
"ro" to "română",
"ro_MD" to "moldovenească",
"ru" to "русский",
"rw" to "Kinyarwanda",
"se" to "davvisámegiella",
"sg" to "Sängö",
"sh" to "Srpskohrvatski",
"si" to "සිංහල",
"sk" to "slovenčina",
"sl" to "slovenščina",
"sn" to "chiShona",
"so" to "Soomaali",
"sq" to "shqip",
"sr" to "српски",
"sv" to "svenska",
"sw" to "Kiswahili",
"ta" to "தமிழ்",
"te" to "తెలుగు",
"th" to "ไทย",
"ti" to "ትግርኛ",
"tl" to "Tagalog",
"to" to "lea fakatonga",
"tr" to "Türkçe",
"ug" to "ئۇيغۇرچە",
"uk" to "українська",
"ur" to "اردو",
"uz" to "oʻzbekcha",
"vi" to "Tiếng Việt",
"yi" to "ייִדיש",
"yo" to "Èdè Yorùbá",
"zh" to "中文",
"zh_CN" to "简体中文",
"zh_TW" to "正體中文",
"zu" to "isiZulu",
"af" to LanguageName("Afrikaans", "Afrikaans"),
"ak" to LanguageName("Akan", "Akan"),
"am" to LanguageName("አማርኛ", "Amharic"),
"ar" to LanguageName("العربية", "Arabic"),
"as" to LanguageName("অসমীয়া", "Assamese"),
"az" to LanguageName("azərbaycan", "Azerbaijani"),
"be" to LanguageName("беларуская", "Belarusian"),
"bg" to LanguageName("български", "Bulgarian"),
"bm" to LanguageName("bamanakan", "Bambara"),
"bn" to LanguageName("বাংলা", "Bengali"),
"bo" to LanguageName("བོད་སྐད་", "Tibetan"),
"br" to LanguageName("brezhoneg", "Breton"),
"bs" to LanguageName("bosanski", "Bosnian"),
"ca" to LanguageName("català", "Catalan"),
"cs" to LanguageName("čeština", "Czech"),
"cy" to LanguageName("Cymraeg", "Welsh"),
"da" to LanguageName("dansk", "Danish"),
"de" to LanguageName("Deutsch", "German"),
"de_AT" to LanguageName("Österreichisches Deutsch", "Austrian German"),
"de_CH" to LanguageName("Schweizer Hochdeutsch", "Swiss German"),
"dz" to LanguageName("རྫོང་ཁ", "Dzongkha"),
"ee" to LanguageName("eʋegbe", "Ewe"),
"el" to LanguageName("Ελληνικά", "Greek"),
"en" to LanguageName("English", "English"),
"eo" to LanguageName("esperanto", "Esperanto"),
"es" to LanguageName("español", "Spanish"),
"et" to LanguageName("eesti", "Estonian"),
"eu" to LanguageName("euskara", "Basque"),
"fa" to LanguageName("فارسی", "Persian"),
"ff" to LanguageName("Pulaar", "Fulah"),
"fi" to LanguageName("suomi", "Finnish"),
"fo" to LanguageName("føroyskt", "Faroese"),
"fr" to LanguageName("français", "French"),
"fr_CA" to LanguageName("français canadien", "Canadian French"),
"fr_CH" to LanguageName("français suisse", "Swiss French"),
"fy" to LanguageName("West-Frysk", "Western Frisian"),
"ga" to LanguageName("Gaeilge", "Irish"),
"gd" to LanguageName("Gàidhlig", "Scottish Gaelic"),
"gl" to LanguageName("galego", "Galician"),
"gu" to LanguageName("ગુજરાતી", "Gujarati"),
"gv" to LanguageName("Gaelg", "Manx"),
"ha" to LanguageName("Hausa", "Hausa"),
"he" to LanguageName("עברית", "Hebrew"),
"hi" to LanguageName("हिंदी", "Hindi"),
"hr" to LanguageName("hrvatski", "Croatian"),
"hu" to LanguageName("magyar", "Hungarian"),
"hy" to LanguageName("հայերեն", "Armenian"),
"id" to LanguageName("Bahasa Indonesia", "Indonesian"),
"ig" to LanguageName("Igbo", "Igbo"),
"ii" to LanguageName("ꆈꌠꉙ", "Sichuan Yi"),
"is" to LanguageName("íslenska", "Icelandic"),
"it" to LanguageName("italiano", "Italian"),
"ja" to LanguageName("日本語", "Japanese"),
"ka" to LanguageName("ქართული", "Georgian"),
"ki" to LanguageName("Gikuyu", "Kikuyu"),
"kk" to LanguageName("қазақ тілі", "Kazakh"),
"kl" to LanguageName("kalaallisut", "Greenlandic"),
"km" to LanguageName("ខ្មែរ", "Khmer"),
"kn" to LanguageName("ಕನ್ನಡ", "Kannada"),
"ko" to LanguageName("한국어", "Korean"),
"ks" to LanguageName("کٲشُر", "Kashmiri"),
"kw" to LanguageName("kernewek", "Cornish"),
"ky" to LanguageName("кыргызча", "Kyrgyz"),
"lb" to LanguageName("Lëtzebuergesch", "Luxembourgish"),
"lg" to LanguageName("Luganda", "Ganda"),
"ln" to LanguageName("lingála", "Lingala"),
"lo" to LanguageName("ລາວ", "Lao"),
"lt" to LanguageName("lietuvių", "Lithuanian"),
"lu" to LanguageName("Tshiluba", "Luba-Katanga"),
"lv" to LanguageName("latviešu", "Latvian"),
"mg" to LanguageName("Malagasy", "Malagasy"),
"mk" to LanguageName("македонски", "Macedonian"),
"ml" to LanguageName("മലയാളം", "Malayalam"),
"mn" to LanguageName("монгол", "Mongolian"),
"mr" to LanguageName("मराठी", "Marathi"),
"ms" to LanguageName("Bahasa Melayu", "Malay"),
"mt" to LanguageName("Malti", "Maltese"),
"my" to LanguageName("ဗမာ", "Burmese"),
"nb" to LanguageName("norsk bokmål", "Norwegian Bokmål"),
"nd" to LanguageName("isiNdebele", "North Ndebele"),
"ne" to LanguageName("नेपाली", "Nepali"),
"nl" to LanguageName("Nederlands", "Dutch"),
"nl_BE" to LanguageName("Vlaams", "Flemish"),
"nn" to LanguageName("nynorsk", "Norwegian Nynorsk"),
"no" to LanguageName("norsk", "Norwegian"),
"om" to LanguageName("Oromoo", "Oromo"),
"or" to LanguageName("ଓଡ଼ିଆ", "Odia"),
"os" to LanguageName("ирон", "Ossetic"),
"pa" to LanguageName("ਪੰਜਾਬੀ", "Punjabi"),
"pl" to LanguageName("polski", "Polish"),
"ps" to LanguageName("پښتو", "Pashto"),
"pt" to LanguageName("português", "Portuguese"),
"pt_BR" to LanguageName("português do Brasil", "Brazilian Portuguese"),
"qu" to LanguageName("Runasimi", "Quechua"),
"rm" to LanguageName("rumantsch", "Romansh"),
"rn" to LanguageName("Ikirundi", "Rundi"),
"ro" to LanguageName("română", "Romanian"),
"ro_MD" to LanguageName("moldovenească", "Moldovan"),
"ru" to LanguageName("русский", "Russian"),
"rw" to LanguageName("Kinyarwanda", "Kinyarwanda"),
"se" to LanguageName("davvisámegiella", "Northern Sami"),
"sg" to LanguageName("Sängö", "Sango"),
"sh" to LanguageName("Srpskohrvatski", "Serbo-Croatian"),
"si" to LanguageName("සිංහල", "Sinhala"),
"sk" to LanguageName("slovenčina", "Slovak"),
"sl" to LanguageName("slovenščina", "Slovene"),
"sn" to LanguageName("chiShona", "Shona"),
"so" to LanguageName("Soomaali", "Somali"),
"sq" to LanguageName("shqip", "Albanian"),
"sr" to LanguageName("српски", "Serbian"),
"sv" to LanguageName("svenska", "Swedish"),
"sw" to LanguageName("Kiswahili", "Swahili"),
"ta" to LanguageName("தமிழ்", "Tamil"),
"te" to LanguageName("తెలుగు", "Telugu"),
"th" to LanguageName("ไทย", "Thai"),
"ti" to LanguageName("ትግርኛ", "Tigrinya"),
"tl" to LanguageName("Tagalog", "Tagalog"),
"to" to LanguageName("lea fakatonga", "Tongan"),
"tr" to LanguageName("Türkçe", "Turkish"),
"ug" to LanguageName("ئۇيغۇرچە", "Uyghur"),
"uk" to LanguageName("українська", "Ukrainian"),
"ur" to LanguageName("اردو", "Urdu"),
"uz" to LanguageName("oʻzbekcha", "Uzbek"),
"vi" to LanguageName("Tiếng Việt", "Vietnamese"),
"yi" to LanguageName("ייִדיש", "Yiddish"),
"yo" to LanguageName("Èdè Yorùbá", "Yoruba"),
"zh" to LanguageName("中文", "Chinese"),
"zh_CN" to LanguageName("简体中文", "Simplified Chinese"),
"zh_TW" to LanguageName("正體中文", "Traditional Chinese"),
"zu" to LanguageName("isiZulu", "Zulu")
)
override fun getNativeName(myLocale: MyLocale): String {
return getName(myLocale).nativeName
}
override fun getEnglishName(myLocale: MyLocale): String {
return getName(myLocale).englishName
}
override fun getName(myLocale: MyLocale): LanguageName {
val languageCode = myLocale.languageCode
val countryCode = myLocale.countryCode
if (countryCode != null) {
@ -151,9 +166,9 @@ object LanguageNameProvider : ILanguageNameProvider {
list[languageCode]?.let {
return it
}
return default(myLocale)
return default(myLocale).let { LanguageName(it, it) }
}
private fun default(myLocale: MyLocale): String {
return myLocale
.toLocale()

View File

@ -0,0 +1,132 @@
{
"ar": [
{
"name": "Hani Rouatbi (lamjed001)",
"link": "https://crowdin.com/profile/lamjed001"
},
{
"name": "Mohamed Mahmoud (master3lwa)",
"link": "https://crowdin.com/profile/master3lwa"
},
{
"name": "amirhosseinshammari (hosseinSh1379)",
"link": "https://crowdin.com/profile/hosseinSh1379"
}
],
"bn": [
{
"name": "Md Shariful Islam (Shariful7972)",
"link": "https://crowdin.com/profile/Shariful7972"
}
],
"de": [
{
"name": "u!^DEV (uDEV)",
"link": "https://crowdin.com/profile/uDEV"
}
],
"es-ES": [
{
"name": "seniordevops",
"link": "https://crowdin.com/profile/seniordevops"
},
{
"name": "c-sanchez",
"link": "https://crowdin.com/profile/c-sanchez"
}
],
"fa": [
{
"name": "AmirHossein Abdolmotallebi (amira1376)",
"link": "https://crowdin.com/profile/amira1376"
}
],
"fr": [
{
"name": "Gooseman (realgooseman)",
"link": "https://crowdin.com/profile/realgooseman"
},
{
"name": "Hani Rouatbi (lamjed001)",
"link": "https://crowdin.com/profile/lamjed001"
}
],
"id": [
{
"name": "kuydukuy (hilmy2nd)",
"link": "https://crowdin.com/profile/hilmy2nd"
},
{
"name": "Christian Elbrianno (crse)",
"link": "https://crowdin.com/profile/crse"
}
],
"pt-BR": [
{
"name": "Guilherme (whatahelll)",
"link": "https://crowdin.com/profile/whatahelll"
},
{
"name": "Davy J. (deaveipslon)",
"link": "https://crowdin.com/profile/deaveipslon"
}
],
"ru": [
{
"name": "in-ferno",
"link": "https://crowdin.com/profile/in-ferno"
}
],
"sq": [
{
"name": "Mario Balla (marjob1234)",
"link": "https://crowdin.com/profile/marjob1234"
}
],
"tr": [
{
"name": "𝗦𝗵𝗟𝗲𝗿𝗣 (mikropsoft)",
"link": "https://crowdin.com/profile/mikropsoft"
}
],
"uk": [
{
"name": "YALdysse",
"link": "https://crowdin.com/profile/YALdysse"
},
{
"name": "Misha Dyshlenko (lony_official)",
"link": "https://crowdin.com/profile/lony_official"
},
{
"name": "YALdysse_",
"link": "https://crowdin.com/profile/YALdysse_"
}
],
"vi": [
{
"name": "Zenfast",
"link": "https://crowdin.com/profile/Zenfast"
}
],
"zh-CN": [
{
"name": "Pylogmon (pylogmon)",
"link": "https://crowdin.com/profile/pylogmon"
},
{
"name": "Pesy Wu (GamerNoTitle)",
"link": "https://crowdin.com/profile/GamerNoTitle"
},
{
"name": "Sendevia",
"link": "https://crowdin.com/profile/Sendevia"
}
],
"zh-TW": [
{
"name": "ɴᴇᴋᴏ (NeKoOuO)",
"link": "https://crowdin.com/profile/NeKoOuO"
}
]
}

View File

@ -289,4 +289,12 @@ use_authentication=Use Authentication
warning_you_may_have_to_restart_the_download_later=You may have to restart the download later!
edit_download_title=Edit Download
edit_download_update_from_download_page=Update from Download Page
edit_download_update_from_download_page_description=When this window is open, you can go to the Download Page and click the download button. The app will capture and update the new download credentials so you can save them.
edit_download_update_from_download_page_description=When this window is open, you can go to the Download Page and click the download button. The app will capture and update the new download credentials so you can save them.
translators_page_thanks=With Gratitude to Those Who Helped Translate This Project ❤️
translators=Translators
language=Language
translators_contribute_title=Improve Translations
translators_contribute_description=Want to help improve this project? If your language is missing or needs some tweaks, you can contribute your translations and make it better!
contribute=Contribute
meet_the_translators=Meet the Translators
localized_by_translators=Localized by Translators