Merge remote-tracking branch 'origin/release/2.36.0'

This commit is contained in:
Michael Kirk 2019-02-19 12:50:00 -07:00
commit d26c095fe7
37 changed files with 447 additions and 296 deletions

2
Pods

@ -1 +1 @@
Subproject commit 30865575d8f4ef74dc51e5bde3eab1cf7d458784
Subproject commit 434610837e73668d186716fd2e5b8913c84ef46a

View File

@ -47,7 +47,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2.36.0.5</string>
<string>2.36.0.7</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LOGS_EMAIL</key>
@ -69,7 +69,7 @@
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
<key>api-staging.directory.signal.org </key>
<key>api-staging.directory.signal.org</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>

View File

@ -732,7 +732,7 @@ static NSTimeInterval launchStartedAt;
OWSLogInfo(@"running post launch block for unregistered user.");
// Unregistered user should have no unread messages. e.g. if you delete your account.
[SignalApp clearAllNotifications];
[AppEnvironment.shared.notificationPresenter clearAllNotifications];
[self.socketManager requestSocketOpen];
@ -791,6 +791,7 @@ static NSTimeInterval launchStartedAt;
OWSLogWarn(@"applicationWillResignActive.");
[self updateShouldEnableLandscape];
[self clearAllNotificationsAndRestoreBadgeCount];
[DDLog flushLog];
}
@ -799,8 +800,8 @@ static NSTimeInterval launchStartedAt;
{
OWSAssertIsOnMainThread();
[SignalApp clearAllNotifications];
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
[AppEnvironment.shared.notificationPresenter clearAllNotifications];
[OWSMessageUtils.sharedManager updateApplicationBadgeCount];
}];
}

View File

@ -137,7 +137,8 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
@objc
public override init() {
if #available(iOS 10, *) {
let userNotificationsFeatureEnabled = false
if userNotificationsFeatureEnabled, #available(iOS 10, *) {
self.adaptee = UserNotificationPresenterAdaptee()
} else {
self.adaptee = LegacyNotificationPresenterAdaptee()
@ -613,15 +614,7 @@ class NotificationActionHandler {
throw NotificationError.failDebug("unable to find thread with id: \(threadId)")
}
return Promise { resolver in
self.dbConnection.asyncReadWrite({ transaction in
thread.markAllAsRead(with: transaction)
},
completionBlock: {
self.notificationPresenter.cancelNotifications(threadId: threadId)
resolver.fulfill(())
})
}
return markAsRead(thread: thread)
}
func reply(userInfo: [AnyHashable: Any], replyText: String) throws -> Promise<Void> {
@ -633,12 +626,16 @@ class NotificationActionHandler {
throw NotificationError.failDebug("unable to find thread with id: \(threadId)")
}
return ThreadUtil.sendMessageNonDurably(text: replyText,
thread: thread,
quotedReplyModel: nil,
messageSender: messageSender).recover { error in
Logger.warn("Failed to send reply message from notification with error: \(error)")
self.notificationPresenter.notifyForFailedSend(inThread: thread)
return markAsRead(thread: thread).then { () -> Promise<Void> in
let sendPromise = ThreadUtil.sendMessageNonDurably(text: replyText,
thread: thread,
quotedReplyModel: nil,
messageSender: self.messageSender)
return sendPromise.recover { error in
Logger.warn("Failed to send reply message from notification with error: \(error)")
self.notificationPresenter.notifyForFailedSend(inThread: thread)
}
}
}
@ -654,6 +651,12 @@ class NotificationActionHandler {
signalApp.presentConversation(forThreadId: threadId, animated: shouldAnimate)
return Promise.value(())
}
private func markAsRead(thread: TSThread) -> Promise<Void> {
return dbConnection.readWritePromise { transaction in
thread.markAllAsRead(with: transaction)
}
}
}
extension ThreadUtil {

View File

@ -222,6 +222,17 @@ extension LegacyNotificationPresenterAdaptee: NotificationPresenterAdaptee {
for (_, notification) in notifications {
cancelNotification(notification)
}
type(of: self).clearExistingNotifications()
}
public class func clearExistingNotifications() {
// This will cancel all "scheduled" local notifications that haven't
// been presented yet.
UIApplication.shared.cancelAllLocalNotifications()
// To clear all already presented local notifications, we need to
// set the app badge number to zero after setting it to a non-zero value.
UIApplication.shared.applicationIconBadgeNumber = 1
UIApplication.shared.applicationIconBadgeNumber = 0
}
}

View File

@ -181,6 +181,7 @@ extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee {
AssertIsOnMainThread()
notificationCenter.removeAllPendingNotificationRequests()
notificationCenter.removeAllDeliveredNotifications()
LegacyNotificationPresenterAdaptee.clearExistingNotifications()
}
func shouldPresentNotification(category: AppNotificationCategory, userInfo: [AnyHashable: Any]) -> Bool {

View File

@ -291,7 +291,9 @@ class PhotoLibrary: NSObject, PHPhotoLibraryChangeObserver {
var collections = [PhotoCollection]()
var collectionIds = Set<String>()
let processPHCollection: (PHCollection) -> Void = { (collection) in
let processPHCollection: ((collection: PHCollection, hideIfEmpty: Bool)) -> Void = { arg in
let (collection, hideIfEmpty) = arg
// De-duplicate by id.
let collectionId = collection.localIdentifier
guard !collectionIds.contains(collectionId) else {
@ -304,15 +306,14 @@ class PhotoLibrary: NSObject, PHPhotoLibraryChangeObserver {
return
}
let photoCollection = PhotoCollection(collection: assetCollection)
// Hide empty collections.
guard photoCollection.contents().assetCount > 0 else {
guard !hideIfEmpty || photoCollection.contents().assetCount > 0 else {
return
}
collections.append(photoCollection)
}
let processPHAssetCollections: (PHFetchResult<PHAssetCollection>) -> Void = { (fetchResult) in
// undocumented constant
let processPHAssetCollections: ((fetchResult: PHFetchResult<PHAssetCollection>, hideIfEmpty: Bool)) -> Void = { arg in
let (fetchResult, hideIfEmpty) = arg
fetchResult.enumerateObjects { (assetCollection, _, _) in
// We're already sorting albums by last-updated. "Recently Added" is mostly redundant
@ -320,33 +321,40 @@ class PhotoLibrary: NSObject, PHPhotoLibraryChangeObserver {
return
}
// undocumented constant
let kRecentlyDeletedAlbumSubtype = PHAssetCollectionSubtype(rawValue: 1000000201)
guard assetCollection.assetCollectionSubtype != kRecentlyDeletedAlbumSubtype else {
return
}
processPHCollection(assetCollection)
processPHCollection((collection: assetCollection, hideIfEmpty: hideIfEmpty))
}
}
let processPHCollections: (PHFetchResult<PHCollection>) -> Void = { (fetchResult) in
let processPHCollections: ((fetchResult: PHFetchResult<PHCollection>, hideIfEmpty: Bool)) -> Void = { arg in
let (fetchResult, hideIfEmpty) = arg
for index in 0..<fetchResult.count {
processPHCollection(fetchResult.object(at: index))
processPHCollection((collection: fetchResult.object(at: index), hideIfEmpty: hideIfEmpty))
}
}
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "endDate", ascending: true)]
// Try to add "Camera Roll" first.
processPHAssetCollections(PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: fetchOptions))
processPHAssetCollections((fetchResult: PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: fetchOptions),
hideIfEmpty: false))
// Favorites
processPHAssetCollections(PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumFavorites, options: fetchOptions))
processPHAssetCollections((fetchResult: PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumFavorites, options: fetchOptions),
hideIfEmpty: true))
// Smart albums.
processPHAssetCollections(PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: fetchOptions))
processPHAssetCollections((fetchResult: PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: fetchOptions),
hideIfEmpty: true))
// User-created albums.
processPHCollections(PHAssetCollection.fetchTopLevelUserCollections(with: fetchOptions))
processPHCollections((fetchResult: PHAssetCollection.fetchTopLevelUserCollections(with: fetchOptions),
hideIfEmpty: true))
return collections
}

View File

@ -50,7 +50,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)resetAppData;
+ (void)clearAllNotifications;
- (void)showHomeView;

View File

@ -138,26 +138,12 @@ NS_ASSUME_NONNULL_BEGIN
[OWSStorage resetAllStorage];
[OWSUserProfile resetProfileStorage];
[Environment.shared.preferences clear];
[self clearAllNotifications];
[AppEnvironment.shared.notificationPresenter clearAllNotifications];
[DebugLogger.sharedLogger wipeLogs];
exit(0);
}
+ (void)clearAllNotifications
{
OWSLogInfo(@"clearAllNotifications.");
// This will cancel all "scheduled" local notifications that haven't
// been presented yet.
[UIApplication.sharedApplication cancelAllLocalNotifications];
// To clear all already presented local notifications, we need to
// set the app badge number to zero after setting it to a non-zero value.
[UIApplication sharedApplication].applicationIconBadgeNumber = 1;
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
}
- (void)showHomeView
{
HomeViewController *homeView = [HomeViewController new];

View File

@ -211,7 +211,8 @@ public class LinkPreviewSent: NSObject, LinkPreviewState {
return nil
}
guard let image = UIImage(contentsOfFile: imageFilepath) else {
owsFail("Could not load image: \(imageFilepath)")
owsFailDebug("Could not load image: \(imageFilepath)")
return nil
}
return image
}

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Es necessita accés al micròfon";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Trucada entrant";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Telefona";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Trucada perduda perquè el número de seguretat de l'emissor ha canviat.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Trucada perduda";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Sense resposta";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Membre";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ a %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Nom d'aquest grup";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Envia";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "El teu missatge no s'ha enviat.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Ha fallat l'enviament de la invitació, torneu a provar-ho més tard.";

View File

@ -8,7 +8,7 @@
"ACTION_AUDIO_CALL" = "Hovor Signal";
/* Label for 'invite' button in contact view. */
"ACTION_INVITE" = "Pozvat do aplikace Signal";
"ACTION_INVITE" = "Pozvat do Signalu";
/* Label for 'send message' button in contact view.
Label for button that lets you send a message to a contact. */
@ -60,7 +60,7 @@
"APN_Message" = "Nová zpráva!";
/* Message for the 'app launch failed' alert. */
"APP_LAUNCH_FAILURE_ALERT_MESSAGE" = "Aplikaci Signal nelze spustit. Odešlete prosím debug log na support@signal.org, pokusíme se problém opravit.";
"APP_LAUNCH_FAILURE_ALERT_MESSAGE" = "Signal nelze spustit. Odešlete prosím debug log na support@signal.org, pokusíme se problém opravit.";
/* Title for the 'app launch failed' alert. */
"APP_LAUNCH_FAILURE_ALERT_TITLE" = "Chyba";
@ -75,7 +75,7 @@
"APP_UPDATE_NAG_ALERT_MESSAGE_FORMAT" = "V App Storu je nyní dostupná verze %@.";
/* Title for the 'new app version available' alert. */
"APP_UPDATE_NAG_ALERT_TITLE" = "Je dostupná nová verze aplikace Signal";
"APP_UPDATE_NAG_ALERT_TITLE" = "Je dostupná nová verze Signalu";
/* Label for the 'update' button in the 'new app version available' alert. */
"APP_UPDATE_NAG_ALERT_UPDATE_BUTTON" = "Aktualizovat";
@ -294,7 +294,7 @@
"BLOCK_OFFER_ACTIONSHEET_TITLE_FORMAT" = "Zablokovat %@?";
/* An explanation of the consequences of blocking another user. */
"BLOCK_USER_BEHAVIOR_EXPLANATION" = "Zablokovaní uživatelé vám nebudou moci zavolat nebo vám posílat zprávy.";
"BLOCK_USER_BEHAVIOR_EXPLANATION" = "Zablokovaní uživatelé vám nebudou moci zavolat nebo posílat zprávy.";
/* Label for 'continue' button. */
"BUTTON_CONTINUE" = "Pokračovat";
@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Vyžadován přístup k mikrofonu";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Příchozí hovor";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Zavolat";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Zmeškaný hovor. Změnilo se bezpečnostní číslo volajícího.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Zmeškaný hovor";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Žádná odpověď";
@ -363,10 +363,10 @@
"CALL_VIEW_MUTE_LABEL" = "Ztlumit";
/* Reminder to the user of the benefits of enabling CallKit and disabling CallKit privacy. */
"CALL_VIEW_SETTINGS_NAG_DESCRIPTION_ALL" = "Můžete povolit integraci s iOS hovory v nastavení soukromí aplikace Signal, abyste mohli přijímat příchozí hovory ze zamčené obrazovky.";
"CALL_VIEW_SETTINGS_NAG_DESCRIPTION_ALL" = "Můžete povolit integraci s iOS hovory v nastavení soukromí Signalu, abyste mohli přijímat příchozí hovory ze zamčené obrazovky.";
/* Reminder to the user of the benefits of disabling CallKit privacy. */
"CALL_VIEW_SETTINGS_NAG_DESCRIPTION_PRIVACY" = "Můžete povolit integraci s iOS hovory v nastavení soukromí aplikace Signal, abyste mohli vidět jméno a telefonní číslo pro příchozí hovory.";
"CALL_VIEW_SETTINGS_NAG_DESCRIPTION_PRIVACY" = "Můžete povolit integraci s iOS hovory v nastavení soukromí Signalu, abyste uviděli jméno a telefonní číslo příchozích hovorů.";
/* Label for button that dismiss the call view's settings nag. */
"CALL_VIEW_SETTINGS_NAG_NOT_NOW_BUTTON" = "Nyní ne";
@ -375,13 +375,13 @@
"CALL_VIEW_SETTINGS_NAG_SHOW_CALL_SETTINGS" = "Zobrazit nastavení soukromí";
/* Accessibility label to toggle front- vs. rear-facing camera */
"CALL_VIEW_SWITCH_CAMERA_DIRECTION" = "Přepnout kameru";
"CALL_VIEW_SWITCH_CAMERA_DIRECTION" = "Převrátit kameru";
/* Accessibility label to switch to audio only */
"CALL_VIEW_SWITCH_TO_AUDIO_LABEL" = "Přepnout na audio hovor";
/* Accessibility label to switch to video call */
"CALL_VIEW_SWITCH_TO_VIDEO_LABEL" = "Přepnout na video hovor";
"CALL_VIEW_SWITCH_TO_VIDEO_LABEL" = "Přepnout na videohovor";
/* Label for the 'return to call' banner. */
"CALL_WINDOW_RETURN_TO_CALL" = "Klepněte pro návrat k hovoru";
@ -390,7 +390,7 @@
"CALLBACK_BUTTON_TITLE" = "Zavolat zpět";
/* The generic name used for calls if CallKit privacy is enabled */
"CALLKIT_ANONYMOUS_CONTACT_NAME" = "Uživatel aplikace Signal";
"CALLKIT_ANONYMOUS_CONTACT_NAME" = "Uživatel Signalu";
/* Message for alert explaining that a user cannot be verified. */
"CANT_VERIFY_IDENTITY_ALERT_MESSAGE" = "Tento uživatel nemůže být ověřen, dokud neodešlete zprávu.";
@ -414,13 +414,13 @@
"CHECK_FOR_BACKUP_RESTORE" = "Obnovení";
/* Error indicating that the app could not determine that user's iCloud account status */
"CLOUDKIT_STATUS_COULD_NOT_DETERMINE" = "Nebylo možné zjistit stav účtu iCloud. Přihlašte se do svého účtu iCloud v nastavení iOS pro zálohování dat v Signalu.";
"CLOUDKIT_STATUS_COULD_NOT_DETERMINE" = "Nebylo možné zjistit stav účtu iCloud. Přihlašte se do svého účtu iCloud v nastavení iOS pro zálohování dat Signalu.";
/* Error indicating that user does not have an iCloud account. */
"CLOUDKIT_STATUS_NO_ACCOUNT" = "Žádný účet iCloud. Přihlašte se do svého účtu iCloud v nastavení iOS pro zálohování dat v Signalu.";
"CLOUDKIT_STATUS_NO_ACCOUNT" = "Žádný účet iCloud. Přihlašte se do svého účtu iCloud v nastavení iOS pro zálohování dat Signalu.";
/* Error indicating that the app was prevented from accessing the user's iCloud account. */
"CLOUDKIT_STATUS_RESTRICTED" = "Signalu byl odepřen přístup k účtu iCloud pro vytvoření zálohy. Povolte Signalu přístup k vašemu účtu iCloud v nastavení iOS pro zálohování dat.";
"CLOUDKIT_STATUS_RESTRICTED" = "Signalu byl odepřen přístup k účtu iCloud pro vytvoření zálohy. Pro zálohování dat povolte Signalu přístup k vašemu účtu iCloud v nastavení iOS.";
/* The first of two messages demonstrating the chosen conversation color, by rendering this message in an outgoing message bubble. */
"COLOR_PICKER_DEMO_MESSAGE_1" = "Vyberte barvu odchozích zpráv v této konverzaci.";
@ -486,7 +486,7 @@
"CONTACT_EDIT_NAME_BUTTON" = "Upravit";
/* Label for a contact's email address. */
"CONTACT_EMAIL" = "Email";
"CONTACT_EMAIL" = "E-mail";
/* Label for the 'city' field of a contact's address. */
"CONTACT_FIELD_ADDRESS_CITY" = "Město";
@ -516,13 +516,13 @@
"CONTACT_FIELD_GIVEN_NAME" = "Křestní jméno";
/* Label for the 'middle name' field of a contact. */
"CONTACT_FIELD_MIDDLE_NAME" = "Prostřední jméno";
"CONTACT_FIELD_MIDDLE_NAME" = "Druhé jméno";
/* Label for the 'name prefix' field of a contact. */
"CONTACT_FIELD_NAME_PREFIX" = "Titul před jménem";
"CONTACT_FIELD_NAME_PREFIX" = "Před jménem";
/* Label for the 'name suffix' field of a contact. */
"CONTACT_FIELD_NAME_SUFFIX" = "Titul za jménem";
"CONTACT_FIELD_NAME_SUFFIX" = "Za jménem";
/* Label for the 'organization' field of a contact. */
"CONTACT_FIELD_ORGANIZATION" = "Organizace";
@ -552,7 +552,7 @@
"CONTACT_SHARE_NO_FIELDS_SELECTED" = "Nebylo vybráno žádné pole kontaktu.";
/* Label for 'open address in maps app' button in contact view. */
"CONTACT_VIEW_OPEN_ADDRESS_IN_MAPS_APP" = "Otevřít v mapách";
"CONTACT_VIEW_OPEN_ADDRESS_IN_MAPS_APP" = "Otevřít v Mapách";
/* Label for 'open email in email app' button in contact view. */
"CONTACT_VIEW_OPEN_EMAIL_IN_EMAIL_APP" = "Poslat email";
@ -570,7 +570,7 @@
"CONVERSATION_SETTINGS" = "Nastavení konverzace";
/* Label for 'new contact' button in conversation settings view. */
"CONVERSATION_SETTINGS_ADD_TO_EXISTING_CONTACT" = "Přidat k existujícímu kontaktu";
"CONVERSATION_SETTINGS_ADD_TO_EXISTING_CONTACT" = "Přidat ke kontaktu";
/* table cell label in conversation settings */
"CONVERSATION_SETTINGS_BLOCK_THIS_GROUP" = "Zablokovat tuto skupinu";
@ -594,7 +594,7 @@
"CONVERSATION_SETTINGS_MUTE_LABEL" = "Ztlumit";
/* Indicates that the current thread is not muted. */
"CONVERSATION_SETTINGS_MUTE_NOT_MUTED" = "Neztlumen";
"CONVERSATION_SETTINGS_MUTE_NOT_MUTED" = "Neztlumeno";
/* Label for button to mute a thread for a day. */
"CONVERSATION_SETTINGS_MUTE_ONE_DAY_ACTION" = "Ztlumit na den";
@ -672,7 +672,7 @@
"DATABASE_VIEW_OVERLAY_SUBTITLE" = "Toto může pár minut trvat.";
/* Title shown while the app is updating its database. */
"DATABASE_VIEW_OVERLAY_TITLE" = "Optimalizuji databázi";
"DATABASE_VIEW_OVERLAY_TITLE" = "Optimalizace databáze";
/* Format string for a relative time, expressed as a certain number of hours in the past. Embeds {{The number of hours}}. */
"DATE_HOURS_AGO_FORMAT" = "před %@h";
@ -739,7 +739,7 @@
"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER" = "Znovu zaregistrovat toto telefonní číslo";
/* Label warning the user that they have been de-registered. */
"DEREGISTRATION_WARNING" = "Zařízení již není registrováno. Vaše telefonní číslo může být registrováno v aplikaci Signal na jiném zařízení. Klepněte pro opětovnou registraci.";
"DEREGISTRATION_WARNING" = "Zařízení již není registrováno. Je možné, že je vaše telefonní číslo registrováno v aplikaci Signal na jiném zařízení. Klepněte pro opětovnou registraci.";
/* {{Short Date}} when device last communicated with Signal Server. */
"DEVICE_LAST_ACTIVE_AT_LABEL" = "Naposledy aktivní: %@";
@ -757,7 +757,7 @@
"DISAPPEARING_MESSAGES_CONFIGURATION_GROUP_EXISTING_FORMAT" = "Zprávy v konverzaci zmizí za %@.";
/* subheading in conversation settings */
"DISAPPEARING_MESSAGES_DESCRIPTION" = "Pokud je povoleno, odeslané a přiaté zprávy v této konverzaci zmizí, jakmile budou viděny.";
"DISAPPEARING_MESSAGES_DESCRIPTION" = "Pokud je povoleno, odeslané a přijaté zprávy v této konverzaci zmizí, jakmile budou zobrazeny.";
/* Accessibility hint that contains current timeout information */
"DISAPPEARING_MESSAGES_HINT" = "Nyní zprávy zmizí po %@";
@ -775,7 +775,7 @@
"EDIT_CONTACT_WITHOUT_CONTACTS_PERMISSION_ALERT_BODY" = "Můžete povolit přístup v nastavení iOS.";
/* Alert title for when the user has just tried to edit a contacts after declining to give Signal contacts permissions */
"EDIT_CONTACT_WITHOUT_CONTACTS_PERMISSION_ALERT_TITLE" = "Aplikace Signal pro úpravu informací kontaktu vyžaduje přístup ke kontaktům.";
"EDIT_CONTACT_WITHOUT_CONTACTS_PERMISSION_ALERT_TITLE" = "Signal pro úpravu informací o kontaktu vyžaduje přístup ke kontaktům.";
/* table cell label in conversation settings */
"EDIT_GROUP_ACTION" = "Upravit skupinu";
@ -799,7 +799,7 @@
"EDIT_GROUP_UPDATE_BUTTON" = "Aktualizovat";
/* The alert message if user tries to exit update group view without saving changes. */
"EDIT_GROUP_VIEW_UNSAVED_CHANGES_MESSAGE" = "Chcete uložit změny vykonané na této skupině?";
"EDIT_GROUP_VIEW_UNSAVED_CHANGES_MESSAGE" = "Změnil(a) jste tuto skupinu. Chcete tyto změny uložit?";
/* The alert title if user tries to exit update group view without saving changes. */
"EDIT_GROUP_VIEW_UNSAVED_CHANGES_TITLE" = "Neuložené změny";
@ -823,7 +823,7 @@
"EMPTY_ARCHIVE_TITLE" = "Vyčistit váš seznam konverzací.";
/* Full width label displayed when attempting to compose message */
"EMPTY_CONTACTS_LABEL_LINE1" = "Žádný z vašich kontaktů nemá aplikaci Signal.";
"EMPTY_CONTACTS_LABEL_LINE1" = "Žádný z vašich kontaktů nemá Signal.";
/* Full width label displayed when attempting to compose message */
"EMPTY_CONTACTS_LABEL_LINE2" = "Proč někoho nepozvete?";
@ -832,13 +832,13 @@
"EMPTY_INBOX_NEW_USER_TEXT" = "Klepněte na tlačítko vytvořit.";
/* Header text a new user sees when viewing an empty inbox */
"EMPTY_INBOX_NEW_USER_TITLE" = "Zahajte vaši první konverzaci Signal!";
"EMPTY_INBOX_NEW_USER_TITLE" = "Začněte vaši první konverzaci na Signalu!";
/* Body text an existing user sees when viewing an empty inbox */
"EMPTY_INBOX_TEXT" = "Nula nula nic.";
/* Header text an existing user sees when viewing an empty inbox */
"EMPTY_INBOX_TITLE" = "Schránka prázdná, nikde nic.";
"EMPTY_INBOX_TITLE" = "Schránka prázdná, velký kulový.";
/* Indicates that user should confirm their 'two factor auth pin'. */
"ENABLE_2FA_VIEW_CONFIRM_PIN_INSTRUCTIONS" = "Potvrďte Váš PIN.";
@ -862,13 +862,13 @@
"ENABLE_2FA_VIEW_PIN_DOES_NOT_MATCH" = "Zadaný PIN nesouhlasí.";
/* Indicates that user should select a 'two factor auth pin'. */
"ENABLE_2FA_VIEW_SELECT_PIN_INSTRUCTIONS" = "Zadejte PIN registrace. Při změně přístroje s tímto telefonním číslem budete na tento PIN dotázán.";
"ENABLE_2FA_VIEW_SELECT_PIN_INSTRUCTIONS" = "Zadejte PIN zámku registrace. Při příští registraci pod tímto telefonním číslem budete na PIN dotázán(a).";
/* Indicates that user has 'two factor auth pin' disabled. */
"ENABLE_2FA_VIEW_STATUS_DISABLED_INSTRUCTIONS" = "Pro zvýšení bezpečnosti zapněte registrační PIN, který bude vyžadován při změně přístroje s tímto telefonním číslem v aplikaci Signal.";
"ENABLE_2FA_VIEW_STATUS_DISABLED_INSTRUCTIONS" = "Pro zvýšení bezpečnosti povolte PIN zámku registrace, který bude v aplikaci Signal vyžadován při příští registraci pod tímto telefonním číslem.";
/* Indicates that user has 'two factor auth pin' enabled. */
"ENABLE_2FA_VIEW_STATUS_ENABLED_INSTRUCTIONS" = "Registrační zámek je povolen. Budete požádán(a) o zadání PINu při změně přístroje s tímto telefonním číslem v aplikaci Signal.";
"ENABLE_2FA_VIEW_STATUS_ENABLED_INSTRUCTIONS" = "Zámek registrace je povolen. Budete požádán(a) o zadání PINu při příští registraci pod tímto telefonním číslem.";
/* Title for the 'enable two factor auth PIN' views. */
"ENABLE_2FA_VIEW_TITLE" = "Zámek registrace";
@ -880,7 +880,7 @@
"END_CALL_UNCATEGORIZED_FAILURE" = "Hovor selhal.";
/* Error indicating that the phone's contacts could not be retrieved. */
"ERROR_COULD_NOT_FETCH_CONTACTS" = "Nelze přistoupit ke kontaktům";
"ERROR_COULD_NOT_FETCH_CONTACTS" = "Nelze přistoupit ke kontaktům.";
/* Generic notice when message failed to send. */
"ERROR_DESCRIPTION_CLIENT_SENDING_FAILURE" = "Nepodařilo se odeslat zprávu.";
@ -898,7 +898,7 @@
"ERROR_DESCRIPTION_NO_INTERNET" = "Signal se nemohl připojit k internetu. Prosím zkuste to znovu.";
/* Error indicating that an outgoing message had no valid recipients. */
"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS" = "Poslání zprávy selhalo z důvodu platných příjemců.";
"ERROR_DESCRIPTION_NO_VALID_RECIPIENTS" = "Odesílání zprávy selhalo z důvodu neplatných příjemců.";
/* Error indicating that a socket request failed. */
"ERROR_DESCRIPTION_REQUEST_FAILED" = "Síťová žádost selhala.";
@ -919,10 +919,10 @@
"ERROR_DESCRIPTION_UNKNOWN_ERROR" = "Došlo k neznámé chybě.";
/* Error message when attempting to send message */
"ERROR_DESCRIPTION_UNREGISTERED_RECIPIENT" = "Kontakt není uživatelem aplikace Signal.";
"ERROR_DESCRIPTION_UNREGISTERED_RECIPIENT" = "Kontakt není uživatelem Signalu.";
/* Error message when unable to receive an attachment because the sending client is too old. */
"ERROR_MESSAGE_ATTACHMENT_FROM_OLD_CLIENT" = "Chyba přílohy: Odesílatel musí aktualizovat aplikaci Signal a odeslat zprávu znovu.";
"ERROR_MESSAGE_ATTACHMENT_FROM_OLD_CLIENT" = "Chyba přílohy: Požádejte odesílatele, aby aktualizoval(a) Signal a odeslal(a) zprávu znovu.";
/* No comment provided by engineer. */
"ERROR_MESSAGE_DUPLICATE_MESSAGE" = "Přijata duplikovaná zpráva.";
@ -1084,13 +1084,13 @@
"IMAGE_EDITOR_BRUSH_BUTTON" = "Brush";
/* Label for crop button in image editor. */
"IMAGE_EDITOR_CROP_BUTTON" = "Crop";
"IMAGE_EDITOR_CROP_BUTTON" = "Oříznout";
/* Momentarily shown to the user when attempting to select more images than is allowed. Embeds {{max number of items}} that can be shared. */
"IMAGE_PICKER_CAN_SELECT_NO_MORE_TOAST_FORMAT" = "You can't share more than %@ items.";
"IMAGE_PICKER_CAN_SELECT_NO_MORE_TOAST_FORMAT" = "Nemůžete sdílet více než %@ položek.";
/* alert title */
"IMAGE_PICKER_FAILED_TO_PROCESS_ATTACHMENTS" = "Failed to select attachment.";
"IMAGE_PICKER_FAILED_TO_PROCESS_ATTACHMENTS" = "Nepodařilo se odeslat přílohu.";
/* Call setup status label */
"IN_CALL_CONNECTING" = "Připojování…";
@ -1135,7 +1135,7 @@
"INVITE_FLOW_REQUIRES_CONTACT_ACCESS_TITLE" = "Povolit přístup ke kontaktům";
/* Label for the cell that presents the 'invite contacts' workflow. */
"INVITE_FRIENDS_CONTACT_TABLE_BUTTON" = "Pozvat přátele do aplikace Signal";
"INVITE_FRIENDS_CONTACT_TABLE_BUTTON" = "Pozvat přátele do Signalu";
/* Search */
"INVITE_FRIENDS_PICKER_SEARCHBAR_PLACEHOLDER" = "Hledat";
@ -1162,7 +1162,7 @@
"LINK_DEVICE_INVALID_CODE_BODY" = "Tento QR kód je neplatný. Ujistěte se prosím, že snímáte QR kód ze zařízení, které chcete připojit.";
/* report an invalid linking code */
"LINK_DEVICE_INVALID_CODE_TITLE" = "Propojení zařízení selhalo";
"LINK_DEVICE_INVALID_CODE_TITLE" = "Připojení zařízení selhalo";
/* confirm the users intent to link a new device */
"LINK_DEVICE_PERMISSION_ALERT_BODY" = "Toto zařízení uvidí vaše skupiny a kontakty, bude mít přístup k vaším konverzacím a bude moct posílat zprávy ve vašem jménu.";
@ -1174,7 +1174,7 @@
"LINK_DEVICE_RESTART" = "Zkusit znovu";
/* QR Scanning screen instructions, placed alongside a camera view for scanning QR Codes */
"LINK_DEVICE_SCANNING_INSTRUCTIONS" = "Pro připojení naskenujte QR kód zobrazený na zařízení, které chcete připojit.";
"LINK_DEVICE_SCANNING_INSTRUCTIONS" = "Naskenujte QR kód zobrazený na zařízení, které chcete připojit.";
/* Subheading for 'Link New Device' navigation */
"LINK_NEW_DEVICE_SUBTITLE" = "Naskenovat QR kód";
@ -1183,13 +1183,13 @@
"LINK_NEW_DEVICE_TITLE" = "Připojit nové zařízení";
/* Label for link previews with an unknown host. */
"LINK_PREVIEW_UNKNOWN_DOMAIN" = "Link Preview";
"LINK_PREVIEW_UNKNOWN_DOMAIN" = "Náhled odkazu";
/* Menu item and navbar title for the device manager */
"LINKED_DEVICES_TITLE" = "Připojená zařízení";
/* Alert Title */
"LINKING_DEVICE_FAILED_TITLE" = "Propojení zařízení selhalo";
"LINKING_DEVICE_FAILED_TITLE" = "Připojení zařízení selhalo";
/* table cell label in conversation settings */
"LIST_GROUP_MEMBERS_ACTION" = "Členové skupiny";
@ -1240,7 +1240,7 @@
"MESSAGE_ACTION_DELETE_MESSAGE" = "Smazat tuto zprávu";
/* Action sheet button subtitle */
"MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE" = "Bude pouze smazána na tomto zařízení.";
"MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE" = "Bude smazána pouze na tomto zařízení.";
/* Action sheet button title */
"MESSAGE_ACTION_DETAILS" = "Více informací";
@ -1380,7 +1380,7 @@
/* Alert title
Alert title when camera is not authorized */
"MISSING_CAMERA_PERMISSION_TITLE" = "Aplikace Signal vyžaduje přístup k vašemu fotoaparátu.";
"MISSING_CAMERA_PERMISSION_TITLE" = "Signal vyžaduje přístup k vašemu fotoaparátu.";
/* Alert body when user has previously denied media library access */
"MISSING_MEDIA_LIBRARY_PERMISSION_MESSAGE" = "Tento souhlas můžete udělit v nastavení iOS.";
@ -1389,10 +1389,10 @@
"MISSING_MEDIA_LIBRARY_PERMISSION_TITLE" = "Tato funkce Signalu vyžaduje přístup k fotografiím.";
/* alert title: cannot link - reached max linked devices */
"MULTIDEVICE_PAIRING_MAX_DESC" = "Nemůžete propojit další zařízení.";
"MULTIDEVICE_PAIRING_MAX_DESC" = "Nemůžete připojit žádné další zařízení.";
/* alert body: cannot link - reached max linked devices */
"MULTIDEVICE_PAIRING_MAX_RECOVERY" = "Dosáhl(a) jste limitu počtu zařízení, které můžete připojit k vašemu účtu. Prosím odstraňte některé ze zařízení a zkuste to znovu.";
"MULTIDEVICE_PAIRING_MAX_RECOVERY" = "Dosáhl(a) jste maximálního počtu zařízení, které můžete připojit k vašemu účtu. Prosím odstraňte některé ze zařízení a zkuste to znovu.";
/* An explanation of the consequences of muting a thread. */
"MUTE_BEHAVIOR_EXPLANATION" = "Pro ztlumené konverzace nebudete dostávat oznámení.";
@ -1440,10 +1440,10 @@
"NEW_GROUP_MEMBER_LABEL" = "Člen";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ v %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Pojmenujte tento skupinový chat";
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Pojmenujte tuto skupinu";
/* a title for the non-contacts section of the 'new group' view. */
"NEW_GROUP_NON_CONTACTS_SECTION_TITLE" = "Ostatní uživatelé";
@ -1467,7 +1467,7 @@
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Najít kontakty pomocí telefonního čísla";
/* Label for 1:1 conversation with yourself. */
"NOTE_TO_SELF" = "Note to Self";
"NOTE_TO_SELF" = "Poznámka sobě";
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "Je možné, že byly přijaty nové zprávy zatímco váš %@ byl restartován.";
@ -1500,13 +1500,13 @@
"OPEN_SETTINGS_BUTTON" = "Nastavení";
/* Info Message when {{other user}} disables or doesn't support disappearing messages */
"OTHER_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION" = "%@ zakázal mizející zprávy.";
"OTHER_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION" = "%@ zakázal(a) mizející zprávy.";
/* Info Message when {{other user}} updates message expiration to {{time amount}}, see the *_TIME_AMOUNT strings for context. */
"OTHER_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION" = "%@ nastavil čas mizejících zpráv na %@.";
"OTHER_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION" = "%@ nastavil(a) čas mizejících zpráv na %@.";
/* Label warning the user that the Signal service may be down. */
"OUTAGE_WARNING" = "Aplikace Signal se potýká s technickými problémy. Úsilovně pracujeme na co nejrychlejším obnovení služby.";
"OUTAGE_WARNING" = "Signal se potýká s technickými problémy. Usilovně pracujeme na co nejrychlejším obnovení služby.";
/* info message text in conversation view */
"OUTGOING_CALL" = "Odchozí hovor";
@ -1590,22 +1590,22 @@
"PRIVACY_VERIFICATION_FAILED_MISMATCHED_SAFETY_NUMBERS_IN_CLIPBOARD" = "Číslo ve vaší schránce nevypadá jako správné bezpečnostní číslo této konverzace.";
/* Alert body for user error */
"PRIVACY_VERIFICATION_FAILED_NO_SAFETY_NUMBERS_IN_CLIPBOARD" = "Aplikace Signal nemohla ve vaší schránce nalézt žádné bezpečnostní číslo. Zkopírovali jste ho správně?";
"PRIVACY_VERIFICATION_FAILED_NO_SAFETY_NUMBERS_IN_CLIPBOARD" = "Signal nemohl ve vaší schránce nalézt žádné bezpečnostní číslo. Zkopíroval(a) jste ho správně?";
/* Alert body when verifying with {{contact name}} */
"PRIVACY_VERIFICATION_FAILED_THEY_HAVE_WRONG_KEY_FOR_ME" = "Každá dvojice uživatelů Signalu sdílí odlišné bezpečnostní číslo. Ověřte, že %@ zobrazuje *vaše* bezpečnostní číslo.";
/* alert body */
"PRIVACY_VERIFICATION_FAILED_WITH_OLD_LOCAL_VERSION" = "Používáte starou verzi aplikace Signal, před ověřením ji musíte aktualizovat.";
"PRIVACY_VERIFICATION_FAILED_WITH_OLD_LOCAL_VERSION" = "Používáte starou verzi Signalu, před ověřením ji musíte aktualizovat.";
/* alert body */
"PRIVACY_VERIFICATION_FAILED_WITH_OLD_REMOTE_VERSION" = "Váš kontakt používá starou verzi Signalu a musí ji aktualizovat, než bude možné ověření.";
/* alert body */
"PRIVACY_VERIFICATION_FAILURE_INVALID_QRCODE" = "Naskenovaný kód nevypadá jako bezpečnostní číslo. Používate oba aktuální verzi aplikace Signal?";
"PRIVACY_VERIFICATION_FAILURE_INVALID_QRCODE" = "Naskenovaný kód nevypadá jako bezpečnostní číslo. Používate oba aktuální verzi Signalu?";
/* Paragraph(s) shown alongside the safety number when verifying privacy with {{contact name}} */
"PRIVACY_VERIFICATION_INSTRUCTIONS" = "Pokud chcete ověřit bezpečnost koncového šifrování s %@, porovnete čísla výše s čísli na druhém zařízení..\n\nNebo můžete naskenovat kód z druhého telefonu, nebo on může naskenovat ten váš.";
"PRIVACY_VERIFICATION_INSTRUCTIONS" = "Pokud chcete ověřit bezpečnost koncového šifrování s %@, porovnete čísla výše s čísli na druhém zařízení.\n\nTaké můžete naskenovat kód z jejich telefonu, nebo je požádat o naskenování vašeho kódu.";
/* Navbar title */
"PRIVACY_VERIFICATION_TITLE" = "Ověření bezpečnostního čísla";
@ -1671,7 +1671,7 @@
"QUESTIONMARK_PUNCTUATION" = "?";
/* Indicates the author of a quoted message. Embeds {{the author's name or phone number}}. */
"QUOTED_REPLY_AUTHOR_INDICATOR_FORMAT" = "Odpověď na %@";
"QUOTED_REPLY_AUTHOR_INDICATOR_FORMAT" = "Odpověď uživateli %@";
/* message header label when someone else is quoting you */
"QUOTED_REPLY_AUTHOR_INDICATOR_YOU" = "Odpověď vám";
@ -1707,10 +1707,10 @@
"REGISTER_2FA_FORGOT_PIN" = "Zapoměl jsem PIN.";
/* Alert message explaining what happens if you forget your 'two-factor auth pin'. */
"REGISTER_2FA_FORGOT_PIN_ALERT_MESSAGE" = "Registrace tohoto telefonního čísla bude možná bez zámku registrace po uplynutí 7 dní po poslední aktivitě čísla v aplikaci Signal.";
"REGISTER_2FA_FORGOT_PIN_ALERT_MESSAGE" = "Registrace tohoto telefonního čísla bude možná bez PINu zámku registrace po uplynutí 7 dní od poslední aktivity telefonního čísla na Signalu.";
/* Instructions to enter the 'two-factor auth pin' in the 2FA registration view. */
"REGISTER_2FA_INSTRUCTIONS" = "Toto telefonní číslo má povolený zámek registrace. Prosím zadejte registrační PIN.\n\nVáš registrační PIN se liší od automatického kódu, který byl zadán na Váš přístroj při posledním kroku.";
"REGISTER_2FA_INSTRUCTIONS" = "Toto telefonní číslo má povolený zámek registrace. Prosím zadejte PIN zámku registrace.\n\nPIN zámku registrace se liší od ověřovacího kódu, který vám byl odeslán při předchozím kroku.";
/* Title for alert indicating that attempt to register with 'two-factor auth' failed. */
"REGISTER_2FA_REGISTRATION_FAILED_ALERT_TITLE" = "Registrace neúspěšná";
@ -1785,7 +1785,7 @@
"REGISTRATION_VERIFICATION_FAILED_WRONG_CODE_DESCRIPTION" = "Čísla, která jste zadal(a) neodpovídají tomu, co jsme poslali. Nechcete to zkontrolovat?";
/* Error message indicating that registration failed due to a missing or incorrect 2FA PIN. */
"REGISTRATION_VERIFICATION_FAILED_WRONG_PIN" = "Neplatný registrační PIN.";
"REGISTRATION_VERIFICATION_FAILED_WRONG_PIN" = "Neplatný PIN zámku registrace.";
/* No comment provided by engineer. */
"REGISTRATION_VERIFY_DEVICE" = "Zaregistrovat se";
@ -1809,16 +1809,16 @@
"RELAY_REGISTERED_ERROR_RECOVERY" = "Telefonní číslo, které se snažíte zaregistrovat, je již zaregistrováno na jiném serveru. Zrušte jeho registraci z tohoto serveru a zkuste to znovu.";
/* Body text for when user is periodically prompted to enter their registration lock PIN */
"REMINDER_2FA_BODY" = "Registrační zámek je na Vašem telefonním čísle povolen. Aby Vám pomohla zapamatovat si kód, Vás bude aplikace Signal pravidelně žádat o jeho zadávání.";
"REMINDER_2FA_BODY" = "Registrační zámek je povolen pro vaše telefonní číslo. Signal vás bude pravidelně žádat o zadání kódu, aby vám pomohl si ho zapamatovat.";
/* Body header for when user is periodically prompted to enter their registration lock PIN */
"REMINDER_2FA_BODY_HEADER" = "Připomenutí:";
/* Alert message explaining what happens if you forget your 'two-factor auth pin' */
"REMINDER_2FA_FORGOT_PIN_ALERT_MESSAGE" = "Registračná zámek pomáhá zabezpečit Vaše telefonní číslo před neautorizovanými pokusy o registraci. Tato funkce může být kdykoliv vypnuta v nastavení soukromí v aplikaci Signal.";
"REMINDER_2FA_FORGOT_PIN_ALERT_MESSAGE" = "Zámek registrace pomáhá zabezpečit vaše telefonní číslo před neoprávněnými pokusy o registraci. Tato funkce může být kdykoliv zakázána v nastavení soukromí Signalu.";
/* Navbar title for when user is periodically prompted to enter their registration lock PIN */
"REMINDER_2FA_NAV_TITLE" = "Zadejte Váš registrační PIN.";
"REMINDER_2FA_NAV_TITLE" = "Zadejte váš PIN zámku registrace";
/* Alert body after wrong guess for 'two-factor auth pin' reminder activity */
"REMINDER_2FA_WRONG_PIN_ALERT_BODY" = "Nový PIN můžete nastavit v nastavení soukromí.";
@ -1842,7 +1842,7 @@
"SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION" = "Přesto odeslat";
/* Snippet to share {{safety number}} with a friend. sent e.g. via SMS */
"SAFETY_NUMBER_SHARE_FORMAT" = "Naše bezpečnostní číslo v aplikaci Signal:\n%@";
"SAFETY_NUMBER_SHARE_FORMAT" = "Naše bezpečnostní číslo v Signalu:\n%@";
/* Action sheet heading */
"SAFETY_NUMBERS_ACTIONSHEET_TITLE" = "Vaše bezpečnostní číslo s %@ se změnilo. Možná ho chcete ověřit.";
@ -1866,13 +1866,13 @@
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Příliš mnoho pokusů o autentifikaci. Prosím zkuste později.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Musíte nejdříve povolit zámek v iOS aplikaci Nastavení, před povolením aplikace zámek displeje.";
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Pokud chcete používat zámek displeje, nejprve zapněte kódový zámek v nastavení iOS.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Musíte nejdříve povolit zámek v iOS aplikaci Nastavení, před povolením aplikace zámek displeje.";
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Pokud chcete používat zámek displeje, nejprve zapněte kódový zámek v nastavení iOS.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Musíte nejdříve povolit zámek v iOS aplikaci Nastavení, před povolením aplikace zámek displeje.";
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Pokud chcete používat zámek displeje, nejprve zapněte kódový zámek v nastavení iOS.";
/* Description of how and why Signal iOS uses Touch ID/Face ID/Phone Passcode to unlock 'screen lock'. */
"SCREEN_LOCK_REASON_UNLOCK_SCREEN_LOCK" = "Autentifikovat pro otevření Signalu.";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Zaslat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Vaše zpráva nemohla být odeslána.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Odeslání pozvánky selhalo, zkuste to později.";
@ -1929,7 +1929,7 @@
"SEND_SMS_CONFIRM_TITLE" = "Pozvat kamaráda přes zabezpečenou SMS?";
/* No comment provided by engineer. */
"SEND_SMS_INVITE_TITLE" = "Chcete pozvat toto číslo do aplikace Signal:";
"SEND_SMS_INVITE_TITLE" = "Chcete pozvat následující číslo do Signalu:";
/* Navbar title */
"SETTINGS_ABOUT" = "O aplikaci";
@ -2028,7 +2028,7 @@
"SETTINGS_BLOCK_LIST_ADD_BUTTON" = "Přidat zablokovaného uživatele";
/* A label that indicates the user has no Signal contacts. */
"SETTINGS_BLOCK_LIST_NO_CONTACTS" = "V aplikaci Signal nemáte žádné kontakty.";
"SETTINGS_BLOCK_LIST_NO_CONTACTS" = "Nemáte žádné kontakty v Signalu.";
/* A label that indicates the user's search has no matching results. */
"SETTINGS_BLOCK_LIST_NO_SEARCH_RESULTS" = "Žádné výsledky vyhledávání";
@ -2037,7 +2037,7 @@
"SETTINGS_BLOCK_LIST_TITLE" = "Blokovaní";
/* Table cell label */
"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE" = "Vždy přeposílat hovory";
"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE" = "Vždy přenášet hovory";
/* User settings section footer, a detailed explanation */
"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE_DETAIL" = "Přenášet všechna volání přes server Signal, aby nedošlo k odhalení vaší IP adresy volanému. Povolením dojde ke zhoršení kvality hovoru.";
@ -2055,7 +2055,7 @@
"SETTINGS_DELETE_DATA_BUTTON" = "Smazat veškerá data";
/* Alert message before user confirms clearing history */
"SETTINGS_DELETE_HISTORYLOG_CONFIRMATION" = "Opravdu chcete smazat celou historii (zprávy, přílohy, volání atd.)? Tuto akci nelze vzít zpět.";
"SETTINGS_DELETE_HISTORYLOG_CONFIRMATION" = "Opravdu chcete smazat veškerou historii (zprávy, přílohy, volání atd.)? Tuto akci nelze vzít zpět.";
/* Confirmation text for button which deletes all message, calling, attachments, etc. */
"SETTINGS_DELETE_HISTORYLOG_CONFIRMATION_BUTTON" = "Smazat vše";
@ -2082,13 +2082,13 @@
"SETTINGS_LEGAL_TERMS_CELL" = "Podmínky & Zásady ochrany osobních údajů";
/* Setting for enabling & disabling link previews. */
"SETTINGS_LINK_PREVIEWS" = "Odeslat náhledy odkazu";
"SETTINGS_LINK_PREVIEWS" = "Odeslat náhledy odkazů";
/* Footer for setting for enabling & disabling link previews. */
"SETTINGS_LINK_PREVIEWS_FOOTER" = "Náhledy jsou podporovány pro odkazy na Imgur, Instagram, Reddit, a YouTube.";
/* Header for setting for enabling & disabling link previews. */
"SETTINGS_LINK_PREVIEWS_HEADER" = "Link Previews";
"SETTINGS_LINK_PREVIEWS_HEADER" = "Náhledy odkazů";
/* Title for settings activity */
"SETTINGS_NAV_BAR_TITLE" = "Nastavení";
@ -2106,7 +2106,7 @@
"SETTINGS_PRIVACY_CALLKIT_PRIVACY_TITLE" = "Zobrazit jméno & číslo volajícího";
/* Settings table section footer. */
"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_DESCRIPTION" = "Zobrazit hovory v \"Poslední volané\" v aplikaci Telefon";
"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_DESCRIPTION" = "Zobrazit hovory v historii v aplikaci Telefon.";
/* Short table cell label */
"SETTINGS_PRIVACY_CALLKIT_SYSTEM_CALL_LOG_PREFERENCE_TITLE" = "Zobrazit hovory v \"Poslední volané\"";
@ -2217,13 +2217,13 @@
"SHARE_EXTENSION_LOADING" = "Načítání...";
/* Message indicating that the share extension cannot be used until the user has registered in the main app. */
"SHARE_EXTENSION_NOT_REGISTERED_MESSAGE" = "Spustit aplikaci Signal pro registraci.";
"SHARE_EXTENSION_NOT_REGISTERED_MESSAGE" = "Otevřete Signal pro registraci.";
/* Title indicating that the share extension cannot be used until the user has registered in the main app. */
"SHARE_EXTENSION_NOT_REGISTERED_TITLE" = "Není registrováno";
/* Message indicating that the share extension cannot be used until the main app has been launched at least once. */
"SHARE_EXTENSION_NOT_YET_MIGRATED_MESSAGE" = "Spustit aplikaci Signal pro registraci nebo aktualizaci.";
"SHARE_EXTENSION_NOT_YET_MIGRATED_MESSAGE" = "Otevřete Signal pro registraci nebo aktualizaci.";
/* Title indicating that the share extension cannot be used until the main app has been launched at least once. */
"SHARE_EXTENSION_NOT_YET_MIGRATED_TITLE" = "Nepřipraveno";
@ -2247,7 +2247,7 @@
"SHOW_THREAD_BUTTON_TITLE" = "Zobrazit konverzaci";
/* body sent to contacts when inviting to Install Signal */
"SMS_INVITE_BODY" = "Pozval jsem tě do aplikace Signal! Zde je odkaz:";
"SMS_INVITE_BODY" = "Zvu tě do aplikace Signal! Tady je odkaz:";
/* Label for the 'no sound' option that allows users to disable sounds for notifications, etc. */
"SOUNDS_NONE" = "Žádné";
@ -2328,13 +2328,13 @@
"UNLINK_ACTION" = "Odpojit";
/* Alert message to confirm unlinking a device */
"UNLINK_CONFIRMATION_ALERT_BODY" = "Pokud bude toto zařízení odpojeno, nebude možné dále odesílat a přijímat zprávy.";
"UNLINK_CONFIRMATION_ALERT_BODY" = "Pokud bude zařízení odpojeno, nebude možné dále odesílat nebo přijímat zprávy.";
/* Alert title for confirming device deletion */
"UNLINK_CONFIRMATION_ALERT_TITLE" = "Odpojit \"%@\"?";
/* Alert title when unlinking device fails */
"UNLINKING_FAILED_ALERT_TITLE" = "Aplikaci Signal se nepodařilo odpojit vaše zařízení.";
"UNLINKING_FAILED_ALERT_TITLE" = "Signalu se nepodařilo odpojit vaše zařízení.";
/* Label text in device manager for a device with no name */
"UNNAMED_DEVICE" = "Nepojmenované zařízení";
@ -2367,13 +2367,13 @@
"UPGRADE_EXPERIENCE_ENABLE_TYPING_INDICATOR_BUTTON" = "Zapnout indikátory psaní";
/* Body text for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Volitelné náhledy odkazu jsou nyní podporovány na některých populárních internetových stránkách.";
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Volitelné náhledy odkazů jsou nyní podporovány na některých populárních internetových stránkách.";
/* Subtitle for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_SUBTITLE" = "You can disable or enable this feature anytime in your Signal settings (Privacy > Send link previews).";
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_SUBTITLE" = "Tuto funkci můžete kdykoliv zakázat nebo povolit v nastavení soukromí Signalu (Soukromí > Odeslat náhledy odkazů).";
/* Header for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_TITLE" = "Link Previews";
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_TITLE" = "Náhledy odkazů";
/* Description for notification audio customization */
"UPGRADE_EXPERIENCE_INTRODUCING_NOTIFICATION_AUDIO_DESCRIPTION" = "Nyní můžete zvolit výchozí zvuky chatů, či zvuky pro každou konverzaci zvlášť. Zvuky u hovorů budou stejné, jako byly zvoleny u každého systémového kontaktu zvlášť.";
@ -2388,7 +2388,7 @@
"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_BUTTON" = "Nastavit váš profil";
/* Description of new profile feature for upgrading (existing) users */
"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_DESCRIPTION" = "Nyní můžete s vašimi přáteli v aplikaci Signal sdílet profilovou fotku a jméno.";
"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_DESCRIPTION" = "Nyní můžete s vašimi přáteli na Signalu sdílet profilovou fotku a jméno.";
/* Header for upgrade experience */
"UPGRADE_EXPERIENCE_INTRODUCING_PROFILES_TITLE" = "Připraveni?";
@ -2409,7 +2409,7 @@
"UPGRADE_EXPERIENCE_INTRODUCING_TYPING_INDICATORS_TITLE" = "Představujeme indikátory psaní";
/* Description of video calling to upgrading (existing) users */
"UPGRADE_EXPERIENCE_VIDEO_DESCRIPTION" = "Aplikace Signal nyní podporuje bezpečné video hovory. Začněte hovor jako obvykle, klepněte na tlačítko kamery a zamávejte na pozdrav.";
"UPGRADE_EXPERIENCE_VIDEO_DESCRIPTION" = "Signal nyní podporuje bezpečné video hovory. Začněte hovor jako obvykle, klepněte na tlačítko kamery a zamávejte na pozdrav.";
/* Header for upgrade experience */
"UPGRADE_EXPERIENCE_VIDEO_TITLE" = "Ahoj, bezpečné videohovory!";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Adgang til mikrofonen er påkrævet";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Indkommende opkald";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Ring op";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Ubesvaret opkald, fordi opkalderens sikkerhedsnummer er ændret.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Ubesvaret opkald";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Intet svar";
@ -1395,7 +1395,7 @@
"MULTIDEVICE_PAIRING_MAX_RECOVERY" = "Du har nået det maksimale antal enheder, du i øjeblikket kan tilkoble til din konto. Fjern en enhed, og prøv igen.";
/* An explanation of the consequences of muting a thread. */
"MUTE_BEHAVIOR_EXPLANATION" = "Du modtager ikke underretninger for dæmpede samtaler.";
"MUTE_BEHAVIOR_EXPLANATION" = "Du modtager ikke meddelelser for dæmpede samtaler.";
/* A button to skip a view. */
"NAVIGATION_ITEM_SKIP_BUTTON" = "Spring over";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Medlem";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ til %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Navngiv gruppesamtalen";
@ -1662,7 +1662,7 @@
"PUSH_MANAGER_REPLY" = "Svar";
/* Title of alert shown when push tokens sync job succeeds. */
"PUSH_REGISTER_SUCCESS" = "Genregistreret til push-notifikationer.";
"PUSH_REGISTER_SUCCESS" = "Genregistreret til push-meddelelser.";
/* Used in table section header and alert view title contexts */
"PUSH_REGISTER_TITLE" = "Push meddelelser";
@ -1731,7 +1731,7 @@
"REGISTER_RATE_LIMITING_ERROR" = "Du har forsøgt for ofte. Vent venligst et minut.";
/* Title of alert shown when push tokens sync job fails. */
"REGISTRATION_BODY" = "Kunne ikke genregistrere til push-notifikationer.";
"REGISTRATION_BODY" = "Kunne ikke genregistrere til push-meddelelser.";
/* Label for the country code field */
"REGISTRATION_DEFAULT_COUNTRY_NAME" = "Landekode";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Send";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Din besked kunne ikke sendes.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Kunne ikke sende invitation. Prøv igen senere.";
@ -2094,10 +2094,10 @@
"SETTINGS_NAV_BAR_TITLE" = "Indstillinger";
/* table section footer */
"SETTINGS_NOTIFICATION_CONTENT_DESCRIPTION" = "Opkalds- og meddelelsesnotifikationer kan vises, mens telefonen er låst. Du vil muligvis begrænse, hvad der vises i disse notifikationer.";
"SETTINGS_NOTIFICATION_CONTENT_DESCRIPTION" = "Opkalds- og besked-meddelelser kan vises, mens telefonen er låst. Du vil muligvis begrænse, hvad der vises i disse meddelelser.";
/* table section header */
"SETTINGS_NOTIFICATION_CONTENT_TITLE" = "Notifikations Indhold ";
"SETTINGS_NOTIFICATION_CONTENT_TITLE" = "Meddelelses indhold";
/* No comment provided by engineer. */
"SETTINGS_NOTIFICATIONS" = "Meddelelser";
@ -2130,7 +2130,7 @@
"SETTINGS_SCREEN_LOCK_ACTIVITY_TIMEOUT" = "Skærmlås timeout";
/* Footer for the 'screen lock' section of the privacy settings. */
"SETTINGS_SCREEN_LOCK_SECTION_FOOTER" = "Lås op Signals skærmen ved hjælp af Touch ID, Face ID eller din iOS-enhedens adgangskode. Du kan stadig svare på indgående opkald og modtage meddelelsesnotifikationer, mens Skærmlås er aktiveret. Signal's underretningsindstillinger giver dig mulighed for at tilpasse de oplysninger, der vises.";
"SETTINGS_SCREEN_LOCK_SECTION_FOOTER" = "Lås op Signals skærm ved hjælp af Touch ID, Face ID eller din iOS-enhedens adgangskode. Du kan stadig svare på indgående opkald og modtage besked-meddelelser, mens Skærmlås er aktiveret. Signals meddelelsesindstillinger giver dig mulighed for at tilpasse de oplysninger, der vises.";
/* Title for the 'screen lock' section of the privacy settings. */
"SETTINGS_SCREEN_LOCK_SECTION_TITLE" = "Skærmlås";
@ -2148,7 +2148,7 @@
"SETTINGS_SECTION_CALL_KIT_DESCRIPTION" = "iOS opkaldsintegration viser Signal-opkald på din låseskærm og i systemets opkaldshistorik. Du kan eventuelt vise din kontaktpersons navn og nummer. Hvis iCloud er aktiveret, deles denne opkaldshistorik med Apple.";
/* Label for the notifications section of conversation settings view. */
"SETTINGS_SECTION_NOTIFICATIONS" = "Notifikationer";
"SETTINGS_SECTION_NOTIFICATIONS" = "Meddelelser";
/* Header Label for the sounds section of settings views. */
"SETTINGS_SECTION_SOUNDS" = "Lyde";
@ -2379,7 +2379,7 @@
"UPGRADE_EXPERIENCE_INTRODUCING_NOTIFICATION_AUDIO_DESCRIPTION" = "Du kan nu vælge standard og samtale-specifikke meddelelseslyde, og opkald vil respektere den ringetone, du har valgt for hver systemkontakt.";
/* button label shown one time, after upgrade */
"UPGRADE_EXPERIENCE_INTRODUCING_NOTIFICATION_AUDIO_SETTINGS_BUTTON" = "Gennemse notifikationsindstillinger";
"UPGRADE_EXPERIENCE_INTRODUCING_NOTIFICATION_AUDIO_SETTINGS_BUTTON" = "Gennemse meddelelsesindstillinger";
/* Header for upgrade experience */
"UPGRADE_EXPERIENCE_INTRODUCING_NOTIFICATION_AUDIO_TITLE" = "Opdater opkalds- og beskedlyde";

View File

@ -2367,7 +2367,7 @@
"UPGRADE_EXPERIENCE_ENABLE_TYPING_INDICATOR_BUTTON" = "Ενεργοποίηση δεικτών πληκτρολόγησης";
/* Body text for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Optional link previews are now supported for some of the most popular sites on the Internet.";
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Οι προεπισκοπήσεις συνδέσμων υποστηρίζονται πλέον προαιρετικά για μερικές από τις γνωστότερες ιστοσελίδες του διαδικτύου. ";
/* Subtitle for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_SUBTITLE" = "You can disable or enable this feature anytime in your Signal settings (Privacy > Send link previews).";

View File

@ -1467,7 +1467,7 @@
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Encontrar por núm. de teléfono";
/* Label for 1:1 conversation with yourself. */
"NOTE_TO_SELF" = "Tú mismo";
"NOTE_TO_SELF" = "Mensajes a ti mism@";
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "Has recibido mensajes mientras tu %@ estaba reiniciándose.";
@ -2466,7 +2466,7 @@
"VOICE_MESSAGE_FILE_NAME" = "Nota de voz";
/* Message for the alert indicating the 'voice message' needs to be held to be held down to record. */
"VOICE_MESSAGE_TOO_SHORT_ALERT_MESSAGE" = "Toca y mantén pulsado el icono del micrófono para grabar una nota de voz.";
"VOICE_MESSAGE_TOO_SHORT_ALERT_MESSAGE" = "Toca y mantén pulsado el icono del micrófono para grabar una nota de voz corta. Toca, arrastra hacia arriba y suelta para grabar notas de voz más largas.";
/* Title for the alert indicating the 'voice message' needs to be held to be held down to record. */
"VOICE_MESSAGE_TOO_SHORT_ALERT_TITLE" = "Nota de voz";

