From d201c9528a49fcaf335c6087bf540119517149e7 Mon Sep 17 00:00:00 2001 From: IanRDavies Date: Sat, 16 Apr 2022 09:29:29 +0100 Subject: [PATCH 1/3] android: i18n (#529) * internationalization framework * rearrange strings * typo * minor id & xliff changes * response to comments * colour comments and verb suffixes * add russian language file * fix interpolation error * final strings * russian translations * update Russian translations, refactor strings to full sentences, add prefixes to content description names * fix layouts, improve font spacing * split sentence about User address, font line height * typo Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * update Russian translations Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * remove an * update Russian translations Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * commas Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> --- .../java/chat/simplex/app/MainActivity.kt | 20 +- .../main/java/chat/simplex/app/SimplexApp.kt | 6 +- .../java/chat/simplex/app/model/ChatModel.kt | 30 +-- .../java/chat/simplex/app/model/SimpleXAPI.kt | 44 ++-- .../java/chat/simplex/app/ui/theme/Color.kt | 2 +- .../java/chat/simplex/app/ui/theme/Theme.kt | 4 +- .../chat/simplex/app/views/TerminalView.kt | 4 +- .../chat/simplex/app/views/WelcomeView.kt | 21 +- .../simplex/app/views/chat/ChatInfoView.kt | 18 +- .../chat/simplex/app/views/chat/ChatView.kt | 4 +- .../simplex/app/views/chat/ComposeView.kt | 2 +- .../simplex/app/views/chat/ContextItemView.kt | 4 +- .../simplex/app/views/chat/SendMsgView.kt | 8 +- .../simplex/app/views/chat/item/CIMetaView.kt | 12 +- .../app/views/chat/item/ChatItemView.kt | 20 +- .../app/views/chat/item/DeletedItemView.kt | 9 +- .../app/views/chatlist/ChatHelpView.kt | 70 ++---- .../app/views/chatlist/ChatListNavLinkView.kt | 13 +- .../app/views/chatlist/ChatListView.kt | 22 +- .../app/views/chatlist/ChatPreviewView.kt | 6 +- .../app/views/chatlist/ContactRequestView.kt | 4 +- .../simplex/app/views/helpers/AlertManager.kt | 7 +- .../app/views/helpers/ChatInfoImage.kt | 5 +- .../app/views/helpers/CloseSheetBar.kt | 3 +- .../simplex/app/views/helpers/GetImageView.kt | 10 +- .../simplex/app/views/helpers/LinkPreviews.kt | 7 +- .../simplex/app/views/helpers/ModalView.kt | 3 +- .../chat/simplex/app/views/helpers/Util.kt | 159 ++++++++++++- .../app/views/newchat/AddContactView.kt | 19 +- .../app/views/newchat/ConnectContactView.kt | 41 ++-- .../simplex/app/views/newchat/NewChatSheet.kt | 21 +- .../chat/simplex/app/views/newchat/QRCode.kt | 4 +- .../app/views/usersettings/HelpView.kt | 13 +- .../views/usersettings/MarkdownHelpView.kt | 31 ++- .../app/views/usersettings/SMPServers.kt | 28 +-- .../app/views/usersettings/SettingsView.kt | 48 ++-- .../app/views/usersettings/UserAddressView.kt | 25 +- .../app/views/usersettings/UserProfileView.kt | 25 +- .../app/src/main/res/values-ru/strings.xml | 218 +++++++++++++++++ .../app/src/main/res/values/strings.xml | 219 +++++++++++++++++- 40 files changed, 896 insertions(+), 313 deletions(-) create mode 100644 apps/android/app/src/main/res/values-ru/strings.xml 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 + From 87c688a7395151bd4b5b74e1f03f20215363ccaa Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 16 Apr 2022 09:37:01 +0100 Subject: [PATCH 2/3] ios: i18n (#533) * ios: prepare for i18n * commit localizations * update Russian translations * fix notifications and layouts after localizations * localization docs * update translations Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * fix typo * update translations * fix translations for different link types * update translations Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * update translation Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * update translations * update translations Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> --- apps/ios/LOCALIZATION.md | 29 + apps/ios/Shared/ContentView.swift | 4 +- apps/ios/Shared/Model/ChatModel.swift | 12 +- apps/ios/Shared/Model/NtfManager.swift | 16 +- apps/ios/Shared/Model/SimpleXAPI.swift | 4 +- .../Shared/SimpleX (macOS)-Bridging-Header.h | 11 - apps/ios/Shared/Views/Chat/ChatInfoView.swift | 2 +- .../Views/Chat/ChatItem/FramedItemView.swift | 2 +- apps/ios/Shared/Views/Chat/ChatView.swift | 2 +- .../Chat/ComposeMessage/SendMessageView.swift | 2 +- apps/ios/Shared/Views/ChatList/ChatHelp.swift | 6 +- .../Views/ChatList/ChatListNavLink.swift | 2 +- .../Shared/Views/ChatList/ChatListView.swift | 29 +- .../Shared/Views/NewChat/AddContactView.swift | 4 +- .../Views/NewChat/ConnectContactView.swift | 6 +- .../Views/NewChat/CreateGroupView.swift | 2 +- .../Shared/Views/NewChat/NewChatButton.swift | 9 +- .../Views/UserSettings/MarkdownHelp.swift | 4 +- .../Views/UserSettings/SMPServers.swift | 2 +- .../Views/UserSettings/SettingsView.swift | 2 +- .../Views/UserSettings/UserAddress.swift | 2 +- .../AccentColor.colorset/Contents.json | 15 + .../Shared/Assets.xcassets/Contents.json | 6 + .../en.xcloc/Localized Contents/en.xliff | 801 ++++++++++++++++++ .../AccentColor.colorset/Contents.json | 23 + .../Shared/Assets.xcassets/Contents.json | 6 + .../en.lproj/Localizable.strings | Bin 0 -> 1678 bytes .../en.lproj/SimpleX--iOS--InfoPlist.strings | 4 + .../en.xcloc/contents.json | 12 + .../AccentColor.colorset/Contents.json | 15 + .../Shared/Assets.xcassets/Contents.json | 6 + .../ru.xcloc/Localized Contents/ru.xliff | 800 +++++++++++++++++ .../AccentColor.colorset/Contents.json | 23 + .../Shared/Assets.xcassets/Contents.json | 6 + .../en.lproj/Localizable.strings | Bin 0 -> 1678 bytes .../en.lproj/SimpleX--iOS--InfoPlist.strings | 4 + .../ru.xcloc/contents.json | 12 + apps/ios/SimpleX--macOS--Info.plist | 5 - apps/ios/SimpleX.xcodeproj/project.pbxproj | 310 +------ .../xcschemes/SimpleX (iOS).xcscheme | 89 ++ apps/ios/Tests macOS/Tests_macOS.swift | 42 - .../Tests macOS/Tests_macOSLaunchTests.swift | 32 - apps/ios/macOS/macOS.entitlements | 10 - apps/ios/ru.lproj/Localizable.strings | 462 ++++++++++ .../ru.lproj/SimpleX--iOS--InfoPlist.strings | 6 + 45 files changed, 2403 insertions(+), 438 deletions(-) create mode 100644 apps/ios/LOCALIZATION.md delete mode 100644 apps/ios/Shared/SimpleX (macOS)-Bridging-Header.h create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/Localizable.strings create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/SimpleX--iOS--InfoPlist.strings create mode 100644 apps/ios/SimpleX Localizations/en.xcloc/contents.json create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/Localizable.strings create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/SimpleX--iOS--InfoPlist.strings create mode 100644 apps/ios/SimpleX Localizations/ru.xcloc/contents.json delete mode 100644 apps/ios/SimpleX--macOS--Info.plist create mode 100644 apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme delete mode 100644 apps/ios/Tests macOS/Tests_macOS.swift delete mode 100644 apps/ios/Tests macOS/Tests_macOSLaunchTests.swift delete mode 100644 apps/ios/macOS/macOS.entitlements create mode 100644 apps/ios/ru.lproj/Localizable.strings create mode 100644 apps/ios/ru.lproj/SimpleX--iOS--InfoPlist.strings diff --git a/apps/ios/LOCALIZATION.md b/apps/ios/LOCALIZATION.md new file mode 100644 index 0000000000..40fe37c215 --- /dev/null +++ b/apps/ios/LOCALIZATION.md @@ -0,0 +1,29 @@ +# Localization + +## Creating localization keys + +There are three ways XCode generates localization keys from strings: + +1. Automatically, from the texts used in standard components `Text`, `Label`, `Button`, etc. + +2. All strings passed to view variables and function parameters declared as `LocalizedStringKey` type. Only string constants (possibly, with interpolation) or other variables of type `LocalizedStringKey` can be passed to these parameters. See, for example, ContentView.swift. + +3. All strings wrapped in `NSLocalizedString`. Please note that such strings do not support swift interpolation, instead formatted strings should be used: + +```swift +String.localizedStringWithFormat(NSLocalizedString("You can now send messages to %@", comment: "notification body") +``` + +## Adding strings to the existing localizations + +1. Choose `Product -> Export Localizations...` in the menu, choose `ios` folder as the destination and `SimpleX Localizations` as the folder name, confirm to overwrite it (make sure not to save to subfolder). +2. Add `target` keys to the localizations that were added or changed. +3. Choose `Product -> Import Localizations...` for any non-Enlish folders - that would update Localizable files. + +Localizable files values can be edited directly, the changes will be included in the next export. Following the process above though guarantees that all strings are localized. + +## Development + +Make sure to enable the option `Show non-localized strings` in `Product -> Scheme -> Edit scheme...` menu - it will be showing all non-localized strings as all caps. + +Read more about editing XLIFF and string files here: https://developer.apple.com/documentation/xcode/editing-xliff-and-strings-files diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index b9e7fb066d..7d911d4c82 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -36,7 +36,7 @@ struct ContentView: View { func notificationAlert() -> Alert { Alert( - title: Text("Notification are disabled!"), + title: Text("Notifications are disabled!"), message: Text("The app can notify you when you receive messages or contact requests - please open settings to enable."), primaryButton: .default(Text("Open Settings")) { DispatchQueue.main.async { @@ -61,7 +61,7 @@ final class AlertManager: ObservableObject { } } - func showAlertMsg(title: String, message: String? = nil) { + func showAlertMsg(title: LocalizedStringKey, message: LocalizedStringKey? = nil) { if let message = message { showAlert(Alert(title: Text(title), message: Text(message))) } else { diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index b70aecf771..e05806458e 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -384,7 +384,7 @@ final class Chat: ObservableObject, Identifiable { case disconnected case error(String) - var statusString: String { + var statusString: LocalizedStringKey { get { switch self { case .connected: return "Server connected" @@ -394,7 +394,7 @@ final class Chat: ObservableObject, Identifiable { } } - var statusExplanation: String { + var statusExplanation: LocalizedStringKey { get { switch self { case .connected: return "You are connected to the server used to receive messages from this contact." @@ -719,10 +719,10 @@ enum CIContent: Decodable, ItemContent { switch self { case let .sndMsgContent(mc): return mc.text case let .rcvMsgContent(mc): return mc.text - case .sndDeleted: return "deleted" - case .rcvDeleted: return "deleted" - case .sndFileInvitation: return "sending files is not supported yet" - case .rcvFileInvitation: return "receiving files is not supported yet" + case .sndDeleted: return NSLocalizedString("deleted", comment: "deleted chat item") + case .rcvDeleted: return NSLocalizedString("deleted", comment: "deleted chat item") + case .sndFileInvitation: return NSLocalizedString("sending files is not supported yet", comment: "to be removed") + case .rcvFileInvitation: return NSLocalizedString("receiving files is not supported yet", comment: "to be removed") } } } diff --git a/apps/ios/Shared/Model/NtfManager.swift b/apps/ios/Shared/Model/NtfManager.swift index 5d53cb31a9..8861968b2c 100644 --- a/apps/ios/Shared/Model/NtfManager.swift +++ b/apps/ios/Shared/Model/NtfManager.swift @@ -92,22 +92,22 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { identifier: ntfCategoryContactRequest, actions: [UNNotificationAction( identifier: ntfActionAccept, - title: "Accept" + title: NSLocalizedString("Accept", comment: "accept contact request via notification") )], intentIdentifiers: [], - hiddenPreviewsBodyPlaceholder: "New contact request" + hiddenPreviewsBodyPlaceholder: NSLocalizedString("New contact request", comment: "notification") ), UNNotificationCategory( identifier: ntfCategoryContactConnected, actions: [], intentIdentifiers: [], - hiddenPreviewsBodyPlaceholder: "Contact is connected" + hiddenPreviewsBodyPlaceholder: NSLocalizedString("Contact is connected", comment: "notification") ), UNNotificationCategory( identifier: ntfCategoryMessageReceived, actions: [], intentIdentifiers: [], - hiddenPreviewsBodyPlaceholder: "New message" + hiddenPreviewsBodyPlaceholder: NSLocalizedString("New message", comment: "notifications") ) ]) } @@ -139,8 +139,8 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { logger.debug("NtfManager.notifyContactRequest") addNotification( categoryIdentifier: ntfCategoryContactRequest, - title: "\(contactRequest.displayName) wants to connect!", - body: "Accept contact request from \(contactRequest.chatViewName)?", + title: String.localizedStringWithFormat(NSLocalizedString("%@ wants to connect!", comment: "notification title"), contactRequest.displayName), + body: String.localizedStringWithFormat(NSLocalizedString("Accept contact request from %@?", comment: "notification body"), contactRequest.chatViewName), targetContentIdentifier: nil, userInfo: ["chatId": contactRequest.id, "contactRequestId": contactRequest.apiId] ) @@ -150,8 +150,8 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { logger.debug("NtfManager.notifyContactConnected") addNotification( categoryIdentifier: ntfCategoryContactConnected, - title: "\(contact.displayName) is connected!", - body: "You can now send messages to \(contact.chatViewName)", + title: String.localizedStringWithFormat(NSLocalizedString("%@ is connected!", comment: "notification title"), contact.displayName), + body: String.localizedStringWithFormat(NSLocalizedString("You can now send messages to %@", comment: "notification body"), contact.chatViewName), targetContentIdentifier: contact.id // userInfo: ["chatId": contact.id, "contactId": contact.apiId] ) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index f87a178dc1..7622ac881d 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -467,13 +467,13 @@ func apiConnect(connReq: String) async throws -> Bool { case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))): am.showAlertMsg( title: "Connection timeout", - message: "Please check your network connection and try again" + message: "Please check your network connection and try again." ) return false case .chatCmdError(.errorAgent(.BROKER(.NETWORK))): am.showAlertMsg( title: "Connection error", - message: "Please check your network connection and try again" + message: "Please check your network connection and try again." ) return false default: throw r diff --git a/apps/ios/Shared/SimpleX (macOS)-Bridging-Header.h b/apps/ios/Shared/SimpleX (macOS)-Bridging-Header.h deleted file mode 100644 index bc28b42d38..0000000000 --- a/apps/ios/Shared/SimpleX (macOS)-Bridging-Header.h +++ /dev/null @@ -1,11 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - -extern void hs_init(int argc, char **argv[]); - -typedef void* chat_ctrl; - -extern chat_ctrl chat_init(char *path); -extern char *chat_send_cmd(chat_ctrl ctl, char *cmd); -extern char *chat_recv_msg(chat_ctrl ctl); diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index ec52ac977a..f345f458fb 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -64,7 +64,7 @@ struct ChatInfoView: View { private func deleteContactAlert(_ contact: Contact) -> Alert { Alert( title: Text("Delete contact?"), - message: Text("Contact and all messages will be deleted"), + message: Text("Contact and all messages will be deleted - this cannot be undone!"), primaryButton: .destructive(Text("Delete")) { Task { do { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift index 57ac507d8e..0567598c03 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift @@ -87,7 +87,7 @@ struct FramedItemView: View { } } - private func msgDeliveryError(_ err: String) { + private func msgDeliveryError(_ err: LocalizedStringKey) { AlertManager.shared.showAlertMsg( title: "Message delivery error", message: err diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index ee638d8bf2..04cb218ede 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -100,7 +100,7 @@ struct ChatView: View { Button { chatModel.chatId = nil } label: { HStack(spacing: 4) { Image(systemName: "chevron.backward") - Text("Chats") + Text("Chats", comment: "back button to return to chats list") } } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift index 86ab936803..afaa4788b3 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift @@ -11,7 +11,7 @@ import SwiftUI struct SendMessageView: View { var sendMessage: (String) -> Void var inProgress: Bool = false - @Binding var message: String //Lorem ipsum dolor sit amet, consectetur" // adipiscing elit, sed do eiusmod tempor incididunt ut labor7 et dolore magna aliqua. Ut enim ad minim veniam, quis"// nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."// Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + @Binding var message: String @Namespace var namespace @FocusState.Binding var keyboardVisible: Bool @Binding var editing: Bool diff --git a/apps/ios/Shared/Views/ChatList/ChatHelp.swift b/apps/ios/Shared/Views/ChatList/ChatHelp.swift index 58a43eb6cf..3a9dc58f64 100644 --- a/apps/ios/Shared/Views/ChatList/ChatHelp.swift +++ b/apps/ios/Shared/Views/ChatList/ChatHelp.swift @@ -16,9 +16,9 @@ struct ChatHelp: View { VStack(alignment: .leading, spacing: 10) { Text("Thank you for installing SimpleX Chat!") - HStack(spacing: 4) { - Text("You can") - Button("connect to SimpleX Chat founder.") { + VStack(alignment: .leading, spacing: 0) { + Text("To ask any questions and to receive updates:") + Button("connect to SimpleX Chat developers.") { showSettings = false DispatchQueue.main.async { UIApplication.shared.open(simplexTeamURL) diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index d2ec6c11e2..d25ffafe53 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -116,7 +116,7 @@ struct ChatListNavLink: View { private func deleteContactAlert(_ contact: Contact) -> Alert { Alert( title: Text("Delete contact?"), - message: Text("Contact and all messages will be deleted"), + message: Text("Contact and all messages will be deleted - this cannot be undone!"), primaryButton: .destructive(Text("Delete")) { Task { do { diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index f74e94a957..e220882494 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -20,19 +20,13 @@ struct ChatListView: View { let v = NavigationView { List { if chatModel.chats.isEmpty { - VStack(alignment: .leading) { - ChatHelp(showSettings: $showSettings) - HStack { - Text("This text is available in settings") - SettingsButton() - } - .padding(.leading) + ChatHelp(showSettings: $showSettings) + } else { + ForEach(filteredChats()) { chat in + ChatListNavLink(chat: chat) + .padding(.trailing, -16) } } - ForEach(filteredChats()) { chat in - ChatListNavLink(chat: chat) - .padding(.trailing, -16) - } } .onChange(of: chatModel.chatId) { _ in if chatModel.chatId == nil, let chatId = chatModel.chatToTop { @@ -80,22 +74,23 @@ struct ChatListView: View { logger.debug("ChatListView.connectViaUrlAlert path: \(path)") if (path == "/contact" || path == "/invitation") { path.removeFirst() - let action = path + let action: ConnReqType = path == "contact" ? .contact : .invitation let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)") + let title: LocalizedStringKey + if case .contact = action { title = "Connect via contact link?" } + else { title = "Connect via invitation link?" } return Alert( - title: Text("Connect via \(action) link?"), + title: Text(title), message: Text("Your profile will be sent to the contact that you received this link from"), primaryButton: .default(Text("Connect")) { DispatchQueue.main.async { Task { do { let ok = try await apiConnect(connReq: link) - if ok { - connectionReqSentAlert(action == "contact" ? .contact : .invitation) - } + if ok { connectionReqSentAlert(action) } } catch { let err = error.localizedDescription - AlertManager.shared.showAlertMsg(title: "Connection error", message: err) + AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(err)") logger.debug("ChatListView.connectViaUrlAlert: apiConnect error: \(err)") } } diff --git a/apps/ios/Shared/Views/NewChat/AddContactView.swift b/apps/ios/Shared/Views/NewChat/AddContactView.swift index 3c924697ec..9101e893f8 100644 --- a/apps/ios/Shared/Views/NewChat/AddContactView.swift +++ b/apps/ios/Shared/Views/NewChat/AddContactView.swift @@ -22,9 +22,7 @@ struct AddContactView: View { .multilineTextAlignment(.center) QRCode(uri: connReqInvitation) .padding() - (Text("If you cannot meet in person, you can ") + - Text("scan QR code in the video call").bold() + - Text(", or you can share the invitation link via any other channel.")) + Text("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.") .font(.subheadline) .multilineTextAlignment(.center) .padding(.horizontal) diff --git a/apps/ios/Shared/Views/NewChat/ConnectContactView.swift b/apps/ios/Shared/Views/NewChat/ConnectContactView.swift index 2513801fcc..9944dc21fb 100644 --- a/apps/ios/Shared/Views/NewChat/ConnectContactView.swift +++ b/apps/ios/Shared/Views/NewChat/ConnectContactView.swift @@ -26,7 +26,11 @@ struct ConnectContactView: View { .aspectRatio(1, contentMode: .fit) .border(.gray) } - .padding(13.0) + .padding(12) + Text("If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.") + .font(.subheadline) + .multilineTextAlignment(.center) + .padding(.horizontal) } } diff --git a/apps/ios/Shared/Views/NewChat/CreateGroupView.swift b/apps/ios/Shared/Views/NewChat/CreateGroupView.swift index 89a65f1ecd..54f1d6c206 100644 --- a/apps/ios/Shared/Views/NewChat/CreateGroupView.swift +++ b/apps/ios/Shared/Views/NewChat/CreateGroupView.swift @@ -10,7 +10,7 @@ import SwiftUI struct CreateGroupView: View { var body: some View { - Text("CreateGroupView") + EmptyView() } } diff --git a/apps/ios/Shared/Views/NewChat/NewChatButton.swift b/apps/ios/Shared/Views/NewChat/NewChatButton.swift index 0fff56bf3f..83f7fd85fe 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatButton.swift @@ -65,7 +65,7 @@ struct NewChatButton: View { } func connectionErrorAlert(_ error: Error) { - AlertManager.shared.showAlertMsg(title: "Connection error", message: error.localizedDescription) + AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(error.localizedDescription)") } } @@ -75,12 +75,11 @@ enum ConnReqType: Equatable { } func connectionReqSentAlert(_ type: ConnReqType) { - let whenConnected = type == .contact - ? "your connection request is accepted" - : "your contact's device is online" AlertManager.shared.showAlertMsg( title: "Connection request sent!", - message: "You will be connected when \(whenConnected), please wait or check later!" + message: type == .contact + ? "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!" ) } diff --git a/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift b/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift index 855d0adff1..c5dafc8663 100644 --- a/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift +++ b/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift @@ -30,9 +30,9 @@ struct MarkdownHelp: View { } } -private func mdFormat(_ format: String, _ example: Text) -> some View { +private func mdFormat(_ format: LocalizedStringKey, _ example: Text) -> some View { HStack { - Text(format).frame(width: 88, alignment: .leading) + Text(format).frame(width: 120, alignment: .leading) example } } diff --git a/apps/ios/Shared/Views/UserSettings/SMPServers.swift b/apps/ios/Shared/Views/UserSettings/SMPServers.swift index 861a934980..a687f613c3 100644 --- a/apps/ios/Shared/Views/UserSettings/SMPServers.swift +++ b/apps/ios/Shared/Views/UserSettings/SMPServers.swift @@ -82,7 +82,7 @@ struct SMPServers: View { saveUserSMPServers() } .alert(isPresented: $showBadServersAlert) { - Alert(title: Text("Error saving SMP servers"), message: Text("Make sure SMP server addresses are in correct format, line separated and are not duplicated")) + Alert(title: Text("Error saving SMP servers"), message: Text("Make sure SMP server addresses are in correct format, line separated and are not duplicated.")) } Spacer() howToButton() diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index feb7ec85fc..933f5f95bd 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -100,7 +100,7 @@ struct SettingsView: View { UIApplication.shared.open(simplexTeamURL) } } label: { - Text("Chat with the founder") + Text("Chat with the developers") } } HStack { diff --git a/apps/ios/Shared/Views/UserSettings/UserAddress.swift b/apps/ios/Shared/Views/UserSettings/UserAddress.swift index e15bd167c1..c1ff708233 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddress.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddress.swift @@ -14,7 +14,7 @@ struct UserAddress: View { var body: some View { VStack (alignment: .leading) { - 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.") + Text("You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it.") .padding(.bottom) if let userAdress = chatModel.userAddress { QRCode(uri: userAdress) diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000..d0ffdd59b0 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,15 @@ +{ + "colors" : [ + { + "idiom" : "universal", + "locale" : "en" + } + ], + "properties" : { + "localizable" : true + }, + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff new file mode 100644 index 0000000000..6cea986b34 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -0,0 +1,801 @@ + + + +
+ +
+ + + + + No comment provided by engineer. + + + ( + ( + No comment provided by engineer. + + + (can be copied) + (can be copied) + No comment provided by engineer. + + + !1 colored! + !1 colored! + No comment provided by engineer. + + + #secret# + #secret# + No comment provided by engineer. + + + %@ is connected! + %@ is connected! + notification title + + + %@ wants to connect! + %@ wants to connect! + notification title + + + %lld + %lld + No comment provided by engineer. + + + %lldk + %lldk + No comment provided by engineer. + + + (shared only with your contacts) + (shared only with your contacts) + No comment provided by engineer. + + + ) + ) + No comment provided by engineer. + + + **Add new contact**: to create your one-time QR Code for your contact. + **Add new contact**: to create your one-time QR Code for your contact. + No comment provided by engineer. + + + **Scan QR code**: to connect to your contact who shows QR code to you. + **Scan QR code**: to connect to your contact who shows QR code to you. + No comment provided by engineer. + + + *bold* + *bold* + No comment provided by engineer. + + + , + , + No comment provided by engineer. + + + 6 + 6 + No comment provided by engineer. + + + : + : + No comment provided by engineer. + + + : %@ + : %@ + No comment provided by engineer. + + + Accept + Accept + accept contact request via notification + + + Accept contact + Accept contact + No comment provided by engineer. + + + Accept contact request from %@? + Accept contact request from %@? + notification body + + + Add contact + Add contact + No comment provided by engineer. + + + All your contacts will remain connected + All your contacts will remain connected + No comment provided by engineer. + + + Cancel + Cancel + No comment provided by engineer. + + + Chat console + Chat console + No comment provided by engineer. + + + Chat with the developers + Chat with the developers + No comment provided by engineer. + + + Chats + Chats + back button to return to chats list + + + Choose from library + Choose from library + No comment provided by engineer. + + + Configure SMP servers + Configure SMP servers + No comment provided by engineer. + + + Confirm + Confirm + No comment provided by engineer. + + + Connect + Connect + No comment provided by engineer. + + + Connect via contact link? + Connect via contact link? + No comment provided by engineer. + + + Connect via invitation link? + Connect via invitation link? + No comment provided by engineer. + + + Connecting server… + Connecting server… + No comment provided by engineer. + + + Connecting server… (error: %@) + Connecting server… (error: %@) + No comment provided by engineer. + + + Connecting... + Connecting... + No comment provided by engineer. + + + Connection error + Connection error + No comment provided by engineer. + + + Connection request + Connection request + No comment provided by engineer. + + + Connection request sent! + Connection request sent! + No comment provided by engineer. + + + Connection timeout + Connection timeout + No comment provided by engineer. + + + Contact already exists + Contact already exists + No comment provided by engineer. + + + Contact and all messages will be deleted - this cannot be undone! + Contact and all messages will be deleted - this cannot be undone! + No comment provided by engineer. + + + Contact is connected + Contact is connected + notification + + + Copy + Copy + No comment provided by engineer. + + + Create + Create + No comment provided by engineer. + + + Create address + Create address + No comment provided by engineer. + + + Create group + Create group + No comment provided by engineer. + + + Create profile + Create profile + No comment provided by engineer. + + + Delete + Delete + No comment provided by engineer. + + + Delete address + Delete address + No comment provided by engineer. + + + Delete address? + Delete address? + No comment provided by engineer. + + + Delete contact + Delete contact + No comment provided by engineer. + + + Delete contact? + Delete contact? + No comment provided by engineer. + + + Delete for me + Delete for me + No comment provided by engineer. + + + Delete group + Delete group + No comment provided by engineer. + + + Delete message? + Delete message? + No comment provided by engineer. + + + Develop + Develop + No comment provided by engineer. + + + Display name + Display name + No comment provided by engineer. + + + Edit + Edit + No comment provided by engineer. + + + Enter one SMP server per line: + Enter one SMP server per line: + No comment provided by engineer. + + + Error saving SMP servers + Error saving SMP servers + No comment provided by engineer. + + + Error: %@ + Error: %@ + No comment provided by engineer. + + + Error: URL is invalid + Error: URL is invalid + No comment provided by engineer. + + + Full name (optional) + Full name (optional) + No comment provided by engineer. + + + Group deletion is not supported + Group deletion is not supported + No comment provided by engineer. + + + Help + Help + No comment provided by engineer. + + + How to + How to + No comment provided by engineer. + + + How to use SimpleX Chat + How to use SimpleX Chat + No comment provided by engineer. + + + How to use markdown + How to use markdown + No comment provided by engineer. + + + If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link. + If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link. + No comment provided by engineer. + + + 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. + 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. + No comment provided by engineer. + + + If you received SimpleX Chat invitation link you can open it in your browser: + If you received SimpleX Chat invitation link you can open it in your browser: + No comment provided by engineer. + + + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) + No comment provided by engineer. + + + Invalid connection link + Invalid connection link + No comment provided by engineer. + + + Make sure SMP server addresses are in correct format, line separated and are not duplicated. + Make sure SMP server addresses are in correct format, line separated and are not duplicated. + No comment provided by engineer. + + + Markdown in messages + Markdown in messages + No comment provided by engineer. + + + Message delivery error + Message delivery error + No comment provided by engineer. + + + Most likely this contact has deleted the connection with you. + Most likely this contact has deleted the connection with you. + No comment provided by engineer. + + + New contact request + New contact request + notification + + + New message + New message + notifications + + + Notifications are disabled! + Notifications are disabled! + No comment provided by engineer. + + + Open Settings + Open Settings + No comment provided by engineer. + + + Please check that you used the correct link or ask your contact to send you another one. + Please check that you used the correct link or ask your contact to send you another one. + No comment provided by engineer. + + + Please check your network connection and try again. + Please check your network connection and try again. + No comment provided by engineer. + + + Profile image + Profile image + No comment provided by engineer. + + + Read + Read + No comment provided by engineer. + + + Reject + Reject + No comment provided by engineer. + + + Reject contact (sender NOT notified) + Reject contact (sender NOT notified) + No comment provided by engineer. + + + Reject contact request + Reject contact request + No comment provided by engineer. + + + Reply + Reply + No comment provided by engineer. + + + SMP servers + SMP servers + No comment provided by engineer. + + + Save + Save + No comment provided by engineer. + + + Save (and notify contacts) + Save (and notify contacts) + No comment provided by engineer. + + + Saved SMP servers will be removed + Saved SMP servers will be removed + No comment provided by engineer. + + + Scan QR code + Scan QR code + No comment provided by engineer. + + + Server connected + Server connected + No comment provided by engineer. + + + Settings + Settings + No comment provided by engineer. + + + Share + Share + No comment provided by engineer. + + + Share invitation link + Share invitation link + No comment provided by engineer. + + + Share link + Share link + No comment provided by engineer. + + + Show QR code to your contact +to scan from the app + Show QR code to your contact +to scan from the app + No comment provided by engineer. + + + Start new chat + Start new chat + No comment provided by engineer. + + + Take picture + Take picture + No comment provided by engineer. + + + Tap button + Tap button + No comment provided by engineer. + + + Thank you for installing SimpleX Chat! + Thank you for installing SimpleX Chat! + No comment provided by engineer. + + + The app can notify you when you receive messages or contact requests - please open settings to enable. + The app can notify you when you receive messages or contact requests - please open settings to enable. + No comment provided by engineer. + + + The messaging and application platform 100% private by design! + The messaging and application platform 100% private by design! + No comment provided by engineer. + + + The sender will NOT be notified + The sender will NOT be notified + No comment provided by engineer. + + + To ask any questions and to receive updates: + To ask any questions and to receive updates: + No comment provided by engineer. + + + To connect via link + To connect via link + No comment provided by engineer. + + + To start a new chat + To start a new chat + No comment provided by engineer. + + + Trying to connect to the server used to receive messages from this contact (error: %@). + Trying to connect to the server used to receive messages from this contact (error: %@). + No comment provided by engineer. + + + Trying to connect to the server used to receive messages from this contact. + Trying to connect to the server used to receive messages from this contact. + No comment provided by engineer. + + + Unexpected error: %@ + Unexpected error: %@ + No comment provided by engineer. + + + Use SimpleX Chat servers? + Use SimpleX Chat servers? + No comment provided by engineer. + + + Using SimpleX Chat servers. + Using SimpleX Chat servers. + No comment provided by engineer. + + + Welcome %@! + Welcome %@! + No comment provided by engineer. + + + You + You + No comment provided by engineer. + + + You are already connected to %@ via this link. + You are already connected to %@ via this link. + No comment provided by engineer. + + + You are connected to the server used to receive messages from this contact. + You are connected to the server used to receive messages from this contact. + No comment provided by engineer. + + + You can now send messages to %@ + You can now send messages to %@ + notification body + + + You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it. + You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it. + No comment provided by engineer. + + + You can use markdown to format messages: + You can use markdown to format messages: + No comment provided by engineer. + + + You control your chat! + You control your chat! + No comment provided by engineer. + + + You will be connected when your connection request is accepted, please wait or check later! + You will be connected when your connection request is accepted, please wait or check later! + No comment provided by engineer. + + + You will be connected when your contact's device is online, please wait or check later! + You will be connected when your contact's device is online, please wait or check later! + No comment provided by engineer. + + + Your SMP servers + Your SMP servers + No comment provided by engineer. + + + Your SimpleX contact address + Your SimpleX contact address + No comment provided by engineer. + + + Your chat address + Your chat address + No comment provided by engineer. + + + Your chat profile + Your chat profile + No comment provided by engineer. + + + Your chat profile will be sent to your contact + Your chat profile will be sent to your contact + No comment provided by engineer. + + + Your chats + Your chats + No comment provided by engineer. + + + Your profile is stored on your device and shared only with your contacts. +SimpleX servers cannot see your profile. + Your profile is stored on your device and shared only with your contacts. +SimpleX servers cannot see your profile. + No comment provided by engineer. + + + Your profile will be sent to the contact that you received this link from + Your profile will be sent to the contact that you received this link from + No comment provided by engineer. + + + Your profile, contacts and messages (once delivered) are only stored locally on your device. + Your profile, contacts and messages (once delivered) are only stored locally on your device. + No comment provided by engineer. + + + Your settings + Your settings + No comment provided by engineer. + + + [Send us email](mailto:chat@simplex.chat) + [Send us email](mailto:chat@simplex.chat) + No comment provided by engineer. + + + _italic_ + _italic_ + No comment provided by engineer. + + + `a + b` + `a + b` + No comment provided by engineer. + + + above, then: + above, then: + No comment provided by engineer. + + + bold + bold + No comment provided by engineer. + + + colored + colored + No comment provided by engineer. + + + connect to SimpleX Chat developers. + connect to SimpleX Chat developers. + No comment provided by engineer. + + + deleted + deleted + deleted chat item + + + italic + italic + No comment provided by engineer. + + + receiving files is not supported yet + receiving files is not supported yet + to be removed + + + secret + secret + No comment provided by engineer. + + + sending files is not supported yet + sending files is not supported yet + to be removed + + + strike + strike + No comment provided by engineer. + + + v%@ (%@) + v%@ (%@) + No comment provided by engineer. + + + wants to connect to you! + wants to connect to you! + No comment provided by engineer. + + + ~strike~ + ~strike~ + No comment provided by engineer. + + + 💻 desktop: scan displayed QR code from the app, via **Scan QR code**. + 💻 desktop: scan displayed QR code from the app, via **Scan QR code**. + No comment provided by engineer. + + + 📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app. + 📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app. + No comment provided by engineer. + + +
+ +
+ +
+ + + SimpleX + SimpleX + Bundle name + + + SimpleX needs camera access to scan QR codes to connect to other app users + SimpleX needs camera access to scan QR codes to connect to other app users + Privacy - Camera Usage Description + + +
+
diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000..aaa7f79bc8 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,23 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0.000", + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.533" + } + }, + "idiom" : "universal" + } + ], + "properties" : { + "localizable" : true + }, + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..8a3092dda65330a4b5cb82313b5aa45120322b7e GIT binary patch literal 1678 zcmb`HO-{ow5QS&mQy3&R-Shwm0b8uFM%T;q2pXRQ8&{X6F^UQ|+`ubI6M@)xqX1SgKaWBt_Cl8^0Fh52a3 zYlKWPvZWPm>s9@F?P^|YT4fpu$Ep9;TOP_o{OL?N)Mu=muwuK%d)%)kZ!PwH3=8d) zA&&w+HD4k-c4NE4`_PW` + + +
+ +
+ + + + + No comment provided by engineer. + + + ( + ( + No comment provided by engineer. + + + (can be copied) + (можно скопировать) + No comment provided by engineer. + + + !1 colored! + !1 цвет! + No comment provided by engineer. + + + #secret# + #секрет# + No comment provided by engineer. + + + %@ is connected! + Установлено соединение с %@! + notification title + + + %@ wants to connect! + %@ хочет соединиться! + notification title + + + %lld + %lld + No comment provided by engineer. + + + %lldk + %lldk + No comment provided by engineer. + + + (shared only with your contacts) + (отправляется только вашим контактам) + No comment provided by engineer. + + + ) + ) + No comment provided by engineer. + + + **Add new contact**: to create your one-time QR Code for your contact. + **Добавить новый контакт**: чтобы создать одноразовый QR код или ссылку для вашего контакта. + No comment provided by engineer. + + + **Scan QR code**: to connect to your contact who shows QR code to you. + **Сканировать QR код**: чтобы соединиться с вашим контактом (который показывает вам QR код). + No comment provided by engineer. + + + *bold* + \*жирный* + No comment provided by engineer. + + + , + , + No comment provided by engineer. + + + 6 + 6 + No comment provided by engineer. + + + : + : + No comment provided by engineer. + + + : %@ + : %@ + No comment provided by engineer. + + + Accept + Принять + accept contact request via notification + + + Accept contact + Принять запрос + No comment provided by engineer. + + + Accept contact request from %@? + Принять запрос на соединение от %@? + notification body + + + Add contact + Добавить контакт + No comment provided by engineer. + + + All your contacts will remain connected + Все контакты, которые соединились через этот адрес, сохранятся. + No comment provided by engineer. + + + Cancel + Отменить + No comment provided by engineer. + + + Chat console + Консоль + No comment provided by engineer. + + + Chat with the developers + Соединиться с разработчиками + No comment provided by engineer. + + + Chats + Назад + back button to return to chats list + + + Choose from library + Выбрать из библиотеки + No comment provided by engineer. + + + Configure SMP servers + Настройка SMP серверов + No comment provided by engineer. + + + Confirm + Подтвердить + No comment provided by engineer. + + + Connect + Соединиться + No comment provided by engineer. + + + Connect via contact link? + Соединиться через ссылку-контакт? + No comment provided by engineer. + + + Connect via invitation link? + Соединиться через ссылку-приглашение? + No comment provided by engineer. + + + Connecting server… + Устанавливается соединение с сервером… + No comment provided by engineer. + + + Connecting server… (error: %@) + Устанавливается соединение с сервером… (ошибка: %@) + No comment provided by engineer. + + + Connecting... + Устанавливается соединение… + No comment provided by engineer. + + + Connection error + Ошибка соединения + No comment provided by engineer. + + + Connection request + Запрос на соединение + No comment provided by engineer. + + + Connection request sent! + Запрос на соединение отправлен! + No comment provided by engineer. + + + Connection timeout + Превышено время соединения + No comment provided by engineer. + + + Contact already exists + Существующий контакт + No comment provided by engineer. + + + Contact and all messages will be deleted - this cannot be undone! + Контакт и все сообщения будут удалены - это действие нельзя отменить! + No comment provided by engineer. + + + Contact is connected + Соединение с контактом установлено + notification + + + Copy + Скопировать + No comment provided by engineer. + + + Create + Создать + No comment provided by engineer. + + + Create address + Создать адрес + No comment provided by engineer. + + + Create group + Создать группу + No comment provided by engineer. + + + Create profile + Создать профиль + No comment provided by engineer. + + + Delete + Удалить + No comment provided by engineer. + + + Delete address + Удалить адрес + No comment provided by engineer. + + + Delete address? + Удалить адрес? + No comment provided by engineer. + + + Delete contact + Удалить контакт + No comment provided by engineer. + + + Delete contact? + Удалить контакт? + No comment provided by engineer. + + + Delete for me + Удалить для меня + No comment provided by engineer. + + + Delete group + Удалить группу + No comment provided by engineer. + + + Delete message? + Удалить сообщение? + No comment provided by engineer. + + + Develop + Для разработчиков + No comment provided by engineer. + + + Display name + Имя профиля + No comment provided by engineer. + + + Edit + Редактировать + No comment provided by engineer. + + + Enter one SMP server per line: + Введите SMP серверы, каждый на отдельной строке: + No comment provided by engineer. + + + Error saving SMP servers + Ошибка при сохранении SMP серверов + No comment provided by engineer. + + + Error: %@ + Ошибка: %@ + No comment provided by engineer. + + + Error: URL is invalid + Ошибка: неверная ссылка + No comment provided by engineer. + + + Full name (optional) + Полное имя (не обязательно) + No comment provided by engineer. + + + Group deletion is not supported + Удаление групп не поддерживается + No comment provided by engineer. + + + Help + Помощь + No comment provided by engineer. + + + How to + Информация + No comment provided by engineer. + + + How to use SimpleX Chat + Как использовать SimpleX Chat + No comment provided by engineer. + + + How to use markdown + Как форматировать + No comment provided by engineer. + + + If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link. + Если вы не можете встретиться лично, вы можете **сосканировать QR код во время видеозвонка**, или ваш контакт может отправить вам ссылку. + No comment provided by engineer. + + + 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. + Если вы не можете встретиться лично, вы можете **показать QR код во время видеозвонка** или отправить ссылку через любой другой канал связи. + No comment provided by engineer. + + + If you received SimpleX Chat invitation link you can open it in your browser: + Если вы получили ссылку с приглашением из SimpleX Chat, вы можете открыть её в браузере: + No comment provided by engineer. + + + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) + [SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat) + No comment provided by engineer. + + + Invalid connection link + Ошибка в ссылке контакта + No comment provided by engineer. + + + Make sure SMP server addresses are in correct format, line separated and are not duplicated. + Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется. + No comment provided by engineer. + + + Markdown in messages + Форматирование сообщений + No comment provided by engineer. + + + Message delivery error + Ошибка доставки сообщения + No comment provided by engineer. + + + Most likely this contact has deleted the connection with you. + Скорее всего, этот контакт удалил соединение с вами. + No comment provided by engineer. + + + New contact request + Новый запрос на соединение + notification + + + New message + Новое сообщение + notifications + + + Notifications are disabled! + Уведомления выключены + No comment provided by engineer. + + + Open Settings + Открыть Настройки + No comment provided by engineer. + + + Please check that you used the correct link or ask your contact to send you another one. + Пожалуйста, проверьте, что вы использовали правильную ссылку или попросите, чтобы ваш контакт отправил вам другую ссылку. + No comment provided by engineer. + + + Please check your network connection and try again. + Пожалуйста, проверьте ваше соединение с сетью и попробуйте еще раз. + No comment provided by engineer. + + + Profile image + Аватар + No comment provided by engineer. + + + Read + Прочитано + No comment provided by engineer. + + + Reject + Отклонить + No comment provided by engineer. + + + Reject contact (sender NOT notified) + Отклонить (не уведомляя отправителя) + No comment provided by engineer. + + + Reject contact request + Отклонить запрос + No comment provided by engineer. + + + Reply + Ответить + No comment provided by engineer. + + + SMP servers + SMP серверы + No comment provided by engineer. + + + Save + Сохранить + No comment provided by engineer. + + + Save (and notify contacts) + Сохранить (и уведомить контакты) + No comment provided by engineer. + + + Saved SMP servers will be removed + Сохраненные SMP серверы будут удалены + No comment provided by engineer. + + + Scan QR code + Сканировать QR код + No comment provided by engineer. + + + Server connected + Установлено соединение с сервером + No comment provided by engineer. + + + Settings + Настройки + No comment provided by engineer. + + + Share + Поделиться + No comment provided by engineer. + + + Share invitation link + Поделиться ссылкой + No comment provided by engineer. + + + Share link + Поделиться ссылкой + No comment provided by engineer. + + + Show QR code to your contact +to scan from the app + Покажите QR код вашему контакту для сканирования в приложении + No comment provided by engineer. + + + Start new chat + Начать новый разговор + No comment provided by engineer. + + + Take picture + Сделать фото + No comment provided by engineer. + + + Tap button + Нажмите кнопку + No comment provided by engineer. + + + Thank you for installing SimpleX Chat! + Спасибо, что Вы установили SimpleX Chat! + No comment provided by engineer. + + + The app can notify you when you receive messages or contact requests - please open settings to enable. + Приложение может посылать вам уведомления о сообщениях и запросах на соединение - уведомления можно включить в Настройках. + No comment provided by engineer. + + + The messaging and application platform 100% private by design! + Платформа для сообщений и приложений, которая защищает вашу личную информацию и безопасность. + No comment provided by engineer. + + + The sender will NOT be notified + Отправитель не будет уведомлён + No comment provided by engineer. + + + To ask any questions and to receive updates: + Задать вопросы и получать уведомления о новых версиях: + No comment provided by engineer. + + + To connect via link + Соединиться через ссылку + No comment provided by engineer. + + + To start a new chat + Начать новый разговор + No comment provided by engineer. + + + Trying to connect to the server used to receive messages from this contact (error: %@). + Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта (ошибка: %@). + No comment provided by engineer. + + + Trying to connect to the server used to receive messages from this contact. + Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта. + No comment provided by engineer. + + + Unexpected error: %@ + Неожиданная ошибка: %@ + No comment provided by engineer. + + + Use SimpleX Chat servers? + Использовать серверы предосталенные SimpleX Chat? + No comment provided by engineer. + + + Using SimpleX Chat servers. + Используются серверы, предоставленные SimpleX Chat. + No comment provided by engineer. + + + Welcome %@! + Здравствуйте %@! + No comment provided by engineer. + + + You + Вы + No comment provided by engineer. + + + You are already connected to %@ via this link. + Вы уже соединены с %@ через эту ссылку. + No comment provided by engineer. + + + You are connected to the server used to receive messages from this contact. + Установлено соединение с сервером, через который вы получается сообщения от этого контакта. + No comment provided by engineer. + + + You can now send messages to %@ + Вы теперь можете отправлять сообщения %@ + notification body + + + You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it. + Вы можете использовать ваш адрес как ссылку или как QR код - кто угодно сможет соединиться с вами. Вы сможете удалить адрес, сохранив контакты, которые через него соединились. + No comment provided by engineer. + + + You can use markdown to format messages: + Вы можете форматировать сообщения: + No comment provided by engineer. + + + You control your chat! + Вы котролируете Ваш чат! + No comment provided by engineer. + + + You will be connected when your connection request is accepted, please wait or check later! + Соединение будет установлено, когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже! + No comment provided by engineer. + + + You will be connected when your contact's device is online, please wait or check later! + Соединение будет установлено, когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже! + No comment provided by engineer. + + + Your SMP servers + Ваши SMP серверы + No comment provided by engineer. + + + Your SimpleX contact address + Ваш SimpleX адрес + No comment provided by engineer. + + + Your chat address + Ваш SimpleX адрес + No comment provided by engineer. + + + Your chat profile + Ваш профиль + No comment provided by engineer. + + + Your chat profile will be sent to your contact + Ваш профиль будет отправлен вашему контакту + No comment provided by engineer. + + + Your chats + Ваши чаты + No comment provided by engineer. + + + Your profile is stored on your device and shared only with your contacts. +SimpleX servers cannot see your profile. + Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам. +SimpleX серверы не могут получить доступ к вашему профилю. + No comment provided by engineer. + + + Your profile will be sent to the contact that you received this link from + Ваш профиль будет отправлен контакту, от которого вы получили эту ссылку. + No comment provided by engineer. + + + Your profile, contacts and messages (once delivered) are only stored locally on your device. + Ваш профиль, контакты и сообщения (после доставки) хранятся только на вашем устройстве. + No comment provided by engineer. + + + Your settings + Настройки + No comment provided by engineer. + + + [Send us email](mailto:chat@simplex.chat) + [Отправить email](mailto:chat@simplex.chat) + No comment provided by engineer. + + + _italic_ + \_курсив_ + No comment provided by engineer. + + + `a + b` + \`a + b` + No comment provided by engineer. + + + above, then: + наверху, затем: + No comment provided by engineer. + + + bold + жирный + No comment provided by engineer. + + + colored + цвет + No comment provided by engineer. + + + connect to SimpleX Chat developers. + соединиться с разработчиками. + No comment provided by engineer. + + + deleted + удалено + deleted chat item + + + italic + курсив + No comment provided by engineer. + + + receiving files is not supported yet + получение файлов не поддерживается + to be removed + + + secret + секрет + No comment provided by engineer. + + + sending files is not supported yet + отправка файлов не поддерживается + to be removed + + + strike + зачеркнуть + No comment provided by engineer. + + + v%@ (%@) + v%@ (%@) + No comment provided by engineer. + + + wants to connect to you! + хочет соединиться с вами! + No comment provided by engineer. + + + ~strike~ + \~зачеркнуть~ + No comment provided by engineer. + + + 💻 desktop: scan displayed QR code from the app, via **Scan QR code**. + 💻 на компьютере: сосканируйте QR код из приложения через **Сканировать QR код**. + No comment provided by engineer. + + + 📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app. + 📱 на мобильном: намжите кнопку **Open in mobile app** на веб странице, затем нажмите **Соединиться** в приложении. + No comment provided by engineer. + + +
+ +
+ +
+ + + SimpleX + SimpleX + Bundle name + + + SimpleX needs camera access to scan QR codes to connect to other app users + SimpleX использует камеру для сканирования QR кодов при соединении с другими пользователями + Privacy - Camera Usage Description + + +
+
diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000..aaa7f79bc8 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,23 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0.000", + "alpha" : "1.000", + "blue" : "1.000", + "green" : "0.533" + } + }, + "idiom" : "universal" + } + ], + "properties" : { + "localizable" : true + }, + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..8a3092dda65330a4b5cb82313b5aa45120322b7e GIT binary patch literal 1678 zcmb`HO-{ow5QS&mQy3&R-Shwm0b8uFM%T;q2pXRQ8&{X6F^UQ|+`ubI6M@)xqX1SgKaWBt_Cl8^0Fh52a3 zYlKWPvZWPm>s9@F?P^|YT4fpu$Ep9;TOP_o{OL?N)Mu=muwuK%d)%)kZ!PwH3=8d) zA&&w+HD4k-c4NE4`_PW` - - - - diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 21fdbe7d73..230ca7890e 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -8,120 +8,65 @@ /* Begin PBXBuildFile section */ 3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; }; - 3CDBCF4327FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; }; 3CDBCF4827FF621E00354CDD /* ChatItemLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* ChatItemLinkView.swift */; }; - 3CDBCF4927FF621E00354CDD /* ChatItemLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* ChatItemLinkView.swift */; }; 5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; }; - 5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; }; 5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; }; - 5C116CDD27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; }; 5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; }; - 5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; }; 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; }; - 5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; }; 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; }; - 5C2E260C27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; }; 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; }; - 5C2E261027A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; }; 5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E261127A30FEA00F70299 /* TerminalView.swift */; }; - 5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E261127A30FEA00F70299 /* TerminalView.swift */; }; 5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFC727B2782E00FB6C6D /* BGManager.swift */; }; - 5C35CFC927B2782E00FB6C6D /* BGManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFC727B2782E00FB6C6D /* BGManager.swift */; }; 5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */; }; - 5C35CFCC27B2E91D00FB6C6D /* NtfManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */; }; 5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */; }; - 5C3A88CF27DF50170060F1C2 /* DetermineWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */; }; 5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88D027DF57800060F1C2 /* FramedItemView.swift */; }; - 5C3A88D227DF57800060F1C2 /* FramedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88D027DF57800060F1C2 /* FramedItemView.swift */; }; 5C411598280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411593280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a */; }; - 5C411599280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411593280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a */; }; 5C41159A280048E90054D6CB /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411594280048E90054D6CB /* libffi.a */; }; - 5C41159B280048E90054D6CB /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411594280048E90054D6CB /* libffi.a */; }; 5C41159C280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411595280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a */; }; - 5C41159D280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411595280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a */; }; 5C41159E280048E90054D6CB /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411596280048E90054D6CB /* libgmpxx.a */; }; - 5C41159F280048E90054D6CB /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411596280048E90054D6CB /* libgmpxx.a */; }; 5C4115A0280048E90054D6CB /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411597280048E90054D6CB /* libgmp.a */; }; - 5C4115A1280048E90054D6CB /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411597280048E90054D6CB /* libgmp.a */; }; 5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; }; - 5C5346A927B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; }; 5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; }; - 5C577F7E27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; }; 5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */; }; - 5C5F2B6E27EBC3FE006A9D5F /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */; }; 5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */; }; - 5C5F2B7127EBC704006A9D5F /* ProfileImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */; }; 5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; }; - 5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; }; 5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A127B65FDB00BE3227 /* CIMetaView.swift */; }; - 5C7505A327B65FDB00BE3227 /* CIMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A127B65FDB00BE3227 /* CIMetaView.swift */; }; 5C7505A527B679EE00BE3227 /* NavLinkPlain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */; }; - 5C7505A627B679EE00BE3227 /* NavLinkPlain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */; }; 5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */; }; - 5C7505A927B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */; }; 5C764E80279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; }; - 5C764E81279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; }; 5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7B279C71D4000C6508 /* libiconv.tbd */; }; 5C764E83279C748B000C6508 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7C279C71DB000C6508 /* libz.tbd */; }; - 5C764E84279C748C000C6508 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7B279C71D4000C6508 /* libiconv.tbd */; }; - 5C764E85279C748C000C6508 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7C279C71DB000C6508 /* libz.tbd */; }; 5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.swift */; }; - 5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.swift */; }; 5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = 5C8F01CC27A6F0D8007D2C8D /* CodeScanner */; }; 5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */; }; - 5C971E1E27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */; }; 5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */; }; - 5C971E2227AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */; }; 5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96A27A56D4D0075386C /* JSON.swift */; }; - 5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96A27A56D4D0075386C /* JSON.swift */; }; 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; }; - 5C9FD96F27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; }; 5CA059DC279559F40002BEB4 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DB279559F40002BEB4 /* Tests_iOS.swift */; }; 5CA059DE279559F40002BEB4 /* Tests_iOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DD279559F40002BEB4 /* Tests_iOSLaunchTests.swift */; }; - 5CA059E8279559F40002BEB4 /* Tests_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059E7279559F40002BEB4 /* Tests_macOS.swift */; }; - 5CA059EA279559F40002BEB4 /* Tests_macOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */; }; 5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C3279559F40002BEB4 /* SimpleXApp.swift */; }; - 5CA059EC279559F40002BEB4 /* SimpleXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C3279559F40002BEB4 /* SimpleXApp.swift */; }; 5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C4279559F40002BEB4 /* ContentView.swift */; }; - 5CA059EE279559F40002BEB4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C4279559F40002BEB4 /* ContentView.swift */; }; 5CA059EF279559F40002BEB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5CA059C5279559F40002BEB4 /* Assets.xcassets */; }; - 5CA059F0279559F40002BEB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5CA059C5279559F40002BEB4 /* Assets.xcassets */; }; 5CA05A4C27974EB60002BEB4 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */; }; - 5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */; }; 5CB924D427A853F100ACCCDD /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D327A853F100ACCCDD /* SettingsButton.swift */; }; - 5CB924D527A853F100ACCCDD /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D327A853F100ACCCDD /* SettingsButton.swift */; }; 5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; }; - 5CB924D827A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; }; 5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; }; - 5CB924E227A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; }; 5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; }; - 5CB924E527A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; }; 5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */; }; - 5CB9250E27A9432000ACCCDD /* ChatListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */; }; 5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; }; - 5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; }; 5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; }; - 5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; }; + 5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; }; + 5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; }; 5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; - 5CCD403527A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; 5CCD403727A5F9A200368C90 /* ConnectContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */; }; - 5CCD403827A5F9A200368C90 /* ConnectContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */; }; 5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; }; - 5CCD403B27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; }; 5CE4407227ADB1D0007B033A /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407127ADB1D0007B033A /* Emoji.swift */; }; - 5CE4407327ADB1D0007B033A /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407127ADB1D0007B033A /* Emoji.swift */; }; 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; }; - 5CE4407A27ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; }; 5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; }; - 5CEACCE427DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; }; 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; }; - 5CEACCEE27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; }; 640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; }; - 640F50E427CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; }; 64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; }; - 64AA1C6A27EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; }; 64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; }; - 64AA1C6D27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -132,13 +77,6 @@ remoteGlobalIDString = 5CA059C9279559F40002BEB4; remoteInfo = "SimpleX (iOS)"; }; - 5CA059E4279559F40002BEB4 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 5CA059BE279559F40002BEB4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 5CA059CF279559F40002BEB4; - remoteInfo = "SimpleX (macOS)"; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -172,7 +110,6 @@ 5C764E7B279C71D4000C6508 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libiconv.tbd; sourceTree = DEVELOPER_DIR; }; 5C764E7C279C71DB000C6508 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; 5C764E7D279C7275000C6508 /* SimpleX (iOS)-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SimpleX (iOS)-Bridging-Header.h"; sourceTree = ""; }; - 5C764E7E279C7275000C6508 /* SimpleX (macOS)-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SimpleX (macOS)-Bridging-Header.h"; sourceTree = ""; }; 5C764E7F279C7276000C6508 /* dummy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = dummy.m; sourceTree = ""; }; 5C764E88279CBCB3000C6508 /* ChatModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatModel.swift; sourceTree = ""; }; 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoView.swift; sourceTree = ""; }; @@ -183,13 +120,10 @@ 5CA059C4279559F40002BEB4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 5CA059C5279559F40002BEB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 5CA059CA279559F40002BEB4 /* SimpleX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleX.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 5CA059D0279559F40002BEB4 /* SimpleX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5CA059D7279559F40002BEB4 /* Tests iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 5CA059DB279559F40002BEB4 /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = ""; }; 5CA059DD279559F40002BEB4 /* Tests_iOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOSLaunchTests.swift; sourceTree = ""; }; 5CA059E3279559F40002BEB4 /* Tests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 5CA059E7279559F40002BEB4 /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = ""; }; - 5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = ""; }; 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; 5CB924D327A853F100ACCCDD /* SettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsButton.swift; sourceTree = ""; }; 5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; @@ -198,6 +132,8 @@ 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListNavLink.swift; sourceTree = ""; }; 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = ""; }; 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = ""; }; + 5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = ""; }; 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectContactView.swift; sourceTree = ""; }; 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupView.swift; sourceTree = ""; }; @@ -226,20 +162,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 5CA059CD279559F40002BEB4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 5C41159F280048E90054D6CB /* libgmpxx.a in Frameworks */, - 5C4115A1280048E90054D6CB /* libgmp.a in Frameworks */, - 5C764E85279C748C000C6508 /* libz.tbd in Frameworks */, - 5C41159D280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a in Frameworks */, - 5C764E84279C748C000C6508 /* libiconv.tbd in Frameworks */, - 5C411599280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a in Frameworks */, - 5C41159B280048E90054D6CB /* libffi.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 5CA059D4279559F40002BEB4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -336,12 +258,12 @@ 5CA059BD279559F40002BEB4 = { isa = PBXGroup; children = ( + 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */, + 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */, 5C422A7C27A9A6FA0097A1E1 /* SimpleX (iOS).entitlements */, 5C764E5C279C70B7000C6508 /* Libraries */, 5CA059C2279559F40002BEB4 /* Shared */, - 5CA059D1279559F40002BEB4 /* macOS */, 5CA059DA279559F40002BEB4 /* Tests iOS */, - 5CA059E6279559F40002BEB4 /* Tests macOS */, 5CA059CB279559F40002BEB4 /* Products */, 5C764E7A279C71D4000C6508 /* Frameworks */, ); @@ -356,7 +278,6 @@ 5C2E260D27A30E2400F70299 /* Views */, 5CA059C5279559F40002BEB4 /* Assets.xcassets */, 5C764E7D279C7275000C6508 /* SimpleX (iOS)-Bridging-Header.h */, - 5C764E7E279C7275000C6508 /* SimpleX (macOS)-Bridging-Header.h */, 5C764E7F279C7276000C6508 /* dummy.m */, ); path = Shared; @@ -366,20 +287,12 @@ isa = PBXGroup; children = ( 5CA059CA279559F40002BEB4 /* SimpleX.app */, - 5CA059D0279559F40002BEB4 /* SimpleX.app */, 5CA059D7279559F40002BEB4 /* Tests iOS.xctest */, 5CA059E3279559F40002BEB4 /* Tests macOS.xctest */, ); name = Products; sourceTree = ""; }; - 5CA059D1279559F40002BEB4 /* macOS */ = { - isa = PBXGroup; - children = ( - ); - path = macOS; - sourceTree = ""; - }; 5CA059DA279559F40002BEB4 /* Tests iOS */ = { isa = PBXGroup; children = ( @@ -389,15 +302,6 @@ path = "Tests iOS"; sourceTree = ""; }; - 5CA059E6279559F40002BEB4 /* Tests macOS */ = { - isa = PBXGroup; - children = ( - 5CA059E7279559F40002BEB4 /* Tests_macOS.swift */, - 5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */, - ); - path = "Tests macOS"; - sourceTree = ""; - }; 5CB924DD27A8622200ACCCDD /* NewChat */ = { isa = PBXGroup; children = ( @@ -480,23 +384,6 @@ productReference = 5CA059CA279559F40002BEB4 /* SimpleX.app */; productType = "com.apple.product-type.application"; }; - 5CA059CF279559F40002BEB4 /* SimpleX (macOS) */ = { - isa = PBXNativeTarget; - buildConfigurationList = 5CA059F6279559F40002BEB4 /* Build configuration list for PBXNativeTarget "SimpleX (macOS)" */; - buildPhases = ( - 5CA059CC279559F40002BEB4 /* Sources */, - 5CA059CD279559F40002BEB4 /* Frameworks */, - 5CA059CE279559F40002BEB4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "SimpleX (macOS)"; - productName = "SimpleX (macOS)"; - productReference = 5CA059D0279559F40002BEB4 /* SimpleX.app */; - productType = "com.apple.product-type.application"; - }; 5CA059D6279559F40002BEB4 /* Tests iOS */ = { isa = PBXNativeTarget; buildConfigurationList = 5CA059F9279559F40002BEB4 /* Build configuration list for PBXNativeTarget "Tests iOS" */; @@ -526,7 +413,6 @@ buildRules = ( ); dependencies = ( - 5CA059E5279559F40002BEB4 /* PBXTargetDependency */, ); name = "Tests macOS"; productName = "Tests macOS"; @@ -548,10 +434,6 @@ CreatedOnToolsVersion = 13.2.1; LastSwiftMigration = 1320; }; - 5CA059CF279559F40002BEB4 = { - CreatedOnToolsVersion = 13.2.1; - LastSwiftMigration = 1320; - }; 5CA059D6279559F40002BEB4 = { CreatedOnToolsVersion = 13.2.1; TestTargetID = 5CA059C9279559F40002BEB4; @@ -569,6 +451,7 @@ knownRegions = ( en, Base, + ru, ); mainGroup = 5CA059BD279559F40002BEB4; packageReferences = ( @@ -579,7 +462,6 @@ projectRoot = ""; targets = ( 5CA059C9279559F40002BEB4 /* SimpleX (iOS) */, - 5CA059CF279559F40002BEB4 /* SimpleX (macOS) */, 5CA059D6279559F40002BEB4 /* Tests iOS */, 5CA059E2279559F40002BEB4 /* Tests macOS */, ); @@ -592,14 +474,8 @@ buildActionMask = 2147483647; files = ( 5CA059EF279559F40002BEB4 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 5CA059CE279559F40002BEB4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 5CA059F0279559F40002BEB4 /* Assets.xcassets in Resources */, + 5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */, + 5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -674,60 +550,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 5CA059CC279559F40002BEB4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */, - 5CB924D827A8563F00ACCCDD /* SettingsView.swift in Sources */, - 5CEACCE427DE9246000BD591 /* ComposeView.swift in Sources */, - 5CB924E227A867BA00ACCCDD /* UserProfile.swift in Sources */, - 5CE4407A27ADB701007B033A /* EmojiItemView.swift in Sources */, - 5C5346A927B59A6A004DF848 /* ChatHelp.swift in Sources */, - 3CDBCF4327FAE51000354CDD /* ComposeLinkView.swift in Sources */, - 3CDBCF4927FF621E00354CDD /* ChatItemLinkView.swift in Sources */, - 5C764E81279C7276000C6508 /* dummy.m in Sources */, - 5C7505A927B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */, - 5C3A88D227DF57800060F1C2 /* FramedItemView.swift in Sources */, - 5CB924E527A8683A00ACCCDD /* UserAddress.swift in Sources */, - 640F50E427CF991C001E05C2 /* SMPServers.swift in Sources */, - 5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */, - 5C35CFCC27B2E91D00FB6C6D /* NtfManager.swift in Sources */, - 5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */, - 5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */, - 5C9FD96F27A5D6ED0075386C /* SendMessageView.swift in Sources */, - 5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */, - 5C116CDD27AABE0400E66D01 /* ContactRequestView.swift in Sources */, - 5CB9250E27A9432000ACCCDD /* ChatListNavLink.swift in Sources */, - 5CA059EE279559F40002BEB4 /* ContentView.swift in Sources */, - 5CCD403527A5F6DF00368C90 /* AddContactView.swift in Sources */, - 5C3A88CF27DF50170060F1C2 /* DetermineWidth.swift in Sources */, - 5C7505A627B679EE00BE3227 /* NavLinkPlain.swift in Sources */, - 5C7505A327B65FDB00BE3227 /* CIMetaView.swift in Sources */, - 5C35CFC927B2782E00FB6C6D /* BGManager.swift in Sources */, - 5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */, - 5C2E261027A30FDC00F70299 /* ChatView.swift in Sources */, - 5C2E260C27A30CFA00F70299 /* ChatListView.swift in Sources */, - 5C971E2227AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */, - 5C5F2B6E27EBC3FE006A9D5F /* ImagePicker.swift in Sources */, - 5C577F7E27C83AA10006112D /* MarkdownHelp.swift in Sources */, - 5CA059EC279559F40002BEB4 /* SimpleXApp.swift in Sources */, - 5CCD403827A5F9A200368C90 /* ConnectContactView.swift in Sources */, - 5CCD403B27A5F9BE00368C90 /* CreateGroupView.swift in Sources */, - 5CEACCEE27DEA495000BD591 /* MsgContentView.swift in Sources */, - 5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */, - 5C971E1E27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */, - 5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */, - 5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */, - 5CB924D527A853F100ACCCDD /* SettingsButton.swift in Sources */, - 5C5F2B7127EBC704006A9D5F /* ProfileImage.swift in Sources */, - 64AA1C6D27F3537400AC7277 /* DeletedItemView.swift in Sources */, - 5CE4407327ADB1D0007B033A /* Emoji.swift in Sources */, - 5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */, - 64AA1C6A27EE10C800AC7277 /* ContextItemView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 5CA059D3279559F40002BEB4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -741,8 +563,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5CA059EA279559F40002BEB4 /* Tests_macOSLaunchTests.swift in Sources */, - 5CA059E8279559F40002BEB4 /* Tests_macOS.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -754,13 +574,27 @@ target = 5CA059C9279559F40002BEB4 /* SimpleX (iOS) */; targetProxy = 5CA059D8279559F40002BEB4 /* PBXContainerItemProxy */; }; - 5CA059E5279559F40002BEB4 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 5CA059CF279559F40002BEB4 /* SimpleX (macOS) */; - targetProxy = 5CA059E4279559F40002BEB4 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 5CC2C0FB2809BF11000C35E3 /* ru */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 5CC2C0FE2809BF11000C35E3 /* ru */, + ); + name = "SimpleX--iOS--InfoPlist.strings"; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 5CA059F1279559F40002BEB4 /* Debug */ = { isa = XCBuildConfiguration; @@ -817,6 +651,7 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; @@ -869,6 +704,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; @@ -953,83 +789,6 @@ }; name = Release; }; - 5CA059F7279559F40002BEB4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = "SimpleX (macOS)Debug.entitlements"; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 5NN7GUYB6T; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "SimpleX--macOS--Info.plist"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Libraries", - "$(PROJECT_DIR)/Libraries/ios", - "$(PROJECT_DIR)/Libraries/sim", - ); - MACOSX_DEPLOYMENT_TARGET = 12.1; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; - PRODUCT_NAME = SimpleX; - SDKROOT = macosx; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = "Shared/SimpleX (macOS)-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 5CA059F8279559F40002BEB4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 5NN7GUYB6T; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "SimpleX--macOS--Info.plist"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Libraries", - "$(PROJECT_DIR)/Libraries/ios", - "$(PROJECT_DIR)/Libraries/sim", - ); - MACOSX_DEPLOYMENT_TARGET = 12.1; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; - PRODUCT_NAME = SimpleX; - SDKROOT = macosx; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = "Shared/SimpleX (macOS)-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; 5CA059FA279559F40002BEB4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1130,15 +889,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 5CA059F6279559F40002BEB4 /* Build configuration list for PBXNativeTarget "SimpleX (macOS)" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5CA059F7279559F40002BEB4 /* Debug */, - 5CA059F8279559F40002BEB4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 5CA059F9279559F40002BEB4 /* Build configuration list for PBXNativeTarget "Tests iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme new file mode 100644 index 0000000000..a90949ee3b --- /dev/null +++ b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX (iOS).xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/ios/Tests macOS/Tests_macOS.swift b/apps/ios/Tests macOS/Tests_macOS.swift deleted file mode 100644 index ee05450dc0..0000000000 --- a/apps/ios/Tests macOS/Tests_macOS.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// Tests_macOS.swift -// Tests macOS -// -// Created by Evgeny Poberezkin on 17/01/2022. -// - -import XCTest - -class Tests_macOS: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/apps/ios/Tests macOS/Tests_macOSLaunchTests.swift b/apps/ios/Tests macOS/Tests_macOSLaunchTests.swift deleted file mode 100644 index 84d51dadbd..0000000000 --- a/apps/ios/Tests macOS/Tests_macOSLaunchTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// Tests_macOSLaunchTests.swift -// Tests macOS -// -// Created by Evgeny Poberezkin on 17/01/2022. -// - -import XCTest - -class Tests_macOSLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} diff --git a/apps/ios/macOS/macOS.entitlements b/apps/ios/macOS/macOS.entitlements deleted file mode 100644 index f2ef3ae026..0000000000 --- a/apps/ios/macOS/macOS.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-only - - - diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings new file mode 100644 index 0000000000..2ceedaa689 --- /dev/null +++ b/apps/ios/ru.lproj/Localizable.strings @@ -0,0 +1,462 @@ +/* No comment provided by engineer. */ +" " = " "; + +/* No comment provided by engineer. */ +" (" = " ("; + +/* No comment provided by engineer. */ +" (can be copied)" = " (можно скопировать)"; + +/* No comment provided by engineer. */ +"_italic_" = "\\_курсив_"; + +/* No comment provided by engineer. */ +", " = ", "; + +/* No comment provided by engineer. */ +": " = ": "; + +/* No comment provided by engineer. */ +": %@" = ": %@"; + +/* No comment provided by engineer. */ +"!1 colored!" = "!1 цвет!"; + +/* No comment provided by engineer. */ +"(shared only with your contacts)" = "(отправляется только вашим контактам)"; + +/* No comment provided by engineer. */ +")" = ")"; + +/* No comment provided by engineer. */ +"[Send us email](mailto:chat@simplex.chat)" = "[Отправить email](mailto:chat@simplex.chat)"; + +/* No comment provided by engineer. */ +"**Add new contact**: to create your one-time QR Code for your contact." = "**Добавить новый контакт**: чтобы создать одноразовый QR код или ссылку для вашего контакта."; + +/* No comment provided by engineer. */ +"**Scan QR code**: to connect to your contact who shows QR code to you." = "**Сканировать QR код**: чтобы соединиться с вашим контактом (который показывает вам QR код)."; + +/* No comment provided by engineer. */ +"*bold*" = "\\*жирный*"; + +/* No comment provided by engineer. */ +"#secret#" = "#секрет#"; + +/* notification title */ +"%@ is connected!" = "Установлено соединение с %@!"; + +/* notification title */ +"%@ wants to connect!" = "%@ хочет соединиться!"; + +/* No comment provided by engineer. */ +"%lld" = "%lld"; + +/* No comment provided by engineer. */ +"%lldk" = "%lldk"; + +/* No comment provided by engineer. */ +"`a + b`" = "\\`a + b`"; + +/* No comment provided by engineer. */ +"~strike~" = "\\~зачеркнуть~"; + +/* No comment provided by engineer. */ +"💻 desktop: scan displayed QR code from the app, via **Scan QR code**." = "💻 на компьютере: сосканируйте QR код из приложения через **Сканировать QR код**."; + +/* No comment provided by engineer. */ +"📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app." = "📱 на мобильном: намжите кнопку **Open in mobile app** на веб странице, затем нажмите **Соединиться** в приложении."; + +/* No comment provided by engineer. */ +"6" = "6"; + +/* No comment provided by engineer. */ +"above, then:" = "наверху, затем:"; + +/* accept contact request via notification */ +"Accept" = "Принять"; + +/* No comment provided by engineer. */ +"Accept contact" = "Принять запрос"; + +/* notification body */ +"Accept contact request from %@?" = "Принять запрос на соединение от %@?"; + +/* No comment provided by engineer. */ +"Add contact" = "Добавить контакт"; + +/* No comment provided by engineer. */ +"All your contacts will remain connected" = "Все контакты, которые соединились через этот адрес, сохранятся."; + +/* No comment provided by engineer. */ +"bold" = "жирный"; + +/* No comment provided by engineer. */ +"Cancel" = "Отменить"; + +/* No comment provided by engineer. */ +"Chat console" = "Консоль"; + +/* No comment provided by engineer. */ +"Chat with the developers" = "Соединиться с разработчиками"; + +/* back button to return to chats list */ +"Chats" = "Назад"; + +/* No comment provided by engineer. */ +"Choose from library" = "Выбрать из библиотеки"; + +/* No comment provided by engineer. */ +"colored" = "цвет"; + +/* No comment provided by engineer. */ +"Configure SMP servers" = "Настройка SMP серверов"; + +/* No comment provided by engineer. */ +"Confirm" = "Подтвердить"; + +/* No comment provided by engineer. */ +"Connect" = "Соединиться"; + +/* No comment provided by engineer. */ +"connect to SimpleX Chat developers." = "соединиться с разработчиками."; + +/* No comment provided by engineer. */ +"Connect via contact link?" = "Соединиться через ссылку-контакт?"; + +/* No comment provided by engineer. */ +"Connect via invitation link?" = "Соединиться через ссылку-приглашение?"; + +/* No comment provided by engineer. */ +"Connecting server…" = "Устанавливается соединение с сервером…"; + +/* No comment provided by engineer. */ +"Connecting server… (error: %@)" = "Устанавливается соединение с сервером… (ошибка: %@)"; + +/* No comment provided by engineer. */ +"Connecting..." = "Устанавливается соединение…"; + +/* No comment provided by engineer. */ +"Connection error" = "Ошибка соединения"; + +/* No comment provided by engineer. */ +"Connection request" = "Запрос на соединение"; + +/* No comment provided by engineer. */ +"Connection request sent!" = "Запрос на соединение отправлен!"; + +/* No comment provided by engineer. */ +"Connection timeout" = "Превышено время соединения"; + +/* No comment provided by engineer. */ +"Contact already exists" = "Существующий контакт"; + +/* No comment provided by engineer. */ +"Contact and all messages will be deleted - this cannot be undone!" = "Контакт и все сообщения будут удалены - это действие нельзя отменить!"; + +/* notification */ +"Contact is connected" = "Соединение с контактом установлено"; + +/* No comment provided by engineer. */ +"Copy" = "Скопировать"; + +/* No comment provided by engineer. */ +"Create" = "Создать"; + +/* No comment provided by engineer. */ +"Create address" = "Создать адрес"; + +/* No comment provided by engineer. */ +"Create group" = "Создать группу"; + +/* No comment provided by engineer. */ +"Create profile" = "Создать профиль"; + +/* No comment provided by engineer. */ +"Delete" = "Удалить"; + +/* No comment provided by engineer. */ +"Delete address" = "Удалить адрес"; + +/* No comment provided by engineer. */ +"Delete address?" = "Удалить адрес?"; + +/* No comment provided by engineer. */ +"Delete contact" = "Удалить контакт"; + +/* No comment provided by engineer. */ +"Delete contact?" = "Удалить контакт?"; + +/* No comment provided by engineer. */ +"Delete for me" = "Удалить для меня"; + +/* No comment provided by engineer. */ +"Delete group" = "Удалить группу"; + +/* No comment provided by engineer. */ +"Delete message?" = "Удалить сообщение?"; + +/* deleted chat item */ +"deleted" = "удалено"; + +/* No comment provided by engineer. */ +"Develop" = "Для разработчиков"; + +/* No comment provided by engineer. */ +"Display name" = "Имя профиля"; + +/* No comment provided by engineer. */ +"Edit" = "Редактировать"; + +/* No comment provided by engineer. */ +"Enter one SMP server per line:" = "Введите SMP серверы, каждый на отдельной строке:"; + +/* No comment provided by engineer. */ +"Error saving SMP servers" = "Ошибка при сохранении SMP серверов"; + +/* No comment provided by engineer. */ +"Error: %@" = "Ошибка: %@"; + +/* No comment provided by engineer. */ +"Error: URL is invalid" = "Ошибка: неверная ссылка"; + +/* No comment provided by engineer. */ +"Full name (optional)" = "Полное имя (не обязательно)"; + +/* No comment provided by engineer. */ +"Group deletion is not supported" = "Удаление групп не поддерживается"; + +/* No comment provided by engineer. */ +"Help" = "Помощь"; + +/* No comment provided by engineer. */ +"How to" = "Информация"; + +/* No comment provided by engineer. */ +"How to use markdown" = "Как форматировать"; + +/* No comment provided by engineer. */ +"How to use SimpleX Chat" = "Как использовать SimpleX Chat"; + +/* No comment provided by engineer. */ +"If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." = "Если вы не можете встретиться лично, вы можете **сосканировать QR код во время видеозвонка**, или ваш контакт может отправить вам ссылку."; + +/* No comment provided by engineer. */ +"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." = "Если вы не можете встретиться лично, вы можете **показать QR код во время видеозвонка** или отправить ссылку через любой другой канал связи."; + +/* No comment provided by engineer. */ +"If you received SimpleX Chat invitation link you can open it in your browser:" = "Если вы получили ссылку с приглашением из SimpleX Chat, вы можете открыть её в браузере:"; + +/* No comment provided by engineer. */ +"Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "[SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat)"; + +/* No comment provided by engineer. */ +"Invalid connection link" = "Ошибка в ссылке контакта"; + +/* No comment provided by engineer. */ +"italic" = "курсив"; + +/* No comment provided by engineer. */ +"Make sure SMP server addresses are in correct format, line separated and are not duplicated." = "Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется."; + +/* No comment provided by engineer. */ +"Markdown in messages" = "Форматирование сообщений"; + +/* No comment provided by engineer. */ +"Message delivery error" = "Ошибка доставки сообщения"; + +/* No comment provided by engineer. */ +"Most likely this contact has deleted the connection with you." = "Скорее всего, этот контакт удалил соединение с вами."; + +/* notification */ +"New contact request" = "Новый запрос на соединение"; + +/* notifications */ +"New message" = "Новое сообщение"; + +/* No comment provided by engineer. */ +"Notifications are disabled!" = "Уведомления выключены"; + +/* No comment provided by engineer. */ +"Open Settings" = "Открыть Настройки"; + +/* No comment provided by engineer. */ +"Please check that you used the correct link or ask your contact to send you another one." = "Пожалуйста, проверьте, что вы использовали правильную ссылку или попросите, чтобы ваш контакт отправил вам другую ссылку."; + +/* No comment provided by engineer. */ +"Please check your network connection and try again." = "Пожалуйста, проверьте ваше соединение с сетью и попробуйте еще раз."; + +/* No comment provided by engineer. */ +"Profile image" = "Аватар"; + +/* No comment provided by engineer. */ +"Read" = "Прочитано"; + +/* to be removed */ +"receiving files is not supported yet" = "получение файлов не поддерживается"; + +/* No comment provided by engineer. */ +"Reject" = "Отклонить"; + +/* No comment provided by engineer. */ +"Reject contact (sender NOT notified)" = "Отклонить (не уведомляя отправителя)"; + +/* No comment provided by engineer. */ +"Reject contact request" = "Отклонить запрос"; + +/* No comment provided by engineer. */ +"Reply" = "Ответить"; + +/* No comment provided by engineer. */ +"Save" = "Сохранить"; + +/* No comment provided by engineer. */ +"Save (and notify contacts)" = "Сохранить (и уведомить контакты)"; + +/* No comment provided by engineer. */ +"Saved SMP servers will be removed" = "Сохраненные SMP серверы будут удалены"; + +/* No comment provided by engineer. */ +"Scan QR code" = "Сканировать QR код"; + +/* No comment provided by engineer. */ +"secret" = "секрет"; + +/* to be removed */ +"sending files is not supported yet" = "отправка файлов не поддерживается"; + +/* No comment provided by engineer. */ +"Server connected" = "Установлено соединение с сервером"; + +/* No comment provided by engineer. */ +"Settings" = "Настройки"; + +/* No comment provided by engineer. */ +"Share" = "Поделиться"; + +/* No comment provided by engineer. */ +"Share invitation link" = "Поделиться ссылкой"; + +/* No comment provided by engineer. */ +"Share link" = "Поделиться ссылкой"; + +/* No comment provided by engineer. */ +"Show QR code to your contact\nto scan from the app" = "Покажите QR код вашему контакту для сканирования в приложении"; + +/* No comment provided by engineer. */ +"SMP servers" = "SMP серверы"; + +/* No comment provided by engineer. */ +"Start new chat" = "Начать новый разговор"; + +/* No comment provided by engineer. */ +"strike" = "зачеркнуть"; + +/* No comment provided by engineer. */ +"Take picture" = "Сделать фото"; + +/* No comment provided by engineer. */ +"Tap button " = "Нажмите кнопку"; + +/* No comment provided by engineer. */ +"Thank you for installing SimpleX Chat!" = "Спасибо, что Вы установили SimpleX Chat!"; + +/* No comment provided by engineer. */ +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Приложение может посылать вам уведомления о сообщениях и запросах на соединение - уведомления можно включить в Настройках."; + +/* No comment provided by engineer. */ +"The messaging and application platform 100% private by design!" = "Платформа для сообщений и приложений, которая защищает вашу личную информацию и безопасность."; + +/* No comment provided by engineer. */ +"The sender will NOT be notified" = "Отправитель не будет уведомлён"; + +/* No comment provided by engineer. */ +"To ask any questions and to receive updates:" = "Задать вопросы и получать уведомления о новых версиях:"; + +/* No comment provided by engineer. */ +"To connect via link" = "Соединиться через ссылку"; + +/* No comment provided by engineer. */ +"To start a new chat" = "Начать новый разговор"; + +/* No comment provided by engineer. */ +"Trying to connect to the server used to receive messages from this contact (error: %@)." = "Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта (ошибка: %@)."; + +/* No comment provided by engineer. */ +"Trying to connect to the server used to receive messages from this contact." = "Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта."; + +/* No comment provided by engineer. */ +"Unexpected error: %@" = "Неожиданная ошибка: %@"; + +/* No comment provided by engineer. */ +"Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?"; + +/* No comment provided by engineer. */ +"Using SimpleX Chat servers." = "Используются серверы, предоставленные SimpleX Chat."; + +/* No comment provided by engineer. */ +"v%@ (%@)" = "v%@ (%@)"; + +/* No comment provided by engineer. */ +"wants to connect to you!" = "хочет соединиться с вами!"; + +/* No comment provided by engineer. */ +"Welcome %@!" = "Здравствуйте %@!"; + +/* No comment provided by engineer. */ +"You" = "Вы"; + +/* No comment provided by engineer. */ +"You are already connected to %@ via this link." = "Вы уже соединены с %@ через эту ссылку."; + +/* No comment provided by engineer. */ +"You are connected to the server used to receive messages from this contact." = "Установлено соединение с сервером, через который вы получается сообщения от этого контакта."; + +/* notification body */ +"You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@"; + +/* No comment provided by engineer. */ +"You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it." = "Вы можете использовать ваш адрес как ссылку или как QR код - кто угодно сможет соединиться с вами. Вы сможете удалить адрес, сохранив контакты, которые через него соединились."; + +/* No comment provided by engineer. */ +"You can use markdown to format messages:" = "Вы можете форматировать сообщения:"; + +/* No comment provided by engineer. */ +"You control your chat!" = "Вы котролируете Ваш чат!"; + +/* No comment provided by engineer. */ +"You will be connected when your connection request is accepted, please wait or check later!" = "Соединение будет установлено, когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!"; + +/* No comment provided by engineer. */ +"You will be connected when your contact's device is online, please wait or check later!" = "Соединение будет установлено, когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже!"; + +/* No comment provided by engineer. */ +"Your chat address" = "Ваш SimpleX адрес"; + +/* No comment provided by engineer. */ +"Your chat profile" = "Ваш профиль"; + +/* No comment provided by engineer. */ +"Your chat profile will be sent to your contact" = "Ваш профиль будет отправлен вашему контакту"; + +/* No comment provided by engineer. */ +"Your chats" = "Ваши чаты"; + +/* No comment provided by engineer. */ +"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам.\nSimpleX серверы не могут получить доступ к вашему профилю."; + +/* No comment provided by engineer. */ +"Your profile will be sent to the contact that you received this link from" = "Ваш профиль будет отправлен контакту, от которого вы получили эту ссылку."; + +/* No comment provided by engineer. */ +"Your profile, contacts and messages (once delivered) are only stored locally on your device." = "Ваш профиль, контакты и сообщения (после доставки) хранятся только на вашем устройстве."; + +/* No comment provided by engineer. */ +"Your settings" = "Настройки"; + +/* No comment provided by engineer. */ +"Your SimpleX contact address" = "Ваш SimpleX адрес"; + +/* No comment provided by engineer. */ +"Your SMP servers" = "Ваши SMP серверы"; + diff --git a/apps/ios/ru.lproj/SimpleX--iOS--InfoPlist.strings b/apps/ios/ru.lproj/SimpleX--iOS--InfoPlist.strings new file mode 100644 index 0000000000..a8fa77d9f6 --- /dev/null +++ b/apps/ios/ru.lproj/SimpleX--iOS--InfoPlist.strings @@ -0,0 +1,6 @@ +/* Bundle name */ +"CFBundleName" = "SimpleX"; + +/* Privacy - Camera Usage Description */ +"NSCameraUsageDescription" = "SimpleX использует камеру для сканирования QR кодов при соединении с другими пользователями"; + From 757ca74482d340f79d9e8834937133dc3413ba6e Mon Sep 17 00:00:00 2001 From: JRoberts <8711996+jr-simplex@users.noreply.github.com> Date: Sat, 16 Apr 2022 13:01:07 +0400 Subject: [PATCH 3/3] terminal: version 1.6.0 (#534) --- package.yaml | 2 +- simplex-chat.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.yaml b/package.yaml index f0917c04aa..835bf19a9f 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: simplex-chat -version: 1.5.0 +version: 1.6.0 #synopsis: #description: homepage: https://github.com/simplex-chat/simplex-chat#readme diff --git a/simplex-chat.cabal b/simplex-chat.cabal index d83866cedf..65c7ea92e2 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 1.5.0 +version: 1.6.0 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat