diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index 2435f012bd..1b08da4697 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -25,9 +25,9 @@ import chat.simplex.app.views.WelcomeView import chat.simplex.app.views.chat.ChatView import chat.simplex.app.views.chatlist.ChatListView import chat.simplex.app.views.chatlist.openChat -import chat.simplex.app.views.helpers.AlertManager -import chat.simplex.app.views.helpers.withApi -import chat.simplex.app.views.newchat.* +import chat.simplex.app.views.helpers.* +import chat.simplex.app.views.newchat.connectViaUri +import chat.simplex.app.views.newchat.withUriAction import java.util.concurrent.TimeUnit //import kotlinx.serialization.decodeFromString @@ -122,10 +122,18 @@ fun connectIfOpenedViaUri(uri: Uri, chatModel: ChatModel) { chatModel.appOpenUrl.value = uri } else { withUriAction(uri) { action -> + val title = when (action) { + "contact" -> generalGetString(R.string.connect_via_contact_link) + "invitation" -> generalGetString(R.string.connect_via_invitation_link) + else -> { + Log.e(TAG, "URI has unexpected action. Alert shown.") + action + } + } AlertManager.shared.showAlertMsg( - title = "Connect via $action link?", - text = "Your profile will be sent to the contact that you received this link from.", - confirmText = "Connect", + title = title, + text = generalGetString(R.string.profile_will_be_sent_to_contact_sending_link), + confirmText = generalGetString(R.string.connect_via_link_verb), onConfirm = { withApi { Log.d(TAG, "connectIfOpenedViaUri: connecting") diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt index 5506c72daf..73397f1908 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt @@ -41,6 +41,7 @@ class SimplexApp: Application(), LifecycleEventObserver { override fun onCreate() { super.onCreate() + context = this ProcessLifecycleOwner.get().lifecycle.addObserver(this) withApi { val user = chatController.apiGetActiveUser() @@ -65,9 +66,10 @@ class SimplexApp: Application(), LifecycleEventObserver { } companion object { + lateinit var context: SimplexApp private set + init { val socketName = "local.socket.address.listen.native.cmd2" - val s = Semaphore(0) thread(name="stdout/stderr pipe") { Log.d(TAG, "starting server") @@ -82,7 +84,7 @@ class SimplexApp: Application(), LifecycleEventObserver { val inStreamReader = InputStreamReader(inStream) val input = BufferedReader(inStreamReader) - while(true) { + while (true) { val line = input.readLine() ?: break Log.w("$TAG (stdout/stderr)", line) logbuffer.add(line) diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt index a7e0bada62..8a4dbe9235 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -7,8 +7,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.* import androidx.compose.ui.text.style.TextDecoration +import chat.simplex.app.R import chat.simplex.app.ui.theme.SecretColor import chat.simplex.app.ui.theme.SimplexBlue +import chat.simplex.app.views.helpers.generalGetString import kotlinx.datetime.* import kotlinx.serialization.* import kotlinx.serialization.descriptors.* @@ -267,12 +269,12 @@ data class Chat ( @Serializable sealed class NetworkStatus { - val statusString: String get() = if (this is Connected) "Server connected" else "Connecting server…" + val statusString: String get() = if (this is Connected) generalGetString(R.string.server_connected) else generalGetString(R.string.server_connecting) val statusExplanation: String get() = - when { - this is Connected -> "You are connected to the server used to receive messages from this contact." - this is Error -> "Trying to connect to the server used to receive messages from this contact (error: $error)." - else -> "Trying to connect to the server used to receive messages from this contact." + when (this) { + is Connected -> generalGetString(R.string.connected_to_server_to_receive_messages_from_contact) + is Error -> String.format(generalGetString(R.string.trying_to_connect_to_server_to_receive_messages_with_error), error) + else -> generalGetString(R.string.trying_to_connect_to_server_to_receive_messages) } @Serializable @SerialName("unknown") class Unknown: NetworkStatus() @@ -568,7 +570,7 @@ data class ChatItem ( id: Long = 1, dir: CIDirection = CIDirection.DirectRcv(), ts: Instant = Clock.System.now(), - text: String = "this item is deleted", + text: String = "this item is deleted", // sample not localized status: CIStatus = CIStatus.RcvRead() ) = ChatItem( @@ -694,25 +696,25 @@ sealed class CIContent: ItemContent { @Serializable @SerialName("sndDeleted") class SndDeleted(val deleteMode: CIDeleteMode): CIContent() { - override val text get() = "deleted" + override val text get() = generalGetString(R.string.deleted_description) override val msgContent get() = null } @Serializable @SerialName("rcvDeleted") class RcvDeleted(val deleteMode: CIDeleteMode): CIContent() { - override val text get() = "deleted" + override val text get() = generalGetString(R.string.deleted_description) override val msgContent get() = null } @Serializable @SerialName("sndFileInvitation") class SndFileInvitation(val fileId: Long, val filePath: String): CIContent() { - override val text get() = "sending files is not supported yet" + override val text get() = generalGetString(R.string.sending_files_not_yet_supported) override val msgContent get() = null } @Serializable @SerialName("rcvFileInvitation") class RcvFileInvitation(val rcvFileTransfer: RcvFileTransfer): CIContent() { - override val text get() = "receiving files is not supported yet" + override val text get() = generalGetString(R.string.receiving_files_not_yet_supported) override val msgContent get() = null } } @@ -729,7 +731,7 @@ class CIQuote ( override val text: String get() = content.text fun sender(user: User): String? = when (chatDir) { - is CIDirection.DirectSnd -> "you" + is CIDirection.DirectSnd -> generalGetString(R.string.sender_you_pronoun) is CIDirection.DirectRcv -> null is CIDirection.GroupSnd -> user.displayName is CIDirection.GroupRcv -> chatDir.groupMember.memberProfile.displayName @@ -782,7 +784,7 @@ object MsgContentSerializer : KSerializer { return if (json is JsonObject) { if ("type" in json) { val t = json["type"]?.jsonPrimitive?.content ?: "" - val text = json["text"]?.jsonPrimitive?.content ?: "unknown message format" + val text = json["text"]?.jsonPrimitive?.content ?: generalGetString(R.string.unknown_message_format) when (t) { "text" -> MsgContent.MCText(text) "link" -> { @@ -792,10 +794,10 @@ object MsgContentSerializer : KSerializer { else -> MsgContent.MCUnknown(t, text, json) } } else { - MsgContent.MCUnknown(text = "invalid message format", json = json) + MsgContent.MCUnknown(text = generalGetString(R.string.invalid_message_format), json = json) } } else { - MsgContent.MCUnknown(text = "invalid message format", json = json) + MsgContent.MCUnknown(text = generalGetString(R.string.invalid_message_format), json = json) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index e605b53efd..0234dfce44 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import chat.simplex.app.* +import chat.simplex.app.R import chat.simplex.app.views.helpers.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -182,8 +183,8 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt else -> { Log.e(TAG, "setUserSMPServers bad response: ${r.responseType} ${r.details}") AlertManager.shared.showAlertMsg( - "Error saving SMP servers", - "Make sure SMP server addresses are in correct format, line separated and are not duplicated." + generalGetString(R.string.error_saving_smp_servers), + generalGetString(R.string.ensure_smp_server_address_are_correct_format_and_unique) ) false } @@ -202,15 +203,17 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt when { r is CR.SentConfirmation || r is CR.SentInvitation -> return true r is CR.ContactAlreadyExists -> { - AlertManager.shared.showAlertMsg("Contact already exists", - "You are already connected to ${r.contact.displayName} via this link." + AlertManager.shared.showAlertMsg( + generalGetString(R.string.contact_already_exists), + String.format(generalGetString(R.string.you_are_already_connected_to_vName_via_this_link), r.contact.displayName) ) return false } r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat && r.chatError.errorType is ChatErrorType.InvalidConnReq -> { - AlertManager.shared.showAlertMsg("Invalid connection link", - "Please check that you used the correct link or ask your contact to send you another one." + AlertManager.shared.showAlertMsg( + generalGetString(R.string.invalid_connection_link), + generalGetString(R.string.please_check_correct_link_and_maybe_ask_for_a_new_one) ) return false } @@ -229,8 +232,8 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt val e = r.chatError if (e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.ContactGroups) { AlertManager.shared.showAlertMsg( - "Can't delete contact!", - "Contact ${e.errorType.contact.displayName} cannot be deleted, it is a member of the group(s) ${e.errorType.groupNames}." + generalGetString(R.string.cannot_delete_contact), + String.format(generalGetString(R.string.contact_cannot_be_deleted_as_they_are_in_groups), e.errorType.contact.displayName, e.errorType.groupNames) ) } } @@ -410,35 +413,22 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt Row { Icon( Icons.Outlined.Bolt, - contentDescription = "Instant notifications", + contentDescription = generalGetString(R.string.icon_descr_instant_notifications), ) - Text("Private instant notifications!", fontWeight = FontWeight.Bold) + Text(generalGetString(R.string.private_instant_notifications), fontWeight = FontWeight.Bold) } }, text = { Column { Text( - buildAnnotatedString { - append("To preserve your privacy, instead of push notifications the app has a ") - withStyle(SpanStyle(fontWeight = FontWeight.Medium)) { - append("SimpleX background service") - } - append(" – it uses a few percent of the battery per day.") - }, + annotatedStringResource(R.string.to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery), Modifier.padding(bottom = 8.dp) ) - Text( - buildAnnotatedString { - withStyle(SpanStyle(fontWeight = FontWeight.Medium)) { - append("It can be disabled via settings") - } - append(" – notifications will still be shown while the app is running.") - } - ) + Text(annotatedStringResource(R.string.it_can_disabled_via_settings_notifications_still_shown)) } }, confirmButton = { - Button(onClick = AlertManager.shared::hideAlert) { Text("Ok") } + Button(onClick = AlertManager.shared::hideAlert) { Text(generalGetString(R.string.ok)) } } ) } @@ -716,7 +706,7 @@ sealed class CR { is Invalid -> str } - fun noDetails(): String ="${responseType}: no details" + fun noDetails(): String ="${responseType}: " + generalGetString(R.string.no_details) } abstract class TerminalItem { diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt index 3f4b1c6abf..7aeecca387 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt @@ -7,7 +7,7 @@ val Purple500 = Color(0xFF6200EE) val Purple700 = Color(0xFF3700B3) val Teal200 = Color(0xFF03DAC5) val Gray = Color(0x22222222) -val SimplexBlue = Color(0, 136, 255, 255) +val SimplexBlue = Color(0, 136, 255, 255) // If this value changes also need to update #0088ff in string resource files val SimplexGreen = Color(98, 196, 103, 255) val SecretColor = Color(0x40808080) val LightGray = Color(241, 242, 246, 255) diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt index 3719e860a6..8b1ccfeded 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt @@ -5,7 +5,7 @@ import androidx.compose.material.* import androidx.compose.runtime.Composable private val DarkColorPalette = darkColors( - primary = SimplexBlue, + primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files primaryVariant = SimplexGreen, secondary = DarkGray, // background = Color.Black, @@ -20,7 +20,7 @@ private val DarkColorPalette = darkColors( // onError: Color = Color.Black, ) private val LightColorPalette = lightColors( - primary = SimplexBlue, + primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files primaryVariant = SimplexGreen, secondary = LightGray, // background = Color.White, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt index 8f828f4e19..2042ef469e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -19,9 +19,7 @@ import androidx.compose.ui.unit.sp import chat.simplex.app.model.* import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.SendMsgView -import chat.simplex.app.views.helpers.CloseSheetBar -import chat.simplex.app.views.helpers.withApi -import chat.simplex.app.views.newchat.ModalManager +import chat.simplex.app.views.helpers.* import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding import kotlinx.coroutines.launch diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt index 996b0bd2e1..675655bb9d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt @@ -17,6 +17,7 @@ import chat.simplex.app.R import chat.simplex.app.SimplexService import chat.simplex.app.model.ChatModel import chat.simplex.app.model.Profile +import chat.simplex.app.views.helpers.generalGetString import chat.simplex.app.views.helpers.withApi import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding @@ -39,22 +40,22 @@ fun WelcomeView(chatModel: ChatModel) { ) { Image( painter = painterResource(R.drawable.logo), - contentDescription = "Simplex Logo", + contentDescription = generalGetString(R.string.image_descr_simplex_logo), modifier = Modifier.padding(vertical = 15.dp) ) Text( - "You control your chat!", + generalGetString(R.string.you_control_your_chat), style = MaterialTheme.typography.h4, color = MaterialTheme.colors.onBackground ) Text( - "The messaging and application platform protecting your privacy and security.", + generalGetString(R.string.the_messaging_and_app_platform_protecting_your_privacy_and_security), style = MaterialTheme.typography.body1, color = MaterialTheme.colors.onBackground ) Spacer(Modifier.height(8.dp)) Text( - "We don't store any of your contacts or messages (once delivered) on the servers.", + generalGetString(R.string.we_do_not_store_contacts_or_messages_on_servers), style = MaterialTheme.typography.body1, color = MaterialTheme.colors.onBackground ) @@ -79,19 +80,19 @@ fun CreateProfilePanel(chatModel: ChatModel) { modifier=Modifier.fillMaxSize() ) { Text( - "Create profile", + generalGetString(R.string.create_profile), style = MaterialTheme.typography.h4, color = MaterialTheme.colors.onBackground, modifier = Modifier.padding(vertical = 5.dp) ) Text( - "Your profile is stored on your device and shared only with your contacts.", + generalGetString(R.string.your_profile_is_stored_on_your_decide_and_shared_only_with_your_contacts), style = MaterialTheme.typography.body1, color = MaterialTheme.colors.onBackground ) Spacer(Modifier.height(10.dp)) Text( - "Display Name", + generalGetString(R.string.display_name), style = MaterialTheme.typography.h6, color = MaterialTheme.colors.onBackground, modifier = Modifier.padding(bottom = 3.dp) @@ -113,7 +114,7 @@ fun CreateProfilePanel(chatModel: ChatModel) { ), singleLine = true ) - val errorText = if(!isValidDisplayName(displayName)) "Display name cannot contain whitespace." else "" + val errorText = if(!isValidDisplayName(displayName)) generalGetString(R.string.display_name_cannot_contain_whitespace) else "" Text( errorText, @@ -123,7 +124,7 @@ fun CreateProfilePanel(chatModel: ChatModel) { Spacer(Modifier.height(3.dp)) Text( - "Full Name (Optional)", + generalGetString(R.string.full_name_optional__prompt), style = MaterialTheme.typography.h6, color = MaterialTheme.colors.onBackground, modifier = Modifier.padding(bottom = 5.dp) @@ -157,6 +158,6 @@ fun CreateProfilePanel(chatModel: ChatModel) { } }, enabled = (displayName.isNotEmpty() && isValidDisplayName(displayName)) - ) { Text("Create") } + ) { Text(generalGetString(R.string.create_profile_button)) } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt index ed43311f96..d37c752cbe 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.helpers.* @@ -29,9 +30,9 @@ fun ChatInfoView(chatModel: ChatModel, close: () -> Unit) { close = close, deleteContact = { AlertManager.shared.showAlertMsg( - title = "Delete contact?", - text = "Contact and all messages will be deleted - this cannot be undone!", - confirmText = "Delete", + title = generalGetString(R.string.delete_contact__question), + text = generalGetString(R.string.delete_contact_all_messages_deleted_cannot_undo_warning), + confirmText = generalGetString(R.string.delete_verb), onConfirm = { val cInfo = chat.chatInfo withApi { @@ -96,7 +97,8 @@ fun ChatInfoLayout(chat: Chat, close: () -> Unit, deleteContact: () -> Unit) { Box(Modifier.padding(48.dp)) { SimpleButton( - "Delete contact", icon = Icons.Outlined.Delete, + generalGetString(R.string.button_delete_contact), + icon = Icons.Outlined.Delete, color = Color.Red, click = deleteContact ) @@ -110,13 +112,13 @@ fun ServerImage(chat: Chat) { val status = chat.serverInfo.networkStatus when { status is Chat.NetworkStatus.Connected -> - Icon(Icons.Filled.Circle, "Connected", tint = MaterialTheme.colors.primaryVariant) + Icon(Icons.Filled.Circle, generalGetString(R.string.icon_descr_server_status_connected), tint = MaterialTheme.colors.primaryVariant) status is Chat.NetworkStatus.Disconnected -> - Icon(Icons.Filled.Pending, "Disconnected", tint = HighOrLowlight) + Icon(Icons.Filled.Pending, generalGetString(R.string.icon_descr_server_status_disconnected), tint = HighOrLowlight) status is Chat.NetworkStatus.Error -> - Icon(Icons.Filled.Error, "Error", tint = HighOrLowlight) + Icon(Icons.Filled.Error, generalGetString(R.string.icon_descr_server_status_error), tint = HighOrLowlight) else -> - Icon(Icons.Outlined.Circle, "Pending", tint = HighOrLowlight) + Icon(Icons.Outlined.Circle, generalGetString(R.string.icon_descr_server_status_pending), tint = HighOrLowlight) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 7c82f7659a..3ffab8066e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -22,13 +22,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.TAG import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.chat.item.ChatItemView import chat.simplex.app.views.chatlist.openChat import chat.simplex.app.views.helpers.* -import chat.simplex.app.views.newchat.ModalManager import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding import kotlinx.coroutines.* @@ -169,7 +169,7 @@ fun ChatInfoToolbar(chat: Chat, back: () -> Unit, info: () -> Unit) { IconButton(onClick = back) { Icon( Icons.Outlined.ArrowBackIos, - "Back", + generalGetString(R.string.back), tint = MaterialTheme.colors.primary, modifier = Modifier.padding(10.dp) ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index 2c1973b054..79e4b71ad4 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -1,6 +1,6 @@ package chat.simplex.app.views.chat -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column import androidx.compose.runtime.* import chat.simplex.app.model.* import chat.simplex.app.views.helpers.ComposeLinkView diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt index 3690e79883..8a16d670ec 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContextItemView.kt @@ -12,10 +12,12 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.CIDirection import chat.simplex.app.model.ChatItem import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.item.* +import chat.simplex.app.views.helpers.generalGetString import kotlinx.datetime.Clock @Composable @@ -50,7 +52,7 @@ fun ContextItemView( }) { Icon( Icons.Outlined.Close, - contentDescription = "Cancel", + contentDescription = generalGetString(R.string.cancel_verb), tint = MaterialTheme.colors.primary, modifier = Modifier.padding(10.dp) ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt index 7d1cb8d05b..7fcda1bd03 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt @@ -1,7 +1,6 @@ package chat.simplex.app.views.chat import android.content.res.Configuration -import android.util.Log import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape @@ -21,13 +20,12 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import chat.simplex.app.TAG +import chat.simplex.app.R import chat.simplex.app.model.* import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.item.* -import chat.simplex.app.views.helpers.getLinkPreview -import chat.simplex.app.views.helpers.withApi +import chat.simplex.app.views.helpers.* import kotlinx.coroutines.delay @Composable @@ -129,7 +127,7 @@ fun SendMsgView( val color = if (msg.value.isNotEmpty()) MaterialTheme.colors.primary else Color.Gray Icon( if (editing) Icons.Filled.Check else Icons.Outlined.ArrowUpward, - "Send Message", + generalGetString(R.string.icon_descr_send_message), tint = Color.White, modifier = Modifier .size(36.dp) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt index be21371f30..ca6e5a71c0 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIMetaView.kt @@ -12,9 +12,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.* import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimplexBlue +import chat.simplex.app.views.helpers.generalGetString import kotlinx.datetime.Clock @Composable @@ -25,7 +27,7 @@ fun CIMetaView(chatItem: ChatItem) { Icon( Icons.Filled.Edit, modifier = Modifier.height(12.dp).padding(end = 1.dp), - contentDescription = "Edited", + contentDescription = generalGetString(R.string.icon_descr_edited), tint = HighOrLowlight, ) } @@ -45,16 +47,16 @@ fun CIMetaView(chatItem: ChatItem) { fun CIStatusView(status: CIStatus) { when (status) { is CIStatus.SndSent -> { - Icon(Icons.Filled.Check, "sent", Modifier.height(12.dp), tint = HighOrLowlight) + Icon(Icons.Filled.Check, generalGetString(R.string.icon_descr_sent_msg_status_sent), Modifier.height(12.dp), tint = HighOrLowlight) } is CIStatus.SndErrorAuth -> { - Icon(Icons.Filled.Close, "unauthorized send", Modifier.height(12.dp), tint = Color.Red) + Icon(Icons.Filled.Close, generalGetString(R.string.icon_descr_sent_msg_status_unauthorized_send), Modifier.height(12.dp), tint = Color.Red) } is CIStatus.SndError -> { - Icon(Icons.Filled.WarningAmber, "send failed", Modifier.height(12.dp), tint = Color.Yellow) + Icon(Icons.Filled.WarningAmber, generalGetString(R.string.icon_descr_sent_msg_status_send_failed), Modifier.height(12.dp), tint = Color.Yellow) } is CIStatus.RcvNew -> { - Icon(Icons.Filled.Circle, "unread", Modifier.height(12.dp), tint = SimplexBlue) + Icon(Icons.Filled.Circle, generalGetString(R.string.icon_descr_received_msg_status_unread), Modifier.height(12.dp), tint = SimplexBlue) } else -> {} } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt index dfce857f10..f067e6f8df 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt @@ -16,8 +16,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.* -import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.helpers.* import kotlinx.datetime.Clock @@ -55,21 +55,21 @@ fun ChatItemView( } if (cItem.isMsgContent) { DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) { - ItemAction("Reply", Icons.Outlined.Reply, onClick = { + ItemAction(generalGetString(R.string.reply_verb), Icons.Outlined.Reply, onClick = { editingItem.value = null quotedItem.value = cItem showMenu = false }) - ItemAction("Share", Icons.Outlined.Share, onClick = { + ItemAction(generalGetString(R.string.share_verb), Icons.Outlined.Share, onClick = { shareText(cxt, cItem.content.text) showMenu = false }) - ItemAction("Copy", Icons.Outlined.ContentCopy, onClick = { + ItemAction(generalGetString(R.string.copy_verb), Icons.Outlined.ContentCopy, onClick = { copyText(cxt, cItem.content.text) showMenu = false }) if (cItem.chatDir.sent && cItem.meta.editable) { - ItemAction("Edit", Icons.Filled.Edit, onClick = { + ItemAction(generalGetString(R.string.edit_verb), Icons.Filled.Edit, onClick = { quotedItem.value = null editingItem.value = cItem msg.value = cItem.content.text @@ -77,7 +77,7 @@ fun ChatItemView( }) } ItemAction( - "Delete", + generalGetString(R.string.delete_verb), Icons.Outlined.Delete, onClick = { showMenu = false @@ -109,8 +109,8 @@ private fun ItemAction(text: String, icon: ImageVector, onClick: () -> Unit, col fun deleteMessageAlertDialog(chatItem: ChatItem, deleteMessage: (Long, CIDeleteMode) -> Unit) { AlertManager.shared.showAlertDialogButtons( - title = "Delete message?", - text = "Message will be deleted - this cannot be undone!", + title = generalGetString(R.string.delete_message__question), + text = generalGetString(R.string.delete_message_cannot_be_undone_warning), buttons = { Row( Modifier @@ -121,13 +121,13 @@ fun deleteMessageAlertDialog(chatItem: ChatItem, deleteMessage: (Long, CIDeleteM Button(onClick = { deleteMessage(chatItem.id, CIDeleteMode.cidmInternal) AlertManager.shared.hideAlert() - }) { Text("For me only") } + }) { Text(generalGetString(R.string.for_me_only)) } // if (chatItem.meta.editable) { // Spacer(Modifier.padding(horizontal = 4.dp)) // Button(onClick = { // deleteMessage(chatItem.id, CIDeleteMode.cidmBroadcast) // AlertManager.shared.hideAlert() -// }) { Text("For everyone") } +// }) { Text(generalGetString(R.string.for_everybody)) } // } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt index 849550e557..5ed2f39f2b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/DeletedItemView.kt @@ -1,9 +1,8 @@ package chat.simplex.app.views.chat.item import android.content.res.Configuration -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.Composable @@ -11,10 +10,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.tooling.preview.* +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import chat.simplex.app.model.* +import chat.simplex.app.model.ChatItem import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt index 306797e628..aa03a7d0cf 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt @@ -1,4 +1,4 @@ -package chat.simplex.app.views.chat +package chat.simplex.app.views.chatlist import android.content.res.Configuration import androidx.compose.foundation.clickable @@ -10,11 +10,15 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.* +import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.helpers.annotatedStringResource +import chat.simplex.app.views.helpers.generalGetString import chat.simplex.app.views.usersettings.simplexTeamUri val bold = SpanStyle(fontWeight = FontWeight.Bold) @@ -27,18 +31,13 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) { ) { val uriHandler = LocalUriHandler.current - Text("Thank you for installing SimpleX Chat!") + Text(generalGetString(R.string.thank_you_for_installing_simplex), lineHeight = 22.sp) Text( - buildAnnotatedString { - append("You can ") - withStyle(SpanStyle(color = MaterialTheme.colors.primary)) { - append("connect to SimpleX Chat founder") - } - append(".") - }, + annotatedStringResource(R.string.you_can_connect_to_simplex_chat_founder), modifier = Modifier.clickable(onClick = { uriHandler.openUri(simplexTeamUri) - }) + }), + lineHeight = 22.sp ) Column( @@ -47,33 +46,24 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) { verticalArrangement = Arrangement.spacedBy(10.dp) ) { Text( - "To start a new chat", - style = MaterialTheme.typography.h2 + generalGetString(R.string.to_start_a_new_chat_help_header), + style = MaterialTheme.typography.h2, + lineHeight = 22.sp ) Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - Text("Tap button") + Text(generalGetString(R.string.chat_help_tap_button)) Icon( Icons.Outlined.PersonAdd, - "Add Contact", + generalGetString(R.string.add_contact), modifier = if (addContact != null) Modifier.clickable(onClick = addContact) else Modifier, ) - Text("above, then:") + Text(generalGetString(R.string.above_then_preposition_continuation)) } - Text( - buildAnnotatedString { - withStyle(bold) { append("Add new contact") } - append(": to create your one-time QR Code for your contact.") - } - ) - Text( - buildAnnotatedString { - withStyle(bold) { append("Scan QR code") } - append(": to connect to your contact who shows QR code to you.") - } - ) + Text(annotatedStringResource(R.string.add_new_contact_to_create_one_time_QR_code), lineHeight = 22.sp) + Text(annotatedStringResource(R.string.scan_QR_code_to_connect_to_contact_who_shows_QR_code), lineHeight = 22.sp) } Column( @@ -81,24 +71,10 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) { horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(10.dp) ) { - Text("To connect via link", style = MaterialTheme.typography.h2) - Text("If you received SimpleX Chat invitation link you can open it in your browser:") - Text( - buildAnnotatedString { - append("\uD83D\uDCBB desktop: scan displayed QR code from the app, via ") - withStyle(bold) { append("Scan QR code") } - append(".") - } - ) - Text( - buildAnnotatedString { - append("\uD83D\uDCF1 mobile: tap ") - withStyle(bold) { append("Open in mobile app") } - append(", then tap ") - withStyle(bold) { append("Connect") } - append(" in the app.") - } - ) + Text(generalGetString(R.string.to_connect_via_link_title), style = MaterialTheme.typography.h2) + Text(generalGetString(R.string.if_you_received_simplex_invitation_link_you_can_open_in_browser), lineHeight = 22.sp) + Text(annotatedStringResource(R.string.desktop_scan_QR_code_from_app_via_scan_QR_code), lineHeight = 22.sp) + Text(annotatedStringResource(R.string.mobile_tap_open_in_mobile_app_then_tap_connect_in_app), lineHeight = 22.sp) } } } @@ -112,6 +88,6 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) { @Composable fun PreviewChatHelpLayout() { SimpleXTheme { - ChatHelpView({}) + ChatHelpView {} } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt index fbd8b235a5..f707e62c8b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt @@ -11,11 +11,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp - +import chat.simplex.app.R import chat.simplex.app.model.* import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.helpers.AlertManager -import chat.simplex.app.views.helpers.withApi +import chat.simplex.app.views.helpers.* import kotlinx.datetime.Clock @Composable @@ -42,9 +41,9 @@ suspend fun openChat(chatModel: ChatModel, cInfo: ChatInfo) { fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) { AlertManager.shared.showAlertDialog( - title = "Accept connection request?", - text = "If you choose to reject sender will NOT be notified.", - confirmText = "Accept", + title = generalGetString(R.string.accept_connection_request__question), + text = generalGetString(R.string.if_you_choose_to_reject_the_sender_will_not_be_notified), + confirmText = generalGetString(R.string.accept_contact_button), onConfirm = { withApi { val contact = chatModel.controller.apiAcceptContactRequest(contactRequest.apiId) @@ -54,7 +53,7 @@ fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel } } }, - dismissText = "Reject", + dismissText = generalGetString(R.string.reject_contact_button), onDismiss = { withApi { chatModel.controller.apiRejectContactRequest(contactRequest.apiId) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt index 1f1c6b912e..704fcd4df9 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt @@ -14,11 +14,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.ToolbarDark import chat.simplex.app.ui.theme.ToolbarLight -import chat.simplex.app.views.chat.ChatHelpView -import chat.simplex.app.views.newchat.ModalManager +import chat.simplex.app.views.helpers.ModalManager +import chat.simplex.app.views.helpers.generalGetString import chat.simplex.app.views.newchat.NewChatSheet import chat.simplex.app.views.usersettings.SettingsView import kotlinx.coroutines.CoroutineScope @@ -110,25 +111,28 @@ fun Help(scaffoldCtrl: ScaffoldController, displayName: String?) { .fillMaxWidth() .padding(16.dp) ) { + val welcomeMsg = if (displayName != null) { + String.format(generalGetString(R.string.personal_welcome), displayName) + } else generalGetString(R.string.welcome) Text( - text = if (displayName != null) "Welcome ${displayName}!" else "Welcome!", + text = welcomeMsg, Modifier.padding(bottom = 24.dp), style = MaterialTheme.typography.h1, color = MaterialTheme.colors.onBackground ) - ChatHelpView({ scaffoldCtrl.toggleSheet() }) + ChatHelpView { scaffoldCtrl.toggleSheet() } Row( Modifier.padding(top = 30.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Text( - "This text is available in settings", + generalGetString(R.string.this_text_is_available_in_settings), color = MaterialTheme.colors.onBackground ) Icon( Icons.Outlined.Settings, - "Settings", + generalGetString(R.string.icon_descr_settings), tint = MaterialTheme.colors.onBackground, modifier = Modifier.clickable(onClick = { scaffoldCtrl.toggleDrawer() }) ) @@ -150,13 +154,13 @@ fun ChatListToolbar(scaffoldCtrl: ScaffoldController) { IconButton(onClick = { scaffoldCtrl.toggleDrawer() }) { Icon( Icons.Outlined.Menu, - "Settings", + generalGetString(R.string.icon_descr_settings), tint = MaterialTheme.colors.primary, modifier = Modifier.padding(10.dp) ) } Text( - "Your chats", + generalGetString(R.string.your_chats), color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(5.dp) @@ -164,7 +168,7 @@ fun ChatListToolbar(scaffoldCtrl: ScaffoldController) { IconButton(onClick = { scaffoldCtrl.toggleSheet() }) { Icon( Icons.Outlined.PersonAdd, - "Add Contact", + generalGetString(R.string.add_contact), tint = MaterialTheme.colors.primary, modifier = Modifier.padding(10.dp) ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt index 45f7fe5322..81b10548be 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt @@ -14,13 +14,13 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.Chat import chat.simplex.app.model.getTimestampText import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.item.MarkdownText -import chat.simplex.app.views.helpers.ChatInfoImage -import chat.simplex.app.views.helpers.badgeLayout +import chat.simplex.app.views.helpers.* @Composable fun ChatPreviewView(chat: Chat) { @@ -63,7 +63,7 @@ fun ChatPreviewView(chat: Chat) { val n = chat.chatStats.unreadCount if (n > 0) { Text( - if (n < 1000) "$n" else "${n / 1000}k", + if (n < 1000) "$n" else "${n / 1000}" + generalGetString(R.string.thousand_abbreviation), color = MaterialTheme.colors.onPrimary, fontSize = 14.sp, modifier = Modifier diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt index 4cfa5c48c9..08a7b46795 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ContactRequestView.kt @@ -8,10 +8,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.Chat import chat.simplex.app.model.getTimestampText import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.views.helpers.ChatInfoImage +import chat.simplex.app.views.helpers.generalGetString @Composable fun ContactRequestView(chat: Chat) { @@ -31,7 +33,7 @@ fun ContactRequestView(chat: Chat) { color = MaterialTheme.colors.primary ) Text( - "wants to connect to you!", + generalGetString(R.string.contact_wants_to_connect_with_you), maxLines = 2, overflow = TextOverflow.Ellipsis ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt index a746df1925..7e22b00d2d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt @@ -4,6 +4,7 @@ import android.util.Log import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf +import chat.simplex.app.R import chat.simplex.app.TAG class AlertManager { @@ -40,9 +41,9 @@ class AlertManager { fun showAlertDialog( title: String, text: String? = null, - confirmText: String = "Ok", + confirmText: String = generalGetString(R.string.ok), onConfirm: (() -> Unit)? = null, - dismissText: String = "Cancel", + dismissText: String = generalGetString(R.string.cancel_verb), onDismiss: (() -> Unit)? = null ) { val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } @@ -69,7 +70,7 @@ class AlertManager { fun showAlertMsg( title: String, text: String? = null, - confirmText: String = "Ok", onConfirm: (() -> Unit)? = null + confirmText: String = generalGetString(R.string.ok), onConfirm: (() -> Unit)? = null ) { val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } showAlert { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt index d5d582e9ed..3b92c1bf13 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ChatInfoImage.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.Chat import chat.simplex.app.model.ChatInfo import chat.simplex.app.ui.theme.SimpleXTheme @@ -39,7 +40,7 @@ fun ProfileImage( if (image == null) { Icon( icon, - contentDescription = "profile image placeholder", + contentDescription = generalGetString(R.string.icon_descr_profile_image_placeholder), tint = MaterialTheme.colors.secondary, modifier = Modifier.fillMaxSize() ) @@ -47,7 +48,7 @@ fun ProfileImage( val imageBitmap = base64ToBitmap(image).asImageBitmap() Image( imageBitmap, - "profile image", + generalGetString(R.string.image_descr_profile_image), contentScale = ContentScale.Crop, modifier = Modifier.size(size).padding(size / 12).clip(CircleShape) ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt index 0ee1884b67..2577ae9251 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.ui.theme.SimpleXTheme @Composable @@ -24,7 +25,7 @@ fun CloseSheetBar(close: () -> Unit) { IconButton(onClick = close) { Icon( Icons.Outlined.Close, - "Close button", + generalGetString(R.string.icon_descr_close_button), tint = MaterialTheme.colors.primary, modifier = Modifier.padding(10.dp) ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt index 8c196e6ada..21b9166dbe 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt @@ -27,8 +27,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.core.content.FileProvider -import chat.simplex.app.BuildConfig -import chat.simplex.app.TAG +import chat.simplex.app.* +import chat.simplex.app.R import chat.simplex.app.views.newchat.ActionButton import java.io.ByteArrayOutputStream import java.io.File @@ -148,7 +148,7 @@ fun GetImageBottomSheet( else galleryLauncher.launch("image/*") hideBottomSheet() } else { - Toast.makeText(context, "Permission Denied!", Toast.LENGTH_SHORT).show() + Toast.makeText(context, generalGetString(R.string.toast_camera_permission_denied), Toast.LENGTH_SHORT).show() } } @@ -166,7 +166,7 @@ fun GetImageBottomSheet( .padding(horizontal = 8.dp, vertical = 30.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { - ActionButton(null, "Use Camera", icon = Icons.Outlined.PhotoCamera) { + ActionButton(null, generalGetString(R.string.use_camera_button), icon = Icons.Outlined.PhotoCamera) { when (PackageManager.PERMISSION_GRANTED) { ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) -> { cameraLauncher.launch(null) @@ -178,7 +178,7 @@ fun GetImageBottomSheet( } } } - ActionButton(null, "From Gallery", icon = Icons.Outlined.Collections) { + ActionButton(null, generalGetString(R.string.from_gallery_button), icon = Icons.Outlined.Collections) { when (PackageManager.PERMISSION_GRANTED) { ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) -> { galleryLauncher.launch("image/*") diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt index 102097457f..ec13b5dd27 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LinkPreviews.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.LinkPreview import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme @@ -72,7 +73,7 @@ fun ComposeLinkView(linkPreview: LinkPreview, cancelPreview: () -> Unit) { val imageBitmap = base64ToBitmap(linkPreview.image).asImageBitmap() Image( imageBitmap, - "preview image", + generalGetString(R.string.image_descr_link_preview), modifier = Modifier.width(80.dp).height(60.dp).padding(end = 8.dp) ) Column(Modifier.fillMaxWidth().weight(1F)) { @@ -85,7 +86,7 @@ fun ComposeLinkView(linkPreview: LinkPreview, cancelPreview: () -> Unit) { IconButton(onClick = cancelPreview, modifier = Modifier.padding(0.dp)) { Icon( Icons.Outlined.Close, - contentDescription = "Cancel Preview", + contentDescription = generalGetString(R.string.icon_descr_cancel_link_preview), tint = MaterialTheme.colors.primary, modifier = Modifier.padding(10.dp) ) @@ -98,7 +99,7 @@ fun ChatItemLinkView(linkPreview: LinkPreview) { Column { Image( base64ToBitmap(linkPreview.image).asImageBitmap(), - "link image", + generalGetString(R.string.image_descr_link_preview), modifier = Modifier.fillMaxWidth(), contentScale = ContentScale.FillWidth, ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt index 298867bc8e..6349354c84 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt @@ -1,4 +1,4 @@ -package chat.simplex.app.views.newchat +package chat.simplex.app.views.helpers import android.util.Log import androidx.activity.compose.BackHandler @@ -11,7 +11,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import chat.simplex.app.TAG -import chat.simplex.app.views.helpers.CloseSheetBar @Composable fun ModalView(close: () -> Unit, content: @Composable () -> Unit) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt index 454e880837..c1838b1256 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt @@ -1,9 +1,23 @@ package chat.simplex.app.views.helpers +import android.content.res.Resources import android.graphics.Rect +import android.graphics.Typeface +import android.text.Spanned +import android.text.SpannedString +import android.text.style.* import android.view.ViewTreeObserver +import androidx.annotation.StringRes import androidx.compose.runtime.* -import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.* +import androidx.compose.ui.text.* +import androidx.compose.ui.text.font.* +import androidx.compose.ui.text.style.BaselineShift +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.* +import androidx.core.text.HtmlCompat +import chat.simplex.app.SimplexApp import kotlinx.coroutines.* fun withApi(action: suspend CoroutineScope.() -> Unit): Job = @@ -38,3 +52,146 @@ fun getKeyboardState(): State { return keyboardState } + +// Resource to annotated string from +// https://stackoverflow.com/questions/68549248/android-jetpack-compose-how-to-show-styled-text-from-string-resources + +fun generalGetString(id: Int) : String { + return SimplexApp.context.getString(id) +} + +@Composable +@ReadOnlyComposable +private fun resources(): Resources { + LocalConfiguration.current + return LocalContext.current.resources +} + +fun Spanned.toHtmlWithoutParagraphs(): String { + return HtmlCompat.toHtml(this, HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) + .substringAfter("