View File

@ -2367,7 +2367,7 @@
"UPGRADE_EXPERIENCE_ENABLE_TYPING_INDICATOR_BUTTON" = "Lülita kirjutamisindikaatorid sisse";
/* Body text for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Optional link previews are now supported for some of the most popular sites on the Internet.";
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Valikulised linkide eelvaated on nüüd toetatud populaarseimate saitide puhul.";
/* Subtitle for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_SUBTITLE" = "You can disable or enable this feature anytime in your Signal settings (Privacy > Send link previews).";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Mikrofonin käyttöoikeutta tarvitaan";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Saapuva puhelu";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Soita";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Vastaamatta jäänyt puhelu, koska soittajan turvanumero on vaihtunut.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Vastaamatta jäänyt puhelu ";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Ei vastausta";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Jäsen";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ ryhmälle %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Nimeä tämä ryhmä";
@ -1467,7 +1467,7 @@
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Etsi yhteystietoja puhelinnumerolla";
/* Label for 1:1 conversation with yourself. */
"NOTE_TO_SELF" = "Muistutus itselleni";
"NOTE_TO_SELF" = "Viestit itselleni";
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "Olet saattanut saada uusia viestejä sillä aikaa, kun %@:si uudelleenkäynnistyi.";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Lähetä";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Viestin lähettäminen epäonnistui.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Kutsun lähettäminen epäonnistui. Yritä myöhemmin uudelleen.";

