feat(apple): saved devices and targeted transfers UI

Adds the native SwiftUI Saved Devices experience on top of the production
saved-device core, as a top-level destination in the iOS tab bar and the
macOS sidebar.

Core seam:
- App-facing saved-device domain models mirroring core/SavedDeviceModels.kt,
  with lifecycle helpers (canReceive/canResume/canCancel/canDelete) so views
  never hand-roll state checks.
- 21 gateway methods through CoreGateway/CoreRepository with UniFFI mapping.
  cancelTargetedTransfer, forgetSavedDevice and blockDevice run off the serial
  lane: each must reach the core while a targeted receive is blocking it.
- Payload-free pairingChanged/targetedTransferChanged signals, dispatched
  before the numeric-transferId guard since saved-device events identify
  their subject by peer endpoint or a string transfer id.

Experience:
- Screen lists saved devices and outstanding consent requests only; the
  global targeted-transfer history stays out, reachable per device.
- Details as a sheet with detents on compact layouts and a native inspector
  on macOS, owning Send, label, forget/block and that device's transfers.
- Label editing is transactional: the draft and editor survive a failed
  write, conflicting actions are refused while saving, and the editor closes
  only after the core confirms.
- Pairing and targeted-offer consent hosted at the app root, answerable from
  any tab and suppressed while a transfer approval is up. Dismissing a
  pairing prompt suppresses locally without consuming the single-use
  eligibility; dismissing an offer declines it, since an unanswered offer
  holds a slot in the core's bounded per-sender queue.
- Targeted send reuses the invitation composer's affordances with file,
  folder, rename, replace and cleanup parity. Picker copies are released on
  replace/remove/clear/cancel and after a successful create, but kept after a
  failure so retry does not require re-picking.
- Notifications for pairing requests and offers (withdrawn once answered) and
  for terminal targeted transfers. Wording follows direction: on the sending
  device the peer finished receiving, not us.

Localization:
- Widens 52 saved-device keys from kmp-only to both platforms.
- Five keys carried a literal %1$s with no declared args, which Compose
  renders positionally but the Apple generator emits as a plain constant,
  leaking the placeholder into the UI. They now use named args; Compose
  output is byte-identical.
- Adds targeted_offer_title/body. Reusing the invitation approval copy stated
  the roles backwards, announcing the sender as the receiver.

Also surfaces core startup failures: the startup overlay is drawn above the
snackbar host, so a failed initialize() was indistinguishable from an app
that never finished loading. AppModel now keeps the reason, logs it, and the
overlay shows it with a retry, plus the technical detail in DEBUG builds.

Send and receive between two devices is verified only partially; a missing
endpoint-identity credential currently blocks startup on the test device.
This commit is contained in:
2026-08-13 19:42:37 +02:00
parent bece2af179
commit 8bb1442338
33 changed files with 3837 additions and 247 deletions

View File

