This commit is contained in:
ericszimmermann 2026-04-15 16:50:24 +00:00 committed by GitHub
commit a15feadeb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 1462 additions and 33 deletions

View file

@ -356,6 +356,26 @@ class MeshCoreConnector extends ChangeNotifier {
return List.unmodifiable(_discoveredContacts);
}
String exportDiscoveredContactsJson() {
return _discoveryContactStore.exportContactsJson(_discoveredContacts);
}
Future<int> importDiscoveredContactsJson(String json) async {
final newCount = _discoveryContactStore.importContactsJson(
json: json,
existingContacts: _discoveredContacts,
knownContactKeys: _knownContactKeys,
);
if (newCount == 0 && _discoveredContacts.isEmpty) {
return 0;
}
await _persistDiscoveredContacts();
notifyListeners();
return newCount;
}
List<Channel> get channels => List.unmodifiable(_channels);
bool get isConnected => _state == MeshCoreConnectionState.connected;
bool get isLoadingContacts => _isLoadingContacts;

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_copyContact": "Копирай контакт в клипборда",
"discoveredContacts_deleteContact": "Изтрий контакт",
"discoveredContacts_addContact": "Добави контакт",
"discoveredContacts_export": "Експортирай откритите контакти",
"discoveredContacts_import": "Импортирай откритите контакти",
"discoveredContacts_exported": "Откритите контакти са експортирани в {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Неуспешно експортиране на откритите контакти: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Импортирани са {count} открити контакта.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Във файла за импортиране не са намерени контакти.",
"discoveredContacts_importFailed": "Неуспешно импортиране на откритите контакти: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Когато списъкът с контакти е пълен, най-старият неключов контакт ще бъде заменен.",
"discoveredContacts_deleteContactAll": "Изтриване на Всички Открити Контакти",
"discoveredContacts_deleteContactAllContent": "Сигурни ли сте, че искате да изтриете всички открити контакти?",

View file

@ -1856,6 +1856,41 @@
"common_deleteAll": "Alles löschen",
"discoveredContacts_deleteContactAllContent": "Sind Sie sicher, dass Sie alle gefundenen Kontakte löschen möchten?",
"discoveredContacts_deleteContactAll": "Alle entdeckten Kontakte löschen",
"discoveredContacts_export": "Entdeckte Kontakte exportieren",
"discoveredContacts_import": "Entdeckte Kontakte importieren",
"discoveredContacts_exported": "Entdeckte Kontakte exportiert nach {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Export von entdeckten Kontakten fehlgeschlagen: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "{count} entdeckte Kontakte importiert.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Keine Kontakte in Importdatei gefunden.",
"discoveredContacts_importFailed": "Import von entdeckten Kontakten fehlgeschlagen: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"map_showGuessedLocations": "Zeige die vermuteten Knotenpositionen",
"map_guessedLocation": "Geschätzter Ort",
"usbScreenSubtitle": "Wählen Sie ein erkannten serielles Gerät aus und verbinden Sie es direkt mit Ihrem MeshCore-Knoten.",

View file

@ -1922,6 +1922,41 @@
"contacts_invalidAdvertFormat": "Invalid contact data",
"contacts_contactImported": "Contact has been imported.",
"contacts_contactImportFailed": "Failed to import contact.",
"discoveredContacts_export": "Export discovered contacts",
"discoveredContacts_import": "Import discovered contacts",
"discoveredContacts_exported": "Exported discovered contacts to {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Failed to export discovered contacts: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Imported {count} discovered contacts.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "No contacts found in import file.",
"discoveredContacts_importFailed": "Failed to import discovered contacts: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contacts_zeroHopAdvert": "Zero Hop Advert",
"contacts_floodAdvert": "Flood Advert",
"contacts_copyAdvertToClipboard": "Copy Advert to Clipboard",
@ -2068,7 +2103,6 @@
"radioStats_stripWaiting": "Fetching radio stats…",
"radioStats_settingsTile": "Radio stats",
"radioStats_settingsSubtitle": "Noise floor, RSSI, SNR, and airtime",
"translation_title": "Translation",
"translation_enableTitle": "Enable translation",
"translation_enableSubtitle": "Translate incoming messages and allow pre-send translation.",

View file

@ -1856,6 +1856,41 @@
"common_deleteAll": "Eliminar todo",
"discoveredContacts_deleteContactAll": "Eliminar Todos los Contactos Descubiertos",
"discoveredContacts_deleteContactAllContent": "¿Está seguro de que desea eliminar todos los contactos descubiertos!",
"discoveredContacts_export": "Exportar contactos descubiertos",
"discoveredContacts_import": "Importar contactos descubiertos",
"discoveredContacts_exported": "Contactos descubiertos exportados a {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Error al exportar contactos descubiertos: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "{count} contactos descubiertos importados.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "No se encontraron contactos en el archivo de importación.",
"discoveredContacts_importFailed": "Error al importar contactos descubiertos: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"map_guessedLocation": "Ubicación estimada",
"map_showGuessedLocations": "Mostrar las ubicaciones estimadas de los nodos.",
"usbScreenTitle": "Conecte mediante USB",

View file

@ -1828,6 +1828,41 @@
"common_deleteAll": "Supprimer tout",
"discoveredContacts_deleteContactAll": "Supprimer tous les contacts découverts",
"discoveredContacts_deleteContactAllContent": "Êtes-vous sûr de vouloir supprimer tous les contacts découverts ?",
"discoveredContacts_export": "Exporter les contacts découverts",
"discoveredContacts_import": "Importer les contacts découverts",
"discoveredContacts_exported": "Contacts découverts exportés vers {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Impossible d'exporter les contacts découverts: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "{count} contacts découverts importés.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Aucun contact trouvé dans le fichier d'importation.",
"discoveredContacts_importFailed": "Impossible d'importer les contacts découverts: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"map_showGuessedLocations": "Afficher les emplacements des nœuds estimés",
"map_guessedLocation": "Lieu deviné",
"connectionChoiceUsbLabel": "USB",