View File

@ -342,7 +342,7 @@
"CALL_USER_ALERT_CALL_BUTTON" = "Appeler";
/* Message format for alert offering to call a user. Embeds {{the user's display name or phone number}}. */
"CALL_USER_ALERT_MESSAGE_FORMAT" = "Souhaitez-vous appeler%@?";
"CALL_USER_ALERT_MESSAGE_FORMAT" = "Souhaitez-vous appeler %@?";
/* Title for alert offering to call a user. */
"CALL_USER_ALERT_TITLE" = "Appeler?";
@ -1006,7 +1006,7 @@
"GIF_VIEW_SEARCH_ERROR" = "Erreur. Touchez pour ressayer.";
/* Indicates that the user's search had no results. */
"GIF_VIEW_SEARCH_NO_RESULTS" = "Aucun résultat";
"GIF_VIEW_SEARCH_NO_RESULTS" = "Il ny a aucun résultat.";
/* Placeholder text for the search field in GIF view */
"GIF_VIEW_SEARCH_PLACEHOLDER_TEXT" = "Saisir votre recherche";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "נדרשת גישה למיקרופון";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ שיחה נכנסת";
/* Accessibility label for placing call button */
"CALL_LABEL" = "חייג";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ שיחה לא נענתה מאחר שמספר הביטחון של המתקשר השתנה.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ שיחה שלא נענתה";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "אין מענה";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "חבר קבוצה";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ אל %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "תן שם לשיחת קבוצה זו";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "שלח";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "ההודעה שלך נכשלה להישלח.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "שליחת ההזמנה נכשלה, אנא נסה שוב מאוחר יותר.";

View File

@ -1467,7 +1467,7 @@
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Kontaktok keresése telefonszám alapján";
/* Label for 1:1 conversation with yourself. */
"NOTE_TO_SELF" = "Note to Self";
"NOTE_TO_SELF" = "Privát feljegyzés";
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "You may have received messages while your %@ was restarting.";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Accesso microfono richiesto";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Chiamata in arrivo";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Chiama";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Chiamata persa perché il codice di sicurezza del chiamante è cambiato.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Chiamata persa";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Nessuna risposta";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Membro";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ su %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Nome chat di gruppo";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Invia";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Invio del messaggio fallito.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Invio dell'invito fallito, riprova più tardi";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Krever tilgang til mikrofon";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Innkommende anrop";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Ring";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Tapt anrop grunnet endring av din kontakts sikkerhetsnummer";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Ubesvart anrop";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Ingen svar";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Medlem";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ til %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Navngi gruppesamtalen";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Send";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Sending mislyktes";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Sending av invitasjon mislyktes, vennligst prøv igjen senere.";