@@ -1696,10 +1696,7 @@
"pl": "Urządzenia",
"nl": "Apparaten",
"ru": "Устройства"
},
"targets": [
"kmp"
]
}
},
"nav_send": {
"context": "Bottom navigation: Send tab label.",
@@ -3970,6 +3967,35 @@
"ru": "Все передачи удалены"
}
},
"targeted_offer_body": {
"context": "Targeted transfer offer prompt: body. {device} is the sending saved device, {transferName} is the transfer name.",
"args": [{"name": "device", "type": "string"}, {"name": "transferName", "type": "string"}],
"translations": {
"en": "{device} wants to send you “{transferName}”.",
"fr": "{device} veut vous envoyer « {transferName} ».",
"es": "{device} quiere enviarle “{transferName}”.",
"it": "{device} vuole inviarle “{transferName}”.",
"de": "{device} möchte Ihnen „{transferName}“ senden.",
"pt": "{device} quer enviar-lhe “{transferName}”.",
"pl": "{device} chce wysłać Ci „{transferName}”.",
"nl": "{device} wil u “{transferName}” sturen.",
"ru": "{device} хочет отправить вам «{transferName}»."
}
},
"targeted_offer_title": {
"context": "Targeted transfer offer prompt: title when a saved device wants to send files to this device.",
"translations": {
"en": "Incoming transfer",
"fr": "Transfert entrant",
"es": "Transferencia entrante",
"it": "Trasferimento in arrivo",
"de": "Eingehende Übertragung",
"pt": "Transferência a receber",
"pl": "Transfer przychodzący",
"nl": "Inkomende overdracht",
"ru": "Входящая передача"
}
},
"transfer_activity_description": {
"context": "Transfer details: description under the Activity section.",
"translations": {
@@ -4840,8 +4866,7 @@
"pl": "Wymaga uwagi",
"nl": "Aandacht vereist",
"ru": "Требует внимания"
},
"targets": ["kmp"]
}
},
"saved_devices_transfers_title": {
"context": "Saved devices screen: heading for durable targeted-transfer history.",
@@ -4855,8 +4880,7 @@
"pl": "Transfery bezpośrednie",
"nl": "Directe overdrachten",
"ru": "Прямые передачи"
},
"targets": ["kmp"]
}
},
"saved_devices_transfer_empty": {
"context": "Saved devices screen: empty targeted-transfer history.",
@@ -4870,8 +4894,7 @@
"pl": "Brak transferów bezpośrednich.",
"nl": "Nog geen directe overdrachten.",
"ru": "Прямых передач пока нет."
},
"targets": ["kmp"]
}
},
"saved_devices_transfer_direction_incoming": {
"context": "Targeted transfer card: incoming direction followed by the saved-device display name.",
@@ -4886,8 +4909,7 @@
"pl": "Od {device}",
"nl": "Van {device}",
"ru": "От {device}"
},
"targets": ["kmp"]
}
},
"saved_devices_transfer_direction_outgoing": {
"context": "Targeted transfer card: outgoing direction followed by the saved-device display name.",
@@ -4902,8 +4924,7 @@
"pl": "Do {device}",
"nl": "Naar {device}",
"ru": "Для {device}"
},
"targets": ["kmp"]
}
},
"saved_devices_transfer_files": {
"context": "Targeted transfer card metadata. First placeholder is file count, second is formatted total size.",
@@ -4918,8 +4939,7 @@
"pl": "{count} plików · {size}",
"nl": "{count} bestanden · {size}",
"ru": "{count} файлов · {size}"
},
"targets": ["kmp"]
}
},
"saved_devices_transfer_progress": {
"context": "Targeted transfer card progress. First placeholder is verified bytes, second is total size.",
@@ -4934,97 +4954,81 @@
"pl": "{verified} z {total}",
"nl": "{verified} van {total}",
"ru": "{verified} из {total}"
},
"targets": ["kmp"]
}
},
"saved_devices_transfer_receive": {
"context": "Targeted transfer history action: receive an approved transfer.",
"translations": {"en": "Receive", "fr": "Recevoir", "es": "Recibir", "it": "Ricevi", "de": "Empfangen", "pt": "Receber", "pl": "Odbierz", "nl": "Ontvangen", "ru": "Получить"},
"targets": ["kmp"]
"translations": {"en": "Receive", "fr": "Recevoir", "es": "Recibir", "it": "Ricevi", "de": "Empfangen", "pt": "Receber", "pl": "Odbierz", "nl": "Ontvangen", "ru": "Получить"}
},
"saved_devices_transfer_resume": {
"context": "Targeted transfer history action: resume an interrupted transfer.",
"translations": {"en": "Resume", "fr": "Reprendre", "es": "Reanudar", "it": "Riprendi", "de": "Fortsetzen", "pt": "Retomar", "pl": "Wznów", "nl": "Hervatten", "ru": "Продолжить"},
"targets": ["kmp"]
"translations": {"en": "Resume", "fr": "Reprendre", "es": "Reanudar", "it": "Riprendi", "de": "Fortsetzen", "pt": "Retomar", "pl": "Wznów", "nl": "Hervatten", "ru": "Продолжить"}
},
"saved_devices_transfer_cancel": {
"context": "Targeted transfer history action: cancel an active transfer.",
"translations": {"en": "Cancel", "fr": "Annuler", "es": "Cancelar", "it": "Annulla", "de": "Abbrechen", "pt": "Cancelar", "pl": "Anuluj", "nl": "Annuleren", "ru": "Отменить"},
"targets": ["kmp"]
"translations": {"en": "Cancel", "fr": "Annuler", "es": "Cancelar", "it": "Annulla", "de": "Abbrechen", "pt": "Cancelar", "pl": "Anuluj", "nl": "Annuleren", "ru": "Отменить"}
},
"saved_devices_transfer_delete": {
"context": "Targeted transfer history action: delete a terminal transfer record.",
"translations": {"en": "Delete", "fr": "Supprimer", "es": "Eliminar", "it": "Elimina", "de": "Löschen", "pt": "Eliminar", "pl": "Usuń", "nl": "Verwijderen", "ru": "Удалить"},
"targets": ["kmp"]
"translations": {"en": "Delete", "fr": "Supprimer", "es": "Eliminar", "it": "Elimina", "de": "Löschen", "pt": "Eliminar", "pl": "Usuń", "nl": "Verwijderen", "ru": "Удалить"}
},
"status_offering": {
"context": "Targeted transfer status: offering to the saved device.",
"translations": {"en": "Offering", "fr": "Proposition", "es": "Ofreciendo", "it": "Proposta", "de": "Wird angeboten", "pt": "A propor", "pl": "Oferowanie", "nl": "Aanbieden", "ru": "Предложение"},
"targets": ["kmp"]
"translations": {"en": "Offering", "fr": "Proposition", "es": "Ofreciendo", "it": "Proposta", "de": "Wird angeboten", "pt": "A propor", "pl": "Oferowanie", "nl": "Aanbieden", "ru": "Предложение"}
},
"status_awaiting_approval": {
"context": "Targeted transfer status: waiting for the saved device to approve.",
"translations": {"en": "Waiting for approval", "fr": "En attente dapprobation", "es": "Esperando aprobación", "it": "In attesa di approvazione", "de": "Wartet auf Genehmigung", "pt": "A aguardar aprovação", "pl": "Oczekiwanie na zgodę", "nl": "Wachten op goedkeuring", "ru": "Ожидание подтверждения"},
"targets": ["kmp"]
"translations": {"en": "Waiting for approval", "fr": "En attente dapprobation", "es": "Esperando aprobación", "it": "In attesa di approvazione", "de": "Wartet auf Genehmigung", "pt": "A aguardar aprovação", "pl": "Oczekiwanie na zgodę", "nl": "Wachten op goedkeuring", "ru": "Ожидание подтверждения"}
},
"status_approved": {
"context": "Targeted transfer status: approved by the receiving saved device.",
"translations": {"en": "Approved", "fr": "Approuvé", "es": "Aprobado", "it": "Approvato", "de": "Genehmigt", "pt": "Aprovado", "pl": "Zatwierdzony", "nl": "Goedgekeurd", "ru": "Подтверждено"},
"targets": ["kmp"]
"translations": {"en": "Approved", "fr": "Approuvé", "es": "Aprobado", "it": "Approvato", "de": "Genehmigt", "pt": "Aprovado", "pl": "Zatwierdzony", "nl": "Goedgekeurd", "ru": "Подтверждено"}
},
"status_connecting": {
"context": "Targeted transfer status: connecting to the saved device.",
"translations": {"en": "Connecting", "fr": "Connexion", "es": "Conectando", "it": "Connessione", "de": "Verbindung wird hergestellt", "pt": "A ligar", "pl": "Łączenie", "nl": "Verbinden", "ru": "Подключение"},
"targets": ["kmp"]
"translations": {"en": "Connecting", "fr": "Connexion", "es": "Conectando", "it": "Connessione", "de": "Verbindung wird hergestellt", "pt": "A ligar", "pl": "Łączenie", "nl": "Verbinden", "ru": "Подключение"}
},
"status_interrupted": {
"context": "Targeted transfer status: interrupted and resumable.",
"translations": {"en": "Interrupted", "fr": "Interrompu", "es": "Interrumpido", "it": "Interrotto", "de": "Unterbrochen", "pt": "Interrompido", "pl": "Przerwany", "nl": "Onderbroken", "ru": "Прервано"},
"targets": ["kmp"]
"translations": {"en": "Interrupted", "fr": "Interrompu", "es": "Interrumpido", "it": "Interrotto", "de": "Unterbrochen", "pt": "Interrompido", "pl": "Przerwany", "nl": "Onderbroken", "ru": "Прервано"}
},
"status_transferring": {
"context": "Targeted transfer status: authenticated content transfer is active.",
"translations": {"en": "Transferring", "fr": "Transfert", "es": "Transfiriendo", "it": "Trasferimento", "de": "Übertragung läuft", "pt": "A transferir", "pl": "Przesyłanie", "nl": "Overdragen", "ru": "Передача"},
"targets": ["kmp"]
"translations": {"en": "Transferring", "fr": "Transfert", "es": "Transfiriendo", "it": "Trasferimento", "de": "Übertragung läuft", "pt": "A transferir", "pl": "Przesyłanie", "nl": "Overdragen", "ru": "Передача"}
},
"status_declined": {
"context": "Targeted transfer status: declined by the saved device.",
"translations": {"en": "Declined", "fr": "Refusé", "es": "Rechazado", "it": "Rifiutato", "de": "Abgelehnt", "pt": "Recusado", "pl": "Odrzucony", "nl": "Geweigerd", "ru": "Отклонено"},
"targets": ["kmp"]
"translations": {"en": "Declined", "fr": "Refusé", "es": "Rechazado", "it": "Rifiutato", "de": "Abgelehnt", "pt": "Recusado", "pl": "Odrzucony", "nl": "Geweigerd", "ru": "Отклонено"}
},
"saved_devices_authenticated_name": {
"context": "Saved-device card: authenticated name shared by the remote app when a local label is also shown.",
"args": [{"name": "name", "type": "string"}],
"translations": {
"en": "Remote name: %1$s",
"fr": "Nom distant : %1$s",
"es": "Nombre remoto: %1$s",
"it": "Nome remoto: %1$s",
"de": "Remote-Name: %1$s",
"pt": "Nome remoto: %1$s",
"pl": "Nazwa zdalna: %1$s",
"nl": "Externe naam: %1$s",
"ru": "Имя устройства: %1$s"
},
"targets": [
"kmp"
]
"en": "Remote name: {name}",
"fr": "Nom distant : {name}",
"es": "Nombre remoto: {name}",
"it": "Nome remoto: {name}",
"de": "Remote-Name: {name}",
"pt": "Nome remoto: {name}",
"pl": "Nazwa zdalna: {name}",
"nl": "Externe naam: {name}",
"ru": "Имя устройства: {name}"
}
},
"saved_devices_block_confirm_body": {
"context": "Confirmation message before blocking a saved device. Placeholder is the display name.",
"args": [{"name": "device", "type": "string"}],
"translations": {
"en": "Block %1$s and reject future saved-device transfers?",
"fr": "Bloquer %1$s et refuser les futurs transferts dappareil enregistré ?",
"es": "¿Bloquear a %1$s y rechazar futuros envíos de dispositivos guardados?",
"it": "Bloccare %1$s e rifiutare i futuri trasferimenti da dispositivi salvati?",
"de": "%1$s blockieren und künftige Übertragungen gespeicherter Geräte ablehnen?",
"pt": "Bloquear %1$s e rejeitar futuras transferências de dispositivos guardados?",
"pl": "Zablokować %1$s i odrzucać przyszłe transfery z zapisanych urządzeń?",
"nl": "%1$s blokkeren en toekomstige overdrachten van opgeslagen apparaten weigeren?",
"ru": "Заблокировать %1$s и отклонять будущие передачи с сохранённых устройств?"
},
"targets": [
"kmp"
]
"en": "Block {device} and reject future saved-device transfers?",
"fr": "Bloquer {device} et refuser les futurs transferts dappareil enregistré ?",
"es": "¿Bloquear a {device} y rechazar futuros envíos de dispositivos guardados?",
"it": "Bloccare {device} e rifiutare i futuri trasferimenti da dispositivi salvati?",
"de": "{device} blockieren und künftige Übertragungen gespeicherter Geräte ablehnen?",
"pt": "Bloquear {device} e rejeitar futuras transferências de dispositivos guardados?",
"pl": "Zablokować {device} i odrzucać przyszłe transfery z zapisanych urządzeń?",
"nl": "{device} blokkeren en toekomstige overdrachten van opgeslagen apparaten weigeren?",
"ru": "Заблокировать {device} и отклонять будущие передачи с сохранённых устройств?"
}
},
"saved_devices_block_confirm_title": {
"context": "Confirmation dialog title before blocking a saved device.",
@@ -5038,27 +5042,22 @@
"pl": "Zablokować urządzenie?",
"nl": "Apparaat blokkeren?",
"ru": "Заблокировать устройство?"
},
"targets": [
"kmp"
]
}
},
"saved_devices_endpoint": {
"context": "Saved-device screen: secondary diagnostic endpoint identity. Placeholder is a shortened endpoint ID.",
"args": [{"name": "deviceId", "type": "string"}],
"translations": {
"en": "Device ID: %1$s",
"fr": "ID de lappareil : %1$s",
"es": "ID del dispositivo: %1$s",
"it": "ID dispositivo: %1$s",
"de": "Geräte-ID: %1$s",
"pt": "ID do dispositivo: %1$s",
"pl": "ID urządzenia: %1$s",
"nl": "Apparaat-ID: %1$s",
"ru": "ID устройства: %1$s"
},
"targets": [
"kmp"
]
"en": "Device ID: {deviceId}",
"fr": "ID de lappareil : {deviceId}",
"es": "ID del dispositivo: {deviceId}",
"it": "ID dispositivo: {deviceId}",
"de": "Geräte-ID: {deviceId}",
"pt": "ID do dispositivo: {deviceId}",
"pl": "ID urządzenia: {deviceId}",
"nl": "Apparaat-ID: {deviceId}",
"ru": "ID устройства: {deviceId}"
}
},
"saved_devices_empty": {
"context": "Saved devices screen: guidance shown when no device has been saved.",
@@ -5072,10 +5071,7 @@
"pl": "Ukończ transfer, a następnie zapisz drugie urządzenie.",
"nl": "Voltooi een overdracht en kies daarna om het andere apparaat te onthouden.",
"ru": "Завершите передачу, затем сохраните другое устройство."
},
"targets": [
"kmp"
]
}
},
"saved_devices_empty_title": {
"context": "Saved devices screen: title for the empty state.",
@@ -5089,25 +5085,22 @@
"pl": "Brak zapisanych urządzeń",
"nl": "Geen opgeslagen apparaten",
"ru": "Нет сохранённых устройств"
},
"targets": ["kmp"]
}
},
"saved_devices_forget_confirm_body": {
"context": "Confirmation message before forgetting a saved device. Placeholder is the display name.",
"args": [{"name": "device", "type": "string"}],
"translations": {
"en": "Forget %1$s? You will both need to approve saving again before another direct transfer.",
"fr": "Oublier %1$s ? Vous devrez tous les deux approuver à nouveau lenregistrement avant un autre transfert direct.",
"es": "¿Olvidar a %1$s? Ambos deberán volver a aprobar el guardado antes de otra transferencia directa.",
"it": "Dimenticare %1$s? Entrambi dovrete approvare nuovamente il salvataggio prima di un altro trasferimento diretto.",
"de": "%1$s vergessen? Vor einer weiteren direkten Übertragung müssen beide das Speichern erneut bestätigen.",
"pt": "Esquecer %1$s? Ambos terão de voltar a aprovar antes de outra transferência direta.",
"pl": "Zapomnieć %1$s? Przed kolejnym transferem bezpośrednim obie strony muszą ponownie zatwierdzić zapisanie.",
"nl": "%1$s vergeten? Jullie moeten het opslaan allebei opnieuw goedkeuren voor een volgende directe overdracht.",
"ru": "Забыть %1$s? Перед следующей прямой передачей сохранение снова должны подтвердить обе стороны."
},
"targets": [
"kmp"
]
"en": "Forget {device}? You will both need to approve saving again before another direct transfer.",
"fr": "Oublier {device} ? Vous devrez tous les deux approuver à nouveau lenregistrement avant un autre transfert direct.",
"es": "¿Olvidar a {device}? Ambos deberán volver a aprobar el guardado antes de otra transferencia directa.",
"it": "Dimenticare {device}? Entrambi dovrete approvare nuovamente il salvataggio prima di un altro trasferimento diretto.",
"de": "{device} vergessen? Vor einer weiteren direkten Übertragung müssen beide das Speichern erneut bestätigen.",
"pt": "Esquecer {device}? Ambos terão de voltar a aprovar antes de outra transferência direta.",
"pl": "Zapomnieć {device}? Przed kolejnym transferem bezpośrednim obie strony muszą ponownie zatwierdzić zapisanie.",
"nl": "{device} vergeten? Jullie moeten het opslaan allebei opnieuw goedkeuren voor een volgende directe overdracht.",
"ru": "Забыть {device}? Перед следующей прямой передачей сохранение снова должны подтвердить обе стороны."
}
},
"saved_devices_forget_confirm_title": {
"context": "Confirmation dialog title before forgetting a saved device.",
@@ -5121,10 +5114,7 @@
"pl": "Zapomnieć urządzenie?",
"nl": "Apparaat vergeten?",
"ru": "Забыть устройство?"
},
"targets": [
"kmp"
]
}
},
"saved_devices_eligibility_title": {
"context": "Saved devices screen: section for peers eligible to pair after a transfer.",
@@ -5138,10 +5128,7 @@
"pl": "Ready to remember",
"nl": "Ready to remember",
"ru": "Ready to remember"
},
"targets": [
"kmp"
]
}
},
"saved_devices_pending_title": {
"context": "Saved devices screen: section for in-progress pairing.",
@@ -5155,10 +5142,7 @@
"pl": "Pending pairing",
"nl": "Pending pairing",
"ru": "Pending pairing"
},
"targets": [
"kmp"
]
}
},
"saved_devices_list_title": {
"context": "Saved devices screen title and saved-device list heading.",
@@ -5172,10 +5156,7 @@
"pl": "Saved devices",
"nl": "Saved devices",
"ru": "Saved devices"
},
"targets": [
"kmp"
]
}
},
"saved_devices_load_failed": {
"context": "Saved devices screen: inline error when device state could not be loaded.",
@@ -5189,10 +5170,7 @@
"pl": "Nie udało się wczytać zapisanych urządzeń.",
"nl": "Opgeslagen apparaten konden niet worden geladen.",
"ru": "Не удалось загрузить сохранённые устройства."
},
"targets": [
"kmp"
]
}
},
"saved_devices_loading": {
"context": "Saved devices screen: loading state label.",
@@ -5206,27 +5184,22 @@
"pl": "Wczytywanie zapisanych urządzeń…",
"nl": "Opgeslagen apparaten laden…",
"ru": "Загрузка сохранённых устройств…"
},
"targets": [
"kmp"
]
}
},
"saved_devices_more_actions": {
"context": "Accessibility label for the saved-device overflow menu. Placeholder is the display name.",
"args": [{"name": "device", "type": "string"}],
"translations": {
"en": "More actions for %1$s",
"fr": "Plus dactions pour %1$s",
"es": "Más acciones para %1$s",
"it": "Altre azioni per %1$s",
"de": "Weitere Aktionen für %1$s",
"pt": "Mais ações para %1$s",
"pl": "Więcej działań dla %1$s",
"nl": "Meer acties voor %1$s",
"ru": "Другие действия для %1$s"
},
"targets": [
"kmp"
]
"en": "More actions for {device}",
"fr": "Plus dactions pour {device}",
"es": "Más acciones para {device}",
"it": "Altre azioni per {device}",
"de": "Weitere Aktionen für {device}",
"pt": "Mais ações para {device}",
"pl": "Więcej działań dla {device}",
"nl": "Meer acties voor {device}",
"ru": "Другие действия для {device}"
}
},
"saved_devices_no_pending": {
"context": "Desktop Saved devices screen: status when no pairing decision is waiting.",
@@ -5240,10 +5213,7 @@
"pl": "Żadne prośby o sparowanie nie wymagają uwagi.",
"nl": "Er zijn geen koppelverzoeken die aandacht nodig hebben.",
"ru": "Нет запросов на сопряжение, требующих внимания."
},
"targets": [
"kmp"
]
}
},
"saved_devices_unnamed": {
"context": "Fallback display when a saved device has no local label or remote name.",
@@ -5257,10 +5227,7 @@
"pl": "Saved device",
"nl": "Saved device",
"ru": "Saved device"
},
"targets": [
"kmp"
]
}
},
"saved_devices_remember_action": {
"context": "Action that starts mutual-consent pairing for an eligible peer.",
@@ -5274,10 +5241,7 @@
"pl": "Remember",
"nl": "Remember",
"ru": "Remember"
},
"targets": [
"kmp"
]
}
},
"saved_devices_decline_action": {
"context": "Action that declines pairing eligibility for a peer.",
@@ -5291,10 +5255,7 @@
"pl": "Decline",
"nl": "Decline",
"ru": "Decline"
},
"targets": [
"kmp"
]
}
},
"saved_devices_accept_pairing_action": {
"context": "Action that accepts an incoming pairing request.",
@@ -5308,10 +5269,7 @@
"pl": "Accept",
"nl": "Accept",
"ru": "Accept"
},
"targets": [
"kmp"
]
}
},
"saved_devices_send_action": {
"context": "Action that starts a targeted send to a saved device.",
@@ -5325,10 +5283,7 @@
"pl": "Send files",
"nl": "Send files",
"ru": "Send files"
},
"targets": [
"kmp"
]
}
},
"saved_devices_label_action": {
"context": "Action that edits the local label for a saved device.",
@@ -5342,10 +5297,7 @@
"pl": "Label",
"nl": "Label",
"ru": "Label"
},
"targets": [
"kmp"
]
}
},
"saved_devices_forget_action": {
"context": "Action that forgets a saved device relationship.",
@@ -5359,10 +5311,7 @@
"pl": "Forget",
"nl": "Forget",
"ru": "Forget"
},
"targets": [
"kmp"
]
}
},
"saved_devices_block_action": {
"context": "Action that blocks a device identity.",
@@ -5376,10 +5325,7 @@
"pl": "Block",
"nl": "Block",
"ru": "Block"
},
"targets": [
"kmp"
]
}
},
"saved_devices_label_title": {
"context": "Dialog title for editing a saved device local label.",
@@ -5393,10 +5339,7 @@
"pl": "Device label",
"nl": "Device label",
"ru": "Device label"
},
"targets": [
"kmp"
]
}
},
"saved_devices_label_placeholder": {
"context": "Placeholder for the local label text field.",
@@ -5410,10 +5353,7 @@
"pl": "Label",
"nl": "Label",
"ru": "Label"
},
"targets": [
"kmp"
]
}
},
"saved_devices_label_save": {
"context": "Button that saves the local device label.",
@@ -5427,10 +5367,7 @@
"pl": "Save",
"nl": "Save",
"ru": "Save"
},
"targets": [
"kmp"
]
}
},
"saved_devices_label_clear": {
"context": "Button that clears the local device label.",
@@ -5444,10 +5381,7 @@
"pl": "Clear label",
"nl": "Clear label",
"ru": "Clear label"
},
"targets": [
"kmp"
]
}
},
"saved_devices_pending_outgoing": {
"context": "Status text while waiting for the peer to accept pairing.",
@@ -5461,10 +5395,7 @@
"pl": "Waiting for the other device",
"nl": "Waiting for the other device",
"ru": "Waiting for the other device"
},
"targets": [
"kmp"
]
}
},
"saved_devices_pending_incoming": {
"context": "Status text when this device must accept a pairing request.",
@@ -5478,10 +5409,7 @@
"pl": "Wants to remember this device",
"nl": "Wants to remember this device",
"ru": "Wants to remember this device"
},
"targets": [
"kmp"
]
}
},
"saved_devices_send_started": {
"context": "Snackbar after creating a targeted transfer offer.",
@@ -5495,10 +5423,7 @@
"pl": "Transfer offer sent",
"nl": "Transfer offer sent",
"ru": "Transfer offer sent"
},
"targets": [
"kmp"
]
}
},
"saved_devices_forgotten": {
"context": "Snackbar after forgetting a saved device.",
@@ -5512,10 +5437,7 @@
"pl": "Saved device forgotten",
"nl": "Saved device forgotten",
"ru": "Saved device forgotten"
},
"targets": [
"kmp"
]
}
},
"saved_devices_blocked": {
"context": "Snackbar after blocking a device.",
@@ -5529,10 +5451,7 @@
"pl": "Device blocked",
"nl": "Device blocked",
"ru": "Device blocked"
},
"targets": [
"kmp"
]
}
},
"saved_devices_labeled": {
"context": "Snackbar after updating a saved device label.",
@@ -5546,10 +5465,7 @@
"pl": "Label updated",
"nl": "Label updated",
"ru": "Label updated"
},
"targets": [
"kmp"
]
}
}
}
}