From ed3fb0b2224af7d6d62e520464499b6c6d91b8c8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:50:10 +0100 Subject: [PATCH 1/6] core: refactor incognito membership (#3079) --- src/Simplex/Chat.hs | 21 +++++++++++---------- src/Simplex/Chat/Store/Groups.hs | 29 +++++++++++++---------------- src/Simplex/Chat/Types.hs | 14 ++++++++++++++ src/Simplex/Chat/View.hs | 25 ++++++++++++------------- 4 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index abefac6d6..2e9844f7e 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -1470,13 +1470,13 @@ processChatCommand = \case -- TODO for large groups: no need to load all members to determine if contact is a member (group, contact) <- withStore $ \db -> (,) <$> getGroup db user groupId <*> getContact db user contactId assertDirectAllowed user MDSnd contact XGrpInv_ - let Group gInfo@GroupInfo {membership} members = group + let Group gInfo members = group Contact {localDisplayName = cName} = contact assertUserGroupRole gInfo $ max GRAdmin memRole -- [incognito] forbid to invite contact to whom user is connected incognito when (contactConnIncognito contact) $ throwChatError CEContactIncognitoCantInvite -- [incognito] forbid to invite contacts if user joined the group using an incognito profile - when (memberIncognito membership) $ throwChatError CEGroupIncognitoCantInvite + when (incognitoMembership gInfo) $ throwChatError CEGroupIncognitoCantInvite let sendInvitation = sendGrpInvitation user contact gInfo case contactMember contact members of Nothing -> do @@ -3103,10 +3103,10 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do groupConnIds <- createAgentConnectionAsync user CFCreateConnGrpInv True SCMInvitation subMode withStore $ \db -> createNewContactMemberAsync db gVar user groupId ct gLinkMemRole groupConnIds (fromJVersionRange peerChatVRange) subMode _ -> pure () - Just (gInfo@GroupInfo {membership}, m@GroupMember {activeConn}) -> + Just (gInfo, m@GroupMember {activeConn}) -> when (maybe False ((== ConnReady) . connStatus) activeConn) $ do notifyMemberConnected gInfo m $ Just ct - let connectedIncognito = contactConnIncognito ct || memberIncognito membership + let connectedIncognito = contactConnIncognito ct || incognitoMembership gInfo when (memberCategory m == GCPreMember) $ probeMatchingContacts ct connectedIncognito SENT msgId -> do sentMsgDeliveryEvent conn msgId @@ -3279,7 +3279,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do Just ct@Contact {activeConn = Connection {connStatus}} -> when (connStatus == ConnReady) $ do notifyMemberConnected gInfo m $ Just ct - let connectedIncognito = contactConnIncognito ct || memberIncognito membership + let connectedIncognito = contactConnIncognito ct || incognitoMembership gInfo when (memberCategory m == GCPreMember) $ probeMatchingContacts ct connectedIncognito MSG msgMeta _msgFlags msgBody -> do cmdId <- createAckCmd conn @@ -3565,8 +3565,8 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do ct <- acceptContactRequestAsync user cReq incognitoProfile toView $ CRAcceptingContactRequest user ct Just groupId -> do - gInfo@GroupInfo {membership = membership@GroupMember {memberProfile}} <- withStore $ \db -> getGroupInfo db user groupId - let profileMode = if memberIncognito membership then Just $ ExistingIncognito memberProfile else Nothing + gInfo <- withStore $ \db -> getGroupInfo db user groupId + let profileMode = ExistingIncognito <$> incognitoMembershipProfile gInfo ct <- acceptContactRequestAsync user cReq profileMode toView $ CRAcceptingGroupJoinRequest user gInfo ct _ -> do @@ -4413,7 +4413,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do toView $ CRJoinedGroupMemberConnecting user gInfo m newMember xGrpMemIntro :: GroupInfo -> GroupMember -> MemberInfo -> m () - xGrpMemIntro gInfo@GroupInfo {membership, chatSettings = ChatSettings {enableNtfs}} m@GroupMember {memberRole, localDisplayName = c} memInfo@(MemberInfo memId _ memberChatVRange _) = do + xGrpMemIntro gInfo@GroupInfo {chatSettings = ChatSettings {enableNtfs}} m@GroupMember {memberRole, localDisplayName = c} memInfo@(MemberInfo memId _ memberChatVRange _) = do case memberCategory m of GCHostMember -> do members <- withStore' $ \db -> getGroupMembers db user gInfo @@ -4429,7 +4429,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do Just mcvr | isCompatibleRange (fromChatVRange mcvr) groupNoDirectVRange -> Just <$> createConn subMode -- pure Nothing | otherwise -> Just <$> createConn subMode - let customUserProfileId = if memberIncognito membership then Just (localProfileId $ memberProfile membership) else Nothing + let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo void $ withStore $ \db -> createIntroReMember db user gInfo m memInfo groupConnIds directConnIds customUserProfileId subMode _ -> messageError "x.grp.mem.intro can be only sent by host member" where @@ -4473,7 +4473,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do -- [async agent commands] no continuation needed, but commands should be asynchronous for stability groupConnIds <- joinAgentConnectionAsync user enableNtfs groupConnReq dm subMode directConnIds <- forM directConnReq $ \dcr -> joinAgentConnectionAsync user enableNtfs dcr dm subMode - let customUserProfileId = if memberIncognito membership then Just (localProfileId $ memberProfile membership) else Nothing + let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo mcvr = maybe chatInitialVRange fromChatVRange memberChatVRange withStore' $ \db -> createIntroToMemberContact db user m toMember mcvr groupConnIds directConnIds customUserProfileId subMode @@ -4598,6 +4598,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do (mCt', m') <- withStore' $ \db -> createMemberContactInvited db user connIds g m mConn subMode createItems mCt' m' joinConn subMode = do + -- TODO send user's profile for this group membership dm <- directMessage XOk joinAgentConnectionAsync user True connReq dm subMode createItems mCt' m' = do diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index ea7f19a6b..61a0cf713 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -420,13 +420,12 @@ deleteGroupConnectionsAndFiles db User {userId} GroupInfo {groupId} members = do DB.execute db "DELETE FROM files WHERE user_id = ? AND group_id = ?" (userId, groupId) deleteGroupItemsAndMembers :: DB.Connection -> User -> GroupInfo -> [GroupMember] -> IO () -deleteGroupItemsAndMembers db user@User {userId} GroupInfo {groupId} members = do +deleteGroupItemsAndMembers db user@User {userId} g@GroupInfo {groupId} members = do DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ?" (userId, groupId) void $ runExceptT cleanupHostGroupLinkConn_ -- to allow repeat connection via the same group link if one was used DB.execute db "DELETE FROM group_members WHERE user_id = ? AND group_id = ?" (userId, groupId) - forM_ members $ \m@GroupMember {memberProfile = LocalProfile {profileId}} -> do - cleanupMemberProfileAndName_ db user m - when (memberIncognito m) $ deleteUnusedIncognitoProfileById_ db user profileId + forM_ members $ cleanupMemberProfileAndName_ db user + forM_ (incognitoMembershipProfile g) $ deleteUnusedIncognitoProfileById_ db user . localProfileId where cleanupHostGroupLinkConn_ = do hostId <- getHostMemberId_ db user groupId @@ -444,11 +443,11 @@ deleteGroupItemsAndMembers db user@User {userId} GroupInfo {groupId} members = d (userId, userId, hostId) deleteGroup :: DB.Connection -> User -> GroupInfo -> IO () -deleteGroup db user@User {userId} GroupInfo {groupId, localDisplayName, membership = membership@GroupMember {memberProfile = LocalProfile {profileId}}} = do +deleteGroup db user@User {userId} g@GroupInfo {groupId, localDisplayName} = do deleteGroupProfile_ db userId groupId DB.execute db "DELETE FROM groups WHERE user_id = ? AND group_id = ?" (userId, groupId) DB.execute db "DELETE FROM display_names WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName) - when (memberIncognito membership) $ deleteUnusedIncognitoProfileById_ db user profileId + forM_ (incognitoMembershipProfile g) $ deleteUnusedIncognitoProfileById_ db user . localProfileId deleteGroupProfile_ :: DB.Connection -> UserId -> GroupId -> IO () deleteGroupProfile_ db userId groupId = @@ -815,12 +814,12 @@ checkGroupMemberHasItems db User {userId} GroupMember {groupMemberId, groupId} = maybeFirstRow fromOnly $ DB.query db "SELECT chat_item_id FROM chat_items WHERE user_id = ? AND group_id = ? AND group_member_id = ? LIMIT 1" (userId, groupId, groupMemberId) deleteGroupMember :: DB.Connection -> User -> GroupMember -> IO () -deleteGroupMember db user@User {userId} m@GroupMember {groupMemberId, groupId, memberProfile = LocalProfile {profileId}} = do +deleteGroupMember db user@User {userId} m@GroupMember {groupMemberId, groupId, memberProfile} = do deleteGroupMemberConnection db user m DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ? AND group_member_id = ?" (userId, groupId, groupMemberId) DB.execute db "DELETE FROM group_members WHERE user_id = ? AND group_member_id = ?" (userId, groupMemberId) cleanupMemberProfileAndName_ db user m - when (memberIncognito m) $ deleteUnusedIncognitoProfileById_ db user profileId + when (memberIncognito m) $ deleteUnusedIncognitoProfileById_ db user $ localProfileId memberProfile cleanupMemberProfileAndName_ :: DB.Connection -> User -> GroupMember -> IO () cleanupMemberProfileAndName_ db User {userId} GroupMember {groupMemberId, memberContactId, memberContactProfileId, localDisplayName} = @@ -1398,12 +1397,12 @@ createMemberContact user@User {userId, profile = LocalProfile {preferences}} acId cReq - GroupInfo {membership = membership@GroupMember {memberProfile = membershipProfile}} + gInfo GroupMember {groupMemberId, localDisplayName, memberProfile, memberContactProfileId} Connection {connLevel, peerChatVRange = peerChatVRange@(JVersionRange (VersionRange minV maxV))} subMode = do currentTs <- getCurrentTime - let incognitoProfile = if memberIncognito membership then Just membershipProfile else Nothing + let incognitoProfile = incognitoMembershipProfile gInfo customUserProfileId = localProfileId <$> incognitoProfile userPreferences = fromMaybe emptyChatPrefs $ incognitoProfile >> preferences DB.execute @@ -1464,13 +1463,12 @@ createMemberContactInvited db user@User {userId, profile = LocalProfile {preferences}} connIds - gInfo@GroupInfo {membership = membership@GroupMember {memberProfile = membershipProfile}} + gInfo m@GroupMember {groupMemberId, localDisplayName = memberLDN, memberProfile, memberContactProfileId} mConn subMode = do currentTs <- liftIO getCurrentTime - let incognitoProfile = if memberIncognito membership then Just membershipProfile else Nothing - userPreferences = fromMaybe emptyChatPrefs $ incognitoProfile >> preferences + let userPreferences = fromMaybe emptyChatPrefs $ incognitoMembershipProfile gInfo >> preferences contactId <- createContactUpdateMember currentTs userPreferences ctConn <- createMemberContactConn_ db user connIds gInfo mConn contactId subMode let mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn @@ -1523,13 +1521,12 @@ createMemberContactConn_ db user@User {userId} (cmdId, acId) - GroupInfo {membership = membership@GroupMember {memberProfile = membershipProfile}} + gInfo _memberConn@Connection {connLevel, peerChatVRange = peerChatVRange@(JVersionRange (VersionRange minV maxV))} contactId subMode = do currentTs <- liftIO getCurrentTime - let incognitoProfile = if memberIncognito membership then Just membershipProfile else Nothing - customUserProfileId = localProfileId <$> incognitoProfile + let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo DB.execute db [sql| diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 31a2319c1..0c29be281 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -590,8 +590,13 @@ data GroupMember = GroupMember memberStatus :: GroupMemberStatus, invitedBy :: InvitedBy, localDisplayName :: ContactName, + -- for membership, memberProfile can be either user's profile or incognito profile, based on memberIncognito test. + -- for other members it's whatever profile the local user can see (there is no info about whether it's main or incognito profile for remote users). memberProfile :: LocalProfile, + -- this is the ID of the associated contact (it will be used to send direct messages to the member) memberContactId :: Maybe ContactId, + -- for membership it would always point to user's contact + -- it is used to test for incognito status by comparing with ID in memberProfile memberContactProfileId :: ProfileId, activeConn :: Maybe Connection } @@ -622,6 +627,15 @@ groupMemberId' GroupMember {groupMemberId} = groupMemberId memberIncognito :: GroupMember -> IncognitoEnabled memberIncognito GroupMember {memberProfile, memberContactProfileId} = localProfileId memberProfile /= memberContactProfileId +incognitoMembership :: GroupInfo -> IncognitoEnabled +incognitoMembership GroupInfo {membership} = memberIncognito membership + +-- returns profile when membership is incognito, otherwise Nothing +incognitoMembershipProfile :: GroupInfo -> Maybe LocalProfile +incognitoMembershipProfile GroupInfo {membership = m@GroupMember {memberProfile}} + | memberIncognito m = Just memberProfile + | otherwise = Nothing + memberSecurityCode :: GroupMember -> Maybe SecurityCode memberSecurityCode GroupMember {activeConn} = connectionCode =<< activeConn diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index d7a459c38..b150dbfa9 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -775,21 +775,21 @@ viewDirectMessagesProhibited MDSnd c = ["direct messages to indirect contact " < viewDirectMessagesProhibited MDRcv c = ["received prohibited direct message from indirect contact " <> ttyContact' c <> " (discarded)"] viewUserJoinedGroup :: GroupInfo -> [StyledString] -viewUserJoinedGroup g@GroupInfo {membership = membership@GroupMember {memberProfile}} = - if memberIncognito membership - then [ttyGroup' g <> ": you joined the group incognito as " <> incognitoProfile' (fromLocalProfile memberProfile)] - else [ttyGroup' g <> ": you joined the group"] +viewUserJoinedGroup g = + case incognitoMembershipProfile g of + Just mp -> [ttyGroup' g <> ": you joined the group incognito as " <> incognitoProfile' (fromLocalProfile mp)] + Nothing -> [ttyGroup' g <> ": you joined the group"] viewJoinedGroupMember :: GroupInfo -> GroupMember -> [StyledString] viewJoinedGroupMember g m = [ttyGroup' g <> ": " <> ttyMember m <> " joined the group "] viewReceivedGroupInvitation :: GroupInfo -> Contact -> GroupMemberRole -> [StyledString] -viewReceivedGroupInvitation g@GroupInfo {membership = membership@GroupMember {memberProfile}} c role = +viewReceivedGroupInvitation g c role = ttyFullGroup g <> ": " <> ttyContact' c <> " invites you to join the group as " <> plain (strEncode role) : - if memberIncognito membership - then ["use " <> highlight ("/j " <> groupName' g) <> " to join incognito as " <> incognitoProfile' (fromLocalProfile memberProfile)] - else ["use " <> highlight ("/j " <> groupName' g) <> " to accept"] + case incognitoMembershipProfile g of + Just mp -> ["use " <> highlight ("/j " <> groupName' g) <> " to join incognito as " <> incognitoProfile' (fromLocalProfile mp)] + Nothing -> ["use " <> highlight ("/j " <> groupName' g) <> " to accept"] groupPreserved :: GroupInfo -> [StyledString] groupPreserved g = ["use " <> highlight ("/d #" <> groupName' g) <> " to delete the group"] @@ -879,7 +879,7 @@ viewGroupsList gs = map groupSS $ sortOn (ldn_ . fst) gs memberCount = sShow currentMembers <> " member" <> if currentMembers == 1 then "" else "s" groupInvitation' :: GroupInfo -> StyledString -groupInvitation' GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}, membership = membership@GroupMember {memberProfile}} = +groupInvitation' g@GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}} = highlight ("#" <> ldn) <> optFullName ldn fullName <> " - you are invited (" @@ -888,10 +888,9 @@ groupInvitation' GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile <> highlight ("/d #" <> ldn) <> " to delete invitation)" where - joinText = - if memberIncognito membership - then " to join as " <> incognitoProfile' (fromLocalProfile memberProfile) <> ", " - else " to join, " + joinText = case incognitoMembershipProfile g of + Just mp -> " to join as " <> incognitoProfile' (fromLocalProfile mp) <> ", " + Nothing -> " to join, " viewContactsMerged :: Contact -> Contact -> [StyledString] viewContactsMerged _into@Contact {localDisplayName = c1} _merged@Contact {localDisplayName = c2} = From 1928256b09401caef5967f0a164c7f6fe0ca7e02 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 20 Sep 2023 00:26:03 +0400 Subject: [PATCH 2/6] core: connect existing contacts to new members when profile matches, enable skipping direct connections in groups (#3056) * core: test group members are assigned different LDNs in group when direct connections aren't created * disable test output * core: connect existing contacts to new members when profile matches (#3059) * core: connect existing contacts to new members when profile matches * fix migration * progress * xInfoProbeOk for member * fix tests * add test * fix tests * tests * remove deleteSentProbe * remove deleteContactProfile_ * views * don't check connections in deleteUnusedProfile_ * Revert "don't check connections in deleteUnusedProfile_" This reverts commit 2016a0efded2e66a004ae589e744e41fa89a929a. * fix test * core: update member merge * update saved schema * fix queries and tests * rename tables to original names * remove index, corrections * update schema dump --------- Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- simplex-chat.cabal | 1 + src/Simplex/Chat.hs | 108 ++++-- src/Simplex/Chat/Controller.hs | 1 + .../Migrations/M20230914_member_probes.hs | 169 ++++++++++ src/Simplex/Chat/Migrations/chat_schema.sql | 76 +++-- src/Simplex/Chat/Store/Direct.hs | 29 ++ src/Simplex/Chat/Store/Groups.hs | 252 +++++++++----- src/Simplex/Chat/Store/Migrations.hs | 4 +- src/Simplex/Chat/Types.hs | 19 +- src/Simplex/Chat/View.hs | 3 +- tests/ChatTests.hs | 8 +- tests/ChatTests/Groups.hs | 308 +++++++++++------- tests/SchemaDump.hs | 4 +- 13 files changed, 707 insertions(+), 275 deletions(-) create mode 100644 src/Simplex/Chat/Migrations/M20230914_member_probes.hs diff --git a/simplex-chat.cabal b/simplex-chat.cabal index f1b6eb053..77592f756 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -112,6 +112,7 @@ library Simplex.Chat.Migrations.M20230829_connections_chat_vrange Simplex.Chat.Migrations.M20230903_connections_to_subscribe Simplex.Chat.Migrations.M20230913_member_contacts + Simplex.Chat.Migrations.M20230914_member_probes Simplex.Chat.Mobile Simplex.Chat.Mobile.File Simplex.Chat.Mobile.Shared diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 2e9844f7e..34981fa56 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -3037,7 +3037,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do XFileAcptInv sharedMsgId fileConnReq_ fName -> xFileAcptInv ct' sharedMsgId fileConnReq_ fName msgMeta XInfo p -> xInfo ct' p XGrpInv gInv -> processGroupInvitation ct' gInv msg msgMeta - XInfoProbe probe -> xInfoProbe ct' probe + XInfoProbe probe -> xInfoProbe (CGMContact ct') probe XInfoProbeCheck probeHash -> xInfoProbeCheck ct' probeHash XInfoProbeOk probe -> xInfoProbeOk ct' probe XCallInv callId invitation -> xCallInv ct' callId invitation msg msgMeta @@ -3169,7 +3169,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do groupConnReq@(CRInvitationUri _ _) -> case cmdFunction of -- [async agent commands] XGrpMemIntro continuation on receiving INV CFCreateConnGrpMemInv - | isCompatibleRange (fromJVersionRange $ peerChatVRange conn) groupNoDirectVRange -> sendWithDirectCReq -- sendWithoutDirectCReq + | isCompatibleRange (fromJVersionRange $ peerChatVRange conn) groupNoDirectVRange -> sendWithoutDirectCReq | otherwise -> sendWithDirectCReq where sendWithoutDirectCReq = do @@ -3270,12 +3270,12 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do void $ sendDirectMessage conn (XGrpMemIntro $ memberInfo (reMember intro)) (GroupId groupId) withStore' $ \db -> updateIntroStatus db introId GMIntroSent _ -> do - -- TODO send probe and decide whether to use existing contact connection or the new contact connection -- TODO notify member who forwarded introduction - question - where it is stored? There is via_contact but probably there should be via_member in group_members table withStore' (\db -> getViaGroupContact db user m) >>= \case Nothing -> do notifyMemberConnected gInfo m Nothing - messageWarning "connected member does not have contact" + let connectedIncognito = memberIncognito membership + when (memberCategory m == GCPreMember) $ probeMatchingMemberContact gInfo m connectedIncognito Just ct@Contact {activeConn = Connection {connStatus}} -> when (connStatus == ConnReady) $ do notifyMemberConnected gInfo m $ Just ct @@ -3308,6 +3308,9 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do XGrpDel -> xGrpDel gInfo m' msg msgMeta XGrpInfo p' -> xGrpInfo gInfo m' p' msg msgMeta XGrpDirectInv connReq mContent_ -> canSend m' $ xGrpDirectInv gInfo m' conn' connReq mContent_ msg msgMeta + XInfoProbe probe -> xInfoProbe (CGMGroupMember gInfo m') probe + -- XInfoProbeCheck -- TODO merge members? + -- XInfoProbeOk -- TODO merge members? BFileChunk sharedMsgId chunk -> bFileChunkGroup gInfo sharedMsgId chunk msgMeta _ -> messageError $ "unsupported message: " <> T.pack (show event) currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo @@ -3674,19 +3677,42 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do probeMatchingContacts :: Contact -> IncognitoEnabled -> m () probeMatchingContacts ct connectedIncognito = do gVar <- asks idsDrg - (probe, probeId) <- withStore $ \db -> createSentProbe db gVar userId ct - void . sendDirectContactMessage ct $ XInfoProbe probe if connectedIncognito - then withStore' $ \db -> deleteSentProbe db userId probeId + then sendProbe . Probe =<< liftIO (encodedRandomBytes gVar 32) else do + (probe, probeId) <- withStore $ \db -> createSentProbe db gVar userId (CGMContact ct) + sendProbe probe cs <- withStore' $ \db -> getMatchingContacts db user ct - let probeHash = ProbeHash $ C.sha256Hash (unProbe probe) - forM_ cs $ \c -> sendProbeHash c probeHash probeId `catchChatError` \_ -> pure () + sendProbeHashes cs probe probeId where - sendProbeHash :: Contact -> ProbeHash -> Int64 -> m () - sendProbeHash c probeHash probeId = do + sendProbe :: Probe -> m () + sendProbe probe = void . sendDirectContactMessage ct $ XInfoProbe probe + + probeMatchingMemberContact :: GroupInfo -> GroupMember -> IncognitoEnabled -> m () + probeMatchingMemberContact _ GroupMember {activeConn = Nothing} _ = pure () + probeMatchingMemberContact g m@GroupMember {groupId, activeConn = Just conn} connectedIncognito = do + gVar <- asks idsDrg + if connectedIncognito + then sendProbe . Probe =<< liftIO (encodedRandomBytes gVar 32) + else do + (probe, probeId) <- withStore $ \db -> createSentProbe db gVar userId $ CGMGroupMember g m + sendProbe probe + cs <- withStore' $ \db -> getMatchingMemberContacts db user m + sendProbeHashes cs probe probeId + where + sendProbe :: Probe -> m () + sendProbe probe = void $ sendDirectMessage conn (XInfoProbe probe) (GroupId groupId) + + -- TODO currently we only send probe hashes to contacts + sendProbeHashes :: [Contact] -> Probe -> Int64 -> m () + sendProbeHashes cs probe probeId = + forM_ cs $ \c -> sendProbeHash c `catchChatError` \_ -> pure () + where + probeHash = ProbeHash $ C.sha256Hash (unProbe probe) + sendProbeHash :: Contact -> m () + sendProbeHash c = do void . sendDirectContactMessage c $ XInfoProbeCheck probeHash - withStore' $ \db -> createSentProbeHash db userId probeId c + withStore' $ \db -> createSentProbeHash db userId probeId $ CGMContact c messageWarning :: Text -> m () messageWarning = toView . CRMessageError user "warning" @@ -4247,35 +4273,48 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do (_, param) = groupFeatureState p createInternalChatItem user (CDGroupRcv g m) (CIRcvGroupFeature (toGroupFeature f) (toGroupPreference p) param) Nothing - xInfoProbe :: Contact -> Probe -> m () - xInfoProbe c2 probe = + xInfoProbe :: ContactOrGroupMember -> Probe -> m () + xInfoProbe cgm2 probe = -- [incognito] unless connected incognito - unless (contactConnIncognito c2) $ do - r <- withStore' $ \db -> matchReceivedProbe db user c2 probe - forM_ r $ \c1 -> probeMatch c1 c2 probe + unless (contactOrGroupMemberIncognito cgm2) $ do + r <- withStore' $ \db -> matchReceivedProbe db user cgm2 probe + forM_ r $ \case + CGMContact c1 -> probeMatch c1 cgm2 probe + CGMGroupMember _ _ -> messageWarning "xInfoProbe ignored: matched member (no probe hashes sent to members)" + -- TODO currently we send probe hashes only to contacts xInfoProbeCheck :: Contact -> ProbeHash -> m () xInfoProbeCheck c1 probeHash = -- [incognito] unless connected incognito unless (contactConnIncognito c1) $ do - r <- withStore' $ \db -> matchReceivedProbeHash db user c1 probeHash + r <- withStore' $ \db -> matchReceivedProbeHash db user (CGMContact c1) probeHash forM_ r . uncurry $ probeMatch c1 - probeMatch :: Contact -> Contact -> Probe -> m () - probeMatch c1@Contact {contactId = cId1, profile = p1} c2@Contact {contactId = cId2, profile = p2} probe = - if profilesMatch (fromLocalProfile p1) (fromLocalProfile p2) && cId1 /= cId2 - then do - void . sendDirectContactMessage c1 $ XInfoProbeOk probe - mergeContacts c1 c2 - else messageWarning "probeMatch ignored: profiles don't match or same contact id" + probeMatch :: Contact -> ContactOrGroupMember -> Probe -> m () + probeMatch c1@Contact {contactId = cId1, profile = p1} cgm2 probe = + case cgm2 of + CGMContact c2@Contact {contactId = cId2, profile = p2} + | cId1 /= cId2 && profilesMatch p1 p2 -> do + void . sendDirectContactMessage c1 $ XInfoProbeOk probe + mergeContacts c1 c2 + | otherwise -> messageWarning "probeMatch ignored: profiles don't match or same contact id" + CGMGroupMember g m2@GroupMember {memberProfile = p2, memberContactId} + | isNothing memberContactId && profilesMatch p1 p2 -> do + void . sendDirectContactMessage c1 $ XInfoProbeOk probe + connectContactToMember c1 g m2 + | otherwise -> messageWarning "probeMatch ignored: profiles don't match or member already has contact" + -- TODO currently we send probe hashes only to contacts xInfoProbeOk :: Contact -> Probe -> m () - xInfoProbeOk c1@Contact {contactId = cId1} probe = do - r <- withStore' $ \db -> matchSentProbe db user c1 probe - forM_ r $ \c2@Contact {contactId = cId2} -> - if cId1 /= cId2 - then mergeContacts c1 c2 - else messageWarning "xInfoProbeOk ignored: same contact id" + xInfoProbeOk c1@Contact {contactId = cId1} probe = + withStore' (\db -> matchSentProbe db user (CGMContact c1) probe) >>= \case + Just (CGMContact c2@Contact {contactId = cId2}) + | cId1 /= cId2 -> mergeContacts c1 c2 + | otherwise -> messageWarning "xInfoProbeOk ignored: same contact id" + Just (CGMGroupMember g m2@GroupMember {memberContactId}) + | isNothing memberContactId -> connectContactToMember c1 g m2 + | otherwise -> messageWarning "xInfoProbeOk ignored: member already has contact" + _ -> pure () -- to party accepting call xCallInv :: Contact -> CallId -> CallInvitation -> RcvMessage -> MsgMeta -> m () @@ -4387,6 +4426,11 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do withStore' $ \db -> mergeContactRecords db userId c1 c2 toView $ CRContactsMerged user c1 c2 + connectContactToMember :: Contact -> GroupInfo -> GroupMember -> m () + connectContactToMember c1 g m2 = do + withStore' $ \db -> updateMemberContact db user c1 m2 + toView $ CRMemberContactConnected user c1 g m2 + saveConnInfo :: Connection -> ConnInfo -> m Connection saveConnInfo activeConn connInfo = do ChatMessage {chatVRange, chatMsgEvent} <- parseChatMessage activeConn connInfo @@ -4427,7 +4471,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do directConnIds <- case memberChatVRange of Nothing -> Just <$> createConn subMode Just mcvr - | isCompatibleRange (fromChatVRange mcvr) groupNoDirectVRange -> Just <$> createConn subMode -- pure Nothing + | isCompatibleRange (fromChatVRange mcvr) groupNoDirectVRange -> pure Nothing | otherwise -> Just <$> createConn subMode let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo void $ withStore $ \db -> createIntroReMember db user gInfo m memInfo groupConnIds directConnIds customUserProfileId subMode diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 7b6212650..2c829e4a9 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -560,6 +560,7 @@ data ChatResponse | CRNewMemberContact {user :: User, contact :: Contact, groupInfo :: GroupInfo, member :: GroupMember} | CRNewMemberContactSentInv {user :: User, contact :: Contact, groupInfo :: GroupInfo, member :: GroupMember} | CRNewMemberContactReceivedInv {user :: User, contact :: Contact, groupInfo :: GroupInfo, member :: GroupMember} + | CRMemberContactConnected {user :: User, contact :: Contact, groupInfo :: GroupInfo, member :: GroupMember} | CRMemberSubError {user :: User, groupInfo :: GroupInfo, member :: GroupMember, chatError :: ChatError} | CRMemberSubSummary {user :: User, memberSubscriptions :: [MemberSubStatus]} | CRGroupSubscribed {user :: User, groupInfo :: GroupInfo} diff --git a/src/Simplex/Chat/Migrations/M20230914_member_probes.hs b/src/Simplex/Chat/Migrations/M20230914_member_probes.hs new file mode 100644 index 000000000..8772b6cda --- /dev/null +++ b/src/Simplex/Chat/Migrations/M20230914_member_probes.hs @@ -0,0 +1,169 @@ +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Migrations.M20230914_member_probes where + +import Database.SQLite.Simple (Query) +import Database.SQLite.Simple.QQ (sql) + +m20230914_member_probes :: Query +m20230914_member_probes = + [sql| +CREATE TABLE new__sent_probes( + sent_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER REFERENCES contacts ON DELETE CASCADE, + group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE, + probe BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL), + UNIQUE(user_id, probe) +); + +CREATE TABLE new__sent_probe_hashes( + sent_probe_hash_id INTEGER PRIMARY KEY, + sent_probe_id INTEGER NOT NULL REFERENCES new__sent_probes ON DELETE CASCADE, + contact_id INTEGER REFERENCES contacts ON DELETE CASCADE, + group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL) +); + +CREATE TABLE new__received_probes( + received_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER REFERENCES contacts ON DELETE CASCADE, + group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE, + probe BLOB, + probe_hash BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL) +); + +INSERT INTO new__sent_probes + (sent_probe_id, contact_id, probe, user_id, created_at, updated_at) +SELECT + sent_probe_id, contact_id, probe, user_id, created_at, updated_at + FROM sent_probes; + +INSERT INTO new__sent_probe_hashes + (sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at) +SELECT + sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at + FROM sent_probe_hashes; + +INSERT INTO new__received_probes + (received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at) +SELECT + received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at + FROM received_probes; + +DROP INDEX idx_sent_probe_hashes_user_id; +DROP INDEX idx_sent_probe_hashes_contact_id; +DROP INDEX idx_received_probes_user_id; +DROP INDEX idx_received_probes_contact_id; + +DROP TABLE sent_probes; +DROP TABLE sent_probe_hashes; +DROP TABLE received_probes; + +ALTER TABLE new__sent_probes RENAME TO sent_probes; +ALTER TABLE new__sent_probe_hashes RENAME TO sent_probe_hashes; +ALTER TABLE new__received_probes RENAME TO received_probes; + +CREATE INDEX idx_sent_probes_user_id ON sent_probes(user_id); +CREATE INDEX idx_sent_probes_contact_id ON sent_probes(contact_id); +CREATE INDEX idx_sent_probes_group_member_id ON sent_probes(group_member_id); + +CREATE INDEX idx_sent_probe_hashes_user_id ON sent_probe_hashes(user_id); +CREATE INDEX idx_sent_probe_hashes_sent_probe_id ON sent_probe_hashes(sent_probe_id); +CREATE INDEX idx_sent_probe_hashes_contact_id ON sent_probe_hashes(contact_id); +CREATE INDEX idx_sent_probe_hashes_group_member_id ON sent_probe_hashes(group_member_id); + +CREATE INDEX idx_received_probes_user_id ON received_probes(user_id); +CREATE INDEX idx_received_probes_contact_id ON received_probes(contact_id); +CREATE INDEX idx_received_probes_probe ON received_probes(probe); +CREATE INDEX idx_received_probes_probe_hash ON received_probes(probe_hash); +|] + +down_m20230914_member_probes :: Query +down_m20230914_member_probes = + [sql| +CREATE TABLE old__sent_probes( + sent_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE, + probe BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL), + UNIQUE(user_id, probe) +); + +CREATE TABLE old__sent_probe_hashes( + sent_probe_hash_id INTEGER PRIMARY KEY, + sent_probe_id INTEGER NOT NULL REFERENCES old__sent_probes ON DELETE CASCADE, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL) +); + +CREATE TABLE old__received_probes( + received_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE, + probe BLOB, + probe_hash BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL) +); + +DELETE FROM sent_probes WHERE contact_id IS NULL; +DELETE FROM sent_probe_hashes WHERE contact_id IS NULL; +DELETE FROM received_probes WHERE contact_id IS NULL; + +INSERT INTO old__sent_probes + (sent_probe_id, contact_id, probe, user_id, created_at, updated_at) +SELECT + sent_probe_id, contact_id, probe, user_id, created_at, updated_at + FROM sent_probes; + +INSERT INTO old__sent_probe_hashes + (sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at) +SELECT + sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at + FROM sent_probe_hashes; + +INSERT INTO old__received_probes + (received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at) +SELECT + received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at + FROM received_probes; + +DROP INDEX idx_sent_probes_user_id; +DROP INDEX idx_sent_probes_contact_id; +DROP INDEX idx_sent_probes_group_member_id; + +DROP INDEX idx_sent_probe_hashes_user_id; +DROP INDEX idx_sent_probe_hashes_sent_probe_id; +DROP INDEX idx_sent_probe_hashes_contact_id; +DROP INDEX idx_sent_probe_hashes_group_member_id; + +DROP INDEX idx_received_probes_user_id; +DROP INDEX idx_received_probes_contact_id; +DROP INDEX idx_received_probes_probe; +DROP INDEX idx_received_probes_probe_hash; + +DROP TABLE sent_probes; +DROP TABLE sent_probe_hashes; +DROP TABLE received_probes; + +ALTER TABLE old__sent_probes RENAME TO sent_probes; +ALTER TABLE old__sent_probe_hashes RENAME TO sent_probe_hashes; +ALTER TABLE old__received_probes RENAME TO received_probes; + +CREATE INDEX idx_received_probes_user_id ON received_probes(user_id); +CREATE INDEX idx_received_probes_contact_id ON received_probes(contact_id); +CREATE INDEX idx_sent_probe_hashes_user_id ON sent_probe_hashes(user_id); +CREATE INDEX idx_sent_probe_hashes_contact_id ON sent_probe_hashes(contact_id); +|] diff --git a/src/Simplex/Chat/Migrations/chat_schema.sql b/src/Simplex/Chat/Migrations/chat_schema.sql index 4cc351aff..141247e59 100644 --- a/src/Simplex/Chat/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Migrations/chat_schema.sql @@ -78,34 +78,6 @@ CREATE TABLE contacts( UNIQUE(user_id, local_display_name), UNIQUE(user_id, contact_profile_id) ); -CREATE TABLE sent_probes( - sent_probe_id INTEGER PRIMARY KEY, - contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE, - probe BLOB NOT NULL, - user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, - created_at TEXT CHECK(created_at NOT NULL), - updated_at TEXT CHECK(updated_at NOT NULL), - UNIQUE(user_id, probe) -); -CREATE TABLE sent_probe_hashes( - sent_probe_hash_id INTEGER PRIMARY KEY, - sent_probe_id INTEGER NOT NULL REFERENCES sent_probes ON DELETE CASCADE, - contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE, - user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, - created_at TEXT CHECK(created_at NOT NULL), - updated_at TEXT CHECK(updated_at NOT NULL), - UNIQUE(sent_probe_id, contact_id) -); -CREATE TABLE received_probes( - received_probe_id INTEGER PRIMARY KEY, - contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE, - probe BLOB, - probe_hash BLOB NOT NULL, - user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE - , - created_at TEXT CHECK(created_at NOT NULL), - updated_at TEXT CHECK(updated_at NOT NULL) -); CREATE TABLE known_servers( server_id INTEGER PRIMARY KEY, host TEXT NOT NULL, @@ -514,6 +486,35 @@ CREATE TABLE group_snd_item_statuses( created_at TEXT NOT NULL DEFAULT(datetime('now')), updated_at TEXT NOT NULL DEFAULT(datetime('now')) ); +CREATE TABLE IF NOT EXISTS "sent_probes"( + sent_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER REFERENCES contacts ON DELETE CASCADE, + group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE, + probe BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL), + UNIQUE(user_id, probe) +); +CREATE TABLE IF NOT EXISTS "sent_probe_hashes"( + sent_probe_hash_id INTEGER PRIMARY KEY, + sent_probe_id INTEGER NOT NULL REFERENCES "sent_probes" ON DELETE CASCADE, + contact_id INTEGER REFERENCES contacts ON DELETE CASCADE, + group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL) +); +CREATE TABLE IF NOT EXISTS "received_probes"( + received_probe_id INTEGER PRIMARY KEY, + contact_id INTEGER REFERENCES contacts ON DELETE CASCADE, + group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE, + probe BLOB, + probe_hash BLOB NOT NULL, + user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, + created_at TEXT CHECK(created_at NOT NULL), + updated_at TEXT CHECK(updated_at NOT NULL) +); CREATE INDEX contact_profiles_index ON contact_profiles( display_name, full_name @@ -627,10 +628,6 @@ CREATE INDEX idx_pending_group_messages_group_member_id ON pending_group_message ); CREATE INDEX idx_rcv_file_chunks_file_id ON rcv_file_chunks(file_id); CREATE INDEX idx_rcv_files_group_member_id ON rcv_files(group_member_id); -CREATE INDEX idx_received_probes_user_id ON received_probes(user_id); -CREATE INDEX idx_received_probes_contact_id ON received_probes(contact_id); -CREATE INDEX idx_sent_probe_hashes_user_id ON sent_probe_hashes(user_id); -CREATE INDEX idx_sent_probe_hashes_contact_id ON sent_probe_hashes(contact_id); CREATE INDEX idx_settings_user_id ON settings(user_id); CREATE INDEX idx_snd_file_chunks_file_id_connection_id ON snd_file_chunks( file_id, @@ -719,3 +716,18 @@ CREATE INDEX idx_connections_to_subscribe ON connections(to_subscribe); CREATE INDEX idx_contacts_contact_group_member_id ON contacts( contact_group_member_id ); +CREATE INDEX idx_sent_probes_user_id ON sent_probes(user_id); +CREATE INDEX idx_sent_probes_contact_id ON sent_probes(contact_id); +CREATE INDEX idx_sent_probes_group_member_id ON sent_probes(group_member_id); +CREATE INDEX idx_sent_probe_hashes_user_id ON sent_probe_hashes(user_id); +CREATE INDEX idx_sent_probe_hashes_sent_probe_id ON sent_probe_hashes( + sent_probe_id +); +CREATE INDEX idx_sent_probe_hashes_contact_id ON sent_probe_hashes(contact_id); +CREATE INDEX idx_sent_probe_hashes_group_member_id ON sent_probe_hashes( + group_member_id +); +CREATE INDEX idx_received_probes_user_id ON received_probes(user_id); +CREATE INDEX idx_received_probes_contact_id ON received_probes(contact_id); +CREATE INDEX idx_received_probes_probe ON received_probes(probe); +CREATE INDEX idx_received_probes_probe_hash ON received_probes(probe_hash); diff --git a/src/Simplex/Chat/Store/Direct.hs b/src/Simplex/Chat/Store/Direct.hs index 2134981fb..7e8cee0e7 100644 --- a/src/Simplex/Chat/Store/Direct.hs +++ b/src/Simplex/Chat/Store/Direct.hs @@ -15,6 +15,7 @@ module Simplex.Chat.Store.Direct updateContactProfile_, updateContactProfile_', deleteContactProfile_, + deleteUnusedProfile_, -- * Contacts and connections functions getPendingContactConnection, @@ -272,6 +273,34 @@ deleteContactProfile_ db userId contactId = |] (userId, contactId) +deleteUnusedProfile_ :: DB.Connection -> UserId -> ProfileId -> IO () +deleteUnusedProfile_ db userId profileId = + DB.executeNamed + db + [sql| + DELETE FROM contact_profiles + WHERE user_id = :user_id AND contact_profile_id = :profile_id + AND 1 NOT IN ( + SELECT 1 FROM connections + WHERE user_id = :user_id AND custom_user_profile_id = :profile_id LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM contacts + WHERE user_id = :user_id AND contact_profile_id = :profile_id LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM contact_requests + WHERE user_id = :user_id AND contact_profile_id = :profile_id LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM group_members + WHERE user_id = :user_id + AND (member_profile_id = :profile_id OR contact_profile_id = :profile_id) + LIMIT 1 + ) + |] + [":user_id" := userId, ":profile_id" := profileId] + updateContactProfile :: DB.Connection -> User -> Contact -> Profile -> ExceptT StoreError IO Contact updateContactProfile db user@User {userId} c p' | displayName == newName = do diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index 61a0cf713..6656208ab 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -77,13 +77,14 @@ module Simplex.Chat.Store.Groups getViaGroupMember, getViaGroupContact, getMatchingContacts, + getMatchingMemberContacts, createSentProbe, createSentProbeHash, - deleteSentProbe, matchReceivedProbe, matchReceivedProbeHash, matchSentProbe, mergeContactRecords, + updateMemberContact, updateGroupSettings, getXGrpMemIntroContDirect, getXGrpMemIntroContGroup, @@ -120,7 +121,7 @@ import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Protocol (SubscriptionMode (..)) -import Simplex.Messaging.Util (eitherToMaybe) +import Simplex.Messaging.Util (eitherToMaybe, ($>>=), (<$$>)) import Simplex.Messaging.Version import UnliftIO.STM @@ -1158,109 +1159,136 @@ getActiveMembersByName db user@User {userId} groupMemberName = do getMatchingContacts :: DB.Connection -> User -> Contact -> IO [Contact] getMatchingContacts db user@User {userId} Contact {contactId, profile = LocalProfile {displayName, fullName, image}} = do contactIds <- - map fromOnly - <$> DB.query - db - [sql| - SELECT ct.contact_id - FROM contacts ct - JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id - WHERE ct.user_id = ? AND ct.contact_id != ? - AND ct.deleted = 0 - AND p.display_name = ? AND p.full_name = ? - AND ((p.image IS NULL AND ? IS NULL) OR p.image = ?) - |] - (userId, contactId, displayName, fullName, image, image) + map fromOnly <$> case image of + Just img -> DB.query db (q <> " AND p.image = ?") (userId, contactId, displayName, fullName, img) + Nothing -> DB.query db (q <> " AND p.image is NULL") (userId, contactId, displayName, fullName) rights <$> mapM (runExceptT . getContact db user) contactIds + where + -- this query is different from one in getMatchingMemberContacts + -- it checks that it's not the same contact + q = + [sql| + SELECT ct.contact_id + FROM contacts ct + JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id + WHERE ct.user_id = ? AND ct.contact_id != ? + AND ct.deleted = 0 + AND p.display_name = ? AND p.full_name = ? + |] -createSentProbe :: DB.Connection -> TVar ChaChaDRG -> UserId -> Contact -> ExceptT StoreError IO (Probe, Int64) -createSentProbe db gVar userId _to@Contact {contactId} = +getMatchingMemberContacts :: DB.Connection -> User -> GroupMember -> IO [Contact] +getMatchingMemberContacts _ _ GroupMember {memberContactId = Just _} = pure [] +getMatchingMemberContacts db user@User {userId} GroupMember {memberProfile = LocalProfile {displayName, fullName, image}} = do + contactIds <- + map fromOnly <$> case image of + Just img -> DB.query db (q <> " AND p.image = ?") (userId, displayName, fullName, img) + Nothing -> DB.query db (q <> " AND p.image is NULL") (userId, displayName, fullName) + rights <$> mapM (runExceptT . getContact db user) contactIds + where + q = + [sql| + SELECT ct.contact_id + FROM contacts ct + JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id + WHERE ct.user_id = ? + AND ct.deleted = 0 + AND p.display_name = ? AND p.full_name = ? + |] + +createSentProbe :: DB.Connection -> TVar ChaChaDRG -> UserId -> ContactOrGroupMember -> ExceptT StoreError IO (Probe, Int64) +createSentProbe db gVar userId to = createWithRandomBytes 32 gVar $ \probe -> do currentTs <- getCurrentTime + let (ctId, gmId) = contactOrGroupMemberIds to DB.execute db - "INSERT INTO sent_probes (contact_id, probe, user_id, created_at, updated_at) VALUES (?,?,?,?,?)" - (contactId, probe, userId, currentTs, currentTs) - (Probe probe,) <$> insertedRowId db + "INSERT INTO sent_probes (contact_id, group_member_id, probe, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)" + (ctId, gmId, probe, userId, currentTs, currentTs) + (Probe probe,) <$> insertedRowId db -createSentProbeHash :: DB.Connection -> UserId -> Int64 -> Contact -> IO () -createSentProbeHash db userId probeId _to@Contact {contactId} = do +createSentProbeHash :: DB.Connection -> UserId -> Int64 -> ContactOrGroupMember -> IO () +createSentProbeHash db userId probeId to = do currentTs <- getCurrentTime + let (ctId, gmId) = contactOrGroupMemberIds to DB.execute db - "INSERT INTO sent_probe_hashes (sent_probe_id, contact_id, user_id, created_at, updated_at) VALUES (?,?,?,?,?)" - (probeId, contactId, userId, currentTs, currentTs) + "INSERT INTO sent_probe_hashes (sent_probe_id, contact_id, group_member_id, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)" + (probeId, ctId, gmId, userId, currentTs, currentTs) -deleteSentProbe :: DB.Connection -> UserId -> Int64 -> IO () -deleteSentProbe db userId probeId = - DB.execute - db - "DELETE FROM sent_probes WHERE user_id = ? AND sent_probe_id = ?" - (userId, probeId) - -matchReceivedProbe :: DB.Connection -> User -> Contact -> Probe -> IO (Maybe Contact) -matchReceivedProbe db user@User {userId} _from@Contact {contactId} (Probe probe) = do +matchReceivedProbe :: DB.Connection -> User -> ContactOrGroupMember -> Probe -> IO (Maybe ContactOrGroupMember) +matchReceivedProbe db user@User {userId} from (Probe probe) = do let probeHash = C.sha256Hash probe - contactIds <- - map fromOnly - <$> DB.query + cgmIds <- + maybeFirstRow id $ + DB.query db [sql| - SELECT c.contact_id - FROM contacts c - JOIN received_probes r ON r.contact_id = c.contact_id - WHERE c.user_id = ? AND c.deleted = 0 AND r.probe_hash = ? AND r.probe IS NULL + SELECT r.contact_id, g.group_id, r.group_member_id + FROM received_probes r + LEFT JOIN contacts c ON r.contact_id = c.contact_id AND c.deleted = 0 + LEFT JOIN group_members m ON r.group_member_id = m.group_member_id + LEFT JOIN groups g ON g.group_id = m.group_id + WHERE r.user_id = ? AND r.probe_hash = ? AND r.probe IS NULL |] (userId, probeHash) currentTs <- getCurrentTime + let (ctId, gmId) = contactOrGroupMemberIds from DB.execute db - "INSERT INTO received_probes (contact_id, probe, probe_hash, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)" - (contactId, probe, probeHash, userId, currentTs, currentTs) - case contactIds of - [] -> pure Nothing - cId : _ -> eitherToMaybe <$> runExceptT (getContact db user cId) + "INSERT INTO received_probes (contact_id, group_member_id, probe, probe_hash, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?)" + (ctId, gmId, probe, probeHash, userId, currentTs, currentTs) + pure cgmIds $>>= getContactOrGroupMember_ db user -matchReceivedProbeHash :: DB.Connection -> User -> Contact -> ProbeHash -> IO (Maybe (Contact, Probe)) -matchReceivedProbeHash db user@User {userId} _from@Contact {contactId} (ProbeHash probeHash) = do - namesAndProbes <- - DB.query - db - [sql| - SELECT c.contact_id, r.probe - FROM contacts c - JOIN received_probes r ON r.contact_id = c.contact_id - WHERE c.user_id = ? AND c.deleted = 0 AND r.probe_hash = ? AND r.probe IS NOT NULL - |] - (userId, probeHash) - currentTs <- getCurrentTime - DB.execute - db - "INSERT INTO received_probes (contact_id, probe_hash, user_id, created_at, updated_at) VALUES (?,?,?,?,?)" - (contactId, probeHash, userId, currentTs, currentTs) - case namesAndProbes of - [] -> pure Nothing - (cId, probe) : _ -> - either (const Nothing) (Just . (,Probe probe)) - <$> runExceptT (getContact db user cId) - -matchSentProbe :: DB.Connection -> User -> Contact -> Probe -> IO (Maybe Contact) -matchSentProbe db user@User {userId} _from@Contact {contactId} (Probe probe) = do - contactIds <- - map fromOnly - <$> DB.query +matchReceivedProbeHash :: DB.Connection -> User -> ContactOrGroupMember -> ProbeHash -> IO (Maybe (ContactOrGroupMember, Probe)) +matchReceivedProbeHash db user@User {userId} from (ProbeHash probeHash) = do + probeIds <- + maybeFirstRow id $ + DB.query db [sql| - SELECT c.contact_id - FROM contacts c - JOIN sent_probes s ON s.contact_id = c.contact_id - JOIN sent_probe_hashes h ON h.sent_probe_id = s.sent_probe_id - WHERE c.user_id = ? AND c.deleted = 0 AND s.probe = ? AND h.contact_id = ? + SELECT r.probe, r.contact_id, g.group_id, r.group_member_id + FROM received_probes r + LEFT JOIN contacts c ON r.contact_id = c.contact_id AND c.deleted = 0 + LEFT JOIN group_members m ON r.group_member_id = m.group_member_id + LEFT JOIN groups g ON g.group_id = m.group_id + WHERE r.user_id = ? AND r.probe_hash = ? AND r.probe IS NOT NULL |] - (userId, probe, contactId) - case contactIds of - [] -> pure Nothing - cId : _ -> eitherToMaybe <$> runExceptT (getContact db user cId) + (userId, probeHash) + currentTs <- getCurrentTime + let (ctId, gmId) = contactOrGroupMemberIds from + DB.execute + db + "INSERT INTO received_probes (contact_id, group_member_id, probe_hash, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)" + (ctId, gmId, probeHash, userId, currentTs, currentTs) + pure probeIds $>>= \(Only probe :. cgmIds) -> (,Probe probe) <$$> getContactOrGroupMember_ db user cgmIds + +matchSentProbe :: DB.Connection -> User -> ContactOrGroupMember -> Probe -> IO (Maybe ContactOrGroupMember) +matchSentProbe db user@User {userId} _from (Probe probe) = + cgmIds $>>= getContactOrGroupMember_ db user + where + (ctId, gmId) = contactOrGroupMemberIds _from + cgmIds = + maybeFirstRow id $ + DB.query + db + [sql| + SELECT s.contact_id, g.group_id, s.group_member_id + FROM sent_probes s + LEFT JOIN contacts c ON s.contact_id = c.contact_id AND c.deleted = 0 + LEFT JOIN group_members m ON s.group_member_id = m.group_member_id + LEFT JOIN groups g ON g.group_id = m.group_id + JOIN sent_probe_hashes h ON h.sent_probe_id = s.sent_probe_id + WHERE s.user_id = ? AND s.probe = ? + AND (h.contact_id = ? OR h.group_member_id = ?) + |] + (userId, probe, ctId, gmId) + +getContactOrGroupMember_ :: DB.Connection -> User -> (Maybe ContactId, Maybe GroupId, Maybe GroupMemberId) -> IO (Maybe ContactOrGroupMember) +getContactOrGroupMember_ db user ids = + fmap eitherToMaybe . runExceptT $ case ids of + (Just ctId, _, _) -> CGMContact <$> getContact db user ctId + (_, Just gId, Just gmId) -> CGMGroupMember <$> getGroupInfo db user gId <*> getGroupMember db user gId gmId + _ -> throwError $ SEInternalError "" mergeContactRecords :: DB.Connection -> UserId -> Contact -> Contact -> IO () mergeContactRecords db userId ct1 ct2 = do @@ -1308,7 +1336,7 @@ mergeContactRecords db userId ct1 ct2 = do ] deleteContactProfile_ db userId fromContactId DB.execute db "DELETE FROM contacts WHERE contact_id = ? AND user_id = ?" (fromContactId, userId) - DB.execute db "DELETE FROM display_names WHERE local_display_name = ? AND user_id = ?" (localDisplayName, userId) + deleteUnusedDisplayName_ db userId localDisplayName where toFromContacts :: Contact -> Contact -> (Contact, Contact) toFromContacts c1 c2 @@ -1321,6 +1349,64 @@ mergeContactRecords db userId ct1 ct2 = do d2 = directOrUsed c2 ctCreatedAt Contact {createdAt} = createdAt +updateMemberContact :: DB.Connection -> User -> Contact -> GroupMember -> IO () +updateMemberContact + db + User {userId} + Contact {contactId, localDisplayName, profile = LocalProfile {profileId}} + GroupMember {groupId, groupMemberId, localDisplayName = memLDN, memberProfile = LocalProfile {profileId = memProfileId}} = do + -- TODO possibly, we should update profiles and local_display_names of all members linked to the same remote user, + -- once we decide on how we identify it, either based on shared contact_profile_id or on local_display_name + currentTs <- getCurrentTime + DB.execute + db + [sql| + UPDATE group_members + SET contact_id = ?, local_display_name = ?, contact_profile_id = ?, updated_at = ? + WHERE user_id = ? AND group_id = ? AND group_member_id = ? + |] + (contactId, localDisplayName, profileId, currentTs, userId, groupId, groupMemberId) + when (memProfileId /= profileId) $ deleteUnusedProfile_ db userId memProfileId + when (memLDN /= localDisplayName) $ deleteUnusedDisplayName_ db userId memLDN + +deleteUnusedDisplayName_ :: DB.Connection -> UserId -> ContactName -> IO () +deleteUnusedDisplayName_ db userId localDisplayName = + DB.executeNamed + db + [sql| + DELETE FROM display_names + WHERE user_id = :user_id AND local_display_name = :local_display_name + AND 1 NOT IN ( + SELECT 1 FROM users + WHERE local_display_name = :local_display_name LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM contacts + WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM groups + WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM group_members + WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM user_contact_links + WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM contact_requests + WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1 + ) + AND 1 NOT IN ( + SELECT 1 FROM contact_requests + WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1 + ) + |] + [":user_id" := userId, ":local_display_name" := localDisplayName] + updateGroupSettings :: DB.Connection -> User -> Int64 -> ChatSettings -> IO () updateGroupSettings db User {userId} groupId ChatSettings {enableNtfs, sendRcpts, favorite} = DB.execute db "UPDATE groups SET enable_ntfs = ?, send_rcpts = ?, favorite = ? WHERE user_id = ? AND group_id = ?" (enableNtfs, sendRcpts, favorite, userId, groupId) diff --git a/src/Simplex/Chat/Store/Migrations.hs b/src/Simplex/Chat/Store/Migrations.hs index 94b01adab..d8bab817e 100644 --- a/src/Simplex/Chat/Store/Migrations.hs +++ b/src/Simplex/Chat/Store/Migrations.hs @@ -80,6 +80,7 @@ import Simplex.Chat.Migrations.M20230827_file_encryption import Simplex.Chat.Migrations.M20230829_connections_chat_vrange import Simplex.Chat.Migrations.M20230903_connections_to_subscribe import Simplex.Chat.Migrations.M20230913_member_contacts +import Simplex.Chat.Migrations.M20230914_member_probes import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) schemaMigrations :: [(String, Query, Maybe Query)] @@ -159,7 +160,8 @@ schemaMigrations = ("20230827_file_encryption", m20230827_file_encryption, Just down_m20230827_file_encryption), ("20230829_connections_chat_vrange", m20230829_connections_chat_vrange, Just down_m20230829_connections_chat_vrange), ("20230903_connections_to_subscribe", m20230903_connections_to_subscribe, Just down_m20230903_connections_to_subscribe), - ("20230913_member_contacts", m20230913_member_contacts, Just down_m20230913_member_contacts) + ("20230913_member_contacts", m20230913_member_contacts, Just down_m20230913_member_contacts), + ("20230914_member_probes", m20230914_member_probes, Just down_m20230914_member_probes) ] -- | The list of migrations in ascending order by date diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index 0c29be281..ac2c55735 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -218,6 +218,19 @@ data ContactRef = ContactRef instance ToJSON ContactRef where toEncoding = J.genericToEncoding J.defaultOptions +data ContactOrGroupMember = CGMContact Contact | CGMGroupMember GroupInfo GroupMember + deriving (Show) + +contactOrGroupMemberIds :: ContactOrGroupMember -> (Maybe ContactId, Maybe GroupMemberId) +contactOrGroupMemberIds = \case + CGMContact Contact {contactId} -> (Just contactId, Nothing) + CGMGroupMember _ GroupMember {groupMemberId} -> (Nothing, Just groupMemberId) + +contactOrGroupMemberIncognito :: ContactOrGroupMember -> IncognitoEnabled +contactOrGroupMemberIncognito = \case + CGMContact ct -> contactConnIncognito ct + CGMGroupMember _ m -> memberIncognito m + data UserContact = UserContact { userContactLinkId :: Int64, connReqContact :: ConnReqContact, @@ -429,10 +442,10 @@ instance ToJSON Profile where toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True} -- check if profiles match ignoring preferences -profilesMatch :: Profile -> Profile -> Bool +profilesMatch :: LocalProfile -> LocalProfile -> Bool profilesMatch - Profile {displayName = n1, fullName = fn1, image = i1} - Profile {displayName = n2, fullName = fn2, image = i2} = + LocalProfile {displayName = n1, fullName = fn1, image = i1} + LocalProfile {displayName = n2, fullName = fn2, image = i2} = n1 == n2 && fn1 == fn2 && i1 == i2 data IncognitoProfile = NewIncognito Profile | ExistingIncognito LocalProfile diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index b150dbfa9..3607bbda5 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -231,10 +231,11 @@ responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView CRGroupLink u g cReq mRole -> ttyUser u $ groupLink_ "Group link:" g cReq mRole CRGroupLinkDeleted u g -> ttyUser u $ viewGroupLinkDeleted g CRAcceptingGroupJoinRequest _ g c -> [ttyFullContact c <> ": accepting request to join group " <> ttyGroup' g <> "..."] - CRNoMemberContactCreating u g m -> ttyUser u ["member " <> ttyGroup' g <> " " <> ttyMember m <> " does not have associated contact, creating contact"] + CRNoMemberContactCreating u g m -> ttyUser u ["member " <> ttyGroup' g <> " " <> ttyMember m <> " does not have direct connection, creating"] CRNewMemberContact u _ g m -> ttyUser u ["contact for member " <> ttyGroup' g <> " " <> ttyMember m <> " is created"] CRNewMemberContactSentInv u _ct g m -> ttyUser u ["sent invitation to connect directly to member " <> ttyGroup' g <> " " <> ttyMember m] CRNewMemberContactReceivedInv u ct g m -> ttyUser u [ttyGroup' g <> " " <> ttyMember m <> " is creating direct contact " <> ttyContact' ct <> " with you"] + CRMemberContactConnected u ct g m -> ttyUser u ["member " <> ttyGroup' g <> " " <> ttyMember m <> " is merged into " <> ttyContact' ct] CRMemberSubError u g m e -> ttyUser u [ttyGroup' g <> " member " <> ttyMember m <> " error: " <> sShow e] CRMemberSubSummary u summary -> ttyUser u $ viewErrorsSummary (filter (isJust . memberError) summary) " group member errors" CRGroupSubscribed u g -> ttyUser u $ viewGroupSubscribed g diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index ed81853ac..eeb96503e 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -8,7 +8,7 @@ import Test.Hspec chatTests :: SpecWith FilePath chatTests = do - chatDirectTests - chatGroupTests - chatFileTests - chatProfileTests + describe "direct tests" chatDirectTests + describe "group tests" chatGroupTests + describe "file tests" chatFileTests + describe "profile tests" chatProfileTests diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 4f2c61b92..7cdb9a309 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -68,19 +68,12 @@ chatGroupTests = do it "should send delivery receipts in group depending on configuration" testConfigureGroupDeliveryReceipts describe "direct connections in group are not established based on chat protocol version" $ do describe "3 members group" $ do - testNoDirect _0 _0 False -- True - testNoDirect _0 _1 False -- True + testNoDirect _0 _0 True + testNoDirect _0 _1 True testNoDirect _1 _0 False testNoDirect _1 _1 False - describe "4 members group" $ do - testNoDirect4 _0 _0 _0 False False False -- True True True - testNoDirect4 _0 _0 _1 False False False -- True True True - testNoDirect4 _0 _1 _0 False False False -- True True False - testNoDirect4 _0 _1 _1 False False False -- True True False - testNoDirect4 _1 _0 _0 False False False -- False False True - testNoDirect4 _1 _0 _1 False False False -- False False True - testNoDirect4 _1 _1 _0 False False False - testNoDirect4 _1 _1 _1 False False False + it "members have different local display names in different groups" testNoDirectDifferentLDNs + it "member should connect to contact when profile match" testConnectMemberToContact describe "create member contact" $ do it "create contact with group member with invitation message" testMemberContactMessage it "create contact with group member without invitation message" testMemberContactNoMessage @@ -101,17 +94,6 @@ chatGroupTests = do <> (if noConns then " : 2 3" else " : 2 <##> 3") ) $ testNoGroupDirectConns supportedChatVRange vrMem2 vrMem3 noConns - testNoDirect4 vrMem2 vrMem3 vrMem4 noConns23 noConns24 noConns34 = - it - ( "host " <> vRangeStr supportedChatVRange - <> (", 2nd mem " <> vRangeStr vrMem2) - <> (", 3rd mem " <> vRangeStr vrMem3) - <> (", 4th mem " <> vRangeStr vrMem4) - <> (if noConns23 then " : 2 3" else " : 2 <##> 3") - <> (if noConns24 then " : 2 4" else " : 2 <##> 4") - <> (if noConns34 then " : 3 4" else " : 3 <##> 4") - ) - $ testNoGroupDirectConns4Members supportedChatVRange vrMem2 vrMem3 vrMem4 noConns23 noConns24 noConns34 testGroup :: HasCallStack => FilePath -> IO () testGroup = @@ -240,7 +222,7 @@ testGroupShared alice bob cath checkMessages = do alice `send` "@bob hey" alice <### [ "@bob hey", - "member #team bob does not have associated contact, creating contact", + "member #team bob does not have direct connection, creating", "peer chat protocol version range incompatible" ] when checkMessages $ threadDelay 1000000 @@ -657,7 +639,7 @@ testGroupDeleteInvitedContact = alice `send` "@bob hey" alice <### [ WithTime "@bob hey", - "member #team bob does not have associated contact, creating contact", + "member #team bob does not have direct connection, creating", "contact for member #team bob is created", "sent invitation to connect directly to member #team bob" ] @@ -2658,56 +2640,140 @@ testNoGroupDirectConns hostVRange mem2VRange mem3VRange noDirectConns tmp = createGroup3 "team" alice bob cath if noDirectConns then contactsDontExist bob cath - else bob <##> cath + else contactsExist bob cath where contactsDontExist bob cath = do - bob ##> "@cath hi" - bob <## "no contact cath" - cath ##> "@bob hi" - cath <## "no contact bob" + bob ##> "/contacts" + bob <## "alice (Alice)" + cath ##> "/contacts" + cath <## "alice (Alice)" + contactsExist bob cath = do + bob ##> "/contacts" + bob + <### [ "alice (Alice)", + "cath (Catherine)" + ] + cath ##> "/contacts" + cath + <### [ "alice (Alice)", + "bob (Bob)" + ] + bob <##> cath -testNoGroupDirectConns4Members :: HasCallStack => VersionRange -> VersionRange -> VersionRange -> VersionRange -> Bool -> Bool -> Bool -> FilePath -> IO () -testNoGroupDirectConns4Members hostVRange mem2VRange mem3VRange mem4VRange noConns23 noConns24 noConns34 tmp = - withNewTestChatCfg tmp testCfg {chatVRange = hostVRange} "alice" aliceProfile $ \alice -> do - withNewTestChatCfg tmp testCfg {chatVRange = mem2VRange} "bob" bobProfile $ \bob -> do - withNewTestChatCfg tmp testCfg {chatVRange = mem3VRange} "cath" cathProfile $ \cath -> do - withNewTestChatCfg tmp testCfg {chatVRange = mem4VRange} "dan" danProfile $ \dan -> do - createGroup3 "team" alice bob cath - connectUsers alice dan - addMember "team" alice dan GRMember - dan ##> "/j team" - concurrentlyN_ - [ alice <## "#team: dan joined the group", - do - dan <## "#team: you joined the group" - dan - <### [ "#team: member bob (Bob) is connected", - "#team: member cath (Catherine) is connected" - ], - aliceAddedDan bob, - aliceAddedDan cath - ] - if noConns23 - then contactsDontExist bob cath - else bob <##> cath - if noConns24 - then contactsDontExist bob dan - else bob <##> dan - if noConns34 - then contactsDontExist cath dan - else cath <##> dan +testNoDirectDifferentLDNs :: HasCallStack => FilePath -> IO () +testNoDirectDifferentLDNs = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + alice ##> "/g club" + alice <## "group #club is created" + alice <## "to add members use /a club or /create link #club" + addMember "club" alice bob GRAdmin + bob ##> "/j club" + concurrently_ + (alice <## "#club: bob joined the group") + (bob <## "#club: you joined the group") + addMember "club" alice cath GRAdmin + cath ##> "/j club" + concurrentlyN_ + [ alice <## "#club: cath joined the group", + do + cath <## "#club: you joined the group" + cath <## "#club: member bob_1 (Bob) is connected", + do + bob <## "#club: alice added cath_1 (Catherine) to the group (connecting...)" + bob <## "#club: new member cath_1 is connected" + ] + + testGroupLDNs alice bob cath "team" "bob" "cath" + testGroupLDNs alice bob cath "club" "bob_1" "cath_1" + + alice `hasContactProfiles` ["alice", "bob", "cath"] + bob `hasContactProfiles` ["bob", "alice", "cath", "cath"] + cath `hasContactProfiles` ["cath", "alice", "bob", "bob"] where - aliceAddedDan :: HasCallStack => TestCC -> IO () - aliceAddedDan cc = do - cc <## "#team: alice added dan (Daniel) to the group (connecting...)" - cc <## "#team: new member dan is connected" - contactsDontExist cc1 cc2 = do - name1 <- userName cc1 - name2 <- userName cc2 - cc1 ##> ("@" <> name2 <> " hi") - cc1 <## ("no contact " <> name2) - cc2 ##> ("@" <> name1 <> " hi") - cc2 <## ("no contact " <> name1) + testGroupLDNs alice bob cath gName bobLDN cathLDN = do + alice ##> ("/ms " <> gName) + alice + <### [ "alice (Alice): owner, you, created group", + "bob (Bob): admin, invited, connected", + "cath (Catherine): admin, invited, connected" + ] + + bob ##> ("/ms " <> gName) + bob + <### [ "alice (Alice): owner, host, connected", + "bob (Bob): admin, you, connected", + ConsoleString (cathLDN <> " (Catherine): admin, connected") + ] + + cath ##> ("/ms " <> gName) + cath + <### [ "alice (Alice): owner, host, connected", + ConsoleString (bobLDN <> " (Bob): admin, connected"), + "cath (Catherine): admin, you, connected" + ] + + alice #> ("#" <> gName <> " hello") + concurrentlyN_ + [ bob <# ("#" <> gName <> " alice> hello"), + cath <# ("#" <> gName <> " alice> hello") + ] + bob #> ("#" <> gName <> " hi there") + concurrentlyN_ + [ alice <# ("#" <> gName <> " bob> hi there"), + cath <# ("#" <> gName <> " " <> bobLDN <> "> hi there") + ] + cath #> ("#" <> gName <> " hey") + concurrentlyN_ + [ alice <# ("#" <> gName <> " cath> hey"), + bob <# ("#" <> gName <> " " <> cathLDN <> "> hey") + ] + +testConnectMemberToContact :: HasCallStack => FilePath -> IO () +testConnectMemberToContact = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + connectUsers alice bob + connectUsers alice cath + createGroup2 "team" bob cath + bob ##> "/a #team alice" + bob <## "invitation to join the group #team sent to alice" + alice <## "#team: bob invites you to join the group as member" + alice <## "use /j team to accept" + alice ##> "/j team" + concurrentlyN_ + [ do + alice <## "#team: you joined the group" + alice <## "#team: member cath_1 (Catherine) is connected" + alice <## "member #team cath_1 is merged into cath", + do + bob <## "#team: alice joined the group", + do + cath <## "#team: bob added alice_1 (Alice) to the group (connecting...)" + cath <## "#team: new member alice_1 is connected" + cath <## "member #team alice_1 is merged into alice" + ] + alice <##> cath + alice #> "#team hello" + bob <# "#team alice> hello" + cath <# "#team alice> hello" + cath #> "#team hello too" + bob <# "#team cath> hello too" + alice <# "#team cath> hello too" + + alice ##> "/contacts" + alice + <### [ "bob (Bob)", + "cath (Catherine)" + ] + cath ##> "/contacts" + cath + <### [ "alice (Alice)", + "bob (Bob)" + ] + alice `hasContactProfiles` ["alice", "bob", "cath"] + cath `hasContactProfiles` ["cath", "alice", "bob"] testMemberContactMessage :: HasCallStack => FilePath -> IO () testMemberContactMessage = @@ -2715,7 +2781,7 @@ testMemberContactMessage = \alice bob cath -> do createGroup3 "team" alice bob cath - -- TODO here and in following tests there would be no direct contacts initially, after "no direct conns" functionality is uncommented + -- alice and bob delete contacts, connect alice ##> "/d bob" alice <## "bob: contact is deleted" bob ##> "/d alice" @@ -2723,7 +2789,7 @@ testMemberContactMessage = alice ##> "@#team bob hi" alice - <### [ "member #team bob does not have associated contact, creating contact", + <### [ "member #team bob does not have direct connection, creating", "contact for member #team bob is created", "sent invitation to connect directly to member #team bob", WithTime "@bob hi" @@ -2739,29 +2805,44 @@ testMemberContactMessage = bob #$> ("/_get chat #1 count=1", chat, [(0, "started direct connection with you")]) alice <##> bob + -- bob and cath connect + bob ##> "@#team cath hi" + bob + <### [ "member #team cath does not have direct connection, creating", + "contact for member #team cath is created", + "sent invitation to connect directly to member #team cath", + WithTime "@cath hi" + ] + cath + <### [ "#team bob is creating direct contact bob with you", + WithTime "bob> hi" + ] + concurrently_ + (bob <## "cath (Catherine): contact is connected") + (cath <## "bob (Bob): contact is connected") + + cath #$> ("/_get chat #1 count=1", chat, [(0, "started direct connection with you")]) + bob <##> cath + testMemberContactNoMessage :: HasCallStack => FilePath -> IO () testMemberContactNoMessage = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do createGroup3 "team" alice bob cath - alice ##> "/d bob" - alice <## "bob: contact is deleted" - bob ##> "/d alice" - bob <## "alice: contact is deleted" + -- bob and cath connect + bob ##> "/_create member contact #1 3" + bob <## "contact for member #team cath is created" - alice ##> "/_create member contact #1 2" - alice <## "contact for member #team bob is created" - - alice ##> "/_invite member contact @4" -- cath is 3, new bob contact is 4 - alice <## "sent invitation to connect directly to member #team bob" - bob <## "#team alice is creating direct contact alice with you" + bob ##> "/_invite member contact @3" + bob <## "sent invitation to connect directly to member #team cath" + cath <## "#team bob is creating direct contact bob with you" concurrently_ - (alice <## "bob (Bob): contact is connected") - (bob <## "alice (Alice): contact is connected") + (bob <## "cath (Catherine): contact is connected") + (cath <## "bob (Bob): contact is connected") - bob #$> ("/_get chat #1 count=1", chat, [(0, "started direct connection with you")]) - alice <##> bob + cath #$> ("/_get chat #1 count=1", chat, [(0, "started direct connection with you")]) + bob <##> cath testMemberContactProhibitedContactExists :: HasCallStack => FilePath -> IO () testMemberContactProhibitedContactExists = @@ -2782,30 +2863,25 @@ testMemberContactProhibitedRepeatInv = \alice bob cath -> do createGroup3 "team" alice bob cath - alice ##> "/d bob" - alice <## "bob: contact is deleted" - bob ##> "/d alice" - bob <## "alice: contact is deleted" + bob ##> "/_create member contact #1 3" + bob <## "contact for member #team cath is created" - alice ##> "/_create member contact #1 2" - alice <## "contact for member #team bob is created" - - alice ##> "/_invite member contact @4 text hi" -- cath is 3, new bob contact is 4 - alice - <### [ "sent invitation to connect directly to member #team bob", - WithTime "@bob hi" - ] - alice ##> "/_invite member contact @4 text hey" - alice <## "bad chat command: x.grp.direct.inv already sent" + bob ##> "/_invite member contact @3 text hi" bob - <### [ "#team alice is creating direct contact alice with you", - WithTime "alice> hi" + <### [ "sent invitation to connect directly to member #team cath", + WithTime "@cath hi" + ] + bob ##> "/_invite member contact @3 text hey" + bob <## "bad chat command: x.grp.direct.inv already sent" + cath + <### [ "#team bob is creating direct contact bob with you", + WithTime "bob> hi" ] concurrently_ - (alice <## "bob (Bob): contact is connected") - (bob <## "alice (Alice): contact is connected") + (bob <## "cath (Catherine): contact is connected") + (cath <## "bob (Bob): contact is connected") - alice <##> bob + bob <##> cath testMemberContactInvitedConnectionReplaced :: HasCallStack => FilePath -> IO () testMemberContactInvitedConnectionReplaced tmp = do @@ -2819,7 +2895,7 @@ testMemberContactInvitedConnectionReplaced tmp = do alice ##> "@#team bob hi" alice - <### [ "member #team bob does not have associated contact, creating contact", + <### [ "member #team bob does not have direct connection, creating", "contact for member #team bob is created", "sent invitation to connect directly to member #team bob", WithTime "@bob hi" @@ -2836,20 +2912,20 @@ testMemberContactInvitedConnectionReplaced tmp = do bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "received invitation to join group team as admin"), (0, "hi"), (0, "security code changed")] <> chatFeatures) withTestChat tmp "bob" $ \bob -> do - subscriptions bob + subscriptions bob 1 checkConnectionsWork alice bob withTestChat tmp "alice" $ \alice -> do - subscriptions alice + subscriptions alice 2 withTestChat tmp "bob" $ \bob -> do - subscriptions bob + subscriptions bob 1 checkConnectionsWork alice bob withTestChat tmp "cath" $ \cath -> do - subscriptions cath + subscriptions cath 1 -- group messages work alice #> "#team hello" @@ -2865,8 +2941,9 @@ testMemberContactInvitedConnectionReplaced tmp = do (alice <# "#team cath> hey team") (bob <# "#team cath> hey team") where - subscriptions cc = do - cc <## "2 contacts connected (use /cs for the list)" + subscriptions :: TestCC -> Int -> IO () + subscriptions cc n = do + cc <## (show n <> " contacts connected (use /cs for the list)") cc <## "#team: connected to server(s)" checkConnectionsWork alice bob = do alice <##> bob @@ -2924,14 +3001,9 @@ testMemberContactIncognito = cath `hasContactProfiles` ["cath", "alice", T.pack bobIncognito, T.pack cathIncognito] -- bob creates member contact with cath - both share incognito profile - bob ##> ("/d " <> cathIncognito) - bob <## (cathIncognito <> ": contact is deleted") - cath ##> ("/d " <> bobIncognito) - cath <## (bobIncognito <> ": contact is deleted") - bob ##> ("@#team " <> cathIncognito <> " hi") bob - <### [ ConsoleString ("member #team " <> cathIncognito <> " does not have associated contact, creating contact"), + <### [ ConsoleString ("member #team " <> cathIncognito <> " does not have direct connection, creating"), ConsoleString ("contact for member #team " <> cathIncognito <> " is created"), ConsoleString ("sent invitation to connect directly to member #team " <> cathIncognito), WithTime ("i @" <> cathIncognito <> " hi") diff --git a/tests/SchemaDump.hs b/tests/SchemaDump.hs index cd493ab34..f4538e4b3 100644 --- a/tests/SchemaDump.hs +++ b/tests/SchemaDump.hs @@ -69,7 +69,9 @@ skipComparisonForDownMigrations = [ -- on down migration msg_delivery_events table moves down to the end of the file "20230504_recreate_msg_delivery_events_cleanup_messages", -- on down migration idx_chat_items_timed_delete_at index moves down to the end of the file - "20230529_indexes" + "20230529_indexes", + -- table and index definitions move down the file, so fields are re-created as not unique + "20230914_member_probes" ] getSchema :: FilePath -> FilePath -> IO String From 0dab4491e2946cef733a1ead0b5f61d34e850b55 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 20 Sep 2023 12:26:16 +0400 Subject: [PATCH 3/6] ios: create contacts with group members (#3077) * wip * observed * network status * contact ready * add padding * Update apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift * setContactNetworkStatus * update text * different context view * update text, icon * spinner * reload all chats on swipe * Revert "reload all chats on swipe" This reverts commit 22c83723012ab2716dbe9c6476e74b84090bef70. * export localizations --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- apps/ios/Shared/Model/SimpleXAPI.swift | 24 ++ apps/ios/Shared/Views/Chat/ChatInfoView.swift | 42 +-- .../ChatItem/CIMemberCreatedContactView.swift | 79 ++++++ apps/ios/Shared/Views/Chat/ChatItemView.swift | 1 + apps/ios/Shared/Views/Chat/ChatView.swift | 17 ++ .../Chat/ComposeMessage/ComposeView.swift | 23 +- .../ContextInvitingContactMemberView.swift | 32 +++ .../Chat/ComposeMessage/SendMessageView.swift | 23 +- .../Chat/Group/GroupMemberInfoView.swift | 254 ++++++++++-------- .../Views/ChatList/ChatListNavLink.swift | 13 +- .../Views/ChatList/ChatPreviewView.swift | 6 +- .../cs.xcloc/Localized Contents/cs.xliff | 24 ++ .../de.xcloc/Localized Contents/de.xliff | 24 ++ .../en.xcloc/Localized Contents/en.xliff | 30 +++ .../es.xcloc/Localized Contents/es.xliff | 24 ++ .../fi.xcloc/Localized Contents/fi.xliff | 24 ++ .../fr.xcloc/Localized Contents/fr.xliff | 24 ++ .../it.xcloc/Localized Contents/it.xliff | 24 ++ .../ja.xcloc/Localized Contents/ja.xliff | 24 ++ .../nl.xcloc/Localized Contents/nl.xliff | 24 ++ .../pl.xcloc/Localized Contents/pl.xliff | 24 ++ .../ru.xcloc/Localized Contents/ru.xliff | 24 ++ .../th.xcloc/Localized Contents/th.xliff | 24 ++ .../uk.xcloc/Localized Contents/uk.xliff | 24 ++ .../Localized Contents/zh-Hans.xliff | 24 ++ apps/ios/SimpleX.xcodeproj/project.pbxproj | 8 + apps/ios/SimpleXChat/APITypes.swift | 17 ++ apps/ios/SimpleXChat/ChatTypes.swift | 34 ++- 28 files changed, 769 insertions(+), 146 deletions(-) create mode 100644 apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift create mode 100644 apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 7a625bae6..aef8711f3 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -15,6 +15,12 @@ import SimpleXChat private var chatController: chat_ctrl? +// currentChatVersion in core +public let CURRENT_CHAT_VERSION: Int = 2 + +// version range that supports establishing direct connection with a group member (xGrpDirectInvVRange in core) +public let CREATE_MEMBER_CONTACT_VRANGE = VersionRange(minVersion: 2, maxVersion: CURRENT_CHAT_VERSION) + enum TerminalItem: Identifiable { case cmd(Date, ChatCommand) case resp(Date, ChatResponse) @@ -1090,6 +1096,18 @@ func apiGetGroupLink(_ groupId: Int64) throws -> (String, GroupMemberRole)? { } } +func apiCreateMemberContact(_ groupId: Int64, _ groupMemberId: Int64) async throws -> Contact { + let r = await chatSendCmd(.apiCreateMemberContact(groupId: groupId, groupMemberId: groupMemberId)) + if case let .newMemberContact(_, contact, _, _) = r { return contact } + throw r +} + +func apiSendMemberContactInvitation(_ contactId: Int64, _ msg: MsgContent) async throws -> Contact { + let r = await chatSendCmd(.apiSendMemberContactInvitation(contactId: contactId, msg: msg), bgDelay: msgDelay) + if case let .newMemberContactSentInv(_, contact, _, _) = r { return contact } + throw r +} + func apiGetVersion() throws -> CoreVersionInfo { let r = chatSendCmdSync(.showVersion) if case let .versionInfo(info, _, _) = r { return info } @@ -1487,6 +1505,12 @@ func processReceivedMsg(_ res: ChatResponse) async { m.updateGroup(groupInfo) } } + case let .newMemberContactReceivedInv(user, contact, _, _): + if active(user) { + await MainActor.run { + m.updateContact(contact) + } + } case let .rcvFileAccepted(user, aChatItem): // usually rcvFileAccepted is a response, but it's also an event for XFTP files auto-accepted from NSE await chatItemSimpleUpdate(user, aChatItem) case let .rcvFileStart(user, aChatItem): diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index 3b0861feb..e7a597804 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -164,6 +164,7 @@ struct ChatInfoView: View { // synchronizeConnectionButtonForce() // } } + .disabled(!contact.ready) if let contactLink = contact.contactLink { Section { @@ -180,30 +181,33 @@ struct ChatInfoView: View { } } - Section("Servers") { - networkStatusRow() - .onTapGesture { - alert = .networkStatusAlert - } - if let connStats = connectionStats { - Button("Change receiving address") { - alert = .switchAddressAlert - } - .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } - || connStats.ratchetSyncSendProhibited - ) - if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { - Button("Abort changing address") { - alert = .abortSwitchAddressAlert + if contact.ready { + Section("Servers") { + networkStatusRow() + .onTapGesture { + alert = .networkStatusAlert + } + if let connStats = connectionStats { + Button("Change receiving address") { + alert = .switchAddressAlert } .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + !contact.ready + || connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } || connStats.ratchetSyncSendProhibited ) + if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { + Button("Abort changing address") { + alert = .abortSwitchAddressAlert + } + .disabled( + connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + || connStats.ratchetSyncSendProhibited + ) + } + smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }) + smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }) } - smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }) - smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift new file mode 100644 index 000000000..a204d83f1 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift @@ -0,0 +1,79 @@ +// +// CIMemberCreatedContactView.swift +// SimpleX (iOS) +// +// Created by spaced4ndy on 19.09.2023. +// Copyright © 2023 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct CIMemberCreatedContactView: View { + var chatItem: ChatItem + + var body: some View { + HStack(alignment: .bottom, spacing: 0) { + switch chatItem.chatDir { + case let .groupRcv(groupMember): + if let contactId = groupMember.memberContactId { + memberCreatedContactView(openText: "Open") + .onTapGesture { + dismissAllSheets(animated: true) + DispatchQueue.main.async { + ChatModel.shared.chatId = "@\(contactId)" + } + } + } else { + memberCreatedContactView() + } + default: + EmptyView() + } + } + .padding(.leading, 6) + .padding(.bottom, 6) + .textSelection(.disabled) + } + + private func memberCreatedContactView(openText: LocalizedStringKey? = nil) -> some View { + var r = eventText() + if let openText { + r = r + + Text(openText) + .fontWeight(.medium) + .foregroundColor(.accentColor) + + Text(" ") + } + r = r + chatItem.timestampText + .fontWeight(.light) + .foregroundColor(.secondary) + return r.font(.caption) + } + + private func eventText() -> Text { + if let member = chatItem.memberDisplayName { + return Text(member + " " + chatItem.content.text + " ") + .fontWeight(.light) + .foregroundColor(.secondary) + } else { + return Text(chatItem.content.text + " ") + .fontWeight(.light) + .foregroundColor(.secondary) + } + } +} + +struct CIMemberCreatedContactView_Previews: PreviewProvider { + static var previews: some View { + let content = CIContent.rcvGroupEvent(rcvGroupEvent: .memberCreatedContact) + let chatItem = ChatItem( + chatDir: .groupRcv(groupMember: GroupMember.sampleData), + meta: CIMeta.getSample(1, .now, content.text, .rcvRead), + content: content, + quotedItem: nil, + file: nil + ) + CIMemberCreatedContactView(chatItem: chatItem) + } +} diff --git a/apps/ios/Shared/Views/Chat/ChatItemView.swift b/apps/ios/Shared/Views/Chat/ChatItemView.swift index 5d816ac64..5ec61a8c2 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemView.swift @@ -74,6 +74,7 @@ struct ChatItemContentView: View { case let .rcvGroupInvitation(groupInvitation, memberRole): groupInvitationItemView(groupInvitation, memberRole) case let .sndGroupInvitation(groupInvitation, memberRole): groupInvitationItemView(groupInvitation, memberRole) case .rcvGroupEvent(.memberConnected): CIEventView(eventText: membersConnectedItemText) + case .rcvGroupEvent(.memberCreatedContact): CIMemberCreatedContactView(chatItem: chatItem) case .rcvGroupEvent: eventItemView() case .sndGroupEvent: eventItemView() case .rcvConnEvent: eventItemView() diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 2a0cd4f2c..81a063dcf 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -64,6 +64,7 @@ struct ChatView: View { Spacer(minLength: 0) + connectingText() ComposeView( chat: chat, composeState: $composeState, @@ -149,6 +150,7 @@ struct ChatView: View { HStack { if contact.allowsFeature(.calls) { callButton(contact, .audio, imageName: "phone") + .disabled(!contact.ready) } Menu { if contact.allowsFeature(.calls) { @@ -157,9 +159,11 @@ struct ChatView: View { } label: { Label("Video call", systemImage: "video") } + .disabled(!contact.ready) } searchButton() toggleNtfsButton(chat) + .disabled(!contact.ready) } label: { Image(systemName: "ellipsis") } @@ -313,6 +317,19 @@ struct ChatView: View { } .scaleEffect(x: 1, y: -1, anchor: .center) } + + @ViewBuilder private func connectingText() -> some View { + if case let .direct(contact) = chat.chatInfo, + !contact.ready, + !contact.nextSendGrpInv { + Text("connecting…") + .font(.caption) + .foregroundColor(.secondary) + .padding(.top) + } else { + EmptyView() + } + } private func floatingButtons(_ proxy: ScrollViewProxy) -> some View { let counts = chatModel.unreadChatItemCounts(itemsInView: itemsInView) diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index c999c9dca..3328da8db 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -257,6 +257,9 @@ struct ComposeView: View { var body: some View { VStack(spacing: 0) { + if chat.chatInfo.contact?.nextSendGrpInv ?? false { + ContextInvitingContactMemberView() + } contextItemView() switch (composeState.editing, composeState.preview) { case (true, .filePreview): EmptyView() @@ -270,7 +273,7 @@ struct ComposeView: View { Image(systemName: "paperclip") .resizable() } - .disabled(composeState.attachmentDisabled || !chat.userCanSend) + .disabled(composeState.attachmentDisabled || !chat.userCanSend || (chat.chatInfo.contact?.nextSendGrpInv ?? false)) .frame(width: 25, height: 25) .padding(.bottom, 12) .padding(.leading, 12) @@ -298,6 +301,7 @@ struct ComposeView: View { composeState.liveMessage = nil chatModel.removeLiveDummy() }, + nextSendGrpInv: chat.chatInfo.contact?.nextSendGrpInv ?? false, voiceMessageAllowed: chat.chatInfo.featureEnabled(.voice), showEnableVoiceMessagesAlert: chat.chatInfo.showEnableVoiceMessagesAlert, startVoiceMessageRecording: { @@ -617,7 +621,9 @@ struct ComposeView: View { if liveMessage != nil { composeState = composeState.copy(liveMessage: nil) } await sending() } - if case let .editingItem(ci) = composeState.contextItem { + if chat.chatInfo.contact?.nextSendGrpInv ?? false { + await sendMemberContactInvitation() + } else if case let .editingItem(ci) = composeState.contextItem { sent = await updateMessage(ci, live: live) } else if let liveMessage = liveMessage, liveMessage.sentMsg != nil { sent = await updateMessage(liveMessage.chatItem, live: live) @@ -669,6 +675,19 @@ struct ComposeView: View { await MainActor.run { composeState.inProgress = true } } + func sendMemberContactInvitation() async { + do { + let mc = checkLinkPreview() + let contact = try await apiSendMemberContactInvitation(chat.chatInfo.apiId, mc) + await MainActor.run { + self.chatModel.updateContact(contact) + } + } catch { + logger.error("ChatView.sendMemberContactInvitation error: \(error.localizedDescription)") + AlertManager.shared.showAlertMsg(title: "Error sending member contact invitation", message: "Error: \(responseError(error))") + } + } + func updateMessage(_ ei: ChatItem, live: Bool) async -> ChatItem? { if let oldMsgContent = ei.content.msgContent { do { diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift new file mode 100644 index 000000000..acb4f6d3e --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift @@ -0,0 +1,32 @@ +// +// ContextInvitingContactMemberView.swift +// SimpleX (iOS) +// +// Created by spaced4ndy on 18.09.2023. +// Copyright © 2023 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +struct ContextInvitingContactMemberView: View { + @Environment(\.colorScheme) var colorScheme + + var body: some View { + HStack { + Image(systemName: "message") + .foregroundColor(.secondary) + Text("Send direct message to connect") + } + .padding(12) + .frame(minHeight: 50) + .frame(maxWidth: .infinity, alignment: .leading) + .background(colorScheme == .light ? sentColorLight : sentColorDark) + .padding(.top, 8) + } +} + +struct ContextInvitingContactMemberView_Previews: PreviewProvider { + static var previews: some View { + ContextInvitingContactMemberView() + } +} diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift index 73c728692..6eed51788 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift @@ -17,6 +17,7 @@ struct SendMessageView: View { var sendLiveMessage: (() async -> Void)? = nil var updateLiveMessage: (() async -> Void)? = nil var cancelLiveMessage: (() -> Void)? = nil + var nextSendGrpInv: Bool = false var showVoiceMessageButton: Bool = true var voiceMessageAllowed: Bool = true var showEnableVoiceMessagesAlert: ChatInfo.ShowEnableVoiceMessagesAlert = .other @@ -118,7 +119,9 @@ struct SendMessageView: View { @ViewBuilder private func composeActionButtons() -> some View { let vmrs = composeState.voiceMessageRecordingState - if showVoiceMessageButton + if nextSendGrpInv { + inviteMemberContactButton() + } else if showVoiceMessageButton && composeState.message.isEmpty && !composeState.editing && composeState.liveMessage == nil @@ -162,6 +165,24 @@ struct SendMessageView: View { .padding([.top, .trailing], 4) } + private func inviteMemberContactButton() -> some View { + Button { + sendMessage(nil) + } label: { + Image(systemName: "arrow.up.circle.fill") + .resizable() + .foregroundColor(sendButtonColor) + .frame(width: sendButtonSize, height: sendButtonSize) + .opacity(sendButtonOpacity) + } + .disabled( + !composeState.sendEnabled || + composeState.inProgress + ) + .frame(width: 29, height: 29) + .padding([.bottom, .trailing], 4) + } + private func sendMessageButton() -> some View { Button { sendMessage(nil) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index 6842e93f0..5ec14f5be 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -22,6 +22,7 @@ struct GroupMemberInfoView: View { @State private var connectToMemberDialog: Bool = false @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @State private var justOpened = true + @State private var progressIndicator = false enum GroupMemberInfoViewAlert: Identifiable { case removeMemberAlert(mem: GroupMember) @@ -65,146 +66,154 @@ struct GroupMemberInfoView: View { } private func groupMemberInfoView() -> some View { - VStack { - List { - groupMemberInfoHeader(member) - .listRowBackground(Color.clear) + ZStack { + VStack { + List { + groupMemberInfoHeader(member) + .listRowBackground(Color.clear) - if member.memberActive { - Section { - if let contactId = member.memberContactId { - if let chat = knownDirectChat(contactId) { + if member.memberActive { + Section { + if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) { knownDirectChatButton(chat) } else if groupInfo.fullGroupPreferences.directMessages.on { - newDirectChatButton(contactId) + if let contactId = member.memberContactId { + newDirectChatButton(contactId) + } else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false { + createMemberContactButton() + } } + if let code = connectionCode { verifyCodeButton(code) } + if let connStats = connectionStats, + connStats.ratchetSyncAllowed { + synchronizeConnectionButton() + } + // } else if developerTools { + // synchronizeConnectionButtonForce() + // } } - if let code = connectionCode { verifyCodeButton(code) } - if let connStats = connectionStats, - connStats.ratchetSyncAllowed { - synchronizeConnectionButton() - } -// } else if developerTools { -// synchronizeConnectionButtonForce() -// } } - } - if let contactLink = member.contactLink { - Section { - QRCode(uri: contactLink) - Button { - showShareSheet(items: [contactLink]) - } label: { - Label("Share address", systemImage: "square.and.arrow.up") - } - if let contactId = member.memberContactId { - if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on { + if let contactLink = member.contactLink { + Section { + QRCode(uri: contactLink) + Button { + showShareSheet(items: [contactLink]) + } label: { + Label("Share address", systemImage: "square.and.arrow.up") + } + if let contactId = member.memberContactId { + if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on { + connectViaAddressButton(contactLink) + } + } else { connectViaAddressButton(contactLink) } - } else { - connectViaAddressButton(contactLink) + } header: { + Text("Address") + } footer: { + Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.") } - } header: { - Text("Address") - } footer: { - Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.") } - } - Section("Member") { - infoRow("Group", groupInfo.displayName) + Section("Member") { + infoRow("Group", groupInfo.displayName) - if let roles = member.canChangeRoleTo(groupInfo: groupInfo) { - Picker("Change role", selection: $newRole) { - ForEach(roles) { role in - Text(role.text) + if let roles = member.canChangeRoleTo(groupInfo: groupInfo) { + Picker("Change role", selection: $newRole) { + ForEach(roles) { role in + Text(role.text) + } } + .frame(height: 36) + } else { + infoRow("Role", member.memberRole.text) + } + + // TODO invited by - need to get contact by contact id + if let conn = member.activeConn { + let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) + infoRow("Connection", connLevelDesc) } - .frame(height: 36) - } else { - infoRow("Role", member.memberRole.text) } - // TODO invited by - need to get contact by contact id - if let conn = member.activeConn { - let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) - infoRow("Connection", connLevelDesc) - } - } - - if let connStats = connectionStats { - Section("Servers") { - // TODO network connection status - Button("Change receiving address") { - alert = .switchAddressAlert - } - .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } - || connStats.ratchetSyncSendProhibited - ) - if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { - Button("Abort changing address") { - alert = .abortSwitchAddressAlert + if let connStats = connectionStats { + Section("Servers") { + // TODO network connection status + Button("Change receiving address") { + alert = .switchAddressAlert } .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } || connStats.ratchetSyncSendProhibited ) + if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { + Button("Abort changing address") { + alert = .abortSwitchAddressAlert + } + .disabled( + connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + || connStats.ratchetSyncSendProhibited + ) + } + smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }) + smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }) } - smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }) - smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }) } - } - if member.canBeRemoved(groupInfo: groupInfo) { - Section { - removeMemberButton(member) + if member.canBeRemoved(groupInfo: groupInfo) { + Section { + removeMemberButton(member) + } } - } - if developerTools { - Section("For console") { - infoRow("Local name", member.localDisplayName) - infoRow("Database ID", "\(member.groupMemberId)") + if developerTools { + Section("For console") { + infoRow("Local name", member.localDisplayName) + infoRow("Database ID", "\(member.groupMemberId)") + } + } + } + .navigationBarHidden(true) + .onAppear { + if #unavailable(iOS 16) { + // this condition prevents re-setting picker + if !justOpened { return } + } + newRole = member.memberRole + do { + let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId) + let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil) + member = mem + connectionStats = stats + connectionCode = code + } catch let error { + logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))") + } + justOpened = false + } + .onChange(of: newRole) { _ in + if newRole != member.memberRole { + alert = .changeMemberRoleAlert(mem: member, role: newRole) } } } - .navigationBarHidden(true) - .onAppear { - if #unavailable(iOS 16) { - // this condition prevents re-setting picker - if !justOpened { return } - } - newRole = member.memberRole - do { - let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId) - let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil) - member = mem - connectionStats = stats - connectionCode = code - } catch let error { - logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))") - } - justOpened = false - } - .onChange(of: newRole) { _ in - if newRole != member.memberRole { - alert = .changeMemberRoleAlert(mem: member, role: newRole) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + .alert(item: $alert) { alertItem in + switch(alertItem) { + case let .removeMemberAlert(mem): return removeMemberAlert(mem) + case let .changeMemberRoleAlert(mem, _): return changeMemberRoleAlert(mem) + case .switchAddressAlert: return switchAddressAlert(switchMemberAddress) + case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchMemberAddress) + case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) }) + case let .connRequestSentAlert(type): return connReqSentAlert(type) + case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .other(alert): return alert } } - } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) - .alert(item: $alert) { alertItem in - switch(alertItem) { - case let .removeMemberAlert(mem): return removeMemberAlert(mem) - case let .changeMemberRoleAlert(mem, _): return changeMemberRoleAlert(mem) - case .switchAddressAlert: return switchAddressAlert(switchMemberAddress) - case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchMemberAddress) - case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) }) - case let .connRequestSentAlert(type): return connReqSentAlert(type) - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) - case let .other(alert): return alert + + if progressIndicator { + ProgressView().scaleEffect(2) } } } @@ -260,6 +269,33 @@ struct GroupMemberInfoView: View { } } + func createMemberContactButton() -> some View { + Button { + progressIndicator = true + Task { + do { + let memberContact = try await apiCreateMemberContact(groupInfo.apiId, member.groupMemberId) + await MainActor.run { + progressIndicator = false + chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact))) + dismissAllSheets(animated: true) + chatModel.chatId = memberContact.id + chatModel.setContactNetworkStatus(memberContact, .connected) + } + } catch let error { + logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))") + let a = getErrorAlert(error, "Error creating member contact") + await MainActor.run { + progressIndicator = false + alert = .error(title: a.title, error: a.message) + } + } + } + } label: { + Label("Send direct message", systemImage: "message") + } + } + private func groupMemberInfoHeader(_ mem: GroupMember) -> some View { VStack { ProfileImage(imageStr: mem.image, color: Color(uiColor: .tertiarySystemFill)) diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index 025c765a7..e7580530b 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -49,11 +49,10 @@ struct ChatListNavLink: View { } @ViewBuilder private func contactNavLink(_ contact: Contact) -> some View { - let v = NavLinkPlain( + NavLinkPlain( tag: chat.chatInfo.id, selection: $chatModel.chatId, - label: { ChatPreviewView(chat: chat) }, - disabled: !contact.ready + label: { ChatPreviewView(chat: chat) } ) .swipeActions(edge: .leading, allowsFullSwipe: true) { markReadButton() @@ -76,14 +75,6 @@ struct ChatListNavLink: View { .tint(.red) } .frame(height: rowHeights[dynamicTypeSize]) - - if contact.ready { - v - } else { - v.onTapGesture { - AlertManager.shared.showAlert(pendingContactAlert(chat, contact)) - } - } } @ViewBuilder private func groupNavLink(_ groupInfo: GroupInfo) -> some View { diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift index eccf0b5b4..3ac8fada7 100644 --- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift @@ -181,7 +181,11 @@ struct ChatPreviewView: View { switch (chat.chatInfo) { case let .direct(contact): if !contact.ready { - chatPreviewInfoText("connecting…") + if contact.nextSendGrpInv { + chatPreviewInfoText("send direct message") + } else { + chatPreviewInfoText("connecting…") + } } case let .group(groupInfo): switch (groupInfo.membership.memberStatus) { diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff index 97bc90919..bbfd95e6b 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -1978,6 +1978,10 @@ Chyba při vytváření odkazu skupiny No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Chyba při vytváření profilu! @@ -2107,6 +2111,10 @@ Chyba odesílání e-mailu No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Chyba při odesílání zprávy @@ -3337,6 +3345,10 @@ Hlasové zprávy může odesílat pouze váš kontakt. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Otevřít nastavení @@ -4071,6 +4083,10 @@ Odeslat přímou zprávu No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Poslat mizící zprávu @@ -5613,6 +5629,10 @@ Servery SimpleX nevidí váš profil. připojeno No comment provided by engineer. + + connected directly + rcv group event chat item + connecting připojování @@ -6072,6 +6092,10 @@ Servery SimpleX nevidí váš profil. bezpečnostní kód změněn chat item text + + send direct message + No comment provided by engineer. + starting… začíná… diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff index ba23f4854..114b7f3e7 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -1979,6 +1979,10 @@ Fehler beim Erzeugen des Gruppen-Links No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Fehler beim Erstellen des Profils! @@ -2109,6 +2113,10 @@ Fehler beim Senden der eMail No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Fehler beim Senden der Nachricht @@ -3340,6 +3348,10 @@ Nur Ihr Kontakt kann Sprachnachrichten versenden. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Geräte-Einstellungen öffnen @@ -4075,6 +4087,10 @@ Direktnachricht senden No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Verschwindende Nachricht senden @@ -5625,6 +5641,10 @@ SimpleX-Server können Ihr Profil nicht einsehen. Verbunden No comment provided by engineer. + + connected directly + rcv group event chat item + connecting verbinde @@ -6086,6 +6106,10 @@ SimpleX-Server können Ihr Profil nicht einsehen. Sicherheitscode wurde geändert chat item text + + send direct message + No comment provided by engineer. + starting… Verbindung wird gestartet… diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff index 9fbc9ebd7..0aeeecfbe 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -1988,6 +1988,11 @@ Error creating group link No comment provided by engineer. + + Error creating member contact + Error creating member contact + No comment provided by engineer. + Error creating profile! Error creating profile! @@ -2118,6 +2123,11 @@ Error sending email No comment provided by engineer. + + Error sending member contact invitation + Error sending member contact invitation + No comment provided by engineer. + Error sending message Error sending message @@ -3350,6 +3360,11 @@ Only your contact can send voice messages. No comment provided by engineer. + + Open + Open + No comment provided by engineer. + Open Settings Open Settings @@ -4085,6 +4100,11 @@ Send direct message No comment provided by engineer. + + Send direct message to connect + Send direct message to connect + No comment provided by engineer. + Send disappearing message Send disappearing message @@ -5637,6 +5657,11 @@ SimpleX servers cannot see your profile. connected No comment provided by engineer. + + connected directly + connected directly + rcv group event chat item + connecting connecting @@ -6098,6 +6123,11 @@ SimpleX servers cannot see your profile. security code changed chat item text + + send direct message + send direct message + No comment provided by engineer. + starting… starting… diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff index 4657a938e..85f02bba1 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -1978,6 +1978,10 @@ Error al crear enlace de grupo No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! ¡Error al crear perfil! @@ -2107,6 +2111,10 @@ Error al enviar email No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Error al enviar mensaje @@ -3338,6 +3346,10 @@ Sólo tu contacto puede enviar mensajes de voz. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Abrir Configuración @@ -4073,6 +4085,10 @@ Enviar mensaje directo No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Enviar mensaje temporal @@ -5624,6 +5640,10 @@ Los servidores de SimpleX no pueden ver tu perfil. conectado No comment provided by engineer. + + connected directly + rcv group event chat item + connecting conectando @@ -6085,6 +6105,10 @@ Los servidores de SimpleX no pueden ver tu perfil. código de seguridad cambiado chat item text + + send direct message + No comment provided by engineer. + starting… inicializando… diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff index ebd1ed774..c7e970f6f 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -1979,6 +1979,10 @@ Virhe ryhmälinkin luomisessa No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Virhe profiilin luomisessa! @@ -2109,6 +2113,10 @@ Virhe sähköpostin lähettämisessä No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Virhe viestin lähettämisessä @@ -3340,6 +3348,10 @@ Vain kontaktisi voi lähettää ääniviestejä. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Avaa Asetukset @@ -4075,6 +4087,10 @@ Lähetä yksityisviesti No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Lähetä katoava viesti @@ -5625,6 +5641,10 @@ SimpleX-palvelimet eivät näe profiiliasi. yhdistetty No comment provided by engineer. + + connected directly + rcv group event chat item + connecting yhdistää @@ -6086,6 +6106,10 @@ SimpleX-palvelimet eivät näe profiiliasi. turvakoodi on muuttunut chat item text + + send direct message + No comment provided by engineer. + starting… alkaa… diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff index 27d9b103d..3960c54b2 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -1979,6 +1979,10 @@ Erreur lors de la création du lien du groupe No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Erreur lors de la création du profil ! @@ -2109,6 +2113,10 @@ Erreur lors de l'envoi de l'e-mail No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Erreur lors de l'envoi du message @@ -3340,6 +3348,10 @@ Seul votre contact peut envoyer des messages vocaux. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Ouvrir les Paramètres @@ -4075,6 +4087,10 @@ Envoi de message direct No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Envoyer un message éphémère @@ -5625,6 +5641,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. connecté No comment provided by engineer. + + connected directly + rcv group event chat item + connecting connexion @@ -6086,6 +6106,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. code de sécurité modifié chat item text + + send direct message + No comment provided by engineer. + starting… lancement… diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff index 758e66f93..0a05bdedd 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -1979,6 +1979,10 @@ Errore nella creazione del link del gruppo No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Errore nella creazione del profilo! @@ -2109,6 +2113,10 @@ Errore nell'invio dell'email No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Errore nell'invio del messaggio @@ -3340,6 +3348,10 @@ Solo il tuo contatto può inviare messaggi vocali. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Apri le impostazioni @@ -4075,6 +4087,10 @@ Invia messaggio diretto No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Invia messaggio a tempo @@ -5625,6 +5641,10 @@ I server di SimpleX non possono vedere il tuo profilo. connesso/a No comment provided by engineer. + + connected directly + rcv group event chat item + connecting in connessione @@ -6086,6 +6106,10 @@ I server di SimpleX non possono vedere il tuo profilo. codice di sicurezza modificato chat item text + + send direct message + No comment provided by engineer. + starting… avvio… diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff index 29acf9ddf..27d7cb54f 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -1978,6 +1978,10 @@ グループリンク生成にエラー発生 No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! プロフィール作成にエラー発生! @@ -2107,6 +2111,10 @@ メールの送信にエラー発生 No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message メッセージ送信にエラー発生 @@ -3336,6 +3344,10 @@ 音声メッセージを送れるのはあなたの連絡相手だけです。 No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings 設定を開く @@ -4069,6 +4081,10 @@ ダイレクトメッセージを送信 No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message 消えるメッセージを送信 @@ -5611,6 +5627,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 接続中 No comment provided by engineer. + + connected directly + rcv group event chat item + connecting 接続待ち @@ -6072,6 +6092,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 セキュリティコードが変更されました chat item text + + send direct message + No comment provided by engineer. + starting… 接続中… diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff index c1504a25c..afa779f66 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -1979,6 +1979,10 @@ Fout bij maken van groep link No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Fout bij aanmaken van profiel! @@ -2109,6 +2113,10 @@ Fout bij het verzenden van e-mail No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Fout bij verzenden van bericht @@ -3340,6 +3348,10 @@ Alleen uw contact kan spraak berichten verzenden. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Open instellingen @@ -4075,6 +4087,10 @@ Direct bericht sturen No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Stuur een verdwijnend bericht @@ -5625,6 +5641,10 @@ SimpleX servers kunnen uw profiel niet zien. verbonden No comment provided by engineer. + + connected directly + rcv group event chat item + connecting Verbinden @@ -6086,6 +6106,10 @@ SimpleX servers kunnen uw profiel niet zien. beveiligingscode gewijzigd chat item text + + send direct message + No comment provided by engineer. + starting… beginnen… diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff index c17e89c91..d35d14933 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -1979,6 +1979,10 @@ Błąd tworzenia linku grupy No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Błąd tworzenia profilu! @@ -2109,6 +2113,10 @@ Błąd wysyłania e-mail No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Błąd wysyłania wiadomości @@ -3340,6 +3348,10 @@ Tylko Twój kontakt może wysyłać wiadomości głosowe. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Otwórz Ustawienia @@ -4075,6 +4087,10 @@ Wyślij wiadomość bezpośrednią No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Wyślij znikającą wiadomość @@ -5625,6 +5641,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. połączony No comment provided by engineer. + + connected directly + rcv group event chat item + connecting łączenie @@ -6086,6 +6106,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. kod bezpieczeństwa zmieniony chat item text + + send direct message + No comment provided by engineer. + starting… uruchamianie… diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff index 1d0cf4e8e..4a4431d60 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -1978,6 +1978,10 @@ Ошибка при создании ссылки группы No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Ошибка создания профиля! @@ -2107,6 +2111,10 @@ Ошибка отправки email No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Ошибка при отправке сообщения @@ -3338,6 +3346,10 @@ Только Ваш контакт может отправлять голосовые сообщения. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Открыть Настройки @@ -4073,6 +4085,10 @@ Отправить сообщение No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Отправить исчезающее сообщение @@ -5623,6 +5639,10 @@ SimpleX серверы не могут получить доступ к Ваше соединение установлено No comment provided by engineer. + + connected directly + rcv group event chat item + connecting соединяется @@ -6084,6 +6104,10 @@ SimpleX серверы не могут получить доступ к Ваше код безопасности изменился chat item text + + send direct message + No comment provided by engineer. + starting… инициализация… diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff index 439161a7f..19681b315 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -1966,6 +1966,10 @@ เกิดข้อผิดพลาดในการสร้างลิงก์กลุ่ม No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! เกิดข้อผิดพลาดในการสร้างโปรไฟล์! @@ -2095,6 +2099,10 @@ เกิดข้อผิดพลาดในการส่งอีเมล No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message เกิดข้อผิดพลาดในการส่งข้อความ @@ -3322,6 +3330,10 @@ ผู้ติดต่อของคุณเท่านั้นที่สามารถส่งข้อความเสียงได้ No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings เปิดการตั้งค่า @@ -4054,6 +4066,10 @@ ส่งข้อความโดยตรง No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message ส่งข้อความแบบที่หายไป @@ -5595,6 +5611,10 @@ SimpleX servers cannot see your profile. เชื่อมต่อสำเร็จ No comment provided by engineer. + + connected directly + rcv group event chat item + connecting กำลังเชื่อมต่อ @@ -6054,6 +6074,10 @@ SimpleX servers cannot see your profile. เปลี่ยนรหัสความปลอดภัยแล้ว chat item text + + send direct message + No comment provided by engineer. + starting… กำลังเริ่มต้น… diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff index 9d289854a..4947bf80c 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -1978,6 +1978,10 @@ Помилка створення посилання на групу No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! Помилка створення профілю! @@ -2107,6 +2111,10 @@ Помилка надсилання електронного листа No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message Помилка надсилання повідомлення @@ -3338,6 +3346,10 @@ Тільки ваш контакт може надсилати голосові повідомлення. No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings Відкрийте Налаштування @@ -4073,6 +4085,10 @@ Надішліть пряме повідомлення No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message Надіслати зникаюче повідомлення @@ -5623,6 +5639,10 @@ SimpleX servers cannot see your profile. з'єднаний No comment provided by engineer. + + connected directly + rcv group event chat item + connecting з'єднання @@ -6084,6 +6104,10 @@ SimpleX servers cannot see your profile. змінено код безпеки chat item text + + send direct message + No comment provided by engineer. + starting… починаючи… diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff index 08a446d2d..304dac1d2 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff @@ -1979,6 +1979,10 @@ 创建群组链接错误 No comment provided by engineer. + + Error creating member contact + No comment provided by engineer. + Error creating profile! 创建资料错误! @@ -2109,6 +2113,10 @@ 发送电邮错误 No comment provided by engineer. + + Error sending member contact invitation + No comment provided by engineer. + Error sending message 发送消息错误 @@ -3340,6 +3348,10 @@ 只有您的联系人可以发送语音消息。 No comment provided by engineer. + + Open + No comment provided by engineer. + Open Settings 打开设置 @@ -4075,6 +4087,10 @@ 发送私信 No comment provided by engineer. + + Send direct message to connect + No comment provided by engineer. + Send disappearing message 发送限时消息中 @@ -5625,6 +5641,10 @@ SimpleX 服务器无法看到您的资料。 已连接 No comment provided by engineer. + + connected directly + rcv group event chat item + connecting 连接中 @@ -6086,6 +6106,10 @@ SimpleX 服务器无法看到您的资料。 安全密码已更改 chat item text + + send direct message + No comment provided by engineer. + starting… 启动中…… diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index cae3722e8..55b271ed0 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -153,6 +153,8 @@ 5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */; }; + 6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */; }; + 6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */; }; 6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */; }; 6440CA00288857A10062C672 /* CIEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIEventView.swift */; }; 6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; }; @@ -429,6 +431,8 @@ 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = ""; }; 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; }; 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIInvalidJSONView.swift; sourceTree = ""; }; + 6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextInvitingContactMemberView.swift; sourceTree = ""; }; + 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMemberCreatedContactView.swift; sourceTree = ""; }; 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = ""; }; 6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = ""; }; 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = ""; }; @@ -822,6 +826,7 @@ 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */, 18415FD2E36F13F596A45BB4 /* CIVideoView.swift */, 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */, + 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */, ); path = ChatItem; sourceTree = ""; @@ -837,6 +842,7 @@ 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */, 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */, D72A9087294BD7A70047C86D /* NativeTextEditor.swift */, + 6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */, ); path = ComposeMessage; sourceTree = ""; @@ -1094,6 +1100,7 @@ 5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */, 5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */, 5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */, + 6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */, 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */, 5C3F1D562842B68D00EC8A82 /* IntegrityErrorItemView.swift in Sources */, 5C029EAA283942EA004A9677 /* CallController.swift in Sources */, @@ -1155,6 +1162,7 @@ 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */, 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */, 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */, + 6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */, 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */, 64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */, 5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */, diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 635534fea..e67a24538 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -61,6 +61,8 @@ public enum ChatCommand { case apiGroupLinkMemberRole(groupId: Int64, memberRole: GroupMemberRole) case apiDeleteGroupLink(groupId: Int64) case apiGetGroupLink(groupId: Int64) + case apiCreateMemberContact(groupId: Int64, groupMemberId: Int64) + case apiSendMemberContactInvitation(contactId: Int64, msg: MsgContent) case apiGetUserProtoServers(userId: Int64, serverProtocol: ServerProtocol) case apiSetUserProtoServers(userId: Int64, serverProtocol: ServerProtocol, servers: [ServerCfg]) case apiTestProtoServer(userId: Int64, server: String) @@ -181,6 +183,8 @@ public enum ChatCommand { case let .apiGroupLinkMemberRole(groupId, memberRole): return "/_set link role #\(groupId) \(memberRole)" case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)" case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)" + case let .apiCreateMemberContact(groupId, groupMemberId): return "/_create member contact #\(groupId) \(groupMemberId)" + case let .apiSendMemberContactInvitation(contactId, mc): return "/_invite member contact @\(contactId) \(mc.cmdString)" case let .apiGetUserProtoServers(userId, serverProtocol): return "/_servers \(userId) \(serverProtocol)" case let .apiSetUserProtoServers(userId, serverProtocol, servers): return "/_servers \(userId) \(serverProtocol) \(protoServersStr(servers))" case let .apiTestProtoServer(userId, server): return "/_server test \(userId) \(server)" @@ -304,6 +308,8 @@ public enum ChatCommand { case .apiGroupLinkMemberRole: return "apiGroupLinkMemberRole" case .apiDeleteGroupLink: return "apiDeleteGroupLink" case .apiGetGroupLink: return "apiGetGroupLink" + case .apiCreateMemberContact: return "apiCreateMemberContact" + case .apiSendMemberContactInvitation: return "apiSendMemberContactInvitation" case .apiGetUserProtoServers: return "apiGetUserProtoServers" case .apiSetUserProtoServers: return "apiSetUserProtoServers" case .apiTestProtoServer: return "apiTestProtoServer" @@ -514,6 +520,9 @@ public enum ChatResponse: Decodable, Error { case groupLinkCreated(user: UserRef, groupInfo: GroupInfo, connReqContact: String, memberRole: GroupMemberRole) case groupLink(user: UserRef, groupInfo: GroupInfo, connReqContact: String, memberRole: GroupMemberRole) case groupLinkDeleted(user: UserRef, groupInfo: GroupInfo) + case newMemberContact(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) + case newMemberContactSentInv(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) + case newMemberContactReceivedInv(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) // receiving file events case rcvFileAccepted(user: UserRef, chatItem: AChatItem) case rcvFileAcceptedSndCancelled(user: UserRef, rcvFileTransfer: RcvFileTransfer) @@ -647,6 +656,9 @@ public enum ChatResponse: Decodable, Error { case .groupLinkCreated: return "groupLinkCreated" case .groupLink: return "groupLink" case .groupLinkDeleted: return "groupLinkDeleted" + case .newMemberContact: return "newMemberContact" + case .newMemberContactSentInv: return "newMemberContactSentInv" + case .newMemberContactReceivedInv: return "newMemberContactReceivedInv" case .rcvFileAccepted: return "rcvFileAccepted" case .rcvFileAcceptedSndCancelled: return "rcvFileAcceptedSndCancelled" case .rcvFileStart: return "rcvFileStart" @@ -780,6 +792,9 @@ public enum ChatResponse: Decodable, Error { case let .groupLinkCreated(u, groupInfo, connReqContact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\nconnReqContact: \(connReqContact)\nmemberRole: \(memberRole)") case let .groupLink(u, groupInfo, connReqContact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\nconnReqContact: \(connReqContact)\nmemberRole: \(memberRole)") case let .groupLinkDeleted(u, groupInfo): return withUser(u, String(describing: groupInfo)) + case let .newMemberContact(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") + case let .newMemberContactSentInv(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") + case let .newMemberContactReceivedInv(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem)) case .rcvFileAcceptedSndCancelled: return noDetails case let .rcvFileStart(u, chatItem): return withUser(u, String(describing: chatItem)) @@ -1454,6 +1469,7 @@ public enum ChatErrorType: Decodable { case agentCommandError(message: String) case invalidFileDescription(message: String) case connectionIncognitoChangeProhibited + case peerChatVRangeIncompatible case internalError(message: String) case exception(message: String) } @@ -1479,6 +1495,7 @@ public enum StoreError: Decodable { case groupMemberNameNotFound(groupId: Int64, groupMemberName: ContactName) case groupMemberNotFound(groupMemberId: Int64) case groupMemberNotFoundByMemberId(memberId: String) + case memberContactGroupMemberNotFound(contactId: Int64) case groupWithoutUser case duplicateGroupMember case groupAlreadyJoined diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index ce8bd426c..a24c82110 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -1378,11 +1378,17 @@ public struct Contact: Identifiable, Decodable, NamedChat { public var mergedPreferences: ContactUserPreferences var createdAt: Date var updatedAt: Date + var contactGroupMemberId: Int64? + var contactGrpInvSent: Bool public var id: ChatId { get { "@\(contactId)" } } public var apiId: Int64 { get { contactId } } public var ready: Bool { get { activeConn.connStatus == .ready } } - public var sendMsgEnabled: Bool { get { !(activeConn.connectionStats?.ratchetSyncSendProhibited ?? false) } } + public var sendMsgEnabled: Bool { get { + (ready && !(activeConn.connectionStats?.ratchetSyncSendProhibited ?? false)) + || nextSendGrpInv + } } + public var nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } } public var displayName: String { localAlias == "" ? profile.displayName : localAlias } public var fullName: String { get { profile.fullName } } public var image: String? { get { profile.image } } @@ -1428,7 +1434,8 @@ public struct Contact: Identifiable, Decodable, NamedChat { userPreferences: Preferences.sampleData, mergedPreferences: ContactUserPreferences.sampleData, createdAt: .now, - updatedAt: .now + updatedAt: .now, + contactGrpInvSent: false ) } @@ -1449,6 +1456,7 @@ public struct ContactSubStatus: Decodable { public struct Connection: Decodable { public var connId: Int64 public var agentConnId: String + public var peerChatVRange: VersionRange var connStatus: ConnStatus public var connLevel: Int public var viaGroupLink: Bool @@ -1458,7 +1466,7 @@ public struct Connection: Decodable { public var connectionStats: ConnectionStats? = nil private enum CodingKeys: String, CodingKey { - case connId, agentConnId, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode + case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode } public var id: ChatId { get { ":\(connId)" } } @@ -1466,12 +1474,27 @@ public struct Connection: Decodable { static let sampleData = Connection( connId: 1, agentConnId: "abc", + peerChatVRange: VersionRange(minVersion: 1, maxVersion: 1), connStatus: .ready, connLevel: 0, viaGroupLink: false ) } +public struct VersionRange: Decodable { + public init(minVersion: Int, maxVersion: Int) { + self.minVersion = minVersion + self.maxVersion = maxVersion + } + + public var minVersion: Int + public var maxVersion: Int + + public func isCompatibleRange(_ vRange: VersionRange) -> Bool { + self.minVersion <= vRange.maxVersion && vRange.minVersion <= self.maxVersion + } +} + public struct SecurityCode: Decodable, Equatable { public init(securityCode: String, verifiedAt: Date) { self.securityCode = securityCode @@ -1503,6 +1526,7 @@ public struct UserContact: Decodable { public struct UserContactRequest: Decodable, NamedChat { var contactRequestId: Int64 public var userContactLinkId: Int64 + public var cReqChatVRange: VersionRange var localDisplayName: ContactName var profile: Profile var createdAt: Date @@ -1520,6 +1544,7 @@ public struct UserContactRequest: Decodable, NamedChat { public static let sampleData = UserContactRequest( contactRequestId: 1, userContactLinkId: 1, + cReqChatVRange: VersionRange(minVersion: 1, maxVersion: 1), localDisplayName: "alice", profile: Profile.sampleData, createdAt: .now, @@ -2078,6 +2103,7 @@ public struct ChatItem: Identifiable, Decodable { case .memberLeft: return false case .memberDeleted: return false case .invitedViaGroupLink: return false + case .memberCreatedContact: return false } case .sndGroupEvent: return showNtfDir case .rcvConnEvent: return false @@ -3181,6 +3207,7 @@ public enum RcvGroupEvent: Decodable { case groupDeleted case groupUpdated(groupProfile: GroupProfile) case invitedViaGroupLink + case memberCreatedContact var text: String { switch self { @@ -3198,6 +3225,7 @@ public enum RcvGroupEvent: Decodable { case .groupDeleted: return NSLocalizedString("deleted group", comment: "rcv group event chat item") case .groupUpdated: return NSLocalizedString("updated group profile", comment: "rcv group event chat item") case .invitedViaGroupLink: return NSLocalizedString("invited via your group link", comment: "rcv group event chat item") + case .memberCreatedContact: return NSLocalizedString("connected directly", comment: "rcv group event chat item") } } } From f19fae615d12e545020df31a8e26e1c09f964ff8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:53:54 +0100 Subject: [PATCH 4/6] ios: add Bulgarian language --- .../AccentColor.colorset/Contents.json | 15 + .../Shared/Assets.xcassets/Contents.json | 6 + .../bg.xcloc/Localized Contents/bg.xliff | 5428 ++++++++--------- .../AccentColor.colorset/Contents.json | 23 + .../Shared/Assets.xcassets/Contents.json | 6 + .../SimpleX NSE/en.lproj/InfoPlist.strings | 6 + .../en.lproj/Localizable.strings | 30 + .../en.lproj/SimpleX--iOS--InfoPlist.strings | 10 + .../bg.xcloc/contents.json | 12 + .../SimpleX NSE/bg.lproj/InfoPlist.strings | 9 + apps/ios/SimpleX.xcodeproj/project.pbxproj | 7 + apps/ios/bg.lproj/Localizable.strings | 3681 +++++++++++ .../bg.lproj/SimpleX--iOS--InfoPlist.strings | 15 + 13 files changed, 6525 insertions(+), 2723 deletions(-) create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/SimpleX--iOS--InfoPlist.strings create mode 100644 apps/ios/SimpleX Localizations/bg.xcloc/contents.json create mode 100644 apps/ios/SimpleX NSE/bg.lproj/InfoPlist.strings create mode 100644 apps/ios/bg.lproj/Localizable.strings create mode 100644 apps/ios/bg.lproj/SimpleX--iOS--InfoPlist.strings diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..59aaf6069 --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,15 @@ +{ + "colors" : [ + { + "idiom" : "universal", + "locale" : "bg" + } + ], + "properties" : { + "localizable" : true + }, + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff index 5874b7537..dddd9158e 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -2,6324 +2,6306 @@
- +
- + - + No comment provided by engineer. - + - + No comment provided by engineer. - + - + No comment provided by engineer. - + - + No comment provided by engineer. - + ( - ( + ( No comment provided by engineer. - + (can be copied) - (може да се копира) + (може да се копира) No comment provided by engineer. - + !1 colored! - !1 цветно! + !1 цветно! No comment provided by engineer. - - #secret# - #тайно# - No comment provided by engineer. + + # %@ + # %@ + copied message info title, # <title> - - %@ - %@ - No comment provided by engineer. - - - %@ %@ - %@ %@ - No comment provided by engineer. - - - %@ (current) - %@ (текущ) - No comment provided by engineer. - - - %@ (current): - %@ (текущ): + + ## History + ## История copied message info - - %@ / %@ - %@ / %@ + + ## In reply to + ## В отговор на + copied message info + + + #secret# + #тайно# No comment provided by engineer. - + + %@ + %@ + No comment provided by engineer. + + + %@ %@ + %@ %@ + No comment provided by engineer. + + + %@ (current) + %@ (текущ) + No comment provided by engineer. + + + %@ (current): + %@ (текущ): + copied message info + + + %@ / %@ + %@ / %@ + No comment provided by engineer. + + + %@ and %@ connected + %@ и %@ са свързани + No comment provided by engineer. + + %1$@ at %2$@: - %1$@ в %2$@: + %1$@ в %2$@: copied message info, <sender> at <time> - + %@ is connected! - %@ е свързан! + %@ е свързан! notification title - + %@ is not verified - %@ не е потвърдено + %@ не е потвърдено No comment provided by engineer. - + %@ is verified - %@ е потвърдено + %@ е потвърдено No comment provided by engineer. - + %@ servers - %@ сървъри + %@ сървъри No comment provided by engineer. - + %@ wants to connect! - %@ иска да се свърже! + %@ иска да се свърже! notification title - + + %@, %@ and %lld other members connected + %@, %@ и %lld други членове са свързани + No comment provided by engineer. + + %@: - %@: + %@: copied message info - + %d days - %d дни + %d дни time interval - + %d hours - %d часа + %d часа time interval - + %d min - %d мин. + %d мин. time interval - + %d months - %d месеца + %d месеца time interval - + %d sec - %d сек. + %d сек. time interval - + %d skipped message(s) - %d пропуснато(и) съобщение(я) + %d пропуснато(и) съобщение(я) integrity error chat item - + %d weeks - %d седмици + %d седмици time interval - + %lld - %lld + %lld No comment provided by engineer. - + %lld %@ - %lld %@ + %lld %@ No comment provided by engineer. - + %lld contact(s) selected - %lld избран(и) контакт(а) + %lld избран(и) контакт(а) No comment provided by engineer. - + %lld file(s) with total size of %@ - %lld файл(а) с общ размер от %@ + %lld файл(а) с общ размер от %@ No comment provided by engineer. - + %lld members - %lld членове + %lld членове No comment provided by engineer. - + %lld minutes - %lld минути + %lld минути No comment provided by engineer. - + + %lld new interface languages + No comment provided by engineer. + + %lld second(s) - %lld секунда(и) + %lld секунда(и) No comment provided by engineer. - + %lld seconds - %lld секунди + %lld секунди No comment provided by engineer. - + %lldd - %lldд + %lldд No comment provided by engineer. - + %lldh - %lldч + %lldч No comment provided by engineer. - + %lldk - %lldk + %lldk No comment provided by engineer. - + %lldm - %lldм + %lldм No comment provided by engineer. - + %lldmth - %lldмесц. + %lldмесц. No comment provided by engineer. - + %llds - %lldс + %lldс No comment provided by engineer. - + %lldw - %lldсед. + %lldсед. No comment provided by engineer. - + %u messages failed to decrypt. - %u съобщения не успяха да се декриптират. + %u съобщения не успяха да се декриптират. No comment provided by engineer. - + %u messages skipped. - %u пропуснати съобщения. + %u пропуснати съобщения. No comment provided by engineer. - + ( - ( + ( No comment provided by engineer. - + ) - ) + ) No comment provided by engineer. - + **Add new contact**: to create your one-time QR Code or link for your contact. - **Добави нов контакт**: за да създадете своя еднократен QR код или линк за вашия контакт. + **Добави нов контакт**: за да създадете своя еднократен QR код или линк за вашия контакт. No comment provided by engineer. - + **Create link / QR code** for your contact to use. - **Създай линк / QR код**, който вашият контакт да използва. + **Създай линк / QR код**, който вашият контакт да използва. No comment provided by engineer. - + **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - **По поверително**: проверявайте новите съобщения на всеки 20 минути. Токенът на устройството се споделя със сървъра за чат SimpleX, но не и колко контакти или съобщения имате. + **По поверително**: проверявайте новите съобщения на всеки 20 минути. Токенът на устройството се споделя със сървъра за чат SimpleX, но не и колко контакти или съобщения имате. No comment provided by engineer. - + **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - **Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението). + **Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението). No comment provided by engineer. - + **Paste received link** or open it in the browser and tap **Open in mobile app**. - **Поставете получения линк** или го отворете в браузъра и докоснете **Отваряне в мобилно приложение**. + **Поставете получения линк** или го отворете в браузъра и докоснете **Отваряне в мобилно приложение**. No comment provided by engineer. - + **Please note**: you will NOT be able to recover or change passphrase if you lose it. - **Моля, обърнете внимание**: НЯМА да можете да възстановите или промените паролата, ако я загубите. + **Моля, обърнете внимание**: НЯМА да можете да възстановите или промените паролата, ако я загубите. No comment provided by engineer. - + **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. - **Препоръчително**: токенът на устройството и известията се изпращат до сървъра за уведомяване на SimpleX Chat, но не и съдържанието, размерът на съобщението или от кого е. + **Препоръчително**: токенът на устройството и известията се изпращат до сървъра за уведомяване на SimpleX Chat, но не и съдържанието, размерът на съобщението или от кого е. No comment provided by engineer. - + **Scan QR code**: to connect to your contact in person or via video call. - **Сканирай QR код**: за да се свържете с вашия контакт лично или чрез видеообаждане. + **Сканирай QR код**: за да се свържете с вашия контакт лично или чрез видеообаждане. No comment provided by engineer. - + **Warning**: Instant push notifications require passphrase saved in Keychain. - **Внимание**: Незабавните push известия изискват парола, запазена в Keychain. + **Внимание**: Незабавните push известия изискват парола, запазена в Keychain. No comment provided by engineer. - + **e2e encrypted** audio call - **e2e криптиран**аудио разговор + **e2e криптиран**аудио разговор No comment provided by engineer. - + **e2e encrypted** video call - **e2e криптирано** видео разговор + **e2e криптирано** видео разговор No comment provided by engineer. - + \*bold* - \*удебелен* + \*удебелен* No comment provided by engineer. - + , - , + , No comment provided by engineer. - + + - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! +- delivery receipts (up to 20 members). +- faster and more stable. + No comment provided by engineer. + + + - more stable message delivery. +- a bit better groups. +- and more! + - по-стабилна доставка на съобщения. +- малко по-добри групи. +- и още! + No comment provided by engineer. + + - voice messages up to 5 minutes. - custom time to disappear. - editing history. - - гласови съобщения до 5 минути. + - гласови съобщения до 5 минути. - персонализирано време за изчезване. - история на редактиране. No comment provided by engineer. - + . - . + . No comment provided by engineer. - + 0s - 0s + 0s No comment provided by engineer. - + 1 day - 1 ден + 1 ден time interval - + 1 hour - 1 час + 1 час time interval - + 1 minute - 1 минута + 1 минута No comment provided by engineer. - + 1 month - 1 месец + 1 месец time interval - + 1 week - 1 седмица + 1 седмица time interval - + 1-time link - Еднократен линк + Еднократен линк No comment provided by engineer. - + 5 minutes - 5 минути + 5 минути No comment provided by engineer. - + 6 - 6 + 6 No comment provided by engineer. - + 30 seconds - 30 секунди + 30 секунди No comment provided by engineer. - + : - : + : No comment provided by engineer. - + <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> - <p>Здравейте!</p> + <p>Здравейте!</p> <p><a href="%@">Свържете се с мен чрез SimpleX Chat</a></p> email text - + + A few more things + Още няколко неща + No comment provided by engineer. + + A new contact - Нов контакт + Нов контакт notification title - - A random profile will be sent to the contact that you received this link from + + A new random profile will be shared. + Нов автоматично генериран профил ще бъде споделен. No comment provided by engineer. - - A random profile will be sent to your contact - No comment provided by engineer. - - + A separate TCP connection will be used **for each chat profile you have in the app**. - Ще се използва отделна TCP връзка **за всеки чатпрофил, който имате в приложението**. + Ще се използва отделна TCP връзка **за всеки чатпрофил, който имате в приложението**. No comment provided by engineer. - + A separate TCP connection will be used **for each contact and group member**. **Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. - Ще се използва отделна TCP връзка **за всеки контакт и член на групата**. + Ще се използва отделна TCP връзка **за всеки контакт и член на групата**. **Моля, обърнете внимание**: ако имате много връзки, консумацията на батерията и трафика може да бъде значително по-висока и някои връзки може да се провалят. No comment provided by engineer. - + Abort - Откажи + Откажи No comment provided by engineer. - + Abort changing address - Откажи смяна на адрес + Откажи смяна на адрес No comment provided by engineer. - + Abort changing address? - Откажи смяна на адрес? + Откажи смяна на адрес? No comment provided by engineer. - + About SimpleX - За SimpleX + За SimpleX No comment provided by engineer. - + About SimpleX Chat - За SimpleX Chat + За SimpleX Chat No comment provided by engineer. - + About SimpleX address - Повече за SimpleX адреса + Повече за SimpleX адреса No comment provided by engineer. - + Accent color - Основен цвят + Основен цвят No comment provided by engineer. - + Accept - Приеми + Приеми accept contact request via notification accept incoming call via notification - - Accept contact + + Accept connection request? + Приемане на заявка за връзка? No comment provided by engineer. - + Accept contact request from %@? - Приемане на заявка за контакт от %@? + Приемане на заявка за контакт от %@? notification body - + Accept incognito - Приеми инкогнито - No comment provided by engineer. + Приеми инкогнито + accept contact request via notification - + Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. - Добавете адрес към вашия профил, така че вашите контакти да могат да го споделят с други хора. Актуализацията на профила ще бъде изпратена до вашите контакти. + Добавете адрес към вашия профил, така че вашите контакти да могат да го споделят с други хора. Актуализацията на профила ще бъде изпратена до вашите контакти. No comment provided by engineer. - + Add preset servers - Добави предварително зададени сървъри + Добави предварително зададени сървъри No comment provided by engineer. - + Add profile - Добави профил + Добави профил No comment provided by engineer. - + Add servers by scanning QR codes. - Добави сървъри чрез сканиране на QR кодове. + Добави сървъри чрез сканиране на QR кодове. No comment provided by engineer. - + Add server… - Добави сървър… + Добави сървър… No comment provided by engineer. - + Add to another device - Добави към друго устройство + Добави към друго устройство No comment provided by engineer. - + Add welcome message - Добави съобщение при посрещане + Добави съобщение при посрещане No comment provided by engineer. - + Address - Адрес + Адрес No comment provided by engineer. - + Address change will be aborted. Old receiving address will be used. - Промяната на адреса ще бъде прекъсната. Ще се използва старият адрес за получаване. + Промяната на адреса ще бъде прекъсната. Ще се използва старият адрес за получаване. No comment provided by engineer. - + Admins can create the links to join groups. - Админите могат да създадат линкове за присъединяване към групи. + Админите могат да създадат линкове за присъединяване към групи. No comment provided by engineer. - + Advanced network settings - Разширени мрежови настройки + Разширени мрежови настройки No comment provided by engineer. - + All app data is deleted. - Всички данни от приложението бяха изтрити. + Всички данни от приложението бяха изтрити. No comment provided by engineer. - + All chats and messages will be deleted - this cannot be undone! - Всички чатове и съобщения ще бъдат изтрити - това не може да бъде отменено! + Всички чатове и съобщения ще бъдат изтрити - това не може да бъде отменено! No comment provided by engineer. - + All data is erased when it is entered. - Всички данни се изтриват при въвеждане. + Всички данни се изтриват при въвеждане. No comment provided by engineer. - + All group members will remain connected. - Всички членове на групата ще останат свързани. + Всички членове на групата ще останат свързани. No comment provided by engineer. - + All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - Всички съобщения ще бъдат изтрити - това не може да бъде отменено! Съобщенията ще бъдат изтрити САМО за вас. + Всички съобщения ще бъдат изтрити - това не може да бъде отменено! Съобщенията ще бъдат изтрити САМО за вас. No comment provided by engineer. - + All your contacts will remain connected. - Всички ваши контакти ще останат свързани. + Всички ваши контакти ще останат свързани. No comment provided by engineer. - + All your contacts will remain connected. Profile update will be sent to your contacts. - Всички ваши контакти ще останат свързани. Актуализацията на профила ще бъде изпратена до вашите контакти. + Всички ваши контакти ще останат свързани. Актуализацията на профила ще бъде изпратена до вашите контакти. No comment provided by engineer. - + Allow - Позволи + Позволи No comment provided by engineer. - + Allow calls only if your contact allows them. - Позволи обаждания само ако вашият контакт ги разрешава. + Позволи обаждания само ако вашият контакт ги разрешава. No comment provided by engineer. - + Allow disappearing messages only if your contact allows it to you. - Позволи изчезващи съобщения само ако вашият контакт ги разрешава. + Позволи изчезващи съобщения само ако вашият контакт ги разрешава. No comment provided by engineer. - + Allow irreversible message deletion only if your contact allows it to you. - Позволи необратимо изтриване на съобщение само ако вашият контакт го рарешава. + Позволи необратимо изтриване на съобщение само ако вашият контакт го рарешава. No comment provided by engineer. - + Allow message reactions only if your contact allows them. - Позволи реакции на съобщения само ако вашият контакт ги разрешава. + Позволи реакции на съобщения само ако вашият контакт ги разрешава. No comment provided by engineer. - + Allow message reactions. - Позволи реакции на съобщения. + Позволи реакции на съобщения. No comment provided by engineer. - + Allow sending direct messages to members. - Позволи изпращането на лични съобщения до членовете. + Позволи изпращането на лични съобщения до членовете. No comment provided by engineer. - + Allow sending disappearing messages. - Разреши изпращането на изчезващи съобщения. + Разреши изпращането на изчезващи съобщения. No comment provided by engineer. - + Allow to irreversibly delete sent messages. - Позволи необратимо изтриване на изпратените съобщения. + Позволи необратимо изтриване на изпратените съобщения. No comment provided by engineer. - + Allow to send files and media. - Позволи изпращане на файлове и медия. + Позволи изпращане на файлове и медия. No comment provided by engineer. - + Allow to send voice messages. - Позволи изпращане на гласови съобщения. + Позволи изпращане на гласови съобщения. No comment provided by engineer. - + Allow voice messages only if your contact allows them. - Позволи гласови съобщения само ако вашият контакт ги разрешава. + Позволи гласови съобщения само ако вашият контакт ги разрешава. No comment provided by engineer. - + Allow voice messages? - Позволи гласови съобщения? + Позволи гласови съобщения? No comment provided by engineer. - + Allow your contacts adding message reactions. - Позволи на вашите контакти да добавят реакции към съобщения. + Позволи на вашите контакти да добавят реакции към съобщения. No comment provided by engineer. - + Allow your contacts to call you. - Позволи на вашите контакти да ви се обаждат. + Позволи на вашите контакти да ви се обаждат. No comment provided by engineer. - + Allow your contacts to irreversibly delete sent messages. - Позволи на вашите контакти да изтриват необратимо изпратените съобщения. + Позволи на вашите контакти да изтриват необратимо изпратените съобщения. No comment provided by engineer. - + Allow your contacts to send disappearing messages. - Позволи на вашите контакти да изпращат изчезващи съобщения. + Позволи на вашите контакти да изпращат изчезващи съобщения. No comment provided by engineer. - + Allow your contacts to send voice messages. - Позволи на вашите контакти да изпращат гласови съобщения. + Позволи на вашите контакти да изпращат гласови съобщения. No comment provided by engineer. - + Already connected? - Вече сте свързани? + Вече сте свързани? No comment provided by engineer. - + Always use relay - Винаги използвай реле + Винаги използвай реле No comment provided by engineer. - + An empty chat profile with the provided name is created, and the app opens as usual. - Създаен беше празен профил за чат с предоставеното име и приложението се отвари както обикновено. + Създаен беше празен профил за чат с предоставеното име и приложението се отвари както обикновено. No comment provided by engineer. - + Answer call - Отговор на повикване + Отговор на повикване No comment provided by engineer. - + App build: %@ - Компилация на приложението: %@ + Компилация на приложението: %@ No comment provided by engineer. - + + App encrypts new local files (except videos). + No comment provided by engineer. + + App icon - Икона на приложението + Икона на приложението No comment provided by engineer. - + App passcode - Код за достъп до приложението + Код за достъп до приложението No comment provided by engineer. - + App passcode is replaced with self-destruct passcode. - Кода за достъп до приложение се заменя с код за самоунищожение. + Кода за достъп до приложение се заменя с код за самоунищожение. No comment provided by engineer. - + App version - Версия на приложението + Версия на приложението No comment provided by engineer. - + App version: v%@ - Версия на приложението: v%@ + Версия на приложението: v%@ No comment provided by engineer. - + Appearance - Изглед + Изглед No comment provided by engineer. - + Attach - Прикачи + Прикачи No comment provided by engineer. - + Audio & video calls - Аудио и видео разговори + Аудио и видео разговори No comment provided by engineer. - + Audio and video calls - Аудио и видео разговори + Аудио и видео разговори No comment provided by engineer. - + Audio/video calls - Аудио/видео разговори + Аудио/видео разговори chat feature - + Audio/video calls are prohibited. - Аудио/видео разговорите са забранени. + Аудио/видео разговорите са забранени. No comment provided by engineer. - + Authentication cancelled - Идентификацията е отменена + Идентификацията е отменена PIN entry - + Authentication failed - Неуспешна идентификация + Неуспешна идентификация No comment provided by engineer. - + Authentication is required before the call is connected, but you may miss calls. - Изисква се идентификацията, преди да се осъществи обаждането, но може да пропуснете повиквания. + Изисква се идентификацията, преди да се осъществи обаждането, но може да пропуснете повиквания. No comment provided by engineer. - + Authentication unavailable - Идентификацията е недостъпна + Идентификацията е недостъпна No comment provided by engineer. - + Auto-accept - Автоматично приемане + Автоматично приемане No comment provided by engineer. - + Auto-accept contact requests - Автоматично приемане на заявки за контакт + Автоматично приемане на заявки за контакт No comment provided by engineer. - + Auto-accept images - Автоматично приемане на изображения + Автоматично приемане на изображения No comment provided by engineer. - + Back - Назад + Назад No comment provided by engineer. - + Bad message ID - Лошо ID на съобщението + Лошо ID на съобщението No comment provided by engineer. - + Bad message hash - Лош хеш на съобщението + Лош хеш на съобщението No comment provided by engineer. - + Better messages - По-добри съобщения + По-добри съобщения No comment provided by engineer. - + Both you and your contact can add message reactions. - И вие, и вашият контакт можете да добавяте реакции към съобщението. + И вие, и вашият контакт можете да добавяте реакции към съобщението. No comment provided by engineer. - + Both you and your contact can irreversibly delete sent messages. - И вие, и вашият контакт можете да изтриете необратимо изпратените съобщения. + И вие, и вашият контакт можете да изтриете необратимо изпратените съобщения. No comment provided by engineer. - + Both you and your contact can make calls. - И вие, и вашият контакт можете да осъществявате обаждания. + И вие, и вашият контакт можете да осъществявате обаждания. No comment provided by engineer. - + Both you and your contact can send disappearing messages. - И вие, и вашият контакт можете да изпращате изчезващи съобщения. + И вие, и вашият контакт можете да изпращате изчезващи съобщения. No comment provided by engineer. - + Both you and your contact can send voice messages. - И вие, и вашият контакт можете да изпращате гласови съобщения. + И вие, и вашият контакт можете да изпращате гласови съобщения. No comment provided by engineer. - + + Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + No comment provided by engineer. + + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - Чрез чат профил (по подразбиране) или [чрез връзка](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (БЕТА). + Чрез чат профил (по подразбиране) или [чрез връзка](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (БЕТА). No comment provided by engineer. - + Call already ended! - Разговорът вече приключи! + Разговорът вече приключи! No comment provided by engineer. - + Calls - Обаждания + Обаждания No comment provided by engineer. - + Can't delete user profile! - Потребителският профил не може да се изтрие! + Потребителският профил не може да се изтрие! No comment provided by engineer. - + Can't invite contact! - Не може да покани контакта! + Не може да покани контакта! No comment provided by engineer. - + Can't invite contacts! - Не може да поканят контактите! + Не може да поканят контактите! No comment provided by engineer. - + Cancel - Отказ + Отказ No comment provided by engineer. - + Cannot access keychain to save database password - Няма достъп до Keychain за запазване на паролата за базата данни + Няма достъп до Keychain за запазване на паролата за базата данни No comment provided by engineer. - + Cannot receive file - Файлът не може да бъде получен + Файлът не може да бъде получен No comment provided by engineer. - + Change - Промени + Промени No comment provided by engineer. - + Change database passphrase? - Промяна на паролата на базата данни? + Промяна на паролата на базата данни? No comment provided by engineer. - + Change lock mode - Промяна на режима на заключване + Промяна на режима на заключване authentication reason - + Change member role? - Промяна на ролята на члена? + Промяна на ролята на члена? No comment provided by engineer. - + Change passcode - Промени kодa за достъп + Промени kодa за достъп authentication reason - + Change receiving address - Промени адреса за получаване + Промени адреса за получаване No comment provided by engineer. - + Change receiving address? - Промени адреса за получаване? + Промени адреса за получаване? No comment provided by engineer. - + Change role - Промени ролята + Промени ролята No comment provided by engineer. - + Change self-destruct mode - Промени режима на самоунищожение + Промени режима на самоунищожение authentication reason - + Change self-destruct passcode - Промени кода за достъп за самоунищожение + Промени кода за достъп за самоунищожение authentication reason set passcode view - + Chat archive - Архив на чата + Архив на чата No comment provided by engineer. - + Chat console - Конзола + Конзола No comment provided by engineer. - + Chat database - База данни за чата + База данни за чата No comment provided by engineer. - + Chat database deleted - Базата данни на чата е изтрита + Базата данни на чата е изтрита No comment provided by engineer. - + Chat database imported - Базата данни на чат е импортирана + Базата данни на чат е импортирана No comment provided by engineer. - + Chat is running - Чатът работи + Чатът работи No comment provided by engineer. - + Chat is stopped - Чатът е спрян + Чатът е спрян No comment provided by engineer. - + Chat preferences - Чат настройки + Чат настройки No comment provided by engineer. - + Chats - Чатове + Чатове No comment provided by engineer. - + Check server address and try again. - Проверете адреса на сървъра и опитайте отново. + Проверете адреса на сървъра и опитайте отново. No comment provided by engineer. - + Chinese and Spanish interface - Китайски и Испански интерфейс + Китайски и Испански интерфейс No comment provided by engineer. - + Choose file - Избери файл + Избери файл No comment provided by engineer. - + Choose from library - Избери от библиотеката + Избери от библиотеката No comment provided by engineer. - + Clear - Изчисти + Изчисти No comment provided by engineer. - + Clear conversation - Изчисти разговора + Изчисти разговора No comment provided by engineer. - + Clear conversation? - Изчисти разговора? + Изчисти разговора? No comment provided by engineer. - + Clear verification - Изчисти проверката + Изчисти проверката No comment provided by engineer. - + Colors - Цветове + Цветове No comment provided by engineer. - + Compare file - Сравни файл + Сравни файл server test step - + Compare security codes with your contacts. - Сравнете кодовете за сигурност с вашите контакти. + Сравнете кодовете за сигурност с вашите контакти. No comment provided by engineer. - + Configure ICE servers - Конфигурирай ICE сървъри + Конфигурирай ICE сървъри No comment provided by engineer. - + Confirm - Потвърди + Потвърди No comment provided by engineer. - + Confirm Passcode - Потвърди kодa за достъп + Потвърди kодa за достъп No comment provided by engineer. - + Confirm database upgrades - Потвърди актуализаациите на базата данни + Потвърди актуализаациите на базата данни No comment provided by engineer. - + Confirm new passphrase… - Потвърди новата парола… + Потвърди новата парола… No comment provided by engineer. - + Confirm password - Потвърди парола + Потвърди парола No comment provided by engineer. - + Connect - Свързване + Свързване server test step - - Connect via contact link? + + Connect directly + Свързване директно No comment provided by engineer. - + + Connect incognito + Свързване инкогнито + No comment provided by engineer. + + + Connect via contact link + Свързване чрез линк на контакта + No comment provided by engineer. + + Connect via group link? - Свързване чрез групов линк? + Свързване чрез групов линк? No comment provided by engineer. - + Connect via link - Свърване чрез линк + Свърване чрез линк No comment provided by engineer. - + Connect via link / QR code - Свърване чрез линк/QR код + Свърване чрез линк/QR код No comment provided by engineer. - - Connect via one-time link? + + Connect via one-time link + Свързване чрез еднократен линк за връзка No comment provided by engineer. - + Connecting to server… - Свързване със сървъра… + Свързване със сървъра… No comment provided by engineer. - + Connecting to server… (error: %@) - Свързване със сървър…(грешка: %@) + Свързване със сървър…(грешка: %@) No comment provided by engineer. - + Connection - Връзка + Връзка No comment provided by engineer. - + Connection error - Грешка при свързване + Грешка при свързване No comment provided by engineer. - + Connection error (AUTH) - Грешка при свързване (AUTH) + Грешка при свързване (AUTH) 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 allows - Контактът позволява + Контактът позволява 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 hidden: - Контактът е скрит: + Контактът е скрит: notification - + Contact is connected - Контактът е свързан + Контактът е свързан notification - + Contact is not connected yet! - Контактът все още не е свързан! + Контактът все още не е свързан! No comment provided by engineer. - + Contact name - Име на контакт + Име на контакт No comment provided by engineer. - + Contact preferences - Настройки за контакт + Настройки за контакт No comment provided by engineer. - + Contacts - Контакти + Контакти No comment provided by engineer. - + Contacts can mark messages for deletion; you will be able to view them. - Контактите могат да маркират съобщения за изтриване; ще можете да ги разглеждате. + Контактите могат да маркират съобщения за изтриване; ще можете да ги разглеждате. No comment provided by engineer. - + Continue - Продължи + Продължи No comment provided by engineer. - + Copy - Копирай + Копирай chat item action - + Core version: v%@ - Версия на ядрото: v%@ + Версия на ядрото: v%@ No comment provided by engineer. - + Create - Създай + Създай No comment provided by engineer. - + Create SimpleX address - Създай SimpleX адрес + Създай SimpleX адрес No comment provided by engineer. - + Create an address to let people connect with you. - Създайте адрес, за да позволите на хората да се свързват с вас. + Създайте адрес, за да позволите на хората да се свързват с вас. No comment provided by engineer. - + Create file - Създай файл + Създай файл server test step - + Create group link - Създай групов линк + Създай групов линк No comment provided by engineer. - + Create link - Създай линк + Създай линк No comment provided by engineer. - + + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 + No comment provided by engineer. + + Create one-time invitation link - Създай линк за еднократна покана + Създай линк за еднократна покана No comment provided by engineer. - + Create queue - Създай опашка + Създай опашка server test step - + Create secret group - Създай тайна група + Създай тайна група No comment provided by engineer. - + Create your profile - Създай своя профил + Създай своя профил No comment provided by engineer. - + Created on %@ - Създаден на %@ + Създаден на %@ No comment provided by engineer. - + Current Passcode - Текущ kод за достъп + Текущ kод за достъп No comment provided by engineer. - + Current passphrase… - Текуща парола… + Текуща парола… No comment provided by engineer. - + Currently maximum supported file size is %@. - В момента максималният поддържан размер на файла е %@. + В момента максималният поддържан размер на файла е %@. No comment provided by engineer. - + Custom time - Персонализирано време + Персонализирано време No comment provided by engineer. - + Dark - Тъмна + Тъмна No comment provided by engineer. - + Database ID - ID в базата данни + ID в базата данни No comment provided by engineer. - + Database ID: %d - ID в базата данни: %d + ID в базата данни: %d copied message info - + Database IDs and Transport isolation option. - Идентификатори в базата данни и опция за изолация на транспорта. + Идентификатори в базата данни и опция за изолация на транспорта. No comment provided by engineer. - + Database downgrade - Понижаване на версията на базата данни + Понижаване на версията на базата данни No comment provided by engineer. - + Database encrypted! - Базата данни е криптирана! + Базата данни е криптирана! No comment provided by engineer. - + Database encryption passphrase will be updated and stored in the keychain. - Паролата за криптиране на базата данни ще бъде актуализирана и съхранена в Keychain. + Паролата за криптиране на базата данни ще бъде актуализирана и съхранена в Keychain. No comment provided by engineer. - + Database encryption passphrase will be updated. - Паролата за криптиране на базата данни ще бъде актуализирана. + Паролата за криптиране на базата данни ще бъде актуализирана. No comment provided by engineer. - + Database error - Грешка в базата данни + Грешка в базата данни No comment provided by engineer. - + Database is encrypted using a random passphrase, you can change it. - Базата данни е криптирана с автоматично генерирана парола, можете да я промените. + Базата данни е криптирана с автоматично генерирана парола, можете да я промените. No comment provided by engineer. - + Database is encrypted using a random passphrase. Please change it before exporting. - Базата данни е криптирана с автоматично генерирана парола. Моля, променете я преди експортиране. + Базата данни е криптирана с автоматично генерирана парола. Моля, променете я преди експортиране. No comment provided by engineer. - + Database passphrase - Парола за базата данни + Парола за базата данни No comment provided by engineer. - + Database passphrase & export - Парола за базата данни и експортиране + Парола за базата данни и експортиране No comment provided by engineer. - + Database passphrase is different from saved in the keychain. - Паролата на базата данни е различна от записаната в Keychain. + Паролата на базата данни е различна от записаната в Keychain. No comment provided by engineer. - + Database passphrase is required to open chat. - Изисква се паролата за базата данни, за да се отвори чата. + Изисква се паролата за базата данни, за да се отвори чата. No comment provided by engineer. - + Database upgrade - Актуализация на базата данни + Актуализация на базата данни No comment provided by engineer. - + Database will be encrypted and the passphrase stored in the keychain. - Базата данни ще бъде криптирана и паролата ще бъде съхранена в Keychain. + Базата данни ще бъде криптирана и паролата ще бъде съхранена в Keychain. No comment provided by engineer. - + Database will be encrypted. - Базата данни ще бъде криптирана. + Базата данни ще бъде криптирана. No comment provided by engineer. - + Database will be migrated when the app restarts - Базата данни ще бъде мигрирана, когато приложението се рестартира + Базата данни ще бъде мигрирана, когато приложението се рестартира No comment provided by engineer. - + Decentralized - Децентрализиран + Децентрализиран No comment provided by engineer. - + Decryption error - Грешка при декриптиране + Грешка при декриптиране message decrypt error item - + Delete - Изтрий + Изтрий chat item action - + Delete Contact - Изтрий контакт + Изтрий контакт No comment provided by engineer. - + Delete address - Изтрий адрес + Изтрий адрес No comment provided by engineer. - + Delete address? - Изтрий адрес? + Изтрий адрес? No comment provided by engineer. - + Delete after - Изтрий след + Изтрий след No comment provided by engineer. - + Delete all files - Изтрий всички файлове + Изтрий всички файлове No comment provided by engineer. - + Delete archive - Изтрий архив + Изтрий архив No comment provided by engineer. - + Delete chat archive? - Изтриване на архива на чата? + Изтриване на архива на чата? No comment provided by engineer. - + Delete chat profile - Изтрий чат профила + Изтрий чат профила No comment provided by engineer. - + Delete chat profile? - Изтриване на чат профила? + Изтриване на чат профила? No comment provided by engineer. - + Delete connection - Изтрий връзката + Изтрий връзката No comment provided by engineer. - + Delete contact - Изтрий контакт + Изтрий контакт No comment provided by engineer. - + Delete contact? - Изтрий контакт? + Изтрий контакт? No comment provided by engineer. - + Delete database - Изтрий базата данни + Изтрий базата данни No comment provided by engineer. - + Delete file - Изтрий файл + Изтрий файл server test step - + Delete files and media? - Изтрий файлове и медия? + Изтрий файлове и медия? No comment provided by engineer. - + Delete files for all chat profiles - Изтрий файловете за всички чат профили + Изтрий файловете за всички чат профили No comment provided by engineer. - + Delete for everyone - Изтрий за всички + Изтрий за всички chat feature - + Delete for me - Изтрий за мен + Изтрий за мен No comment provided by engineer. - + Delete group - Изтрий група + Изтрий група No comment provided by engineer. - + Delete group? - Изтрий група? + Изтрий група? No comment provided by engineer. - + Delete invitation - Изтрий поканата + Изтрий поканата No comment provided by engineer. - + Delete link - Изтрий линк + Изтрий линк No comment provided by engineer. - + Delete link? - Изтрий линк? + Изтрий линк? No comment provided by engineer. - + Delete member message? - Изтрий съобщението на члена? + Изтрий съобщението на члена? No comment provided by engineer. - + Delete message? - Изтрий съобщението? + Изтрий съобщението? No comment provided by engineer. - + Delete messages - Изтрий съобщенията + Изтрий съобщенията No comment provided by engineer. - + Delete messages after - Изтрий съобщенията след + Изтрий съобщенията след No comment provided by engineer. - + Delete old database - Изтрий старата база данни + Изтрий старата база данни No comment provided by engineer. - + Delete old database? - Изтрий старата база данни? + Изтрий старата база данни? No comment provided by engineer. - + Delete pending connection - Изтрий предстоящата връзка + Изтрий предстоящата връзка No comment provided by engineer. - + Delete pending connection? - Изтрий предстоящата връзка? + Изтрий предстоящата връзка? No comment provided by engineer. - + Delete profile - Изтрий профил + Изтрий профил No comment provided by engineer. - + Delete queue - Изтрий опашка + Изтрий опашка server test step - + Delete user profile? - Изтрий потребителския профил? + Изтрий потребителския профил? No comment provided by engineer. - + Deleted at - Изтрито на + Изтрито на No comment provided by engineer. - + Deleted at: %@ - Изтрито на: %@ + Изтрито на: %@ copied message info - + + Delivery + Доставка + No comment provided by engineer. + + Delivery receipts are disabled! - Потвърждениeто за доставка е деактивирано! + Потвърждениeто за доставка е деактивирано! No comment provided by engineer. - - Delivery receipts will be enabled for all contacts in all visible chat profiles. - No comment provided by engineer. - - - Delivery receipts will be enabled for all contacts. - No comment provided by engineer. - - + Delivery receipts! - Потвърждениe за доставка! + Потвърждениe за доставка! No comment provided by engineer. - + Description - Описание + Описание No comment provided by engineer. - + Develop - Разработване + Разработване No comment provided by engineer. - + Developer tools - Инструменти за разработчици + Инструменти за разработчици No comment provided by engineer. - + Device - Устройство + Устройство No comment provided by engineer. - + Device authentication is disabled. Turning off SimpleX Lock. - Идентификацията на устройството е деактивирано. Изключване на SimpleX заключване. + Идентификацията на устройството е деактивирано. Изключване на SimpleX заключване. No comment provided by engineer. - + Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication. - Идентификацията на устройството не е активирана. Можете да включите SimpleX заключване през Настройки, след като активирате идентификацията на устройството. + Идентификацията на устройството не е активирана. Можете да включите SimpleX заключване през Настройки, след като активирате идентификацията на устройството. No comment provided by engineer. - + Different names, avatars and transport isolation. - Различни имена, аватари и транспортна изолация. + Различни имена, аватари и транспортна изолация. No comment provided by engineer. - + Direct messages - Лични съобщения + Лични съобщения chat feature - + Direct messages between members are prohibited in this group. - Личните съобщения между членовете са забранени в тази група. + Личните съобщения между членовете са забранени в тази група. No comment provided by engineer. - + + Disable (keep overrides) + Деактивиране (запазване на промените) + No comment provided by engineer. + + Disable SimpleX Lock - Деактивирай SimpleX заключване + Деактивирай SimpleX заключване authentication reason - - Disappearing message - Изчезващо съобщение + + Disable for all + Деактивиране за всички No comment provided by engineer. - + + Disappearing message + Изчезващо съобщение + No comment provided by engineer. + + Disappearing messages - Изчезващи съобщения + Изчезващи съобщения chat feature - + Disappearing messages are prohibited in this chat. - Изчезващите съобщения са забранени в този чат. + Изчезващите съобщения са забранени в този чат. No comment provided by engineer. - + Disappearing messages are prohibited in this group. - Изчезващите съобщения са забранени в тази група. + Изчезващите съобщения са забранени в тази група. No comment provided by engineer. - + Disappears at - Изчезва в + Изчезва в No comment provided by engineer. - + Disappears at: %@ - Изчезва в: %@ + Изчезва в: %@ copied message info - + Disconnect - Прекъсни връзката + Прекъсни връзката server test step - + + Discover and join groups + No comment provided by engineer. + + Display name - Показвано Име + Показвано Име No comment provided by engineer. - + Display name: - Показвано име: + Показвано име: No comment provided by engineer. - + Do NOT use SimpleX for emergency calls. - НЕ използвайте SimpleX за спешни повиквания. + НЕ използвайте SimpleX за спешни повиквания. No comment provided by engineer. - + Do it later - Отложи + Отложи No comment provided by engineer. - + Don't create address - Не създавай адрес + Не създавай адрес No comment provided by engineer. - + + Don't enable + Не активирай + No comment provided by engineer. + + Don't show again - Не показвай отново + Не показвай отново No comment provided by engineer. - + Downgrade and open chat - Понижи версията и отвори чата + Понижи версията и отвори чата No comment provided by engineer. - + Download file - Свали файл + Свали файл server test step - + Duplicate display name! - Дублирано показвано име! + Дублирано показвано име! No comment provided by engineer. - + Duration - Продължителност + Продължителност No comment provided by engineer. - + Edit - Редактирай + Редактирай chat item action - + Edit group profile - Редактирай групов профил + Редактирай групов профил No comment provided by engineer. - + Enable - Активирай + Активирай No comment provided by engineer. - + + Enable (keep overrides) + Активиране (запазване на промените) + No comment provided by engineer. + + Enable SimpleX Lock - Активирай SimpleX заключване + Активирай SimpleX заключване authentication reason - + Enable TCP keep-alive - Активирай TCP keep-alive + Активирай TCP keep-alive No comment provided by engineer. - + Enable automatic message deletion? - Активиране на автоматично изтриване на съобщения? + Активиране на автоматично изтриване на съобщения? No comment provided by engineer. - + + Enable for all + Активиране за всички + No comment provided by engineer. + + Enable instant notifications? - Активирай незабавни известия? + Активирай незабавни известия? No comment provided by engineer. - - Enable later via Settings - No comment provided by engineer. - - + Enable lock - Активирай заключване + Активирай заключване No comment provided by engineer. - + Enable notifications - Активирай известията + Активирай известията No comment provided by engineer. - + Enable periodic notifications? - Активирай периодични известия? + Активирай периодични известия? No comment provided by engineer. - + Enable self-destruct - Активирай самоунищожение + Активирай самоунищожение No comment provided by engineer. - + Enable self-destruct passcode - Активирай kод за достъп за самоунищожение + Активирай kод за достъп за самоунищожение set passcode view - + Encrypt - Криптирай + Криптирай No comment provided by engineer. - + Encrypt database? - Криптиране на база данни? + Криптиране на база данни? No comment provided by engineer. - + + Encrypt local files + Криптирай локални файлове + No comment provided by engineer. + + + Encrypt stored files & media + No comment provided by engineer. + + Encrypted database - Криптирана база данни + Криптирана база данни No comment provided by engineer. - + Encrypted message or another event - Криптирано съобщение или друго събитие + Криптирано съобщение или друго събитие notification - + Encrypted message: database error - Криптирано съобщение: грешка в базата данни + Криптирано съобщение: грешка в базата данни notification - + Encrypted message: database migration error - Криптирано съобщение: грешка при мигрирането на база данни + Криптирано съобщение: грешка при мигрирането на база данни notification - + Encrypted message: keychain error - Криптирано съобщение: грешка в keychain + Криптирано съобщение: грешка в keychain notification - + Encrypted message: no passphrase - Криптирано съобщение: няма парола + Криптирано съобщение: няма парола notification - + Encrypted message: unexpected error - Криптирано съобщение: неочаквана грешка + Криптирано съобщение: неочаквана грешка notification - + Enter Passcode - Въведете kодa за достъп + Въведете kодa за достъп No comment provided by engineer. - + Enter correct passphrase. - Въведи правилна парола. + Въведи правилна парола. No comment provided by engineer. - + Enter passphrase… - Въведи парола… + Въведи парола… No comment provided by engineer. - + Enter password above to show! - Въведете парола по-горе, за да се покаже! + Въведете парола по-горе, за да се покаже! No comment provided by engineer. - + Enter server manually - Въведи сървъра ръчно + Въведи сървъра ръчно No comment provided by engineer. - + Enter welcome message… - Въведи съобщение при посрещане… + Въведи съобщение при посрещане… placeholder - + Enter welcome message… (optional) - Въведи съобщение при посрещане…(незадължително) + Въведи съобщение при посрещане…(незадължително) placeholder - + Error - Грешка при свързване със сървъра + Грешка при свързване със сървъра No comment provided by engineer. - + Error aborting address change - Грешка при отказване на промяна на адреса + Грешка при отказване на промяна на адреса No comment provided by engineer. - + Error accepting contact request - Грешка при приемане на заявка за контакт + Грешка при приемане на заявка за контакт No comment provided by engineer. - + Error accessing database file - Грешка при достъпа до файла с базата данни + Грешка при достъпа до файла с базата данни No comment provided by engineer. - + Error adding member(s) - Грешка при добавяне на член(ове) + Грешка при добавяне на член(ове) No comment provided by engineer. - + Error changing address - Грешка при промяна на адреса + Грешка при промяна на адреса No comment provided by engineer. - + Error changing role - Грешка при промяна на ролята + Грешка при промяна на ролята No comment provided by engineer. - + Error changing setting - Грешка при промяна на настройката + Грешка при промяна на настройката No comment provided by engineer. - + Error creating address - Грешка при създаване на адрес + Грешка при създаване на адрес No comment provided by engineer. - + Error creating group - Грешка при създаване на група + Грешка при създаване на група No comment provided by engineer. - + Error creating group link - Грешка при създаване на групов линк + Грешка при създаване на групов линк No comment provided by engineer. - + + Error creating member contact + No comment provided by engineer. + + Error creating profile! - Грешка при създаване на профил! + Грешка при създаване на профил! No comment provided by engineer. - + + Error decrypting file + Грешка при декриптирането на файла + No comment provided by engineer. + + Error deleting chat database - Грешка при изтриване на чат базата данни + Грешка при изтриване на чат базата данни No comment provided by engineer. - + Error deleting chat! - Грешка при изтриването на чата! + Грешка при изтриването на чата! No comment provided by engineer. - + Error deleting connection - Грешка при изтриване на връзката + Грешка при изтриване на връзката No comment provided by engineer. - + Error deleting contact - Грешка при изтриване на контакт + Грешка при изтриване на контакт No comment provided by engineer. - + Error deleting database - Грешка при изтриване на базата данни + Грешка при изтриване на базата данни No comment provided by engineer. - + Error deleting old database - Грешка при изтриване на старата база данни + Грешка при изтриване на старата база данни No comment provided by engineer. - + Error deleting token - Грешка при изтриването на токена + Грешка при изтриването на токена No comment provided by engineer. - + Error deleting user profile - Грешка при изтриване на потребителския профил + Грешка при изтриване на потребителския профил No comment provided by engineer. - + + Error enabling delivery receipts! + Грешка при активирането на потвърждениeто за доставка! + No comment provided by engineer. + + Error enabling notifications - Грешка при активирането на известията + Грешка при активирането на известията No comment provided by engineer. - + Error encrypting database - Грешка при криптиране на базата данни + Грешка при криптиране на базата данни No comment provided by engineer. - + Error exporting chat database - Грешка при експортиране на чат базата данни + Грешка при експортиране на чат базата данни No comment provided by engineer. - + Error importing chat database - Грешка при импортиране на чат базата данни + Грешка при импортиране на чат базата данни No comment provided by engineer. - + Error joining group - Грешка при присъединяване към група + Грешка при присъединяване към група No comment provided by engineer. - + Error loading %@ servers - Грешка при зареждане на %@ сървъри + Грешка при зареждане на %@ сървъри No comment provided by engineer. - + Error receiving file - Грешка при получаване на файл + Грешка при получаване на файл No comment provided by engineer. - + Error removing member - Грешка при отстраняване на член + Грешка при отстраняване на член No comment provided by engineer. - + Error saving %@ servers - Грешка при запазване на %@ сървъра + Грешка при запазване на %@ сървъра No comment provided by engineer. - + Error saving ICE servers - Грешка при запазване на ICE сървърите + Грешка при запазване на ICE сървърите No comment provided by engineer. - + Error saving group profile - Грешка при запазване на профила на групата + Грешка при запазване на профила на групата No comment provided by engineer. - + Error saving passcode - Грешка при запазване на кода за достъп + Грешка при запазване на кода за достъп No comment provided by engineer. - + Error saving passphrase to keychain - Грешка при запазване на парола в Кeychain + Грешка при запазване на парола в Кeychain No comment provided by engineer. - + Error saving user password - Грешка при запазване на потребителска парола + Грешка при запазване на потребителска парола No comment provided by engineer. - + Error sending email - Грешка при изпращане на имейл + Грешка при изпращане на имейл No comment provided by engineer. - + + Error sending member contact invitation + No comment provided by engineer. + + Error sending message - Грешка при изпращане на съобщение + Грешка при изпращане на съобщение No comment provided by engineer. - + + Error setting delivery receipts! + Грешка при настройването на потвърждениeто за доставка!! + No comment provided by engineer. + + Error starting chat - Грешка при стартиране на чата + Грешка при стартиране на чата No comment provided by engineer. - + Error stopping chat - Грешка при спиране на чата + Грешка при спиране на чата No comment provided by engineer. - + Error switching profile! - Грешка при смяна на профил! + Грешка при смяна на профил! No comment provided by engineer. - + Error synchronizing connection - Грешка при синхронизиране на връзката + Грешка при синхронизиране на връзката No comment provided by engineer. - + Error updating group link - Грешка при актуализиране на груповия линк + Грешка при актуализиране на груповия линк No comment provided by engineer. - + Error updating message - Грешка при актуализиране на съобщението + Грешка при актуализиране на съобщението No comment provided by engineer. - + Error updating settings - Грешка при актуализиране на настройките + Грешка при актуализиране на настройките No comment provided by engineer. - + Error updating user privacy - Грешка при актуализиране на поверителността на потребителя + Грешка при актуализиране на поверителността на потребителя No comment provided by engineer. - + Error: - Грешка: + Грешка: No comment provided by engineer. - + Error: %@ - Грешка: %@ + Грешка: %@ No comment provided by engineer. - + Error: URL is invalid - Грешка: URL адресът е невалиден + Грешка: URL адресът е невалиден No comment provided by engineer. - + Error: no database file - Грешка: няма файл с база данни + Грешка: няма файл с база данни No comment provided by engineer. - + + Even when disabled in the conversation. + Дори когато е деактивиран в разговора. + No comment provided by engineer. + + Exit without saving - Изход без запазване + Изход без запазване No comment provided by engineer. - + Export database - Експортирай база данни + Експортирай база данни No comment provided by engineer. - + Export error: - Грешка при експортиране: + Грешка при експортиране: No comment provided by engineer. - + Exported database archive. - Експортиран архив на базата данни. + Експортиран архив на базата данни. No comment provided by engineer. - - Exporting database archive... - No comment provided by engineer. - - + Exporting database archive… - Експортиране на архив на базата данни… + Експортиране на архив на базата данни… No comment provided by engineer. - + Failed to remove passphrase - Премахването на паролата е неуспешно + Премахването на паролата е неуспешно No comment provided by engineer. - + Fast and no wait until the sender is online! - Бързо и без чакане, докато подателят е онлайн! + Бързо и без чакане, докато подателят е онлайн! No comment provided by engineer. - + Favorite - Любим + Любим No comment provided by engineer. - + File will be deleted from servers. - Файлът ще бъде изтрит от сървърите. + Файлът ще бъде изтрит от сървърите. No comment provided by engineer. - + File will be received when your contact completes uploading it. - Файлът ще бъде получен, когато вашият контакт завърши качването му. + Файлът ще бъде получен, когато вашият контакт завърши качването му. No comment provided by engineer. - + File will be received when your contact is online, please wait or check later! - Файлът ще бъде получен, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно! + Файлът ще бъде получен, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно! No comment provided by engineer. - + File: %@ - Файл: %@ + Файл: %@ No comment provided by engineer. - + Files & media - Файлове и медия + Файлове и медия No comment provided by engineer. - + Files and media - Файлове и медия + Файлове и медия chat feature - + Files and media are prohibited in this group. - Файловете и медията са забранени в тази група. + Файловете и медията са забранени в тази група. No comment provided by engineer. - + Files and media prohibited! - Файловете и медията са забранени! + Файловете и медията са забранени! No comment provided by engineer. - + + Filter unread and favorite chats. + Филтрирайте непрочетените и любимите чатове. + No comment provided by engineer. + + Finally, we have them! 🚀 - Най-накрая ги имаме! 🚀 + Най-накрая ги имаме! 🚀 No comment provided by engineer. - + + Find chats faster + Намирайте чатове по-бързо + No comment provided by engineer. + + Fix - Поправи + Поправи No comment provided by engineer. - + Fix connection - Поправи връзката + Поправи връзката No comment provided by engineer. - + Fix connection? - Поправи връзката? + Поправи връзката? No comment provided by engineer. - + + Fix encryption after restoring backups. + Оправяне на криптирането след възстановяване от резервни копия. + No comment provided by engineer. + + Fix not supported by contact - Поправката не се поддържа от контакта + Поправката не се поддържа от контакта No comment provided by engineer. - + Fix not supported by group member - Поправката не се поддържа от члена на групата + Поправката не се поддържа от члена на групата No comment provided by engineer. - + For console - За конзолата + За конзолата No comment provided by engineer. - + French interface - Френски интерфейс + Френски интерфейс No comment provided by engineer. - + Full link - Цял линк + Цял линк No comment provided by engineer. - + Full name (optional) - Пълно име (незадължително) + Пълно име (незадължително) No comment provided by engineer. - + Full name: - Пълно име: + Пълно име: No comment provided by engineer. - + Fully re-implemented - work in background! - Напълно преработено - работi във фонов режим! + Напълно преработено - работi във фонов режим! No comment provided by engineer. - + Further reduced battery usage - Допълнително намален разход на батерията + Допълнително намален разход на батерията No comment provided by engineer. - + GIFs and stickers - GIF файлове и стикери + GIF файлове и стикери No comment provided by engineer. - + Group - Група + Група No comment provided by engineer. - + Group display name - Показвано име на групата + Показвано име на групата No comment provided by engineer. - + Group full name (optional) - Пълно име на групата (незадължително) + Пълно име на групата (незадължително) No comment provided by engineer. - + Group image - Групово изображение + Групово изображение No comment provided by engineer. - + Group invitation - Групова покана + Групова покана No comment provided by engineer. - + Group invitation expired - Груповата покана е изтекла + Груповата покана е изтекла No comment provided by engineer. - + Group invitation is no longer valid, it was removed by sender. - Груповата покана вече е невалидна, премахната е от подателя. + Груповата покана вече е невалидна, премахната е от подателя. No comment provided by engineer. - + Group link - Групов линк + Групов линк No comment provided by engineer. - + Group links - Групови линкове + Групови линкове No comment provided by engineer. - + Group members can add message reactions. - Членовете на групата могат да добавят реакции към съобщенията. + Членовете на групата могат да добавят реакции към съобщенията. No comment provided by engineer. - + Group members can irreversibly delete sent messages. - Членовете на групата могат необратимо да изтриват изпратените съобщения. + Членовете на групата могат необратимо да изтриват изпратените съобщения. No comment provided by engineer. - + Group members can send direct messages. - Членовете на групата могат да изпращат лични съобщения. + Членовете на групата могат да изпращат лични съобщения. No comment provided by engineer. - + Group members can send disappearing messages. - Членовете на групата могат да изпращат изчезващи съобщения. + Членовете на групата могат да изпращат изчезващи съобщения. No comment provided by engineer. - + Group members can send files and media. - Членовете на групата могат да изпращат файлове и медия. + Членовете на групата могат да изпращат файлове и медия. No comment provided by engineer. - + Group members can send voice messages. - Членовете на групата могат да изпращат гласови съобщения. + Членовете на групата могат да изпращат гласови съобщения. No comment provided by engineer. - + Group message: - Групово съобщение: + Групово съобщение: notification - + Group moderation - Групово модериране + Групово модериране No comment provided by engineer. - + Group preferences - Групови настройки + Групови настройки No comment provided by engineer. - + Group profile - Групов профил + Групов профил No comment provided by engineer. - + Group profile is stored on members' devices, not on the servers. - Груповият профил се съхранява на устройствата на членовете, а не на сървърите. + Груповият профил се съхранява на устройствата на членовете, а не на сървърите. No comment provided by engineer. - + Group welcome message - Съобщение при посрещане в групата + Съобщение при посрещане в групата No comment provided by engineer. - + Group will be deleted for all members - this cannot be undone! - Групата ще бъде изтрита за всички членове - това не може да бъде отменено! + Групата ще бъде изтрита за всички членове - това не може да бъде отменено! No comment provided by engineer. - + Group will be deleted for you - this cannot be undone! - Групата ще бъде изтрита за вас - това не може да бъде отменено! + Групата ще бъде изтрита за вас - това не може да бъде отменено! No comment provided by engineer. - + Help - Помощ + Помощ No comment provided by engineer. - + Hidden - Скрит + Скрит No comment provided by engineer. - + Hidden chat profiles - Скрити чат профили + Скрити чат профили No comment provided by engineer. - + Hidden profile password - Парола за скрит профил + Парола за скрит профил No comment provided by engineer. - + Hide - Скрий + Скрий chat item action - + Hide app screen in the recent apps. - Скриване на екрана на приложението в изгледа на скоро отворнените приложения. + Скриване на екрана на приложението в изгледа на скоро отворнените приложения. No comment provided by engineer. - + Hide profile - Скрий профила + Скрий профила No comment provided by engineer. - + Hide: - Скрий: + Скрий: No comment provided by engineer. - + History - История - copied message info + История + No comment provided by engineer. - + How SimpleX works - Как работи SimpleX + Как работи SimpleX No comment provided by engineer. - + How it works - Как работи + Как работи No comment provided by engineer. - + How to - Информация + Информация No comment provided by engineer. - + How to use it - Как се използва + Как се използва No comment provided by engineer. - + How to use your servers - Как да използвате вашите сървъри + Как да използвате вашите сървъри No comment provided by engineer. - + ICE servers (one per line) - ICE сървъри (по един на ред) + ICE сървъри (по един на ред) No comment provided by engineer. - + If you can't meet in person, show QR code in a video call, or share the link. - Ако не можете да се срещнете лично, покажете QR код във видеоразговора или споделете линка. + Ако не можете да се срещнете лично, покажете QR код във видеоразговора или споделете линка. 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 код във видеообаждането** или вашият контакт може да сподели линк за покана. + Ако не можете да се срещнете на живо, можете да **сканирате QR код във видеообаждането** или вашият контакт може да сподели линк за покана. No comment provided by engineer. - + If you enter this passcode when opening the app, all app data will be irreversibly removed! - Ако въведете този kод за достъп, когато отваряте приложението, всички данни от приложението ще бъдат необратимо изтрити! + Ако въведете този kод за достъп, когато отваряте приложението, всички данни от приложението ще бъдат необратимо изтрити! No comment provided by engineer. - + If you enter your self-destruct passcode while opening the app: - Ако въведете kодa за достъп за самоунищожение, докато отваряте приложението: + Ако въведете kодa за достъп за самоунищожение, докато отваряте приложението: No comment provided by engineer. - + If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app). - Ако трябва да използвате чата сега, докоснете **Отложи** отдолу (ще ви бъде предложено да мигрирате базата данни, когато рестартирате приложението). + Ако трябва да използвате чата сега, докоснете **Отложи** отдолу (ще ви бъде предложено да мигрирате базата данни, когато рестартирате приложението). No comment provided by engineer. - + Ignore - Игнорирай + Игнорирай No comment provided by engineer. - + Image will be received when your contact completes uploading it. - Изображението ще бъде получено, когато вашият контакт завърши качването му. + Изображението ще бъде получено, когато вашият контакт завърши качването му. No comment provided by engineer. - + Image will be received when your contact is online, please wait or check later! - Изображението ще бъде получено, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно! + Изображението ще бъде получено, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно! No comment provided by engineer. - + Immediately - Веднага + Веднага No comment provided by engineer. - + Immune to spam and abuse - Защитен от спам и злоупотреби + Защитен от спам и злоупотреби No comment provided by engineer. - + Import - Импортиране + Импортиране No comment provided by engineer. - + Import chat database? - Импортиране на чат база данни? + Импортиране на чат база данни? No comment provided by engineer. - + Import database - Импортиране на база данни + Импортиране на база данни No comment provided by engineer. - + Improved privacy and security - Подобрена поверителност и сигурност + Подобрена поверителност и сигурност No comment provided by engineer. - + Improved server configuration - Подобрена конфигурация на сървъра + Подобрена конфигурация на сървъра No comment provided by engineer. - + In reply to - В отговор на - copied message info + В отговор на + No comment provided by engineer. - + Incognito - Инкогнито + Инкогнито No comment provided by engineer. - + Incognito mode - Режим инкогнито + Режим инкогнито No comment provided by engineer. - - Incognito mode is not supported here - your main profile will be sent to group members + + Incognito mode protects your privacy by using a new random profile for each contact. + Режимът инкогнито защитава вашата поверителност, като използва нов автоматично генериран профил за всеки контакт. No comment provided by engineer. - - Incognito mode protects the privacy of your main profile name and image — for each new contact a new random profile is created. - No comment provided by engineer. - - + Incoming audio call - Входящо аудио повикване + Входящо аудио повикване notification - + Incoming call - Входящо повикване + Входящо повикване notification - + Incoming video call - Входящо видео повикване + Входящо видео повикване notification - + Incompatible database version - Несъвместима версия на базата данни + Несъвместима версия на базата данни No comment provided by engineer. - + Incorrect passcode - Неправилен kод за достъп + Неправилен kод за достъп PIN entry - + Incorrect security code! - Неправилен код за сигурност! + Неправилен код за сигурност! No comment provided by engineer. - + Info - Информация + Информация chat item action - + Initial role - Първоначална роля + Първоначална роля 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) + Инсталирайте [SimpleX Chat за терминал](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. - + Instant push notifications will be hidden! - Незабавните push известия ще бъдат скрити! + Незабавните push известия ще бъдат скрити! No comment provided by engineer. - + Instantly - Мигновено + Мигновено No comment provided by engineer. - + Interface - Интерфейс + Интерфейс No comment provided by engineer. - + Invalid connection link - Невалиден линк за връзка + Невалиден линк за връзка No comment provided by engineer. - + Invalid server address! - Невалиден адрес на сървъра! + Невалиден адрес на сървъра! No comment provided by engineer. - + + Invalid status + Невалиден статус + item status text + + Invitation expired! - Поканата е изтекла! + Поканата е изтекла! No comment provided by engineer. - + Invite friends - Покани приятели + Покани приятели No comment provided by engineer. - + Invite members - Покани членове + Покани членове No comment provided by engineer. - + Invite to group - Покани в групата + Покани в групата No comment provided by engineer. - + Irreversible message deletion - Необратимо изтриване на съобщение + Необратимо изтриване на съобщение No comment provided by engineer. - + Irreversible message deletion is prohibited in this chat. - Необратимото изтриване на съобщения е забранено в този чат. + Необратимото изтриване на съобщения е забранено в този чат. No comment provided by engineer. - + Irreversible message deletion is prohibited in this group. - Необратимото изтриване на съобщения е забранено в тази група. + Необратимото изтриване на съобщения е забранено в тази група. No comment provided by engineer. - + It allows having many anonymous connections without any shared data between them in a single chat profile. - Позволява да имате много анонимни връзки без споделени данни между тях в един чат профил . + Позволява да имате много анонимни връзки без споделени данни между тях в един чат профил . No comment provided by engineer. - + It can happen when you or your connection used the old database backup. - Това може да се случи, когато вие или вашата връзка използвате старо резервно копие на базата данни. + Това може да се случи, когато вие или вашата връзка използвате старо резервно копие на базата данни. No comment provided by engineer. - + It can happen when: 1. The messages expired in the sending client after 2 days or on the server after 30 days. 2. Message decryption failed, because you or your contact used old database backup. 3. The connection was compromised. - Това може да се случи, когато: + Това може да се случи, когато: 1. Времето за пазене на съобщенията е изтекло - в изпращащия клиент е 2 дена а на сървъра е 30. 2. Декриптирането на съобщението е неуспешно, защото вие или вашият контакт сте използвали старо копие на базата данни. 3. Връзката е била компрометирана. No comment provided by engineer. - + It seems like you are already connected via this link. If it is not the case, there was an error (%@). - Изглежда, че вече сте свързани чрез този линк. Ако не е така, има грешка (%@). + Изглежда, че вече сте свързани чрез този линк. Ако не е така, има грешка (%@). No comment provided by engineer. - + Italian interface - Италиански интерфейс + Италиански интерфейс No comment provided by engineer. - + Japanese interface - Японски интерфейс + Японски интерфейс No comment provided by engineer. - + Join - Присъединяване + Присъединяване No comment provided by engineer. - + Join group - Влез в групата + Влез в групата No comment provided by engineer. - + Join incognito - Влез инкогнито + Влез инкогнито No comment provided by engineer. - + Joining group - Присъединяване към групата + Присъединяване към групата No comment provided by engineer. - + + Keep your connections + Запазете връзките си + No comment provided by engineer. + + KeyChain error - KeyChain грешка + KeyChain грешка No comment provided by engineer. - + Keychain error - Keychain грешка + Keychain грешка No comment provided by engineer. - + LIVE - НА ЖИВО + НА ЖИВО No comment provided by engineer. - + Large file! - Голям файл! + Голям файл! No comment provided by engineer. - + Learn more - Научете повече + Научете повече No comment provided by engineer. - + Leave - Напусни + Напусни No comment provided by engineer. - + Leave group - Напусни групата + Напусни групата No comment provided by engineer. - + Leave group? - Напусни групата? + Напусни групата? No comment provided by engineer. - + Let's talk in SimpleX Chat - Нека да поговорим в SimpleX Chat + Нека да поговорим в SimpleX Chat email subject - + Light - Светла + Светла No comment provided by engineer. - + Limitations - Ограничения + Ограничения No comment provided by engineer. - + Live message! - Съобщение на живо! + Съобщение на живо! No comment provided by engineer. - + Live messages - Съобщения на живо + Съобщения на живо No comment provided by engineer. - + Local name - Локално име + Локално име No comment provided by engineer. - + Local profile data only - Само данни за локален профил + Само данни за локален профил No comment provided by engineer. - + Lock after - Заключване след + Заключване след No comment provided by engineer. - + Lock mode - Режим на заключване + Режим на заключване No comment provided by engineer. - + Make a private connection - Добави поверителна връзка + Добави поверителна връзка No comment provided by engineer. - + + Make one message disappear + Накарайте едно съобщение да изчезне + No comment provided by engineer. + + Make profile private! - Направи профила поверителен! + Направи профила поверителен! No comment provided by engineer. - + Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Уверете се, че %@ сървърните адреси са в правилен формат, разделени на редове и не се дублират (%@). + Уверете се, че %@ сървърните адреси са в правилен формат, разделени на редове и не се дублират (%@). No comment provided by engineer. - + Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. - Уверете се, че адресите на WebRTC ICE сървъра са в правилен формат, разделени на редове и не са дублирани. + Уверете се, че адресите на WebRTC ICE сървъра са в правилен формат, разделени на редове и не са дублирани. No comment provided by engineer. - + Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Много хора попитаха: *ако SimpleX няма потребителски идентификатори, как може да доставя съобщения?* + Много хора попитаха: *ако SimpleX няма потребителски идентификатори, как може да доставя съобщения?* No comment provided by engineer. - + Mark deleted for everyone - Маркирай като изтрито за всички + Маркирай като изтрито за всички No comment provided by engineer. - + Mark read - Маркирай като прочетено + Маркирай като прочетено No comment provided by engineer. - + Mark verified - Маркирай като проверено + Маркирай като проверено No comment provided by engineer. - + Markdown in messages - Форматиране на съобщения + Форматиране на съобщения No comment provided by engineer. - + Max 30 seconds, received instantly. - Макс. 30 секунди, получено незабавно. + Макс. 30 секунди, получено незабавно. No comment provided by engineer. - + Member - Член + Член No comment provided by engineer. - + Member role will be changed to "%@". All group members will be notified. - Ролята на члена ще бъде променена на "%@". Всички членове на групата ще бъдат уведомени. + Ролята на члена ще бъде променена на "%@". Всички членове на групата ще бъдат уведомени. No comment provided by engineer. - + Member role will be changed to "%@". The member will receive a new invitation. - Ролята на члена ще бъде променена на "%@". Членът ще получи нова покана. + Ролята на члена ще бъде променена на "%@". Членът ще получи нова покана. No comment provided by engineer. - + Member will be removed from group - this cannot be undone! - Членът ще бъде премахнат от групата - това не може да бъде отменено! + Членът ще бъде премахнат от групата - това не може да бъде отменено! No comment provided by engineer. - + Message delivery error - Грешка при доставката на съобщението + Грешка при доставката на съобщението + item status text + + + Message delivery receipts! + Потвърждениe за доставка на съобщения! No comment provided by engineer. - + Message draft - Чернова на съобщение + Чернова на съобщение No comment provided by engineer. - + Message reactions - Реакции на съобщения + Реакции на съобщения chat feature - + Message reactions are prohibited in this chat. - Реакциите на съобщения са забранени в този чат. + Реакциите на съобщения са забранени в този чат. No comment provided by engineer. - + Message reactions are prohibited in this group. - Реакциите на съобщения са забранени в тази група. + Реакциите на съобщения са забранени в тази група. No comment provided by engineer. - + Message text - Текст на съобщението + Текст на съобщението No comment provided by engineer. - + Messages - Съобщения + Съобщения No comment provided by engineer. - + Messages & files - Съобщения и файлове + Съобщения и файлове No comment provided by engineer. - - Migrating database archive... - No comment provided by engineer. - - + Migrating database archive… - Архивът на базата данни се мигрира… + Архивът на базата данни се мигрира… No comment provided by engineer. - + Migration error: - Грешка при мигриране: + Грешка при мигриране: No comment provided by engineer. - + Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). - Мигрирането е неуспешно. Докоснете **Пропускане** по-долу, за да продължите да използвате текущата база данни. Моля, докладвайте проблема на разработчиците на приложението чрез чат или имейл [chat@simplex.chat](mailto:chat@simplex.chat). + Мигрирането е неуспешно. Докоснете **Пропускане** по-долу, за да продължите да използвате текущата база данни. Моля, докладвайте проблема на разработчиците на приложението чрез чат или имейл [chat@simplex.chat](mailto:chat@simplex.chat). No comment provided by engineer. - + Migration is completed - Миграцията е завършена + Миграцията е завършена No comment provided by engineer. - + Migrations: %@ - Миграции: %@ + Миграции: %@ No comment provided by engineer. - + Moderate - Модерирай + Модерирай chat item action - + Moderated at - Модерирано в + Модерирано в No comment provided by engineer. - + Moderated at: %@ - Модерирано в: %@ + Модерирано в: %@ copied message info - + More improvements are coming soon! - Очаквайте скоро още подобрения! + Очаквайте скоро още подобрения! No comment provided by engineer. - + + Most likely this connection is deleted. + Най-вероятно тази връзка е изтрита. + item status description + + Most likely this contact has deleted the connection with you. - Най-вероятно този контакт е изтрил връзката с вас. + Най-вероятно този контакт е изтрил връзката с вас. No comment provided by engineer. - + Multiple chat profiles - Множество профили за чат + Множество профили за чат No comment provided by engineer. - + Mute - Без звук + Без звук No comment provided by engineer. - + Muted when inactive! - Без звук при неактивност! + Без звук при неактивност! No comment provided by engineer. - + Name - Име + Име No comment provided by engineer. - + Network & servers - Мрежа и сървъри + Мрежа и сървъри No comment provided by engineer. - + Network settings - Мрежови настройки + Мрежови настройки No comment provided by engineer. - + Network status - Състояние на мрежата + Състояние на мрежата No comment provided by engineer. - + New Passcode - Нов kод за достъп + Нов kод за достъп No comment provided by engineer. - + New contact request - Нова заявка за контакт + Нова заявка за контакт notification - + New contact: - Нов контакт: + Нов контакт: notification - + New database archive - Нов архив на база данни + Нов архив на база данни No comment provided by engineer. - + + New desktop app! + No comment provided by engineer. + + New display name - Ново показвано име + Ново показвано име No comment provided by engineer. - + New in %@ - Ново в %@ + Ново в %@ No comment provided by engineer. - + New member role - Нова членска роля + Нова членска роля No comment provided by engineer. - + New message - Ново съобщение + Ново съобщение notification - + New passphrase… - Нова парола… + Нова парола… No comment provided by engineer. - + No - Не + Не No comment provided by engineer. - + No app password - Приложението няма kод за достъп + Приложението няма kод за достъп Authentication unavailable - + No contacts selected - Няма избрани контакти + Няма избрани контакти No comment provided by engineer. - + No contacts to add - Няма контакти за добавяне + Няма контакти за добавяне No comment provided by engineer. - + + No delivery information + Няма информация за доставката + No comment provided by engineer. + + No device token! - Няма токен за устройство! + Няма токен за устройство! No comment provided by engineer. - + No filtered chats - Няма филтрирани чатове + Няма филтрирани чатове No comment provided by engineer. - + Group not found! - Групата не е намерена! + Групата не е намерена! No comment provided by engineer. - + No history - Няма история + Няма история No comment provided by engineer. - + No permission to record voice message - Няма разрешение за запис на гласово съобщение + Няма разрешение за запис на гласово съобщение No comment provided by engineer. - + No received or sent files - Няма получени или изпратени файлове + Няма получени или изпратени файлове No comment provided by engineer. - + Notifications - Известия + Известия No comment provided by engineer. - + Notifications are disabled! - Известията са деактивирани! + Известията са деактивирани! No comment provided by engineer. - + Now admins can: - delete members' messages. - disable members ("observer" role) - Сега администраторите могат: + Сега администраторите могат: - да изтриват съобщения на членове. - да деактивират членове (роля "наблюдател") No comment provided by engineer. - + Off - Изключено + Изключено No comment provided by engineer. - + Off (Local) - Изключено (Локално) + Изключено (Локално) No comment provided by engineer. - + Ok - Ок + Ок No comment provided by engineer. - + Old database - Стара база данни + Стара база данни No comment provided by engineer. - + Old database archive - Стар архив на база данни + Стар архив на база данни No comment provided by engineer. - + One-time invitation link - Линк за еднократна покана + Линк за еднократна покана No comment provided by engineer. - + Onion hosts will be required for connection. Requires enabling VPN. - За свързване ще са необходими Onion хостове. Изисква се активиране на VPN. + За свързване ще са необходими Onion хостове. Изисква се активиране на VPN. No comment provided by engineer. - + Onion hosts will be used when available. Requires enabling VPN. - Ще се използват Onion хостове, когато са налични. Изисква се активиране на VPN. + Ще се използват Onion хостове, когато са налични. Изисква се активиране на VPN. No comment provided by engineer. - + Onion hosts will not be used. - Няма се използват Onion хостове. + Няма се използват Onion хостове. No comment provided by engineer. - + Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с **двуслойно криптиране от край до край**. + Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с **двуслойно криптиране от край до край**. No comment provided by engineer. - + Only group owners can change group preferences. - Само собствениците на групата могат да променят груповите настройки. + Само собствениците на групата могат да променят груповите настройки. No comment provided by engineer. - + Only group owners can enable files and media. - Само собствениците на групата могат да активират файлове и медията. + Само собствениците на групата могат да активират файлове и медията. No comment provided by engineer. - + Only group owners can enable voice messages. - Само собствениците на групата могат да активират гласови съобщения. + Само собствениците на групата могат да активират гласови съобщения. No comment provided by engineer. - + Only you can add message reactions. - Само вие можете да добавяте реакции на съобщенията. + Само вие можете да добавяте реакции на съобщенията. No comment provided by engineer. - + Only you can irreversibly delete messages (your contact can mark them for deletion). - Само вие можете необратимо да изтриете съобщения (вашият контакт може да ги маркира за изтриване). + Само вие можете необратимо да изтриете съобщения (вашият контакт може да ги маркира за изтриване). No comment provided by engineer. - + Only you can make calls. - Само вие можете да извършвате разговори. + Само вие можете да извършвате разговори. No comment provided by engineer. - + Only you can send disappearing messages. - Само вие можете да изпращате изчезващи съобщения. + Само вие можете да изпращате изчезващи съобщения. No comment provided by engineer. - + Only you can send voice messages. - Само вие можете да изпращате гласови съобщения. + Само вие можете да изпращате гласови съобщения. No comment provided by engineer. - + Only your contact can add message reactions. - Само вашият контакт може да добавя реакции на съобщенията. + Само вашият контакт може да добавя реакции на съобщенията. No comment provided by engineer. - + Only your contact can irreversibly delete messages (you can mark them for deletion). - Само вашият контакт може необратимо да изтрие съобщения (можете да ги маркирате за изтриване). + Само вашият контакт може необратимо да изтрие съобщения (можете да ги маркирате за изтриване). No comment provided by engineer. - + Only your contact can make calls. - Само вашият контакт може да извършва разговори. + Само вашият контакт може да извършва разговори. No comment provided by engineer. - + Only your contact can send disappearing messages. - Само вашият контакт може да изпраща изчезващи съобщения. + Само вашият контакт може да изпраща изчезващи съобщения. No comment provided by engineer. - + Only your contact can send voice messages. - Само вашият контакт може да изпраща гласови съобщения. + Само вашият контакт може да изпраща гласови съобщения. No comment provided by engineer. - + + Open + No comment provided by engineer. + + Open Settings - Отвори настройки + Отвори настройки No comment provided by engineer. - + Open chat - Отвори чат + Отвори чат No comment provided by engineer. - + Open chat console - Отвори конзолата + Отвори конзолата authentication reason - + Open user profiles - Отвори потребителските профили + Отвори потребителските профили authentication reason - + Open-source protocol and code – anybody can run the servers. - Протокол и код с отворен код – всеки може да оперира собствени сървъри. + Протокол и код с отворен код – всеки може да оперира собствени сървъри. No comment provided by engineer. - + Opening database… - Отваряне на база данни… + Отваряне на база данни… No comment provided by engineer. - + Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red. - Отварянето на линка в браузъра може да намали поверителността и сигурността на връзката. Несигурните SimpleX линкове ще бъдат червени. + Отварянето на линка в браузъра може да намали поверителността и сигурността на връзката. Несигурните SimpleX линкове ще бъдат червени. No comment provided by engineer. - + PING count - PING бройка + PING бройка No comment provided by engineer. - + PING interval - PING интервал + PING интервал No comment provided by engineer. - + Passcode - Код за достъп + Код за достъп No comment provided by engineer. - + Passcode changed! - Кодът за достъп е променен! + Кодът за достъп е променен! No comment provided by engineer. - + Passcode entry - Въвеждане на код за достъп + Въвеждане на код за достъп No comment provided by engineer. - + Passcode not changed! - Кодът за достъп не е променен! + Кодът за достъп не е променен! No comment provided by engineer. - + Passcode set! - Кодът за достъп е зададен! + Кодът за достъп е зададен! No comment provided by engineer. - + Password to show - Парола за показване + Парола за показване No comment provided by engineer. - + Paste - Постави + Постави No comment provided by engineer. - + Paste image - Постави изображение + Постави изображение No comment provided by engineer. - + Paste received link - Постави получения линк + Постави получения линк No comment provided by engineer. - - Paste the link you received into the box below to connect with your contact. - No comment provided by engineer. + + Paste the link you received to connect with your contact. + Поставете линка, който сте получили, за да се свържете с вашия контакт. + placeholder - + People can connect to you only via the links you share. - Хората могат да се свържат с вас само чрез ликовете, които споделяте. + Хората могат да се свържат с вас само чрез ликовете, които споделяте. No comment provided by engineer. - + Periodically - Периодично + Периодично No comment provided by engineer. - + Permanent decryption error - Постоянна грешка при декриптиране + Постоянна грешка при декриптиране message decrypt error item - + Please ask your contact to enable sending voice messages. - Моля, попитайте вашия контакт, за да активирате изпращане на гласови съобщения. + Моля, попитайте вашия контакт, за да активирате изпращане на гласови съобщения. 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 with %@ and try again. - Моля, проверете мрежовата си връзка с %@ и опитайте отново. + Моля, проверете мрежовата си връзка с %@ и опитайте отново. No comment provided by engineer. - + Please check yours and your contact preferences. - Моля, проверете вашите настройки и тези вашия за контакт. + Моля, проверете вашите настройки и тези вашия за контакт. No comment provided by engineer. - + Please contact group admin. - Моля, свържете се с груповия администартор. + Моля, свържете се с груповия администартор. No comment provided by engineer. - + Please enter correct current passphrase. - Моля, въведете правилната текуща парола. + Моля, въведете правилната текуща парола. No comment provided by engineer. - + Please enter the previous password after restoring database backup. This action can not be undone. - Моля, въведете предишната парола след възстановяване на резервното копие на базата данни. Това действие не може да бъде отменено. + Моля, въведете предишната парола след възстановяване на резервното копие на базата данни. Това действие не може да бъде отменено. No comment provided by engineer. - + Please remember or store it securely - there is no way to recover a lost passcode! - Моля, запомнете го или го съхранявайте на сигурно място - няма начин да възстановите изгубен код за достъп! + Моля, запомнете го или го съхранявайте на сигурно място - няма начин да възстановите изгубен код за достъп! No comment provided by engineer. - + Please report it to the developers. - Моля, докладвайте го на разработчиците. + Моля, докладвайте го на разработчиците. No comment provided by engineer. - + Please restart the app and migrate the database to enable push notifications. - Моля, рестартирайте приложението и мигрирайте базата данни, за да активирате push известия. + Моля, рестартирайте приложението и мигрирайте базата данни, за да активирате push известия. No comment provided by engineer. - + Please store passphrase securely, you will NOT be able to access chat if you lose it. - Моля, съхранявайте паролата на сигурно място, НЯМА да имате достъп до чата, ако я загубите. + Моля, съхранявайте паролата на сигурно място, НЯМА да имате достъп до чата, ако я загубите. No comment provided by engineer. - + Please store passphrase securely, you will NOT be able to change it if you lose it. - Моля, съхранявайте паролата на сигурно място, НЯМА да можете да я промените, ако я загубите. + Моля, съхранявайте паролата на сигурно място, НЯМА да можете да я промените, ако я загубите. No comment provided by engineer. - + Polish interface - Полски интерфейс + Полски интерфейс No comment provided by engineer. - + Possibly, certificate fingerprint in server address is incorrect - Въжможно е пръстовият отпечатък на сертификата в адреса на сървъра да е неправилен + Въжможно е пръстовият отпечатък на сертификата в адреса на сървъра да е неправилен server test error - + Preserve the last message draft, with attachments. - Запазете последната чернова на съобщението с прикачени файлове. + Запазете последната чернова на съобщението с прикачени файлове. No comment provided by engineer. - + Preset server - Предварително зададен сървър + Предварително зададен сървър No comment provided by engineer. - + Preset server address - Предварително зададен адрес на сървъра + Предварително зададен адрес на сървъра No comment provided by engineer. - + Preview - Визуализация + Визуализация No comment provided by engineer. - + Privacy & security - Поверителност и сигурност + Поверителност и сигурност No comment provided by engineer. - + Privacy redefined - Поверителността преосмислена + Поверителността преосмислена No comment provided by engineer. - + Private filenames - Поверителни имена на файлове + Поверителни имена на файлове No comment provided by engineer. - + Profile and server connections - Профилни и сървърни връзки + Профилни и сървърни връзки No comment provided by engineer. - + Profile image - Профилно изображение + Профилно изображение No comment provided by engineer. - + Profile password - Профилна парола + Профилна парола No comment provided by engineer. - + Profile update will be sent to your contacts. - Актуализацията на профила ще бъде изпратена до вашите контакти. + Актуализацията на профила ще бъде изпратена до вашите контакти. No comment provided by engineer. - + Prohibit audio/video calls. - Забрани аудио/видео разговорите. + Забрани аудио/видео разговорите. No comment provided by engineer. - + Prohibit irreversible message deletion. - Забрани необратимото изтриване на съобщения. + Забрани необратимото изтриване на съобщения. No comment provided by engineer. - + Prohibit message reactions. - Забрани реакциите на съобщенията. + Забрани реакциите на съобщенията. No comment provided by engineer. - + Prohibit messages reactions. - Забрани реакциите на съобщенията. + Забрани реакциите на съобщенията. No comment provided by engineer. - + Prohibit sending direct messages to members. - Забрани изпращането на лични съобщения до членовете. + Забрани изпращането на лични съобщения до членовете. No comment provided by engineer. - + Prohibit sending disappearing messages. - Забрани изпращането на изчезващи съобщения. + Забрани изпращането на изчезващи съобщения. No comment provided by engineer. - + Prohibit sending files and media. - Забрани изпращането на файлове и медия. + Забрани изпращането на файлове и медия. No comment provided by engineer. - + Prohibit sending voice messages. - Забрани изпращането на гласови съобщения. + Забрани изпращането на гласови съобщения. No comment provided by engineer. - + Protect app screen - Защити екрана на приложението + Защити екрана на приложението No comment provided by engineer. - + Protect your chat profiles with a password! - Защитете чат профилите с парола! + Защитете чат профилите с парола! No comment provided by engineer. - + Protocol timeout - Време за изчакване на протокола + Време за изчакване на протокола No comment provided by engineer. - + Protocol timeout per KB - Време за изчакване на протокола за KB + Време за изчакване на протокола за KB No comment provided by engineer. - + Push notifications - Push известия + Push известия No comment provided by engineer. - + Rate the app - Оценете приложението + Оценете приложението No comment provided by engineer. - + React… - Реагирай… + Реагирай… chat item menu - + Read - Прочетено + Прочетено No comment provided by engineer. - + Read more - Прочетете още + Прочетете още No comment provided by engineer. - + Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). + Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). No comment provided by engineer. - + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). - Прочетете повече в [Ръководство на потребителя](https://simplex.chat/docs/guide/readme.html#connect-to-friends). + Прочетете повече в [Ръководство на потребителя](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - + Read more in our GitHub repository. - Прочетете повече в нашето хранилище в GitHub. + Прочетете повече в нашето хранилище в GitHub. No comment provided by engineer. - + Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). - Прочетете повече в нашето [GitHub хранилище](https://github.com/simplex-chat/simplex-chat#readme). + Прочетете повече в нашето [GitHub хранилище](https://github.com/simplex-chat/simplex-chat#readme). No comment provided by engineer. - + + Receipts are disabled + Потвърждениeто за доставка е деактивирано + No comment provided by engineer. + + Received at - Получено в + Получено в No comment provided by engineer. - + Received at: %@ - Получено в: %@ + Получено в: %@ copied message info - + Received file event - Събитие за получен файл + Събитие за получен файл notification - + Received message - Получено съобщение + Получено съобщение message info title - + Receiving address will be changed to a different server. Address change will complete after sender comes online. - Получаващият адрес ще бъде променен към друг сървър. Промяната на адреса ще завърши, след като подателят е онлайн. + Получаващият адрес ще бъде променен към друг сървър. Промяната на адреса ще завърши, след като подателят е онлайн. No comment provided by engineer. - + Receiving file will be stopped. - Получаващият се файл ще бъде спрян. + Получаващият се файл ще бъде спрян. No comment provided by engineer. - + Receiving via - Получаване чрез + Получаване чрез No comment provided by engineer. - + Recipients see updates as you type them. - Получателите виждат актуализации, докато ги въвеждате. + Получателите виждат актуализации, докато ги въвеждате. No comment provided by engineer. - + Reconnect all connected servers to force message delivery. It uses additional traffic. - Повторно се свържете с всички свързани сървъри, за да принудите доставката на съобщенията. Използва се допълнителен трафик. + Повторно се свържете с всички свързани сървъри, за да принудите доставката на съобщенията. Използва се допълнителен трафик. No comment provided by engineer. - + Reconnect servers? - Повторно свърване със сървърите? + Повторно свърване със сървърите? No comment provided by engineer. - + Record updated at - Записът е актуализиран на + Записът е актуализиран на No comment provided by engineer. - + Record updated at: %@ - Записът е актуализиран на: %@ + Записът е актуализиран на: %@ copied message info - + Reduced battery usage - Намалена консумация на батерията + Намалена консумация на батерията No comment provided by engineer. - + Reject - Отхвърляне + Отхвърляне reject incoming call via notification - - Reject contact (sender NOT notified) + + Reject (sender NOT notified) + Отхвърляне (подателят НЕ бива уведомен) No comment provided by engineer. - + Reject contact request - Отхвърли заявката за контакт + Отхвърли заявката за контакт No comment provided by engineer. - + Relay server is only used if necessary. Another party can observe your IP address. - Реле сървър се използва само ако е необходимо. Друга страна може да наблюдава вашия IP адрес. + Реле сървър се използва само ако е необходимо. Друга страна може да наблюдава вашия IP адрес. No comment provided by engineer. - + Relay server protects your IP address, but it can observe the duration of the call. - Relay сървърът защитава вашия IP адрес, но може да наблюдава продължителността на разговора. + Relay сървърът защитава вашия IP адрес, но може да наблюдава продължителността на разговора. No comment provided by engineer. - + Remove - Премахване + Премахване No comment provided by engineer. - + Remove member - Острани член + Острани член No comment provided by engineer. - + Remove member? - Острани член? + Острани член? No comment provided by engineer. - + Remove passphrase from keychain? - Премахване на паролата от keychain? + Премахване на паролата от keychain? No comment provided by engineer. - + Renegotiate - Предоговоряне + Предоговоряне No comment provided by engineer. - + Renegotiate encryption - Предоговори криптирането + Предоговори криптирането No comment provided by engineer. - + Renegotiate encryption? - Предоговори криптирането? + Предоговори криптирането? No comment provided by engineer. - + Reply - Отговори + Отговори chat item action - + Required - Задължително + Задължително No comment provided by engineer. - + Reset - Нулиране + Нулиране No comment provided by engineer. - + Reset colors - Нулирай цветовете + Нулирай цветовете No comment provided by engineer. - + Reset to defaults - Възстановяване на настройките по подразбиране + Възстановяване на настройките по подразбиране No comment provided by engineer. - + Restart the app to create a new chat profile - Рестартирайте приложението, за да създадете нов чат профил + Рестартирайте приложението, за да създадете нов чат профил No comment provided by engineer. - + Restart the app to use imported chat database - Рестартирайте приложението, за да използвате импортирана чат база данни + Рестартирайте приложението, за да използвате импортирана чат база данни No comment provided by engineer. - + Restore - Възстанови + Възстанови No comment provided by engineer. - + Restore database backup - Възстанови резервно копие на база данни + Възстанови резервно копие на база данни No comment provided by engineer. - + Restore database backup? - Възстанови резервно копие на база данни? + Възстанови резервно копие на база данни? No comment provided by engineer. - + Restore database error - Грешка при възстановяване на базата данни + Грешка при възстановяване на базата данни No comment provided by engineer. - + Reveal - Покажи + Покажи chat item action - + Revert - Отмени промените + Отмени промените No comment provided by engineer. - + Revoke - Отзови + Отзови No comment provided by engineer. - + Revoke file - Отзови файл + Отзови файл cancel file action - + Revoke file? - Отзови файл? + Отзови файл? No comment provided by engineer. - + Role - Роля + Роля No comment provided by engineer. - + Run chat - Стартиране на чат + Стартиране на чат No comment provided by engineer. - + SMP servers - SMP сървъри + SMP сървъри No comment provided by engineer. - + Save - Запази + Запази chat item action - + Save (and notify contacts) - Запази (и уведоми контактите) + Запази (и уведоми контактите) No comment provided by engineer. - + Save and notify contact - Запази и уведоми контакта + Запази и уведоми контакта No comment provided by engineer. - + Save and notify group members - Запази и уведоми членовете на групата + Запази и уведоми членовете на групата No comment provided by engineer. - + Save and update group profile - Запази и актуализирай профила на групата + Запази и актуализирай профила на групата No comment provided by engineer. - + Save archive - Запази архив + Запази архив No comment provided by engineer. - + Save auto-accept settings - Запази настройките за автоматично приемане + Запази настройките за автоматично приемане No comment provided by engineer. - + Save group profile - Запази профила на групата + Запази профила на групата No comment provided by engineer. - + Save passphrase and open chat - Запази паролата и отвори чата + Запази паролата и отвори чата No comment provided by engineer. - + Save passphrase in Keychain - Запази паролата в Keychain + Запази паролата в Keychain No comment provided by engineer. - + Save preferences? - Запази настройките? + Запази настройките? No comment provided by engineer. - + Save profile password - Запази паролата на профила + Запази паролата на профила No comment provided by engineer. - + Save servers - Запази сървърите + Запази сървърите No comment provided by engineer. - + Save servers? - Запази сървърите? + Запази сървърите? No comment provided by engineer. - + Save settings? - Запази настройките? + Запази настройките? No comment provided by engineer. - + Save welcome message? - Запази съобщението при посрещане? + Запази съобщението при посрещане? No comment provided by engineer. - + Saved WebRTC ICE servers will be removed - Запазените WebRTC ICE сървъри ще бъдат премахнати + Запазените WebRTC ICE сървъри ще бъдат премахнати No comment provided by engineer. - + Scan QR code - Сканирай QR код + Сканирай QR код No comment provided by engineer. - + Scan code - Сканирай код + Сканирай код No comment provided by engineer. - + Scan security code from your contact's app. - Сканирайте кода за сигурност от приложението на вашия контакт. + Сканирайте кода за сигурност от приложението на вашия контакт. No comment provided by engineer. - + Scan server QR code - Сканирай QR кода на сървъра + Сканирай QR кода на сървъра No comment provided by engineer. - + Search - Търсене + Търсене No comment provided by engineer. - + Secure queue - Сигурна опашка + Сигурна опашка server test step - + Security assessment - Оценка на сигурността + Оценка на сигурността No comment provided by engineer. - + Security code - Код за сигурност + Код за сигурност No comment provided by engineer. - + Select - Избери + Избери No comment provided by engineer. - + Self-destruct - Самоунищожение + Самоунищожение No comment provided by engineer. - + Self-destruct passcode - Код за достъп за самоунищожение + Код за достъп за самоунищожение No comment provided by engineer. - + Self-destruct passcode changed! - Кодът за достъп за самоунищожение е променен! + Кодът за достъп за самоунищожение е променен! No comment provided by engineer. - + Self-destruct passcode enabled! - Кодът за достъп за самоунищожение е активиран! + Кодът за достъп за самоунищожение е активиран! No comment provided by engineer. - + Send - Изпрати + Изпрати No comment provided by engineer. - + Send a live message - it will update for the recipient(s) as you type it - Изпратете съобщение на живо - то ще се актуализира за получателя(ите), докато го пишете + Изпратете съобщение на живо - то ще се актуализира за получателя(ите), докато го пишете No comment provided by engineer. - + Send delivery receipts to - Изпращайте потвърждениe за доставка на + Изпращайте потвърждениe за доставка на No comment provided by engineer. - + Send direct message - Изпрати лично съобщение + Изпрати лично съобщение No comment provided by engineer. - + + Send direct message to connect + No comment provided by engineer. + + Send disappearing message - Изпрати изчезващо съобщение + Изпрати изчезващо съобщение No comment provided by engineer. - + Send link previews - Изпрати визуализация на линковете + Изпрати визуализация на линковете No comment provided by engineer. - + Send live message - Изпрати съобщение на живо + Изпрати съобщение на живо No comment provided by engineer. - + Send notifications - Изпращай известия + Изпращай известия No comment provided by engineer. - + Send notifications: - Изпратени известия: + Изпратени известия: No comment provided by engineer. - + Send questions and ideas - Изпращайте въпроси и идеи + Изпращайте въпроси и идеи No comment provided by engineer. - + Send receipts - Изпращане на потвърждениe за доставка + Изпращане на потвърждениe за доставка No comment provided by engineer. - + Send them from gallery or custom keyboards. - Изпрати от галерия или персонализирани клавиатури. + Изпрати от галерия или персонализирани клавиатури. No comment provided by engineer. - + Sender cancelled file transfer. - Подателят отмени прехвърлянето на файла. + Подателят отмени прехвърлянето на файла. No comment provided by engineer. - + Sender may have deleted the connection request. - Подателят може да е изтрил заявката за връзка. + Подателят може да е изтрил заявката за връзка. No comment provided by engineer. - + + Sending delivery receipts will be enabled for all contacts in all visible chat profiles. + Изпращането на потвърждениe за доставка ще бъде активирано за всички контакти във всички видими чат профили. + No comment provided by engineer. + + + Sending delivery receipts will be enabled for all contacts. + Изпращането на потвърждениe за доставка ще бъде активирано за всички контакти. + No comment provided by engineer. + + Sending file will be stopped. - Изпращането на файла ще бъде спряно. + Изпращането на файла ще бъде спряно. No comment provided by engineer. - + + Sending receipts is disabled for %lld contacts + Изпращането на потвърждениe за доставка е деактивирано за %lld контакта + No comment provided by engineer. + + + Sending receipts is disabled for %lld groups + Изпращането на потвърждениe за доставка е деактивирано за %lld групи + No comment provided by engineer. + + + Sending receipts is enabled for %lld contacts + Изпращането на потвърждениe за доставка е активирано за %lld контакта + No comment provided by engineer. + + + Sending receipts is enabled for %lld groups + Изпращането на потвърждениe за доставка е активирано за %lld групи + No comment provided by engineer. + + Sending via - Изпращане чрез + Изпращане чрез No comment provided by engineer. - + Sent at - Изпратено на + Изпратено на No comment provided by engineer. - + Sent at: %@ - Изпратено на: %@ + Изпратено на: %@ copied message info - + Sent file event - Събитие за изпратен файл + Събитие за изпратен файл notification - + Sent message - Изпратено съобщение + Изпратено съобщение message info title - + Sent messages will be deleted after set time. - Изпратените съобщения ще бъдат изтрити след зададеното време. + Изпратените съобщения ще бъдат изтрити след зададеното време. No comment provided by engineer. - + Server requires authorization to create queues, check password - Сървърът изисква оторизация за създаване на опашки, проверете паролата + Сървърът изисква оторизация за създаване на опашки, проверете паролата server test error - + Server requires authorization to upload, check password - Сървърът изисква оторизация за качване, проверете паролата + Сървърът изисква оторизация за качване, проверете паролата server test error - + Server test failed! - Тестът на сървъра е неуспешен! + Тестът на сървъра е неуспешен! No comment provided by engineer. - + Servers - Сървъри + Сървъри No comment provided by engineer. - + Set 1 day - Задай 1 ден + Задай 1 ден No comment provided by engineer. - + Set contact name… - Задай име на контакт… + Задай име на контакт… No comment provided by engineer. - + Set group preferences - Задай групови настройки + Задай групови настройки No comment provided by engineer. - + Set it instead of system authentication. - Задайте го вместо системната идентификация. + Задайте го вместо системната идентификация. No comment provided by engineer. - + Set passcode - Задай kод за достъп + Задай kод за достъп No comment provided by engineer. - + Set passphrase to export - Задай парола за експортиране + Задай парола за експортиране No comment provided by engineer. - + Set the message shown to new members! - Задай съобщението, показано на новите членове! + Задай съобщението, показано на новите членове! No comment provided by engineer. - + Set timeouts for proxy/VPN - Задай време за изчакване за прокси/VPN + Задай време за изчакване за прокси/VPN No comment provided by engineer. - + Settings - Настройки + Настройки No comment provided by engineer. - + Share - Сподели + Сподели chat item action - + Share 1-time link - Сподели еднократен линк + Сподели еднократен линк No comment provided by engineer. - + Share address - Сподели адрес + Сподели адрес No comment provided by engineer. - + Share address with contacts? - Сподели адреса с контактите? + Сподели адреса с контактите? No comment provided by engineer. - + Share link - Сподели линк + Сподели линк No comment provided by engineer. - + Share one-time invitation link - Сподели линк за еднократна покана + Сподели линк за еднократна покана No comment provided by engineer. - + Share with contacts - Сподели с контактите + Сподели с контактите No comment provided by engineer. - + Show calls in phone history - Показване на обажданията в хронологията на телефона + Показване на обажданията в хронологията на телефона No comment provided by engineer. - + Show developer options - Покажи опциите за разработчици + Покажи опциите за разработчици No comment provided by engineer. - + + Show last messages + Показване на последните съобщения в листа с чатовете + No comment provided by engineer. + + Show preview - Показване на визуализация + Показване на визуализация No comment provided by engineer. - + Show: - Покажи: + Покажи: No comment provided by engineer. - + SimpleX Address - SimpleX Адрес + SimpleX Адрес No comment provided by engineer. - + SimpleX Chat security was audited by Trail of Bits. - Сигурността на SimpleX Chat беше одитирана от Trail of Bits. + Сигурността на SimpleX Chat беше одитирана от Trail of Bits. No comment provided by engineer. - + SimpleX Lock - SimpleX заключване + SimpleX заключване No comment provided by engineer. - + SimpleX Lock mode - Режим на SimpleX заключване + Режим на SimpleX заключване No comment provided by engineer. - + SimpleX Lock not enabled! - SimpleX заключване не е активирано! + SimpleX заключване не е активирано! No comment provided by engineer. - + SimpleX Lock turned on - SimpleX заключване е включено + SimpleX заключване е включено No comment provided by engineer. - + SimpleX address - SimpleX адрес + SimpleX адрес No comment provided by engineer. - + SimpleX contact address - SimpleX адрес за контакт + SimpleX адрес за контакт simplex link type - + SimpleX encrypted message or connection event - SimpleX криптирано съобщение или събитие за връзка + SimpleX криптирано съобщение или събитие за връзка notification - + SimpleX group link - SimpleX групов линк + SimpleX групов линк simplex link type - + SimpleX links - SimpleX линкове + SimpleX линкове No comment provided by engineer. - + SimpleX one-time invitation - Еднократна покана за SimpleX + Еднократна покана за SimpleX simplex link type - + + Simplified incognito mode + No comment provided by engineer. + + Skip - Пропускане + Пропускане No comment provided by engineer. - + Skipped messages - Пропуснати съобщения + Пропуснати съобщения No comment provided by engineer. - - Small groups (max 10) + + Small groups (max 20) + Малки групи (максимум 20) No comment provided by engineer. - + Some non-fatal errors occurred during import - you may see Chat console for more details. - Някои не-фатални грешки са възникнали по време на импортиране - може да видите конзолата за повече подробности. + Някои не-фатални грешки са възникнали по време на импортиране - може да видите конзолата за повече подробности. No comment provided by engineer. - + Somebody - Някой + Някой notification title - + Start a new chat - Започни нов чат + Започни нов чат No comment provided by engineer. - + Start chat - Започни чат + Започни чат No comment provided by engineer. - + Start migration - Започни миграция + Започни миграция No comment provided by engineer. - + Stop - Спри + Спри No comment provided by engineer. - + Stop SimpleX - Спри SimpleX + Спри SimpleX authentication reason - + Stop chat to enable database actions - Спрете чата, за да активирате действията с базата данни + Спрете чата, за да активирате действията с базата данни No comment provided by engineer. - + Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. - Спрете чата, за да експортирате, импортирате или изтриете чат базата данни. Няма да можете да получавате и изпращате съобщения, докато чатът е спрян. + Спрете чата, за да експортирате, импортирате или изтриете чат базата данни. Няма да можете да получавате и изпращате съобщения, докато чатът е спрян. No comment provided by engineer. - + Stop chat? - Спри чата? + Спри чата? No comment provided by engineer. - + Stop file - Спри файл + Спри файл cancel file action - + Stop receiving file? - Спри получаването на файла? + Спри получаването на файла? No comment provided by engineer. - + Stop sending file? - Спри изпращането на файла? + Спри изпращането на файла? No comment provided by engineer. - + Stop sharing - Спри споделянето + Спри споделянето No comment provided by engineer. - + Stop sharing address? - Спри споделянето на адреса? + Спри споделянето на адреса? No comment provided by engineer. - + Submit - Изпрати + Изпрати No comment provided by engineer. - + Support SimpleX Chat - Подкрепете SimpleX Chat + Подкрепете SimpleX Chat No comment provided by engineer. - + System - Системен + Системен No comment provided by engineer. - + System authentication - Системна идентификация + Системна идентификация No comment provided by engineer. - + TCP connection timeout - Времето на изчакване за установяване на TCP връзка + Времето на изчакване за установяване на TCP връзка No comment provided by engineer. - + TCP_KEEPCNT - TCP_KEEPCNT + TCP_KEEPCNT No comment provided by engineer. - + TCP_KEEPIDLE - TCP_KEEPIDLE + TCP_KEEPIDLE No comment provided by engineer. - + TCP_KEEPINTVL - TCP_KEEPINTVL + TCP_KEEPINTVL No comment provided by engineer. - + Take picture - Направи снимка + Направи снимка No comment provided by engineer. - + Tap button - Докосни бутона + Докосни бутона No comment provided by engineer. - + Tap to activate profile. - Докосни за активиране на профил. + Докосни за активиране на профил. No comment provided by engineer. - + Tap to join - Докосни за вход + Докосни за вход No comment provided by engineer. - + Tap to join incognito - Докосни за инкогнито вход + Докосни за инкогнито вход No comment provided by engineer. - + Tap to start a new chat - Докосни за започване на нов чат + Докосни за започване на нов чат No comment provided by engineer. - + Test failed at step %@. - Тестът е неуспешен на стъпка %@. + Тестът е неуспешен на стъпка %@. server test failure - + Test server - Тествай сървър + Тествай сървър No comment provided by engineer. - + Test servers - Тествай сървърите + Тествай сървърите No comment provided by engineer. - + Tests failed! - Тестовете са неуспешни! + Тестовете са неуспешни! No comment provided by engineer. - + Thank you for installing SimpleX Chat! - Благодарим Ви, че инсталирахте SimpleX Chat! + Благодарим Ви, че инсталирахте SimpleX Chat! No comment provided by engineer. - + Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! - Благодарение на потребителите – [допринесете през Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + Благодарение на потребителите – [допринесете през Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. - + Thanks to the users – contribute via Weblate! - Благодарение на потребителите – допринесете през Weblate! + Благодарение на потребителите – допринесете през Weblate! No comment provided by engineer. - + The 1st platform without any user identifiers – private by design. - Първата платформа без никакви потребителски идентификатори – поверителна по дизайн. + Първата платформа без никакви потребителски идентификатори – поверителна по дизайн. No comment provided by engineer. - + The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. - Неправилно ID на следващото съобщение (по-малко или еднакво с предишното). + Неправилно ID на следващото съобщение (по-малко или еднакво с предишното). Това може да се случи поради някаква грешка или когато връзката е компрометирана. 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 attempt to change database passphrase was not completed. - Опитът за промяна на паролата на базата данни не беше завършен. + Опитът за промяна на паролата на базата данни не беше завършен. No comment provided by engineer. - + The connection you accepted will be cancelled! - Връзката, която приехте, ще бъде отказана! + Връзката, която приехте, ще бъде отказана! No comment provided by engineer. - + The contact you shared this link with will NOT be able to connect! - Контактът, с когото споделихте този линк, НЯМА да може да се свърже! + Контактът, с когото споделихте този линк, НЯМА да може да се свърже! No comment provided by engineer. - + The created archive is available via app Settings / Database / Old database archive. - Създаденият архив е достъпен чрез Настройки на приложението / База данни / Стар архив на база данни. + Създаденият архив е достъпен чрез Настройки на приложението / База данни / Стар архив на база данни. No comment provided by engineer. - + The encryption is working and the new encryption agreement is not required. It may result in connection errors! - Криптирането работи и новото споразумение за криптиране не е необходимо. Това може да доведе до грешки при свързване! + Криптирането работи и новото споразумение за криптиране не е необходимо. Това може да доведе до грешки при свързване! No comment provided by engineer. - + The group is fully decentralized – it is visible only to the members. - Групата е напълно децентрализирана – видима е само за членовете. + Групата е напълно децентрализирана – видима е само за членовете. No comment provided by engineer. - + The hash of the previous message is different. - Хешът на предишното съобщение е различен. + Хешът на предишното съобщение е различен. No comment provided by engineer. - + The message will be deleted for all members. - Съобщението ще бъде изтрито за всички членове. + Съобщението ще бъде изтрито за всички членове. No comment provided by engineer. - + The message will be marked as moderated for all members. - Съобщението ще бъде маркирано като модерирано за всички членове. + Съобщението ще бъде маркирано като модерирано за всички членове. No comment provided by engineer. - + The next generation of private messaging - Ново поколение поверителни съобщения + Ново поколение поверителни съобщения No comment provided by engineer. - + The old database was not removed during the migration, it can be deleted. - Старата база данни не бе премахната по време на миграцията, тя може да бъде изтрита. + Старата база данни не бе премахната по време на миграцията, тя може да бъде изтрита. No comment provided by engineer. - + The profile is only shared with your contacts. - Профилът се споделя само с вашите контакти. + Профилът се споделя само с вашите контакти. No comment provided by engineer. - + + The second tick we missed! ✅ + Втората отметка, която пропуснахме! ✅ + No comment provided by engineer. + + The sender will NOT be notified - Подателят НЯМА да бъде уведомен + Подателят НЯМА да бъде уведомен No comment provided by engineer. - + The servers for new connections of your current chat profile **%@**. - Сървърите за нови връзки на текущия ви чат профил **%@**. + Сървърите за нови връзки на текущия ви чат профил **%@**. No comment provided by engineer. - + Theme - Тема + Тема No comment provided by engineer. - + There should be at least one user profile. - Трябва да има поне един потребителски профил. + Трябва да има поне един потребителски профил. No comment provided by engineer. - + There should be at least one visible user profile. - Трябва да има поне един видим потребителски профил. + Трябва да има поне един видим потребителски профил. No comment provided by engineer. - + These settings are for your current profile **%@**. - Тези настройки са за текущия ви профил **%@**. + Тези настройки са за текущия ви профил **%@**. No comment provided by engineer. - - They can be overridden in contact and group settings + + They can be overridden in contact and group settings. + Те могат да бъдат променени в настройките за всеки контакт и група. No comment provided by engineer. - + This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - Това действие не може да бъде отменено - всички получени и изпратени файлове и медия ще бъдат изтрити. Снимките с ниска разделителна способност ще бъдат запазени. + Това действие не може да бъде отменено - всички получени и изпратени файлове и медия ще бъдат изтрити. Снимките с ниска разделителна способност ще бъдат запазени. No comment provided by engineer. - + This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - Това действие не може да бъде отменено - съобщенията, изпратени и получени по-рано от избраното, ще бъдат изтрити. Може да отнеме няколко минути. + Това действие не може да бъде отменено - съобщенията, изпратени и получени по-рано от избраното, ще бъдат изтрити. Може да отнеме няколко минути. No comment provided by engineer. - + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. - Това действие не може да бъде отменено - вашият профил, контакти, съобщения и файлове ще бъдат безвъзвратно загубени. + Това действие не може да бъде отменено - вашият профил, контакти, съобщения и файлове ще бъдат безвъзвратно загубени. No comment provided by engineer. - + + This group has over %lld members, delivery receipts are not sent. + Тази група има над %lld членове, потвърждения за доставка не се изпращат. + No comment provided by engineer. + + This group no longer exists. - Тази група вече не съществува. + Тази група вече не съществува. No comment provided by engineer. - + This setting applies to messages in your current chat profile **%@**. - Тази настройка се прилага за съобщения в текущия ви профил **%@**. + Тази настройка се прилага за съобщения в текущия ви профил **%@**. No comment provided by engineer. - + To ask any questions and to receive updates: - За да задавате въпроси и да получавате актуализации: + За да задавате въпроси и да получавате актуализации: No comment provided by engineer. - + To connect, your contact can scan QR code or use the link in the app. - За да се свърже, вашият контакт може да сканира QR код или да използва линка в приложението. + За да се свърже, вашият контакт може да сканира QR код или да използва линка в приложението. No comment provided by engineer. - - To find the profile used for an incognito connection, tap the contact or group name on top of the chat. - No comment provided by engineer. - - + To make a new connection - За да направите нова връзка + За да направите нова връзка No comment provided by engineer. - + To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - За да се защити поверителността, вместо потребителски идентификатори, използвани от всички други платформи, SimpleX има идентификатори за опашки от съобщения, отделни за всеки от вашите контакти. + За да се защити поверителността, вместо потребителски идентификатори, използвани от всички други платформи, SimpleX има идентификатори за опашки от съобщения, отделни за всеки от вашите контакти. No comment provided by engineer. - + To protect timezone, image/voice files use UTC. - За да не се разкрива часовата зона, файловете с изображения/глас използват UTC. + За да не се разкрива часовата зона, файловете с изображения/глас използват UTC. No comment provided by engineer. - + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. - За да защитите информацията си, включете SimpleX заключване. + За да защитите информацията си, включете SimpleX заключване. Ще бъдете подканени да извършите идентификация, преди тази функция да бъде активирана. No comment provided by engineer. - + To record voice message please grant permission to use Microphone. - За да запишете гласово съобщение, моля, дайте разрешение за използване на микрофон. + За да запишете гласово съобщение, моля, дайте разрешение за използване на микрофон. No comment provided by engineer. - + To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. - За да разкриете своя скрит профил, въведете пълна парола в полето за търсене на страницата **Вашите чат профили**. + За да разкриете своя скрит профил, въведете пълна парола в полето за търсене на страницата **Вашите чат профили**. No comment provided by engineer. - + To support instant push notifications the chat database has to be migrated. - За поддръжка на незабавни push известия, базата данни за чат трябва да бъде мигрирана. + За поддръжка на незабавни push известия, базата данни за чат трябва да бъде мигрирана. No comment provided by engineer. - + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. - За да проверите криптирането от край до край с вашия контакт, сравнете (или сканирайте) кода на вашите устройства. + За да проверите криптирането от край до край с вашия контакт, сравнете (или сканирайте) кода на вашите устройства. No comment provided by engineer. - + + Toggle incognito when connecting. + No comment provided by engineer. + + Transport isolation - Транспортна изолация + Транспортна изолация 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. - + Turn off - Изключи + Изключи No comment provided by engineer. - + Turn off notifications? - Изключи известията? + Изключи известията? No comment provided by engineer. - + Turn on - Включи + Включи No comment provided by engineer. - + Unable to record voice message - Не може да се запише гласово съобщение + Не може да се запише гласово съобщение No comment provided by engineer. - + Unexpected error: %@ - Неочаквана грешка: %@ - No comment provided by engineer. + Неочаквана грешка: %@ + item status description - + Unexpected migration state - Неочаквано състояние на миграция + Неочаквано състояние на миграция No comment provided by engineer. - + Unfav. - Премахни от любимите + Премахни от любимите No comment provided by engineer. - + Unhide - Покажи + Покажи No comment provided by engineer. - + Unhide chat profile - Покажи чат профила + Покажи чат профила No comment provided by engineer. - + Unhide profile - Покажи профила + Покажи профила No comment provided by engineer. - + Unit - Мерна единица + Мерна единица No comment provided by engineer. - + Unknown caller - Неизвестен номер + Неизвестен номер callkit banner - + Unknown database error: %@ - Неизвестна грешка в базата данни: %@ + Неизвестна грешка в базата данни: %@ No comment provided by engineer. - + Unknown error - Непозната грешка + Непозната грешка No comment provided by engineer. - + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. - Освен ако не използвате интерфейса за повикване на iOS, активирайте режима "Не безпокой", за да избегнете прекъсвания. + Освен ако не използвате интерфейса за повикване на iOS, активирайте режима "Не безпокой", за да избегнете прекъсвания. No comment provided by engineer. - + Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. To connect, please ask your contact to create another connection link and check that you have a stable network connection. - Освен ако вашият контакт не е изтрил връзката или този линк вече е бил използван, това може да е грешка - моля, докладвайте. + Освен ако вашият контакт не е изтрил връзката или този линк вече е бил използван, това може да е грешка - моля, докладвайте. За да се свържете, моля, помолете вашия контакт да създаде друг линк за връзка и проверете дали имате стабилна мрежова връзка. No comment provided by engineer. - + Unlock - Отключи + Отключи No comment provided by engineer. - + Unlock app - Отключи приложението + Отключи приложението authentication reason - + Unmute - Уведомявай + Уведомявай No comment provided by engineer. - + Unread - Непрочетено + Непрочетено No comment provided by engineer. - + Update - Актуализация + Актуализация No comment provided by engineer. - + Update .onion hosts setting? - Актуализиране на настройката за .onion хостове? + Актуализиране на настройката за .onion хостове? No comment provided by engineer. - + Update database passphrase - Актуализирай паролата на базата данни + Актуализирай паролата на базата данни No comment provided by engineer. - + Update network settings? - Актуализиране на мрежовите настройки? + Актуализиране на мрежовите настройки? No comment provided by engineer. - + Update transport isolation mode? - Актуализиране на режима на изолация на транспорта? + Актуализиране на режима на изолация на транспорта? No comment provided by engineer. - + Updating settings will re-connect the client to all servers. - Актуализирането на настройките ще свърже отново клиента към всички сървъри. + Актуализирането на настройките ще свърже отново клиента към всички сървъри. No comment provided by engineer. - + Updating this setting will re-connect the client to all servers. - Актуализирането на тази настройка ще свърже повторно клиента към всички сървъри. + Актуализирането на тази настройка ще свърже повторно клиента към всички сървъри. No comment provided by engineer. - + Upgrade and open chat - Актуализирай и отвори чата + Актуализирай и отвори чата No comment provided by engineer. - + Upload file - Качи файл + Качи файл server test step - + Use .onion hosts - Използвай .onion хостове + Използвай .onion хостове No comment provided by engineer. - + Use SimpleX Chat servers? - Използвай сървърите на SimpleX Chat? + Използвай сървърите на SimpleX Chat? No comment provided by engineer. - + Use chat - Използвай чата + Използвай чата No comment provided by engineer. - + + Use current profile + Използвай текущия профил + No comment provided by engineer. + + Use for new connections - Използвай за нови връзки + Използвай за нови връзки No comment provided by engineer. - + Use iOS call interface - Използвай интерфейса за повикване на iOS + Използвай интерфейса за повикване на iOS No comment provided by engineer. - + + Use new incognito profile + Използвай нов инкогнито профил + No comment provided by engineer. + + Use server - Използвай сървър + Използвай сървър No comment provided by engineer. - + User profile - Потребителски профил + Потребителски профил No comment provided by engineer. - + Using .onion hosts requires compatible VPN provider. - Използването на .onion хостове изисква съвместим VPN доставчик. + Използването на .onion хостове изисква съвместим VPN доставчик. No comment provided by engineer. - + Using SimpleX Chat servers. - Използват се сървърите на SimpleX Chat. + Използват се сървърите на SimpleX Chat. No comment provided by engineer. - + Verify connection security - Потвръди сигурността на връзката + Потвръди сигурността на връзката No comment provided by engineer. - + Verify security code - Потвръди кода за сигурност + Потвръди кода за сигурност No comment provided by engineer. - + Via browser - Чрез браузър + Чрез браузър No comment provided by engineer. - + Video call - Видео разговор + Видео разговор No comment provided by engineer. - + Video will be received when your contact completes uploading it. - Видеото ще бъде получено, когато вашият контакт завърши качването му. + Видеото ще бъде получено, когато вашият контакт завърши качването му. No comment provided by engineer. - + Video will be received when your contact is online, please wait or check later! - Видеото ще бъде получено, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно! + Видеото ще бъде получено, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно! No comment provided by engineer. - + Videos and files up to 1gb - Видео и файлове до 1gb + Видео и файлове до 1gb No comment provided by engineer. - + View security code - Виж кода за сигурност + Виж кода за сигурност No comment provided by engineer. - + Voice messages - Гласови съобщения + Гласови съобщения chat feature - + Voice messages are prohibited in this chat. - Гласовите съобщения са забранени в този чат. + Гласовите съобщения са забранени в този чат. No comment provided by engineer. - + Voice messages are prohibited in this group. - Гласовите съобщения са забранени в тази група. + Гласовите съобщения са забранени в тази група. No comment provided by engineer. - + Voice messages prohibited! - Гласовите съобщения са забранени! + Гласовите съобщения са забранени! No comment provided by engineer. - + Voice message… - Гласово съобщение… + Гласово съобщение… No comment provided by engineer. - + Waiting for file - Изчаква се получаването на файла + Изчаква се получаването на файла No comment provided by engineer. - + Waiting for image - Изчаква се получаването на изображението + Изчаква се получаването на изображението No comment provided by engineer. - + Waiting for video - Изчаква се получаването на видеото + Изчаква се получаването на видеото No comment provided by engineer. - + Warning: you may lose some data! - Предупреждение: Може да загубите някои данни! + Предупреждение: Може да загубите някои данни! No comment provided by engineer. - + WebRTC ICE servers - WebRTC ICE сървъри + WebRTC ICE сървъри No comment provided by engineer. - + Welcome %@! - Добре дошли %@! + Добре дошли %@! No comment provided by engineer. - + Welcome message - Съобщение при посрещане + Съобщение при посрещане No comment provided by engineer. - + What's new - Какво е новото + Какво е новото No comment provided by engineer. - + When available - Когато са налични + Когато са налични No comment provided by engineer. - + When people request to connect, you can accept or reject it. - Когато хората искат да се свържат с вас, можете да ги приемете или отхвърлите. + Когато хората искат да се свържат с вас, можете да ги приемете или отхвърлите. No comment provided by engineer. - + When you share an incognito profile with somebody, this profile will be used for the groups they invite you to. - Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани. + Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани. No comment provided by engineer. - + With optional welcome message. - С незадължително съобщение при посрещане. + С незадължително съобщение при посрещане. No comment provided by engineer. - + Wrong database passphrase - Грешна парола за базата данни + Грешна парола за базата данни No comment provided by engineer. - + Wrong passphrase! - Грешна парола! + Грешна парола! No comment provided by engineer. - + XFTP servers - XFTP сървъри + XFTP сървъри No comment provided by engineer. - + You - Вие + Вие No comment provided by engineer. - + You accepted connection - Вие приехте връзката + Вие приехте връзката No comment provided by engineer. - + You allow - Вие позволявате + Вие позволявате No comment provided by engineer. - + You already have a chat profile with the same display name. Please choose another name. - Вече имате чат профил със същото показвано име. Моля, изберете друго име. + Вече имате чат профил със същото показвано име. Моля, изберете друго име. No comment provided by engineer. - + You are already connected to %@. - Вече сте вече свързани с %@. + Вече сте вече свързани с %@. No comment provided by engineer. - + You are connected to the server used to receive messages from this contact. - Вие сте свързани към сървъра, използван за получаване на съобщения от този контакт. + Вие сте свързани към сървъра, използван за получаване на съобщения от този контакт. No comment provided by engineer. - + You are invited to group - Поканени сте в групата + Поканени сте в групата No comment provided by engineer. - + You can accept calls from lock screen, without device and app authentication. - Можете да приемате обаждания от заключен екран, без идентификация на устройство и приложението. + Можете да приемате обаждания от заключен екран, без идентификация на устройство и приложението. No comment provided by engineer. - + You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button. - Можете също да се свържете, като натиснете върху линка. Ако се отвори в браузъра, натиснете върху бутона **Отваряне в мобилно приложение**. + Можете също да се свържете, като натиснете върху линка. Ако се отвори в браузъра, натиснете върху бутона **Отваряне в мобилно приложение**. No comment provided by engineer. - + You can create it later - Можете да го създадете по-късно + Можете да го създадете по-късно No comment provided by engineer. - + + You can enable later via Settings + Можете да активирате по-късно през Настройки + No comment provided by engineer. + + You can enable them later via app Privacy & Security settings. - Можете да ги активирате по-късно през настройките за "Поверителност и сигурност" на приложението. + Можете да ги активирате по-късно през настройките за "Поверителност и сигурност" на приложението. No comment provided by engineer. - + You can hide or mute a user profile - swipe it to the right. - Можете да скриете или заглушите известията за потребителски профил - плъзнете надясно. + Можете да скриете или заглушите известията за потребителски профил - плъзнете надясно. No comment provided by engineer. - + You can now send messages to %@ - Вече можете да изпращате съобщения до %@ + Вече можете да изпращате съобщения до %@ notification body - + You can set lock screen notification preview via settings. - Можете да зададете визуализация на известията на заключен екран през настройките. + Можете да зададете визуализация на известията на заключен екран през настройките. No comment provided by engineer. - + You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it. - Можете да споделите линк или QR код - всеки ще може да се присъедини към групата. Няма да загубите членовете на групата, ако по-късно я изтриете. + Можете да споделите линк или QR код - всеки ще може да се присъедини към групата. Няма да загубите членовете на групата, ако по-късно я изтриете. No comment provided by engineer. - + You can share this address with your contacts to let them connect with **%@**. - Можете да споделите този адрес с вашите контакти, за да им позволите да се свържат с **%@**. + Можете да споделите този адрес с вашите контакти, за да им позволите да се свържат с **%@**. No comment provided by engineer. - + You can share your address as a link or QR code - anybody can connect to you. - Можете да споделите адреса си като линк или QR код - всеки може да се свърже с вас. + Можете да споделите адреса си като линк или QR код - всеки може да се свърже с вас. No comment provided by engineer. - + You can start chat via app Settings / Database or by restarting the app - Можете да започнете чат през Настройки на приложението / База данни или като рестартирате приложението + Можете да започнете чат през Настройки на приложението / База данни или като рестартирате приложението No comment provided by engineer. - + You can turn on SimpleX Lock via Settings. - Можете да включите SimpleX заключване през Настройки. + Можете да включите SimpleX заключване през Настройки. No comment provided by engineer. - + You can use markdown to format messages: - Можете да използвате markdown за форматиране на съобщенията: + Можете да използвате markdown за форматиране на съобщенията: No comment provided by engineer. - + You can't send messages! - Не може да изпращате съобщения! + Не може да изпращате съобщения! No comment provided by engineer. - + You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Вие контролирате през кой сървър(и) **да получавате** съобщенията, вашите контакти – сървърите, които използвате, за да им изпращате съобщения. + Вие контролирате през кой сървър(и) **да получавате** съобщенията, вашите контакти – сървърите, които използвате, за да им изпращате съобщения. No comment provided by engineer. - + You could not be verified; please try again. - Не можахте да бъдете потвърдени; Моля, опитайте отново. + Не можахте да бъдете потвърдени; Моля, опитайте отново. No comment provided by engineer. - + You have no chats - Нямате чатове + Нямате чатове No comment provided by engineer. - + You have to enter passphrase every time the app starts - it is not stored on the device. - Трябва да въвеждате парола при всяко стартиране на приложението - тя не се съхранява на устройството. + Трябва да въвеждате парола при всяко стартиране на приложението - тя не се съхранява на устройството. No comment provided by engineer. - - You invited your contact + + You invited a contact + Вие поканихте контакта No comment provided by engineer. - + You joined this group - Вие се присъединихте към тази група + Вие се присъединихте към тази група No comment provided by engineer. - + You joined this group. Connecting to inviting group member. - Вие се присъединихте към тази група. Свързване с поканващия член на групата. + Вие се присъединихте към тази група. Свързване с поканващия член на групата. No comment provided by engineer. - + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. - Трябва да използвате най-новата версия на вашата чат база данни САМО на едно устройство, в противен случай може да спрете да получавате съобщения от някои контакти. + Трябва да използвате най-новата версия на вашата чат база данни САМО на едно устройство, в противен случай може да спрете да получавате съобщения от някои контакти. No comment provided by engineer. - + You need to allow your contact to send voice messages to be able to send them. - Трябва да разрешите на вашия контакт да изпраща гласови съобщения, за да можете да ги изпращате. + Трябва да разрешите на вашия контакт да изпраща гласови съобщения, за да можете да ги изпращате. No comment provided by engineer. - + You rejected group invitation - Отхвърлихте поканата за групата + Отхвърлихте поканата за групата No comment provided by engineer. - + You sent group invitation - Изпратихте покана за групата + Изпратихте покана за групата No comment provided by engineer. - + You will be connected to group when the group host's device is online, please wait or check later! - Ще бъдете свързани с групата, когато устройството на домакина на групата е онлайн, моля, изчакайте или проверете по-късно! + Ще бъдете свързани с групата, когато устройството на домакина на групата е онлайн, моля, изчакайте или проверете по-късно! 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. - + You will be required to authenticate when you start or resume the app after 30 seconds in background. - Ще трябва да се идентифицирате, когато стартирате или възобновите приложението след 30 секунди във фонов режим. + Ще трябва да се идентифицирате, когато стартирате или възобновите приложението след 30 секунди във фонов режим. No comment provided by engineer. - + You will join a group this link refers to and connect to its group members. - Ще се присъедините към групата, към която този линк препраща, и ще се свържете с нейните членове. + Ще се присъедините към групата, към която този линк препраща, и ще се свържете с нейните членове. No comment provided by engineer. - + You will still receive calls and notifications from muted profiles when they are active. - Все още ще получавате обаждания и известия от заглушени профили, когато са активни. + Все още ще получавате обаждания и известия от заглушени профили, когато са активни. No comment provided by engineer. - + You will stop receiving messages from this group. Chat history will be preserved. - Ще спрете да получавате съобщения от тази група. Историята на чата ще бъде запазена. + Ще спрете да получавате съобщения от тази група. Историята на чата ще бъде запазена. No comment provided by engineer. - + You won't lose your contacts if you later delete your address. - Няма да загубите контактите си, ако по-късно изтриете адреса си. + Няма да загубите контактите си, ако по-късно изтриете адреса си. No comment provided by engineer. - + You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile - Опитвате се да поканите контакт, с когото сте споделили инкогнито профил, в групата, в която използвате основния си профил + Опитвате се да поканите контакт, с когото сте споделили инкогнито профил, в групата, в която използвате основния си профил No comment provided by engineer. - + You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed - Използвате инкогнито профил за тази група - за да се предотврати споделянето на основния ви профил, поканите на контакти не са разрешени + Използвате инкогнито профил за тази група - за да се предотврати споделянето на основния ви профил, поканите на контакти не са разрешени No comment provided by engineer. - + Your %@ servers - Вашите %@ сървъри + Вашите %@ сървъри No comment provided by engineer. - + Your ICE servers - Вашите ICE сървъри + Вашите ICE сървъри No comment provided by engineer. - + Your SMP servers - Вашите SMP сървъри + Вашите SMP сървъри No comment provided by engineer. - + Your SimpleX address - Вашият SimpleX адрес + Вашият SimpleX адрес No comment provided by engineer. - + Your XFTP servers - Вашите XFTP сървъри + Вашите XFTP сървъри No comment provided by engineer. - + Your calls - Вашите обаждания + Вашите обаждания No comment provided by engineer. - + Your chat database - Вашата чат база данни + Вашата чат база данни No comment provided by engineer. - + Your chat database is not encrypted - set passphrase to encrypt it. - Вашата чат база данни не е криптирана - задайте парола, за да я криптирате. + Вашата чат база данни не е криптирана - задайте парола, за да я криптирате. No comment provided by engineer. - + Your chat profile will be sent to group members - Вашият чат профил ще бъде изпратен на членовете на групата + Вашият чат профил ще бъде изпратен на членовете на групата No comment provided by engineer. - - Your chat profile will be sent to your contact - No comment provided by engineer. - - + Your chat profiles - Вашите чат профили + Вашите чат профили No comment provided by engineer. - + Your contact needs to be online for the connection to complete. You can cancel this connection and remove the contact (and try later with a new link). - Вашият контакт трябва да бъде онлайн, за да осъществите връзката. + Вашият контакт трябва да бъде онлайн, за да осъществите връзката. Можете да откажете тази връзка и да премахнете контакта (и да опитате по -късно с нов линк). No comment provided by engineer. - + Your contact sent a file that is larger than currently supported maximum size (%@). - Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@). + Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@). No comment provided by engineer. - + Your contacts can allow full message deletion. - Вашите контакти могат да позволят пълното изтриване на съобщението. + Вашите контакти могат да позволят пълното изтриване на съобщението. No comment provided by engineer. - + Your contacts in SimpleX will see it. You can change it in Settings. - Вашите контакти в SimpleX ще го видят. + Вашите контакти в SimpleX ще го видят. Можете да го промените в Настройки. No comment provided by engineer. - + Your contacts will remain connected. - Вашите контакти ще останат свързани. + Вашите контакти ще останат свързани. No comment provided by engineer. - + Your current chat database will be DELETED and REPLACED with the imported one. - Вашата текуща чат база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната. + Вашата текуща чат база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната. No comment provided by engineer. - + Your current profile - Вашият текущ профил + Вашият текущ профил No comment provided by engineer. - + Your preferences - Вашите настройки + Вашите настройки No comment provided by engineer. - + Your privacy - Вашата поверителност + Вашата поверителност No comment provided by engineer. - + + Your profile **%@** will be shared. + Вашият профил **%@** ще бъде споделен. + 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 delivered messages are stored on your device. - Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство. + Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство. No comment provided by engineer. - + Your random profile - Вашият автоматично генериран профил + Вашият автоматично генериран профил No comment provided by engineer. - + Your server - Вашият сървър + Вашият сървър No comment provided by engineer. - + Your server address - Вашият адрес на сървъра + Вашият адрес на сървъра No comment provided by engineer. - + Your settings - Вашите настройки + Вашите настройки No comment provided by engineer. - + [Contribute](https://github.com/simplex-chat/simplex-chat#contribute) - [Допринеси](https://github.com/simplex-chat/simplex-chat#contribute) + [Допринеси](https://github.com/simplex-chat/simplex-chat#contribute) No comment provided by engineer. - + [Send us email](mailto:chat@simplex.chat) - [Изпратете ни имейл](mailto:chat@simplex.chat) + [Изпратете ни имейл](mailto:chat@simplex.chat) No comment provided by engineer. - + [Star on GitHub](https://github.com/simplex-chat/simplex-chat) - [Звезда в GitHub](https://github.com/simplex-chat/simplex-chat) + [Звезда в GitHub](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. - + \_italic_ - \_курсив_ + \_курсив_ No comment provided by engineer. - + \`a + b` - \`a + b` + \`a + b` No comment provided by engineer. - + above, then choose: - по-горе, след това избери: + по-горе, след това избери: No comment provided by engineer. - + accepted call - обаждането прието + обаждането прието call status - + admin - админ + админ member role - + agreeing encryption for %@… - съгласуване на криптиране за %@… + съгласуване на криптиране за %@… chat item text - + agreeing encryption… - съгласуване на криптиране… + съгласуване на криптиране… chat item text - + always - винаги + винаги pref value - + audio call (not e2e encrypted) - аудио разговор (не е e2e криптиран) + аудио разговор (не е e2e криптиран) No comment provided by engineer. - + bad message ID - лошо ID на съобщението + лошо ID на съобщението integrity error chat item - + bad message hash - лош хеш на съобщението + лош хеш на съобщението integrity error chat item - + bold - удебелен + удебелен No comment provided by engineer. - + call error - грешка при повикване + грешка при повикване call status - + call in progress - в момента тече разговор + в момента тече разговор call status - + calling… - повикване… + повикване… call status - + cancelled %@ - отменен %@ + отменен %@ feature offered item - + changed address for you - променен е адреса за вас + променен е адреса за вас chat item text - + changed role of %1$@ to %2$@ - променена роля от %1$@ на %2$@ + променена роля от %1$@ на %2$@ rcv group event chat item - + changed your role to %@ - променена е вашата ролята на %@ + променена е вашата ролята на %@ rcv group event chat item - + changing address for %@… - промяна на адреса за %@… + промяна на адреса за %@… chat item text - + changing address… - промяна на адреса… + промяна на адреса… chat item text - + colored - цветен + цветен No comment provided by engineer. - + complete - завършен + завършен No comment provided by engineer. - + connect to SimpleX Chat developers. - свържете се с разработчиците на SimpleX Chat. + свържете се с разработчиците на SimpleX Chat. No comment provided by engineer. - + connected - свързан + свързан No comment provided by engineer. - + + connected directly + rcv group event chat item + + connecting - свързване + свързване No comment provided by engineer. - + connecting (accepted) - свързване (прието) + свързване (прието) No comment provided by engineer. - + connecting (announced) - свързване (обявено) + свързване (обявено) No comment provided by engineer. - + connecting (introduced) - свързване (представен) + свързване (представен) No comment provided by engineer. - + connecting (introduction invitation) - свързване (покана за представяне) + свързване (покана за представяне) No comment provided by engineer. - + connecting call… - разговорът се свързва… + разговорът се свързва… call status - + connecting… - свързване… + свързване… chat list item title - + connection established - установена е връзка + установена е връзка chat list item title (it should not be shown - + connection:%@ - връзка:%@ + връзка:%@ connection information - + contact has e2e encryption - контактът има e2e криптиране + контактът има e2e криптиране No comment provided by engineer. - + contact has no e2e encryption - контактът няма e2e криптиране + контактът няма e2e криптиране No comment provided by engineer. - + creator - създател + създател No comment provided by engineer. - + custom - персонализиран + персонализиран dropdown time picker choice - + database version is newer than the app, but no down migration for: %@ - версията на базата данни е по-нова от приложението, но няма миграция надолу за: %@ + версията на базата данни е по-нова от приложението, но няма миграция надолу за: %@ No comment provided by engineer. - + days - дни + дни time unit - + default (%@) - по подразбиране (%@) + по подразбиране (%@) pref value - + default (no) - по подразбиране (не) + по подразбиране (не) No comment provided by engineer. - + default (yes) - по подразбиране (да) + по подразбиране (да) No comment provided by engineer. - + deleted - изтрит + изтрит deleted chat item - + deleted group - групата изтрита + групата изтрита rcv group event chat item - + different migration in the app/database: %@ / %@ - различна миграция в приложението/базата данни: %@ / %@ + различна миграция в приложението/базата данни: %@ / %@ No comment provided by engineer. - + direct - директна + директна connection level description - + + disabled + деактивирано + No comment provided by engineer. + + duplicate message - дублирано съобщение + дублирано съобщение integrity error chat item - + e2e encrypted - e2e криптиран + e2e криптиран No comment provided by engineer. - + enabled - активирано + активирано enabled status - + enabled for contact - активирано за контакт + активирано за контакт enabled status - + enabled for you - активирано за вас + активирано за вас enabled status - + encryption agreed - криптирането е съгласувано + криптирането е съгласувано chat item text - + encryption agreed for %@ - криптирането е съгласувано за %@ + криптирането е съгласувано за %@ chat item text - + encryption ok - криптирането работи + криптирането работи chat item text - + encryption ok for %@ - криптирането работи за %@ + криптирането работи за %@ chat item text - + encryption re-negotiation allowed - разрешено повторно договаряне на криптиране + разрешено повторно договаряне на криптиране chat item text - + encryption re-negotiation allowed for %@ - разрешено повторно договаряне на криптиране за %@ + разрешено повторно договаряне на криптиране за %@ chat item text - + encryption re-negotiation required - необходимо е повторно договаряне на криптиране + необходимо е повторно договаряне на криптиране chat item text - + encryption re-negotiation required for %@ - необходимо е повторно договаряне на криптиране за %@ + необходимо е повторно договаряне на криптиране за %@ chat item text - + ended - приключен + приключен No comment provided by engineer. - + ended call %@ - приключи разговор %@ + приключи разговор %@ call status - + error - грешка + грешка No comment provided by engineer. - + + event happened + събитие се случи + No comment provided by engineer. + + group deleted - групата е изтрита + групата е изтрита No comment provided by engineer. - + group profile updated - профилът на групата е актуализиран + профилът на групата е актуализиран snd group event chat item - + hours - часове + часове time unit - + iOS Keychain is used to securely store passphrase - it allows receiving push notifications. - iOS Keychain се използва за сигурно съхраняване на парола - позволява получаване на push известия. + iOS Keychain се използва за сигурно съхраняване на парола - позволява получаване на push известия. No comment provided by engineer. - + iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications. - iOS Keychain ще се използва за сигурно съхраняване на паролата, след като рестартирате приложението или промените паролата - това ще позволи получаването на push известия. + iOS Keychain ще се използва за сигурно съхраняване на паролата, след като рестартирате приложението или промените паролата - това ще позволи получаването на push известия. No comment provided by engineer. - + incognito via contact address link - инкогнито чрез линк с адрес за контакт + инкогнито чрез линк с адрес за контакт chat list item description - + incognito via group link - инкогнито чрез групов линк + инкогнито чрез групов линк chat list item description - + incognito via one-time link - инкогнито чрез еднократен линк за връзка + инкогнито чрез еднократен линк за връзка chat list item description - + indirect (%d) - индиректна (%d) + индиректна (%d) connection level description - + invalid chat - невалиден чат + невалиден чат invalid chat data - + invalid chat data - невалидни данни за чат + невалидни данни за чат No comment provided by engineer. - + invalid data - невалидни данни + невалидни данни invalid chat item - + invitation to group %@ - покана за група %@ + покана за група %@ group name - + invited - поканен + поканен No comment provided by engineer. - + invited %@ - поканен %@ + поканен %@ rcv group event chat item - + invited to connect - поканен да се свърже + поканен да се свърже chat list item title - + invited via your group link - поканен чрез вашия групов линк + поканен чрез вашия групов линк rcv group event chat item - + italic - курсив + курсив No comment provided by engineer. - + join as %@ - присъединяване като %@ + присъединяване като %@ No comment provided by engineer. - + left - напусна + напусна rcv group event chat item - + marked deleted - маркирано като изтрито + маркирано като изтрито marked deleted chat item preview text - + member - член + член member role - + connected - свързан + свързан rcv group event chat item - + message received - получено съобщение + получено съобщение notification - + minutes - минути + минути time unit - + missed call - пропуснато повикване + пропуснато повикване call status - + moderated - модерирано + модерирано moderated chat item - + moderated by %@ - модерирано от %@ + модерирано от %@ No comment provided by engineer. - + months - месеци + месеци time unit - + never - никога + никога No comment provided by engineer. - + new message - ново съобщение + ново съобщение notification - + no - не + не pref value - + no e2e encryption - липсва e2e криптиране + липсва e2e криптиране No comment provided by engineer. - + no text - няма текст + няма текст copied message info in history - + observer - наблюдател + наблюдател member role - + off - изключено + изключено enabled status group pref value - + offered %@ - предлага %@ + предлага %@ feature offered item - + offered %1$@: %2$@ - предлага %1$@: %2$@ + предлага %1$@: %2$@ feature offered item - + on - включено + включено group pref value - + or chat with the developers - или пишете на разработчиците + или пишете на разработчиците No comment provided by engineer. - + owner - собственик + собственик member role - + peer-to-peer - peer-to-peer + peer-to-peer No comment provided by engineer. - + received answer… - получен отговор… + получен отговор… No comment provided by engineer. - + received confirmation… - получено потвърждение… + получено потвърждение… No comment provided by engineer. - + rejected call - отхвърлено повикване + отхвърлено повикване call status - + removed - отстранен + отстранен No comment provided by engineer. - + removed %@ - отстранен %@ + отстранен %@ rcv group event chat item - + removed you - ви острани + ви острани rcv group event chat item - + sec - сек. + сек. network option - + seconds - секунди + секунди time unit - + secret - таен + таен No comment provided by engineer. - + security code changed - кодът за сигурност е променен + кодът за сигурност е променен chat item text - + + send direct message + No comment provided by engineer. + + starting… - стартиране… + стартиране… No comment provided by engineer. - + strike - зачеркнат + зачеркнат No comment provided by engineer. - + this contact - този контакт + този контакт notification title - + unknown - неизвестен + неизвестен connection info - + updated group profile - актуализиран профил на групата + актуализиран профил на групата rcv group event chat item - + v%@ (%@) - v%@ (%@) + v%@ (%@) No comment provided by engineer. - + via contact address link - чрез линк с адрес за контакт + чрез линк с адрес за контакт chat list item description - + via group link - чрез групов линк + чрез групов линк chat list item description - + via one-time link - чрез еднократен линк за връзка + чрез еднократен линк за връзка chat list item description - + via relay - чрез реле + чрез реле No comment provided by engineer. - + video call (not e2e encrypted) - видео разговор (не е e2e криптиран) + видео разговор (не е e2e криптиран) No comment provided by engineer. - + waiting for answer… - чака се отговор… + чака се отговор… No comment provided by engineer. - + waiting for confirmation… - чака се за потвърждение… + чака се за потвърждение… No comment provided by engineer. - + wants to connect to you! - иска да се свърже с вас! + иска да се свърже с вас! No comment provided by engineer. - + weeks - седмици + седмици time unit - + yes - да + да pref value - + you are invited to group - вие сте поканени в групата + вие сте поканени в групата No comment provided by engineer. - + you are observer - вие сте наблюдател + вие сте наблюдател No comment provided by engineer. - + you changed address - променихте адреса + променихте адреса chat item text - + you changed address for %@ - променихте адреса за %@ + променихте адреса за %@ chat item text - + you changed role for yourself to %@ - променихте ролята си на %@ + променихте ролята си на %@ snd group event chat item - + you changed role of %1$@ to %2$@ - променихте ролята на %1$@ на %2$@ + променихте ролята на %1$@ на %2$@ snd group event chat item - + you left - вие напуснахте + вие напуснахте snd group event chat item - + you removed %@ - премахнахте %@ + премахнахте %@ snd group event chat item - + you shared one-time link - споделихте еднократен линк за връзка + споделихте еднократен линк за връзка chat list item description - + you shared one-time link incognito - споделихте еднократен инкогнито линк за връзка + споделихте еднократен инкогнито линк за връзка chat list item description - + you: - вие: + вие: No comment provided by engineer. - + \~strike~ - \~зачеркнат~ - No comment provided by engineer. - - - # %@ - # %@ - copied message info title, # <title> - - - ## History - ## История - copied message info - - - ## In reply to - ## В отговор на - copied message info - - - A few more things - Още няколко неща - No comment provided by engineer. - - - A new random profile will be shared. - Нов автоматично генериран профил ще бъде споделен. - No comment provided by engineer. - - - - more stable message delivery. -- a bit better groups. -- and more! - - по-стабилна доставка на съобщения. -- малко по-добри групи. -- и още! - No comment provided by engineer. - - - Accept connection request? - Приемане на заявка за връзка? - No comment provided by engineer. - - - Connect incognito - Свързване инкогнито - No comment provided by engineer. - - - Connect via one-time link - Свързване чрез еднократен линк за връзка - No comment provided by engineer. - - - Delivery - Доставка - No comment provided by engineer. - - - Disable (keep overrides) - Деактивиране (запазване на промените) - No comment provided by engineer. - - - Disable for all - Деактивиране за всички - No comment provided by engineer. - - - Error enabling delivery receipts! - Грешка при активирането на потвърждениeто за доставка! - No comment provided by engineer. - - - Even when disabled in the conversation. - Дори когато е деактивиран в разговора. - No comment provided by engineer. - - - Fix encryption after restoring backups. - Оправяне на криптирането след възстановяване от резервни копия. - No comment provided by engineer. - - - Incognito mode protects your privacy by using a new random profile for each contact. - Режимът инкогнито защитава вашата поверителност, като използва нов автоматично генериран профил за всеки контакт. - No comment provided by engineer. - - - Don't enable - Не активирай - No comment provided by engineer. - - - Filter unread and favorite chats. - Филтрирайте непрочетените и любимите чатове. - No comment provided by engineer. - - - Find chats faster - Намирайте чатове по-бързо - No comment provided by engineer. - - - Enable (keep overrides) - Активиране (запазване на промените) - No comment provided by engineer. - - - Enable for all - Активиране за всички - No comment provided by engineer. - - - Error setting delivery receipts! - Грешка при настройването на потвърждениeто за доставка!! - No comment provided by engineer. - - - Invalid status - Невалиден статус - item status text - - - Keep your connections - Запазете връзките си - No comment provided by engineer. - - - Make one message disappear - Накарайте едно съобщение да изчезне - No comment provided by engineer. - - - Message delivery receipts! - Потвърждениe за доставка на съобщения! - No comment provided by engineer. - - - %@ and %@ connected - %@ и %@ са свързани - No comment provided by engineer. - - - No delivery information - Няма информация за доставката - No comment provided by engineer. - - - Sending receipts is disabled for %lld contacts - Изпращането на потвърждениe за доставка е деактивирано за %lld контакта - No comment provided by engineer. - - - Connect directly - Свързване директно - No comment provided by engineer. - - - Sending receipts is enabled for %lld groups - Изпращането на потвърждениe за доставка е активирано за %lld групи - No comment provided by engineer. - - - Sending receipts is disabled for %lld groups - Изпращането на потвърждениe за доставка е деактивирано за %lld групи - No comment provided by engineer. - - - Sending delivery receipts will be enabled for all contacts. - Изпращането на потвърждениe за доставка ще бъде активирано за всички контакти. - No comment provided by engineer. - - - Sending receipts is enabled for %lld contacts - Изпращането на потвърждениe за доставка е активирано за %lld контакта - No comment provided by engineer. - - - Receipts are disabled - Потвърждениeто за доставка е деактивирано - No comment provided by engineer. - - - This group has over %lld members, delivery receipts are not sent. - Тази група има над %lld членове, потвърждения за доставка не се изпращат. - No comment provided by engineer. - - - Sending delivery receipts will be enabled for all contacts in all visible chat profiles. - Изпращането на потвърждениe за доставка ще бъде активирано за всички контакти във всички видими чат профили. - No comment provided by engineer. - - - They can be overridden in contact and group settings. - Те могат да бъдат променени в настройките за всеки контакт и група. - No comment provided by engineer. - - - Connect via contact link - Свързване чрез линк на контакта - No comment provided by engineer. - - - Use current profile - Използвай текущия профил - No comment provided by engineer. - - - You can enable later via Settings - Можете да активирате по-късно през Настройки - No comment provided by engineer. - - - Reject (sender NOT notified) - Отхвърляне (подателят НЕ бива уведомен) - No comment provided by engineer. - - - Most likely this connection is deleted. - Най-вероятно тази връзка е изтрита. - item status description - - - Use new incognito profile - Използвай нов инкогнито профил - No comment provided by engineer. - - - You invited a contact - Вие поканихте контакта - No comment provided by engineer. - - - Paste the link you received to connect with your contact. - Поставете линка, който сте получили, за да се свържете с вашия контакт. - placeholder - - - The second tick we missed! ✅ - Втората отметка, която пропуснахме! ✅ - No comment provided by engineer. - - - %@, %@ and %lld other members connected - %@, %@ и %lld други членове са свързани - No comment provided by engineer. - - - Small groups (max 20) - Малки групи (максимум 20) - No comment provided by engineer. - - - Show last messages - Показване на последните съобщения в листа с чатовете - No comment provided by engineer. - - - disabled - деактивирано - No comment provided by engineer. - - - Your profile **%@** will be shared. - Вашият профил **%@** ще бъде споделен. - No comment provided by engineer. - - - event happened - събитие се случи - No comment provided by engineer. - - - Error decrypting file - Грешка при декриптирането на файла - No comment provided by engineer. - - - Encrypt local files - Криптирай локални файлове + \~зачеркнат~ No comment provided by engineer.
- +
- + SimpleX - SimpleX + SimpleX Bundle name - + SimpleX needs camera access to scan QR codes to connect to other users and for video calls. - SimpleX се нуждае от достъп до камерата, за да сканира QR кодове, за да се свърже с други потребители и за видео разговори. + SimpleX се нуждае от достъп до камерата, за да сканира QR кодове, за да се свърже с други потребители и за видео разговори. Privacy - Camera Usage Description - + SimpleX uses Face ID for local authentication - SimpleX използва Face ID за локалнa идентификация + SimpleX използва Face ID за локалнa идентификация Privacy - Face ID Usage Description - + SimpleX needs microphone access for audio and video calls, and to record voice messages. - SimpleX се нуждае от достъп до микрофона за аудио и видео разговори и за запис на гласови съобщения. + SimpleX се нуждае от достъп до микрофона за аудио и видео разговори и за запис на гласови съобщения. Privacy - Microphone Usage Description - + SimpleX needs access to Photo Library for saving captured and received media - SimpleX се нуждае от достъп до фотобиблиотека за запазване на заснета и получена медия + SimpleX се нуждае от достъп до фотобиблиотека за запазване на заснета и получена медия Privacy - Photo Library Additions Usage Description
- +
- + SimpleX NSE - SimpleX NSE + SimpleX NSE Bundle display name - + SimpleX NSE - SimpleX NSE + SimpleX NSE Bundle name - + Copyright © 2022 SimpleX Chat. All rights reserved. - Авторско право © 2022 SimpleX Chat. Всички права запазени. + Авторско право © 2022 SimpleX Chat. Всички права запазени. Copyright (human-readable) diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..aaa7f79bc --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.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/bg.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/Shared/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings new file mode 100644 index 000000000..124ddbcc3 --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -0,0 +1,6 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX NSE"; +/* Bundle name */ +"CFBundleName" = "SimpleX NSE"; +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings new file mode 100644 index 000000000..cf485752e --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings @@ -0,0 +1,30 @@ +/* No comment provided by engineer. */ +"_italic_" = "\\_italic_"; + +/* 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 or link for your contact."; + +/* No comment provided by engineer. */ +"*bold*" = "\\*bold*"; + +/* No comment provided by engineer. */ +"`a + b`" = "\\`a + b`"; + +/* No comment provided by engineer. */ +"~strike~" = "\\~strike~"; + +/* call status */ +"connecting call" = "connecting call…"; + +/* No comment provided by engineer. */ +"Connecting server…" = "Connecting to server…"; + +/* No comment provided by engineer. */ +"Connecting server… (error: %@)" = "Connecting to server… (error: %@)"; + +/* rcv group event chat item */ +"member connected" = "connected"; + +/* No comment provided by engineer. */ +"No group!" = "Group not found!"; + diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/SimpleX--iOS--InfoPlist.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/SimpleX--iOS--InfoPlist.strings new file mode 100644 index 000000000..3af673b19 --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/SimpleX--iOS--InfoPlist.strings @@ -0,0 +1,10 @@ +/* Bundle name */ +"CFBundleName" = "SimpleX"; +/* Privacy - Camera Usage Description */ +"NSCameraUsageDescription" = "SimpleX needs camera access to scan QR codes to connect to other users and for video calls."; +/* Privacy - Face ID Usage Description */ +"NSFaceIDUsageDescription" = "SimpleX uses Face ID for local authentication"; +/* Privacy - Microphone Usage Description */ +"NSMicrophoneUsageDescription" = "SimpleX needs microphone access for audio and video calls, and to record voice messages."; +/* Privacy - Photo Library Additions Usage Description */ +"NSPhotoLibraryAddUsageDescription" = "SimpleX needs access to Photo Library for saving captured and received media"; diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/contents.json b/apps/ios/SimpleX Localizations/bg.xcloc/contents.json new file mode 100644 index 000000000..23e8239ce --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/contents.json @@ -0,0 +1,12 @@ +{ + "developmentRegion" : "en", + "project" : "SimpleX.xcodeproj", + "targetLocale" : "bg", + "toolInfo" : { + "toolBuildNumber" : "15A240d", + "toolID" : "com.apple.dt.xcode", + "toolName" : "Xcode", + "toolVersion" : "15.0" + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/apps/ios/SimpleX NSE/bg.lproj/InfoPlist.strings b/apps/ios/SimpleX NSE/bg.lproj/InfoPlist.strings new file mode 100644 index 000000000..b1c515fbb --- /dev/null +++ b/apps/ios/SimpleX NSE/bg.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX NSE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX NSE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Авторско право © 2022 SimpleX Chat. Всички права запазени."; + diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 55b271ed0..736ee80a2 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -295,6 +295,9 @@ 5C55A92D283D0FDE00C4E99E /* sounds */ = {isa = PBXFileReference; lastKnownFileType = folder; path = sounds; sourceTree = ""; }; 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownHelp.swift; sourceTree = ""; }; 5C58BCD5292BEBE600AF9E4F /* CIChatFeatureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIChatFeatureView.swift; sourceTree = ""; }; + 5C5B67912ABAF4B500DA9412 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; + 5C5B67922ABAF56000DA9412 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = "bg.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; + 5C5B67932ABAF56000DA9412 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; 5C5DB70D289ABDD200730FFF /* AppearanceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceSettings.swift; sourceTree = ""; }; 5C5E5D3A2824468B00B0488A /* ActiveCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveCallView.swift; sourceTree = ""; }; 5C5E5D3C282447AB00B0488A /* CallTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallTypes.swift; sourceTree = ""; }; @@ -1020,6 +1023,7 @@ th, fi, uk, + bg, ); mainGroup = 5CA059BD279559F40002BEB4; packageReferences = ( @@ -1305,6 +1309,7 @@ 5CA3ED502A9422D1005D71E2 /* th */, 5C136D8F2AAB3D14006DE2FC /* fi */, 5C636F672AAB3D2400751C84 /* uk */, + 5C5B67932ABAF56000DA9412 /* bg */, ); name = InfoPlist.strings; sourceTree = ""; @@ -1326,6 +1331,7 @@ 5CA3ED4D2A942170005D71E2 /* th */, 5CE6C7B32AAB1515007F345C /* fi */, 5CE6C7B42AAB1527007F345C /* uk */, + 5C5B67912ABAF4B500DA9412 /* bg */, ); name = Localizable.strings; sourceTree = ""; @@ -1346,6 +1352,7 @@ 5CA3ED4F2A9422D1005D71E2 /* th */, 5C136D8E2AAB3D14006DE2FC /* fi */, 5C636F662AAB3D2400751C84 /* uk */, + 5C5B67922ABAF56000DA9412 /* bg */, ); name = "SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; diff --git a/apps/ios/bg.lproj/Localizable.strings b/apps/ios/bg.lproj/Localizable.strings new file mode 100644 index 000000000..729713fed --- /dev/null +++ b/apps/ios/bg.lproj/Localizable.strings @@ -0,0 +1,3681 @@ +/* No comment provided by engineer. */ +"\n" = "\n"; + +/* No comment provided by engineer. */ +" " = " "; + +/* No comment provided by engineer. */ +" " = " "; + +/* 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. */ +"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- по-стабилна доставка на съобщения.\n- малко по-добри групи.\n- и още!"; + +/* No comment provided by engineer. */ +"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- гласови съобщения до 5 минути.\n- персонализирано време за изчезване.\n- история на редактиране."; + +/* No comment provided by engineer. */ +", " = ", "; + +/* No comment provided by engineer. */ +": " = ": "; + +/* No comment provided by engineer. */ +"!1 colored!" = "!1 цветно!"; + +/* No comment provided by engineer. */ +"." = "."; + +/* No comment provided by engineer. */ +"(" = "("; + +/* No comment provided by engineer. */ +")" = ")"; + +/* No comment provided by engineer. */ +"[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Допринеси](https://github.com/simplex-chat/simplex-chat#contribute)"; + +/* No comment provided by engineer. */ +"[Send us email](mailto:chat@simplex.chat)" = "[Изпратете ни имейл](mailto:chat@simplex.chat)"; + +/* No comment provided by engineer. */ +"[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Звезда в GitHub](https://github.com/simplex-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. */ +"**Create link / QR code** for your contact to use." = "**Създай линк / QR код**, който вашият контакт да използва."; + +/* No comment provided by engineer. */ +"**e2e encrypted** audio call" = "**e2e криптиран**аудио разговор"; + +/* No comment provided by engineer. */ +"**e2e encrypted** video call" = "**e2e криптирано** видео разговор"; + +/* No comment provided by engineer. */ +"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**По поверително**: проверявайте новите съобщения на всеки 20 минути. Токенът на устройството се споделя със сървъра за чат SimpleX, но не и колко контакти или съобщения имате."; + +/* No comment provided by engineer. */ +"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението)."; + +/* No comment provided by engineer. */ +"**Paste received link** or open it in the browser and tap **Open in mobile app**." = "**Поставете получения линк** или го отворете в браузъра и докоснете **Отваряне в мобилно приложение**."; + +/* No comment provided by engineer. */ +"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Моля, обърнете внимание**: НЯМА да можете да възстановите или промените паролата, ако я загубите."; + +/* No comment provided by engineer. */ +"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Препоръчително**: токенът на устройството и известията се изпращат до сървъра за уведомяване на SimpleX Chat, но не и съдържанието, размерът на съобщението или от кого е."; + +/* No comment provided by engineer. */ +"**Scan QR code**: to connect to your contact in person or via video call." = "**Сканирай QR код**: за да се свържете с вашия контакт лично или чрез видеообаждане."; + +/* No comment provided by engineer. */ +"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Внимание**: Незабавните push известия изискват парола, запазена в Keychain."; + +/* No comment provided by engineer. */ +"*bold*" = "\\*удебелен*"; + +/* copied message info title, # */ +"# %@" = "# %@"; + +/* copied message info */ +"## History" = "## История"; + +/* copied message info */ +"## In reply to" = "## В отговор на"; + +/* No comment provided by engineer. */ +"#secret#" = "#тайно#"; + +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"%@ (current)" = "%@ (текущ)"; + +/* copied message info */ +"%@ (current):" = "%@ (текущ):"; + +/* No comment provided by engineer. */ +"%@ / %@" = "%@ / %@"; + +/* No comment provided by engineer. */ +"%@ %@" = "%@ %@"; + +/* No comment provided by engineer. */ +"%@ and %@ connected" = "%@ и %@ са свързани"; + +/* copied message info, <sender> at <time> */ +"%@ at %@:" = "%1$@ в %2$@:"; + +/* notification title */ +"%@ is connected!" = "%@ е свързан!"; + +/* No comment provided by engineer. */ +"%@ is not verified" = "%@ не е потвърдено"; + +/* No comment provided by engineer. */ +"%@ is verified" = "%@ е потвърдено"; + +/* No comment provided by engineer. */ +"%@ servers" = "%@ сървъри"; + +/* notification title */ +"%@ wants to connect!" = "%@ иска да се свърже!"; + +/* No comment provided by engineer. */ +"%@, %@ and %lld other members connected" = "%@, %@ и %lld други членове са свързани"; + +/* copied message info */ +"%@:" = "%@:"; + +/* time interval */ +"%d days" = "%d дни"; + +/* time interval */ +"%d hours" = "%d часа"; + +/* time interval */ +"%d min" = "%d мин."; + +/* time interval */ +"%d months" = "%d месеца"; + +/* time interval */ +"%d sec" = "%d сек."; + +/* integrity error chat item */ +"%d skipped message(s)" = "%d пропуснато(и) съобщение(я)"; + +/* time interval */ +"%d weeks" = "%d седмици"; + +/* No comment provided by engineer. */ +"%lld" = "%lld"; + +/* No comment provided by engineer. */ +"%lld %@" = "%lld %@"; + +/* No comment provided by engineer. */ +"%lld contact(s) selected" = "%lld избран(и) контакт(а)"; + +/* No comment provided by engineer. */ +"%lld file(s) with total size of %@" = "%lld файл(а) с общ размер от %@"; + +/* No comment provided by engineer. */ +"%lld members" = "%lld членове"; + +/* No comment provided by engineer. */ +"%lld minutes" = "%lld минути"; + +/* No comment provided by engineer. */ +"%lld second(s)" = "%lld секунда(и)"; + +/* No comment provided by engineer. */ +"%lld seconds" = "%lld секунди"; + +/* No comment provided by engineer. */ +"%lldd" = "%lldд"; + +/* No comment provided by engineer. */ +"%lldh" = "%lldч"; + +/* No comment provided by engineer. */ +"%lldk" = "%lldk"; + +/* No comment provided by engineer. */ +"%lldm" = "%lldм"; + +/* No comment provided by engineer. */ +"%lldmth" = "%lldмесц."; + +/* No comment provided by engineer. */ +"%llds" = "%lldс"; + +/* No comment provided by engineer. */ +"%lldw" = "%lldсед."; + +/* No comment provided by engineer. */ +"%u messages failed to decrypt." = "%u съобщения не успяха да се декриптират."; + +/* No comment provided by engineer. */ +"%u messages skipped." = "%u пропуснати съобщения."; + +/* No comment provided by engineer. */ +"`a + b`" = "\\`a + b`"; + +/* email text */ +"<p>Hi!</p>\n<p><a href=\"%@\">Connect to me via SimpleX Chat</a></p>" = "<p>Здравейте!</p>\n<p><a href=\"%@\">Свържете се с мен чрез SimpleX Chat</a></p>"; + +/* No comment provided by engineer. */ +"~strike~" = "\\~зачеркнат~"; + +/* No comment provided by engineer. */ +"0s" = "0s"; + +/* time interval */ +"1 day" = "1 ден"; + +/* time interval */ +"1 hour" = "1 час"; + +/* No comment provided by engineer. */ +"1 minute" = "1 минута"; + +/* time interval */ +"1 month" = "1 месец"; + +/* time interval */ +"1 week" = "1 седмица"; + +/* No comment provided by engineer. */ +"1-time link" = "Еднократен линк"; + +/* No comment provided by engineer. */ +"5 minutes" = "5 минути"; + +/* No comment provided by engineer. */ +"6" = "6"; + +/* No comment provided by engineer. */ +"30 seconds" = "30 секунди"; + +/* No comment provided by engineer. */ +"A few more things" = "Още няколко неща"; + +/* notification title */ +"A new contact" = "Нов контакт"; + +/* No comment provided by engineer. */ +"A new random profile will be shared." = "Нов автоматично генериран профил ще бъде споделен."; + +/* No comment provided by engineer. */ +"A separate TCP connection will be used **for each chat profile you have in the app**." = "Ще се използва отделна TCP връзка **за всеки чатпрофил, който имате в приложението**."; + +/* No comment provided by engineer. */ +"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "Ще се използва отделна TCP връзка **за всеки контакт и член на групата**.\n**Моля, обърнете внимание**: ако имате много връзки, консумацията на батерията и трафика може да бъде значително по-висока и някои връзки може да се провалят."; + +/* No comment provided by engineer. */ +"Abort" = "Откажи"; + +/* No comment provided by engineer. */ +"Abort changing address" = "Откажи смяна на адрес"; + +/* No comment provided by engineer. */ +"Abort changing address?" = "Откажи смяна на адрес?"; + +/* No comment provided by engineer. */ +"About SimpleX" = "За SimpleX"; + +/* No comment provided by engineer. */ +"About SimpleX address" = "Повече за SimpleX адреса"; + +/* No comment provided by engineer. */ +"About SimpleX Chat" = "За SimpleX Chat"; + +/* No comment provided by engineer. */ +"above, then choose:" = "по-горе, след това избери:"; + +/* No comment provided by engineer. */ +"Accent color" = "Основен цвят"; + +/* accept contact request via notification + accept incoming call via notification */ +"Accept" = "Приеми"; + +/* No comment provided by engineer. */ +"Accept connection request?" = "Приемане на заявка за връзка?"; + +/* notification body */ +"Accept contact request from %@?" = "Приемане на заявка за контакт от %@?"; + +/* accept contact request via notification */ +"Accept incognito" = "Приеми инкогнито"; + +/* call status */ +"accepted call" = "обаждането прието"; + +/* No comment provided by engineer. */ +"Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Добавете адрес към вашия профил, така че вашите контакти да могат да го споделят с други хора. Актуализацията на профила ще бъде изпратена до вашите контакти."; + +/* No comment provided by engineer. */ +"Add preset servers" = "Добави предварително зададени сървъри"; + +/* No comment provided by engineer. */ +"Add profile" = "Добави профил"; + +/* No comment provided by engineer. */ +"Add server…" = "Добави сървър…"; + +/* No comment provided by engineer. */ +"Add servers by scanning QR codes." = "Добави сървъри чрез сканиране на QR кодове."; + +/* No comment provided by engineer. */ +"Add to another device" = "Добави към друго устройство"; + +/* No comment provided by engineer. */ +"Add welcome message" = "Добави съобщение при посрещане"; + +/* No comment provided by engineer. */ +"Address" = "Адрес"; + +/* No comment provided by engineer. */ +"Address change will be aborted. Old receiving address will be used." = "Промяната на адреса ще бъде прекъсната. Ще се използва старият адрес за получаване."; + +/* member role */ +"admin" = "админ"; + +/* No comment provided by engineer. */ +"Admins can create the links to join groups." = "Админите могат да създадат линкове за присъединяване към групи."; + +/* No comment provided by engineer. */ +"Advanced network settings" = "Разширени мрежови настройки"; + +/* chat item text */ +"agreeing encryption for %@…" = "съгласуване на криптиране за %@…"; + +/* chat item text */ +"agreeing encryption…" = "съгласуване на криптиране…"; + +/* No comment provided by engineer. */ +"All app data is deleted." = "Всички данни от приложението бяха изтрити."; + +/* No comment provided by engineer. */ +"All chats and messages will be deleted - this cannot be undone!" = "Всички чатове и съобщения ще бъдат изтрити - това не може да бъде отменено!"; + +/* No comment provided by engineer. */ +"All data is erased when it is entered." = "Всички данни се изтриват при въвеждане."; + +/* No comment provided by engineer. */ +"All group members will remain connected." = "Всички членове на групата ще останат свързани."; + +/* No comment provided by engineer. */ +"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Всички съобщения ще бъдат изтрити - това не може да бъде отменено! Съобщенията ще бъдат изтрити САМО за вас."; + +/* No comment provided by engineer. */ +"All your contacts will remain connected." = "Всички ваши контакти ще останат свързани."; + +/* No comment provided by engineer. */ +"All your contacts will remain connected. Profile update will be sent to your contacts." = "Всички ваши контакти ще останат свързани. Актуализацията на профила ще бъде изпратена до вашите контакти."; + +/* No comment provided by engineer. */ +"Allow" = "Позволи"; + +/* No comment provided by engineer. */ +"Allow calls only if your contact allows them." = "Позволи обаждания само ако вашият контакт ги разрешава."; + +/* No comment provided by engineer. */ +"Allow disappearing messages only if your contact allows it to you." = "Позволи изчезващи съобщения само ако вашият контакт ги разрешава."; + +/* No comment provided by engineer. */ +"Allow irreversible message deletion only if your contact allows it to you." = "Позволи необратимо изтриване на съобщение само ако вашият контакт го рарешава."; + +/* No comment provided by engineer. */ +"Allow message reactions only if your contact allows them." = "Позволи реакции на съобщения само ако вашият контакт ги разрешава."; + +/* No comment provided by engineer. */ +"Allow message reactions." = "Позволи реакции на съобщения."; + +/* No comment provided by engineer. */ +"Allow sending direct messages to members." = "Позволи изпращането на лични съобщения до членовете."; + +/* No comment provided by engineer. */ +"Allow sending disappearing messages." = "Разреши изпращането на изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Allow to irreversibly delete sent messages." = "Позволи необратимо изтриване на изпратените съобщения."; + +/* No comment provided by engineer. */ +"Allow to send files and media." = "Позволи изпращане на файлове и медия."; + +/* No comment provided by engineer. */ +"Allow to send voice messages." = "Позволи изпращане на гласови съобщения."; + +/* No comment provided by engineer. */ +"Allow voice messages only if your contact allows them." = "Позволи гласови съобщения само ако вашият контакт ги разрешава."; + +/* No comment provided by engineer. */ +"Allow voice messages?" = "Позволи гласови съобщения?"; + +/* No comment provided by engineer. */ +"Allow your contacts adding message reactions." = "Позволи на вашите контакти да добавят реакции към съобщения."; + +/* No comment provided by engineer. */ +"Allow your contacts to call you." = "Позволи на вашите контакти да ви се обаждат."; + +/* No comment provided by engineer. */ +"Allow your contacts to irreversibly delete sent messages." = "Позволи на вашите контакти да изтриват необратимо изпратените съобщения."; + +/* No comment provided by engineer. */ +"Allow your contacts to send disappearing messages." = "Позволи на вашите контакти да изпращат изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Allow your contacts to send voice messages." = "Позволи на вашите контакти да изпращат гласови съобщения."; + +/* No comment provided by engineer. */ +"Already connected?" = "Вече сте свързани?"; + +/* pref value */ +"always" = "винаги"; + +/* No comment provided by engineer. */ +"Always use relay" = "Винаги използвай реле"; + +/* No comment provided by engineer. */ +"An empty chat profile with the provided name is created, and the app opens as usual." = "Създаен беше празен профил за чат с предоставеното име и приложението се отвари както обикновено."; + +/* No comment provided by engineer. */ +"Answer call" = "Отговор на повикване"; + +/* No comment provided by engineer. */ +"App build: %@" = "Компилация на приложението: %@"; + +/* No comment provided by engineer. */ +"App icon" = "Икона на приложението"; + +/* No comment provided by engineer. */ +"App passcode" = "Код за достъп до приложението"; + +/* No comment provided by engineer. */ +"App passcode is replaced with self-destruct passcode." = "Кода за достъп до приложение се заменя с код за самоунищожение."; + +/* No comment provided by engineer. */ +"App version" = "Версия на приложението"; + +/* No comment provided by engineer. */ +"App version: v%@" = "Версия на приложението: v%@"; + +/* No comment provided by engineer. */ +"Appearance" = "Изглед"; + +/* No comment provided by engineer. */ +"Attach" = "Прикачи"; + +/* No comment provided by engineer. */ +"Audio & video calls" = "Аудио и видео разговори"; + +/* No comment provided by engineer. */ +"Audio and video calls" = "Аудио и видео разговори"; + +/* No comment provided by engineer. */ +"audio call (not e2e encrypted)" = "аудио разговор (не е e2e криптиран)"; + +/* chat feature */ +"Audio/video calls" = "Аудио/видео разговори"; + +/* No comment provided by engineer. */ +"Audio/video calls are prohibited." = "Аудио/видео разговорите са забранени."; + +/* PIN entry */ +"Authentication cancelled" = "Идентификацията е отменена"; + +/* No comment provided by engineer. */ +"Authentication failed" = "Неуспешна идентификация"; + +/* No comment provided by engineer. */ +"Authentication is required before the call is connected, but you may miss calls." = "Изисква се идентификацията, преди да се осъществи обаждането, но може да пропуснете повиквания."; + +/* No comment provided by engineer. */ +"Authentication unavailable" = "Идентификацията е недостъпна"; + +/* No comment provided by engineer. */ +"Auto-accept" = "Автоматично приемане"; + +/* No comment provided by engineer. */ +"Auto-accept contact requests" = "Автоматично приемане на заявки за контакт"; + +/* No comment provided by engineer. */ +"Auto-accept images" = "Автоматично приемане на изображения"; + +/* No comment provided by engineer. */ +"Back" = "Назад"; + +/* integrity error chat item */ +"bad message hash" = "лош хеш на съобщението"; + +/* No comment provided by engineer. */ +"Bad message hash" = "Лош хеш на съобщението"; + +/* integrity error chat item */ +"bad message ID" = "лошо ID на съобщението"; + +/* No comment provided by engineer. */ +"Bad message ID" = "Лошо ID на съобщението"; + +/* No comment provided by engineer. */ +"Better messages" = "По-добри съобщения"; + +/* No comment provided by engineer. */ +"bold" = "удебелен"; + +/* No comment provided by engineer. */ +"Both you and your contact can add message reactions." = "И вие, и вашият контакт можете да добавяте реакции към съобщението."; + +/* No comment provided by engineer. */ +"Both you and your contact can irreversibly delete sent messages." = "И вие, и вашият контакт можете да изтриете необратимо изпратените съобщения."; + +/* No comment provided by engineer. */ +"Both you and your contact can make calls." = "И вие, и вашият контакт можете да осъществявате обаждания."; + +/* No comment provided by engineer. */ +"Both you and your contact can send disappearing messages." = "И вие, и вашият контакт можете да изпращате изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Both you and your contact can send voice messages." = "И вие, и вашият контакт можете да изпращате гласови съобщения."; + +/* No comment provided by engineer. */ +"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Чрез чат профил (по подразбиране) или [чрез връзка](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (БЕТА)."; + +/* No comment provided by engineer. */ +"Call already ended!" = "Разговорът вече приключи!"; + +/* call status */ +"call error" = "грешка при повикване"; + +/* call status */ +"call in progress" = "в момента тече разговор"; + +/* call status */ +"calling…" = "повикване…"; + +/* No comment provided by engineer. */ +"Calls" = "Обаждания"; + +/* No comment provided by engineer. */ +"Can't delete user profile!" = "Потребителският профил не може да се изтрие!"; + +/* No comment provided by engineer. */ +"Can't invite contact!" = "Не може да покани контакта!"; + +/* No comment provided by engineer. */ +"Can't invite contacts!" = "Не може да поканят контактите!"; + +/* No comment provided by engineer. */ +"Cancel" = "Отказ"; + +/* feature offered item */ +"cancelled %@" = "отменен %@"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Няма достъп до Keychain за запазване на паролата за базата данни"; + +/* No comment provided by engineer. */ +"Cannot receive file" = "Файлът не може да бъде получен"; + +/* No comment provided by engineer. */ +"Change" = "Промени"; + +/* No comment provided by engineer. */ +"Change database passphrase?" = "Промяна на паролата на базата данни?"; + +/* authentication reason */ +"Change lock mode" = "Промяна на режима на заключване"; + +/* No comment provided by engineer. */ +"Change member role?" = "Промяна на ролята на члена?"; + +/* authentication reason */ +"Change passcode" = "Промени kодa за достъп"; + +/* No comment provided by engineer. */ +"Change receiving address" = "Промени адреса за получаване"; + +/* No comment provided by engineer. */ +"Change receiving address?" = "Промени адреса за получаване?"; + +/* No comment provided by engineer. */ +"Change role" = "Промени ролята"; + +/* authentication reason */ +"Change self-destruct mode" = "Промени режима на самоунищожение"; + +/* authentication reason + set passcode view */ +"Change self-destruct passcode" = "Промени кода за достъп за самоунищожение"; + +/* chat item text */ +"changed address for you" = "променен е адреса за вас"; + +/* rcv group event chat item */ +"changed role of %@ to %@" = "променена роля от %1$@ на %2$@"; + +/* rcv group event chat item */ +"changed your role to %@" = "променена е вашата ролята на %@"; + +/* chat item text */ +"changing address for %@…" = "промяна на адреса за %@…"; + +/* chat item text */ +"changing address…" = "промяна на адреса…"; + +/* No comment provided by engineer. */ +"Chat archive" = "Архив на чата"; + +/* No comment provided by engineer. */ +"Chat console" = "Конзола"; + +/* No comment provided by engineer. */ +"Chat database" = "База данни за чата"; + +/* No comment provided by engineer. */ +"Chat database deleted" = "Базата данни на чата е изтрита"; + +/* No comment provided by engineer. */ +"Chat database imported" = "Базата данни на чат е импортирана"; + +/* No comment provided by engineer. */ +"Chat is running" = "Чатът работи"; + +/* No comment provided by engineer. */ +"Chat is stopped" = "Чатът е спрян"; + +/* No comment provided by engineer. */ +"Chat preferences" = "Чат настройки"; + +/* No comment provided by engineer. */ +"Chats" = "Чатове"; + +/* No comment provided by engineer. */ +"Check server address and try again." = "Проверете адреса на сървъра и опитайте отново."; + +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Китайски и Испански интерфейс"; + +/* No comment provided by engineer. */ +"Choose file" = "Избери файл"; + +/* No comment provided by engineer. */ +"Choose from library" = "Избери от библиотеката"; + +/* No comment provided by engineer. */ +"Clear" = "Изчисти"; + +/* No comment provided by engineer. */ +"Clear conversation" = "Изчисти разговора"; + +/* No comment provided by engineer. */ +"Clear conversation?" = "Изчисти разговора?"; + +/* No comment provided by engineer. */ +"Clear verification" = "Изчисти проверката"; + +/* No comment provided by engineer. */ +"colored" = "цветен"; + +/* No comment provided by engineer. */ +"Colors" = "Цветове"; + +/* server test step */ +"Compare file" = "Сравни файл"; + +/* No comment provided by engineer. */ +"Compare security codes with your contacts." = "Сравнете кодовете за сигурност с вашите контакти."; + +/* No comment provided by engineer. */ +"complete" = "завършен"; + +/* No comment provided by engineer. */ +"Configure ICE servers" = "Конфигурирай ICE сървъри"; + +/* No comment provided by engineer. */ +"Confirm" = "Потвърди"; + +/* No comment provided by engineer. */ +"Confirm database upgrades" = "Потвърди актуализаациите на базата данни"; + +/* No comment provided by engineer. */ +"Confirm new passphrase…" = "Потвърди новата парола…"; + +/* No comment provided by engineer. */ +"Confirm Passcode" = "Потвърди kодa за достъп"; + +/* No comment provided by engineer. */ +"Confirm password" = "Потвърди парола"; + +/* server test step */ +"Connect" = "Свързване"; + +/* No comment provided by engineer. */ +"Connect directly" = "Свързване директно"; + +/* No comment provided by engineer. */ +"Connect incognito" = "Свързване инкогнито"; + +/* No comment provided by engineer. */ +"connect to SimpleX Chat developers." = "свържете се с разработчиците на SimpleX Chat."; + +/* No comment provided by engineer. */ +"Connect via contact link" = "Свързване чрез линк на контакта"; + +/* No comment provided by engineer. */ +"Connect via group link?" = "Свързване чрез групов линк?"; + +/* No comment provided by engineer. */ +"Connect via link" = "Свърване чрез линк"; + +/* No comment provided by engineer. */ +"Connect via link / QR code" = "Свърване чрез линк/QR код"; + +/* No comment provided by engineer. */ +"Connect via one-time link" = "Свързване чрез еднократен линк за връзка"; + +/* No comment provided by engineer. */ +"connected" = "свързан"; + +/* No comment provided by engineer. */ +"connecting" = "свързване"; + +/* No comment provided by engineer. */ +"connecting (accepted)" = "свързване (прието)"; + +/* No comment provided by engineer. */ +"connecting (announced)" = "свързване (обявено)"; + +/* No comment provided by engineer. */ +"connecting (introduced)" = "свързване (представен)"; + +/* No comment provided by engineer. */ +"connecting (introduction invitation)" = "свързване (покана за представяне)"; + +/* call status */ +"connecting call" = "разговорът се свързва…"; + +/* No comment provided by engineer. */ +"Connecting server…" = "Свързване със сървъра…"; + +/* No comment provided by engineer. */ +"Connecting server… (error: %@)" = "Свързване със сървър…(грешка: %@)"; + +/* chat list item title */ +"connecting…" = "свързване…"; + +/* No comment provided by engineer. */ +"Connection" = "Връзка"; + +/* No comment provided by engineer. */ +"Connection error" = "Грешка при свързване"; + +/* No comment provided by engineer. */ +"Connection error (AUTH)" = "Грешка при свързване (AUTH)"; + +/* chat list item title (it should not be shown */ +"connection established" = "установена е връзка"; + +/* No comment provided by engineer. */ +"Connection request sent!" = "Заявката за връзка е изпратена!"; + +/* No comment provided by engineer. */ +"Connection timeout" = "Времето на изчакване за установяване на връзката изтече"; + +/* connection information */ +"connection:%@" = "връзка:%@"; + +/* No comment provided by engineer. */ +"Contact allows" = "Контактът позволява"; + +/* 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 has e2e encryption" = "контактът има e2e криптиране"; + +/* No comment provided by engineer. */ +"contact has no e2e encryption" = "контактът няма e2e криптиране"; + +/* notification */ +"Contact hidden:" = "Контактът е скрит:"; + +/* notification */ +"Contact is connected" = "Контактът е свързан"; + +/* No comment provided by engineer. */ +"Contact is not connected yet!" = "Контактът все още не е свързан!"; + +/* No comment provided by engineer. */ +"Contact name" = "Име на контакт"; + +/* No comment provided by engineer. */ +"Contact preferences" = "Настройки за контакт"; + +/* No comment provided by engineer. */ +"Contacts" = "Контакти"; + +/* No comment provided by engineer. */ +"Contacts can mark messages for deletion; you will be able to view them." = "Контактите могат да маркират съобщения за изтриване; ще можете да ги разглеждате."; + +/* No comment provided by engineer. */ +"Continue" = "Продължи"; + +/* chat item action */ +"Copy" = "Копирай"; + +/* No comment provided by engineer. */ +"Core version: v%@" = "Версия на ядрото: v%@"; + +/* No comment provided by engineer. */ +"Create" = "Създай"; + +/* No comment provided by engineer. */ +"Create an address to let people connect with you." = "Създайте адрес, за да позволите на хората да се свързват с вас."; + +/* server test step */ +"Create file" = "Създай файл"; + +/* No comment provided by engineer. */ +"Create group link" = "Създай групов линк"; + +/* No comment provided by engineer. */ +"Create link" = "Създай линк"; + +/* No comment provided by engineer. */ +"Create one-time invitation link" = "Създай линк за еднократна покана"; + +/* server test step */ +"Create queue" = "Създай опашка"; + +/* No comment provided by engineer. */ +"Create secret group" = "Създай тайна група"; + +/* No comment provided by engineer. */ +"Create SimpleX address" = "Създай SimpleX адрес"; + +/* No comment provided by engineer. */ +"Create your profile" = "Създай своя профил"; + +/* No comment provided by engineer. */ +"Created on %@" = "Създаден на %@"; + +/* No comment provided by engineer. */ +"creator" = "създател"; + +/* No comment provided by engineer. */ +"Current Passcode" = "Текущ kод за достъп"; + +/* No comment provided by engineer. */ +"Current passphrase…" = "Текуща парола…"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "В момента максималният поддържан размер на файла е %@."; + +/* dropdown time picker choice */ +"custom" = "персонализиран"; + +/* No comment provided by engineer. */ +"Custom time" = "Персонализирано време"; + +/* No comment provided by engineer. */ +"Dark" = "Тъмна"; + +/* No comment provided by engineer. */ +"Database downgrade" = "Понижаване на версията на базата данни"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Базата данни е криптирана!"; + +/* No comment provided by engineer. */ +"Database encryption passphrase will be updated and stored in the keychain.\n" = "Паролата за криптиране на базата данни ще бъде актуализирана и съхранена в Keychain.\n"; + +/* No comment provided by engineer. */ +"Database encryption passphrase will be updated.\n" = "Паролата за криптиране на базата данни ще бъде актуализирана.\n"; + +/* No comment provided by engineer. */ +"Database error" = "Грешка в базата данни"; + +/* No comment provided by engineer. */ +"Database ID" = "ID в базата данни"; + +/* copied message info */ +"Database ID: %d" = "ID в базата данни: %d"; + +/* No comment provided by engineer. */ +"Database IDs and Transport isolation option." = "Идентификатори в базата данни и опция за изолация на транспорта."; + +/* No comment provided by engineer. */ +"Database is encrypted using a random passphrase, you can change it." = "Базата данни е криптирана с автоматично генерирана парола, можете да я промените."; + +/* No comment provided by engineer. */ +"Database is encrypted using a random passphrase. Please change it before exporting." = "Базата данни е криптирана с автоматично генерирана парола. Моля, променете я преди експортиране."; + +/* No comment provided by engineer. */ +"Database passphrase" = "Парола за базата данни"; + +/* No comment provided by engineer. */ +"Database passphrase & export" = "Парола за базата данни и експортиране"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Паролата на базата данни е различна от записаната в Keychain."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Изисква се паролата за базата данни, за да се отвори чата."; + +/* No comment provided by engineer. */ +"Database upgrade" = "Актуализация на базата данни"; + +/* No comment provided by engineer. */ +"database version is newer than the app, but no down migration for: %@" = "версията на базата данни е по-нова от приложението, но няма миграция надолу за: %@"; + +/* No comment provided by engineer. */ +"Database will be encrypted and the passphrase stored in the keychain.\n" = "Базата данни ще бъде криптирана и паролата ще бъде съхранена в Keychain.\n"; + +/* No comment provided by engineer. */ +"Database will be encrypted.\n" = "Базата данни ще бъде криптирана.\n"; + +/* No comment provided by engineer. */ +"Database will be migrated when the app restarts" = "Базата данни ще бъде мигрирана, когато приложението се рестартира"; + +/* time unit */ +"days" = "дни"; + +/* No comment provided by engineer. */ +"Decentralized" = "Децентрализиран"; + +/* message decrypt error item */ +"Decryption error" = "Грешка при декриптиране"; + +/* pref value */ +"default (%@)" = "по подразбиране (%@)"; + +/* No comment provided by engineer. */ +"default (no)" = "по подразбиране (не)"; + +/* No comment provided by engineer. */ +"default (yes)" = "по подразбиране (да)"; + +/* chat item action */ +"Delete" = "Изтрий"; + +/* No comment provided by engineer. */ +"Delete address" = "Изтрий адрес"; + +/* No comment provided by engineer. */ +"Delete address?" = "Изтрий адрес?"; + +/* No comment provided by engineer. */ +"Delete after" = "Изтрий след"; + +/* No comment provided by engineer. */ +"Delete all files" = "Изтрий всички файлове"; + +/* No comment provided by engineer. */ +"Delete archive" = "Изтрий архив"; + +/* No comment provided by engineer. */ +"Delete chat archive?" = "Изтриване на архива на чата?"; + +/* No comment provided by engineer. */ +"Delete chat profile" = "Изтрий чат профила"; + +/* No comment provided by engineer. */ +"Delete chat profile?" = "Изтриване на чат профила?"; + +/* No comment provided by engineer. */ +"Delete connection" = "Изтрий връзката"; + +/* No comment provided by engineer. */ +"Delete contact" = "Изтрий контакт"; + +/* No comment provided by engineer. */ +"Delete Contact" = "Изтрий контакт"; + +/* No comment provided by engineer. */ +"Delete contact?" = "Изтрий контакт?"; + +/* No comment provided by engineer. */ +"Delete database" = "Изтрий базата данни"; + +/* server test step */ +"Delete file" = "Изтрий файл"; + +/* No comment provided by engineer. */ +"Delete files and media?" = "Изтрий файлове и медия?"; + +/* No comment provided by engineer. */ +"Delete files for all chat profiles" = "Изтрий файловете за всички чат профили"; + +/* chat feature */ +"Delete for everyone" = "Изтрий за всички"; + +/* No comment provided by engineer. */ +"Delete for me" = "Изтрий за мен"; + +/* No comment provided by engineer. */ +"Delete group" = "Изтрий група"; + +/* No comment provided by engineer. */ +"Delete group?" = "Изтрий група?"; + +/* No comment provided by engineer. */ +"Delete invitation" = "Изтрий поканата"; + +/* No comment provided by engineer. */ +"Delete link" = "Изтрий линк"; + +/* No comment provided by engineer. */ +"Delete link?" = "Изтрий линк?"; + +/* No comment provided by engineer. */ +"Delete member message?" = "Изтрий съобщението на члена?"; + +/* No comment provided by engineer. */ +"Delete message?" = "Изтрий съобщението?"; + +/* No comment provided by engineer. */ +"Delete messages" = "Изтрий съобщенията"; + +/* No comment provided by engineer. */ +"Delete messages after" = "Изтрий съобщенията след"; + +/* No comment provided by engineer. */ +"Delete old database" = "Изтрий старата база данни"; + +/* No comment provided by engineer. */ +"Delete old database?" = "Изтрий старата база данни?"; + +/* No comment provided by engineer. */ +"Delete pending connection" = "Изтрий предстоящата връзка"; + +/* No comment provided by engineer. */ +"Delete pending connection?" = "Изтрий предстоящата връзка?"; + +/* No comment provided by engineer. */ +"Delete profile" = "Изтрий профил"; + +/* server test step */ +"Delete queue" = "Изтрий опашка"; + +/* No comment provided by engineer. */ +"Delete user profile?" = "Изтрий потребителския профил?"; + +/* deleted chat item */ +"deleted" = "изтрит"; + +/* No comment provided by engineer. */ +"Deleted at" = "Изтрито на"; + +/* copied message info */ +"Deleted at: %@" = "Изтрито на: %@"; + +/* rcv group event chat item */ +"deleted group" = "групата изтрита"; + +/* No comment provided by engineer. */ +"Delivery" = "Доставка"; + +/* No comment provided by engineer. */ +"Delivery receipts are disabled!" = "Потвърждениeто за доставка е деактивирано!"; + +/* No comment provided by engineer. */ +"Delivery receipts!" = "Потвърждениe за доставка!"; + +/* No comment provided by engineer. */ +"Description" = "Описание"; + +/* No comment provided by engineer. */ +"Develop" = "Разработване"; + +/* No comment provided by engineer. */ +"Developer tools" = "Инструменти за разработчици"; + +/* No comment provided by engineer. */ +"Device" = "Устройство"; + +/* No comment provided by engineer. */ +"Device authentication is disabled. Turning off SimpleX Lock." = "Идентификацията на устройството е деактивирано. Изключване на SimpleX заключване."; + +/* No comment provided by engineer. */ +"Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication." = "Идентификацията на устройството не е активирана. Можете да включите SimpleX заключване през Настройки, след като активирате идентификацията на устройството."; + +/* No comment provided by engineer. */ +"different migration in the app/database: %@ / %@" = "различна миграция в приложението/базата данни: %@ / %@"; + +/* No comment provided by engineer. */ +"Different names, avatars and transport isolation." = "Различни имена, аватари и транспортна изолация."; + +/* connection level description */ +"direct" = "директна"; + +/* chat feature */ +"Direct messages" = "Лични съобщения"; + +/* No comment provided by engineer. */ +"Direct messages between members are prohibited in this group." = "Личните съобщения между членовете са забранени в тази група."; + +/* No comment provided by engineer. */ +"Disable (keep overrides)" = "Деактивиране (запазване на промените)"; + +/* No comment provided by engineer. */ +"Disable for all" = "Деактивиране за всички"; + +/* authentication reason */ +"Disable SimpleX Lock" = "Деактивирай SimpleX заключване"; + +/* No comment provided by engineer. */ +"disabled" = "деактивирано"; + +/* No comment provided by engineer. */ +"Disappearing message" = "Изчезващо съобщение"; + +/* chat feature */ +"Disappearing messages" = "Изчезващи съобщения"; + +/* No comment provided by engineer. */ +"Disappearing messages are prohibited in this chat." = "Изчезващите съобщения са забранени в този чат."; + +/* No comment provided by engineer. */ +"Disappearing messages are prohibited in this group." = "Изчезващите съобщения са забранени в тази група."; + +/* No comment provided by engineer. */ +"Disappears at" = "Изчезва в"; + +/* copied message info */ +"Disappears at: %@" = "Изчезва в: %@"; + +/* server test step */ +"Disconnect" = "Прекъсни връзката"; + +/* No comment provided by engineer. */ +"Display name" = "Показвано Име"; + +/* No comment provided by engineer. */ +"Display name:" = "Показвано име:"; + +/* No comment provided by engineer. */ +"Do it later" = "Отложи"; + +/* No comment provided by engineer. */ +"Do NOT use SimpleX for emergency calls." = "НЕ използвайте SimpleX за спешни повиквания."; + +/* No comment provided by engineer. */ +"Don't create address" = "Не създавай адрес"; + +/* No comment provided by engineer. */ +"Don't enable" = "Не активирай"; + +/* No comment provided by engineer. */ +"Don't show again" = "Не показвай отново"; + +/* No comment provided by engineer. */ +"Downgrade and open chat" = "Понижи версията и отвори чата"; + +/* server test step */ +"Download file" = "Свали файл"; + +/* No comment provided by engineer. */ +"Duplicate display name!" = "Дублирано показвано име!"; + +/* integrity error chat item */ +"duplicate message" = "дублирано съобщение"; + +/* No comment provided by engineer. */ +"Duration" = "Продължителност"; + +/* No comment provided by engineer. */ +"e2e encrypted" = "e2e криптиран"; + +/* chat item action */ +"Edit" = "Редактирай"; + +/* No comment provided by engineer. */ +"Edit group profile" = "Редактирай групов профил"; + +/* No comment provided by engineer. */ +"Enable" = "Активирай"; + +/* No comment provided by engineer. */ +"Enable (keep overrides)" = "Активиране (запазване на промените)"; + +/* No comment provided by engineer. */ +"Enable automatic message deletion?" = "Активиране на автоматично изтриване на съобщения?"; + +/* No comment provided by engineer. */ +"Enable for all" = "Активиране за всички"; + +/* No comment provided by engineer. */ +"Enable instant notifications?" = "Активирай незабавни известия?"; + +/* No comment provided by engineer. */ +"Enable lock" = "Активирай заключване"; + +/* No comment provided by engineer. */ +"Enable notifications" = "Активирай известията"; + +/* No comment provided by engineer. */ +"Enable periodic notifications?" = "Активирай периодични известия?"; + +/* No comment provided by engineer. */ +"Enable self-destruct" = "Активирай самоунищожение"; + +/* set passcode view */ +"Enable self-destruct passcode" = "Активирай kод за достъп за самоунищожение"; + +/* authentication reason */ +"Enable SimpleX Lock" = "Активирай SimpleX заключване"; + +/* No comment provided by engineer. */ +"Enable TCP keep-alive" = "Активирай TCP keep-alive"; + +/* enabled status */ +"enabled" = "активирано"; + +/* enabled status */ +"enabled for contact" = "активирано за контакт"; + +/* enabled status */ +"enabled for you" = "активирано за вас"; + +/* No comment provided by engineer. */ +"Encrypt" = "Криптирай"; + +/* No comment provided by engineer. */ +"Encrypt database?" = "Криптиране на база данни?"; + +/* No comment provided by engineer. */ +"Encrypt local files" = "Криптирай локални файлове"; + +/* No comment provided by engineer. */ +"Encrypted database" = "Криптирана база данни"; + +/* notification */ +"Encrypted message or another event" = "Криптирано съобщение или друго събитие"; + +/* notification */ +"Encrypted message: database error" = "Криптирано съобщение: грешка в базата данни"; + +/* notification */ +"Encrypted message: database migration error" = "Криптирано съобщение: грешка при мигрирането на база данни"; + +/* notification */ +"Encrypted message: keychain error" = "Криптирано съобщение: грешка в keychain"; + +/* notification */ +"Encrypted message: no passphrase" = "Криптирано съобщение: няма парола"; + +/* notification */ +"Encrypted message: unexpected error" = "Криптирано съобщение: неочаквана грешка"; + +/* chat item text */ +"encryption agreed" = "криптирането е съгласувано"; + +/* chat item text */ +"encryption agreed for %@" = "криптирането е съгласувано за %@"; + +/* chat item text */ +"encryption ok" = "криптирането работи"; + +/* chat item text */ +"encryption ok for %@" = "криптирането работи за %@"; + +/* chat item text */ +"encryption re-negotiation allowed" = "разрешено повторно договаряне на криптиране"; + +/* chat item text */ +"encryption re-negotiation allowed for %@" = "разрешено повторно договаряне на криптиране за %@"; + +/* chat item text */ +"encryption re-negotiation required" = "необходимо е повторно договаряне на криптиране"; + +/* chat item text */ +"encryption re-negotiation required for %@" = "необходимо е повторно договаряне на криптиране за %@"; + +/* No comment provided by engineer. */ +"ended" = "приключен"; + +/* call status */ +"ended call %@" = "приключи разговор %@"; + +/* No comment provided by engineer. */ +"Enter correct passphrase." = "Въведи правилна парола."; + +/* No comment provided by engineer. */ +"Enter Passcode" = "Въведете kодa за достъп"; + +/* No comment provided by engineer. */ +"Enter passphrase…" = "Въведи парола…"; + +/* No comment provided by engineer. */ +"Enter password above to show!" = "Въведете парола по-горе, за да се покаже!"; + +/* No comment provided by engineer. */ +"Enter server manually" = "Въведи сървъра ръчно"; + +/* placeholder */ +"Enter welcome message…" = "Въведи съобщение при посрещане…"; + +/* placeholder */ +"Enter welcome message… (optional)" = "Въведи съобщение при посрещане…(незадължително)"; + +/* No comment provided by engineer. */ +"error" = "грешка"; + +/* No comment provided by engineer. */ +"Error" = "Грешка при свързване със сървъра"; + +/* No comment provided by engineer. */ +"Error aborting address change" = "Грешка при отказване на промяна на адреса"; + +/* No comment provided by engineer. */ +"Error accepting contact request" = "Грешка при приемане на заявка за контакт"; + +/* No comment provided by engineer. */ +"Error accessing database file" = "Грешка при достъпа до файла с базата данни"; + +/* No comment provided by engineer. */ +"Error adding member(s)" = "Грешка при добавяне на член(ове)"; + +/* No comment provided by engineer. */ +"Error changing address" = "Грешка при промяна на адреса"; + +/* No comment provided by engineer. */ +"Error changing role" = "Грешка при промяна на ролята"; + +/* No comment provided by engineer. */ +"Error changing setting" = "Грешка при промяна на настройката"; + +/* No comment provided by engineer. */ +"Error creating address" = "Грешка при създаване на адрес"; + +/* No comment provided by engineer. */ +"Error creating group" = "Грешка при създаване на група"; + +/* No comment provided by engineer. */ +"Error creating group link" = "Грешка при създаване на групов линк"; + +/* No comment provided by engineer. */ +"Error creating profile!" = "Грешка при създаване на профил!"; + +/* No comment provided by engineer. */ +"Error decrypting file" = "Грешка при декриптирането на файла"; + +/* No comment provided by engineer. */ +"Error deleting chat database" = "Грешка при изтриване на чат базата данни"; + +/* No comment provided by engineer. */ +"Error deleting chat!" = "Грешка при изтриването на чата!"; + +/* No comment provided by engineer. */ +"Error deleting connection" = "Грешка при изтриване на връзката"; + +/* No comment provided by engineer. */ +"Error deleting contact" = "Грешка при изтриване на контакт"; + +/* No comment provided by engineer. */ +"Error deleting database" = "Грешка при изтриване на базата данни"; + +/* No comment provided by engineer. */ +"Error deleting old database" = "Грешка при изтриване на старата база данни"; + +/* No comment provided by engineer. */ +"Error deleting token" = "Грешка при изтриването на токена"; + +/* No comment provided by engineer. */ +"Error deleting user profile" = "Грешка при изтриване на потребителския профил"; + +/* No comment provided by engineer. */ +"Error enabling delivery receipts!" = "Грешка при активирането на потвърждениeто за доставка!"; + +/* No comment provided by engineer. */ +"Error enabling notifications" = "Грешка при активирането на известията"; + +/* No comment provided by engineer. */ +"Error encrypting database" = "Грешка при криптиране на базата данни"; + +/* No comment provided by engineer. */ +"Error exporting chat database" = "Грешка при експортиране на чат базата данни"; + +/* No comment provided by engineer. */ +"Error importing chat database" = "Грешка при импортиране на чат базата данни"; + +/* No comment provided by engineer. */ +"Error joining group" = "Грешка при присъединяване към група"; + +/* No comment provided by engineer. */ +"Error loading %@ servers" = "Грешка при зареждане на %@ сървъри"; + +/* No comment provided by engineer. */ +"Error receiving file" = "Грешка при получаване на файл"; + +/* No comment provided by engineer. */ +"Error removing member" = "Грешка при отстраняване на член"; + +/* No comment provided by engineer. */ +"Error saving %@ servers" = "Грешка при запазване на %@ сървъра"; + +/* No comment provided by engineer. */ +"Error saving group profile" = "Грешка при запазване на профила на групата"; + +/* No comment provided by engineer. */ +"Error saving ICE servers" = "Грешка при запазване на ICE сървърите"; + +/* No comment provided by engineer. */ +"Error saving passcode" = "Грешка при запазване на кода за достъп"; + +/* No comment provided by engineer. */ +"Error saving passphrase to keychain" = "Грешка при запазване на парола в Кeychain"; + +/* No comment provided by engineer. */ +"Error saving user password" = "Грешка при запазване на потребителска парола"; + +/* No comment provided by engineer. */ +"Error sending email" = "Грешка при изпращане на имейл"; + +/* No comment provided by engineer. */ +"Error sending message" = "Грешка при изпращане на съобщение"; + +/* No comment provided by engineer. */ +"Error setting delivery receipts!" = "Грешка при настройването на потвърждениeто за доставка!!"; + +/* No comment provided by engineer. */ +"Error starting chat" = "Грешка при стартиране на чата"; + +/* No comment provided by engineer. */ +"Error stopping chat" = "Грешка при спиране на чата"; + +/* No comment provided by engineer. */ +"Error switching profile!" = "Грешка при смяна на профил!"; + +/* No comment provided by engineer. */ +"Error synchronizing connection" = "Грешка при синхронизиране на връзката"; + +/* No comment provided by engineer. */ +"Error updating group link" = "Грешка при актуализиране на груповия линк"; + +/* No comment provided by engineer. */ +"Error updating message" = "Грешка при актуализиране на съобщението"; + +/* No comment provided by engineer. */ +"Error updating settings" = "Грешка при актуализиране на настройките"; + +/* No comment provided by engineer. */ +"Error updating user privacy" = "Грешка при актуализиране на поверителността на потребителя"; + +/* No comment provided by engineer. */ +"Error: " = "Грешка: "; + +/* No comment provided by engineer. */ +"Error: %@" = "Грешка: %@"; + +/* No comment provided by engineer. */ +"Error: no database file" = "Грешка: няма файл с база данни"; + +/* No comment provided by engineer. */ +"Error: URL is invalid" = "Грешка: URL адресът е невалиден"; + +/* No comment provided by engineer. */ +"Even when disabled in the conversation." = "Дори когато е деактивиран в разговора."; + +/* No comment provided by engineer. */ +"event happened" = "събитие се случи"; + +/* No comment provided by engineer. */ +"Exit without saving" = "Изход без запазване"; + +/* No comment provided by engineer. */ +"Export database" = "Експортирай база данни"; + +/* No comment provided by engineer. */ +"Export error:" = "Грешка при експортиране:"; + +/* No comment provided by engineer. */ +"Exported database archive." = "Експортиран архив на базата данни."; + +/* No comment provided by engineer. */ +"Exporting database archive…" = "Експортиране на архив на базата данни…"; + +/* No comment provided by engineer. */ +"Failed to remove passphrase" = "Премахването на паролата е неуспешно"; + +/* No comment provided by engineer. */ +"Fast and no wait until the sender is online!" = "Бързо и без чакане, докато подателят е онлайн!"; + +/* No comment provided by engineer. */ +"Favorite" = "Любим"; + +/* No comment provided by engineer. */ +"File will be deleted from servers." = "Файлът ще бъде изтрит от сървърите."; + +/* No comment provided by engineer. */ +"File will be received when your contact completes uploading it." = "Файлът ще бъде получен, когато вашият контакт завърши качването му."; + +/* No comment provided by engineer. */ +"File will be received when your contact is online, please wait or check later!" = "Файлът ще бъде получен, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно!"; + +/* No comment provided by engineer. */ +"File: %@" = "Файл: %@"; + +/* No comment provided by engineer. */ +"Files & media" = "Файлове и медия"; + +/* chat feature */ +"Files and media" = "Файлове и медия"; + +/* No comment provided by engineer. */ +"Files and media are prohibited in this group." = "Файловете и медията са забранени в тази група."; + +/* No comment provided by engineer. */ +"Files and media prohibited!" = "Файловете и медията са забранени!"; + +/* No comment provided by engineer. */ +"Filter unread and favorite chats." = "Филтрирайте непрочетените и любимите чатове."; + +/* No comment provided by engineer. */ +"Finally, we have them! 🚀" = "Най-накрая ги имаме! 🚀"; + +/* No comment provided by engineer. */ +"Find chats faster" = "Намирайте чатове по-бързо"; + +/* No comment provided by engineer. */ +"Fix" = "Поправи"; + +/* No comment provided by engineer. */ +"Fix connection" = "Поправи връзката"; + +/* No comment provided by engineer. */ +"Fix connection?" = "Поправи връзката?"; + +/* No comment provided by engineer. */ +"Fix encryption after restoring backups." = "Оправяне на криптирането след възстановяване от резервни копия."; + +/* No comment provided by engineer. */ +"Fix not supported by contact" = "Поправката не се поддържа от контакта"; + +/* No comment provided by engineer. */ +"Fix not supported by group member" = "Поправката не се поддържа от члена на групата"; + +/* No comment provided by engineer. */ +"For console" = "За конзолата"; + +/* No comment provided by engineer. */ +"French interface" = "Френски интерфейс"; + +/* No comment provided by engineer. */ +"Full link" = "Цял линк"; + +/* No comment provided by engineer. */ +"Full name (optional)" = "Пълно име (незадължително)"; + +/* No comment provided by engineer. */ +"Full name:" = "Пълно име:"; + +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "Напълно преработено - работi във фонов режим!"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Допълнително намален разход на батерията"; + +/* No comment provided by engineer. */ +"GIFs and stickers" = "GIF файлове и стикери"; + +/* No comment provided by engineer. */ +"Group" = "Група"; + +/* No comment provided by engineer. */ +"group deleted" = "групата е изтрита"; + +/* No comment provided by engineer. */ +"Group display name" = "Показвано име на групата"; + +/* No comment provided by engineer. */ +"Group full name (optional)" = "Пълно име на групата (незадължително)"; + +/* No comment provided by engineer. */ +"Group image" = "Групово изображение"; + +/* No comment provided by engineer. */ +"Group invitation" = "Групова покана"; + +/* No comment provided by engineer. */ +"Group invitation expired" = "Груповата покана е изтекла"; + +/* No comment provided by engineer. */ +"Group invitation is no longer valid, it was removed by sender." = "Груповата покана вече е невалидна, премахната е от подателя."; + +/* No comment provided by engineer. */ +"Group link" = "Групов линк"; + +/* No comment provided by engineer. */ +"Group links" = "Групови линкове"; + +/* No comment provided by engineer. */ +"Group members can add message reactions." = "Членовете на групата могат да добавят реакции към съобщенията."; + +/* No comment provided by engineer. */ +"Group members can irreversibly delete sent messages." = "Членовете на групата могат необратимо да изтриват изпратените съобщения."; + +/* No comment provided by engineer. */ +"Group members can send direct messages." = "Членовете на групата могат да изпращат лични съобщения."; + +/* No comment provided by engineer. */ +"Group members can send disappearing messages." = "Членовете на групата могат да изпращат изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Group members can send files and media." = "Членовете на групата могат да изпращат файлове и медия."; + +/* No comment provided by engineer. */ +"Group members can send voice messages." = "Членовете на групата могат да изпращат гласови съобщения."; + +/* notification */ +"Group message:" = "Групово съобщение:"; + +/* No comment provided by engineer. */ +"Group moderation" = "Групово модериране"; + +/* No comment provided by engineer. */ +"Group preferences" = "Групови настройки"; + +/* No comment provided by engineer. */ +"Group profile" = "Групов профил"; + +/* No comment provided by engineer. */ +"Group profile is stored on members' devices, not on the servers." = "Груповият профил се съхранява на устройствата на членовете, а не на сървърите."; + +/* snd group event chat item */ +"group profile updated" = "профилът на групата е актуализиран"; + +/* No comment provided by engineer. */ +"Group welcome message" = "Съобщение при посрещане в групата"; + +/* No comment provided by engineer. */ +"Group will be deleted for all members - this cannot be undone!" = "Групата ще бъде изтрита за всички членове - това не може да бъде отменено!"; + +/* No comment provided by engineer. */ +"Group will be deleted for you - this cannot be undone!" = "Групата ще бъде изтрита за вас - това не може да бъде отменено!"; + +/* No comment provided by engineer. */ +"Help" = "Помощ"; + +/* No comment provided by engineer. */ +"Hidden" = "Скрит"; + +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Скрити чат профили"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Парола за скрит профил"; + +/* chat item action */ +"Hide" = "Скрий"; + +/* No comment provided by engineer. */ +"Hide app screen in the recent apps." = "Скриване на екрана на приложението в изгледа на скоро отворнените приложения."; + +/* No comment provided by engineer. */ +"Hide profile" = "Скрий профила"; + +/* No comment provided by engineer. */ +"Hide:" = "Скрий:"; + +/* No comment provided by engineer. */ +"History" = "История"; + +/* time unit */ +"hours" = "часове"; + +/* No comment provided by engineer. */ +"How it works" = "Как работи"; + +/* No comment provided by engineer. */ +"How SimpleX works" = "Как работи SimpleX"; + +/* No comment provided by engineer. */ +"How to" = "Информация"; + +/* No comment provided by engineer. */ +"How to use it" = "Как се използва"; + +/* No comment provided by engineer. */ +"How to use your servers" = "Как да използвате вашите сървъри"; + +/* No comment provided by engineer. */ +"ICE servers (one per line)" = "ICE сървъри (по един на ред)"; + +/* No comment provided by engineer. */ +"If you can't meet in person, show QR code in a video call, or share the link." = "Ако не можете да се срещнете лично, покажете QR код във видеоразговора или споделете линка."; + +/* 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 enter this passcode when opening the app, all app data will be irreversibly removed!" = "Ако въведете този kод за достъп, когато отваряте приложението, всички данни от приложението ще бъдат необратимо изтрити!"; + +/* No comment provided by engineer. */ +"If you enter your self-destruct passcode while opening the app:" = "Ако въведете kодa за достъп за самоунищожение, докато отваряте приложението:"; + +/* No comment provided by engineer. */ +"If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app)." = "Ако трябва да използвате чата сега, докоснете **Отложи** отдолу (ще ви бъде предложено да мигрирате базата данни, когато рестартирате приложението)."; + +/* No comment provided by engineer. */ +"Ignore" = "Игнорирай"; + +/* No comment provided by engineer. */ +"Image will be received when your contact completes uploading it." = "Изображението ще бъде получено, когато вашият контакт завърши качването му."; + +/* No comment provided by engineer. */ +"Image will be received when your contact is online, please wait or check later!" = "Изображението ще бъде получено, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно!"; + +/* No comment provided by engineer. */ +"Immediately" = "Веднага"; + +/* No comment provided by engineer. */ +"Immune to spam and abuse" = "Защитен от спам и злоупотреби"; + +/* No comment provided by engineer. */ +"Import" = "Импортиране"; + +/* No comment provided by engineer. */ +"Import chat database?" = "Импортиране на чат база данни?"; + +/* No comment provided by engineer. */ +"Import database" = "Импортиране на база данни"; + +/* No comment provided by engineer. */ +"Improved privacy and security" = "Подобрена поверителност и сигурност"; + +/* No comment provided by engineer. */ +"Improved server configuration" = "Подобрена конфигурация на сървъра"; + +/* No comment provided by engineer. */ +"In reply to" = "В отговор на"; + +/* No comment provided by engineer. */ +"Incognito" = "Инкогнито"; + +/* No comment provided by engineer. */ +"Incognito mode" = "Режим инкогнито"; + +/* No comment provided by engineer. */ +"Incognito mode protects your privacy by using a new random profile for each contact." = "Режимът инкогнито защитава вашата поверителност, като използва нов автоматично генериран профил за всеки контакт."; + +/* chat list item description */ +"incognito via contact address link" = "инкогнито чрез линк с адрес за контакт"; + +/* chat list item description */ +"incognito via group link" = "инкогнито чрез групов линк"; + +/* chat list item description */ +"incognito via one-time link" = "инкогнито чрез еднократен линк за връзка"; + +/* notification */ +"Incoming audio call" = "Входящо аудио повикване"; + +/* notification */ +"Incoming call" = "Входящо повикване"; + +/* notification */ +"Incoming video call" = "Входящо видео повикване"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Несъвместима версия на базата данни"; + +/* PIN entry */ +"Incorrect passcode" = "Неправилен kод за достъп"; + +/* No comment provided by engineer. */ +"Incorrect security code!" = "Неправилен код за сигурност!"; + +/* connection level description */ +"indirect (%d)" = "индиректна (%d)"; + +/* chat item action */ +"Info" = "Информация"; + +/* No comment provided by engineer. */ +"Initial role" = "Първоначална роля"; + +/* 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. */ +"Instant push notifications will be hidden!\n" = "Незабавните push известия ще бъдат скрити!\n"; + +/* No comment provided by engineer. */ +"Instantly" = "Мигновено"; + +/* No comment provided by engineer. */ +"Interface" = "Интерфейс"; + +/* invalid chat data */ +"invalid chat" = "невалиден чат"; + +/* No comment provided by engineer. */ +"invalid chat data" = "невалидни данни за чат"; + +/* No comment provided by engineer. */ +"Invalid connection link" = "Невалиден линк за връзка"; + +/* invalid chat item */ +"invalid data" = "невалидни данни"; + +/* No comment provided by engineer. */ +"Invalid server address!" = "Невалиден адрес на сървъра!"; + +/* item status text */ +"Invalid status" = "Невалиден статус"; + +/* No comment provided by engineer. */ +"Invitation expired!" = "Поканата е изтекла!"; + +/* group name */ +"invitation to group %@" = "покана за група %@"; + +/* No comment provided by engineer. */ +"Invite friends" = "Покани приятели"; + +/* No comment provided by engineer. */ +"Invite members" = "Покани членове"; + +/* No comment provided by engineer. */ +"Invite to group" = "Покани в групата"; + +/* No comment provided by engineer. */ +"invited" = "поканен"; + +/* rcv group event chat item */ +"invited %@" = "поканен %@"; + +/* chat list item title */ +"invited to connect" = "поканен да се свърже"; + +/* rcv group event chat item */ +"invited via your group link" = "поканен чрез вашия групов линк"; + +/* No comment provided by engineer. */ +"iOS Keychain is used to securely store passphrase - it allows receiving push notifications." = "iOS Keychain се използва за сигурно съхраняване на парола - позволява получаване на push известия."; + +/* No comment provided by engineer. */ +"iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "iOS Keychain ще се използва за сигурно съхраняване на паролата, след като рестартирате приложението или промените паролата - това ще позволи получаването на push известия."; + +/* No comment provided by engineer. */ +"Irreversible message deletion" = "Необратимо изтриване на съобщение"; + +/* No comment provided by engineer. */ +"Irreversible message deletion is prohibited in this chat." = "Необратимото изтриване на съобщения е забранено в този чат."; + +/* No comment provided by engineer. */ +"Irreversible message deletion is prohibited in this group." = "Необратимото изтриване на съобщения е забранено в тази група."; + +/* No comment provided by engineer. */ +"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Позволява да имате много анонимни връзки без споделени данни между тях в един чат профил ."; + +/* No comment provided by engineer. */ +"It can happen when you or your connection used the old database backup." = "Това може да се случи, когато вие или вашата връзка използвате старо резервно копие на базата данни."; + +/* No comment provided by engineer. */ +"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Това може да се случи, когато:\n1. Времето за пазене на съобщенията е изтекло - в изпращащия клиент е 2 дена а на сървъра е 30.\n2. Декриптирането на съобщението е неуспешно, защото вие или вашият контакт сте използвали старо копие на базата данни.\n3. Връзката е била компрометирана."; + +/* No comment provided by engineer. */ +"It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Изглежда, че вече сте свързани чрез този линк. Ако не е така, има грешка (%@)."; + +/* No comment provided by engineer. */ +"Italian interface" = "Италиански интерфейс"; + +/* No comment provided by engineer. */ +"italic" = "курсив"; + +/* No comment provided by engineer. */ +"Japanese interface" = "Японски интерфейс"; + +/* No comment provided by engineer. */ +"Join" = "Присъединяване"; + +/* No comment provided by engineer. */ +"join as %@" = "присъединяване като %@"; + +/* No comment provided by engineer. */ +"Join group" = "Влез в групата"; + +/* No comment provided by engineer. */ +"Join incognito" = "Влез инкогнито"; + +/* No comment provided by engineer. */ +"Joining group" = "Присъединяване към групата"; + +/* No comment provided by engineer. */ +"Keep your connections" = "Запазете връзките си"; + +/* No comment provided by engineer. */ +"Keychain error" = "Keychain грешка"; + +/* No comment provided by engineer. */ +"KeyChain error" = "KeyChain грешка"; + +/* No comment provided by engineer. */ +"Large file!" = "Голям файл!"; + +/* No comment provided by engineer. */ +"Learn more" = "Научете повече"; + +/* No comment provided by engineer. */ +"Leave" = "Напусни"; + +/* No comment provided by engineer. */ +"Leave group" = "Напусни групата"; + +/* No comment provided by engineer. */ +"Leave group?" = "Напусни групата?"; + +/* rcv group event chat item */ +"left" = "напусна"; + +/* email subject */ +"Let's talk in SimpleX Chat" = "Нека да поговорим в SimpleX Chat"; + +/* No comment provided by engineer. */ +"Light" = "Светла"; + +/* No comment provided by engineer. */ +"Limitations" = "Ограничения"; + +/* No comment provided by engineer. */ +"LIVE" = "НА ЖИВО"; + +/* No comment provided by engineer. */ +"Live message!" = "Съобщение на живо!"; + +/* No comment provided by engineer. */ +"Live messages" = "Съобщения на живо"; + +/* No comment provided by engineer. */ +"Local name" = "Локално име"; + +/* No comment provided by engineer. */ +"Local profile data only" = "Само данни за локален профил"; + +/* No comment provided by engineer. */ +"Lock after" = "Заключване след"; + +/* No comment provided by engineer. */ +"Lock mode" = "Режим на заключване"; + +/* No comment provided by engineer. */ +"Make a private connection" = "Добави поверителна връзка"; + +/* No comment provided by engineer. */ +"Make one message disappear" = "Накарайте едно съобщение да изчезне"; + +/* No comment provided by engineer. */ +"Make profile private!" = "Направи профила поверителен!"; + +/* No comment provided by engineer. */ +"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Уверете се, че %@ сървърните адреси са в правилен формат, разделени на редове и не се дублират (%@)."; + +/* No comment provided by engineer. */ +"Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Уверете се, че адресите на WebRTC ICE сървъра са в правилен формат, разделени на редове и не са дублирани."; + +/* No comment provided by engineer. */ +"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Много хора попитаха: *ако SimpleX няма потребителски идентификатори, как може да доставя съобщения?*"; + +/* No comment provided by engineer. */ +"Mark deleted for everyone" = "Маркирай като изтрито за всички"; + +/* No comment provided by engineer. */ +"Mark read" = "Маркирай като прочетено"; + +/* No comment provided by engineer. */ +"Mark verified" = "Маркирай като проверено"; + +/* No comment provided by engineer. */ +"Markdown in messages" = "Форматиране на съобщения"; + +/* marked deleted chat item preview text */ +"marked deleted" = "маркирано като изтрито"; + +/* No comment provided by engineer. */ +"Max 30 seconds, received instantly." = "Макс. 30 секунди, получено незабавно."; + +/* member role */ +"member" = "член"; + +/* No comment provided by engineer. */ +"Member" = "Член"; + +/* rcv group event chat item */ +"member connected" = "свързан"; + +/* No comment provided by engineer. */ +"Member role will be changed to \"%@\". All group members will be notified." = "Ролята на члена ще бъде променена на \"%@\". Всички членове на групата ще бъдат уведомени."; + +/* No comment provided by engineer. */ +"Member role will be changed to \"%@\". The member will receive a new invitation." = "Ролята на члена ще бъде променена на \"%@\". Членът ще получи нова покана."; + +/* No comment provided by engineer. */ +"Member will be removed from group - this cannot be undone!" = "Членът ще бъде премахнат от групата - това не може да бъде отменено!"; + +/* item status text */ +"Message delivery error" = "Грешка при доставката на съобщението"; + +/* No comment provided by engineer. */ +"Message delivery receipts!" = "Потвърждениe за доставка на съобщения!"; + +/* No comment provided by engineer. */ +"Message draft" = "Чернова на съобщение"; + +/* chat feature */ +"Message reactions" = "Реакции на съобщения"; + +/* No comment provided by engineer. */ +"Message reactions are prohibited in this chat." = "Реакциите на съобщения са забранени в този чат."; + +/* No comment provided by engineer. */ +"Message reactions are prohibited in this group." = "Реакциите на съобщения са забранени в тази група."; + +/* notification */ +"message received" = "получено съобщение"; + +/* No comment provided by engineer. */ +"Message text" = "Текст на съобщението"; + +/* No comment provided by engineer. */ +"Messages" = "Съобщения"; + +/* No comment provided by engineer. */ +"Messages & files" = "Съобщения и файлове"; + +/* No comment provided by engineer. */ +"Migrating database archive…" = "Архивът на базата данни се мигрира…"; + +/* No comment provided by engineer. */ +"Migration error:" = "Грешка при мигриране:"; + +/* No comment provided by engineer. */ +"Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." = "Мигрирането е неуспешно. Докоснете **Пропускане** по-долу, за да продължите да използвате текущата база данни. Моля, докладвайте проблема на разработчиците на приложението чрез чат или имейл [chat@simplex.chat](mailto:chat@simplex.chat)."; + +/* No comment provided by engineer. */ +"Migration is completed" = "Миграцията е завършена"; + +/* No comment provided by engineer. */ +"Migrations: %@" = "Миграции: %@"; + +/* time unit */ +"minutes" = "минути"; + +/* call status */ +"missed call" = "пропуснато повикване"; + +/* chat item action */ +"Moderate" = "Модерирай"; + +/* moderated chat item */ +"moderated" = "модерирано"; + +/* No comment provided by engineer. */ +"Moderated at" = "Модерирано в"; + +/* copied message info */ +"Moderated at: %@" = "Модерирано в: %@"; + +/* No comment provided by engineer. */ +"moderated by %@" = "модерирано от %@"; + +/* time unit */ +"months" = "месеци"; + +/* No comment provided by engineer. */ +"More improvements are coming soon!" = "Очаквайте скоро още подобрения!"; + +/* item status description */ +"Most likely this connection is deleted." = "Най-вероятно тази връзка е изтрита."; + +/* No comment provided by engineer. */ +"Most likely this contact has deleted the connection with you." = "Най-вероятно този контакт е изтрил връзката с вас."; + +/* No comment provided by engineer. */ +"Multiple chat profiles" = "Множество профили за чат"; + +/* No comment provided by engineer. */ +"Mute" = "Без звук"; + +/* No comment provided by engineer. */ +"Muted when inactive!" = "Без звук при неактивност!"; + +/* No comment provided by engineer. */ +"Name" = "Име"; + +/* No comment provided by engineer. */ +"Network & servers" = "Мрежа и сървъри"; + +/* No comment provided by engineer. */ +"Network settings" = "Мрежови настройки"; + +/* No comment provided by engineer. */ +"Network status" = "Състояние на мрежата"; + +/* No comment provided by engineer. */ +"never" = "никога"; + +/* notification */ +"New contact request" = "Нова заявка за контакт"; + +/* notification */ +"New contact:" = "Нов контакт:"; + +/* No comment provided by engineer. */ +"New database archive" = "Нов архив на база данни"; + +/* No comment provided by engineer. */ +"New display name" = "Ново показвано име"; + +/* No comment provided by engineer. */ +"New in %@" = "Ново в %@"; + +/* No comment provided by engineer. */ +"New member role" = "Нова членска роля"; + +/* notification */ +"new message" = "ново съобщение"; + +/* notification */ +"New message" = "Ново съобщение"; + +/* No comment provided by engineer. */ +"New Passcode" = "Нов kод за достъп"; + +/* No comment provided by engineer. */ +"New passphrase…" = "Нова парола…"; + +/* pref value */ +"no" = "не"; + +/* No comment provided by engineer. */ +"No" = "Не"; + +/* Authentication unavailable */ +"No app password" = "Приложението няма kод за достъп"; + +/* No comment provided by engineer. */ +"No contacts selected" = "Няма избрани контакти"; + +/* No comment provided by engineer. */ +"No contacts to add" = "Няма контакти за добавяне"; + +/* No comment provided by engineer. */ +"No delivery information" = "Няма информация за доставката"; + +/* No comment provided by engineer. */ +"No device token!" = "Няма токен за устройство!"; + +/* No comment provided by engineer. */ +"no e2e encryption" = "липсва e2e криптиране"; + +/* No comment provided by engineer. */ +"No filtered chats" = "Няма филтрирани чатове"; + +/* No comment provided by engineer. */ +"No group!" = "Групата не е намерена!"; + +/* No comment provided by engineer. */ +"No history" = "Няма история"; + +/* No comment provided by engineer. */ +"No permission to record voice message" = "Няма разрешение за запис на гласово съобщение"; + +/* No comment provided by engineer. */ +"No received or sent files" = "Няма получени или изпратени файлове"; + +/* copied message info in history */ +"no text" = "няма текст"; + +/* No comment provided by engineer. */ +"Notifications" = "Известия"; + +/* No comment provided by engineer. */ +"Notifications are disabled!" = "Известията са деактивирани!"; + +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Сега администраторите могат:\n- да изтриват съобщения на членове.\n- да деактивират членове (роля \"наблюдател\")"; + +/* member role */ +"observer" = "наблюдател"; + +/* enabled status + group pref value */ +"off" = "изключено"; + +/* No comment provided by engineer. */ +"Off" = "Изключено"; + +/* No comment provided by engineer. */ +"Off (Local)" = "Изключено (Локално)"; + +/* feature offered item */ +"offered %@" = "предлага %@"; + +/* feature offered item */ +"offered %@: %@" = "предлага %1$@: %2$@"; + +/* No comment provided by engineer. */ +"Ok" = "Ок"; + +/* No comment provided by engineer. */ +"Old database" = "Стара база данни"; + +/* No comment provided by engineer. */ +"Old database archive" = "Стар архив на база данни"; + +/* group pref value */ +"on" = "включено"; + +/* No comment provided by engineer. */ +"One-time invitation link" = "Линк за еднократна покана"; + +/* No comment provided by engineer. */ +"Onion hosts will be required for connection. Requires enabling VPN." = "За свързване ще са необходими Onion хостове. Изисква се активиране на VPN."; + +/* No comment provided by engineer. */ +"Onion hosts will be used when available. Requires enabling VPN." = "Ще се използват Onion хостове, когато са налични. Изисква се активиране на VPN."; + +/* No comment provided by engineer. */ +"Onion hosts will not be used." = "Няма се използват Onion хостове."; + +/* No comment provided by engineer. */ +"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с **двуслойно криптиране от край до край**."; + +/* No comment provided by engineer. */ +"Only group owners can change group preferences." = "Само собствениците на групата могат да променят груповите настройки."; + +/* No comment provided by engineer. */ +"Only group owners can enable files and media." = "Само собствениците на групата могат да активират файлове и медията."; + +/* No comment provided by engineer. */ +"Only group owners can enable voice messages." = "Само собствениците на групата могат да активират гласови съобщения."; + +/* No comment provided by engineer. */ +"Only you can add message reactions." = "Само вие можете да добавяте реакции на съобщенията."; + +/* No comment provided by engineer. */ +"Only you can irreversibly delete messages (your contact can mark them for deletion)." = "Само вие можете необратимо да изтриете съобщения (вашият контакт може да ги маркира за изтриване)."; + +/* No comment provided by engineer. */ +"Only you can make calls." = "Само вие можете да извършвате разговори."; + +/* No comment provided by engineer. */ +"Only you can send disappearing messages." = "Само вие можете да изпращате изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Only you can send voice messages." = "Само вие можете да изпращате гласови съобщения."; + +/* No comment provided by engineer. */ +"Only your contact can add message reactions." = "Само вашият контакт може да добавя реакции на съобщенията."; + +/* No comment provided by engineer. */ +"Only your contact can irreversibly delete messages (you can mark them for deletion)." = "Само вашият контакт може необратимо да изтрие съобщения (можете да ги маркирате за изтриване)."; + +/* No comment provided by engineer. */ +"Only your contact can make calls." = "Само вашият контакт може да извършва разговори."; + +/* No comment provided by engineer. */ +"Only your contact can send disappearing messages." = "Само вашият контакт може да изпраща изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Only your contact can send voice messages." = "Само вашият контакт може да изпраща гласови съобщения."; + +/* No comment provided by engineer. */ +"Open chat" = "Отвори чат"; + +/* authentication reason */ +"Open chat console" = "Отвори конзолата"; + +/* No comment provided by engineer. */ +"Open Settings" = "Отвори настройки"; + +/* authentication reason */ +"Open user profiles" = "Отвори потребителските профили"; + +/* No comment provided by engineer. */ +"Open-source protocol and code – anybody can run the servers." = "Протокол и код с отворен код – всеки може да оперира собствени сървъри."; + +/* No comment provided by engineer. */ +"Opening database…" = "Отваряне на база данни…"; + +/* No comment provided by engineer. */ +"Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." = "Отварянето на линка в браузъра може да намали поверителността и сигурността на връзката. Несигурните SimpleX линкове ще бъдат червени."; + +/* No comment provided by engineer. */ +"or chat with the developers" = "или пишете на разработчиците"; + +/* member role */ +"owner" = "собственик"; + +/* No comment provided by engineer. */ +"Passcode" = "Код за достъп"; + +/* No comment provided by engineer. */ +"Passcode changed!" = "Кодът за достъп е променен!"; + +/* No comment provided by engineer. */ +"Passcode entry" = "Въвеждане на код за достъп"; + +/* No comment provided by engineer. */ +"Passcode not changed!" = "Кодът за достъп не е променен!"; + +/* No comment provided by engineer. */ +"Passcode set!" = "Кодът за достъп е зададен!"; + +/* No comment provided by engineer. */ +"Password to show" = "Парола за показване"; + +/* No comment provided by engineer. */ +"Paste" = "Постави"; + +/* No comment provided by engineer. */ +"Paste image" = "Постави изображение"; + +/* No comment provided by engineer. */ +"Paste received link" = "Постави получения линк"; + +/* placeholder */ +"Paste the link you received to connect with your contact." = "Поставете линка, който сте получили, за да се свържете с вашия контакт."; + +/* No comment provided by engineer. */ +"peer-to-peer" = "peer-to-peer"; + +/* No comment provided by engineer. */ +"People can connect to you only via the links you share." = "Хората могат да се свържат с вас само чрез ликовете, които споделяте."; + +/* No comment provided by engineer. */ +"Periodically" = "Периодично"; + +/* message decrypt error item */ +"Permanent decryption error" = "Постоянна грешка при декриптиране"; + +/* No comment provided by engineer. */ +"PING count" = "PING бройка"; + +/* No comment provided by engineer. */ +"PING interval" = "PING интервал"; + +/* No comment provided by engineer. */ +"Please ask your contact to enable sending voice messages." = "Моля, попитайте вашия контакт, за да активирате изпращане на гласови съобщения."; + +/* 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 with %@ and try again." = "Моля, проверете мрежовата си връзка с %@ и опитайте отново."; + +/* No comment provided by engineer. */ +"Please check yours and your contact preferences." = "Моля, проверете вашите настройки и тези вашия за контакт."; + +/* No comment provided by engineer. */ +"Please contact group admin." = "Моля, свържете се с груповия администартор."; + +/* No comment provided by engineer. */ +"Please enter correct current passphrase." = "Моля, въведете правилната текуща парола."; + +/* No comment provided by engineer. */ +"Please enter the previous password after restoring database backup. This action can not be undone." = "Моля, въведете предишната парола след възстановяване на резервното копие на базата данни. Това действие не може да бъде отменено."; + +/* No comment provided by engineer. */ +"Please remember or store it securely - there is no way to recover a lost passcode!" = "Моля, запомнете го или го съхранявайте на сигурно място - няма начин да възстановите изгубен код за достъп!"; + +/* No comment provided by engineer. */ +"Please report it to the developers." = "Моля, докладвайте го на разработчиците."; + +/* No comment provided by engineer. */ +"Please restart the app and migrate the database to enable push notifications." = "Моля, рестартирайте приложението и мигрирайте базата данни, за да активирате push известия."; + +/* No comment provided by engineer. */ +"Please store passphrase securely, you will NOT be able to access chat if you lose it." = "Моля, съхранявайте паролата на сигурно място, НЯМА да имате достъп до чата, ако я загубите."; + +/* No comment provided by engineer. */ +"Please store passphrase securely, you will NOT be able to change it if you lose it." = "Моля, съхранявайте паролата на сигурно място, НЯМА да можете да я промените, ако я загубите."; + +/* No comment provided by engineer. */ +"Polish interface" = "Полски интерфейс"; + +/* server test error */ +"Possibly, certificate fingerprint in server address is incorrect" = "Въжможно е пръстовият отпечатък на сертификата в адреса на сървъра да е неправилен"; + +/* No comment provided by engineer. */ +"Preserve the last message draft, with attachments." = "Запазете последната чернова на съобщението с прикачени файлове."; + +/* No comment provided by engineer. */ +"Preset server" = "Предварително зададен сървър"; + +/* No comment provided by engineer. */ +"Preset server address" = "Предварително зададен адрес на сървъра"; + +/* No comment provided by engineer. */ +"Preview" = "Визуализация"; + +/* No comment provided by engineer. */ +"Privacy & security" = "Поверителност и сигурност"; + +/* No comment provided by engineer. */ +"Privacy redefined" = "Поверителността преосмислена"; + +/* No comment provided by engineer. */ +"Private filenames" = "Поверителни имена на файлове"; + +/* No comment provided by engineer. */ +"Profile and server connections" = "Профилни и сървърни връзки"; + +/* No comment provided by engineer. */ +"Profile image" = "Профилно изображение"; + +/* No comment provided by engineer. */ +"Profile password" = "Профилна парола"; + +/* No comment provided by engineer. */ +"Profile update will be sent to your contacts." = "Актуализацията на профила ще бъде изпратена до вашите контакти."; + +/* No comment provided by engineer. */ +"Prohibit audio/video calls." = "Забрани аудио/видео разговорите."; + +/* No comment provided by engineer. */ +"Prohibit irreversible message deletion." = "Забрани необратимото изтриване на съобщения."; + +/* No comment provided by engineer. */ +"Prohibit message reactions." = "Забрани реакциите на съобщенията."; + +/* No comment provided by engineer. */ +"Prohibit messages reactions." = "Забрани реакциите на съобщенията."; + +/* No comment provided by engineer. */ +"Prohibit sending direct messages to members." = "Забрани изпращането на лични съобщения до членовете."; + +/* No comment provided by engineer. */ +"Prohibit sending disappearing messages." = "Забрани изпращането на изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Prohibit sending files and media." = "Забрани изпращането на файлове и медия."; + +/* No comment provided by engineer. */ +"Prohibit sending voice messages." = "Забрани изпращането на гласови съобщения."; + +/* No comment provided by engineer. */ +"Protect app screen" = "Защити екрана на приложението"; + +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Защитете чат профилите с парола!"; + +/* No comment provided by engineer. */ +"Protocol timeout" = "Време за изчакване на протокола"; + +/* No comment provided by engineer. */ +"Protocol timeout per KB" = "Време за изчакване на протокола за KB"; + +/* No comment provided by engineer. */ +"Push notifications" = "Push известия"; + +/* No comment provided by engineer. */ +"Rate the app" = "Оценете приложението"; + +/* chat item menu */ +"React…" = "Реагирай…"; + +/* No comment provided by engineer. */ +"Read" = "Прочетено"; + +/* No comment provided by engineer. */ +"Read more" = "Прочетете още"; + +/* No comment provided by engineer. */ +"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; + +/* No comment provided by engineer. */ +"Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Прочетете повече в [Ръководство на потребителя](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; + +/* No comment provided by engineer. */ +"Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Прочетете повече в нашето [GitHub хранилище](https://github.com/simplex-chat/simplex-chat#readme)."; + +/* No comment provided by engineer. */ +"Read more in our GitHub repository." = "Прочетете повече в нашето хранилище в GitHub."; + +/* No comment provided by engineer. */ +"Receipts are disabled" = "Потвърждениeто за доставка е деактивирано"; + +/* No comment provided by engineer. */ +"received answer…" = "получен отговор…"; + +/* No comment provided by engineer. */ +"Received at" = "Получено в"; + +/* copied message info */ +"Received at: %@" = "Получено в: %@"; + +/* No comment provided by engineer. */ +"received confirmation…" = "получено потвърждение…"; + +/* notification */ +"Received file event" = "Събитие за получен файл"; + +/* message info title */ +"Received message" = "Получено съобщение"; + +/* No comment provided by engineer. */ +"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "Получаващият адрес ще бъде променен към друг сървър. Промяната на адреса ще завърши, след като подателят е онлайн."; + +/* No comment provided by engineer. */ +"Receiving file will be stopped." = "Получаващият се файл ще бъде спрян."; + +/* No comment provided by engineer. */ +"Receiving via" = "Получаване чрез"; + +/* No comment provided by engineer. */ +"Recipients see updates as you type them." = "Получателите виждат актуализации, докато ги въвеждате."; + +/* No comment provided by engineer. */ +"Reconnect all connected servers to force message delivery. It uses additional traffic." = "Повторно се свържете с всички свързани сървъри, за да принудите доставката на съобщенията. Използва се допълнителен трафик."; + +/* No comment provided by engineer. */ +"Reconnect servers?" = "Повторно свърване със сървърите?"; + +/* No comment provided by engineer. */ +"Record updated at" = "Записът е актуализиран на"; + +/* copied message info */ +"Record updated at: %@" = "Записът е актуализиран на: %@"; + +/* No comment provided by engineer. */ +"Reduced battery usage" = "Намалена консумация на батерията"; + +/* reject incoming call via notification */ +"Reject" = "Отхвърляне"; + +/* No comment provided by engineer. */ +"Reject (sender NOT notified)" = "Отхвърляне (подателят НЕ бива уведомен)"; + +/* No comment provided by engineer. */ +"Reject contact request" = "Отхвърли заявката за контакт"; + +/* call status */ +"rejected call" = "отхвърлено повикване"; + +/* No comment provided by engineer. */ +"Relay server is only used if necessary. Another party can observe your IP address." = "Реле сървър се използва само ако е необходимо. Друга страна може да наблюдава вашия IP адрес."; + +/* No comment provided by engineer. */ +"Relay server protects your IP address, but it can observe the duration of the call." = "Relay сървърът защитава вашия IP адрес, но може да наблюдава продължителността на разговора."; + +/* No comment provided by engineer. */ +"Remove" = "Премахване"; + +/* No comment provided by engineer. */ +"Remove member" = "Острани член"; + +/* No comment provided by engineer. */ +"Remove member?" = "Острани член?"; + +/* No comment provided by engineer. */ +"Remove passphrase from keychain?" = "Премахване на паролата от keychain?"; + +/* No comment provided by engineer. */ +"removed" = "отстранен"; + +/* rcv group event chat item */ +"removed %@" = "отстранен %@"; + +/* rcv group event chat item */ +"removed you" = "ви острани"; + +/* No comment provided by engineer. */ +"Renegotiate" = "Предоговоряне"; + +/* No comment provided by engineer. */ +"Renegotiate encryption" = "Предоговори криптирането"; + +/* No comment provided by engineer. */ +"Renegotiate encryption?" = "Предоговори криптирането?"; + +/* chat item action */ +"Reply" = "Отговори"; + +/* No comment provided by engineer. */ +"Required" = "Задължително"; + +/* No comment provided by engineer. */ +"Reset" = "Нулиране"; + +/* No comment provided by engineer. */ +"Reset colors" = "Нулирай цветовете"; + +/* No comment provided by engineer. */ +"Reset to defaults" = "Възстановяване на настройките по подразбиране"; + +/* No comment provided by engineer. */ +"Restart the app to create a new chat profile" = "Рестартирайте приложението, за да създадете нов чат профил"; + +/* No comment provided by engineer. */ +"Restart the app to use imported chat database" = "Рестартирайте приложението, за да използвате импортирана чат база данни"; + +/* No comment provided by engineer. */ +"Restore" = "Възстанови"; + +/* No comment provided by engineer. */ +"Restore database backup" = "Възстанови резервно копие на база данни"; + +/* No comment provided by engineer. */ +"Restore database backup?" = "Възстанови резервно копие на база данни?"; + +/* No comment provided by engineer. */ +"Restore database error" = "Грешка при възстановяване на базата данни"; + +/* chat item action */ +"Reveal" = "Покажи"; + +/* No comment provided by engineer. */ +"Revert" = "Отмени промените"; + +/* No comment provided by engineer. */ +"Revoke" = "Отзови"; + +/* cancel file action */ +"Revoke file" = "Отзови файл"; + +/* No comment provided by engineer. */ +"Revoke file?" = "Отзови файл?"; + +/* No comment provided by engineer. */ +"Role" = "Роля"; + +/* No comment provided by engineer. */ +"Run chat" = "Стартиране на чат"; + +/* chat item action */ +"Save" = "Запази"; + +/* No comment provided by engineer. */ +"Save (and notify contacts)" = "Запази (и уведоми контактите)"; + +/* No comment provided by engineer. */ +"Save and notify contact" = "Запази и уведоми контакта"; + +/* No comment provided by engineer. */ +"Save and notify group members" = "Запази и уведоми членовете на групата"; + +/* No comment provided by engineer. */ +"Save and update group profile" = "Запази и актуализирай профила на групата"; + +/* No comment provided by engineer. */ +"Save archive" = "Запази архив"; + +/* No comment provided by engineer. */ +"Save auto-accept settings" = "Запази настройките за автоматично приемане"; + +/* No comment provided by engineer. */ +"Save group profile" = "Запази профила на групата"; + +/* No comment provided by engineer. */ +"Save passphrase and open chat" = "Запази паролата и отвори чата"; + +/* No comment provided by engineer. */ +"Save passphrase in Keychain" = "Запази паролата в Keychain"; + +/* No comment provided by engineer. */ +"Save preferences?" = "Запази настройките?"; + +/* No comment provided by engineer. */ +"Save profile password" = "Запази паролата на профила"; + +/* No comment provided by engineer. */ +"Save servers" = "Запази сървърите"; + +/* No comment provided by engineer. */ +"Save servers?" = "Запази сървърите?"; + +/* No comment provided by engineer. */ +"Save settings?" = "Запази настройките?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "Запази съобщението при посрещане?"; + +/* No comment provided by engineer. */ +"Saved WebRTC ICE servers will be removed" = "Запазените WebRTC ICE сървъри ще бъдат премахнати"; + +/* No comment provided by engineer. */ +"Scan code" = "Сканирай код"; + +/* No comment provided by engineer. */ +"Scan QR code" = "Сканирай QR код"; + +/* No comment provided by engineer. */ +"Scan security code from your contact's app." = "Сканирайте кода за сигурност от приложението на вашия контакт."; + +/* No comment provided by engineer. */ +"Scan server QR code" = "Сканирай QR кода на сървъра"; + +/* No comment provided by engineer. */ +"Search" = "Търсене"; + +/* network option */ +"sec" = "сек."; + +/* time unit */ +"seconds" = "секунди"; + +/* No comment provided by engineer. */ +"secret" = "таен"; + +/* server test step */ +"Secure queue" = "Сигурна опашка"; + +/* No comment provided by engineer. */ +"Security assessment" = "Оценка на сигурността"; + +/* No comment provided by engineer. */ +"Security code" = "Код за сигурност"; + +/* chat item text */ +"security code changed" = "кодът за сигурност е променен"; + +/* No comment provided by engineer. */ +"Select" = "Избери"; + +/* No comment provided by engineer. */ +"Self-destruct" = "Самоунищожение"; + +/* No comment provided by engineer. */ +"Self-destruct passcode" = "Код за достъп за самоунищожение"; + +/* No comment provided by engineer. */ +"Self-destruct passcode changed!" = "Кодът за достъп за самоунищожение е променен!"; + +/* No comment provided by engineer. */ +"Self-destruct passcode enabled!" = "Кодът за достъп за самоунищожение е активиран!"; + +/* No comment provided by engineer. */ +"Send" = "Изпрати"; + +/* No comment provided by engineer. */ +"Send a live message - it will update for the recipient(s) as you type it" = "Изпратете съобщение на живо - то ще се актуализира за получателя(ите), докато го пишете"; + +/* No comment provided by engineer. */ +"Send delivery receipts to" = "Изпращайте потвърждениe за доставка на"; + +/* No comment provided by engineer. */ +"Send direct message" = "Изпрати лично съобщение"; + +/* No comment provided by engineer. */ +"Send disappearing message" = "Изпрати изчезващо съобщение"; + +/* No comment provided by engineer. */ +"Send link previews" = "Изпрати визуализация на линковете"; + +/* No comment provided by engineer. */ +"Send live message" = "Изпрати съобщение на живо"; + +/* No comment provided by engineer. */ +"Send notifications" = "Изпращай известия"; + +/* No comment provided by engineer. */ +"Send notifications:" = "Изпратени известия:"; + +/* No comment provided by engineer. */ +"Send questions and ideas" = "Изпращайте въпроси и идеи"; + +/* No comment provided by engineer. */ +"Send receipts" = "Изпращане на потвърждениe за доставка"; + +/* No comment provided by engineer. */ +"Send them from gallery or custom keyboards." = "Изпрати от галерия или персонализирани клавиатури."; + +/* No comment provided by engineer. */ +"Sender cancelled file transfer." = "Подателят отмени прехвърлянето на файла."; + +/* No comment provided by engineer. */ +"Sender may have deleted the connection request." = "Подателят може да е изтрил заявката за връзка."; + +/* No comment provided by engineer. */ +"Sending delivery receipts will be enabled for all contacts in all visible chat profiles." = "Изпращането на потвърждениe за доставка ще бъде активирано за всички контакти във всички видими чат профили."; + +/* No comment provided by engineer. */ +"Sending delivery receipts will be enabled for all contacts." = "Изпращането на потвърждениe за доставка ще бъде активирано за всички контакти."; + +/* No comment provided by engineer. */ +"Sending file will be stopped." = "Изпращането на файла ще бъде спряно."; + +/* No comment provided by engineer. */ +"Sending receipts is disabled for %lld contacts" = "Изпращането на потвърждениe за доставка е деактивирано за %lld контакта"; + +/* No comment provided by engineer. */ +"Sending receipts is disabled for %lld groups" = "Изпращането на потвърждениe за доставка е деактивирано за %lld групи"; + +/* No comment provided by engineer. */ +"Sending receipts is enabled for %lld contacts" = "Изпращането на потвърждениe за доставка е активирано за %lld контакта"; + +/* No comment provided by engineer. */ +"Sending receipts is enabled for %lld groups" = "Изпращането на потвърждениe за доставка е активирано за %lld групи"; + +/* No comment provided by engineer. */ +"Sending via" = "Изпращане чрез"; + +/* No comment provided by engineer. */ +"Sent at" = "Изпратено на"; + +/* copied message info */ +"Sent at: %@" = "Изпратено на: %@"; + +/* notification */ +"Sent file event" = "Събитие за изпратен файл"; + +/* message info title */ +"Sent message" = "Изпратено съобщение"; + +/* No comment provided by engineer. */ +"Sent messages will be deleted after set time." = "Изпратените съобщения ще бъдат изтрити след зададеното време."; + +/* server test error */ +"Server requires authorization to create queues, check password" = "Сървърът изисква оторизация за създаване на опашки, проверете паролата"; + +/* server test error */ +"Server requires authorization to upload, check password" = "Сървърът изисква оторизация за качване, проверете паролата"; + +/* No comment provided by engineer. */ +"Server test failed!" = "Тестът на сървъра е неуспешен!"; + +/* No comment provided by engineer. */ +"Servers" = "Сървъри"; + +/* No comment provided by engineer. */ +"Set 1 day" = "Задай 1 ден"; + +/* No comment provided by engineer. */ +"Set contact name…" = "Задай име на контакт…"; + +/* No comment provided by engineer. */ +"Set group preferences" = "Задай групови настройки"; + +/* No comment provided by engineer. */ +"Set it instead of system authentication." = "Задайте го вместо системната идентификация."; + +/* No comment provided by engineer. */ +"Set passcode" = "Задай kод за достъп"; + +/* No comment provided by engineer. */ +"Set passphrase to export" = "Задай парола за експортиране"; + +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "Задай съобщението, показано на новите членове!"; + +/* No comment provided by engineer. */ +"Set timeouts for proxy/VPN" = "Задай време за изчакване за прокси/VPN"; + +/* No comment provided by engineer. */ +"Settings" = "Настройки"; + +/* chat item action */ +"Share" = "Сподели"; + +/* No comment provided by engineer. */ +"Share 1-time link" = "Сподели еднократен линк"; + +/* No comment provided by engineer. */ +"Share address" = "Сподели адрес"; + +/* No comment provided by engineer. */ +"Share address with contacts?" = "Сподели адреса с контактите?"; + +/* No comment provided by engineer. */ +"Share link" = "Сподели линк"; + +/* No comment provided by engineer. */ +"Share one-time invitation link" = "Сподели линк за еднократна покана"; + +/* No comment provided by engineer. */ +"Share with contacts" = "Сподели с контактите"; + +/* No comment provided by engineer. */ +"Show calls in phone history" = "Показване на обажданията в хронологията на телефона"; + +/* No comment provided by engineer. */ +"Show developer options" = "Покажи опциите за разработчици"; + +/* No comment provided by engineer. */ +"Show last messages" = "Показване на последните съобщения в листа с чатовете"; + +/* No comment provided by engineer. */ +"Show preview" = "Показване на визуализация"; + +/* No comment provided by engineer. */ +"Show:" = "Покажи:"; + +/* No comment provided by engineer. */ +"SimpleX address" = "SimpleX адрес"; + +/* No comment provided by engineer. */ +"SimpleX Address" = "SimpleX Адрес"; + +/* No comment provided by engineer. */ +"SimpleX Chat security was audited by Trail of Bits." = "Сигурността на SimpleX Chat беше одитирана от Trail of Bits."; + +/* simplex link type */ +"SimpleX contact address" = "SimpleX адрес за контакт"; + +/* notification */ +"SimpleX encrypted message or connection event" = "SimpleX криптирано съобщение или събитие за връзка"; + +/* simplex link type */ +"SimpleX group link" = "SimpleX групов линк"; + +/* No comment provided by engineer. */ +"SimpleX links" = "SimpleX линкове"; + +/* No comment provided by engineer. */ +"SimpleX Lock" = "SimpleX заключване"; + +/* No comment provided by engineer. */ +"SimpleX Lock mode" = "Режим на SimpleX заключване"; + +/* No comment provided by engineer. */ +"SimpleX Lock not enabled!" = "SimpleX заключване не е активирано!"; + +/* No comment provided by engineer. */ +"SimpleX Lock turned on" = "SimpleX заключване е включено"; + +/* simplex link type */ +"SimpleX one-time invitation" = "Еднократна покана за SimpleX"; + +/* No comment provided by engineer. */ +"Skip" = "Пропускане"; + +/* No comment provided by engineer. */ +"Skipped messages" = "Пропуснати съобщения"; + +/* No comment provided by engineer. */ +"Small groups (max 20)" = "Малки групи (максимум 20)"; + +/* No comment provided by engineer. */ +"SMP servers" = "SMP сървъри"; + +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import - you may see Chat console for more details." = "Някои не-фатални грешки са възникнали по време на импортиране - може да видите конзолата за повече подробности."; + +/* notification title */ +"Somebody" = "Някой"; + +/* No comment provided by engineer. */ +"Start a new chat" = "Започни нов чат"; + +/* No comment provided by engineer. */ +"Start chat" = "Започни чат"; + +/* No comment provided by engineer. */ +"Start migration" = "Започни миграция"; + +/* No comment provided by engineer. */ +"starting…" = "стартиране…"; + +/* No comment provided by engineer. */ +"Stop" = "Спри"; + +/* No comment provided by engineer. */ +"Stop chat to enable database actions" = "Спрете чата, за да активирате действията с базата данни"; + +/* No comment provided by engineer. */ +"Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Спрете чата, за да експортирате, импортирате или изтриете чат базата данни. Няма да можете да получавате и изпращате съобщения, докато чатът е спрян."; + +/* No comment provided by engineer. */ +"Stop chat?" = "Спри чата?"; + +/* cancel file action */ +"Stop file" = "Спри файл"; + +/* No comment provided by engineer. */ +"Stop receiving file?" = "Спри получаването на файла?"; + +/* No comment provided by engineer. */ +"Stop sending file?" = "Спри изпращането на файла?"; + +/* No comment provided by engineer. */ +"Stop sharing" = "Спри споделянето"; + +/* No comment provided by engineer. */ +"Stop sharing address?" = "Спри споделянето на адреса?"; + +/* authentication reason */ +"Stop SimpleX" = "Спри SimpleX"; + +/* No comment provided by engineer. */ +"strike" = "зачеркнат"; + +/* No comment provided by engineer. */ +"Submit" = "Изпрати"; + +/* No comment provided by engineer. */ +"Support SimpleX Chat" = "Подкрепете SimpleX Chat"; + +/* No comment provided by engineer. */ +"System" = "Системен"; + +/* No comment provided by engineer. */ +"System authentication" = "Системна идентификация"; + +/* No comment provided by engineer. */ +"Take picture" = "Направи снимка"; + +/* No comment provided by engineer. */ +"Tap button " = "Докосни бутона "; + +/* No comment provided by engineer. */ +"Tap to activate profile." = "Докосни за активиране на профил."; + +/* No comment provided by engineer. */ +"Tap to join" = "Докосни за вход"; + +/* No comment provided by engineer. */ +"Tap to join incognito" = "Докосни за инкогнито вход"; + +/* No comment provided by engineer. */ +"Tap to start a new chat" = "Докосни за започване на нов чат"; + +/* No comment provided by engineer. */ +"TCP connection timeout" = "Времето на изчакване за установяване на TCP връзка"; + +/* No comment provided by engineer. */ +"TCP_KEEPCNT" = "TCP_KEEPCNT"; + +/* No comment provided by engineer. */ +"TCP_KEEPIDLE" = "TCP_KEEPIDLE"; + +/* No comment provided by engineer. */ +"TCP_KEEPINTVL" = "TCP_KEEPINTVL"; + +/* server test failure */ +"Test failed at step %@." = "Тестът е неуспешен на стъпка %@."; + +/* No comment provided by engineer. */ +"Test server" = "Тествай сървър"; + +/* No comment provided by engineer. */ +"Test servers" = "Тествай сървърите"; + +/* No comment provided by engineer. */ +"Tests failed!" = "Тестовете са неуспешни!"; + +/* No comment provided by engineer. */ +"Thank you for installing SimpleX Chat!" = "Благодарим Ви, че инсталирахте SimpleX Chat!"; + +/* No comment provided by engineer. */ +"Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Благодарение на потребителите – [допринесете през Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; + +/* No comment provided by engineer. */ +"Thanks to the users – contribute via Weblate!" = "Благодарение на потребителите – допринесете през Weblate!"; + +/* No comment provided by engineer. */ +"The 1st platform without any user identifiers – private by design." = "Първата платформа без никакви потребителски идентификатори – поверителна по дизайн."; + +/* 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 attempt to change database passphrase was not completed." = "Опитът за промяна на паролата на базата данни не беше завършен."; + +/* No comment provided by engineer. */ +"The connection you accepted will be cancelled!" = "Връзката, която приехте, ще бъде отказана!"; + +/* No comment provided by engineer. */ +"The contact you shared this link with will NOT be able to connect!" = "Контактът, с когото споделихте този линк, НЯМА да може да се свърже!"; + +/* No comment provided by engineer. */ +"The created archive is available via app Settings / Database / Old database archive." = "Създаденият архив е достъпен чрез Настройки на приложението / База данни / Стар архив на база данни."; + +/* No comment provided by engineer. */ +"The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Криптирането работи и новото споразумение за криптиране не е необходимо. Това може да доведе до грешки при свързване!"; + +/* No comment provided by engineer. */ +"The group is fully decentralized – it is visible only to the members." = "Групата е напълно децентрализирана – видима е само за членовете."; + +/* No comment provided by engineer. */ +"The hash of the previous message is different." = "Хешът на предишното съобщение е различен."; + +/* No comment provided by engineer. */ +"The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "Неправилно ID на следващото съобщение (по-малко или еднакво с предишното).\nТова може да се случи поради някаква грешка или когато връзката е компрометирана."; + +/* No comment provided by engineer. */ +"The message will be deleted for all members." = "Съобщението ще бъде изтрито за всички членове."; + +/* No comment provided by engineer. */ +"The message will be marked as moderated for all members." = "Съобщението ще бъде маркирано като модерирано за всички членове."; + +/* No comment provided by engineer. */ +"The next generation of private messaging" = "Ново поколение поверителни съобщения"; + +/* No comment provided by engineer. */ +"The old database was not removed during the migration, it can be deleted." = "Старата база данни не бе премахната по време на миграцията, тя може да бъде изтрита."; + +/* No comment provided by engineer. */ +"The profile is only shared with your contacts." = "Профилът се споделя само с вашите контакти."; + +/* No comment provided by engineer. */ +"The second tick we missed! ✅" = "Втората отметка, която пропуснахме! ✅"; + +/* No comment provided by engineer. */ +"The sender will NOT be notified" = "Подателят НЯМА да бъде уведомен"; + +/* No comment provided by engineer. */ +"The servers for new connections of your current chat profile **%@**." = "Сървърите за нови връзки на текущия ви чат профил **%@**."; + +/* No comment provided by engineer. */ +"Theme" = "Тема"; + +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Трябва да има поне един потребителски профил."; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "Трябва да има поне един видим потребителски профил."; + +/* No comment provided by engineer. */ +"These settings are for your current profile **%@**." = "Тези настройки са за текущия ви профил **%@**."; + +/* No comment provided by engineer. */ +"They can be overridden in contact and group settings." = "Те могат да бъдат променени в настройките за всеки контакт и група."; + +/* No comment provided by engineer. */ +"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Това действие не може да бъде отменено - всички получени и изпратени файлове и медия ще бъдат изтрити. Снимките с ниска разделителна способност ще бъдат запазени."; + +/* No comment provided by engineer. */ +"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Това действие не може да бъде отменено - съобщенията, изпратени и получени по-рано от избраното, ще бъдат изтрити. Може да отнеме няколко минути."; + +/* No comment provided by engineer. */ +"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Това действие не може да бъде отменено - вашият профил, контакти, съобщения и файлове ще бъдат безвъзвратно загубени."; + +/* notification title */ +"this contact" = "този контакт"; + +/* No comment provided by engineer. */ +"This group has over %lld members, delivery receipts are not sent." = "Тази група има над %lld членове, потвърждения за доставка не се изпращат."; + +/* No comment provided by engineer. */ +"This group no longer exists." = "Тази група вече не съществува."; + +/* No comment provided by engineer. */ +"This setting applies to messages in your current chat profile **%@**." = "Тази настройка се прилага за съобщения в текущия ви профил **%@**."; + +/* No comment provided by engineer. */ +"To ask any questions and to receive updates:" = "За да задавате въпроси и да получавате актуализации:"; + +/* No comment provided by engineer. */ +"To connect, your contact can scan QR code or use the link in the app." = "За да се свърже, вашият контакт може да сканира QR код или да използва линка в приложението."; + +/* No comment provided by engineer. */ +"To make a new connection" = "За да направите нова връзка"; + +/* No comment provided by engineer. */ +"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "За да се защити поверителността, вместо потребителски идентификатори, използвани от всички други платформи, SimpleX има идентификатори за опашки от съобщения, отделни за всеки от вашите контакти."; + +/* No comment provided by engineer. */ +"To protect timezone, image/voice files use UTC." = "За да не се разкрива часовата зона, файловете с изображения/глас използват UTC."; + +/* No comment provided by engineer. */ +"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "За да защитите информацията си, включете SimpleX заключване.\nЩе бъдете подканени да извършите идентификация, преди тази функция да бъде активирана."; + +/* No comment provided by engineer. */ +"To record voice message please grant permission to use Microphone." = "За да запишете гласово съобщение, моля, дайте разрешение за използване на микрофон."; + +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "За да разкриете своя скрит профил, въведете пълна парола в полето за търсене на страницата **Вашите чат профили**."; + +/* No comment provided by engineer. */ +"To support instant push notifications the chat database has to be migrated." = "За поддръжка на незабавни push известия, базата данни за чат трябва да бъде мигрирана."; + +/* No comment provided by engineer. */ +"To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "За да проверите криптирането от край до край с вашия контакт, сравнете (или сканирайте) кода на вашите устройства."; + +/* No comment provided by engineer. */ +"Transport isolation" = "Транспортна изолация"; + +/* 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. */ +"Turn off" = "Изключи"; + +/* No comment provided by engineer. */ +"Turn off notifications?" = "Изключи известията?"; + +/* No comment provided by engineer. */ +"Turn on" = "Включи"; + +/* No comment provided by engineer. */ +"Unable to record voice message" = "Не може да се запише гласово съобщение"; + +/* item status description */ +"Unexpected error: %@" = "Неочаквана грешка: %@"; + +/* No comment provided by engineer. */ +"Unexpected migration state" = "Неочаквано състояние на миграция"; + +/* No comment provided by engineer. */ +"Unfav." = "Премахни от любимите"; + +/* No comment provided by engineer. */ +"Unhide" = "Покажи"; + +/* No comment provided by engineer. */ +"Unhide chat profile" = "Покажи чат профила"; + +/* No comment provided by engineer. */ +"Unhide profile" = "Покажи профила"; + +/* No comment provided by engineer. */ +"Unit" = "Мерна единица"; + +/* connection info */ +"unknown" = "неизвестен"; + +/* callkit banner */ +"Unknown caller" = "Неизвестен номер"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Неизвестна грешка в базата данни: %@"; + +/* No comment provided by engineer. */ +"Unknown error" = "Непозната грешка"; + +/* No comment provided by engineer. */ +"Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." = "Освен ако не използвате интерфейса за повикване на iOS, активирайте режима \"Не безпокой\", за да избегнете прекъсвания."; + +/* No comment provided by engineer. */ +"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Освен ако вашият контакт не е изтрил връзката или този линк вече е бил използван, това може да е грешка - моля, докладвайте.\nЗа да се свържете, моля, помолете вашия контакт да създаде друг линк за връзка и проверете дали имате стабилна мрежова връзка."; + +/* No comment provided by engineer. */ +"Unlock" = "Отключи"; + +/* authentication reason */ +"Unlock app" = "Отключи приложението"; + +/* No comment provided by engineer. */ +"Unmute" = "Уведомявай"; + +/* No comment provided by engineer. */ +"Unread" = "Непрочетено"; + +/* No comment provided by engineer. */ +"Update" = "Актуализация"; + +/* No comment provided by engineer. */ +"Update .onion hosts setting?" = "Актуализиране на настройката за .onion хостове?"; + +/* No comment provided by engineer. */ +"Update database passphrase" = "Актуализирай паролата на базата данни"; + +/* No comment provided by engineer. */ +"Update network settings?" = "Актуализиране на мрежовите настройки?"; + +/* No comment provided by engineer. */ +"Update transport isolation mode?" = "Актуализиране на режима на изолация на транспорта?"; + +/* rcv group event chat item */ +"updated group profile" = "актуализиран профил на групата"; + +/* No comment provided by engineer. */ +"Updating settings will re-connect the client to all servers." = "Актуализирането на настройките ще свърже отново клиента към всички сървъри."; + +/* No comment provided by engineer. */ +"Updating this setting will re-connect the client to all servers." = "Актуализирането на тази настройка ще свърже повторно клиента към всички сървъри."; + +/* No comment provided by engineer. */ +"Upgrade and open chat" = "Актуализирай и отвори чата"; + +/* server test step */ +"Upload file" = "Качи файл"; + +/* No comment provided by engineer. */ +"Use .onion hosts" = "Използвай .onion хостове"; + +/* No comment provided by engineer. */ +"Use chat" = "Използвай чата"; + +/* No comment provided by engineer. */ +"Use current profile" = "Използвай текущия профил"; + +/* No comment provided by engineer. */ +"Use for new connections" = "Използвай за нови връзки"; + +/* No comment provided by engineer. */ +"Use iOS call interface" = "Използвай интерфейса за повикване на iOS"; + +/* No comment provided by engineer. */ +"Use new incognito profile" = "Използвай нов инкогнито профил"; + +/* No comment provided by engineer. */ +"Use server" = "Използвай сървър"; + +/* No comment provided by engineer. */ +"Use SimpleX Chat servers?" = "Използвай сървърите на SimpleX Chat?"; + +/* No comment provided by engineer. */ +"User profile" = "Потребителски профил"; + +/* No comment provided by engineer. */ +"Using .onion hosts requires compatible VPN provider." = "Използването на .onion хостове изисква съвместим VPN доставчик."; + +/* No comment provided by engineer. */ +"Using SimpleX Chat servers." = "Използват се сървърите на SimpleX Chat."; + +/* No comment provided by engineer. */ +"v%@ (%@)" = "v%@ (%@)"; + +/* No comment provided by engineer. */ +"Verify connection security" = "Потвръди сигурността на връзката"; + +/* No comment provided by engineer. */ +"Verify security code" = "Потвръди кода за сигурност"; + +/* No comment provided by engineer. */ +"Via browser" = "Чрез браузър"; + +/* chat list item description */ +"via contact address link" = "чрез линк с адрес за контакт"; + +/* chat list item description */ +"via group link" = "чрез групов линк"; + +/* chat list item description */ +"via one-time link" = "чрез еднократен линк за връзка"; + +/* No comment provided by engineer. */ +"via relay" = "чрез реле"; + +/* No comment provided by engineer. */ +"Video call" = "Видео разговор"; + +/* No comment provided by engineer. */ +"video call (not e2e encrypted)" = "видео разговор (не е e2e криптиран)"; + +/* No comment provided by engineer. */ +"Video will be received when your contact completes uploading it." = "Видеото ще бъде получено, когато вашият контакт завърши качването му."; + +/* No comment provided by engineer. */ +"Video will be received when your contact is online, please wait or check later!" = "Видеото ще бъде получено, когато вашият контакт е онлайн, моля, изчакайте или проверете по-късно!"; + +/* No comment provided by engineer. */ +"Videos and files up to 1gb" = "Видео и файлове до 1gb"; + +/* No comment provided by engineer. */ +"View security code" = "Виж кода за сигурност"; + +/* No comment provided by engineer. */ +"Voice message…" = "Гласово съобщение…"; + +/* chat feature */ +"Voice messages" = "Гласови съобщения"; + +/* No comment provided by engineer. */ +"Voice messages are prohibited in this chat." = "Гласовите съобщения са забранени в този чат."; + +/* No comment provided by engineer. */ +"Voice messages are prohibited in this group." = "Гласовите съобщения са забранени в тази група."; + +/* No comment provided by engineer. */ +"Voice messages prohibited!" = "Гласовите съобщения са забранени!"; + +/* No comment provided by engineer. */ +"waiting for answer…" = "чака се отговор…"; + +/* No comment provided by engineer. */ +"waiting for confirmation…" = "чака се за потвърждение…"; + +/* No comment provided by engineer. */ +"Waiting for file" = "Изчаква се получаването на файла"; + +/* No comment provided by engineer. */ +"Waiting for image" = "Изчаква се получаването на изображението"; + +/* No comment provided by engineer. */ +"Waiting for video" = "Изчаква се получаването на видеото"; + +/* No comment provided by engineer. */ +"wants to connect to you!" = "иска да се свърже с вас!"; + +/* No comment provided by engineer. */ +"Warning: you may lose some data!" = "Предупреждение: Може да загубите някои данни!"; + +/* No comment provided by engineer. */ +"WebRTC ICE servers" = "WebRTC ICE сървъри"; + +/* time unit */ +"weeks" = "седмици"; + +/* No comment provided by engineer. */ +"Welcome %@!" = "Добре дошли %@!"; + +/* No comment provided by engineer. */ +"Welcome message" = "Съобщение при посрещане"; + +/* No comment provided by engineer. */ +"What's new" = "Какво е новото"; + +/* No comment provided by engineer. */ +"When available" = "Когато са налични"; + +/* No comment provided by engineer. */ +"When people request to connect, you can accept or reject it." = "Когато хората искат да се свържат с вас, можете да ги приемете или отхвърлите."; + +/* No comment provided by engineer. */ +"When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани."; + +/* No comment provided by engineer. */ +"With optional welcome message." = "С незадължително съобщение при посрещане."; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Грешна парола за базата данни"; + +/* No comment provided by engineer. */ +"Wrong passphrase!" = "Грешна парола!"; + +/* No comment provided by engineer. */ +"XFTP servers" = "XFTP сървъри"; + +/* pref value */ +"yes" = "да"; + +/* No comment provided by engineer. */ +"You" = "Вие"; + +/* No comment provided by engineer. */ +"You accepted connection" = "Вие приехте връзката"; + +/* No comment provided by engineer. */ +"You allow" = "Вие позволявате"; + +/* No comment provided by engineer. */ +"You already have a chat profile with the same display name. Please choose another name." = "Вече имате чат профил със същото показвано име. Моля, изберете друго име."; + +/* No comment provided by engineer. */ +"You are already connected to %@." = "Вече сте вече свързани с %@."; + +/* No comment provided by engineer. */ +"You are connected to the server used to receive messages from this contact." = "Вие сте свързани към сървъра, използван за получаване на съобщения от този контакт."; + +/* No comment provided by engineer. */ +"you are invited to group" = "вие сте поканени в групата"; + +/* No comment provided by engineer. */ +"You are invited to group" = "Поканени сте в групата"; + +/* No comment provided by engineer. */ +"you are observer" = "вие сте наблюдател"; + +/* No comment provided by engineer. */ +"You can accept calls from lock screen, without device and app authentication." = "Можете да приемате обаждания от заключен екран, без идентификация на устройство и приложението."; + +/* No comment provided by engineer. */ +"You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "Можете също да се свържете, като натиснете върху линка. Ако се отвори в браузъра, натиснете върху бутона **Отваряне в мобилно приложение**."; + +/* No comment provided by engineer. */ +"You can create it later" = "Можете да го създадете по-късно"; + +/* No comment provided by engineer. */ +"You can enable later via Settings" = "Можете да активирате по-късно през Настройки"; + +/* No comment provided by engineer. */ +"You can enable them later via app Privacy & Security settings." = "Можете да ги активирате по-късно през настройките за \"Поверителност и сигурност\" на приложението."; + +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right." = "Можете да скриете или заглушите известията за потребителски профил - плъзнете надясно."; + +/* notification body */ +"You can now send messages to %@" = "Вече можете да изпращате съобщения до %@"; + +/* No comment provided by engineer. */ +"You can set lock screen notification preview via settings." = "Можете да зададете визуализация на известията на заключен екран през настройките."; + +/* No comment provided by engineer. */ +"You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." = "Можете да споделите линк или QR код - всеки ще може да се присъедини към групата. Няма да загубите членовете на групата, ако по-късно я изтриете."; + +/* No comment provided by engineer. */ +"You can share this address with your contacts to let them connect with **%@**." = "Можете да споделите този адрес с вашите контакти, за да им позволите да се свържат с **%@**."; + +/* No comment provided by engineer. */ +"You can share your address as a link or QR code - anybody can connect to you." = "Можете да споделите адреса си като линк или QR код - всеки може да се свърже с вас."; + +/* No comment provided by engineer. */ +"You can start chat via app Settings / Database or by restarting the app" = "Можете да започнете чат през Настройки на приложението / База данни или като рестартирате приложението"; + +/* No comment provided by engineer. */ +"You can turn on SimpleX Lock via Settings." = "Можете да включите SimpleX заключване през Настройки."; + +/* No comment provided by engineer. */ +"You can use markdown to format messages:" = "Можете да използвате markdown за форматиране на съобщенията:"; + +/* No comment provided by engineer. */ +"You can't send messages!" = "Не може да изпращате съобщения!"; + +/* chat item text */ +"you changed address" = "променихте адреса"; + +/* chat item text */ +"you changed address for %@" = "променихте адреса за %@"; + +/* snd group event chat item */ +"you changed role for yourself to %@" = "променихте ролята си на %@"; + +/* snd group event chat item */ +"you changed role of %@ to %@" = "променихте ролята на %1$@ на %2$@"; + +/* No comment provided by engineer. */ +"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Вие контролирате през кой сървър(и) **да получавате** съобщенията, вашите контакти – сървърите, които използвате, за да им изпращате съобщения."; + +/* No comment provided by engineer. */ +"You could not be verified; please try again." = "Не можахте да бъдете потвърдени; Моля, опитайте отново."; + +/* No comment provided by engineer. */ +"You have no chats" = "Нямате чатове"; + +/* No comment provided by engineer. */ +"You have to enter passphrase every time the app starts - it is not stored on the device." = "Трябва да въвеждате парола при всяко стартиране на приложението - тя не се съхранява на устройството."; + +/* No comment provided by engineer. */ +"You invited a contact" = "Вие поканихте контакта"; + +/* No comment provided by engineer. */ +"You joined this group" = "Вие се присъединихте към тази група"; + +/* No comment provided by engineer. */ +"You joined this group. Connecting to inviting group member." = "Вие се присъединихте към тази група. Свързване с поканващия член на групата."; + +/* snd group event chat item */ +"you left" = "вие напуснахте"; + +/* No comment provided by engineer. */ +"You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Трябва да използвате най-новата версия на вашата чат база данни САМО на едно устройство, в противен случай може да спрете да получавате съобщения от някои контакти."; + +/* No comment provided by engineer. */ +"You need to allow your contact to send voice messages to be able to send them." = "Трябва да разрешите на вашия контакт да изпраща гласови съобщения, за да можете да ги изпращате."; + +/* No comment provided by engineer. */ +"You rejected group invitation" = "Отхвърлихте поканата за групата"; + +/* snd group event chat item */ +"you removed %@" = "премахнахте %@"; + +/* No comment provided by engineer. */ +"You sent group invitation" = "Изпратихте покана за групата"; + +/* chat list item description */ +"you shared one-time link" = "споделихте еднократен линк за връзка"; + +/* chat list item description */ +"you shared one-time link incognito" = "споделихте еднократен инкогнито линк за връзка"; + +/* No comment provided by engineer. */ +"You will be connected to group when the group host's device is online, please wait or check later!" = "Ще бъдете свързани с групата, когато устройството на домакина на групата е онлайн, моля, изчакайте или проверете по-късно!"; + +/* 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. */ +"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Ще трябва да се идентифицирате, когато стартирате или възобновите приложението след 30 секунди във фонов режим."; + +/* No comment provided by engineer. */ +"You will join a group this link refers to and connect to its group members." = "Ще се присъедините към групата, към която този линк препраща, и ще се свържете с нейните членове."; + +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "Все още ще получавате обаждания и известия от заглушени профили, когато са активни."; + +/* No comment provided by engineer. */ +"You will stop receiving messages from this group. Chat history will be preserved." = "Ще спрете да получавате съобщения от тази група. Историята на чата ще бъде запазена."; + +/* No comment provided by engineer. */ +"You won't lose your contacts if you later delete your address." = "Няма да загубите контактите си, ако по-късно изтриете адреса си."; + +/* No comment provided by engineer. */ +"you: " = "вие: "; + +/* No comment provided by engineer. */ +"You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile" = "Опитвате се да поканите контакт, с когото сте споделили инкогнито профил, в групата, в която използвате основния си профил"; + +/* No comment provided by engineer. */ +"You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Използвате инкогнито профил за тази група - за да се предотврати споделянето на основния ви профил, поканите на контакти не са разрешени"; + +/* No comment provided by engineer. */ +"Your %@ servers" = "Вашите %@ сървъри"; + +/* No comment provided by engineer. */ +"Your calls" = "Вашите обаждания"; + +/* No comment provided by engineer. */ +"Your chat database" = "Вашата чат база данни"; + +/* No comment provided by engineer. */ +"Your chat database is not encrypted - set passphrase to encrypt it." = "Вашата чат база данни не е криптирана - задайте парола, за да я криптирате."; + +/* No comment provided by engineer. */ +"Your chat profile will be sent to group members" = "Вашият чат профил ще бъде изпратен на членовете на групата"; + +/* No comment provided by engineer. */ +"Your chat profiles" = "Вашите чат профили"; + +/* No comment provided by engineer. */ +"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Вашият контакт трябва да бъде онлайн, за да осъществите връзката.\nМожете да откажете тази връзка и да премахнете контакта (и да опитате по -късно с нов линк)."; + +/* No comment provided by engineer. */ +"Your contact sent a file that is larger than currently supported maximum size (%@)." = "Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@)."; + +/* No comment provided by engineer. */ +"Your contacts can allow full message deletion." = "Вашите контакти могат да позволят пълното изтриване на съобщението."; + +/* No comment provided by engineer. */ +"Your contacts in SimpleX will see it.\nYou can change it in Settings." = "Вашите контакти в SimpleX ще го видят.\nМожете да го промените в Настройки."; + +/* No comment provided by engineer. */ +"Your contacts will remain connected." = "Вашите контакти ще останат свързани."; + +/* No comment provided by engineer. */ +"Your current chat database will be DELETED and REPLACED with the imported one." = "Вашата текуща чат база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната."; + +/* No comment provided by engineer. */ +"Your current profile" = "Вашият текущ профил"; + +/* No comment provided by engineer. */ +"Your ICE servers" = "Вашите ICE сървъри"; + +/* No comment provided by engineer. */ +"Your preferences" = "Вашите настройки"; + +/* No comment provided by engineer. */ +"Your privacy" = "Вашата поверителност"; + +/* No comment provided by engineer. */ +"Your profile **%@** will be shared." = "Вашият профил **%@** ще бъде споделен."; + +/* 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, contacts and delivered messages are stored on your device." = "Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство."; + +/* No comment provided by engineer. */ +"Your random profile" = "Вашият автоматично генериран профил"; + +/* No comment provided by engineer. */ +"Your server" = "Вашият сървър"; + +/* No comment provided by engineer. */ +"Your server address" = "Вашият адрес на сървъра"; + +/* No comment provided by engineer. */ +"Your settings" = "Вашите настройки"; + +/* No comment provided by engineer. */ +"Your SimpleX address" = "Вашият SimpleX адрес"; + +/* No comment provided by engineer. */ +"Your SMP servers" = "Вашите SMP сървъри"; + +/* No comment provided by engineer. */ +"Your XFTP servers" = "Вашите XFTP сървъри"; + diff --git a/apps/ios/bg.lproj/SimpleX--iOS--InfoPlist.strings b/apps/ios/bg.lproj/SimpleX--iOS--InfoPlist.strings new file mode 100644 index 000000000..d85455d87 --- /dev/null +++ b/apps/ios/bg.lproj/SimpleX--iOS--InfoPlist.strings @@ -0,0 +1,15 @@ +/* Bundle name */ +"CFBundleName" = "SimpleX"; + +/* Privacy - Camera Usage Description */ +"NSCameraUsageDescription" = "SimpleX се нуждае от достъп до камерата, за да сканира QR кодове, за да се свърже с други потребители и за видео разговори."; + +/* Privacy - Face ID Usage Description */ +"NSFaceIDUsageDescription" = "SimpleX използва Face ID за локалнa идентификация"; + +/* Privacy - Microphone Usage Description */ +"NSMicrophoneUsageDescription" = "SimpleX се нуждае от достъп до микрофона за аудио и видео разговори и за запис на гласови съобщения."; + +/* Privacy - Photo Library Additions Usage Description */ +"NSPhotoLibraryAddUsageDescription" = "SimpleX се нуждае от достъп до фотобиблиотека за запазване на заснета и получена медия"; + From 52966e7e3dcec8bcf39b86a3f227eea4bef8eb74 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:05:09 +0100 Subject: [PATCH 5/6] core: optionally encrypt SMP files (#3082) * core: optionally encrypt SMP files * encrypt to temp file and rename or remove encryption args if it fails * fix file encryption error handling --- src/Simplex/Chat.hs | 75 +++++++++++++++++++-------------- src/Simplex/Chat/Mobile/File.hs | 15 +------ src/Simplex/Chat/Store/Files.hs | 18 +++++--- src/Simplex/Chat/Types.hs | 8 ++-- src/Simplex/Chat/Util.hs | 28 +++++++++++- src/Simplex/Chat/View.hs | 4 +- tests/ChatTests/Files.hs | 32 ++++++++++++++ 7 files changed, 124 insertions(+), 56 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 34981fa56..abf7c8f3c 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -73,6 +73,7 @@ import Simplex.Chat.Store.Shared import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Chat.Types.Util +import Simplex.Chat.Util (encryptFile) import Simplex.FileTransfer.Client.Main (maxFileSize) import Simplex.FileTransfer.Client.Presets (defaultXFTPServers) import Simplex.FileTransfer.Description (ValidFileDescription, gb, kb, mb) @@ -1734,22 +1735,15 @@ processChatCommand = \case ft' <- if encrypted then encryptLocalFile ft else pure ft receiveFile' user ft' rcvInline_ filePath_ where - encryptLocalFile ft@RcvFileTransfer {xftpRcvFile} = case xftpRcvFile of - Nothing -> throwChatError $ CEFileInternal "locally encrypted files can't be received via SMP" - Just f -> do - cfArgs <- liftIO $ CF.randomArgs - withStore' $ \db -> setFileCryptoArgs db fileId cfArgs - pure ft {xftpRcvFile = Just ((f :: XFTPRcvFile) {cryptoArgs = Just cfArgs})} + encryptLocalFile ft = do + cfArgs <- liftIO $ CF.randomArgs + withStore' $ \db -> setFileCryptoArgs db fileId cfArgs + pure (ft :: RcvFileTransfer) {cryptoArgs = Just cfArgs} SetFileToReceive fileId encrypted -> withUser $ \_ -> do withChatLock "setFileToReceive" . procCmd $ do - cfArgs <- if encrypted then fileCryptoArgs else pure Nothing + cfArgs <- if encrypted then Just <$> liftIO CF.randomArgs else pure Nothing withStore' $ \db -> setRcvFileToReceive db fileId cfArgs ok_ - where - fileCryptoArgs = do - (_, RcvFileTransfer {xftpRcvFile = f}) <- withStore (`getRcvFileTransferById` fileId) - unless (isJust f) $ throwChatError $ CEFileInternal "locally encrypted files can't be received via SMP" - liftIO $ Just <$> CF.randomArgs CancelFile fileId -> withUser $ \user@User {userId} -> withChatLock "cancelFile" . procCmd $ withStore (\db -> getFileTransfer db user fileId) >>= \case @@ -2319,7 +2313,7 @@ receiveFile' user ft rcvInline_ filePath_ = do e -> throwError e acceptFileReceive :: forall m. ChatMonad m => User -> RcvFileTransfer -> Maybe Bool -> Maybe FilePath -> m AChatItem -acceptFileReceive user@User {userId} RcvFileTransfer {fileId, xftpRcvFile, fileInvitation = FileInvitation {fileName = fName, fileConnReq, fileInline, fileSize}, fileStatus, grpMemberId} rcvInline_ filePath_ = do +acceptFileReceive user@User {userId} RcvFileTransfer {fileId, xftpRcvFile, fileInvitation = FileInvitation {fileName = fName, fileConnReq, fileInline, fileSize}, fileStatus, grpMemberId, cryptoArgs} rcvInline_ filePath_ = do unless (fileStatus == RFSNew) $ case fileStatus of RFSCancelled _ -> throwChatError $ CEFileCancelled fName _ -> throwChatError $ CEFileAlreadyReceiving fName @@ -2332,7 +2326,7 @@ acceptFileReceive user@User {userId} RcvFileTransfer {fileId, xftpRcvFile, fileI filePath <- getRcvFilePath fileId filePath_ fName True withStoreCtx (Just "acceptFileReceive, acceptRcvFileTransfer") $ \db -> acceptRcvFileTransfer db user fileId connIds ConnJoined filePath subMode -- XFTP - (Just XFTPRcvFile {cryptoArgs}, _) -> do + (Just XFTPRcvFile {}, _) -> do filePath <- getRcvFilePath fileId filePath_ fName False (ci, rfd) <- withStoreCtx (Just "acceptFileReceive, xftpAcceptRcvFT ...") $ \db -> do -- marking file as accepted and reading description in the same transaction @@ -2406,7 +2400,7 @@ getRcvFilePath fileId fPath_ fn keepHandle = case fPath_ of asks filesFolder >>= readTVarIO >>= \case Nothing -> do dir <- (`combine` "Downloads") <$> getHomeDirectory - ifM (doesDirectoryExist dir) (pure dir) getTemporaryDirectory + ifM (doesDirectoryExist dir) (pure dir) getChatTempDirectory >>= (`uniqueCombine` fn) >>= createEmptyFile Just filesFolder -> @@ -2434,14 +2428,18 @@ getRcvFilePath fileId fPath_ fn keepHandle = case fPath_ of pure fPath getTmpHandle :: FilePath -> m Handle getTmpHandle fPath = openFile fPath AppendMode `catchThrow` (ChatError . CEFileInternal . show) - uniqueCombine :: FilePath -> String -> m FilePath - uniqueCombine filePath fileName = tryCombine (0 :: Int) - where - tryCombine n = - let (name, ext) = splitExtensions fileName - suffix = if n == 0 then "" else "_" <> show n - f = filePath `combine` (name <> suffix <> ext) - in ifM (doesFileExist f) (tryCombine $ n + 1) (pure f) + +uniqueCombine :: MonadIO m => FilePath -> String -> m FilePath +uniqueCombine filePath fileName = tryCombine (0 :: Int) + where + tryCombine n = + let (name, ext) = splitExtensions fileName + suffix = if n == 0 then "" else "_" <> show n + f = filePath `combine` (name <> suffix <> ext) + in ifM (doesFileExist f) (tryCombine $ n + 1) (pure f) + +getChatTempDirectory :: ChatMonad m => m FilePath +getChatTempDirectory = chatReadVar tempDirectory >>= maybe getTemporaryDirectory pure acceptContactRequest :: ChatMonad m => User -> UserContactRequest -> Maybe IncognitoProfile -> m Contact acceptContactRequest user UserContactRequest {agentInvitationId = AgentInvId invId, cReqChatVRange, localDisplayName = cName, profileId, profile = cp, userContactLinkId, xContactId} incognitoProfile = do @@ -3513,12 +3511,12 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do RcvChunkOk -> if B.length chunk /= fromInteger chunkSize then badRcvFileChunk ft "incorrect chunk size" - else ack $ appendFileChunk ft chunkNo chunk + else ack $ appendFileChunk ft chunkNo chunk False RcvChunkFinal -> if B.length chunk > fromInteger chunkSize then badRcvFileChunk ft "incorrect chunk size" else do - appendFileChunk ft chunkNo chunk + appendFileChunk ft chunkNo chunk True ci <- withStore $ \db -> do liftIO $ do updateRcvFileStatus db fileId FSComplete @@ -3526,7 +3524,6 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do deleteRcvFileChunks db ft getChatItemByFileId db user fileId toView $ CRRcvFileComplete user ci - closeFileHandle fileId rcvFiles forM_ conn_ $ \conn -> deleteAgentConnectionAsync user (aConnId conn) RcvChunkDuplicate -> ack $ pure () RcvChunkError -> badRcvFileChunk ft $ "incorrect chunk number " <> show chunkNo @@ -3772,14 +3769,14 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do processFDMessage fileId fileDescr = do ft <- withStore $ \db -> getRcvFileTransfer db user fileId unless (rcvFileCompleteOrCancelled ft) $ do - (rfd, RcvFileTransfer {fileStatus, xftpRcvFile}) <- withStore $ \db -> do + (rfd, RcvFileTransfer {fileStatus, xftpRcvFile, cryptoArgs}) <- withStore $ \db -> do rfd <- appendRcvFD db userId fileId fileDescr -- reading second time in the same transaction as appending description -- to prevent race condition with accept ft' <- getRcvFileTransfer db user fileId pure (rfd, ft') case (fileStatus, xftpRcvFile) of - (RFSAccepted _, Just XFTPRcvFile {cryptoArgs}) -> receiveViaCompleteFD user fileId rfd cryptoArgs + (RFSAccepted _, Just XFTPRcvFile {}) -> receiveViaCompleteFD user fileId rfd cryptoArgs _ -> pure () cancelMessageFile :: Contact -> SharedMsgId -> MsgMeta -> m () @@ -4787,8 +4784,8 @@ readFileChunk SndFileTransfer {fileId, filePath, chunkSize} chunkNo = do parseFileChunk :: ChatMonad m => ByteString -> m FileChunk parseFileChunk = liftEither . first (ChatError . CEFileRcvChunk) . smpDecode -appendFileChunk :: ChatMonad m => RcvFileTransfer -> Integer -> ByteString -> m () -appendFileChunk ft@RcvFileTransfer {fileId, fileStatus} chunkNo chunk = +appendFileChunk :: forall m. ChatMonad m => RcvFileTransfer -> Integer -> ByteString -> Bool -> m () +appendFileChunk ft@RcvFileTransfer {fileId, fileStatus, cryptoArgs} chunkNo chunk final = case fileStatus of RFSConnected RcvFileInfo {filePath} -> append_ filePath -- sometimes update of file transfer status to FSConnected @@ -4797,11 +4794,27 @@ appendFileChunk ft@RcvFileTransfer {fileId, fileStatus} chunkNo chunk = RFSCancelled _ -> pure () _ -> throwChatError $ CEFileInternal "receiving file transfer not in progress" where + append_ :: FilePath -> m () append_ filePath = do fsFilePath <- toFSFilePath filePath h <- getFileHandle fileId fsFilePath rcvFiles AppendMode - liftIO (B.hPut h chunk >> hFlush h) `catchThrow` (ChatError . CEFileWrite filePath . show) + liftIO (B.hPut h chunk >> hFlush h) `catchThrow` (fileErr . show) withStore' $ \db -> updatedRcvFileChunkStored db ft chunkNo + when final $ do + closeFileHandle fileId rcvFiles + forM_ cryptoArgs $ \cfArgs -> do + tmpFile <- getChatTempDirectory >>= (`uniqueCombine` ft.fileInvitation.fileName) + tryChatError (liftError encryptErr $ encryptFile fsFilePath tmpFile cfArgs) >>= \case + Right () -> do + removeFile fsFilePath `catchChatError` \_ -> pure () + renameFile tmpFile fsFilePath + Left e -> do + toView $ CRChatError Nothing e + removeFile tmpFile `catchChatError` \_ -> pure () + withStore' (`removeFileCryptoArgs` fileId) + where + encryptErr e = fileErr $ e <> ", received file not encrypted" + fileErr = ChatError . CEFileWrite filePath getFileHandle :: ChatMonad m => Int64 -> FilePath -> (ChatController -> TVar (Map Int64 Handle)) -> IOMode -> m Handle getFileHandle fileId filePath files ioMode = do diff --git a/src/Simplex/Chat/Mobile/File.hs b/src/Simplex/Chat/Mobile/File.hs index 9dc4b5c98..e30b899f1 100644 --- a/src/Simplex/Chat/Mobile/File.hs +++ b/src/Simplex/Chat/Mobile/File.hs @@ -34,6 +34,7 @@ import Foreign.Ptr import Foreign.Storable (poke) import GHC.Generics (Generic) import Simplex.Chat.Mobile.Shared +import Simplex.Chat.Util (chunkSize, encryptFile) import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..), CryptoFileHandle, FTCryptoError (..)) import qualified Simplex.Messaging.Crypto.File as CF import Simplex.Messaging.Encoding.String @@ -105,16 +106,8 @@ chatEncryptFile fromPath toPath = where encrypt = do cfArgs <- liftIO $ CF.randomArgs - let toFile = CryptoFile toPath $ Just cfArgs - withExceptT show $ - withFile fromPath ReadMode $ \r -> CF.withFile toFile WriteMode $ \w -> do - encryptChunks r w - liftIO $ CF.hPutTag w + encryptFile fromPath toPath cfArgs pure cfArgs - encryptChunks r w = do - ch <- liftIO $ LB.hGet r chunkSize - unless (LB.null ch) $ liftIO $ CF.hPut w ch - unless (LB.length ch < chunkSize) $ encryptChunks r w cChatDecryptFile :: CString -> CString -> CString -> CString -> IO CString cChatDecryptFile cFromPath cKey cNonce cToPath = do @@ -149,7 +142,3 @@ chatDecryptFile fromPath keyStr nonceStr toPath = fromLeft "" <$> runCatchExcept runCatchExceptT :: ExceptT String IO a -> IO (Either String a) runCatchExceptT action = runExceptT action `catchAll` (pure . Left . show) - -chunkSize :: Num a => a -chunkSize = 65536 -{-# INLINE chunkSize #-} diff --git a/src/Simplex/Chat/Store/Files.hs b/src/Simplex/Chat/Store/Files.hs index 001c41d2d..a710696da 100644 --- a/src/Simplex/Chat/Store/Files.hs +++ b/src/Simplex/Chat/Store/Files.hs @@ -57,6 +57,7 @@ module Simplex.Chat.Store.Files xftpAcceptRcvFT, setRcvFileToReceive, setFileCryptoArgs, + removeFileCryptoArgs, getRcvFilesToReceive, setRcvFTAgentDeleted, updateRcvFileStatus, @@ -487,7 +488,7 @@ createRcvFileTransfer db userId Contact {contactId, localDisplayName = c} f@File rfd_ <- mapM (createRcvFD_ db userId currentTs) fileDescr let rfdId = (\RcvFileDescr {fileDescrId} -> fileDescrId) <$> rfd_ -- cryptoArgs = Nothing here, the decision to encrypt is made when receiving it - xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False, cryptoArgs = Nothing}) <$> rfd_ + xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False}) <$> rfd_ fileProtocol = if isJust rfd_ then FPXFTP else FPSMP fileId <- liftIO $ do DB.execute @@ -500,7 +501,7 @@ createRcvFileTransfer db userId Contact {contactId, localDisplayName = c} f@File db "INSERT INTO rcv_files (file_id, file_status, file_queue_info, file_inline, rcv_file_inline, file_descr_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)" (fileId, FSNew, fileConnReq, fileInline, rcvFileInline, rfdId, currentTs, currentTs) - pure RcvFileTransfer {fileId, xftpRcvFile, fileInvitation = f, fileStatus = RFSNew, rcvFileInline, senderDisplayName = c, chunkSize, cancelled = False, grpMemberId = Nothing} + pure RcvFileTransfer {fileId, xftpRcvFile, fileInvitation = f, fileStatus = RFSNew, rcvFileInline, senderDisplayName = c, chunkSize, cancelled = False, grpMemberId = Nothing, cryptoArgs = Nothing} createRcvGroupFileTransfer :: DB.Connection -> UserId -> GroupMember -> FileInvitation -> Maybe InlineFileMode -> Integer -> ExceptT StoreError IO RcvFileTransfer createRcvGroupFileTransfer db userId GroupMember {groupId, groupMemberId, localDisplayName = c} f@FileInvitation {fileName, fileSize, fileConnReq, fileInline, fileDescr} rcvFileInline chunkSize = do @@ -508,7 +509,7 @@ createRcvGroupFileTransfer db userId GroupMember {groupId, groupMemberId, localD rfd_ <- mapM (createRcvFD_ db userId currentTs) fileDescr let rfdId = (\RcvFileDescr {fileDescrId} -> fileDescrId) <$> rfd_ -- cryptoArgs = Nothing here, the decision to encrypt is made when receiving it - xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False, cryptoArgs = Nothing}) <$> rfd_ + xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False}) <$> rfd_ fileProtocol = if isJust rfd_ then FPXFTP else FPSMP fileId <- liftIO $ do DB.execute @@ -521,7 +522,7 @@ createRcvGroupFileTransfer db userId GroupMember {groupId, groupMemberId, localD db "INSERT INTO rcv_files (file_id, file_status, file_queue_info, file_inline, rcv_file_inline, group_member_id, file_descr_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)" (fileId, FSNew, fileConnReq, fileInline, rcvFileInline, groupMemberId, rfdId, currentTs, currentTs) - pure RcvFileTransfer {fileId, xftpRcvFile, fileInvitation = f, fileStatus = RFSNew, rcvFileInline, senderDisplayName = c, chunkSize, cancelled = False, grpMemberId = Just groupMemberId} + pure RcvFileTransfer {fileId, xftpRcvFile, fileInvitation = f, fileStatus = RFSNew, rcvFileInline, senderDisplayName = c, chunkSize, cancelled = False, grpMemberId = Just groupMemberId, cryptoArgs = Nothing} createRcvFD_ :: DB.Connection -> UserId -> UTCTime -> FileDescr -> ExceptT StoreError IO RcvFileDescr createRcvFD_ db userId currentTs FileDescr {fileDescrText, fileDescrPartNo, fileDescrComplete} = do @@ -639,8 +640,8 @@ getRcvFileTransfer db User {userId} fileId = do ft senderDisplayName fileStatus = let fileInvitation = FileInvitation {fileName, fileSize, fileDigest = Nothing, fileConnReq, fileInline, fileDescr = Nothing} cryptoArgs = CFArgs <$> fileKey <*> fileNonce - xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId, agentRcvFileDeleted, cryptoArgs}) <$> rfd_ - in RcvFileTransfer {fileId, xftpRcvFile, fileInvitation, fileStatus, rcvFileInline, senderDisplayName, chunkSize, cancelled, grpMemberId} + xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId, agentRcvFileDeleted}) <$> rfd_ + in RcvFileTransfer {fileId, xftpRcvFile, fileInvitation, fileStatus, rcvFileInline, senderDisplayName, chunkSize, cancelled, grpMemberId, cryptoArgs} rfi = maybe (throwError $ SERcvFileInvalid fileId) pure =<< rfi_ rfi_ = case (filePath_, connId_, agentConnId_) of (Just filePath, connId, agentConnId) -> pure $ Just RcvFileInfo {filePath, connId, agentConnId} @@ -709,6 +710,11 @@ setFileCryptoArgs_ db fileId (CFArgs key nonce) currentTs = "UPDATE files SET file_crypto_key = ?, file_crypto_nonce = ?, updated_at = ? WHERE file_id = ?" (key, nonce, currentTs, fileId) +removeFileCryptoArgs :: DB.Connection -> FileTransferId -> IO () +removeFileCryptoArgs db fileId = do + currentTs <- getCurrentTime + DB.execute db "UPDATE files SET file_crypto_key = NULL, file_crypto_nonce = NULL, updated_at = ? WHERE file_id = ?" (currentTs, fileId) + getRcvFilesToReceive :: DB.Connection -> User -> IO [RcvFileTransfer] getRcvFilesToReceive db user@User {userId} = do cutoffTs <- addUTCTime (- (2 * nominalDay)) <$> getCurrentTime diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index ac2c55735..ecae9eb09 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -986,7 +986,10 @@ data RcvFileTransfer = RcvFileTransfer senderDisplayName :: ContactName, chunkSize :: Integer, cancelled :: Bool, - grpMemberId :: Maybe Int64 + grpMemberId :: Maybe Int64, + -- XFTP files are encrypted as they are received, they are never stored unecrypted + -- SMP files are encrypted after all chunks are received + cryptoArgs :: Maybe CryptoFileArgs } deriving (Eq, Show, Generic) @@ -995,8 +998,7 @@ instance ToJSON RcvFileTransfer where toEncoding = J.genericToEncoding J.default data XFTPRcvFile = XFTPRcvFile { rcvFileDescription :: RcvFileDescr, agentRcvFileId :: Maybe AgentRcvFileId, - agentRcvFileDeleted :: Bool, - cryptoArgs :: Maybe CryptoFileArgs + agentRcvFileDeleted :: Bool } deriving (Eq, Show, Generic) diff --git a/src/Simplex/Chat/Util.hs b/src/Simplex/Chat/Util.hs index 7a350705f..46b5be28b 100644 --- a/src/Simplex/Chat/Util.hs +++ b/src/Simplex/Chat/Util.hs @@ -1,6 +1,32 @@ -module Simplex.Chat.Util (week) where +module Simplex.Chat.Util (week, encryptFile, chunkSize) where +import Control.Monad +import Control.Monad.Except +import Control.Monad.IO.Class +import qualified Data.ByteString.Lazy as LB import Data.Time (NominalDiffTime) +import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..)) +import qualified Simplex.Messaging.Crypto.File as CF +import UnliftIO.IO (IOMode (..), withFile) week :: NominalDiffTime week = 7 * 86400 + +encryptFile :: FilePath -> FilePath -> CryptoFileArgs -> ExceptT String IO () +encryptFile fromPath toPath cfArgs = do + let toFile = CryptoFile toPath $ Just cfArgs + -- uncomment to test encryption error in runTestFileTransferEncrypted + -- throwError "test error" + withExceptT show $ + withFile fromPath ReadMode $ \r -> CF.withFile toFile WriteMode $ \w -> do + encryptChunks r w + liftIO $ CF.hPutTag w + where + encryptChunks r w = do + ch <- liftIO $ LB.hGet r chunkSize + unless (LB.null ch) $ liftIO $ CF.hPut w ch + unless (LB.length ch < chunkSize) $ encryptChunks r w + +chunkSize :: Num a => a +chunkSize = 65536 +{-# INLINE chunkSize #-} diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 3607bbda5..5db0c317e 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -1592,8 +1592,8 @@ viewChatError logLevel = \case CEFileCancelled f -> ["file cancelled: " <> plain f] CEFileCancel fileId e -> ["error cancelling file " <> sShow fileId <> ": " <> sShow e] CEFileAlreadyExists f -> ["file already exists: " <> plain f] - CEFileRead f e -> ["cannot read file " <> plain f, sShow e] - CEFileWrite f e -> ["cannot write file " <> plain f, sShow e] + CEFileRead f e -> ["cannot read file " <> plain f <> ": " <> plain e] + CEFileWrite f e -> ["cannot write file " <> plain f <> ": " <> plain e] CEFileSend fileId e -> ["error sending file " <> sShow fileId <> ": " <> sShow e] CEFileRcvChunk e -> ["error receiving file: " <> plain e] CEFileInternal e -> ["file error: " <> plain e] diff --git a/tests/ChatTests/Files.hs b/tests/ChatTests/Files.hs index 386b917f8..f84d4dcb4 100644 --- a/tests/ChatTests/Files.hs +++ b/tests/ChatTests/Files.hs @@ -31,6 +31,7 @@ chatFileTests :: SpecWith FilePath chatFileTests = do describe "sending and receiving files" $ do describe "send and receive file" $ fileTestMatrix2 runTestFileTransfer + describe "send file, receive and locally encrypt file" $ fileTestMatrix2 runTestFileTransferEncrypted it "send and receive file inline (without accepting)" testInlineFileTransfer xit'' "accept inline file transfer, sender cancels during transfer" testAcceptInlineFileSndCancelDuringTransfer it "send and receive small file inline (default config)" testSmallInlineFileTransfer @@ -97,6 +98,37 @@ runTestFileTransfer alice bob = do dest <- B.readFile "./tests/tmp/test.pdf" dest `shouldBe` src +runTestFileTransferEncrypted :: HasCallStack => TestCC -> TestCC -> IO () +runTestFileTransferEncrypted alice bob = do + connectUsers alice bob + alice #> "/f @bob ./tests/fixtures/test.pdf" + alice <## "use /fc 1 to cancel sending" + bob <# "alice> sends file test.pdf (266.0 KiB / 272376 bytes)" + bob <## "use /fr 1 [<dir>/ | <path>] to receive it" + bob ##> "/fr 1 encrypt=on ./tests/tmp" + bob <## "saving file 1 from alice to ./tests/tmp/test.pdf" + Just (CFArgs key nonce) <- J.decode . LB.pack <$> getTermLine bob + concurrently_ + (bob <## "started receiving file 1 (test.pdf) from alice") + (alice <## "started sending file 1 (test.pdf) to bob") + + concurrentlyN_ + [ do + bob #> "@alice receiving here..." + -- uncomment this and below to test encryption error in encryptFile + -- bob <## "cannot write file ./tests/tmp/test.pdf: test error, received file not encrypted" + bob <## "completed receiving file 1 (test.pdf) from alice", + alice + <### [ WithTime "bob> receiving here...", + "completed sending file 1 (test.pdf) to bob" + ] + ] + src <- B.readFile "./tests/fixtures/test.pdf" + -- dest <- B.readFile "./tests/tmp/test.pdf" + -- dest `shouldBe` src + Right dest <- chatReadFile "./tests/tmp/test.pdf" (strEncode key) (strEncode nonce) + LB.toStrict dest `shouldBe` src + testInlineFileTransfer :: HasCallStack => FilePath -> IO () testInlineFileTransfer = testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> do From 92ac3e2a8a769310df7ad254c9021544c773631f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:45:04 +0100 Subject: [PATCH 6/6] core: update contact and member profiles for both sides when contact is created with member (WIP) (#3081) * core: update contact and member profiles for both sides when contact is created with member (WIP) * send both sides, correctly process update * refactor * revert diff * comments * test --------- Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> --- src/Simplex/Chat.hs | 47 +++++++++++++++---------- tests/ChatTests/Groups.hs | 73 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 18 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index abf7c8f3c..e74eaa0f5 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -3060,10 +3060,14 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do -- TODO update member profile -- [async agent commands] no continuation needed, but command should be asynchronous for stability allowAgentConnectionAsync user conn' confId XOk - XOk -> do - allowAgentConnectionAsync user conn' confId XOk - void $ withStore' $ \db -> resetMemberContactFields db ct - _ -> messageError "CONF for existing contact must have x.grp.mem.info or x.ok" + XInfo profile -> do + ct' <- processContactProfileUpdate ct profile False `catchChatError` const (pure ct) + -- [incognito] send incognito profile + incognitoProfile <- forM customUserProfileId $ \profileId -> withStore $ \db -> getProfileById db userId profileId + let p = userProfileToSend user (fromLocalProfile <$> incognitoProfile) (Just ct') + allowAgentConnectionAsync user conn' confId $ XInfo p + void $ withStore' $ \db -> resetMemberContactFields db ct' + _ -> messageError "CONF for existing contact must have x.grp.mem.info or x.info" INFO connInfo -> do ChatMessage {chatVRange, chatMsgEvent} <- parseChatMessage conn connInfo _conn' <- updatePeerChatVRange conn chatVRange @@ -3072,9 +3076,8 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do -- TODO check member ID -- TODO update member profile pure () - XInfo _profile -> do - -- TODO update contact profile - pure () + XInfo profile -> + void $ processContactProfileUpdate ct profile False XOk -> pure () _ -> messageError "INFO for existing contact must have x.grp.mem.info, x.info or x.ok" CON -> @@ -4233,15 +4236,22 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do MsgError e -> createInternalChatItem user cd (CIRcvIntegrityError e) (Just brokerTs) xInfo :: Contact -> Profile -> m () - xInfo c@Contact {profile = p} p' = unless (fromLocalProfile p == p') $ do - c' <- withStore $ \db -> - if userTTL == rcvTTL - then updateContactProfile db user c p' - else do - c' <- liftIO $ updateContactUserPreferences db user c ctUserPrefs' - updateContactProfile db user c' p' - when (directOrUsed c') $ createRcvFeatureItems user c c' - toView $ CRContactUpdated user c c' + xInfo c p' = void $ processContactProfileUpdate c p' True + + processContactProfileUpdate :: Contact -> Profile -> Bool -> m Contact + processContactProfileUpdate c@Contact {profile = p} p' createItems + | fromLocalProfile p /= p' = do + c' <- withStore $ \db -> + if userTTL == rcvTTL + then updateContactProfile db user c p' + else do + c' <- liftIO $ updateContactUserPreferences db user c ctUserPrefs' + updateContactProfile db user c' p' + when (directOrUsed c' && createItems) $ createRcvFeatureItems user c c' + toView $ CRContactUpdated user c c' + pure c' + | otherwise = + pure c where Contact {userPreferences = ctUserPrefs@Preferences {timedMessages = ctUserTMPref}} = c userTTL = prefParam $ getPreference SCFTimedMessages ctUserPrefs @@ -4639,8 +4649,9 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do (mCt', m') <- withStore' $ \db -> createMemberContactInvited db user connIds g m mConn subMode createItems mCt' m' joinConn subMode = do - -- TODO send user's profile for this group membership - dm <- directMessage XOk + -- [incognito] send membership incognito profile + let p = userProfileToSend user (fromLocalProfile <$> incognitoMembershipProfile g) Nothing + dm <- directMessage $ XInfo p joinAgentConnectionAsync user True connReq dm subMode createItems mCt' m' = do checkIntegrityCreateItem (CDGroupRcv g m') msgMeta diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 7cdb9a309..bf740a960 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -81,6 +81,7 @@ chatGroupTests = do it "prohibited to repeat sending x.grp.direct.inv" testMemberContactProhibitedRepeatInv it "invited member replaces member contact reference if it already exists" testMemberContactInvitedConnectionReplaced it "share incognito profile" testMemberContactIncognito + it "sends and updates profile when creating contact" testMemberContactProfileUpdate where _0 = supportedChatVRange -- don't create direct connections _1 = groupCreateDirectVRange @@ -3047,3 +3048,75 @@ testMemberContactIncognito = [ alice <# ("#team " <> cathIncognito <> "> hey"), bob ?<# ("#team " <> cathIncognito <> "> hey") ] + +testMemberContactProfileUpdate :: HasCallStack => FilePath -> IO () +testMemberContactProfileUpdate = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + + bob ##> "/p rob Rob" + bob <## "user profile is changed to rob (Rob) (your 1 contacts are notified)" + alice <## "contact bob changed to rob (Rob)" + alice <## "use @rob <message> to send messages" + + cath ##> "/p kate Kate" + cath <## "user profile is changed to kate (Kate) (your 1 contacts are notified)" + alice <## "contact cath changed to kate (Kate)" + alice <## "use @kate <message> to send messages" + + alice #> "#team hello" + bob <# "#team alice> hello" + cath <# "#team alice> hello" + + bob #> "#team hello too" + alice <# "#team rob> hello too" + cath <# "#team bob> hello too" -- not updated profile + + cath #> "#team hello there" + alice <# "#team kate> hello there" + bob <# "#team cath> hello there" -- not updated profile + + bob `send` "@cath hi" + bob + <### [ "member #team cath does not have direct connection, creating", + "contact for member #team cath is created", + "sent invitation to connect directly to member #team cath", + WithTime "@cath hi" + ] + cath + <### [ "#team bob is creating direct contact bob with you", + WithTime "bob> hi" + ] + concurrentlyN_ + [ do + bob <## "contact cath changed to kate (Kate)" + bob <## "use @kate <message> to send messages" + bob <## "kate (Kate): contact is connected", + do + cath <## "contact bob changed to rob (Rob)" + cath <## "use @rob <message> to send messages" + cath <## "rob (Rob): contact is connected" + ] + + bob ##> "/contacts" + bob + <### [ "alice (Alice)", + "kate (Kate)" + ] + cath ##> "/contacts" + cath + <### [ "alice (Alice)", + "rob (Rob)" + ] + alice `hasContactProfiles` ["alice", "rob", "kate"] + bob `hasContactProfiles` ["rob", "alice", "kate"] + cath `hasContactProfiles` ["kate", "alice", "rob"] + + bob #> "#team hello too" + alice <# "#team rob> hello too" + cath <# "#team rob> hello too" -- updated profile + + cath #> "#team hello there" + alice <# "#team kate> hello there" + bob <# "#team kate> hello there" -- updated profile