View File

@ -462,7 +462,7 @@
"CONFIRM_LINK_NEW_DEVICE_ACTION" = "Nieuw apparaat koppelen";
/* Action sheet body presented when a user's SN has recently changed. Embeds {{contact's name or phone number}} */
"CONFIRM_SENDING_TO_CHANGED_IDENTITY_BODY_FORMAT" = "%@ is mogelijk van apparaat veranderd. Verifieer jullie veiligheidsnummers om zeker te zijn van je privacy.";
"CONFIRM_SENDING_TO_CHANGED_IDENTITY_BODY_FORMAT" = "%@ heet mogelijk de app opnieuw geïnstalleerd of heeft een ander apparaat in gebruik genomen. Verifieer jullie veiligheidsnummers om zeker te zijn dat je met de juiste persoon communiceert.";
/* Action sheet title presented when a user's SN has recently changed. Embeds {{contact's name or phone number}} */
"CONFIRM_SENDING_TO_CHANGED_IDENTITY_TITLE_FORMAT" = "Veiligheidsnummer met %@ is veranderd";
@ -1036,7 +1036,7 @@
"GROUP_MEMBERS_CALL" = "Bellen";
/* Label for the button that clears all verification errors in the 'group members' view. */
"GROUP_MEMBERS_RESET_NO_LONGER_VERIFIED" = "Alle verificatiefouten wissen";
"GROUP_MEMBERS_RESET_NO_LONGER_VERIFIED" = "Alle groepsleden als niet geverifieerd markeren";
/* Label for the 'reset all no-longer-verified group members' confirmation alert. */
"GROUP_MEMBERS_RESET_NO_LONGER_VERIFIED_ALERT_MESSAGE" = "Dit zal de verificatie wissen voor alle groepsleden wier veiligheidsnummers veranderd zijn sinds ze als geverifieerd zijn gemarkeerd.";
@ -1045,7 +1045,7 @@
"GROUP_MEMBERS_SECTION_TITLE_MEMBERS" = "Leden";
/* Title for the 'no longer verified' section of the 'group members' view. */
"GROUP_MEMBERS_SECTION_TITLE_NO_LONGER_VERIFIED" = "Niet meer gemarkeerd als geverifieerd";
"GROUP_MEMBERS_SECTION_TITLE_NO_LONGER_VERIFIED" = "Niet langer gemarkeerd als geverifieerd";
/* Button label for the 'send message to group member' button */
"GROUP_MEMBERS_SEND_MESSAGE" = "Bericht verzenden";
@ -1336,13 +1336,13 @@
"MESSAGE_TEXT_FIELD_PLACEHOLDER" = "Nieuw bericht";
/* Indicates that one member of this group conversation is no longer verified. Embeds {{user's name or phone number}}. */
"MESSAGES_VIEW_1_MEMBER_NO_LONGER_VERIFIED_FORMAT" = "%@ is niet meer gemarkeerd als geverifieerd. Tik voor opties.";
"MESSAGES_VIEW_1_MEMBER_NO_LONGER_VERIFIED_FORMAT" = "%@ is niet langer gemarkeerd als geverifieerd. Tik voor opties.";
/* Indicates that this 1:1 conversation has been blocked. */
"MESSAGES_VIEW_CONTACT_BLOCKED" = "Je hebt deze gebruiker geblokkeerd";
/* Indicates that this 1:1 conversation is no longer verified. Embeds {{user's name or phone number}}. */
"MESSAGES_VIEW_CONTACT_NO_LONGER_VERIFIED_FORMAT" = "%@ is niet meer gemarkeerd als geverifieerd. Tik voor opties.";
"MESSAGES_VIEW_CONTACT_NO_LONGER_VERIFIED_FORMAT" = "%@ is niet langer gemarkeerd als geverifieerd. Tik voor opties.";
/* Indicates that a single member of this group has been blocked. */
"MESSAGES_VIEW_GROUP_1_MEMBER_BLOCKED" = "Je hebt één lid van deze groep geblokkeerd";
@ -1357,7 +1357,7 @@
"MESSAGES_VIEW_GROUP_PROFILE_WHITELIST_BANNER" = "Je profiel met deze groep delen?";
/* Indicates that more than one member of this group conversation is no longer verified. */
"MESSAGES_VIEW_N_MEMBERS_NO_LONGER_VERIFIED" = "Meer dan een lid van deze groep is niet meer gemarkeerd als geverifieerd. Tik voor opties.";
"MESSAGES_VIEW_N_MEMBERS_NO_LONGER_VERIFIED" = "Meer dan een lid van deze groep is niet langer gemarkeerd als geverifieerd. Tik voor opties.";
/* The subtitle for the messages view title indicates that the title can be tapped to access settings for this conversation. */
"MESSAGES_VIEW_TITLE_SUBTITLE" = "Tik hier voor instellingen";
@ -1584,7 +1584,7 @@
"PRIVACY_UNVERIFY_BUTTON" = "Verificatie wissen";
/* Alert body when verifying with {{contact name}} */
"PRIVACY_VERIFICATION_FAILED_I_HAVE_WRONG_KEY_FOR_THEM" = "Dit lijkt niet op je veiligheidsnummer met %@. Verifieer je wel het juiste contact?";
"PRIVACY_VERIFICATION_FAILED_I_HAVE_WRONG_KEY_FOR_THEM" = "Dit lijkt niet op je veiligheidsnummer met %@. Verifieer je wel de juiste contactpersoon?";
/* Alert body */
"PRIVACY_VERIFICATION_FAILED_MISMATCHED_SAFETY_NUMBERS_IN_CLIPBOARD" = "Het nummer in je klembord lijkt niet op het juiste veiligheidsnummer voor dit gesprek.";
@ -1599,7 +1599,7 @@
"PRIVACY_VERIFICATION_FAILED_WITH_OLD_LOCAL_VERSION" = "Je gebruikt een oude versie van Signal. Om te kunnen verifiëren moet je Signal eerst bijwerken.";
/* alert body */
"PRIVACY_VERIFICATION_FAILED_WITH_OLD_REMOTE_VERSION" = "Je partner gebruikt een oude versie van Signal. Om te kunnen bevestigen moet hij/zij Signal eerst bijwerken.";
"PRIVACY_VERIFICATION_FAILED_WITH_OLD_REMOTE_VERSION" = "Je gesprekspartner gebruikt een oude versie van Signal. Om te kunnen verifiëren moet hij/zij Signal eerst bijwerken.";
/* alert body */
"PRIVACY_VERIFICATION_FAILURE_INVALID_QRCODE" = "De gescande code lijkt niet op een veiligheidsnummer. Gebruiken jullie allebei de laatste versie van Signal?";
@ -1845,7 +1845,7 @@
"SAFETY_NUMBER_SHARE_FORMAT" = "Ons Signal-veiligheidsnummer:\n%@";
/* Action sheet heading */
"SAFETY_NUMBERS_ACTIONSHEET_TITLE" = "Je veiligheidsnummer met %@ is veranderd. Je verifieert het best.";
"SAFETY_NUMBERS_ACTIONSHEET_TITLE" = "Je veiligheidsnummer met %@ is veranderd. Het wordt aanbevolen het nieuwe nummer te verifiëren.";
/* label presented once scanning (camera) view is visible. */
"SCAN_CODE_INSTRUCTIONS" = "Scan de QR-code op het apparaat van je contact.";
@ -2211,7 +2211,7 @@
"SHARE_ACTION_TWEET" = "Twitter";
/* alert body when sharing file failed because of untrusted/changed identity keys */
"SHARE_EXTENSION_FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_FORMAT" = "Je veiligheidsnummer met %@ is recent veranderd. Je verifieert het best in de hoofdapp vooraleer je je bericht opnieuw stuurt.";
"SHARE_EXTENSION_FAILED_SENDING_BECAUSE_UNTRUSTED_IDENTITY_FORMAT" = "Je veiligheidsnummer met %@ is recentelijk veranderd. Het wordt aanbevolen om dit nieuwe nummer te verifiëren is de Signal app.";
/* Indicates that the share extension is still loading. */
"SHARE_EXTENSION_LOADING" = "Aan het laden…";

View File

@ -183,7 +183,7 @@
"BACKUP_EXPORT_ERROR_INVALID_CLOUDKIT_RESPONSE" = "Nieprawidłowa odpowiedź serwera";
/* Indicates that the cloud is being cleaned up. */
"BACKUP_EXPORT_PHASE_CLEAN_UP" = "Czyszczenie kopii zapasowych";
"BACKUP_EXPORT_PHASE_CLEAN_UP" = "Czyszczenie kopii zapasowej";
/* Indicates that the backup export is being configured. */
"BACKUP_EXPORT_PHASE_CONFIGURATION" = "Uruchamianie kopii zapasowej";
@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Wymagany dostęp do mikrofonu";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Połączenie przychodzące";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Połączenie";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Nieodebrane połączenie z powodu zmiany numeru bezpieczeństwa dzwoniącego.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Nieodebrane połączenie";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Brak odpowiedzi";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Członek";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ do %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Nazwij tę rozmowę grupową";
@ -1467,7 +1467,7 @@
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Odszukaj kontakty po numerze telefonu";
/* Label for 1:1 conversation with yourself. */
"NOTE_TO_SELF" = "Note to Self";
"NOTE_TO_SELF" = "Uwaga dla siebie";
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "Możliwe, że otrzymałeś wiadomości podczas restartowania %@.";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Wyślij";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Twoja wiadomość nie została wysłana.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Wysyłanie zaproszenia nie powiodło się, spróbuj później.";

View File

@ -72,7 +72,7 @@
"APP_UPDATE_NAG_ALERT_DISMISS_BUTTON" = "Agora Não";
/* Message format for the 'new app version available' alert. Embeds: {{The latest app version number}} */
"APP_UPDATE_NAG_ALERT_MESSAGE_FORMAT" = "A versão %@ está disponível na App Store.";
"APP_UPDATE_NAG_ALERT_MESSAGE_FORMAT" = "A versão %@ está disponível agora na App Store.";
/* Title for the 'new app version available' alert. */
"APP_UPDATE_NAG_ALERT_TITLE" = "Uma Nova Versão do Signal Está Disponível";
@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Necessário acesso ao microfone";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Recebendo ligação";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Chamar";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Chamada perdida porque o número de segurança do chamador mudou.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Ligação perdida";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Sem Resposta.";
@ -339,7 +339,7 @@
"CALL_STATUS_FORMAT" = "Signal %@";
/* Label for call button for alert offering to call a user. */
"CALL_USER_ALERT_CALL_BUTTON" = "Chamar";
"CALL_USER_ALERT_CALL_BUTTON" = "Ligar";
/* Message format for alert offering to call a user. Embeds {{the user's display name or phone number}}. */
"CALL_USER_ALERT_MESSAGE_FORMAT" = "Gostaria de chamar %@?";
@ -1081,7 +1081,7 @@
"HOME_VIEW_TITLE_INBOX" = "Signal";
/* Label for brush button in image editor. */
"IMAGE_EDITOR_BRUSH_BUTTON" = "Brush";
"IMAGE_EDITOR_BRUSH_BUTTON" = "Escova ";
/* Label for crop button in image editor. */
"IMAGE_EDITOR_CROP_BUTTON" = "Cortar";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Membro";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ para %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Nomear esse grupo de bate-papo";
@ -1467,7 +1467,7 @@
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Encontrar Contatos pelo Número do Telefone";
/* Label for 1:1 conversation with yourself. */
"NOTE_TO_SELF" = "Note to Self";
"NOTE_TO_SELF" = "Recado para mim";
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "Pode ser que você tenha recebido mensagens enquanto seu %@ reiniciava.";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Enviar";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Sua mensagem não pôde ser enviada.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Falha no envie de convite. Tente novamente mais tarde.";
@ -2085,7 +2085,7 @@
"SETTINGS_LINK_PREVIEWS" = "Enviar pré-visualizações de links";
/* Footer for setting for enabling & disabling link previews. */
"SETTINGS_LINK_PREVIEWS_FOOTER" = "Previews are supported for Imgur, Instagram, Reddit, and YouTube links.";
"SETTINGS_LINK_PREVIEWS_FOOTER" = "Visualizações são suportadas para Imgur, Instagram, Reddit e links do YouTube.";
/* Header for setting for enabling & disabling link previews. */
"SETTINGS_LINK_PREVIEWS_HEADER" = "Pré-visualizações de links";
@ -2367,10 +2367,10 @@
"UPGRADE_EXPERIENCE_ENABLE_TYPING_INDICATOR_BUTTON" = "Ativar indicadores de digitação";
/* Body text for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Optional link previews are now supported for some of the most popular sites on the Internet.";
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_DESCRIPTION" = "Agora é possível mostrar pré-visualizações de links de alguns dos sites mais populares da internet.";
/* Subtitle for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_SUBTITLE" = "You can disable or enable this feature anytime in your Signal settings (Privacy > Send link previews).";
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_SUBTITLE" = "Você pode desativar ou habilitar essa característica a qualquer momento em suas configurações do Signal (Privacidade > Mandar link de visualizações).";
/* Header for upgrading users */
"UPGRADE_EXPERIENCE_INTRODUCING_LINK_PREVIEWS_TITLE" = "Pré-visualizações de links";
@ -2472,7 +2472,7 @@
"VOICE_MESSAGE_TOO_SHORT_ALERT_TITLE" = "Mensagem de voz";
/* Activity indicator title, shown upon returning to the device manager, until you complete the provisioning process on desktop */
"WAITING_TO_COMPLETE_DEVICE_LINK_TEXT" = "Termine a configuração no Signal Desktop";
"WAITING_TO_COMPLETE_DEVICE_LINK_TEXT" = "Termine a configuração no Signal Desktop.";
/* Info Message when you disable disappearing messages */
"YOU_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION" = "Você desabilitou mensagens efêmeras.";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Требуется доступ к микрофону";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Входящий вызов";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Позвонить";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Пропущенный вызов по причине изменения кода безопасности звонящего.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Пропущенный вызов";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Нет ответа";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Участники";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ в %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Дайте имя этой группе";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Отправить";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Ваше сообщение не было отправлено.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Ошибка отправки приглашения, пожалуйста, повторить попытку.";