View file

@ -1949,6 +1949,41 @@
"discoveredContacts_deleteContact": "Törölj a feltalált kapcsolatot",
"discoveredContacts_deleteContactAll": "Törölj minden megtalált kapcsolatot",
"discoveredContacts_deleteContactAllContent": "Biztos, hogy szeretné törölni az összes eddig megtalált kapcsolatot?",
"discoveredContacts_export": "Felfedezett kapcsolatok exportálása",
"discoveredContacts_import": "Felfedezett kapcsolatok importálása",
"discoveredContacts_exported": "A felfedezett kapcsolatok exportálva ide: {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "A felfedezett kapcsolatok exportálása nem sikerült: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "{count} felfedezett kapcsolat importálva.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Az importfájlban nem találhatók kapcsolatok.",
"discoveredContacts_importFailed": "A felfedezett kapcsolatok importálása nem sikerült: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"chat_sendCooldown": "Kérjük, várjon egy pillanatot, mielőtt újra elküldené.",
"appSettings_jumpToOldestUnread": "Jelentkezzen az legörebb, olvasatlan üzenetre",
"appSettings_jumpToOldestUnreadSubtitle": "Amikor egy új csevet indítunk, amelyben vannak olvashatatlan üzenetek, görgessük a listát, hogy a legelső, olvashatatlan üzenet megjelenjen, nem pedig az utolsó.",

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_contactAdded": "Contatto aggiunto",
"discoveredContacts_deleteContact": "Elimina Contatto",
"discoveredContacts_copyContact": "Copia contatto negli appunti",
"discoveredContacts_export": "Esporta contatti scoperti",
"discoveredContacts_import": "Importa contatti scoperti",
"discoveredContacts_exported": "Contatti scoperti esportati in {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Esportazione dei contatti scoperti non riuscita: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Importati {count} contatti scoperti.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Nessun contatto trovato nel file di importazione.",
"discoveredContacts_importFailed": "Importazione dei contatti scoperti non riuscita: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Quando l'elenco dei contatti è pieno, il contatto più vecchio non tra i preferiti verrà sostituito.",
"common_deleteAll": "Elimina tutto",
"discoveredContacts_deleteContactAllContent": "Sei sicuro di voler eliminare tutti i contatti scoperti?",

View file

@ -1949,6 +1949,41 @@
"discoveredContacts_deleteContact": "発見された連絡先を削除",
"discoveredContacts_deleteContactAll": "発見されたすべての連絡先を削除",
"discoveredContacts_deleteContactAllContent": "本当に、見つけたすべての連絡先を削除してもよろしいですか?",
"discoveredContacts_export": "発見済みの連絡先をエクスポート",
"discoveredContacts_import": "発見済みの連絡先をインポート",
"discoveredContacts_exported": "発見済みの連絡先を {path} にエクスポートしました。",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "発見済みの連絡先のエクスポートに失敗しました: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "{count} 件の発見済み連絡先をインポートしました。",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "インポートファイルに連絡先が見つかりませんでした。",
"discoveredContacts_importFailed": "発見済みの連絡先のインポートに失敗しました: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"chat_sendCooldown": "再度送信する前に、しばらくお待ちください。",
"appSettings_jumpToOldestUnread": "最も古い未読のメッセージへ移動",
"appSettings_jumpToOldestUnreadSubtitle": "未読メッセージがあるチャットを開く際、「最新のメッセージ」ではなく、最初に未読のメッセージまでスクロールしてください。",

View file

@ -1949,6 +1949,41 @@
"discoveredContacts_deleteContact": "발견된 연락처 삭제",
"discoveredContacts_deleteContactAll": "발견된 모든 연락처 삭제",
"discoveredContacts_deleteContactAllContent": "정말로 모든 검색된 연락처를 삭제하시겠습니까?",
"discoveredContacts_export": "발견된 연락처 내보내기",
"discoveredContacts_import": "발견된 연락처 가져오기",
"discoveredContacts_exported": "발견된 연락처를 {path}(으)로 내보냈습니다.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "발견된 연락처 내보내기에 실패했습니다: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "발견된 연락처 {count}개를 가져왔습니다.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "가져오기 파일에서 연락처를 찾을 수 없습니다.",
"discoveredContacts_importFailed": "발견된 연락처 가져오기에 실패했습니다: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"chat_sendCooldown": "다시 보내기 전에 잠시 기다려 주시기 바랍니다.",
"appSettings_jumpToOldestUnread": "가장 오래된, 아직 읽지 않은 항목으로 이동",
"appSettings_jumpToOldestUnreadSubtitle": "새로운 메시지가 없는 채팅을 열 때, 최신 메시지가 아닌 첫 번째 읽지 않은 메시지로 스크롤하세요.",

View file

