diff --git a/app/src/fdroid/res/values/strings.xml b/app/src/fdroid/res/values/strings.xml index 1fe888d..abe9161 100644 --- a/app/src/fdroid/res/values/strings.xml +++ b/app/src/fdroid/res/values/strings.xml @@ -1,4 +1,4 @@ - GitLab - "https://gitlab.com/sirekanyan.org/outline" + GitLab + "https://gitlab.com/sirekanyan.org/outline" diff --git a/app/src/main/java/org/sirekanyan/outline/MainContent.kt b/app/src/main/java/org/sirekanyan/outline/MainContent.kt index b7c492e..4f871ed 100644 --- a/app/src/main/java/org/sirekanyan/outline/MainContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/MainContent.kt @@ -81,7 +81,7 @@ fun MainContent(state: MainState) { TextButton(onClick = { state.dialog = AddServerDialog }) { Icon(Icons.Default.Add, null) Spacer(Modifier.size(8.dp)) - Text("Add server") + Text(stringResource(R.string.outln_text_add_server)) } } } @@ -98,8 +98,12 @@ fun MainContent(state: MainState) { listOf() } else { listOf( - MenuItem("Sort by…", IconSort) { isSortingVisible = true }, - MenuItem("Search", Icons.Default.Search) { search.openSearch() }, + MenuItem(R.string.outln_menu_sort, IconSort) { + isSortingVisible = true + }, + MenuItem(R.string.outln_menu_search, Icons.Default.Search) { + search.openSearch() + }, ) } MainTopAppBar( @@ -173,15 +177,15 @@ fun MainContent(state: MainState) { }, onMenuClick = state::openDrawer, visibleItems = listOf( - MenuItem("Sort by…", IconSort) { + MenuItem(R.string.outln_menu_sort, IconSort) { isSortingVisible = true }, ), overflowItems = listOf( - MenuItem("Edit", Icons.Default.Edit) { + MenuItem(R.string.outln_menu_edit, Icons.Default.Edit) { state.dialog = RenameServerDialog(page.server) }, - MenuItem("Delete", Icons.Default.Delete) { + MenuItem(R.string.outln_menu_delete, Icons.Default.Delete) { state.dialog = DeleteServerDialog(page.server) }, ), diff --git a/app/src/main/java/org/sirekanyan/outline/MainTopAppBar.kt b/app/src/main/java/org/sirekanyan/outline/MainTopAppBar.kt index b4fdca0..42a98ce 100644 --- a/app/src/main/java/org/sirekanyan/outline/MainTopAppBar.kt +++ b/app/src/main/java/org/sirekanyan/outline/MainTopAppBar.kt @@ -1,5 +1,6 @@ package org.sirekanyan.outline +import androidx.annotation.StringRes import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.MoreVert @@ -19,9 +20,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -data class MenuItem(val text: String, val icon: ImageVector, val onClick: () -> Unit) +data class MenuItem(@StringRes val text: Int, val icon: ImageVector, val onClick: () -> Unit) @Composable @OptIn(ExperimentalMaterial3Api::class) @@ -57,7 +59,7 @@ private fun MainMenu(visibleItems: List, overflowItems: List DropdownMenu(isMenuVisible, { isMenuVisible = false }) { overflowItems.forEach { (text, icon, onClick) -> DropdownMenuItem( - text = { Text(text) }, + text = { Text(stringResource(text)) }, leadingIcon = { Icon(icon, null) }, onClick = { isMenuVisible = false diff --git a/app/src/main/java/org/sirekanyan/outline/NotSupportedContent.kt b/app/src/main/java/org/sirekanyan/outline/NotSupportedContent.kt index 4ed139d..f73a42e 100644 --- a/app/src/main/java/org/sirekanyan/outline/NotSupportedContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/NotSupportedContent.kt @@ -20,27 +20,32 @@ fun NotSupportedContent(onDismissRequest: () -> Unit) { val isInstalled = remember { isOutlineInstalled(context) } AlertDialog( icon = { Icon(Icons.Default.Info, null) }, - title = { Text("Not supported") }, + title = { Text(stringResource(R.string.outln_title_not_supported)) }, text = { Text( - text = stringResource(R.string.outln_app_name) + " does not support ss:// links. " + - "Would you like to ${if (isInstalled) "open" else "install"} Outline?", + text = stringResource( + if (isInstalled) { + R.string.outln_text_not_supported_open + } else { + R.string.outln_text_not_supported_install + } + ), ) }, onDismissRequest = onDismissRequest, dismissButton = { TextButton(onClick = onDismissRequest) { - Text("Cancel") + Text(stringResource(R.string.outln_btn_cancel)) } }, confirmButton = { if (isInstalled) { TextButton(onClick = { onDismissRequest(); openOutline(context) }) { - Text("Open Outline") + Text(stringResource(R.string.outln_btn_not_supported_open)) } } else { TextButton(onClick = { onDismissRequest(); installOutline(context) }) { - Text("Install Outline") + Text(stringResource(R.string.outln_btn_not_supported_install)) } } }, diff --git a/app/src/main/java/org/sirekanyan/outline/ext/Context.kt b/app/src/main/java/org/sirekanyan/outline/ext/Context.kt index f8e5149..37afaeb 100644 --- a/app/src/main/java/org/sirekanyan/outline/ext/Context.kt +++ b/app/src/main/java/org/sirekanyan/outline/ext/Context.kt @@ -3,12 +3,13 @@ package org.sirekanyan.outline.ext import android.content.Context import android.content.Intent import android.net.Uri +import org.sirekanyan.outline.R fun Context.openGooglePlay(uri: String) { try { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(uri))) } catch (exception: Exception) { logDebug("Cannot open Google Play", exception) - showToast("Cannot open Google Play") + showToast(R.string.outln_toast_cannot_open_play) } } diff --git a/app/src/main/java/org/sirekanyan/outline/ext/CoroutineScope.kt b/app/src/main/java/org/sirekanyan/outline/ext/CoroutineScope.kt index 7dc346b..63b9596 100644 --- a/app/src/main/java/org/sirekanyan/outline/ext/CoroutineScope.kt +++ b/app/src/main/java/org/sirekanyan/outline/ext/CoroutineScope.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.job import kotlinx.coroutines.plus +import org.sirekanyan.outline.R import java.net.ConnectException import java.net.UnknownHostException @@ -24,10 +25,10 @@ fun rememberStateScope(): CoroutineScope { } when (throwable) { is UnknownHostException, is ConnectException -> { - context.showToast("Check network connection") + context.showToast(R.string.outln_toast_check_network) } else -> { - context.showToast("Something went wrong") + context.showToast(R.string.outln_toast_something_wrong) } } } diff --git a/app/src/main/java/org/sirekanyan/outline/ext/Toast.kt b/app/src/main/java/org/sirekanyan/outline/ext/Toast.kt index 36d71cb..756b5b9 100644 --- a/app/src/main/java/org/sirekanyan/outline/ext/Toast.kt +++ b/app/src/main/java/org/sirekanyan/outline/ext/Toast.kt @@ -4,10 +4,6 @@ import android.content.Context import android.widget.Toast import androidx.annotation.StringRes -fun Context.showToast(text: String) { - Toast.makeText(this, text, Toast.LENGTH_SHORT).show() -} - fun Context.showToast(@StringRes text: Int) { Toast.makeText(this, text, Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/org/sirekanyan/outline/feature/keys/KeysErrorContent.kt b/app/src/main/java/org/sirekanyan/outline/feature/keys/KeysErrorContent.kt index d9b6c5c..59b7705 100644 --- a/app/src/main/java/org/sirekanyan/outline/feature/keys/KeysErrorContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/feature/keys/KeysErrorContent.kt @@ -29,7 +29,7 @@ fun KeysErrorContent(insets: PaddingValues, onRetry: () -> Unit) { textAlign = TextAlign.Center, ) Button(onClick = onRetry) { - Text(text = "Try again") + Text(text = stringResource(R.string.outln_btn_try_again)) } } } diff --git a/app/src/main/java/org/sirekanyan/outline/feature/sort/SortBottomSheet.kt b/app/src/main/java/org/sirekanyan/outline/feature/sort/SortBottomSheet.kt index 149d944..b71cb99 100644 --- a/app/src/main/java/org/sirekanyan/outline/feature/sort/SortBottomSheet.kt +++ b/app/src/main/java/org/sirekanyan/outline/feature/sort/SortBottomSheet.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import kotlinx.coroutines.launch +import org.sirekanyan.outline.R import org.sirekanyan.outline.ui.SimpleBottomSheet @Composable @@ -25,7 +26,7 @@ fun SortBottomSheet( ) { val coroutineScope = rememberCoroutineScope() SimpleBottomSheet( - title = "Sort by…", + title = stringResource(R.string.outln_sorting_by), onDismissRequest = onDismissRequest, items = { sheetState -> Sorting.entries.forEach { option -> diff --git a/app/src/main/java/org/sirekanyan/outline/ui/AboutDialog.kt b/app/src/main/java/org/sirekanyan/outline/ui/AboutDialog.kt index b128ed9..b501594 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/AboutDialog.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/AboutDialog.kt @@ -3,6 +3,7 @@ package org.sirekanyan.outline.ui import android.content.Intent import android.content.Intent.ACTION_SENDTO import android.net.Uri +import androidx.annotation.StringRes import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn @@ -77,7 +78,7 @@ fun AboutDialogContent(onDismiss: () -> Unit) { confirmButton = { val context = LocalContext.current val clipboard = LocalClipboardManager.current - AboutItem(Icons.Default.Email, "Send feedback") { + AboutItem(Icons.Default.Email, R.string.outln_btn_send_feedback) { val email = "outline@sirekanyan.org" val subject = Uri.encode("Feedback: $appName $appVersion") val intent = Intent(ACTION_SENDTO, Uri.parse("mailto:$email?subject=$subject")) @@ -86,13 +87,13 @@ fun AboutDialogContent(onDismiss: () -> Unit) { } catch (exception: Exception) { logDebug("Cannot find email app", exception) clipboard.setText(AnnotatedString(email)) - context.showToast("Email is copied") + context.showToast(R.string.outln_toast_email_copied) } } if (isPlayFlavor() || isDebugBuild()) { val packageName = "org.sirekanyan.outline" val playUri = "https://play.google.com/store/apps/details?id=$packageName" - AboutItem(IconPlayStore, "Rate on Play Store") { + AboutItem(IconPlayStore, R.string.outln_btn_rate_on_play_store) { context.openGooglePlay(playUri) onDismiss() } @@ -102,7 +103,7 @@ fun AboutDialogContent(onDismiss: () -> Unit) { } @Composable -private fun AboutItem(icon: ImageVector, text: String, onClick: () -> Unit) { +private fun AboutItem(icon: ImageVector, @StringRes text: Int, onClick: () -> Unit) { TextButton( onClick = onClick, modifier = Modifier.fillMaxWidth().heightIn(min = 56.dp), @@ -110,7 +111,7 @@ private fun AboutItem(icon: ImageVector, text: String, onClick: () -> Unit) { ) { Icon(icon, null, Modifier.padding(horizontal = 4.dp)) Text( - text = text, + text = stringResource(text), modifier = Modifier.weight(1f).padding(horizontal = 8.dp), maxLines = 1, overflow = TextOverflow.Ellipsis, diff --git a/app/src/main/java/org/sirekanyan/outline/ui/AddKeyButton.kt b/app/src/main/java/org/sirekanyan/outline/ui/AddKeyButton.kt index da2df57..3560284 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/AddKeyButton.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/AddKeyButton.kt @@ -23,7 +23,9 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import org.sirekanyan.outline.R @Composable fun AddKeyButton(isVisible: Boolean, isLoading: Boolean, onClick: () -> Unit) { @@ -43,7 +45,7 @@ fun AddKeyButton(isVisible: Boolean, isLoading: Boolean, onClick: () -> Unit) { } else { Icon(Icons.Default.Add, null) Spacer(Modifier.width(12.dp)) - Text("Add key") + Text(stringResource(R.string.outln_btn_add_key)) Spacer(Modifier.width(4.dp)) } } diff --git a/app/src/main/java/org/sirekanyan/outline/ui/AddServerContent.kt b/app/src/main/java/org/sirekanyan/outline/ui/AddServerContent.kt index 013a169..b21da3e 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/AddServerContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/AddServerContent.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import kotlinx.coroutines.CoroutineScope @@ -103,9 +104,9 @@ fun AddServerContent(router: Router) { val state = rememberAddServerState(router) Column { DialogToolbar( - title = "Add server", + title = R.string.outln_title_add_server, onCloseClick = { router.dialog = null }, - action = "Add" to { state.onAddClicked() }, + action = R.string.outln_action_add to { state.onAddClicked() }, isLoading = state.isLoading, ) val focusRequester = remember { FocusRequester() } @@ -119,7 +120,7 @@ fun AddServerContent(router: Router) { .fillMaxWidth() .padding(16.dp, 24.dp, 16.dp, 8.dp) .focusRequester(focusRequester), - label = { Text("Management API URL") }, + label = { Text(stringResource(R.string.outln_label_server_url)) }, placeholder = { Text("https://xx.xx.xx.xx:xxx/xxxxx", Modifier.alpha(0.38f)) }, isError = state.error.isNotEmpty(), supportingText = { Text(state.error) }, @@ -136,7 +137,7 @@ fun AddServerContent(router: Router) { onCheckedChange = { state.insecure = it }, ) Text( - text = "Allow insecure connection", + text = stringResource(R.string.outln_label_allow_insecure), modifier = Modifier.padding(end = 16.dp), style = MaterialTheme.typography.bodySmall, color = LocalContentColor.current.copy(alpha = 0.66f), diff --git a/app/src/main/java/org/sirekanyan/outline/ui/ConfirmationAlertDialog.kt b/app/src/main/java/org/sirekanyan/outline/ui/ConfirmationAlertDialog.kt index 1a2ee22..b9a3e9d 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/ConfirmationAlertDialog.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/ConfirmationAlertDialog.kt @@ -8,12 +8,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.sirekanyan.outline.R import org.sirekanyan.outline.api.model.Key @Composable fun DeleteKeyContent(key: Key, onDismiss: () -> Unit, onConfirm: () -> Unit) { ConfirmationAlertDialog( - text = "Are you sure you want to delete the key named \"${key.nameOrDefault}\"?", + text = stringResource(R.string.outln_text_confirm_key, key.nameOrDefault), onDismiss = onDismiss, onConfirm = onConfirm, ) @@ -22,7 +24,7 @@ fun DeleteKeyContent(key: Key, onDismiss: () -> Unit, onConfirm: () -> Unit) { @Composable fun DeleteServerContent(serverName: String, onDismiss: () -> Unit, onConfirm: () -> Unit) { ConfirmationAlertDialog( - text = "Are you sure you want to delete the server named \"$serverName\"?", + text = stringResource(R.string.outln_text_confirm_server, serverName), onDismiss = onDismiss, onConfirm = onConfirm, ) @@ -32,13 +34,22 @@ fun DeleteServerContent(serverName: String, onDismiss: () -> Unit, onConfirm: () private fun ConfirmationAlertDialog(text: String, onDismiss: () -> Unit, onConfirm: () -> Unit) { AlertDialog( icon = { Icon(Icons.Default.Delete, null) }, - title = { Text("Confirmation") }, + title = { Text(stringResource(R.string.outln_title_confirm)) }, text = { Text(text) }, onDismissRequest = onDismiss, - dismissButton = { TextButton(onDismiss) { Text("Cancel") } }, + dismissButton = { + TextButton(onClick = onDismiss) { + Text( + text = stringResource(R.string.outln_btn_cancel), + ) + } + }, confirmButton = { TextButton(onClick = { onConfirm(); onDismiss() }) { - Text("Delete", color = MaterialTheme.colorScheme.error) + Text( + text = stringResource(R.string.outln_btn_confirm_delete), + color = MaterialTheme.colorScheme.error, + ) } }, ) diff --git a/app/src/main/java/org/sirekanyan/outline/ui/DialogToolbar.kt b/app/src/main/java/org/sirekanyan/outline/ui/DialogToolbar.kt index a0e0f93..8a74d6e 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/DialogToolbar.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/DialogToolbar.kt @@ -1,5 +1,6 @@ package org.sirekanyan.outline.ui +import androidx.annotation.StringRes import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons @@ -16,19 +17,20 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @Composable @OptIn(ExperimentalMaterial3Api::class) fun DialogToolbar( - title: String, + @StringRes title: Int, onCloseClick: () -> Unit, - action: Pair Unit>, + action: Pair Unit>, isLoading: Boolean, ) { TopAppBar( - title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, + title = { Text(stringResource(title), maxLines = 1, overflow = TextOverflow.Ellipsis) }, navigationIcon = { IconButton({ onCloseClick() }) { Icon(Icons.Default.Close, null) } }, @@ -37,7 +39,7 @@ fun DialogToolbar( CircularProgressIndicator(Modifier.size(56.dp).padding(16.dp), strokeWidth = 2.dp) } else { val (actionName, onActionClick) = action - TextButton({ onActionClick() }) { Text(actionName) } + TextButton({ onActionClick() }) { Text(stringResource(actionName)) } } }, colors = TopAppBarDefaults.topAppBarColors( diff --git a/app/src/main/java/org/sirekanyan/outline/ui/DrawerContent.kt b/app/src/main/java/org/sirekanyan/outline/ui/DrawerContent.kt index 3260a20..0e972e6 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/DrawerContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/DrawerContent.kt @@ -118,7 +118,7 @@ private fun DrawerSheetContent(state: MainState, insets: PaddingValues) { } DrawerItem( icon = Icons.Default.Add, - label = "Add server", + label = stringResource(R.string.outln_drawer_add), onClick = { state.dialog = AddServerDialog }, @@ -127,7 +127,7 @@ private fun DrawerSheetContent(state: MainState, insets: PaddingValues) { if (servers.isNotEmpty()) { DrawerItem( icon = Icons.Default.Search, - label = "All servers", + label = stringResource(R.string.outln_drawer_all), onClick = { state.page = HelloPage state.closeDrawer() @@ -141,7 +141,7 @@ private fun DrawerSheetContent(state: MainState, insets: PaddingValues) { val scope = rememberCoroutineScope() DrawerItem( icon = Icons.Default.Warning, - label = "Reset database", + label = stringResource(R.string.outln_drawer_reset), onClick = { scope.launch(IO) { debugDao.reset() @@ -151,7 +151,7 @@ private fun DrawerSheetContent(state: MainState, insets: PaddingValues) { } DrawerItem( icon = Icons.Default.Info, - label = "About", + label = stringResource(R.string.outln_drawer_about), onClick = { state.dialog = AboutDialog }, ) } diff --git a/app/src/main/java/org/sirekanyan/outline/ui/KeyBottomSheet.kt b/app/src/main/java/org/sirekanyan/outline/ui/KeyBottomSheet.kt index 0cf3be5..a040deb 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/KeyBottomSheet.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/KeyBottomSheet.kt @@ -15,8 +15,10 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import kotlinx.coroutines.launch +import org.sirekanyan.outline.R import org.sirekanyan.outline.api.model.Key import org.sirekanyan.outline.ext.showToast import org.sirekanyan.outline.ui.icons.IconCopy @@ -37,11 +39,11 @@ fun KeyBottomSheet( onDismissRequest = onDismissRequest, items = { sheetState -> ListItem( - headlineContent = { Text("Copy") }, + headlineContent = { Text(stringResource(R.string.outln_sheet_copy)) }, leadingContent = { Icon(IconCopy, null) }, modifier = Modifier.clickable { localClipboard.setText(AnnotatedString(key.accessUrl)) - localContext.showToast("Copied") + localContext.showToast(R.string.outln_toast_copied) coroutineScope.launch { sheetState.hide() }.invokeOnCompletion { @@ -50,7 +52,7 @@ fun KeyBottomSheet( }, ) ListItem( - headlineContent = { Text("Share") }, + headlineContent = { Text(stringResource(R.string.outln_sheet_share)) }, leadingContent = { Icon(Icons.Default.Share, null) }, modifier = Modifier.clickable { coroutineScope.launch { @@ -66,7 +68,7 @@ fun KeyBottomSheet( }, ) ListItem( - headlineContent = { Text("Edit") }, + headlineContent = { Text(stringResource(R.string.outln_sheet_edit)) }, leadingContent = { Icon(Icons.Default.Edit, null) }, modifier = Modifier.clickable { onEditClick() @@ -74,7 +76,7 @@ fun KeyBottomSheet( }, ) ListItem( - headlineContent = { Text("Delete") }, + headlineContent = { Text(stringResource(R.string.outln_sheet_delete)) }, leadingContent = { Icon(Icons.Default.Delete, null) }, modifier = Modifier.clickable { onDeleteClick() diff --git a/app/src/main/java/org/sirekanyan/outline/ui/RenameContent.kt b/app/src/main/java/org/sirekanyan/outline/ui/RenameContent.kt index 990ef45..adfb622 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/RenameContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/RenameContent.kt @@ -1,5 +1,6 @@ package org.sirekanyan.outline.ui +import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -15,6 +16,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp @@ -68,7 +70,7 @@ class RenameState( fun RenameContent( state: RenameState, router: Router, - dialogTitle: String, + @StringRes dialogTitle: Int, initialName: String, defaultName: String, ) { @@ -79,7 +81,7 @@ fun RenameContent( DialogToolbar( title = dialogTitle, onCloseClick = { router.dialog = null }, - action = "Save" to { + action = R.string.outln_action_save to { val newName = draft.text.ifBlank { defaultName } state.onSaveClicked(newName) }, @@ -96,7 +98,7 @@ fun RenameContent( .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 24.dp) .focusRequester(focusRequester), - label = { Text("Name") }, + label = { Text(stringResource(R.string.outln_label_name)) }, placeholder = { Text(defaultName) }, isError = state.error.isNotEmpty(), supportingText = { Text(state.error) }, diff --git a/app/src/main/java/org/sirekanyan/outline/ui/RenameKeyContent.kt b/app/src/main/java/org/sirekanyan/outline/ui/RenameKeyContent.kt index 1bafc53..83c5111 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/RenameKeyContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/RenameKeyContent.kt @@ -3,6 +3,7 @@ package org.sirekanyan.outline.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext +import org.sirekanyan.outline.R import org.sirekanyan.outline.Router import org.sirekanyan.outline.api.model.Key import org.sirekanyan.outline.app @@ -33,5 +34,5 @@ private class RenameKeyDelegate( fun RenameKeyContent(router: Router, key: Key) { val delegate = rememberRenameKeyDelegate(key) val state = rememberRenameState(router, delegate) - RenameContent(state, router, "Edit key", key.name, key.defaultName) + RenameContent(state, router, R.string.outln_title_edit_key, key.name, key.defaultName) } diff --git a/app/src/main/java/org/sirekanyan/outline/ui/RenameServerContent.kt b/app/src/main/java/org/sirekanyan/outline/ui/RenameServerContent.kt index bddc8a0..e239451 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/RenameServerContent.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/RenameServerContent.kt @@ -3,6 +3,7 @@ package org.sirekanyan.outline.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext +import org.sirekanyan.outline.R import org.sirekanyan.outline.Router import org.sirekanyan.outline.SelectedPage import org.sirekanyan.outline.api.model.Server @@ -31,5 +32,5 @@ private class RenameServerDelegate( fun RenameServerContent(router: Router, server: Server) { val delegate = rememberRenameServerDelegate(router, server) val state = rememberRenameState(router, delegate) - RenameContent(state, router, "Edit server", server.name, server.getHost()) + RenameContent(state, router, R.string.outln_title_edit_server, server.name, server.getHost()) } diff --git a/app/src/main/java/org/sirekanyan/outline/ui/SearchField.kt b/app/src/main/java/org/sirekanyan/outline/ui/SearchField.kt index f7ef7e3..c9765aa 100644 --- a/app/src/main/java/org/sirekanyan/outline/ui/SearchField.kt +++ b/app/src/main/java/org/sirekanyan/outline/ui/SearchField.kt @@ -12,6 +12,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.res.stringResource +import org.sirekanyan.outline.R @Composable fun SearchField(query: String, onQueryChange: (String) -> Unit) { @@ -26,7 +28,7 @@ fun SearchField(query: String, onQueryChange: (String) -> Unit) { singleLine = true, ) if (query.isEmpty()) { - Text("Search…", color = contentColor.copy(alpha = 0.38f)) + Text(stringResource(R.string.outln_hint_search), color = contentColor.copy(alpha = 0.38f)) } LaunchedEffect(Unit) { focusRequester.requestFocus() diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000..8b49847 --- /dev/null +++ b/app/src/main/res/values-ru/strings.xml @@ -0,0 +1,77 @@ + + + + Outline Keeper + Отмена + Скопировано + Проверьте подключение к сети + Что-то пошло не так + Не удалось открыть Google Play + + + Добавить сервер + Все сервера + Очистить данные + О приложении + + + Сортировать по… + Изменить + Удалить + Поиск + + + Сортировать по… + Id + Имя + Трафик + + + Поиск… + Добавить сервер + + + Добавить ключ + Проверьте подключение к сети + Повторить + + + Скопировать + Поделиться + Изменить + Удалить + + + Добавить сервер + Management API URL + Разрешить небезопасное соединение + Добавить + Проверьте URL и повторите снова + Не удалось установить безопасное соединение + + + Не поддерживается + Outline Keeper не поддерживает ссылки вида ss://. Хотите открыть приложение Outline? + Outline Keeper не поддерживает ссылки вида ss://. Хотите установить приложение Outline? + Открыть Outline + Установить Outline + + + Изменить сервер + Изменить ключ + Имя + Сохранить + Проверьте имя и повторите снова + + + Подтверждение + Удалить + Вы точно хотите удалить сервер с именем «%s»? + Вы точно хотите удалить ключ с именем «%s»? + + + Обратная связь + Оценить на Play Store + E-mail скопирован + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4d40ba3..494c278 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,10 +1,77 @@ + + Outline Keeper + Cancel + Copied + Check network connection + Something went wrong + Cannot open Google Play + + + Add server + All servers + Reset database + About + + + Sort by… + Edit + Delete + Search + + + Sort by… Id Name Traffic + + + Search… + Add server + + + Add key Check your network connection + Try again + + + Copy + Share + Edit + Delete + + + Add server + Management API URL + Allow insecure connection + Add Check URL or try again Cannot establish a secure connection + + + Not supported + Outline Keeper does not support ss:// links. Would you like to open Outline? + Outline Keeper does not support ss:// links. Would you like to install Outline? + Open Outline + Install Outline + + + Edit server + Edit key + Name + Save Check name or try again + + + Confirmation + Delete + Are you sure you want to delete the server named \"%s\"? + Are you sure you want to delete the key named \"%s\"? + + + Send feedback + Rate on Play Store + Email is copied + diff --git a/app/src/play/res/values/strings.xml b/app/src/play/res/values/strings.xml index 1c1fadc..1bc8c70 100644 --- a/app/src/play/res/values/strings.xml +++ b/app/src/play/res/values/strings.xml @@ -1,4 +1,4 @@ - GitHub - "https://github.com/sirekanian/outline" + GitHub + "https://github.com/sirekanian/outline"