View File

@ -1467,7 +1467,7 @@
"NO_CONTACTS_SEARCH_BY_PHONE_NUMBER" = "Poišči stike z vnosom telefonske številke";
/* Label for 1:1 conversation with yourself. */
"NOTE_TO_SELF" = "Note to Self";
"NOTE_TO_SELF" = "Sporočilo sebi";
/* Lock screen notification text presented after user powers on their device without unlocking. Embeds {{device model}} (either 'iPad' or 'iPhone') */
"NOTIFICATION_BODY_PHONE_LOCKED_FORMAT" = "Možno je, da ste med ponovnim zagonom naprave %@ prejeli kakšno sporočilo.";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Åtkomst till mikrofon krävs";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Inkommande samtal";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Ring";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missade samtalet, för den andra personens säkerhetsnummer har ändrats.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missat samtal";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Inget svar";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Medlem";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ till %@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Namnge gruppchatten";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Skicka";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Ditt meddelande kunde inte skickas.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Kunde inte skicka inbjudan, vänligen försök senare.";
@ -2436,7 +2436,7 @@
"VERIFICATION_CHALLENGE_SUBMIT_CODE" = "Skicka";
/* Label indicating the phone number currently being verified. */
"VERIFICATION_PHONE_NUMBER_FORMAT" = "Skriv verifieringskoden vi skickade till %@.";
"VERIFICATION_PHONE_NUMBER_FORMAT" = "Ange verifieringskoden vi skickade till %@.";
/* Format for info message indicating that the verification state was unverified on this device. Embeds {{user's name or phone number}}. */
"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_LOCAL" = "Du markerade %@ som ej verifierad.";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "Mikrofon Erişimi Gerekiyor";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Gelen Arama";
/* Accessibility label for placing call button */
"CALL_LABEL" = "Ara";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Arayanın güvenlik numarası değiştiğinden arama karşılanmadı.";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Cevapsız Arama";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "Yanıt Yok";
@ -423,7 +423,7 @@
"CLOUDKIT_STATUS_RESTRICTED" = "Signal'in iCloud erişimi engellenmiş. Signal verilerinizi yedeklemek için iOS ayarlar uygulamasından Signal'in iCloud hesabınıza erişimine izni verin.";
/* The first of two messages demonstrating the chosen conversation color, by rendering this message in an outgoing message bubble. */
"COLOR_PICKER_DEMO_MESSAGE_1" = "Bu sohbete gönderilen mesajarın rengini seçin.";
"COLOR_PICKER_DEMO_MESSAGE_1" = "Bu sohbete gönderilen mesajların rengini seçin.";
/* The second of two messages demonstrating the chosen conversation color, by rendering this message in an incoming message bubble. */
"COLOR_PICKER_DEMO_MESSAGE_2" = "Seçtiğiniz rengi sadece siz göreceksiniz.";
@ -733,7 +733,7 @@
"DEBUG_LOG_GITHUB_ISSUE_ALERT_MESSAGE" = "Gist bağlantısı panonuza kopyalandı. GitHub sorunlar listesine yönlendirilmek üzeresiniz.";
/* Title of the alert before redirecting to GitHub Issues. */
"DEBUG_LOG_GITHUB_ISSUE_ALERT_TITLE" = "GıtHub'a Yönlendirme";
"DEBUG_LOG_GITHUB_ISSUE_ALERT_TITLE" = "GitHub'a Yönlendirme";
/* Label for button that lets users re-register using the same phone number. */
"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER" = "Bu telefon numarasını tekrar kaydet";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "Üye";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@, %@ grubuna";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "Grup sohbetini adlandır";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "Gönder";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "Mesajınız gönderilemedi.";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "Davetiye gönderilemedi, lütfen daha sonra tekrar deneyin.";
@ -2145,7 +2145,7 @@
"SETTINGS_SCREEN_SECURITY_DETAIL" = "Uygulama geçişleri esnasında Signal önizlemelerinin görülmez.";
/* Settings table section footer. */
"SETTINGS_SECTION_CALL_KIT_DESCRIPTION" = "IOS Çağrı Entegrasyonu, Signal çağrılarını kilit ekranınızda ve sistemin arama geçmişinde gösterir. İsteğe bağlı olarak kişinin adını ve numarasını da gösterebilirsiniz. ICloud etkinleştirilmiş ise, bu arama geçmişi Apple ile paylaşılacaktır.";
"SETTINGS_SECTION_CALL_KIT_DESCRIPTION" = "iOS Çağrı Entegrasyonu, Signal çağrılarını kilit ekranınızda ve sistemin arama geçmişinde gösterir. İsteğe bağlı olarak kişinin adını ve numarasını da gösterebilirsiniz. ICloud etkinleştirilmiş ise, bu arama geçmişi Apple ile paylaşılacaktır.";
/* Label for the notifications section of conversation settings view. */
"SETTINGS_SECTION_NOTIFICATIONS" = "Bildirimler";
@ -2355,7 +2355,7 @@
"UPDATE_GROUP_CANT_REMOVE_MEMBERS_ALERT_TITLE" = "Desteklenmiyor";
/* Description of CallKit to upgrading (existing) users */
"UPGRADE_EXPERIENCE_CALLKIT_DESCRIPTION" = "IOS çağrı entegrasyonu ile kilit ekranınızdan gelen aramaları cevaplamak kolaydır. Varsayılan olarak arayan bilgilerini saklarız.";
"UPGRADE_EXPERIENCE_CALLKIT_DESCRIPTION" = "iOS çağrı entegrasyonu ile kilit ekranınızdan gelen aramaları cevaplamak kolaydır. Varsayılan olarak arayan bilgilerini saklarız.";
/* button label shown once when when user upgrades app, in context of call kit */
"UPGRADE_EXPERIENCE_CALLKIT_PRIVACY_SETTINGS_BUTTON" = "Daha fazlasını gizlilik ayarlarınızdan öğrenin.";
@ -2415,7 +2415,7 @@
"UPGRADE_EXPERIENCE_VIDEO_TITLE" = "Merhaba Güvenli Görüntülü Aramalar!";
/* Message for the alert indicating that user should upgrade iOS. */
"UPGRADE_IOS_ALERT_MESSAGE" = "Signal iOS 9 veya daha sonrasını gerektirir. Lütfen iOS Ayarlar uygulamasından Yazılım Güncelleştirme özeliğini kullanın.";
"UPGRADE_IOS_ALERT_MESSAGE" = "Signal iOS 9 veya daha sonrasını gerektirir. Lütfen iOS Ayarlar uygulamasından Yazılım Güncelleme özeliğini kullanın.";
/* Title for the alert indicating that user should upgrade iOS. */
"UPGRADE_IOS_ALERT_TITLE" = "iOS'u Güncelle";

View File

@ -321,16 +321,16 @@
"CALL_AUDIO_PERMISSION_TITLE" = "请求访问麦克风";
/* notification body */
"CALL_INCOMING_NOTIFICATION_BODY" = "☎️ Incoming Call";
"CALL_INCOMING_NOTIFICATION_BODY" = "来电";
/* Accessibility label for placing call button */
"CALL_LABEL" = "打电话";
/* notification body */
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "☎️ Missed call because the caller's safety number changed.";
"CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY" = "未接来电,因为对方的安全码发生了变化。";
/* notification body */
"CALL_MISSED_NOTIFICATION_BODY" = "☎️ Missed Call";
"CALL_MISSED_NOTIFICATION_BODY" = "未接来电";
/* Call setup status label after outgoing call times out */
"CALL_SCREEN_STATUS_NO_ANSWER" = "无应答";
@ -1440,7 +1440,7 @@
"NEW_GROUP_MEMBER_LABEL" = "成员";
/* notification title. Embeds {{author name}} and {{group name}} */
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@ to %@";
"NEW_GROUP_MESSAGE_NOTIFICATION_TITLE" = "%@%@";
/* Placeholder text for group name field */
"NEW_GROUP_NAMEGROUP_REQUEST_DEFAULT" = "为此群组命名";
@ -1914,7 +1914,7 @@
"SEND_BUTTON_TITLE" = "发送";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"SEND_FAILED_NOTIFICATION_BODY" = "您的信息发送失败。";
/* Alert body after invite failed */
"SEND_INVITE_FAILURE" = "发送邀请失败, 请稍候再试.";

View File

@ -1,12 +1,13 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
extern NSErrorUserInfoKey const ContactDiscoveryServiceErrorKey_Reason;
extern NSErrorDomain const ContactDiscoveryServiceErrorDomain;
typedef NS_ERROR_ENUM(ContactDiscoveryServiceErrorDomain, ContactDiscoveryServiceError){
ContactDiscoveryServiceErrorAttestationFailed = 100,
ContactDiscoveryServiceErrorAttestationFailed = 100, ContactDiscoveryServiceErrorAssertionError = 101
};
@class ECKeyPair;

View File