@ -5741,6 +5741,48 @@ abstract class AppLocalizations {
/// **'Failed to import contact.'**
String get contacts_contactImportFailed;
/// No description provided for @discoveredContacts_export.
///
/// In en, this message translates to:
/// **'Export discovered contacts'**
String get discoveredContacts_export;
/// No description provided for @discoveredContacts_import.
///
/// In en, this message translates to:
/// **'Import discovered contacts'**
String get discoveredContacts_import;
/// No description provided for @discoveredContacts_exported.
///
/// In en, this message translates to:
/// **'Exported discovered contacts to {path}.'**
String discoveredContacts_exported(String path);
/// No description provided for @discoveredContacts_exportFailed.
///
/// In en, this message translates to:
/// **'Failed to export discovered contacts: {error}'**
String discoveredContacts_exportFailed(String error);
/// No description provided for @discoveredContacts_imported.
///
/// In en, this message translates to:
/// **'Imported {count} discovered contacts.'**
String discoveredContacts_imported(int count);
/// No description provided for @discoveredContacts_importNoContacts.
///
/// In en, this message translates to:
/// **'No contacts found in import file.'**
String get discoveredContacts_importNoContacts;
/// No description provided for @discoveredContacts_importFailed.
///
/// In en, this message translates to:
/// **'Failed to import discovered contacts: {error}'**
String discoveredContacts_importFailed(String error);
/// No description provided for @contacts_zeroHopAdvert.
///
/// In en, this message translates to:

View file

@ -3296,6 +3296,36 @@ class AppLocalizationsBg extends AppLocalizations {
String get contacts_contactImportFailed =>
'Контактът не е успешно импортиран.';
@override
String get discoveredContacts_export => 'Експортирай откритите контакти';
@override
String get discoveredContacts_import => 'Импортирай откритите контакти';
@override
String discoveredContacts_exported(String path) {
return 'Откритите контакти са експортирани в $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Неуспешно експортиране на откритите контакти: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Импортирани са $count открити контакта.';
}
@override
String get discoveredContacts_importNoContacts =>
'Във файла за импортиране не са намерени контакти.';
@override
String discoveredContacts_importFailed(String error) {
return 'Неуспешно импортиране на откритите контакти: $error';
}
@override
String get contacts_zeroHopAdvert => 'Реклама без скок';

View file

@ -3301,6 +3301,36 @@ class AppLocalizationsDe extends AppLocalizations {
String get contacts_contactImportFailed =>
'Kontakt konnte nicht importiert werden';
@override
String get discoveredContacts_export => 'Entdeckte Kontakte exportieren';
@override
String get discoveredContacts_import => 'Entdeckte Kontakte importieren';
@override
String discoveredContacts_exported(String path) {
return 'Entdeckte Kontakte exportiert nach $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Export von entdeckten Kontakten fehlgeschlagen: $error';
}
@override
String discoveredContacts_imported(int count) {
return '$count entdeckte Kontakte importiert.';
}
@override
String get discoveredContacts_importNoContacts =>
'Keine Kontakte in Importdatei gefunden.';
@override
String discoveredContacts_importFailed(String error) {
return 'Import von entdeckten Kontakten fehlgeschlagen: $error';
}
@override
String get contacts_zeroHopAdvert => 'Zero-Hop-Ankündigung';

View file

@ -3239,6 +3239,36 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get contacts_contactImportFailed => 'Failed to import contact.';
@override
String get discoveredContacts_export => 'Export discovered contacts';
@override
String get discoveredContacts_import => 'Import discovered contacts';
@override
String discoveredContacts_exported(String path) {
return 'Exported discovered contacts to $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Failed to export discovered contacts: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Imported $count discovered contacts.';
}
@override
String get discoveredContacts_importNoContacts =>
'No contacts found in import file.';
@override
String discoveredContacts_importFailed(String error) {
return 'Failed to import discovered contacts: $error';
}
@override
String get contacts_zeroHopAdvert => 'Zero Hop Advert';

View file

@ -3296,6 +3296,36 @@ class AppLocalizationsEs extends AppLocalizations {
String get contacts_contactImportFailed =>
'Contacto no se importó correctamente.';
@override
String get discoveredContacts_export => 'Exportar contactos descubiertos';
@override
String get discoveredContacts_import => 'Importar contactos descubiertos';
@override
String discoveredContacts_exported(String path) {
return 'Contactos descubiertos exportados a $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Error al exportar contactos descubiertos: $error';
}
@override
String discoveredContacts_imported(int count) {
return '$count contactos descubiertos importados.';
}
@override
String get discoveredContacts_importNoContacts =>
'No se encontraron contactos en el archivo de importación.';
@override
String discoveredContacts_importFailed(String error) {
return 'Error al importar contactos descubiertos: $error';
}
@override
String get contacts_zeroHopAdvert => 'Anuncio de Zero Hop';

View file

@ -3313,6 +3313,36 @@ class AppLocalizationsFr extends AppLocalizations {
String get contacts_contactImportFailed =>
'Échec de l\'importation du contact.';
@override
String get discoveredContacts_export => 'Exporter les contacts découverts';
@override
String get discoveredContacts_import => 'Importer les contacts découverts';
@override
String discoveredContacts_exported(String path) {
return 'Contacts découverts exportés vers $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Impossible d\'exporter les contacts découverts: $error';
}
@override
String discoveredContacts_imported(int count) {
return '$count contacts découverts importés.';
}
@override
String get discoveredContacts_importNoContacts =>
'Aucun contact trouvé dans le fichier d\'importation.';
@override
String discoveredContacts_importFailed(String error) {
return 'Impossible d\'importer les contacts découverts: $error';
}
@override
String get contacts_zeroHopAdvert => 'Annonce Zero saut';

View file

@ -3310,6 +3310,36 @@ class AppLocalizationsHu extends AppLocalizations {
String get contacts_contactImportFailed =>
'Nem sikerült a kapcsolatot importálni.';
@override
String get discoveredContacts_export => 'Felfedezett kapcsolatok exportálása';
@override
String get discoveredContacts_import => 'Felfedezett kapcsolatok importálása';
@override
String discoveredContacts_exported(String path) {
return 'A felfedezett kapcsolatok exportálva ide: $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'A felfedezett kapcsolatok exportálása nem sikerült: $error';
}
@override
String discoveredContacts_imported(int count) {
return '$count felfedezett kapcsolat importálva.';
}
@override
String get discoveredContacts_importNoContacts =>
'Az importfájlban nem találhatók kapcsolatok.';
@override
String discoveredContacts_importFailed(String error) {
return 'A felfedezett kapcsolatok importálása nem sikerült: $error';
}
@override
String get contacts_zeroHopAdvert => 'Zero Hop reklám';

View file

@ -3299,6 +3299,36 @@ class AppLocalizationsIt extends AppLocalizations {
String get contacts_contactImportFailed =>
'Contatto non importato con successo.';
@override
String get discoveredContacts_export => 'Esporta contatti scoperti';
@override
String get discoveredContacts_import => 'Importa contatti scoperti';
@override
String discoveredContacts_exported(String path) {
return 'Contatti scoperti esportati in $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Esportazione dei contatti scoperti non riuscita: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Importati $count contatti scoperti.';
}
@override
String get discoveredContacts_importNoContacts =>
'Nessun contatto trovato nel file di importazione.';
@override
String discoveredContacts_importFailed(String error) {
return 'Importazione dei contatti scoperti non riuscita: $error';
}
@override
String get contacts_zeroHopAdvert => 'Annuncio Zero Hop';

View file

@ -3149,6 +3149,35 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get contacts_contactImportFailed => '連絡先のインポートに失敗しました。';
@override
String get discoveredContacts_export => '発見済みの連絡先をエクスポート';
@override
String get discoveredContacts_import => '発見済みの連絡先をインポート';
@override
String discoveredContacts_exported(String path) {
return '発見済みの連絡先を $path にエクスポートしました。';
}
@override
String discoveredContacts_exportFailed(String error) {
return '発見済みの連絡先のエクスポートに失敗しました: $error';
}
@override
String discoveredContacts_imported(int count) {
return '$count 件の発見済み連絡先をインポートしました。';
}
@override
String get discoveredContacts_importNoContacts => 'インポートファイルに連絡先が見つかりませんでした。';
@override
String discoveredContacts_importFailed(String error) {
return '発見済みの連絡先のインポートに失敗しました: $error';
}
@override
String get contacts_zeroHopAdvert => 'ゼロホップ広告';

View file

@ -3149,6 +3149,35 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get contacts_contactImportFailed => '연락처를 가져오지 못했습니다.';
@override
String get discoveredContacts_export => '발견된 연락처 내보내기';
@override
String get discoveredContacts_import => '발견된 연락처 가져오기';
@override
String discoveredContacts_exported(String path) {
return '발견된 연락처를 $path(으)로 내보냈습니다.';
}
@override
String discoveredContacts_exportFailed(String error) {
return '발견된 연락처 내보내기에 실패했습니다: $error';
}
@override
String discoveredContacts_imported(int count) {
return '발견된 연락처 $count개를 가져왔습니다.';
}
@override
String get discoveredContacts_importNoContacts => '가져오기 파일에서 연락처를 찾을 수 없습니다.';
@override
String discoveredContacts_importFailed(String error) {
return '발견된 연락처 가져오기에 실패했습니다: $error';
}
@override
String get contacts_zeroHopAdvert => '제로 홉 광고';

View file

@ -3280,6 +3280,36 @@ class AppLocalizationsNl extends AppLocalizations {
String get contacts_contactImportFailed =>
'Contact kon niet geïmporteerd worden.';
@override
String get discoveredContacts_export => 'Ontdekte contacten exporteren';
@override
String get discoveredContacts_import => 'Ontdekte contacten importeren';
@override
String discoveredContacts_exported(String path) {
return 'Ontdekte contacten geëxporteerd naar $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Exporteren van ontdekte contacten mislukt: $error';
}
@override
String discoveredContacts_imported(int count) {
return '$count ontdekte contacten geïmporteerd.';
}
@override
String get discoveredContacts_importNoContacts =>
'Geen contacten gevonden in het importbestand.';
@override
String discoveredContacts_importFailed(String error) {
return 'Importeren van ontdekte contacten mislukt: $error';
}
@override
String get contacts_zeroHopAdvert => 'Zero Hop Reclame';

View file

@ -3306,6 +3306,36 @@ class AppLocalizationsPl extends AppLocalizations {
String get contacts_contactImportFailed =>
'Kontakt nie został zaimportowany.';
@override
String get discoveredContacts_export => 'Eksportuj odkryte kontakty';
@override
String get discoveredContacts_import => 'Importuj odkryte kontakty';
@override
String discoveredContacts_exported(String path) {
return 'Wyeksportowano odkryte kontakty do $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Nie udało się wyeksportować odkrytych kontaktów: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Zaimportowano $count odkrytych kontaktów.';
}
@override
String get discoveredContacts_importNoContacts =>
'Nie znaleziono kontaktów w pliku importu.';
@override
String discoveredContacts_importFailed(String error) {
return 'Nie udało się zaimportować odkrytych kontaktów: $error';
}
@override
String get contacts_zeroHopAdvert => 'Rozgłoszenie zero-hop';

View file

@ -3293,6 +3293,36 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get contacts_contactImportFailed => 'Contato falhou ao ser importado.';
@override
String get discoveredContacts_export => 'Exportar contatos descobertos';
@override
String get discoveredContacts_import => 'Importar contatos descobertos';
@override
String discoveredContacts_exported(String path) {
return 'Contatos descobertos exportados para $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Falha ao exportar contatos descobertos: $error';
}
@override
String discoveredContacts_imported(int count) {
return '$count contatos descobertos importados.';
}
@override
String get discoveredContacts_importNoContacts =>
'Nenhum contato encontrado no arquivo de importação.';
@override
String discoveredContacts_importFailed(String error) {
return 'Falha ao importar contatos descobertos: $error';
}
@override
String get contacts_zeroHopAdvert => 'Anúncio Zero Hop';

View file

@ -3299,6 +3299,37 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get contacts_contactImportFailed => 'Контакт не удалось импортировать';
@override
String get discoveredContacts_export =>
'Экспортировать обнаруженные контакты';
@override
String get discoveredContacts_import => 'Импортировать обнаруженные контакты';
@override
String discoveredContacts_exported(String path) {
return 'Обнаруженные контакты экспортированы в $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Не удалось экспортировать обнаруженные контакты: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Импортировано $count обнаруженных контактов.';
}
@override
String get discoveredContacts_importNoContacts =>
'В файле импорта не найдены контакты.';
@override
String discoveredContacts_importFailed(String error) {
return 'Не удалось импортировать обнаруженные контакты: $error';
}
@override
String get contacts_zeroHopAdvert => 'Реклама Zero Hop';

View file

@ -3274,6 +3274,36 @@ class AppLocalizationsSk extends AppLocalizations {
String get contacts_contactImportFailed =>
'Kontakt sa nepodarilo importovať.';
@override
String get discoveredContacts_export => 'Exportovať objavené kontakty';
@override
String get discoveredContacts_import => 'Importovať objavené kontakty';
@override
String discoveredContacts_exported(String path) {
return 'Objavené kontakty boli exportované do $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Export objavených kontaktov zlyhal: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Bolo importovaných $count objavených kontaktov.';
}
@override
String get discoveredContacts_importNoContacts =>
'V importovanom súbore sa nenašli žiadne kontakty.';
@override
String discoveredContacts_importFailed(String error) {
return 'Import objavených kontaktov zlyhal: $error';
}
@override
String get contacts_zeroHopAdvert => 'Inzerát Zero Hop';

View file

@ -3274,6 +3274,36 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get contacts_contactImportFailed => 'Kontakt ni bil uspešno uvožen.';
@override
String get discoveredContacts_export => 'Izvozi odkrite stike';
@override
String get discoveredContacts_import => 'Uvozi odkrite stike';
@override
String discoveredContacts_exported(String path) {
return 'Odkriti stiki so izvoženi v $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Izvoz odkritih stikov ni uspel: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Uvoženih je bilo $count odkritih stikov.';
}
@override
String get discoveredContacts_importNoContacts =>
'V uvozni datoteki ni bilo najdenih stikov.';
@override
String discoveredContacts_importFailed(String error) {
return 'Uvoz odkritih stikov ni uspel: $error';
}
@override
String get contacts_zeroHopAdvert => 'Reklama brez posrednikov';

View file

@ -3256,6 +3256,36 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get contacts_contactImportFailed => 'Kontakt kunde inte importeras.';
@override
String get discoveredContacts_export => 'Exportera upptäckta kontakter';
@override
String get discoveredContacts_import => 'Importera upptäckta kontakter';
@override
String discoveredContacts_exported(String path) {
return 'Upptäckta kontakter exporterades till $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Det gick inte att exportera upptäckta kontakter: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Importerade $count upptäckta kontakter.';
}
@override
String get discoveredContacts_importNoContacts =>
'Inga kontakter hittades i importfilen.';
@override
String discoveredContacts_importFailed(String error) {
return 'Det gick inte att importera upptäckta kontakter: $error';
}
@override
String get contacts_zeroHopAdvert => 'Reklam med nollhopp';

View file

@ -3301,6 +3301,36 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get contacts_contactImportFailed => 'Контакт не вдалося імпортувати';
@override
String get discoveredContacts_export => 'Експортувати виявлені контакти';
@override
String get discoveredContacts_import => 'Імпортувати виявлені контакти';
@override
String discoveredContacts_exported(String path) {
return 'Виявлені контакти експортовано до $path.';
}
@override
String discoveredContacts_exportFailed(String error) {
return 'Не вдалося експортувати виявлені контакти: $error';
}
@override
String discoveredContacts_imported(int count) {
return 'Імпортовано $count виявлених контактів.';
}
@override
String get discoveredContacts_importNoContacts =>
'У файлі імпорту не знайдено контактів.';
@override
String discoveredContacts_importFailed(String error) {
return 'Не вдалося імпортувати виявлені контакти: $error';
}
@override
String get contacts_zeroHopAdvert => 'Реклама без перехоплення';

View file

@ -3073,6 +3073,35 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get contacts_contactImportFailed => '导入联系人失败。';
@override
String get discoveredContacts_export => '导出已发现的联系人';
@override
String get discoveredContacts_import => '导入已发现的联系人';
@override
String discoveredContacts_exported(String path) {
return '已将已发现的联系人导出到$path';
}
@override
String discoveredContacts_exportFailed(String error) {
return '导出已发现的联系人失败:$error';
}
@override
String discoveredContacts_imported(int count) {
return '已导入$count个已发现的联系人';
}
@override
String get discoveredContacts_importNoContacts => '在导入文件中未找到联系人。';
@override
String discoveredContacts_importFailed(String error) {
return '导入已发现的联系人失败:$error';
}
@override
String get contacts_zeroHopAdvert => '发送零跳广播';

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_Title": "Ontdekte contacten",
"discoveredContacts_contactAdded": "Contact toegevoegd",
"discoveredContacts_searchHint": "Ontdekte contacten zoeken",
"discoveredContacts_export": "Ontdekte contacten exporteren",
"discoveredContacts_import": "Ontdekte contacten importeren",
"discoveredContacts_exported": "Ontdekte contacten geëxporteerd naar {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Exporteren van ontdekte contacten mislukt: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "{count} ontdekte contacten geïmporteerd.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Geen contacten gevonden in het importbestand.",
"discoveredContacts_importFailed": "Importeren van ontdekte contacten mislukt: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Wanneer de contactenlijst vol is, wordt de oudste niet-favoriete contactpersoon vervangen.",
"common_deleteAll": "Alles verwijderen",
"discoveredContacts_deleteContactAll": "Verwijder alle ontdekte contacten",

View file

@ -1862,6 +1862,41 @@
"contactsSettings_autoAddSensorsSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie wykrytych czujników.",
"discoveredContacts_noMatching": "Brak pasujących kontaktów",
"discoveredContacts_deleteContact": "Usuń kontakt",
"discoveredContacts_export": "Eksportuj odkryte kontakty",
"discoveredContacts_import": "Importuj odkryte kontakty",
"discoveredContacts_exported": "Wyeksportowano odkryte kontakty do {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Nie udało się wyeksportować odkrytych kontaktów: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Zaimportowano {count} odkrytych kontaktów.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Nie znaleziono kontaktów w pliku importu.",
"discoveredContacts_importFailed": "Nie udało się zaimportować odkrytych kontaktów: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Gdy lista kontaktów jest pełna, najstarszy nieulubiony kontakt zostanie zastąpiony.",
"common_deleteAll": "Usuń wszystko",
"discoveredContacts_deleteContactAllContent": "Czy na pewno chcesz usunąć wszystkie znalezione kontakty?",
@ -2100,12 +2135,6 @@
"scanner_linuxPairingPinTitle": "Kod PIN parowania Bluetooth",
"repeater_cliQuickClockSync": "Synchronizacja zegara",
"repeater_cliQuickDiscovery": "Odkryj Sąsiadów",
"@repeater_clockSyncAfterLogin": {
"description": "Repeater setting: auto sync device clock after successful login"
},
"@repeater_clockSyncAfterLoginSubtitle": {
"description": "Repeater setting subtitle: describes the clock sync after login behavior"
},
"repeater_clockSyncAfterLogin": "Synchronizacja zegara po zalogowaniu",
"repeater_clockSyncAfterLoginSubtitle": "Automatycznie wysyłaj powiadomienie \"synchronizacja zegara\" po pomyślnym zalogowaniu.",
"chat_sendMessage": "Wyślij wiadomość",

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_deleteContact": "Excluir Contato",
"discoveredContacts_contactAdded": "Contato adicionado",
"discoveredContacts_addContact": "Adicionar Contato",
"discoveredContacts_export": "Exportar contatos descobertos",
"discoveredContacts_import": "Importar contatos descobertos",
"discoveredContacts_exported": "Contatos descobertos exportados para {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Falha ao exportar contatos descobertos: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "{count} contatos descobertos importados.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Nenhum contato encontrado no arquivo de importação.",
"discoveredContacts_importFailed": "Falha ao importar contatos descobertos: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Quando a lista de contatos estiver cheia, o contato mais antigo não favoritado será substituído.",
"common_deleteAll": "Excluir Tudo",
"discoveredContacts_deleteContactAll": "Excluir Todos os Contatos Descobertos",

View file

@ -1064,6 +1064,41 @@
"discoveredContacts_addContact": "Добавить контакт",
"discoveredContacts_Title": "Обнаруженные контакты",
"discoveredContacts_deleteContact": "Удалить контакт",
"discoveredContacts_export": "Экспортировать обнаруженные контакты",
"discoveredContacts_import": "Импортировать обнаруженные контакты",
"discoveredContacts_exported": "Обнаруженные контакты экспортированы в {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Не удалось экспортировать обнаруженные контакты: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Импортировано {count} обнаруженных контактов.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "В файле импорта не найдены контакты.",
"discoveredContacts_importFailed": "Не удалось импортировать обнаруженные контакты: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Когда список контактов заполнен, будет заменен самый старый контакт, который не находится в избранном.",
"common_deleteAll": "Удалить все",
"discoveredContacts_deleteContactAllContent": "Вы уверены, что хотите удалить все обнаруженные контакты?",
@ -1302,12 +1337,6 @@
"scanner_linuxPairingPinTitle": "PINкод сопряжения Bluetooth",
"repeater_cliQuickDiscovery": "Обнаружить Соседей",
"repeater_cliQuickClockSync": "Синхронизация часов",
"@repeater_clockSyncAfterLogin": {
"description": "Repeater setting: auto sync device clock after successful login"
},
"@repeater_clockSyncAfterLoginSubtitle": {
"description": "Repeater setting subtitle: describes the clock sync after login behavior"
},
"repeater_clockSyncAfterLogin": "Синхронизация часов после входа в систему",
"repeater_clockSyncAfterLoginSubtitle": "Автоматически отправлять сообщение \"синхронизация времени\" после успешной авторизации.",
"chat_sendMessage": "Отправить сообщение",

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_Title": "Objavené kontakty",
"contactsSettings_overwriteOldestTitle": "Prepísať najstaršie",
"discoveredContacts_addContact": "Pridať kontakt",
"discoveredContacts_export": "Exportovať objavené kontakty",
"discoveredContacts_import": "Importovať objavené kontakty",
"discoveredContacts_exported": "Objavené kontakty boli exportované do {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Export objavených kontaktov zlyhal: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Bolo importovaných {count} objavených kontaktov.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "V importovanom súbore sa nenašli žiadne kontakty.",
"discoveredContacts_importFailed": "Import objavených kontaktov zlyhal: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Keď je zoznam kontaktov plný, bude nahradený najstarší neoznačený kontakt.",
"discoveredContacts_deleteContactAll": "Zmazať všetky objavené kontakty",
"common_deleteAll": "Zmazať všetko",

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_Title": "Odkriti stiki",
"discoveredContacts_searchHint": "Najdeni stiki po iskanju",
"discoveredContacts_deleteContact": "Izbriši stik",
"discoveredContacts_export": "Izvozi odkrite stike",
"discoveredContacts_import": "Uvozi odkrite stike",
"discoveredContacts_exported": "Odkriti stiki so izvoženi v {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Izvoz odkritih stikov ni uspel: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Uvoženih je bilo {count} odkritih stikov.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "V uvozni datoteki ni bilo najdenih stikov.",
"discoveredContacts_importFailed": "Uvoz odkritih stikov ni uspel: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Ko je seznam stikov poln, bo najstarejši nestarševski stik zamenjan.",
"common_deleteAll": "Izbriši vse",
"discoveredContacts_deleteContactAllContent": "Ste prepričani, da želite izbrisati vse odkrite kontakte?",

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_contactAdded": "Kontakt tillagd",
"discoveredContacts_addContact": "Lägg till kontakt",
"discoveredContacts_copyContact": "Kopiera kontakt till urklipp",
"discoveredContacts_export": "Exportera upptäckta kontakter",
"discoveredContacts_import": "Importera upptäckta kontakter",
"discoveredContacts_exported": "Upptäckta kontakter exporterades till {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Det gick inte att exportera upptäckta kontakter: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Importerade {count} upptäckta kontakter.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "Inga kontakter hittades i importfilen.",
"discoveredContacts_importFailed": "Det gick inte att importera upptäckta kontakter: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "När kontaktlistan är full ersätts den äldsta icke-favoriterade kontakten.",
"common_deleteAll": "Ta bort alla",
"discoveredContacts_deleteContactAllContent": "Är du säker på att du vill ta bort alla upptäckta kontakter?",
@ -2062,12 +2097,6 @@
"scanner_linuxPairingHidePin": "Dölj PIN",
"repeater_cliQuickDiscovery": "Upptäck grannar",
"repeater_cliQuickClockSync": "Synkronisera klocka",
"@repeater_clockSyncAfterLogin": {
"description": "Repeater setting: auto sync device clock after successful login"
},
"@repeater_clockSyncAfterLoginSubtitle": {
"description": "Repeater setting subtitle: describes the clock sync after login behavior"
},
"repeater_clockSyncAfterLoginSubtitle": "Automatiskt skicka \"klocksynkronisering\" efter en lyckad inloggning.",
"repeater_clockSyncAfterLogin": "Synkronisera klockan efter inloggning",
"repeater_guest": "Information om repetorer",

View file

@ -1824,6 +1824,41 @@
"discoveredContacts_deleteContact": "Видалити контакт",
"discoveredContacts_copyContact": "Копіювати контакт у буфер обміну",
"discoveredContacts_addContact": "Додати контакт",
"discoveredContacts_export": "Експортувати виявлені контакти",
"discoveredContacts_import": "Імпортувати виявлені контакти",
"discoveredContacts_exported": "Виявлені контакти експортовано до {path}.",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "Не вдалося експортувати виявлені контакти: {error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "Імпортовано {count} виявлених контактів.",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "У файлі імпорту не знайдено контактів.",
"discoveredContacts_importFailed": "Не вдалося імпортувати виявлені контакти: {error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "Коли список контактів заповнений, найстарший контакт без позначки улюбленого буде замінений.",
"common_deleteAll": "Видалити все",
"discoveredContacts_deleteContactAll": "Видалити всі виявлені контакти",
@ -2062,12 +2097,6 @@
"scanner_linuxPairingHidePin": "Приховати PIN",
"repeater_cliQuickClockSync": "Синхронізація годинника",
"repeater_cliQuickDiscovery": "Відкрити сусідів",
"@repeater_clockSyncAfterLogin": {
"description": "Repeater setting: auto sync device clock after successful login"
},
"@repeater_clockSyncAfterLoginSubtitle": {
"description": "Repeater setting subtitle: describes the clock sync after login behavior"
},
"repeater_clockSyncAfterLoginSubtitle": "Автоматично надсилати повідомлення \"синхронізація годин\" після успішного входу.",
"repeater_clockSyncAfterLogin": "Синхронізація годин після входу",
"repeater_guestTools": "Інструменти для гостей",

View file

@ -1829,6 +1829,41 @@
"discoveredContacts_noMatching": "没有匹配的联系人",
"discoveredContacts_Title": "已发现的联系人",
"discoveredContacts_copyContact": "复制联系人到剪贴板",
"discoveredContacts_export": "导出已发现的联系人",
"discoveredContacts_import": "导入已发现的联系人",
"discoveredContacts_exported": "已将已发现的联系人导出到{path}。",
"@discoveredContacts_exported": {
"placeholders": {
"path": {
"type": "String"
}
}
},
"discoveredContacts_exportFailed": "导出已发现的联系人失败:{error}",
"@discoveredContacts_exportFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"discoveredContacts_imported": "已导入{count}个已发现的联系人。",
"@discoveredContacts_imported": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"discoveredContacts_importNoContacts": "在导入文件中未找到联系人。",
"discoveredContacts_importFailed": "导入已发现的联系人失败:{error}",
"@discoveredContacts_importFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"contactsSettings_overwriteOldestSubtitle": "当联系人列表已满时,将替换最老的非收藏联系人。",
"common_deleteAll": "删除全部",
"discoveredContacts_deleteContactAllContent": "您确定要删除所有发现的联系人吗?",

View file

@ -18,7 +18,6 @@ class Contact {
final DateTime lastSeen;
final DateTime lastMessageAt;
final bool isActive;
final bool wasPulled;
final Uint8List? rawPacket;
Contact({
@ -35,7 +34,6 @@ class Contact {
required this.lastSeen,
DateTime? lastMessageAt,
this.isActive = true,
this.wasPulled = false,
this.rawPacket,
}) : lastMessageAt = lastMessageAt ?? lastSeen;

View file

@ -1,8 +1,13 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:file_selector/file_selector.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart';
import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart';
@ -66,9 +71,44 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
),
centerTitle: true,
actions: [
PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
PopupMenuButton<String>(
onSelected: (value) {
switch (value) {
case 'export':
unawaited(_exportDiscoveredContacts(context, connector));
break;
case 'import':
unawaited(_importDiscoveredContacts(context, connector));
break;
case 'delete_all':
_deleteContacts(context, connector);
break;
}
},
itemBuilder: (context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'export',
child: Row(
children: [
const Icon(Icons.upload_file),
const SizedBox(width: 8),
Text(l10n.discoveredContacts_export),
],
),
),
PopupMenuItem<String>(
value: 'import',
child: Row(
children: [
const Icon(Icons.download),
const SizedBox(width: 8),
Text(l10n.discoveredContacts_import),
],
),
),
const PopupMenuDivider(),
PopupMenuItem<String>(
value: 'delete_all',
child: Row(
children: [
const Icon(Icons.delete, color: Colors.red),
@ -76,9 +116,6 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
Text(context.l10n.discoveredContacts_deleteContactAll),
],
),
onTap: () {
_deleteContacts(context, connector);
},
),
],
icon: const Icon(Icons.more_vert),
@ -270,6 +307,144 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
);
}
Future<void> _exportDiscoveredContacts(
BuildContext context,
MeshCoreConnector connector,
) async {
final l10n = context.l10n;
final messenger = ScaffoldMessenger.of(context);
final json = connector.exportDiscoveredContactsJson();
try {
const filename = 'meshcore_discovered_contacts.json';
final bytes = Uint8List.fromList(utf8.encode(json));
if (PlatformInfo.isDesktop) {
final location = await getSaveLocation(
suggestedName: filename,
acceptedTypeGroups: [
const XTypeGroup(label: 'JSON', extensions: ['json']),
],
);
if (!mounted) return;
if (location == null) return;
final exportFile = XFile.fromData(
bytes,
mimeType: 'application/json',
name: filename,
);
await exportFile.saveTo(location.path);
if (!mounted) return;
messenger.showSnackBar(
SnackBar(
content: Text(l10n.discoveredContacts_exported(location.path)),
),
);
return;
}
final tempDir = await getTemporaryDirectory();
final exportPath = '${tempDir.path}/$filename';
final exportFile = File(exportPath);
await exportFile.writeAsBytes(bytes, flush: true);
try {
final result = await SharePlus.instance.share(
ShareParams(subject: filename, files: [XFile(exportPath)]),
);
if (!mounted) return;
if (result.status == ShareResultStatus.success) {
messenger.showSnackBar(
SnackBar(content: Text(l10n.discoveredContacts_exported(filename))),
);
}
} finally {
if (await exportFile.exists()) {
await exportFile.delete();
}
}
} catch (e) {
if (!mounted) return;
messenger.showSnackBar(
SnackBar(
content: Text(l10n.discoveredContacts_exportFailed(e.toString())),
),
);
}
}
Future<void> _importDiscoveredContacts(
BuildContext context,
MeshCoreConnector connector,
) async {
final l10n = context.l10n;
final messenger = ScaffoldMessenger.of(context);
try {
final json = await _resolveImportJson(context);
if (json == null) {
// User cancelled the file picker nothing to do.
return;
}
final foundCount = _countContactsInImportJson(json);
if (foundCount == 0) {
if (!mounted) return;
messenger.showSnackBar(
SnackBar(content: Text(l10n.discoveredContacts_importNoContacts)),
);
return;
}
final importedCount = await connector.importDiscoveredContactsJson(json);
if (!mounted) return;
messenger.showSnackBar(
SnackBar(
content: Text(l10n.discoveredContacts_imported(importedCount)),
),
);
} catch (e) {
if (!mounted) return;
messenger.showSnackBar(
SnackBar(
content: Text(l10n.discoveredContacts_importFailed(e.toString())),
),
);
}
}
Future<String?> _resolveImportJson(BuildContext context) async {
final file = await openFile(
acceptedTypeGroups: [
const XTypeGroup(label: 'JSON', extensions: ['json']),
],
);
if (file == null) return null;
final bytes = await file.readAsBytes();
if (bytes.isEmpty) return '';
// Try UTF-8 first (handles BOM automatically)
try {
final text = utf8.decode(bytes, allowMalformed: true);
// Remove BOM if present
return text.startsWith('\ufeff') ? text.substring(1) : text;
} catch (_) {
return '';
}
}
int _countContactsInImportJson(String json) {
try {
final decoded = jsonDecode(json);
if (decoded is List) {
return decoded.length;
}
return 0;
} catch (_) {
return 0;
}
}
Widget _buildFilters(
List<Contact> filteredAndSorted,
MeshCoreConnector connector,

View file

@ -28,6 +28,55 @@ class ContactDiscoveryStore {
await prefs.setString(_keyPrefix, jsonEncode(jsonList));
}
String exportContactsJson(List<Contact> contacts) {
final jsonList = contacts.map(_toJson).toList();
return jsonEncode(jsonList);
}
int importContactsJson({
required String json,
required List<Contact> existingContacts,
required Set<String> knownContactKeys,
}) {
try {
final jsonList = jsonDecode(json) as List<dynamic>;
final importedContacts = jsonList
.map((entry) => _fromJson(entry as Map<String, dynamic>))
.toList();
int newCount = 0;
// Create a set of existing discovered contact keys for deduplication
final existingKeySet = <String>{};
for (final contact in existingContacts) {
existingKeySet.add(contact.publicKeyHex);
}
// Process imported contacts
for (final imported in importedContacts) {
final keyHex = imported.publicKeyHex;
// Skip if already in device's contact list
if (knownContactKeys.contains(keyHex)) {
continue;
}
// Skip if already in discovered contacts (existing is always fresher)
if (existingKeySet.contains(keyHex)) {
continue;
}
// Add as new discovered contact
existingContacts.add(imported);
newCount++;
}
return newCount;
} catch (_) {
return 0;
}
}
Map<String, dynamic> _toJson(Contact contact) {
return {
'publicKey': base64Encode(contact.publicKey),
@ -44,6 +93,7 @@ class ContactDiscoveryStore {
'longitude': contact.longitude,
'lastSeen': contact.lastSeen.millisecondsSinceEpoch,
'lastMessageAt': contact.lastMessageAt.millisecondsSinceEpoch,
'isActive': contact.isActive,
'rawPacket': contact.rawPacket != null
? base64Encode(contact.rawPacket!)
: null,

View file

@ -64,6 +64,7 @@ dependencies:
gpx: ^2.3.0
path_provider: ^2.1.5
share_plus: ^12.0.1
file_selector: ^1.0.3
build_pipe: ^0.3.1
material_symbols_icons: ^4.2906.0
web: ^1.1.1