feat: i18n + crowdin (#31)

This commit is contained in:
rushii 2022-10-22 17:56:14 -07:00 • committed by GitHub
parent 6965f3128b
commit e7e5193dd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 315 additions and 160 deletions

42
.github/workflows/crowdin.yml vendored Normal file
View 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

View file

@ -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()
}
}

View file

@ -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)
)

View file

@ -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))
}
}
}

View file

@ -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
)

View file

@ -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)
)
}
}

View file

@ -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))
}
}
)

View file

@ -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
)
}

View file

@ -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

View file

@ -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,

View file

@ -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)
}

View file

@ -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)
)

View file

@ -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)
)
}
}

View file

@ -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) {

View file

@ -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)
)
}
}

View file

@ -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))
}
}
}

View file

@ -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))
}
}
)

View file

@ -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)
}
}

View file

@ -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))
}
}

View file

@ -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()
}
}

View file

@ -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
View 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