@ -18,8 +18,16 @@
NS_ASSUME_NONNULL_BEGIN
NSErrorUserInfoKey const ContactDiscoveryServiceErrorKey_Reason = @"ContactDiscoveryServiceErrorKey_Reason";
NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.ContactDiscoveryService";
NSError *ContactDiscoveryServiceErrorMakeWithReason(NSInteger code, NSString *reason)
{
return [NSError errorWithDomain:ContactDiscoveryServiceErrorDomain
code:code
userInfo:@{ ContactDiscoveryServiceErrorKey_Reason : reason }];
}
@interface RemoteAttestationAuth ()
@property (nonatomic) NSString *username;
@ -53,17 +61,21 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
+ (nullable RemoteAttestationKeys *)keysForKeyPair:(ECKeyPair *)keyPair
serverEphemeralPublic:(NSData *)serverEphemeralPublic
serverStaticPublic:(NSData *)serverStaticPublic
error:(NSError **)error
{
if (!keyPair) {
OWSFailDebug(@"Missing keyPair");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Missing keyPair");
return nil;
}
if (serverEphemeralPublic.length < 1) {
OWSFailDebug(@"Invalid serverEphemeralPublic");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Invalid serverEphemeralPublic");
return nil;
}
if (serverStaticPublic.length < 1) {
OWSFailDebug(@"Invalid serverStaticPublic");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Invalid serverStaticPublic");
return nil;
}
RemoteAttestationKeys *keys = [RemoteAttestationKeys new];
@ -71,6 +83,8 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
keys.serverEphemeralPublic = serverEphemeralPublic;
keys.serverStaticPublic = serverStaticPublic;
if (![keys deriveKeys]) {
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"failed to derive keys");
return nil;
}
return keys;
@ -350,16 +364,22 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
[[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseJson) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *_Nullable error;
RemoteAttestation *_Nullable attestation = [self parseAttestationResponseJson:responseJson
response:task.response
keyPair:keyPair
enclaveId:enclaveId
auth:auth];
auth:auth
error:&error];
if (!attestation) {
NSError *error = [NSError errorWithDomain:ContactDiscoveryServiceErrorDomain
code:ContactDiscoveryServiceErrorAttestationFailed
userInfo:nil];
if (!error) {
OWSFailDebug(@"error was unexpectedly nil");
error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError,
@"failure when parsing attestation - no reason given");
} else {
OWSFailDebug(@"error with attestation: %@", error);
}
error.isRetryable = NO;
failureHandler(error);
return;
@ -378,6 +398,7 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
keyPair:(ECKeyPair *)keyPair
enclaveId:(NSString *)enclaveId
auth:(RemoteAttestationAuth *)auth
error:(NSError **)error
{
OWSAssertDebug(responseJson);
OWSAssertDebug(response);
@ -385,78 +406,96 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
OWSAssertDebug(enclaveId.length > 0);
if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
OWSFailDebug(@"unexpected response type.");
*error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError, @"unexpected response type.");
return nil;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSArray<NSHTTPCookie *> *cookies =
[NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:httpResponse.URL];
if (cookies.count < 1) {
OWSFailDebug(@"couldn't parse cookie.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse cookie.");
return nil;
}
if (![responseJson isKindOfClass:[NSDictionary class]]) {
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"invalid json response");
return nil;
}
NSDictionary *responseDict = responseJson;
NSData *_Nullable serverEphemeralPublic =
[responseDict base64DataForKey:@"serverEphemeralPublic" expectedLength:32];
if (!serverEphemeralPublic) {
OWSFailDebug(@"couldn't parse serverEphemeralPublic.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse serverEphemeralPublic.");
return nil;
}
NSData *_Nullable serverStaticPublic = [responseDict base64DataForKey:@"serverStaticPublic" expectedLength:32];
if (!serverStaticPublic) {
OWSFailDebug(@"couldn't parse serverStaticPublic.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse serverStaticPublic.");
return nil;
}
NSData *_Nullable encryptedRequestId = [responseDict base64DataForKey:@"ciphertext"];
if (!encryptedRequestId) {
OWSFailDebug(@"couldn't parse encryptedRequestId.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestId.");
return nil;
}
NSData *_Nullable encryptedRequestIv = [responseDict base64DataForKey:@"iv" expectedLength:12];
if (!encryptedRequestIv) {
OWSFailDebug(@"couldn't parse encryptedRequestIv.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestIv.");
return nil;
}
NSData *_Nullable encryptedRequestTag = [responseDict base64DataForKey:@"tag" expectedLength:16];
if (!encryptedRequestTag) {
OWSFailDebug(@"couldn't parse encryptedRequestTag.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encryptedRequestTag.");
return nil;
}
NSData *_Nullable quoteData = [responseDict base64DataForKey:@"quote"];
if (!quoteData) {
OWSFailDebug(@"couldn't parse quote data.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse quote data.");
return nil;
}
NSString *_Nullable signatureBody = [responseDict stringForKey:@"signatureBody"];
if (![signatureBody isKindOfClass:[NSString class]]) {
OWSFailDebug(@"couldn't parse signatureBody.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse signatureBody.");
return nil;
}
NSData *_Nullable signature = [responseDict base64DataForKey:@"signature"];
if (!signature) {
OWSFailDebug(@"couldn't parse signature.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse signature.");
return nil;
}
NSString *_Nullable encodedCertificates = [responseDict stringForKey:@"certificates"];
if (![encodedCertificates isKindOfClass:[NSString class]]) {
OWSFailDebug(@"couldn't parse encodedCertificates.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse encodedCertificates.");
return nil;
}
NSString *_Nullable certificates = [encodedCertificates stringByRemovingPercentEncoding];
if (!certificates) {
OWSFailDebug(@"couldn't parse certificates.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't parse certificates.");
return nil;
}
RemoteAttestationKeys *_Nullable keys = [RemoteAttestationKeys keysForKeyPair:keyPair
serverEphemeralPublic:serverEphemeralPublic
serverStaticPublic:serverStaticPublic];
if (!keys) {
OWSFailDebug(@"couldn't derive keys.");
serverStaticPublic:serverStaticPublic
error:error];
if (!keys || *error != nil) {
if (*error == nil) {
OWSFailDebug(@"missing error specifics");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"Couldn't derive keys. No reason given");
}
return nil;
}
@ -470,20 +509,28 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
encryptedRequestTag:encryptedRequestTag
keys:keys];
if (!requestId) {
OWSFailDebug(@"couldn't decrypt request id.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"couldn't decrypt request id.");
return nil;
}
if (![self verifyServerQuote:quote keys:keys enclaveId:enclaveId]) {
OWSFailDebug(@"couldn't verify quote.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"couldn't verify quote.");
return nil;
}
if (![self verifyIasSignatureWithCertificates:certificates
signatureBody:signatureBody
signature:signature
quoteData:quoteData]) {
OWSFailDebug(@"couldn't verify ias signature.");
quoteData:quoteData
error:error]) {
if (*error == nil) {
OWSFailDebug(@"missing error specifics");
*error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAssertionError,
@"verifyIasSignatureWithCertificates failed. No reason given");
}
return nil;
}
@ -503,61 +550,71 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
signatureBody:(NSString *)signatureBody
signature:(NSData *)signature
quoteData:(NSData *)quoteData
error:(NSError **)error
{
OWSAssertDebug(certificates.length > 0);
OWSAssertDebug(signatureBody.length > 0);
OWSAssertDebug(signature.length > 0);
OWSAssertDebug(quoteData);
NSError *error;
NSError *signingError;
CDSSigningCertificate *_Nullable certificate =
[CDSSigningCertificate parseCertificateFromPem:certificates error:&error];
if (error) {
OWSFailDebug(@"error when parsing signing certificate. %@", error.localizedDescription);
[CDSSigningCertificate parseCertificateFromPem:certificates error:&signingError];
if (signingError) {
*error = signingError;
return NO;
}
if (!certificate) {
OWSFailDebug(@"could not parse signing certificate.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"could not parse signing certificate.");
return NO;
}
if (![certificate verifySignatureOfBody:signatureBody signature:signature]) {
OWSFailDebug(@"could not verify signature.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"could not verify signature.");
return NO;
}
SignatureBodyEntity *_Nullable signatureBodyEntity = [self parseSignatureBodyEntity:signatureBody];
if (!signatureBodyEntity) {
OWSFailDebug(@"could not parse signature body.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"could not parse signature body.");
return NO;
}
// Compare the first N bytes of the quote data with the signed quote body.
const NSUInteger kQuoteBodyComparisonLength = 432;
if (signatureBodyEntity.isvEnclaveQuoteBody.length < kQuoteBodyComparisonLength) {
OWSFailDebug(@"isvEnclaveQuoteBody has unexpected length.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"isvEnclaveQuoteBody has unexpected length.");
return NO;
}
// NOTE: This version is separate from and does _NOT_ match the CDS quote version.
const NSUInteger kSignatureBodyVersion = 3;
if (![signatureBodyEntity.version isEqual:@(kSignatureBodyVersion)]) {
OWSFailDebug(@"signatureBodyEntity has unexpected version.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"signatureBodyEntity has unexpected version.");
return NO;
}
if (quoteData.length < kQuoteBodyComparisonLength) {
OWSFailDebug(@"quoteData has unexpected length.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"quoteData has unexpected length.");
return NO;
}
NSData *isvEnclaveQuoteBodyForComparison =
[signatureBodyEntity.isvEnclaveQuoteBody subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)];
NSData *quoteDataForComparison = [quoteData subdataWithRange:NSMakeRange(0, kQuoteBodyComparisonLength)];
if (![isvEnclaveQuoteBodyForComparison ows_constantTimeIsEqualToData:quoteDataForComparison]) {
OWSFailDebug(@"isvEnclaveQuoteBody and quoteData do not match.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"isvEnclaveQuoteBody and quoteData do not match.");
return NO;
}
if (![@"OK" isEqualToString:signatureBodyEntity.isvEnclaveQuoteStatus]) {
OWSFailDebug(@"invalid isvEnclaveQuoteStatus: %@.", signatureBodyEntity.isvEnclaveQuoteStatus);
NSString *reason =
[NSString stringWithFormat:@"invalid isvEnclaveQuoteStatus: %@", signatureBodyEntity.isvEnclaveQuoteStatus];
*error = ContactDiscoveryServiceErrorMakeWithReason(ContactDiscoveryServiceErrorAttestationFailed, reason);
return NO;
}
@ -567,7 +624,8 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
[dateFormatter setDateFormat:@"yyy-MM-dd'T'HH:mm:ss.SSSSSS"];
NSDate *timestampDate = [dateFormatter dateFromString:signatureBodyEntity.timestamp];
if (!timestampDate) {
OWSFailDebug(@"could not parse signature body timestamp.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAssertionError, @"could not parse signature body timestamp.");
return NO;
}
@ -581,7 +639,8 @@ NSErrorDomain const ContactDiscoveryServiceErrorDomain = @"SignalServiceKit.Cont
BOOL isExpired = [now isAfterDate:timestampDatePlus1Day];
if (isExpired) {
OWSFailDebug(@"Signature is expired.");
*error = ContactDiscoveryServiceErrorMakeWithReason(
ContactDiscoveryServiceErrorAttestationFailed, @"Signature is expired.");
return NO;
}

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -400,11 +400,11 @@ class CDSBatchOperation: OWSOperation {
class CDSFeedbackOperation: OWSOperation {
enum FeedbackResult: String {
enum FeedbackResult {
case ok
case mismatch
case attestationError = "attestation-error"
case unexpectedError = "unexpected-error"
case attestationError(reason: String)
case unexpectedError(reason: String)
}
private let legacyRegisteredRecipientIds: Set<String>
@ -455,10 +455,22 @@ class CDSFeedbackOperation: OWSOperation {
case ContactDiscoveryError.serverError, ContactDiscoveryError.clientError:
// Server already has this information, no need submit feedback
self.reportSuccess()
case ContactDiscoveryServiceError.attestationFailed:
self.makeRequest(result: .attestationError)
case let cdsError as ContactDiscoveryServiceError:
let reason = cdsError.reason
switch cdsError.code {
case .assertionError:
self.makeRequest(result: .unexpectedError(reason: "CDS assertionError: \(reason ?? "unknown")"))
case .attestationFailed:
self.makeRequest(result: .attestationError(reason: "CDS attestationFailed: \(reason ?? "unknown")"))
}
case ContactDiscoveryError.assertionError(let assertionDescription):
self.makeRequest(result: .unexpectedError(reason: "assertionError: \(assertionDescription)"))
case ContactDiscoveryError.parseError(description: let parseErrorDescription):
self.makeRequest(result: .unexpectedError(reason: "parseError: \(parseErrorDescription)"))
default:
self.makeRequest(result: .unexpectedError)
let nsError = error as NSError
let reason = "unexpectedError code:\(nsError.code)"
self.makeRequest(result: .unexpectedError(reason: reason))
}
return
@ -474,7 +486,18 @@ class CDSFeedbackOperation: OWSOperation {
}
func makeRequest(result: FeedbackResult) {
let request = OWSRequestFactory.cdsFeedbackRequest(result: result.rawValue)
let reason: String?
switch result {
case .ok:
reason = nil
case .mismatch:
reason = nil
case .attestationError(let attestationErrorReason):
reason = attestationErrorReason
case .unexpectedError(let unexpectedErrorReason):
reason = unexpectedErrorReason
}
let request = OWSRequestFactory.cdsFeedbackRequest(status: result.statusPath, reason: reason)
self.networkManager.makeRequest(request,
success: { _, _ in self.reportSuccess() },
failure: { _, error in self.reportError(error) })
@ -488,3 +511,24 @@ extension Array {
}
}
}
extension CDSFeedbackOperation.FeedbackResult {
var statusPath: String {
switch self {
case .ok:
return "ok"
case .mismatch:
return "mismatch"
case .attestationError:
return "attestation-error"
case .unexpectedError:
return "unexpected-error"
}
}
}
extension ContactDiscoveryServiceError {
var reason: String? {
return userInfo[ContactDiscoveryServiceErrorKey_Reason] as? String
}
}

View File

@ -103,7 +103,8 @@ typedef NS_ENUM(NSUInteger, TSVerificationTransport) { TSVerificationTransportVo
cookies:(NSArray<NSHTTPCookie *> *)cookies;
+ (TSRequest *)remoteAttestationAuthRequest;
+ (TSRequest *)cdsFeedbackRequestWithResult:(NSString *)result NS_SWIFT_NAME(cdsFeedbackRequest(result:));
+ (TSRequest *)cdsFeedbackRequestWithStatus:(NSString *)status
reason:(nullable NSString *)reason NS_SWIFT_NAME(cdsFeedbackRequest(status:reason:));
#pragma mark - UD

View File

@ -498,10 +498,26 @@ NS_ASSUME_NONNULL_BEGIN
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"GET" parameters:@{}];
}
+ (TSRequest *)cdsFeedbackRequestWithResult:(NSString *)result
+ (TSRequest *)cdsFeedbackRequestWithStatus:(NSString *)status
reason:(nullable NSString *)reason
{
NSString *path = [NSString stringWithFormat:@"/v1/directory/feedback/%@", result];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:@{}];
NSDictionary<NSString *, NSString *> *parameters;
if (reason == nil) {
parameters = @{};
} else {
const NSUInteger kServerReasonLimit = 1000;
NSString *limitedReason;
if (reason.length < kServerReasonLimit) {
limitedReason = reason;
} else {
OWSFailDebug(@"failure: reason should be under 1000");
limitedReason = [reason substringToIndex:kServerReasonLimit - 1];
}
parameters = @{ @"reason": limitedReason };
}
NSString *path = [NSString stringWithFormat:@"/v1/directory/feedback-v2/%@", status];
return [TSRequest requestWithUrl:[NSURL URLWithString:path] method:@"PUT" parameters:parameters];
}
#pragma mark - UD

View File

@ -0,0 +1,20 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import PromiseKit
public extension YapDatabaseConnection {
@objc
func readWritePromise(_ block: @escaping (YapDatabaseReadWriteTransaction) -> Void) -> AnyPromise {
return AnyPromise(readWritePromise(block) as Promise<Void>)
}
func readWritePromise(_ block: @escaping (YapDatabaseReadWriteTransaction) -> Void) -> Promise<Void> {
return Promise { resolver in
self.asyncReadWrite(block, completionBlock: resolver.fulfill)
}
}
}

View File

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>2.36.0</string>
<key>CFBundleVersion</key>
<string>2.36.0.5</string>
<string>2.36.0.7</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSAppTransportSecurity</key>