diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 48ae8147..6a9fa555 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -31,11 +31,12 @@ repositories { dependencies { implementation("com.android.tools.build:gradle:4.1.1") - implementation("org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktLint}") implementation(JetBrains.Compose.gradlePlugin) implementation(JetBrains.Kotlin.gradlePlugin) implementation(JetBrains.Kotlin.serialization) implementation(SqlDelight.gradlePlugin) + implementation("org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktLint}") + implementation("de.comahe.i18n4k:i18n4k-gradle-plugin:0.1.1") } kotlin { diff --git a/buildSrc/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/buildSrc/src/main/kotlin/Versions.kt index fbb3f1b9..6e64166a 100644 --- a/buildSrc/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/buildSrc/src/main/kotlin/Versions.kt @@ -145,6 +145,10 @@ object Ktor { val clientJs = "io.ktor:ktor-client-js:${Versions.ktor}" } +object Internationalization { + const val dep = "de.comahe.i18n4k:i18n4k-core:0.1.1" +} + object Extras { const val youtubeDownloader = "io.github.shabinder:youtube-api-dl:1.2" const val fuzzyWuzzy = "io.github.shabinder:fuzzywuzzy:1.1" diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt index 8fe52a33..76ee29f5 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt @@ -83,6 +83,7 @@ import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain.HomeCategory import com.shabinder.common.models.DownloadRecord import com.shabinder.common.models.methods +import com.shabinder.common.translations.Strings import com.shabinder.common.uikit.dialogs.DonationDialogComponent @Composable @@ -151,16 +152,16 @@ fun HomeTabBar( text = { Text( text = when (category) { - HomeCategory.About -> "About" - HomeCategory.History -> "History" + HomeCategory.About -> Strings.about() + HomeCategory.History -> Strings.history() }, style = MaterialTheme.typography.body2 ) }, icon = { when (category) { - HomeCategory.About -> Icon(Icons.Outlined.Info, "Info Tab") - HomeCategory.History -> Icon(Icons.Outlined.History, "History Tab") + HomeCategory.About -> Icon(Icons.Outlined.Info, Strings.infoTab()) + HomeCategory.History -> Icon(Icons.Outlined.History, Strings.historyTab()) } } ) @@ -183,9 +184,9 @@ fun SearchPanel( value = link, onValueChange = updateLink, leadingIcon = { - Icon(Icons.Rounded.Edit, "Link Text Box", tint = Color.LightGray) + Icon(Icons.Rounded.Edit, Strings.linkTextBox(), tint = Color.LightGray) }, - label = { Text(text = "Paste Link Here...", color = Color.LightGray) }, + label = { Text(text = Strings.pasteLinkHere(), color = Color.LightGray) }, singleLine = true, textStyle = TextStyle.Default.merge(TextStyle(fontSize = 18.sp, color = Color.White)), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), @@ -212,7 +213,7 @@ fun SearchPanel( OutlinedButton( modifier = Modifier.padding(12.dp).wrapContentWidth(), onClick = { - if (link.isBlank()) methods.value.showPopUpMessage("Enter A Link!") + if (link.isBlank()) methods.value.showPopUpMessage(Strings.enterALink()) else { // TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else onSearch(link) @@ -228,7 +229,7 @@ fun SearchPanel( ) ) ) { - Text(text = "Search", style = SpotiFlyerTypography.h6, modifier = Modifier.padding(4.dp)) + Text(text = Strings.search(), style = SpotiFlyerTypography.h6, modifier = Modifier.padding(4.dp)) } } } @@ -251,7 +252,7 @@ fun AboutColumn( ) { Column(modifier.padding(12.dp)) { Text( - text = "Supported Platforms", + text = Strings.supportedPlatforms(), style = SpotiFlyerTypography.body1, color = colorAccent ) @@ -259,7 +260,7 @@ fun AboutColumn( Row(horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxWidth()) { Icon( SpotifyLogo(), - "Open Spotify", + "${Strings.open()} Spotify", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { methods.value.openPlatform("com.spotify.music", "http://open.spotify.com") } @@ -268,7 +269,7 @@ fun AboutColumn( Spacer(modifier = modifier.padding(start = 16.dp)) Icon( GaanaLogo(), - "Open Gaana", + "${Strings.open()} Gaana", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { methods.value.openPlatform("com.gaana", "https://www.gaana.com") } @@ -277,7 +278,7 @@ fun AboutColumn( Spacer(modifier = modifier.padding(start = 16.dp)) Icon( SaavnLogo(), - "Open Jio Saavn", + "${Strings.open()} Jio Saavn", tint = Color.Unspecified, modifier = Modifier.clickable( onClick = { methods.value.openPlatform("com.jio.media.jiobeats", "https://www.jiosaavn.com/") } @@ -286,7 +287,7 @@ fun AboutColumn( Spacer(modifier = modifier.padding(start = 16.dp)) Icon( YoutubeLogo(), - "Open Youtube", + "${Strings.open()} Youtube", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { methods.value.openPlatform("com.google.android.youtube", "https://m.youtube.com") } @@ -295,7 +296,7 @@ fun AboutColumn( Spacer(modifier = modifier.padding(start = 12.dp)) Icon( YoutubeMusicLogo(), - "Open Youtube Music", + "${Strings.open()} Youtube Music", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { methods.value.openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") } @@ -311,7 +312,7 @@ fun AboutColumn( ) { Column(modifier.padding(12.dp)) { Text( - text = "Support Development", + text = Strings.supportDevelopment(), style = SpotiFlyerTypography.body1, color = colorAccent ) @@ -323,7 +324,7 @@ fun AboutColumn( ) .padding(vertical = 6.dp) ) { - Icon(GithubLogo(), "Open Project Repo", Modifier.size(32.dp), tint = Color(0xFFCCCCCC)) + Icon(GithubLogo(), Strings.openProjectRepo(), Modifier.size(32.dp), tint = Color(0xFFCCCCCC)) Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( @@ -331,7 +332,7 @@ fun AboutColumn( style = SpotiFlyerTypography.h6 ) Text( - text = "Star / Fork the project on Github.", + text = Strings.starOrForkProject(), style = SpotiFlyerTypography.subtitle2 ) } @@ -341,15 +342,15 @@ fun AboutColumn( .clickable(onClick = { methods.value.openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.Flag, "Help Translate", Modifier.size(32.dp)) + Icon(Icons.Rounded.Flag, Strings.help() + Strings.translate(), Modifier.size(32.dp)) Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( - text = "Translate", + text = Strings.translate(), style = SpotiFlyerTypography.h6 ) Text( - text = "Help us translate this app in your local language.", + text = Strings.helpTranslateDescription(), style = SpotiFlyerTypography.subtitle2 ) } @@ -360,15 +361,15 @@ fun AboutColumn( .clickable(onClick = openDonationDialog), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.CardGiftcard, "Support Developer", Modifier.size(32.dp)) + Icon(Icons.Rounded.CardGiftcard, Strings.supportDeveloper(), Modifier.size(32.dp)) Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( - text = "Donate", + text = Strings.donate(), style = SpotiFlyerTypography.h6 ) Text( - text = "If you think I deserve to get paid for my work, you can support me here.", + text = Strings.donateDescription(), // text = "SpotiFlyer will always be, Free and Open-Source. You can however show us that you care by sending a small donation.", style = SpotiFlyerTypography.subtitle2 ) @@ -383,15 +384,15 @@ fun AboutColumn( ), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.Share, "Share SpotiFlyer App", Modifier.size(32.dp)) + Icon(Icons.Rounded.Share, Strings.share() + Strings.title() + "App", Modifier.size(32.dp)) Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( - text = "Share", + text = Strings.share(), style = SpotiFlyerTypography.h6 ) Text( - text = "Share this app with your friends and family.", + text = Strings.shareDescription(), style = SpotiFlyerTypography.subtitle2 ) } @@ -405,17 +406,17 @@ fun AboutColumn( ), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.Insights, "Analytics Status", Modifier.size(32.dp)) + Icon(Icons.Rounded.Insights, Strings.analytics() + Strings.status(), Modifier.size(32.dp)) Spacer(modifier = Modifier.padding(start = 16.dp)) Column( Modifier.weight(1f) ) { Text( - text = "Analytics", + text = Strings.analytics(), style = SpotiFlyerTypography.h6 ) Text( - text = "Your Data is Anonymized and never shared with 3rd party service", + text = Strings.analyticsDescription(), style = SpotiFlyerTypography.subtitle2 ) } @@ -446,10 +447,10 @@ fun HistoryColumn( if (it.isEmpty()) { Column(Modifier.padding(8.dp).fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) { Icon( - Icons.Outlined.Info, "No History Available Yet", modifier = Modifier.size(80.dp), + Icons.Outlined.Info, Strings.noHistoryAvailable(), modifier = Modifier.size(80.dp), colorOffWhite ) - Text("No History Available", style = SpotiFlyerTypography.h4.copy(fontWeight = FontWeight.Light), textAlign = TextAlign.Center) + Text(Strings.noHistoryAvailable(), style = SpotiFlyerTypography.h4.copy(fontWeight = FontWeight.Light), textAlign = TextAlign.Center) } } else { Box { @@ -495,7 +496,7 @@ fun DownloadRecordItem( ImageLoad( item.coverUrl, { loadImage(item.coverUrl) }, - "Album Art", + Strings.albumArt(), modifier = Modifier.height(70.dp).width(70.dp).clip(SpotiFlyerShapes.medium) ) Column(modifier = Modifier.padding(horizontal = 8.dp).height(60.dp).weight(1f), verticalArrangement = Arrangement.SpaceEvenly) { @@ -506,12 +507,12 @@ fun DownloadRecordItem( modifier = Modifier.padding(horizontal = 8.dp).fillMaxSize() ) { Text(item.type, fontSize = 13.sp, color = colorOffWhite) - Text("Tracks: ${item.totalFiles}", fontSize = 13.sp, color = colorOffWhite) + Text("${Strings.tracks()}: ${item.totalFiles}", fontSize = 13.sp, color = colorOffWhite) } } Image( ShareImage(), - "Research", + Strings.reSearch(), modifier = Modifier.clickable( onClick = { // if(!isOnline(ctx)) showDialog("Check Your Internet Connection") else diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt index 2e82a6de..ad0112b6 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt @@ -59,6 +59,7 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.animation.child.cros import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot.Child +import com.shabinder.common.translations.Strings import com.shabinder.common.uikit.splash.Splash import com.shabinder.common.uikit.splash.SplashState import com.shabinder.common.uikit.utils.verticalGradientScrim @@ -163,7 +164,7 @@ fun AppBar( AnimatedVisibility(isBackButtonVisible) { Icon( Icons.Rounded.ArrowBackIosNew, - contentDescription = "Back Button", + contentDescription = Strings.backButton(), modifier = Modifier.clickable { onBackPressed() }, tint = Color.LightGray ) @@ -171,12 +172,12 @@ fun AppBar( } Image( SpotiFlyerLogo(), - "SpotiFlyer Logo", + Strings.spotiflyerLogo(), Modifier.size(32.dp), ) Spacer(Modifier.padding(horizontal = 4.dp)) Text( - text = "SpotiFlyer", + text = Strings.title(), style = appNameStyle ) } @@ -185,7 +186,7 @@ fun AppBar( IconButton( onClick = { setDownloadDirectory() } ) { - Icon(Icons.Filled.Settings, "Preferences", tint = Color.Gray) + Icon(Icons.Filled.Settings, Strings.preferences(), tint = Color.Gray) } }, modifier = modifier, diff --git a/common/data-models/build.gradle.kts b/common/data-models/build.gradle.kts index 5f8dd925..2499b0ad 100644 --- a/common/data-models/build.gradle.kts +++ b/common/data-models/build.gradle.kts @@ -1,3 +1,5 @@ +import de.comahe.i18n4k.gradle.plugin.i18n4k + /* * * Copyright (c) 2021 Shabinder Singh * * This program is free software: you can redistribute it and/or modify @@ -20,11 +22,18 @@ plugins { id("multiplatform-setup-test") id("kotlin-parcelize") kotlin("plugin.serialization") + id("de.comahe.i18n4k") } val statelyVersion = "1.1.7" val statelyIsoVersion = "1.1.7-a1" +i18n4k { + inputDirectory = "../../translations" + packageName = "com.shabinder.common.translations" + // sourceCodeLocales = listOf("en", "de") +} + kotlin { sourceSets { /* @@ -45,6 +54,7 @@ kotlin { implementation("co.touchlab:stately-isolate:$statelyIsoVersion") implementation("co.touchlab:stately-iso-collections:$statelyIsoVersion") implementation(Extras.youtubeDownloader) + api(Internationalization.dep) } } androidMain { diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/Ext.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/Ext.kt index 66717056..98e0e23f 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/Ext.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/Ext.kt @@ -1,3 +1,3 @@ package com.shabinder.common -fun T?.requireNotNull() : T = requireNotNull(this) \ No newline at end of file +fun T?.requireNotNull() : T = requireNotNull(this) diff --git a/translations/Strings_en.properties b/translations/Strings_en.properties new file mode 100644 index 00000000..ddbe59f1 --- /dev/null +++ b/translations/Strings_en.properties @@ -0,0 +1,35 @@ +title = SpotiFlyer +about = About +history = History +donate = Donate +preferences = Preferences +search = Search +supportedPlatforms = Supported Platforms +supportDevelopment = Support Development +openProjectRepo = Open Project Repo +starOrForkProject = Star / Fork the project on Github. +help = Help +translate = Translate +helpTranslateDescription = Help us translate this app in your local language. +supportDeveloper = Support Developer +donateDescription = If you think I deserve to get paid for my work, you can support me here. +share = Share +shareDescription = Share this app with your friends and family. +status = Status +analytics = Analytics +analyticsDescription = Your Data is Anonymized and never shared with 3rd party service. +noHistoryAvailable = No History Available + +albumArt = Album Art +tracks = Tracks +reSearch = Re-Search +spotiflyerLogo = SpotiFlyer Logo +backButton = Back Button +infoTab = Info Tab +historyTab = History Tab +linkTextBox = Link Text Box +pasteLinkHere = Paste Link Here... +enterALink = Enter A Link! +madeWith = Made with +inIndia = in India +open = Open