").substringBeforeLast("

") +} + +fun Resources.getText(@StringRes id: Int, vararg args: Any): CharSequence { + val escapedArgs = args.map { + if (it is Spanned) it.toHtmlWithoutParagraphs() else it + }.toTypedArray() + val resource = SpannedString(getText(id)) + val htmlResource = resource.toHtmlWithoutParagraphs() + val formattedHtml = String.format(htmlResource, *escapedArgs) + return HtmlCompat.fromHtml(formattedHtml, HtmlCompat.FROM_HTML_MODE_LEGACY) +} + +@Composable +fun annotatedStringResource(@StringRes id: Int): AnnotatedString { + val resources = resources() + val density = LocalDensity.current + return remember(id) { + val text = resources.getText(id) + spannableStringToAnnotatedString(text, density) + } +} + +private fun spannableStringToAnnotatedString( + text: CharSequence, + density: Density, +): AnnotatedString { + return if (text is Spanned) { + with(density) { + buildAnnotatedString { + append((text.toString())) + text.getSpans(0, text.length, Any::class.java).forEach { + val start = text.getSpanStart(it) + val end = text.getSpanEnd(it) + when (it) { + is StyleSpan -> when (it.style) { + Typeface.NORMAL -> addStyle( + SpanStyle( + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Normal, + ), + start, + end + ) + Typeface.BOLD -> addStyle( + SpanStyle( + fontWeight = FontWeight.Bold, + fontStyle = FontStyle.Normal + ), + start, + end + ) + Typeface.ITALIC -> addStyle( + SpanStyle( + fontWeight = FontWeight.Normal, + fontStyle = FontStyle.Italic + ), + start, + end + ) + Typeface.BOLD_ITALIC -> addStyle( + SpanStyle( + fontWeight = FontWeight.Bold, + fontStyle = FontStyle.Italic + ), + start, + end + ) + } + is TypefaceSpan -> addStyle( + SpanStyle( + fontFamily = when (it.family) { + FontFamily.SansSerif.name -> FontFamily.SansSerif + FontFamily.Serif.name -> FontFamily.Serif + FontFamily.Monospace.name -> FontFamily.Monospace + FontFamily.Cursive.name -> FontFamily.Cursive + else -> FontFamily.Default + } + ), + start, + end + ) + is AbsoluteSizeSpan -> addStyle( + SpanStyle(fontSize = if (it.dip) it.size.dp.toSp() else it.size.toSp()), + start, + end + ) + is RelativeSizeSpan -> addStyle( + SpanStyle(fontSize = it.sizeChange.em), + start, + end + ) + is StrikethroughSpan -> addStyle( + SpanStyle(textDecoration = TextDecoration.LineThrough), + start, + end + ) + is UnderlineSpan -> addStyle( + SpanStyle(textDecoration = TextDecoration.Underline), + start, + end + ) + is SuperscriptSpan -> addStyle( + SpanStyle(baselineShift = BaselineShift.Superscript), + start, + end + ) + is SubscriptSpan -> addStyle( + SpanStyle(baselineShift = BaselineShift.Subscript), + start, + end + ) + is ForegroundColorSpan -> addStyle( + SpanStyle(color = Color(it.foregroundColor)), + start, + end + ) + else -> addStyle(SpanStyle(color = Color.White), start, end) + } + } + } + } + } else { + AnnotatedString(text.toString()) + } +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt index 545bf105f4..4e668e3580 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt @@ -10,15 +10,16 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleButton import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.helpers.generalGetString import chat.simplex.app.views.helpers.shareText @Composable @@ -42,11 +43,11 @@ fun AddContactLayout(connReq: String, share: () -> Unit) { verticalArrangement = Arrangement.SpaceBetween, ) { Text( - "Add contact", + generalGetString(R.string.add_contact), style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal), ) Text( - "Show QR code to your contact\nto scan from the app", + generalGetString(R.string.show_QR_code_for_your_contact_to_scan_from_the_app__multiline), style = MaterialTheme.typography.h3, textAlign = TextAlign.Center, ) @@ -57,20 +58,14 @@ fun AddContactLayout(connReq: String, share: () -> Unit) { .padding(vertical = 3.dp) ) Text( - buildAnnotatedString { - append("If you cannot meet in person, you can ") - withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { - append("scan QR code in the video call") - } - append(", or you can share the invitation link via any other channel.") - }, + generalGetString(R.string.if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel), textAlign = TextAlign.Center, - style = MaterialTheme.typography.caption.copy(fontSize=if(screenHeight > 600.dp) 20.sp else 16.sp), + lineHeight = 22.sp, modifier = Modifier .padding(horizontal = 16.dp) .padding(bottom = if(screenHeight > 600.dp) 16.dp else 8.dp) ) - SimpleButton("Share invitation link", icon = Icons.Outlined.Share, click = share) + SimpleButton(generalGetString(R.string.share_invitation_link), icon = Icons.Outlined.Share, click = share) Spacer(Modifier.height(10.dp)) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt index 06f34cb0a6..f65b000987 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt @@ -8,15 +8,15 @@ import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.helpers.AlertManager -import chat.simplex.app.views.helpers.withApi +import chat.simplex.app.views.helpers.* @Composable fun ConnectContactView(chatModel: ChatModel, close: () -> Unit) { @@ -31,8 +31,8 @@ fun ConnectContactView(chatModel: ChatModel, close: () -> Unit) { } } catch (e: RuntimeException) { AlertManager.shared.showAlertMsg( - title = "Invalid QR code", - text = "This QR code is not a link!" + title = generalGetString(R.string.invalid_QR_code), + text = generalGetString(R.string.this_QR_code_is_not_a_link) ) } close() @@ -48,8 +48,8 @@ fun withUriAction(uri: Uri, run: suspend (String) -> Unit) { withApi { run(action) } } else { AlertManager.shared.showAlertMsg( - title = "Invalid link!", - text = "This link is not a valid connection link!" + title = generalGetString(R.string.invalid_contact_link), + text = generalGetString(R.string.this_link_is_not_a_valid_connection_link) ) } } @@ -57,12 +57,11 @@ fun withUriAction(uri: Uri, run: suspend (String) -> Unit) { suspend fun connectViaUri(chatModel: ChatModel, action: String, uri: Uri) { val r = chatModel.controller.apiConnect(uri.toString()) if (r) { - val whenConnected = - if (action == "contact") "your connection request is accepted" - else "your contact's device is online" AlertManager.shared.showAlertMsg( - title = "Connection request sent!", - text = "You will be connected when $whenConnected, please wait or check later!" + title = generalGetString(R.string.connection_request_sent), + text = + if (action == "contact") generalGetString(R.string.you_will_be_connected_when_your_connection_request_is_accepted) + else generalGetString(R.string.you_will_be_connected_when_your_contacts_device_is_online) ) } } @@ -75,11 +74,11 @@ fun ConnectContactLayout(qrCodeScanner: @Composable () -> Unit, close: () -> Uni verticalArrangement = Arrangement.spacedBy(12.dp) ) { Text( - "Scan QR code", + generalGetString(R.string.scan_QR_code), style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal), ) Text( - "Your chat profile will be sent\nto your contact", + generalGetString(R.string.your_chat_profile_will_be_sent_to_your_contact), style = MaterialTheme.typography.h3, textAlign = TextAlign.Center, modifier = Modifier.padding(bottom = 4.dp) @@ -90,18 +89,8 @@ fun ConnectContactLayout(qrCodeScanner: @Composable () -> Unit, close: () -> Uni .aspectRatio(ratio = 1F) ) { qrCodeScanner() } Text( - buildAnnotatedString { - append("If you cannot meet in person, you can ") - withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { - append("scan QR code in the video call") - } - append(", or you can create the invitation link.") - }, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.caption, - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(top = 4.dp) + annotatedStringResource(R.string.if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link), + lineHeight = 22.sp ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt index 61d2400a01..2d3ff4924a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt @@ -14,11 +14,12 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chatlist.ScaffoldController -import chat.simplex.app.views.helpers.withApi +import chat.simplex.app.views.helpers.* import com.google.accompanist.permissions.rememberPermissionState @Composable @@ -57,8 +58,10 @@ fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) { .weight(1F) .fillMaxWidth()) { ActionButton( - "Add contact", "(create QR code\nor link)", - Icons.Outlined.PersonAdd, click = addContact + generalGetString(R.string.add_contact), + generalGetString(R.string.create_QR_code_or_link__bracketed__multiline), + Icons.Outlined.PersonAdd, + click = addContact ) } Box( @@ -66,8 +69,10 @@ fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) { .weight(1F) .fillMaxWidth()) { ActionButton( - "Scan QR code", "(in person or in video call)", - Icons.Outlined.QrCode, click = scanCode + generalGetString(R.string.scan_QR_code), + generalGetString(R.string.in_person_or_in_video_call__bracketed), + Icons.Outlined.QrCode, + click = scanCode ) } Box( @@ -75,8 +80,10 @@ fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) { .weight(1F) .fillMaxWidth()) { ActionButton( - "Create Group", "(coming soon!)", - Icons.Outlined.GroupAdd, disabled = true + generalGetString(R.string.create_group), + generalGetString(R.string.coming_soon__bracketed), + Icons.Outlined.GroupAdd, + disabled = true ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCode.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCode.kt index cbc56268f4..f55489c7d7 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCode.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/QRCode.kt @@ -7,7 +7,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.tooling.preview.Preview +import chat.simplex.app.R import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.helpers.generalGetString import com.google.zxing.BarcodeFormat import com.google.zxing.EncodeHintType import com.google.zxing.qrcode.QRCodeWriter @@ -16,7 +18,7 @@ import com.google.zxing.qrcode.QRCodeWriter fun QRCode(connReq: String, modifier: Modifier = Modifier) { Image( bitmap = qrCodeBitmap(connReq, 1024).asImageBitmap(), - contentDescription = "QR Code", + contentDescription = generalGetString(R.string.image_descr_qr_code), modifier = modifier ) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt index 9d7d380c7a..dddb7bef80 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt @@ -3,6 +3,8 @@ package chat.simplex.app.views.usersettings import android.content.res.Configuration import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -10,9 +12,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.chat.ChatHelpView +import chat.simplex.app.views.chatlist.ChatHelpView +import chat.simplex.app.views.helpers.generalGetString @Composable fun HelpView(chatModel: ChatModel) { @@ -24,9 +28,12 @@ fun HelpView(chatModel: ChatModel) { @Composable fun HelpLayout(displayName: String) { - Column(horizontalAlignment = Alignment.Start) { + Column( + Modifier.verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.Start + ){ Text( - "Welcome $displayName!", + String.format(generalGetString(R.string.personal_welcome), displayName), Modifier.padding(bottom = 24.dp), style = MaterialTheme.typography.h1, ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt index ef6e29a6d4..b1cb3e68f2 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt @@ -10,29 +10,38 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.* import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.R import chat.simplex.app.model.Format import chat.simplex.app.model.FormatColor import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.helpers.generalGetString @Composable fun MarkdownHelpView() { Column { Text( - "How to use markdown", + generalGetString(R.string.how_to_use_markdown), style = MaterialTheme.typography.h1, ) Text( - "You can use markdown to format messages:", + generalGetString(R.string.you_can_use_markdown_to_format_messages__prompt), Modifier.padding(vertical = 16.dp) ) - MdFormat("*bold*", "bold", Format.Bold()) - MdFormat("_italic_", "italic", Format.Italic()) - MdFormat("~strike~", "strike", Format.StrikeThrough()) - MdFormat("`a + b`", "a + b", Format.Snippet()) + val bold = generalGetString(R.string.bold) + val italic = generalGetString(R.string.italic) + val strikethrough = generalGetString(R.string.strikethrough) + val equation = generalGetString(R.string.a_plus_b) + val colored = generalGetString(R.string.colored) + val secret = generalGetString(R.string.secret) + + MdFormat("*$bold*", bold, Format.Bold()) + MdFormat("_${italic}_", italic, Format.Italic()) + MdFormat("~$strikethrough~", strikethrough, Format.StrikeThrough()) + MdFormat("`$equation`", equation, Format.Snippet()) Row { - MdSyntax("!1 colored!") + MdSyntax("!1 $colored!") Text(buildAnnotatedString { - withStyle(Format.Colored(FormatColor.red).style) { append("colored") } + withStyle(Format.Colored(FormatColor.red).style) { append(colored) } append(" (") appendColor(this, "1", FormatColor.red, ", ") appendColor(this, "2", FormatColor.green, ", ") @@ -43,10 +52,10 @@ fun MarkdownHelpView() { }) } Row { - MdSyntax("#secret#") + MdSyntax("#$secret#") SelectionContainer { Text(buildAnnotatedString { - withStyle(Format.Secret().style) { append("secret") } + withStyle(Format.Secret().style) { append(secret) } }) } } @@ -56,7 +65,7 @@ fun MarkdownHelpView() { @Composable fun MdSyntax(markdown: String) { Text(markdown, Modifier - .width(100.dp) + .width(120.dp) .padding(bottom = 4.dp)) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt index f9a6b4c9ce..6668018792 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SMPServers.kt @@ -20,11 +20,11 @@ import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.helpers.AlertManager -import chat.simplex.app.views.helpers.withApi +import chat.simplex.app.views.helpers.* @Composable fun SMPServersView(chatModel: ChatModel) { @@ -60,9 +60,9 @@ fun SMPServersView(chatModel: ChatModel) { if (userSMPServers != null) { if (userSMPServers.isNotEmpty()) { AlertManager.shared.showAlertMsg( - title = "Use SimpleX Chat servers?", - text = "Saved SMP servers will be removed.", - confirmText = "Confirm", + title = generalGetString(R.string.use_simplex_chat_servers__question), + text = generalGetString(R.string.saved_SMP_servers_will_br_removed), + confirmText = generalGetString(R.string.confirm_verb), onConfirm = { saveSMPServers(listOf()) isUserSMPServers = false @@ -108,14 +108,14 @@ fun SMPServersLayout( verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text( - "Your SMP servers", + generalGetString(R.string.your_SMP_servers), Modifier.padding(bottom = 24.dp), style = MaterialTheme.typography.h1 ) Row( verticalAlignment = Alignment.CenterVertically ) { - Text("Configure SMP servers", Modifier.padding(end = 24.dp)) + Text(generalGetString(R.string.configure_SMP_servers), Modifier.padding(end = 24.dp)) Switch( checked = isUserSMPServers, onCheckedChange = isUserSMPServersOnOff, @@ -127,9 +127,9 @@ fun SMPServersLayout( } if (!isUserSMPServers) { - Text("Using SimpleX Chat servers.") + Text(generalGetString(R.string.using_simplex_chat_servers), lineHeight = 22.sp) } else { - Text("Enter one SMP server per line:") + Text(generalGetString(R.string.enter_one_SMP_server_per_line)) if (editSMPServers) { BasicTextField( value = userSMPServersStr, @@ -173,14 +173,14 @@ fun SMPServersLayout( Column(horizontalAlignment = Alignment.Start) { Row { Text( - "Cancel", + generalGetString(R.string.cancel_verb), color = MaterialTheme.colors.primary, modifier = Modifier .clickable(onClick = cancelEdit) ) Spacer(Modifier.padding(horizontal = 8.dp)) Text( - "Save", + generalGetString(R.string.save_servers_button), color = MaterialTheme.colors.primary, modifier = Modifier.clickable(onClick = { val servers = userSMPServersStr.split("\n") @@ -219,7 +219,7 @@ fun SMPServersLayout( ) { Column(horizontalAlignment = Alignment.Start) { Text( - "Edit", + generalGetString(R.string.edit_verb), color = MaterialTheme.colors.primary, modifier = Modifier .clickable(onClick = editOn) @@ -241,9 +241,9 @@ fun howToButton() { verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { uriHandler.openUri("https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent") } ) { - Text("How to", color = MaterialTheme.colors.primary) + Text(generalGetString(R.string.how_to), color = MaterialTheme.colors.primary) Icon( - Icons.Outlined.OpenInNew, "How to", tint = MaterialTheme.colors.primary, + Icons.Outlined.OpenInNew, generalGetString(R.string.how_to), tint = MaterialTheme.colors.primary, modifier = Modifier.padding(horizontal = 5.dp) ) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt index e74aa7042e..a99a49c99b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -24,8 +23,7 @@ import chat.simplex.app.model.Profile import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.TerminalView -import chat.simplex.app.views.helpers.ProfileImage -import chat.simplex.app.views.newchat.ModalManager +import chat.simplex.app.views.helpers.* @Composable fun SettingsView(chatModel: ChatModel) { @@ -71,7 +69,7 @@ fun SettingsLayout( .padding(top = 16.dp) ) { Text( - "Your settings", + generalGetString(R.string.your_settings), style = MaterialTheme.typography.h1, modifier = Modifier.padding(start = 8.dp) ) @@ -93,39 +91,39 @@ fun SettingsLayout( SettingsSectionView(showModal { UserAddressView(it) }) { Icon( Icons.Outlined.QrCode, - contentDescription = "Address", + contentDescription = generalGetString(R.string.icon_descr_address), ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text("Your SimpleX contact address") + Text(generalGetString(R.string.your_simplex_contact_address)) } Spacer(Modifier.height(24.dp)) SettingsSectionView(showModal { HelpView(it) }) { Icon( Icons.Outlined.HelpOutline, - contentDescription = "Chat help", + contentDescription = generalGetString(R.string.icon_descr_help), ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text("How to use SimpleX Chat") + Text(generalGetString(R.string.how_to_use_simplex_chat)) } Divider(Modifier.padding(horizontal = 8.dp)) SettingsSectionView(showModal { MarkdownHelpView() }) { Icon( Icons.Outlined.TextFormat, - contentDescription = "Markdown help", + contentDescription = generalGetString(R.string.markdown_help), ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text("Markdown in messages") + Text(generalGetString(R.string.markdown_in_messages)) } Divider(Modifier.padding(horizontal = 8.dp)) SettingsSectionView({ uriHandler.openUri(simplexTeamUri) }) { Icon( Icons.Outlined.Tag, - contentDescription = "SimpleX Team", + contentDescription = generalGetString(R.string.icon_descr_simplex_team), ) Spacer(Modifier.padding(horizontal = 4.dp)) Text( - "Chat with the founder", + generalGetString(R.string.chat_with_the_founder), color = MaterialTheme.colors.primary ) } @@ -133,11 +131,11 @@ fun SettingsLayout( SettingsSectionView({ uriHandler.openUri("mailto:chat@simplex.chat") }) { Icon( Icons.Outlined.Email, - contentDescription = "Email", + contentDescription = generalGetString(R.string.icon_descr_email), ) Spacer(Modifier.padding(horizontal = 4.dp)) Text( - "Send us email", + generalGetString(R.string.send_us_an_email), color = MaterialTheme.colors.primary ) } @@ -146,19 +144,20 @@ fun SettingsLayout( SettingsSectionView(showModal { SMPServersView(it) }) { Icon( Icons.Outlined.Dns, - contentDescription = "SMP servers", + contentDescription = generalGetString(R.string.smp_servers), ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text("SMP servers") + Text(generalGetString(R.string.smp_servers)) } Divider(Modifier.padding(horizontal = 8.dp)) SettingsSectionView() { Icon( Icons.Outlined.Bolt, - contentDescription = "Private notifications", + contentDescription = generalGetString(R.string.private_notifications), ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text("Private notifications", Modifier + Text( + generalGetString(R.string.private_notifications), Modifier .padding(end = 24.dp) .fillMaxWidth() .weight(1F)) @@ -176,10 +175,10 @@ fun SettingsLayout( SettingsSectionView(showTerminal) { Icon( painter = painterResource(id = R.drawable.ic_outline_terminal), - contentDescription = "Chat console", + contentDescription = generalGetString(R.string.chat_console), ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text("Chat console") + Text(generalGetString(R.string.chat_console)) } Divider(Modifier.padding(horizontal = 8.dp)) SettingsSectionView({ uriHandler.openUri("https://github.com/simplex-chat/simplex-chat") }) { @@ -188,14 +187,7 @@ fun SettingsLayout( contentDescription = "GitHub", ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text( - buildAnnotatedString { - append("Install ") - withStyle(SpanStyle(color = MaterialTheme.colors.primary)) { - append("SimpleX Chat for terminal") - } - } - ) + Text(annotatedStringResource(R.string.install_simplex_chat_for_terminal)) } Divider(Modifier.padding(horizontal = 8.dp)) SettingsSectionView() { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt index 486260220a..734fa19a05 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt @@ -13,6 +13,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleButton import chat.simplex.app.ui.theme.SimpleXTheme @@ -32,9 +34,9 @@ fun UserAddressView(chatModel: ChatModel) { share = { userAddress: String -> shareText(cxt, userAddress) }, deleteAddress = { AlertManager.shared.showAlertMsg( - title = "Delete address?", - text = "All your contacts will remain connected.", - confirmText = "Delete", + title = generalGetString(R.string.delete_address__question), + text = generalGetString(R.string.all_your_contacts_will_remain_connected), + confirmText = generalGetString(R.string.delete_verb), onConfirm = { withApi { chatModel.controller.apiDeleteUserAddress() @@ -58,14 +60,14 @@ fun UserAddressLayout( verticalArrangement = Arrangement.Top ) { Text( - "Your chat address", + generalGetString(R.string.your_chat_address), Modifier.padding(bottom = 16.dp), style = MaterialTheme.typography.h1, ) Text( - "You can share your address as a link or as a QR code - anybody will be able to connect to you, " + - "and if you later delete it - you won't lose your contacts.", + generalGetString(R.string.you_can_share_your_address_anybody_will_be_able_to_connect), Modifier.padding(bottom = 12.dp), + lineHeight = 22.sp ) Column( Modifier.fillMaxWidth(), @@ -73,7 +75,12 @@ fun UserAddressLayout( verticalArrangement = Arrangement.SpaceEvenly ) { if (userAddress == null) { - SimpleButton("Create address", icon = Icons.Outlined.QrCode, click = createAddress) + Text( + generalGetString(R.string.if_you_delete_address_you_wont_lose_contacts), + Modifier.padding(bottom = 12.dp), + lineHeight = 22.sp + ) + SimpleButton(generalGetString(R.string.create_address), icon = Icons.Outlined.QrCode, click = createAddress) } else { QRCode(userAddress, Modifier.weight(1f, fill = false).aspectRatio(1f)) Row( @@ -82,11 +89,11 @@ fun UserAddressLayout( modifier = Modifier.padding(vertical = 10.dp) ) { SimpleButton( - "Share link", + generalGetString(R.string.share_link), icon = Icons.Outlined.Share, click = { share(userAddress) }) SimpleButton( - "Delete address", + generalGetString(R.string.delete_address), icon = Icons.Outlined.Delete, color = Color.Red, click = deleteAddress diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt index 46ddaffe89..39af8a3c21 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt @@ -19,11 +19,12 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.model.Profile import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.helpers.* -import chat.simplex.app.views.newchat.ModalView import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding import kotlinx.coroutines.launch @@ -89,16 +90,16 @@ fun UserProfileLayout( horizontalAlignment = Alignment.Start ) { Text( - "Your chat profile", + generalGetString(R.string.your_chat_profile), Modifier.padding(bottom = 24.dp), style = MaterialTheme.typography.h1, color = MaterialTheme.colors.onBackground ) Text( - "Your profile is stored on your device and shared only with your contacts.\n\n" + - "SimpleX servers cannot see your profile.", + generalGetString(R.string.your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it), Modifier.padding(bottom = 24.dp), - color = MaterialTheme.colors.onBackground + color = MaterialTheme.colors.onBackground, + lineHeight = 22.sp ) if (editProfile.value) { Column( @@ -124,14 +125,14 @@ fun UserProfileLayout( ProfileNameTextField(displayName) ProfileNameTextField(fullName) Row { - TextButton("Cancel") { + TextButton(generalGetString(R.string.cancel_verb)) { displayName.value = profile.displayName fullName.value = profile.fullName profileImage.value = profile.image editProfile.value = false } Spacer(Modifier.padding(horizontal = 8.dp)) - TextButton("Save (and notify contacts)") { + TextButton(generalGetString(R.string.save_and_notify_contacts)) { saveProfile(displayName.value, fullName.value, profileImage.value) } } @@ -154,9 +155,9 @@ fun UserProfileLayout( } } } - ProfileNameRow("Display name:", profile.displayName) - ProfileNameRow("Full name:", profile.fullName) - TextButton("Edit") { editProfile.value = true } + ProfileNameRow(generalGetString(R.string.display_name__field), profile.displayName) + ProfileNameRow(generalGetString(R.string.full_name__field), profile.fullName) + TextButton(generalGetString(R.string.edit_verb)) { editProfile.value = true } } } if (savedKeyboardState != keyboardState) { @@ -223,7 +224,7 @@ fun EditImageButton(click: () -> Unit) { ) { Icon( Icons.Outlined.PhotoCamera, - contentDescription = "Edit image", + contentDescription = generalGetString(R.string.edit_image), tint = MaterialTheme.colors.primary, modifier = Modifier.size(36.dp) ) @@ -235,7 +236,7 @@ fun DeleteImageButton(click: () -> Unit) { IconButton(onClick = click) { Icon( Icons.Outlined.Close, - contentDescription = "Delete image", + contentDescription = generalGetString(R.string.delete_image), tint = MaterialTheme.colors.primary, ) } diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/android/app/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..1d54970f46 --- /dev/null +++ b/apps/android/app/src/main/res/values-ru/strings.xml @@ -0,0 +1,218 @@ + + SimpleX + + т + + + Соединиться через ссылку-контакт? + Соединиться через ссылку-приглашение? + Ваш профиль будет отправлен контакту, от которого вы получили эту ссылку. + Соединиться + + + Соединение установлено + Соединение устанавливается… + Установлено соединение с сервером, через который вы получаете сообщения от этого контакта. + Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта (ошибка: %1$s). + Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта. + + + удалено + отправка файлов не поддерживается + получение файлов не поддерживается + вы + неизвестный формат сообщения + неверный формат сообщения + + + Ошибка при сохранении SMP серверов + Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется. + + + Существующий контакт + Вы уже соединены с %1$s! через эту ссылку. + Ошибка в ссылке контакта + Пожалуйста, проверьте, что вы использовали правильную ссылку, или попросите ваш контакт отправить вам новую. + Невозможно удалить контакт! + Контакт %1$s! не может быть удален, так как является членом групп(ы) %2$s. + Мгновенные уведомления + + + Приватные мгновенные уведомления! + Чтобы защитить ваши личные данные, вместо уведомлений от сервера приложение запускает фоновый сервис SimpleX, который потребляет несколько процентов батареи в день. + Он может быть выключен через Настройки – вы продолжите получать уведомления о сообщениях пока приложение запущено. + + + SimpleX Chat сервис + Приём сообщений… + + + Ответить + Поделиться + Скопировать + Редактировать + Удалить + Удалить сообщение? + Сообщение будет удалено – это действие нельзя отменить! + Только для меня + Для всех + + + отредактировано + отправлено + ошибка авторизации при отправке + ошибка при отправке + не прочитано + + + Здравствуйте %1$s! + Здравствуйте! + Этот текст можно найти в Настройках + Ваши чаты + + + Удалить контакт? + Контакт и все сообщения будут удалены - это действие нельзя отменить! + Удалить контакт + Соединение с сервером установлено + Соединение с сервером не установлено + Ошибка соединения с сервером + Ожидается соединение с сервером + + + Отправить сообщение + + + Назад + Отменить + Подтвердить + + нет описания + Добавить контакт + Сканировать QR код + + + (создать QR код или ссылку) + (при встрече или через видео звонок) + Создать группу + (скоро!) + + + Разрешение не получено! + Использовать камеру + Открыть галерею + + + Спасибо что установили SimpleX Chat! + Вы можете соединиться с разработчиками, чтобы задать любые вопросы или получать уведомления о новых версиях. + Чтобы начать новый чат + Нажмите кнопку + сверху, затем: + Добавить новый контакт: чтобы создать одноразовый QR код/ссылку для вашего контакта. + Сканировать QR код: чтобы соединиться с контактом, который показывает вам QR код. + Чтобы соединиться через ссылку + Если вы получили ссылку с приглашением из SimpleX Chat, вы можете открыть ее в браузере: + 💻 на компьютере: сосканируйте показанный QR код из приложения через Сканировать QR код. + 📱 на мобильном: намжите кнопку Open in mobile app на веб странице, затем нажмите Соединиться в приложении. + + + Принять запрос на соединение? + Отправителю НЕ будет послано уведомление, если вы отклоните запрос на соединение. + Принять + Отклонить + + + хочет соединиться с вами! + + + аватар не установлен + аватар + + + закрыть + изображение превью ссылки + удалить превью ссылки + Настройки + QR код + SimpleX адрес + Помощь + SimpleX команда + SimpleX логотип + Email + + + Неверный QR код + Этот QR код не является ссылкой! + Неверная ссылка! + Эта ссылка не является ссылкой-приглашением! + Запрос на соединение послан! + Соединение будет установлено когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже! + Соединение будет установлено когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже! + Покажите QR код вашему контакту, чтобы сосканировать его из приложения + Если вы не можете встретиться лично, вы можете показать QR код во время видео звонка или отправить ссылку через любой другой канал связи. + Ваш профиль будет отправлен\nвашему контакту + Если вы не можете встретиться лично, вы можете сосканировать QR код во время видео звонка, или ваш контакт может отправить вам ссылку. + Поделиться ссылкой + + + Настройки + Ваш SimpleX адрес + Как использовать SimpleX Chat + Форматирование сообщений + Форматирование сообщений + Соединиться с разработчиками + Отправить email + Приватные уведомления + Консоль + SMP серверы + SimpleX Chat для терминала + Использовать серверы предосталенные SimpleX Chat? + Сохраненные SMP серверы будут удалены. + Ваши SMP серверы + Настройка SMP серверов + Используются серверы предоставленные SimpleX Chat. + Введите SMP серверы, каждый сервер в отдельной строке: + Информация + Сохранить + + + Создать адрес + Удалить адрес? + Все контакты, которые соединились через этот адрес, сохранятся. + Ваш SimpleX адрес + Вы можете использовать адрес как ссылку или как QR код - через него можно с вами соединиться. + Вы сможете удалить адрес, сохранив контакты, которые через него соединились. + Поделиться\nссылкой + Удалить\nадрес + + + Имя профиля: + "Полное имя: + Ваш профиль + Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам.\n\nSimpleX серверы не могут получить доступ к вашему профилю. + Поменять аватар + Удалить аватар + Сохранить (и послать обновление контактам) + + + Вы котролируете ваш чат! + Платформа для сообщений и приложений, которая защищает вашу личную информацию и безопасность. + Мы не храним ваши контакты и сообщения (после доставки) на серверах. + Создать профиль + Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам. + Имя профиля не может содержать пробелы. + Имя профиля + Полное имя (не обязательно) + Создать + + + Как форматировать + Вы можете форматировать сообщения: + жирный + курсив + зачеркнуть + a + b + цвет + секрет + + diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 228c03d113..dda6fd3439 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -1,7 +1,218 @@ - - SimpleX + + SimpleX + + k + + + Connect via contact link? + Connect via invitation link? + Your profile will be sent to the contact that you received this link from. + Connect + + + Server connected + Connecting server… + You are connected to the server used to receive messages from this contact. + Trying to connect to the server used to receive messages from this contact (error: %1$s). + Trying to connect to the server used to receive messages from this contact. + + + deleted + sending files is not supported yet + receiving files is not supported yet + you + unknown message format + invalid message format + + + Error saving SMP servers + Make sure SMP server addresses are in correct format, line separated and are not duplicated. + + + Contact already exists + You are already connected to %1$s! via this link. + Invalid connection link + Please check that you used the correct link or ask your contact to send you another one. + Can\'t delete contact! + Contact %1$s! cannot be deleted, they are a member of the group(s) %2$s. + Instant notifications + + + Private instant notifications! + To preserve your privacy, instead of push notifications the app has a SimpleX background service – it uses a few percent of the battery per day. + It can be disabled via settings – notifications will still be shown while the app is running. - SimpleX Chat service - Waiting for incoming messages + SimpleX Chat service + Receiving messages… + + + Reply + Share + Copy + Edit + Delete + Delete message? + Message will be deleted - this cannot be undone! + For me only + For everybody + + + edited + sent + unauthorized send + send failed + unread + + + Welcome %1$s! + Welcome! + This text is available in settings + Your chats + + + Delete contact? + Contact and all messages will be deleted - this cannot be undone! + Delete contact + Connected + Disconnected + Error + Pending + + + Send Message + + + Back + Cancel + Confirm + Ok + no details + Add contact + Scan QR code + + + (create QR code\nor link) + (in person or in video call) + Create Group + (coming soon!) + + + Permission Denied! + Use Camera + From Gallery + + + Thank you for installing SimpleX Chat! + You can connect to SimpleX Chat developers to ask any questions and to receive updates. + To start a new chat + Tap button + above, then: + Add new contact: to create your one-time QR Code for your contact. + Scan QR code: to connect to your contact who shows QR code to you. + To connect via link + If you received SimpleX Chat invitation link, you can open it in your browser: + 💻 desktop: scan displayed QR code from the app, via Scan QR code. + 📱 mobile: tap Open in mobile app, then tap Connect in the app. + + + Accept connection request? + If you choose to reject sender will NOT be notified. + Accept + Reject + + + wants to connect to you! + + + profile image placeholder + profile image + + + Close button + link preview image + cancel link preview + Settings + QR Code + SimpleX Address + help + SimpleX Team + SimpleX Logo + Email + + + Invalid QR code + This QR code is not a link! + Invalid link! + This link is not a valid connection link! + Connection request sent! + You will be connected when your connection request is accepted, please wait or check later! + You will be connected when your contact\'s device is online, please wait or check later! + Show QR code for your contact\nto scan from the app + If you cannot meet in person, you can show QR code in the video call, or you can share the invitation link via any other channel. + Your chat profile will be sent\nto your contact + If you cannot meet in person, you can scan QR code in the video call, or your contact can share an invitation link. + Share invitation link + + + Your settings + Your SimpleX contact address + How to use SimpleX Chat + Markdown help + Markdown in messages + Connect to the developers + Send us email + Private notifications + Chat console + SMP servers + Install SimpleX Chat for terminal + Use SimpleX Chat servers? + Saved SMP servers will be removed. + Your SMP servers + Configure SMP servers + Using SimpleX Chat servers. + Enter one SMP server per line: + How to + Save + + + Create address + Delete address? + All your contacts will remain connected. + Your chat address + You can share your address as a link or as a QR code - anybody will be able to connect to you. + If you later delete it - you won\'t lose your contacts. + Share link + Delete address + + + Display name: + "Full name: + Your chat profile + Your profile is stored on your device and shared only with your contacts.\n\nSimpleX servers cannot see your profile. + Edit image + Delete image + Save (and notify contacts) + + + You control your chat! + The messaging and application platform protecting your privacy and security. + We don\'t store any of your contacts or messages (once delivered) on the servers. + Create profile + Your profile is stored on your device and shared only with your contacts. + Display name cannot contain whitespace. + Display Name + Full Name (Optional) + Create + + + How to use markdown + You can use markdown to format messages: + bold + italic + strike + a + b + colored + secret +