From e7e5193dd2f43cd0817fcd90eed49abca3853e8a Mon Sep 17 00:00:00 2001 From: rushii <33725716+DiamondMiner88@users.noreply.github.com> Date: Sat, 22 Oct 2022 17:56:14 -0700 Subject: [PATCH] feat: i18n + crowdin (#31) --- .github/workflows/crowdin.yml | 42 ++++++ .../installer/service/InstallService.kt | 9 +- .../manager/ui/component/Contributors.kt | 2 +- .../manager/ui/component/LoadFailure.kt | 4 +- .../manager/ui/component/home/CommitList.kt | 36 ++--- .../manager/ui/component/home/InfoCard.kt | 26 ++-- .../manager/ui/component/plugins/Changelog.kt | 6 +- .../ui/component/plugins/PluginCard.kt | 19 ++- .../manager/ui/dialog/InstallerDialog.kt | 10 +- .../ui/dialog/StoragePermissionsDialog.kt | 8 +- .../manager/ui/navigation/AppDestination.kt | 6 +- .../aliucord/manager/ui/screen/AboutScreen.kt | 10 +- .../manager/ui/screen/HomeRootScreen.kt | 2 +- .../aliucord/manager/ui/screen/HomeScreen.kt | 1 + .../manager/ui/screen/InstallScreen.kt | 4 +- .../manager/ui/screen/PluginsScreen.kt | 14 +- .../manager/ui/screen/SettingsScreen.kt | 36 ++--- .../com/aliucord/manager/ui/theme/Theme.kt | 17 ++- .../com/aliucord/manager/ui/util/Utils.kt | 57 +++++++- .../manager/ui/viewmodel/HomeViewModel.kt | 26 ++-- app/src/main/res/values/strings.xml | 133 +++++++++++------- crowdin.yml | 7 + 22 files changed, 315 insertions(+), 160 deletions(-) create mode 100644 .github/workflows/crowdin.yml create mode 100644 crowdin.yml diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml new file mode 100644 index 0000000..250e209 --- /dev/null +++ b/.github/workflows/crowdin.yml @@ -0,0 +1,42 @@ +name: Sync Crowdin + +on: + push: + branches: + - 'main' + paths: + - 'app/src/main/res/values/strings.xml' + - '.github/workflows/crowdin.yml' + schedule: + - cron: "0 17 * * 6" # "At 17:00 on Saturday." + workflow_dispatch: + +jobs: + sync-crowdin: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Crowdin + uses: crowdin/github-action@1.4.11 + with: + config: crowdin.yml + upload_translations: true + download_translations: true + push_translations: true + create_pull_request: false + localization_branch_name: l10n + commit_message: 'chore(i18n): sync translations' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_TOKEN }} + + - name: Merge + run: | + sudo chmod -R ugo+rwX . + git checkout main + git add * + git merge l10n + git push diff --git a/app/src/main/kotlin/com/aliucord/manager/installer/service/InstallService.kt b/app/src/main/kotlin/com/aliucord/manager/installer/service/InstallService.kt index c3dd292..698ebf0 100644 --- a/app/src/main/kotlin/com/aliucord/manager/installer/service/InstallService.kt +++ b/app/src/main/kotlin/com/aliucord/manager/installer/service/InstallService.kt @@ -7,6 +7,7 @@ import android.os.IBinder import android.util.Log import android.widget.Toast import com.aliucord.manager.BuildConfig +import com.aliucord.manager.R class InstallService : Service() { override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { @@ -21,19 +22,19 @@ class InstallService : Service() { PackageInstaller.STATUS_SUCCESS -> Toast.makeText( this, - "Successfully installed Aliucord!", + getString(R.string.install_success), Toast.LENGTH_LONG ).show() PackageInstaller.STATUS_FAILURE_ABORTED -> Toast.makeText( this, - "Aborted installation", + getString(R.string.install_aborted), Toast.LENGTH_LONG ).show() else -> { - Log.i(BuildConfig.TAG, "Installation failed: error code $statusCode") - Toast.makeText(this, "Failed: Error $statusCode", Toast.LENGTH_LONG).show() + Log.i(BuildConfig.TAG, "Install failed with error code $statusCode") + Toast.makeText(this, getString(R.string.install_failed, statusCode), Toast.LENGTH_LONG).show() } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/Contributors.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/Contributors.kt index e1a8039..2327774 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/Contributors.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/Contributors.kt @@ -79,7 +79,7 @@ fun ContributorEntry(user: GithubUser) { style = MaterialTheme.typography.bodyLarge ) Text( - text = stringResource(R.string.contributions, user.contributions), + text = stringResource(R.string.contributors_contributions, user.contributions), style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) ) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/LoadFailure.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/LoadFailure.kt index d723dbc..f4a9622 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/LoadFailure.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/LoadFailure.kt @@ -22,7 +22,7 @@ fun LoadFailure(onRetry: () -> Unit) { modifier = Modifier.fillMaxWidth() ) { Text( - text = stringResource(R.string.load_fail), + text = stringResource(R.string.network_load_fail), style = MaterialTheme.typography.labelLarge.copy(fontSize = 16.sp), color = MaterialTheme.colorScheme.error ) @@ -30,7 +30,7 @@ fun LoadFailure(onRetry: () -> Unit) { onClick = onRetry, shape = MaterialTheme.shapes.extraLarge ) { - Text(text = stringResource(R.string.retry)) + Text(text = stringResource(R.string.action_retry)) } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt index 0e3d8a8..c7309c9 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.* +import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -23,6 +23,7 @@ import coil.compose.AsyncImage import com.aliucord.manager.R import com.aliucord.manager.network.dto.Commit import com.aliucord.manager.ui.component.LoadFailure +import com.aliucord.manager.ui.util.annotatingStringResource import kotlinx.coroutines.flow.Flow @Composable @@ -137,36 +138,27 @@ fun CommitItem( model = "https://github.com/${commit.author.name}.png", contentDescription = commit.author.name ) + Text( - buildAnnotatedString { - withStyle( - SpanStyle( + annotatingStringResource( + R.string.contributors_commit_title, + commit.author.name, + commit.sha.take(8) + ) { + when (it) { + "name" -> SpanStyle( fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface ) - ) { - append(commit.author.name) - } - - withStyle( - SpanStyle( - color = MaterialTheme.colorScheme.onSurface.copy( - alpha = 0.6f - ) + "middle" -> SpanStyle( + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) ) - ) { - append(" authored ") - } - - withStyle( - SpanStyle( + "commit" -> SpanStyle( fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.primary ) - ) { - append(commit.sha.substring(0, 7)) + else -> null } - }, style = MaterialTheme.typography.bodySmall ) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/home/InfoCard.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/home/InfoCard.kt index d671656..48a103b 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/home/InfoCard.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/home/InfoCard.kt @@ -1,6 +1,8 @@ package com.aliucord.manager.ui.component.home +import androidx.annotation.StringRes import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.appendInlineContent import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* @@ -18,6 +20,7 @@ import com.aliucord.manager.R fun InfoCard( packageName: String, supportedVersion: String, + @StringRes supportedVersionType: Int, currentVersion: String, onDownloadClick: () -> Unit, onUninstallClick: () -> Unit, @@ -31,22 +34,25 @@ fun InfoCard( verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text( - text = "Aliucord${packageName.let { if (it != "com.aliucord") " ($it)" else "" }}", + text = "${stringResource(R.string.aliucord)} ($packageName)", style = MaterialTheme.typography.titleLarge.copy(fontSize = 23.sp), color = MaterialTheme.colorScheme.primary ) Text( buildAnnotatedString { - append("Supported version: ") - + append(stringResource(R.string.version_supported)) withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { + append(" ") append(supportedVersion) + append(" - ") + append(stringResource(supportedVersionType)) } - append("\nInstalled version: ") - + append('\n') + append(stringResource(R.string.version_installed)) withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { + append(" ") append(currentVersion) } }, @@ -61,9 +67,9 @@ fun InfoCard( ) { val (icon, description) = when (currentVersion) { - "-" -> Icons.Default.Download to R.string.install - supportedVersion -> Icons.Default.Refresh to R.string.reinstall - else -> Icons.Default.Update to R.string.update + "-" -> Icons.Default.Download to R.string.action_install + supportedVersion -> Icons.Default.Refresh to R.string.action_reinstall + else -> Icons.Default.Update to R.string.action_update } FilledTonalIconButton( @@ -98,7 +104,7 @@ fun InfoCard( ) { Icon( imageVector = Icons.Default.Delete, - contentDescription = stringResource(R.string.uninstall) + contentDescription = stringResource(R.string.action_uninstall) ) } @@ -111,7 +117,7 @@ fun InfoCard( ) { Icon( imageVector = Icons.Default.Launch, - contentDescription = stringResource(R.string.launch) + contentDescription = stringResource(R.string.action_launch) ) } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/Changelog.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/Changelog.kt index a6a7821..7011c9a 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/Changelog.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/Changelog.kt @@ -72,7 +72,7 @@ fun Changelog( icon = { Icon( imageVector = Icons.Default.History, - contentDescription = stringResource(R.string.view_plugin_changelog, plugin.manifest.name) + contentDescription = stringResource(R.string.plugins_view_changelog, plugin.manifest.name) ) }, title = { Text(plugin.manifest.name) }, @@ -85,7 +85,7 @@ fun Changelog( .heightIn(max = 90.dp) .clip(RoundedCornerShape(14.dp)), model = mediaUrl, - contentDescription = stringResource(R.string.changelog_media) + contentDescription = stringResource(R.string.plugins_changelog_media) ) } @@ -156,7 +156,7 @@ fun Changelog( }, confirmButton = { Button(onClick = onDismiss) { - Text(stringResource(R.string.close)) + Text(stringResource(R.string.action_close)) } } ) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt index 5aaf7f0..4fae717 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import com.aliucord.manager.R import com.aliucord.manager.domain.model.Plugin +import com.aliucord.manager.ui.util.annotatingStringResource import com.aliucord.manager.ui.util.joinToAnnotatedString @Composable @@ -48,15 +49,19 @@ fun PluginCard( Column { // Name Text( - buildAnnotatedString { - withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { - append(plugin.manifest.name) + annotatingStringResource( + R.string.plugins_plugin_title, + plugin.manifest.name, + plugin.manifest.version + ) { + when (it) { + "plugin" -> SpanStyle(fontWeight = FontWeight.Bold) + else -> null } - - append(" v${plugin.manifest.version}") } ) + // TODO: make this translatable // Authors val authors = buildAnnotatedString { withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) { @@ -139,7 +144,7 @@ fun PluginCard( Icon( modifier = Modifier.fillMaxSize(), imageVector = Icons.Default.History, - contentDescription = stringResource(R.string.view_plugin_changelog, plugin.manifest.name) + contentDescription = stringResource(R.string.plugins_view_changelog, plugin.manifest.name) ) } } @@ -150,7 +155,7 @@ fun PluginCard( Icon( modifier = Modifier.fillMaxSize(), imageVector = Icons.Default.Delete, - contentDescription = stringResource(R.string.uninstall), + contentDescription = stringResource(R.string.action_uninstall), tint = MaterialTheme.colorScheme.error ) } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/dialog/InstallerDialog.kt b/app/src/main/kotlin/com/aliucord/manager/ui/dialog/InstallerDialog.kt index 7ec7fff..fbc009f 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/dialog/InstallerDialog.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/dialog/InstallerDialog.kt @@ -31,16 +31,16 @@ fun InstallerDialog( icon = { Icon( imageVector = Icons.Default.Download, - contentDescription = stringResource(R.string.download_method) + contentDescription = stringResource(R.string.selector_download_method) ) }, title = { - Text(stringResource(R.string.download_method)) + Text(stringResource(R.string.selector_download_method)) }, text = { Column { mapOf( - DownloadMethod.DOWNLOAD to stringResource(R.string.download_apk), + DownloadMethod.DOWNLOAD to stringResource(R.string.selector_download_apk), // DownloadMethod.SELECT to stringResource(R.string.select_apk) ).forEach { (method, name) -> Column { @@ -75,7 +75,7 @@ fun InstallerDialog( onDismissRequest() } ) { - Text(stringResource(R.string.confirm)) + Text(stringResource(R.string.action_confirm)) } }, dismissButton = { @@ -86,7 +86,7 @@ fun InstallerDialog( containerColor = MaterialTheme.colorScheme.secondaryContainer ) ) { - Text(stringResource(R.string.dismiss)) + Text(stringResource(R.string.action_dismiss)) } }, onDismissRequest = onDismissRequest diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/dialog/StoragePermissionsDialog.kt b/app/src/main/kotlin/com/aliucord/manager/ui/dialog/StoragePermissionsDialog.kt index 9949938..ce1c83f 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/dialog/StoragePermissionsDialog.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/dialog/StoragePermissionsDialog.kt @@ -46,10 +46,10 @@ private fun ManageStorageDialog() { .let { launcher.launch(it) } } ) { - Text(stringResource(R.string.open_settings)) + Text(stringResource(R.string.action_open_settings)) } }, - title = { Text(stringResource(R.string.permissions_grant)) }, + title = { Text(stringResource(R.string.permissions_grant_title)) }, text = { Text(stringResource(R.string.permissions_grant_body)) }, properties = DialogProperties( dismissOnBackPress = false, @@ -69,10 +69,10 @@ private fun ExternalStorageDialog() { onDismissRequest = {}, confirmButton = { Button(onClick = writeStorageState::launchPermissionRequest) { - Text(stringResource(R.string.confirm)) + Text(stringResource(R.string.action_confirm)) } }, - title = { Text(stringResource(R.string.permissions_grant)) }, + title = { Text(stringResource(R.string.permissions_grant_title)) }, text = { Text(stringResource(R.string.permissions_grant_body)) }, properties = DialogProperties( dismissOnBackPress = false, diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt index d559693..435ea4d 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt @@ -32,7 +32,7 @@ enum class HomeDestination( val unselectedIcon: @RawValue ImageVector, @StringRes val label: Int ) : Destination { - HOME(Icons.Default.Home, Icons.Outlined.Home, R.string.home), - PLUGINS(Icons.Default.Extension, Icons.Outlined.Extension, R.string.plugins), - SETTINGS(Icons.Default.Settings, Icons.Outlined.Settings, R.string.settings) + HOME(Icons.Default.Home, Icons.Outlined.Home, R.string.navigation_home), + PLUGINS(Icons.Default.Extension, Icons.Outlined.Extension, R.string.plugins_title), + SETTINGS(Icons.Default.Settings, Icons.Outlined.Settings, R.string.navigation_settings) } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/AboutScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/AboutScreen.kt index 4b274d3..b352907 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/AboutScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screen/AboutScreen.kt @@ -43,12 +43,12 @@ fun AboutScreen( Scaffold( topBar = { TopAppBar( - title = { Text(stringResource(R.string.about)) }, + title = { Text(stringResource(R.string.navigation_about)) }, navigationIcon = { IconButton(onClick = onBackClick) { Icon( imageVector = Icons.Default.ArrowBack, - contentDescription = stringResource(R.string.back) + contentDescription = stringResource(R.string.navigation_back) ) } } @@ -68,19 +68,19 @@ fun AboutScreen( ) { AsyncImage( model = "https://github.com/Aliucord.png", - contentDescription = "Aliucord", + contentDescription = stringResource(R.string.aliucord), modifier = Modifier.size(71.dp) ) Text( - text = "Aliucord", + text = stringResource(R.string.aliucord), style = MaterialTheme.typography.titleMedium.copy( fontSize = 26.sp ) ) Text( - text = stringResource(R.string.aliucord_description), + text = stringResource(R.string.app_description), style = MaterialTheme.typography.titleSmall.copy( color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) ) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeRootScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeRootScreen.kt index 01bcc5e..8f92d2f 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeRootScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeRootScreen.kt @@ -45,7 +45,7 @@ fun MainRootScreen( IconButton(onClick = onAboutClick) { Icon( imageVector = Icons.Default.Info, - contentDescription = stringResource(R.string.about) + contentDescription = stringResource(R.string.navigation_about) ) } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeScreen.kt index add37c2..a287862 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeScreen.kt @@ -57,6 +57,7 @@ fun HomeScreen( InfoCard( packageName = viewModel.preferences.packageName, supportedVersion = viewModel.supportedVersion, + supportedVersionType = viewModel.supportedVersionType, currentVersion = viewModel.installedVersion, onDownloadClick = { if (viewModel.preferences.devMode) { diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt index de76683..28e7d49 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt @@ -45,12 +45,12 @@ fun InstallerScreen( Scaffold( topBar = { TopAppBar( - title = { Text("Installer") }, + title = { Text(stringResource(R.string.installer)) }, navigationIcon = { IconButton(onClick = onBackClick) { Icon( imageVector = Icons.Default.NavigateBefore, - contentDescription = stringResource(R.string.back) + contentDescription = stringResource(R.string.navigation_back) ) } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/PluginsScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/PluginsScreen.kt index 9302b31..2254f13 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/PluginsScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screen/PluginsScreen.kt @@ -40,14 +40,14 @@ fun PluginsScreen( icon = { Icon( imageVector = Icons.Default.Delete, - contentDescription = stringResource(R.string.uninstall) + contentDescription = stringResource(R.string.action_uninstall) ) }, title = { - Text("${stringResource(R.string.uninstall)} ${plugin.manifest.name}") + Text("${stringResource(R.string.action_uninstall)} ${plugin.manifest.name}") }, text = { - Text(stringResource(R.string.delete_plugin_body)) + Text(stringResource(R.string.plugins_delete_plugin_body)) }, confirmButton = { Button( @@ -98,19 +98,19 @@ fun PluginsScreen( onValueChange = viewModel::search, singleLine = true, shape = RoundedCornerShape(12.dp), - label = { Text(stringResource(R.string.search)) }, + label = { Text(stringResource(R.string.action_search)) }, trailingIcon = { if (viewModel.search.isNotBlank()) { IconButton(onClick = viewModel::clearSearch) { Icon( imageVector = Icons.Default.Close, - contentDescription = stringResource(R.string.clear) + contentDescription = stringResource(R.string.action_clear) ) } } else { Icon( imageVector = Icons.Default.Search, - contentDescription = stringResource(R.string.search_plugins) + contentDescription = stringResource(R.string.plugins_search) ) } }, @@ -150,7 +150,7 @@ fun PluginsScreen( imageVector = Icons.Default.Extension, contentDescription = null ) - Text(stringResource(R.string.no_plugins_installed)) + Text(stringResource(R.string.plugins_none_installed)) } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/SettingsScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/SettingsScreen.kt index 86920a9..ff3a66e 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/SettingsScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screen/SettingsScreen.kt @@ -42,31 +42,31 @@ fun SettingsScreen( ) } - SettingsHeader(stringResource(R.string.appearance)) + SettingsHeader(stringResource(R.string.settings_appearance)) SettingsItem( modifier = Modifier.clickable(onClick = viewModel::showThemeDialog), icon = { Icon(Icons.Default.Style, null) }, - text = { Text(stringResource(R.string.theme)) } + text = { Text(stringResource(R.string.settings_theme)) } ) { FilledTonalButton(onClick = viewModel::showThemeDialog) { - Text(preferences.theme.displayName) + Text(preferences.theme.toDisplayName()) } } SettingsSwitch( - label = stringResource(R.string.dynamic_color), + label = stringResource(R.string.setting_dynamic_color), pref = preferences.dynamicColor, icon = { Icon(Icons.Default.Palette, null) } ) { preferences.dynamicColor = it } - SettingsHeader(stringResource(R.string.advanced)) + SettingsHeader(stringResource(R.string.settings_advanced)) SettingsSwitch( - label = stringResource(R.string.replace_bg), - secondaryLabel = stringResource(R.string.replace_bg_description), + label = stringResource(R.string.setting_replace_icon), + secondaryLabel = stringResource(R.string.setting_replace_icon_desc), pref = preferences.replaceIcon, icon = { Icon(Icons.Default.AppShortcut, null) } ) { @@ -76,13 +76,13 @@ fun SettingsScreen( Spacer(modifier = Modifier.height(8.dp)) SettingsTextField( - label = stringResource(R.string.app_name_setting), + label = stringResource(R.string.setting_app_name), pref = preferences.appName, onPrefChange = viewModel::setAppName ) SettingsSwitch( - label = stringResource(R.string.developer_options), + label = stringResource(R.string.settings_developer_options), pref = preferences.devMode, icon = { Icon(Icons.Default.Code, null) } ) { @@ -98,20 +98,20 @@ fun SettingsScreen( verticalArrangement = Arrangement.spacedBy(12.dp) ) { SettingsTextField( - label = stringResource(R.string.package_name), + label = stringResource(R.string.setting_package_name), pref = preferences.packageName, onPrefChange = viewModel::setPackageName ) SettingsTextField( - label = stringResource(R.string.version), + label = stringResource(R.string.setting_target_version), pref = preferences.version, onPrefChange = viewModel::setVersion ) SettingsSwitch( - label = stringResource(R.string.debuggable), - secondaryLabel = stringResource(R.string.debuggable_description), + label = stringResource(R.string.setting_debuggable), + secondaryLabel = stringResource(R.string.setting_debuggable_desc), pref = preferences.debuggable, icon = { Icon(Icons.Default.BugReport, null) } ) { @@ -128,7 +128,7 @@ fun SettingsScreen( onClick = viewModel::clearCacheDir ) { Text( - text = stringResource(R.string.clear_cache), + text = stringResource(R.string.setting_clear_cache), textAlign = TextAlign.Center ) } @@ -148,10 +148,10 @@ fun ThemeDialog( icon = { Icon( imageVector = Icons.Default.Style, - contentDescription = stringResource(R.string.theme) + contentDescription = stringResource(R.string.settings_theme) ) }, - title = { Text(stringResource(R.string.theme)) }, + title = { Text(stringResource(R.string.settings_theme)) }, text = { Column { Theme.values().forEach { theme -> @@ -162,7 +162,7 @@ fun ThemeDialog( verticalAlignment = Alignment.CenterVertically ) { Text( - text = theme.displayName, + text = theme.toDisplayName(), style = MaterialTheme.typography.labelLarge ) @@ -183,7 +183,7 @@ fun ThemeDialog( onDismissRequest() } ) { - Text(stringResource(R.string.apply)) + Text(stringResource(R.string.action_apply)) } } ) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/theme/Theme.kt b/app/src/main/kotlin/com/aliucord/manager/ui/theme/Theme.kt index 020a6db..06efa9e 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/theme/Theme.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/theme/Theme.kt @@ -11,6 +11,8 @@ import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import com.aliucord.manager.R import com.google.accompanist.systemuicontroller.rememberSystemUiController @Composable @@ -42,8 +44,15 @@ fun ManagerTheme( ) } -enum class Theme(val displayName: String) { - SYSTEM("System"), - LIGHT("Light"), - DARK("Dark") +enum class Theme { + SYSTEM, + LIGHT, + DARK; + + @Composable + fun toDisplayName() = when (this) { + SYSTEM -> stringResource(R.string.theme_system) + LIGHT -> stringResource(R.string.theme_light) + DARK -> stringResource(R.string.theme_dark) + } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt b/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt index 46c4158..a67adcb 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt @@ -1,6 +1,9 @@ package com.aliucord.manager.ui.util -import androidx.compose.ui.text.AnnotatedString +import androidx.annotation.StringRes +import androidx.compose.runtime.* +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.* context(AnnotatedString.Builder) inline fun Iterable.joinToAnnotatedString( @@ -21,3 +24,55 @@ context(AnnotatedString.Builder) } postfix(this@Builder) } + +/** + * This formats and styles string resources into an [AnnotatedString]. + * By putting `§annotation_name§target string blah blah blah§` into the resource string it can be translated + * (assuming `annotation_name` is not changed between translations), + * when rendering [annotationHandler] will be called on each `annotation_name` and expected to return a target style on it. + */ +@Suppress("NOTHING_TO_INLINE") +@Composable +inline fun annotatingStringResource( + @StringRes id: Int, + vararg args: Any, + crossinline annotationHandler: @Composable (annotationName: String) -> SpanStyle? +): AnnotatedString { + val string = stringResource(id, *args) + + // TODO: figure out a way to put this in remember {} while still having @Composable annotationHandler + val markerIndexes = string + .mapIndexedNotNull { index, elem -> index.takeIf { elem == '§' } } + .takeIf { it.isNotEmpty() && it.size % 3 == 0 } + ?: return AnnotatedString(string) + + return buildAnnotatedString { + var lastIndex = 0 + var offset = 0 + + for ((nameStart, nameEnd, valueEnd) in markerIndexes.chunked(3)) { + append(string.substring(lastIndex, nameStart)) + append(string.substring(nameEnd + 1, valueEnd)) + + val annotationName = string.substring(nameStart + 1, nameEnd) + val newValueStart = nameEnd - annotationName.length - offset - 1 + val newValueEnd = valueEnd - annotationName.length - offset - 2 + + addStringAnnotation( + tag = annotationName, + annotation = annotationName, + start = newValueStart, + end = newValueEnd, + ) + + annotationHandler(annotationName)?.let { + addStyle(it, newValueStart, newValueEnd) + } + + lastIndex = valueEnd + 1 + offset += 3 + annotationName.length + } + + append(string.substring(lastIndex)) + } +} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/HomeViewModel.kt b/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/HomeViewModel.kt index 8486e57..ba86e7c 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/HomeViewModel.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/HomeViewModel.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.* import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.* +import com.aliucord.manager.R import com.aliucord.manager.domain.manager.PreferencesManager import com.aliucord.manager.domain.repository.GithubRepository import com.aliucord.manager.network.dto.Commit @@ -25,7 +26,10 @@ class HomeViewModel( var supportedVersion by mutableStateOf("") private set - var installedVersion by mutableStateOf("-") + var supportedVersionType by mutableStateOf(R.string.version_unknown) + private set + + var installedVersion by mutableStateOf("") private set val commits = Pager(PagingConfig(pageSize = 30)) { @@ -62,6 +66,7 @@ class HomeViewModel( @Suppress("DEPRECATION") packageManager.getPackageInfo(preferences.packageName, 0).versionName } catch (th: Throwable) { + // TODO: translated error message "-" } } @@ -70,16 +75,19 @@ class HomeViewModel( private suspend fun _fetchSupportedVersion() { val version = githubRepository.getVersion() - supportedVersion = version.fold( + version.fold( success = { - "${it.versionName} - " + when (it.versionCode[3]) { - '0' -> "Stable" - '1' -> "Beta" - '2' -> "Alpha" - else -> throw NoWhenBranchMatchedException() + supportedVersion = it.versionName + supportedVersionType = when (it.versionCode[3]) { + '0' -> R.string.version_stable + '1' -> R.string.version_beta + '2' -> R.string.version_alpha + else -> R.string.version_unknown } }, - fail = { "Failed to retrieve version" } + fail = { + supportedVersionType = R.string.version_load_fail + } ) } @@ -95,7 +103,7 @@ class HomeViewModel( if (launchIntent != null) { application.startActivity(launchIntent) } else { - Toast.makeText(application, "Failed to launch Aliucord", Toast.LENGTH_LONG).show() + Toast.makeText(application, R.string.launch_aliucord_fail, Toast.LENGTH_LONG).show() } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 153897f..5a80579 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,59 +1,88 @@ Aliucord Manager - Clear cache - Commits - Dex location - Open settings - Replace app icon - Uses the Aliucord variant as opposed to Discord\'s own icon - Settings - Show menu - Theme - Use Injector.dex from storage - Plugins - Search - View changelog for %1$s - Are you sure you want to uninstall this plugin? - Close - Support Server - Back - Home - Install - Reinstall - Update - Developer options - Debuggable - Enable debuggable flag - About - Team - Contributors - Search Plugins + A modification for the Discord Android App + + Aliucord GitHub + Support Server + Commits + Team Website Installer - %o contributions - Manager appearance - No plugins installed! - Contributor - Apply - Confirm - Dismiss - Download APK - Select APK - Download Method - Changelog Media - App Name - Package Name - Clear - Launch - Uninstall - Download Version - Dynamic color - Appearance - Advanced - Grant Permissions + + Retry + Apply + Confirm + Dismiss + Install + Reinstall + Update + Clear + Launch + Uninstall + Close + Open settings + Search + Loading… + + Grant Permissions In order for Aliucord Manager to work properly, you need to grant file permissions. Since shared data in stored in ~/Aliucord, permissions are required in order for manager to function. - A modification for the Discord Android App - Failed to load - Retry + + Successfully installed Aliucord + Aborted Aliucord installation + Installation failed with error %o + Failed to launch Aliucord + + Theme + Advanced + Appearance + Developer options + Clear cache + Dex location + Use Injector.dex from storage + Replace app icon + Uses the Aliucord variant as opposed to Discord\'s own icon + Debuggable + Enable debuggable flag + Manager appearance + App Name + Package Name + Download Version + Dynamic color + + System + Dark + Light + + Plugins + View changelog for %1$s + Are you sure you want to uninstall this plugin? + Search Plugins + No plugins installed! + Changelog Media + §plugin§%1$s§ v%2$s + + Back + Home + About + Settings + + Contributors + %o contributions + Contributor + §name§%1$s§ §middle§authored§ §commit§%2$s§ + + Download APK + Select APK + Download Method + + Failed to load + + Supported version: + Installed version: + Stable + Beta + Alpha + + Failed to retrieve version diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 0000000..60b6445 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,7 @@ +project_id_env: CROWDIN_PROJECT_ID +api_token_env: CROWDIN_PERSONAL_TOKEN + +preserve_hierarchy: true +files: + - source: app/src/main/res/values/strings.xml + translation: app/src/main/res/values-%android_code%/strings.xml