feat: i18n + crowdin (#31)
This commit is contained in:
parent
6965f3128b
commit
e7e5193dd2
22 changed files with 315 additions and 160 deletions
42
.github/workflows/crowdin.yml
vendored
Normal file
42
.github/workflows/crowdin.yml
vendored
Normal file
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <T> Iterable<T>.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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,59 +1,88 @@
|
|||
<resources>
|
||||
<string name="app_name">Aliucord Manager</string>
|
||||
<string name="clear_cache">Clear cache</string>
|
||||
<string name="commits">Commits</string>
|
||||
<string name="dex_location">Dex location</string>
|
||||
<string name="open_settings">Open settings</string>
|
||||
<string name="replace_bg">Replace app icon</string>
|
||||
<string name="replace_bg_description">Uses the Aliucord variant as opposed to Discord\'s own icon</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="show_menu">Show menu</string>
|
||||
<string name="theme">Theme</string>
|
||||
<string name="use_dex_from_storage">Use Injector.dex from storage</string>
|
||||
<string name="plugins">Plugins</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="view_plugin_changelog">View changelog for %1$s</string>
|
||||
<string name="delete_plugin_body">Are you sure you want to uninstall this plugin?</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="support_server">Support Server</string>
|
||||
<string name="back">Back</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="reinstall">Reinstall</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="developer_options">Developer options</string>
|
||||
<string name="debuggable">Debuggable</string>
|
||||
<string name="debuggable_description">Enable debuggable flag</string>
|
||||
<string name="about">About</string>
|
||||
<string name="team">Team</string>
|
||||
<string name="contributors">Contributors</string>
|
||||
<string name="search_plugins">Search Plugins</string>
|
||||
<string name="app_description">A modification for the Discord Android App</string>
|
||||
|
||||
<string name="aliucord">Aliucord</string>
|
||||
<string name="github">GitHub</string>
|
||||
<string name="support_server">Support Server</string>
|
||||
<string name="commits">Commits</string>
|
||||
<string name="team">Team</string>
|
||||
<string name="website">Website</string>
|
||||
<string name="installer">Installer</string>
|
||||
<string name="contributions">%o contributions</string>
|
||||
<string name="theme_setting_description">Manager appearance</string>
|
||||
<string name="no_plugins_installed">No plugins installed!</string>
|
||||
<string name="contributor">Contributor</string>
|
||||
<string name="apply">Apply</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="dismiss">Dismiss</string>
|
||||
<string name="download_apk">Download APK</string>
|
||||
<string name="select_apk">Select APK</string>
|
||||
<string name="download_method">Download Method</string>
|
||||
<string name="changelog_media">Changelog Media</string>
|
||||
<string name="app_name_setting">App Name</string>
|
||||
<string name="package_name">Package Name</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="launch">Launch</string>
|
||||
<string name="uninstall">Uninstall</string>
|
||||
<string name="version">Download Version</string>
|
||||
<string name="dynamic_color">Dynamic color</string>
|
||||
<string name="appearance">Appearance</string>
|
||||
<string name="advanced">Advanced</string>
|
||||
<string name="permissions_grant">Grant Permissions</string>
|
||||
|
||||
<string name="action_retry">Retry</string>
|
||||
<string name="action_apply">Apply</string>
|
||||
<string name="action_confirm">Confirm</string>
|
||||
<string name="action_dismiss">Dismiss</string>
|
||||
<string name="action_install">Install</string>
|
||||
<string name="action_reinstall">Reinstall</string>
|
||||
<string name="action_update">Update</string>
|
||||
<string name="action_clear">Clear</string>
|
||||
<string name="action_launch">Launch</string>
|
||||
<string name="action_uninstall">Uninstall</string>
|
||||
<string name="action_close">Close</string>
|
||||
<string name="action_open_settings">Open settings</string>
|
||||
<string name="action_search">Search</string>
|
||||
<string name="action_loading_trailing">Loading…</string>
|
||||
|
||||
<string name="permissions_grant_title">Grant Permissions</string>
|
||||
<string name="permissions_grant_body">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.</string>
|
||||
<string name="aliucord_description">A modification for the Discord Android App</string>
|
||||
<string name="load_fail">Failed to load</string>
|
||||
<string name="retry">Retry</string>
|
||||
|
||||
<string name="install_success">Successfully installed Aliucord</string>
|
||||
<string name="install_aborted">Aborted Aliucord installation</string>
|
||||
<string name="install_failed">Installation failed with error %o</string>
|
||||
<string name="launch_aliucord_fail">Failed to launch Aliucord</string>
|
||||
|
||||
<string name="settings_theme">Theme</string>
|
||||
<string name="settings_advanced">Advanced</string>
|
||||
<string name="settings_appearance">Appearance</string>
|
||||
<string name="settings_developer_options">Developer options</string>
|
||||
<string name="setting_clear_cache">Clear cache</string>
|
||||
<string name="setting_dex_location">Dex location</string>
|
||||
<string name="setting_use_dex_from_storage">Use Injector.dex from storage</string>
|
||||
<string name="setting_replace_icon">Replace app icon</string>
|
||||
<string name="setting_replace_icon_desc">Uses the Aliucord variant as opposed to Discord\'s own icon</string>
|
||||
<string name="setting_debuggable">Debuggable</string>
|
||||
<string name="setting_debuggable_desc">Enable debuggable flag</string>
|
||||
<string name="setting_theme_desc">Manager appearance</string>
|
||||
<string name="setting_app_name">App Name</string>
|
||||
<string name="setting_package_name">Package Name</string>
|
||||
<string name="setting_target_version">Download Version</string>
|
||||
<string name="setting_dynamic_color">Dynamic color</string>
|
||||
|
||||
<string name="theme_system">System</string>
|
||||
<string name="theme_dark">Dark</string>
|
||||
<string name="theme_light">Light</string>
|
||||
|
||||
<string name="plugins_title">Plugins</string>
|
||||
<string name="plugins_view_changelog">View changelog for %1$s</string>
|
||||
<string name="plugins_delete_plugin_body">Are you sure you want to uninstall this plugin?</string>
|
||||
<string name="plugins_search">Search Plugins</string>
|
||||
<string name="plugins_none_installed">No plugins installed!</string>
|
||||
<string name="plugins_changelog_media">Changelog Media</string>
|
||||
<string name="plugins_plugin_title">§plugin§%1$s§ v%2$s</string>
|
||||
|
||||
<string name="navigation_back">Back</string>
|
||||
<string name="navigation_home">Home</string>
|
||||
<string name="navigation_about">About</string>
|
||||
<string name="navigation_settings">Settings</string>
|
||||
|
||||
<string name="contributors">Contributors</string>
|
||||
<string name="contributors_contributions">%o contributions</string>
|
||||
<string name="contributors_contributor">Contributor</string>
|
||||
<string name="contributors_commit_title">§name§%1$s§ §middle§authored§ §commit§%2$s§</string>
|
||||
|
||||
<string name="selector_download_apk">Download APK</string>
|
||||
<string name="selector_select_apk">Select APK</string>
|
||||
<string name="selector_download_method">Download Method</string>
|
||||
|
||||
<string name="network_load_fail">Failed to load</string>
|
||||
|
||||
<string name="version_supported">Supported version:</string>
|
||||
<string name="version_installed">Installed version:</string>
|
||||
<string name="version_stable">Stable</string>
|
||||
<string name="version_beta">Beta</string>
|
||||
<string name="version_alpha">Alpha</string>
|
||||
<string name="version_unknown" translatable="false" />
|
||||
<string name="version_load_fail">Failed to retrieve version</string>
|
||||
</resources>
|
||||
|
|
|
|||
7
crowdin.yml
Normal file
7
crowdin.yml
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Reference in a new issue