mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
Notification rate limiting (#110)
* Add notification rate limiting with privacy-safe debug logging - Add batching system to prevent notification storms (3s rate limit, 5s batch window) - Queue rapid notifications and show batch summaries - Debug logs show device names for adverts, sender/channel for messages (no content leaks) - Remove unused _maxBatchSize constant Context: Added after getting notification-flooded while evaluating RF flood management. The irony. * Update notification_service.dart I made a mistake and removed this * Add l10n support for notification strings Addresses PR #110 review feedback to use the translations system: - Add notification strings to app_en.arb (plurals for batch summary) - Update NotificationService to use lookupAppLocalizations() - Wire locale from MaterialApp to NotificationService - Regenerate localization files New strings added (English only, translations needed): - notification_activityTitle: "MeshCore Activity" - notification_messagesCount: "{count} message(s)" - notification_channelMessagesCount: "{count} channel message(s)" - notification_newNodesCount: "{count} new node(s)" - notification_newTypeDiscovered: "New {type} discovered" - notification_receivedNewMessage: "Received new message" * Add notification string translations for all supported languages Translated notification_activityTitle, notification_messagesCount, notification_channelMessagesCount, notification_newNodesCount, notification_newTypeDiscovered, and notification_receivedNewMessage to: bg, de, es, fr, it, nl, pl, pt, ru, sk, sl, sv, uk, zh Includes proper ICU plural forms for Slavic languages (few/many/other) and Slovenian dual form. * Apply dart format to notification_service.dart --------- Co-authored-by: Winston Lowe <wel97459@gmail.com>
This commit is contained in:
parent
ea43cf17eb
commit
daca42701c
34 changed files with 1282 additions and 7 deletions
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_zeroHopContactAdvertFailed": "Неуспешно изпращане на контакт.",
|
||||
"contacts_zeroHopContactAdvertSent": "Изпратен контакт по обява.",
|
||||
"contacts_contactAdvertCopyFailed": "Копирането на обявата в клипборда не успя.",
|
||||
"notification_activityTitle": "Активност на MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{съобщение} other{съобщения}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{съобщение в канал} other{съобщения в канали}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{нов възел} other{нови възли}}",
|
||||
"notification_newTypeDiscovered": "Открит нов {contactType}",
|
||||
"notification_receivedNewMessage": "Получено ново съобщение",
|
||||
"contacts_contactAdvertCopyFailed": "Копирането на обявата в клипборда не успя.",
|
||||
"settings_gpxExportContactsSubtitle": "Експортира спътници с местоположение в GPX файл.",
|
||||
"settings_gpxExportRepeatersSubtitle": "Изпраща повторители / roomserver с местоположение в GPX файл.",
|
||||
"settings_gpxExportAll": "Експортирай всички контакти в GPX",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "Картинни данни изнесени от meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open износ на данни за карта в формат GPX",
|
||||
"pathTrace_someHopsNoLocation": "Един или повече от хмелите липсва местоположение!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,34 @@
|
|||
"contacts_zeroHopContactAdvertSent": "Kontakt über Anzeige gesendet",
|
||||
"contacts_contactAdvertCopied": "Anzeige in die Zwischenablage kopiert.",
|
||||
"contacts_contactAdvertCopyFailed": "Kopieren des Werbeinhalts in die Zwischenablage fehlgeschlagen.",
|
||||
|
||||
"notification_activityTitle": "MeshCore Aktivität",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{Nachricht} other{Nachrichten}}",
|
||||
"@notification_messagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{Kanalnachricht} other{Kanalnachrichten}}",
|
||||
"@notification_channelMessagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{neuer Knoten} other{neue Knoten}}",
|
||||
"@notification_newNodesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_newTypeDiscovered": "Neuer {contactType} entdeckt",
|
||||
"@notification_newTypeDiscovered": {
|
||||
"placeholders": {
|
||||
"contactType": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"notification_receivedNewMessage": "Neue Nachricht empfangen",
|
||||
"contacts_contactAdvertCopyFailed": "Kopieren des Werbeinhalts in die Zwischenablage fehlgeschlagen.",
|
||||
"settings_gpxExportAll": "Alle Kontakte nach GPX exportieren",
|
||||
"settings_gpxExportAllSubtitle": "Exportiert alle Kontakte mit einem Standort in eine GPX-Datei.",
|
||||
"settings_gpxExportRepeaters": "Repeater und Raumserver nach GPX exportieren",
|
||||
|
|
@ -1585,4 +1613,5 @@
|
|||
"settings_gpxExportShareSubject": "meshcore-open GPX-Kartendaten exportieren",
|
||||
"settings_gpxExportShareText": "Kartendaten aus meshcore-open exportiert",
|
||||
"pathTrace_someHopsNoLocation": "Eine oder mehrere der Hopfen fehlen einen Standort!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1346,12 +1346,39 @@
|
|||
"contacts_contactAdvertCopied": "Advert copied to Clipboard.",
|
||||
"contacts_contactAdvertCopyFailed": "Copying advert to Clipboard failed.",
|
||||
|
||||
"notification_activityTitle": "MeshCore Activity",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{message} other{messages}}",
|
||||
"@notification_messagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{channel message} other{channel messages}}",
|
||||
"@notification_channelMessagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{new node} other{new nodes}}",
|
||||
"@notification_newNodesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_newTypeDiscovered": "New {contactType} discovered",
|
||||
"@notification_newTypeDiscovered": {
|
||||
"placeholders": {
|
||||
"contactType": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"notification_receivedNewMessage": "Received new message",
|
||||
|
||||
"settings_gpxExportRepeaters": "Export repeaters / room server to GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exports repeaters / roomserver with a location to GPX file.",
|
||||
"settings_gpxExportContacts": "Export companions to GPX",
|
||||
"settings_gpxExportContactsSubtitle": "Exports companions with a location to GPX file.",
|
||||
"settings_gpxExportAll": "Export all contacts to GPX",
|
||||
"settings_gpxExportAllSubtitle": "Exports all contacts with a location to GPX file.",
|
||||
"settings_gpxExportAllSubtitle": "Exports all contacts with a location to GPX file.",
|
||||
"settings_gpxExportSuccess": "Successfully exported GPX file.",
|
||||
"settings_gpxExportNoContacts": "No contacts to export.",
|
||||
"settings_gpxExportNotAvailable": "Not supported on your device/OS",
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,34 @@
|
|||
"contacts_zeroHopContactAdvertSent": "Envió contacto por anuncio.",
|
||||
"contacts_contactAdvertCopied": "Anuncio copiado al Portapapeles.",
|
||||
"contacts_contactAdvertCopyFailed": "Copiar anuncio al Portapapeles ha fallado.",
|
||||
|
||||
"notification_activityTitle": "Actividad de MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{mensaje} other{mensajes}}",
|
||||
"@notification_messagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{mensaje de canal} other{mensajes de canal}}",
|
||||
"@notification_channelMessagesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{nuevo nodo} other{nuevos nodos}}",
|
||||
"@notification_newNodesCount": {
|
||||
"placeholders": {
|
||||
"count": {"type": "int"}
|
||||
}
|
||||
},
|
||||
"notification_newTypeDiscovered": "Nuevo {contactType} descubierto",
|
||||
"@notification_newTypeDiscovered": {
|
||||
"placeholders": {
|
||||
"contactType": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"notification_receivedNewMessage": "Nuevo mensaje recibido",
|
||||
"contacts_contactAdvertCopyFailed": "Copiar anuncio al Portapapeles ha fallado.",
|
||||
"settings_gpxExportContactsSubtitle": "Exporta compañeros con una ubicación a archivo GPX.",
|
||||
"settings_gpxExportRepeaters": "Exportar repetidores / servidor de sala a GPX",
|
||||
"settings_gpxExportSuccess": "Archivo GPX exportado con éxito.",
|
||||
|
|
@ -1585,4 +1613,5 @@
|
|||
"settings_gpxExportShareText": "Datos del mapa exportados desde meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open exportación de datos de mapa GPX",
|
||||
"pathTrace_someHopsNoLocation": "Uno o más de los lúpulos carecen de una ubicación"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_contactAdvertCopyFailed": "La copie de l'annonce vers le presse-papiers a échoué.",
|
||||
"contacts_zeroHopContactAdvertSent": "Envoyer un contact par annonce.",
|
||||
"contacts_zeroHopContactAdvertFailed": "Échec de l'envoi du contact.",
|
||||
"notification_activityTitle": "Activité MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{message} other{messages}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{message de canal} other{messages de canal}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{nouveau nœud} other{nouveaux nœuds}}",
|
||||
"notification_newTypeDiscovered": "Nouveau {contactType} découvert",
|
||||
"notification_receivedNewMessage": "Nouveau message reçu",
|
||||
"contacts_zeroHopContactAdvertFailed": "Échec de l'envoi du contact.",
|
||||
"settings_gpxExportRepeaters": "Exporter les répéteurs / serveur de salle au format GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exporte les répéteurs / roomserver avec une localisation vers un fichier GPX.",
|
||||
"settings_gpxExportNoContacts": "Aucun contact à exporter.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "Données de carte exportées à partir de meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open exporter les données de carte GPX",
|
||||
"pathTrace_someHopsNoLocation": "Une ou plusieurs des houblons manquent d'une localisation !"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_ShareContactZeroHop": "Condividi contatto tramite annuncio",
|
||||
"contacts_zeroHopContactAdvertFailed": "Invio del contatto non riuscito.",
|
||||
"contacts_contactAdvertCopied": "Annuncio copiato negli Appunti.",
|
||||
"notification_activityTitle": "Attività MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{messaggio} other{messaggi}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{messaggio del canale} other{messaggi del canale}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{nuovo nodo} other{nuovi nodi}}",
|
||||
"notification_newTypeDiscovered": "Nuovo {contactType} scoperto",
|
||||
"notification_receivedNewMessage": "Nuovo messaggio ricevuto",
|
||||
"contacts_contactAdvertCopied": "Annuncio copiato negli Appunti.",
|
||||
"settings_gpxExportRepeaters": "Esporta ripetitori / server di stanza in GPX",
|
||||
"settings_gpxExportContacts": "Esporta compagni in GPX",
|
||||
"settings_gpxExportSuccess": "Esportazione del file GPX completata con successo.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "Dati mappa esportati da meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open esportazione dati mappa GPX",
|
||||
"pathTrace_someHopsNoLocation": "Uno o più dei luppoli mancano di una posizione!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4862,6 +4862,42 @@ abstract class AppLocalizations {
|
|||
/// **'Copying advert to Clipboard failed.'**
|
||||
String get contacts_contactAdvertCopyFailed;
|
||||
|
||||
/// No description provided for @notification_activityTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'MeshCore Activity'**
|
||||
String get notification_activityTitle;
|
||||
|
||||
/// No description provided for @notification_messagesCount.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count} {count, plural, =1{message} other{messages}}'**
|
||||
String notification_messagesCount(int count);
|
||||
|
||||
/// No description provided for @notification_channelMessagesCount.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count} {count, plural, =1{channel message} other{channel messages}}'**
|
||||
String notification_channelMessagesCount(int count);
|
||||
|
||||
/// No description provided for @notification_newNodesCount.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count} {count, plural, =1{new node} other{new nodes}}'**
|
||||
String notification_newNodesCount(int count);
|
||||
|
||||
/// No description provided for @notification_newTypeDiscovered.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'New {contactType} discovered'**
|
||||
String notification_newTypeDiscovered(String contactType);
|
||||
|
||||
/// No description provided for @notification_receivedNewMessage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Received new message'**
|
||||
String get notification_receivedNewMessage;
|
||||
|
||||
/// No description provided for @settings_gpxExportRepeaters.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -2771,6 +2771,50 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Копирането на обявата в клипборда не успя.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Активност на MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'съобщения',
|
||||
one: 'съобщение',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'съобщения в канали',
|
||||
one: 'съобщение в канал',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'нови възли',
|
||||
one: 'нов възел',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Открит нов $contactType';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Получено ново съобщение';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Експортиране на повтарящи се устройства / сървър на стаята до GPX';
|
||||
|
|
|
|||
|
|
@ -2778,6 +2778,50 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Kopieren des Werbeinhalts in die Zwischenablage fehlgeschlagen.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'MeshCore Aktivität';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Nachrichten',
|
||||
one: 'Nachricht',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Kanalnachrichten',
|
||||
one: 'Kanalnachricht',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'neue Knoten',
|
||||
one: 'neuer Knoten',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Neuer $contactType entdeckt';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Neue Nachricht empfangen';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Repeater und Raumserver nach GPX exportieren';
|
||||
|
|
|
|||
|
|
@ -2728,6 +2728,50 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Copying advert to Clipboard failed.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'MeshCore Activity';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'messages',
|
||||
one: 'message',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'channel messages',
|
||||
one: 'channel message',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'new nodes',
|
||||
one: 'new node',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'New $contactType discovered';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Received new message';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Export repeaters / room server to GPX';
|
||||
|
|
|
|||
|
|
@ -2771,6 +2771,50 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Copiar anuncio al Portapapeles ha fallado.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Actividad de MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'mensajes',
|
||||
one: 'mensaje',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'mensajes de canal',
|
||||
one: 'mensaje de canal',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'nuevos nodos',
|
||||
one: 'nuevo nodo',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Nuevo $contactType descubierto';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Nuevo mensaje recibido';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Exportar repetidores / servidor de sala a GPX';
|
||||
|
|
|
|||
|
|
@ -2792,6 +2792,50 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'La copie de l\'annonce vers le presse-papiers a échoué.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Activité MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'messages',
|
||||
one: 'message',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'messages de canal',
|
||||
one: 'message de canal',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'nouveaux nœuds',
|
||||
one: 'nouveau nœud',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Nouveau $contactType découvert';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Nouveau message reçu';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Exporter les répéteurs / serveur de salle au format GPX';
|
||||
|
|
|
|||
|
|
@ -2774,6 +2774,50 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Copia dell\'annuncio nella Clipboard non riuscita.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Attività MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'messaggi',
|
||||
one: 'messaggio',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'messaggi del canale',
|
||||
one: 'messaggio del canale',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'nuovi nodi',
|
||||
one: 'nuovo nodo',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Nuovo $contactType scoperto';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Nuovo messaggio ricevuto';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Esporta ripetitori / server di stanza in GPX';
|
||||
|
|
|
|||
|
|
@ -2763,6 +2763,50 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Kopiëren van advertentie naar Clipboard is mislukt.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'MeshCore Activiteit';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'berichten',
|
||||
one: 'bericht',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'kanaalberichten',
|
||||
one: 'kanaalbericht',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'nieuwe knooppunten',
|
||||
one: 'nieuw knooppunt',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Nieuw $contactType ontdekt';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Nieuw bericht ontvangen';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Exporteer repeaters / roomserver naar GPX';
|
||||
|
|
|
|||
|
|
@ -2771,6 +2771,56 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Kopiowanie ogłoszenia do schowka nie powiodło się.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Aktywność MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'wiadomości',
|
||||
many: 'wiadomości',
|
||||
few: 'wiadomości',
|
||||
one: 'wiadomość',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'wiadomości kanału',
|
||||
many: 'wiadomości kanału',
|
||||
few: 'wiadomości kanału',
|
||||
one: 'wiadomość kanału',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'nowych węzłów',
|
||||
many: 'nowych węzłów',
|
||||
few: 'nowe węzły',
|
||||
one: 'nowy węzeł',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Nowy $contactType wykryty';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Otrzymano nową wiadomość';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Eksportuj powtórki / serwer pokojowy do GPX';
|
||||
|
|
|
|||
|
|
@ -2773,6 +2773,50 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Cópia do anúncio para a Área de Transferência falhou.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Atividade MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'mensagens',
|
||||
one: 'mensagem',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'mensagens de canal',
|
||||
one: 'mensagem de canal',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'novos nós',
|
||||
one: 'novo nó',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Novo $contactType descoberto';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Nova mensagem recebida';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Exportar repetidores / servidor de sala para GPX';
|
||||
|
|
|
|||
|
|
@ -2778,6 +2778,56 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Копирование рекламы в буфер обмена не удалось.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Активность MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'сообщений',
|
||||
many: 'сообщений',
|
||||
few: 'сообщения',
|
||||
one: 'сообщение',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'сообщений канала',
|
||||
many: 'сообщений канала',
|
||||
few: 'сообщения канала',
|
||||
one: 'сообщение канала',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'новых узлов',
|
||||
many: 'новых узлов',
|
||||
few: 'новых узла',
|
||||
one: 'новый узел',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Обнаружен новый $contactType';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Получено новое сообщение';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Экспортировать рипитеры / сервер комнаты в GPX';
|
||||
|
|
|
|||
|
|
@ -2757,6 +2757,53 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Kopírovanie inzerátu do schránky zlyhalo.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Aktivita MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'správ',
|
||||
few: 'správy',
|
||||
one: 'správa',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'správ kanálu',
|
||||
few: 'správy kanálu',
|
||||
one: 'správa kanálu',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'nových uzlov',
|
||||
few: 'nové uzly',
|
||||
one: 'nový uzol',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Nový $contactType objavený';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Prijatá nová správa';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Exportovať repeater / server miestnosti do GPX';
|
||||
|
|
|
|||
|
|
@ -2759,6 +2759,56 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Kopiranje oglasa v odložišče je spodletelo.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Aktivnost MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'sporočil',
|
||||
few: 'sporočila',
|
||||
two: 'sporočili',
|
||||
one: 'sporočilo',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'sporočil kanala',
|
||||
few: 'sporočila kanala',
|
||||
two: 'sporočili kanala',
|
||||
one: 'sporočilo kanala',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'novih vozlišč',
|
||||
few: 'nova vozlišča',
|
||||
two: 'novi vozlišči',
|
||||
one: 'novo vozlišče',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Odkrito novo $contactType';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Prejeto novo sporočilo';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Izvoz ponoviteljev / strežnika sobe v GPX';
|
||||
|
|
|
|||
|
|
@ -2744,6 +2744,50 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Kopiering av annons till Urklipp misslyckades.';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'MeshCore Aktivitet';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'meddelanden',
|
||||
one: 'meddelande',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'kanalmeddelanden',
|
||||
one: 'kanalmeddelande',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'nya noder',
|
||||
one: 'ny nod',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Ny $contactType upptäckt';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Nytt meddelande mottaget';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Exportera repeater / rumsservrar till GPX';
|
||||
|
|
|
|||
|
|
@ -2784,6 +2784,56 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||
String get contacts_contactAdvertCopyFailed =>
|
||||
'Копіювання оголошення в буфер обміну завершилося невдало';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'Активність MeshCore';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'повідомлень',
|
||||
many: 'повідомлень',
|
||||
few: 'повідомлення',
|
||||
one: 'повідомлення',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'повідомлень каналу',
|
||||
many: 'повідомлень каналу',
|
||||
few: 'повідомлення каналу',
|
||||
one: 'повідомлення каналу',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'нових вузлів',
|
||||
many: 'нових вузлів',
|
||||
few: 'нових вузли',
|
||||
one: 'новий вузол',
|
||||
);
|
||||
return '$count $_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return 'Виявлено новий $contactType';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => 'Отримано нове повідомлення';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters =>
|
||||
'Експортувати ретранслятори / сервер кімнати до GPX';
|
||||
|
|
|
|||
|
|
@ -2625,6 +2625,32 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get contacts_contactAdvertCopyFailed => '将广告复制到剪贴板操作失败。';
|
||||
|
||||
@override
|
||||
String get notification_activityTitle => 'MeshCore 活动';
|
||||
|
||||
@override
|
||||
String notification_messagesCount(int count) {
|
||||
return '$count 条消息';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_channelMessagesCount(int count) {
|
||||
return '$count 条频道消息';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newNodesCount(int count) {
|
||||
return '$count 个新节点';
|
||||
}
|
||||
|
||||
@override
|
||||
String notification_newTypeDiscovered(String contactType) {
|
||||
return '发现新 $contactType';
|
||||
}
|
||||
|
||||
@override
|
||||
String get notification_receivedNewMessage => '收到新消息';
|
||||
|
||||
@override
|
||||
String get settings_gpxExportRepeaters => '导出重复器/房间服务器到GPX';
|
||||
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_ShareContact": "Kontakt naar Klembord kopiëren",
|
||||
"contacts_ShareContactZeroHop": "Contact delen via advertentie",
|
||||
"contacts_zeroHopContactAdvertFailed": "Mislukt om contact te verzenden",
|
||||
"notification_activityTitle": "MeshCore Activiteit",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{bericht} other{berichten}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{kanaalbericht} other{kanaalberichten}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{nieuw knooppunt} other{nieuwe knooppunten}}",
|
||||
"notification_newTypeDiscovered": "Nieuw {contactType} ontdekt",
|
||||
"notification_receivedNewMessage": "Nieuw bericht ontvangen",
|
||||
"contacts_zeroHopContactAdvertFailed": "Mislukt om contact te verzenden",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exporteert repeaters / roomserver met een locatie naar GPX-bestand.",
|
||||
"settings_gpxExportRepeaters": "Exporteer repeaters / roomserver naar GPX",
|
||||
"settings_gpxExportSuccess": "Succesvol GPX-bestand geëxporteerd.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "Kaartgegevens geëxporteerd uit meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open GPX kaartgegevens exporteren",
|
||||
"pathTrace_someHopsNoLocation": "Een of meer van de hops ontbreken een locatie!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_ShareContactZeroHop": "Udostępnij kontakt przez ogłoszenie",
|
||||
"contacts_ShareContact": "Kopiuj kontakt do schowka",
|
||||
"contacts_zeroHopContactAdvertFailed": "Nie udało się wysłać kontaktu.",
|
||||
"notification_activityTitle": "Aktywność MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{wiadomość} few{wiadomości} many{wiadomości} other{wiadomości}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{wiadomość kanału} few{wiadomości kanału} many{wiadomości kanału} other{wiadomości kanału}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{nowy węzeł} few{nowe węzły} many{nowych węzłów} other{nowych węzłów}}",
|
||||
"notification_newTypeDiscovered": "Nowy {contactType} wykryty",
|
||||
"notification_receivedNewMessage": "Otrzymano nową wiadomość",
|
||||
"contacts_zeroHopContactAdvertFailed": "Nie udało się wysłać kontaktu.",
|
||||
"settings_gpxExportContacts": "Eksportuj towarzyszy do GPX",
|
||||
"settings_gpxExportRepeaters": "Eksportuj powtórki / serwer pokojowy do GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Eksportuje powtarzacze / roomserver z lokalizacją do pliku GPX.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "Dane mapy wyeksportowane z meshcore-open",
|
||||
"settings_gpxExportShareSubject": "Eksport danych mapy GPX meshcore-open",
|
||||
"pathTrace_someHopsNoLocation": "Jeden lub więcej z chmieli nie ma określonej lokalizacji!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_contactAdvertCopyFailed": "Cópia do anúncio para a Área de Transferência falhou.",
|
||||
"contacts_ShareContactZeroHop": "Compartilhar contato por anúncio",
|
||||
"contacts_zeroHopContactAdvertFailed": "Falha ao enviar contato.",
|
||||
"notification_activityTitle": "Atividade MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{mensagem} other{mensagens}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{mensagem de canal} other{mensagens de canal}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{novo nó} other{novos nós}}",
|
||||
"notification_newTypeDiscovered": "Novo {contactType} descoberto",
|
||||
"notification_receivedNewMessage": "Nova mensagem recebida",
|
||||
"contacts_zeroHopContactAdvertFailed": "Falha ao enviar contato.",
|
||||
"settings_gpxExportRepeaters": "Exportar repetidores / servidor de sala para GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exporta repetidores / roomserver com localização para arquivo GPX.",
|
||||
"settings_gpxExportSuccess": "Arquivo GPX exportado com sucesso.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "Dados do mapa exportados do meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open exportação de dados de mapa GPX",
|
||||
"pathTrace_someHopsNoLocation": "Um ou mais dos lúpulos estão sem localização!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -809,6 +809,13 @@
|
|||
"contacts_addContactFromClipboard": "Добавить контакт из буфера обмена",
|
||||
"contacts_ShareContactZeroHop": "Поделиться контактом по объявлению",
|
||||
"contacts_zeroHopContactAdvertSent": "Отправлено сообщение по объявлению.",
|
||||
"notification_activityTitle": "Активность MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{сообщение} few{сообщения} many{сообщений} other{сообщений}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{сообщение канала} few{сообщения канала} many{сообщений канала} other{сообщений канала}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{новый узел} few{новых узла} many{новых узлов} other{новых узлов}}",
|
||||
"notification_newTypeDiscovered": "Обнаружен новый {contactType}",
|
||||
"notification_receivedNewMessage": "Получено новое сообщение",
|
||||
"contacts_zeroHopContactAdvertSent": "Отправлено сообщение по объявлению.",
|
||||
"settings_gpxExportRepeaters": "Экспортировать рипитеры / сервер комнаты в GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Экспортирует ретрансляторы / сервер комнат с местоположением в файл GPX.",
|
||||
"settings_gpxExportContacts": "Экспортировать спутников в GPX",
|
||||
|
|
@ -825,4 +832,5 @@
|
|||
"settings_gpxExportShareText": "Данные карты экспортированы из meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open экспорт данных карты GPX",
|
||||
"pathTrace_someHopsNoLocation": "Одному или нескольким хмелям не указано местоположение!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_zeroHopContactAdvertFailed": "Zlyhalo odoslanie kontaktu.",
|
||||
"contacts_ShareContactZeroHop": "Zdieľať kontakt cez inzerát",
|
||||
"contacts_ShareContact": "Kopírovať kontakt do schránky",
|
||||
"notification_activityTitle": "Aktivita MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{správa} few{správy} other{správ}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{správa kanálu} few{správy kanálu} other{správ kanálu}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{nový uzol} few{nové uzly} other{nových uzlov}}",
|
||||
"notification_newTypeDiscovered": "Nový {contactType} objavený",
|
||||
"notification_receivedNewMessage": "Prijatá nová správa",
|
||||
"contacts_ShareContact": "Kopírovať kontakt do schránky",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exportuje repeater / roomserver s lokalitou do súboru GPX.",
|
||||
"settings_gpxExportContacts": "Export sprievodcov do GPX",
|
||||
"settings_gpxExportSuccess": "Úspešne exportovaný súbor GPX.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "Mapové údaje exportované z meshcore-open",
|
||||
"settings_gpxExportShareSubject": "meshcore-open export dát GPX mapových údajov",
|
||||
"pathTrace_someHopsNoLocation": "Jedna alebo viac chmeľov chýba lokalita!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_contactAdvertCopyFailed": "Kopiranje oglasa v odložišče je spodletelo.",
|
||||
"contacts_ShareContactZeroHop": "Deliti kontakt prek oglasa",
|
||||
"contacts_ShareContact": "Kopiraj stik v Odložišče",
|
||||
"notification_activityTitle": "Aktivnost MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{sporočilo} =2{sporočili} few{sporočila} other{sporočil}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{sporočilo kanala} =2{sporočili kanala} few{sporočila kanala} other{sporočil kanala}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{novo vozlišče} =2{novi vozlišči} few{nova vozlišča} other{novih vozlišč}}",
|
||||
"notification_newTypeDiscovered": "Odkrito novo {contactType}",
|
||||
"notification_receivedNewMessage": "Prejeto novo sporočilo",
|
||||
"contacts_ShareContact": "Kopiraj stik v Odložišče",
|
||||
"settings_gpxExportAll": "Izvozi vse kontakte v GPX",
|
||||
"settings_gpxExportContacts": "Izvoz spremljevalcev v GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Izvozi ponovljene oddajnike / strežnik sobe z lokacijo v datoteko GPX.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportNotAvailable": "Ni podprto na vašem napravi/operacijskem sistemu",
|
||||
"settings_gpxExportShareSubject": "meshcore-open izvoz podatkov GPX karte",
|
||||
"pathTrace_someHopsNoLocation": "Ena ali več hmelju manjka lokacija!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_ShareContact": "Kopiera kontakt till Urklipp",
|
||||
"contacts_zeroHopContactAdvertFailed": "Misslyckades med att skicka kontakt.",
|
||||
"contacts_ShareContactZeroHop": "Dela kontakt via annons",
|
||||
"notification_activityTitle": "MeshCore Aktivitet",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{meddelande} other{meddelanden}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{kanalmeddelande} other{kanalmeddelanden}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{ny nod} other{nya noder}}",
|
||||
"notification_newTypeDiscovered": "Ny {contactType} upptäckt",
|
||||
"notification_receivedNewMessage": "Nytt meddelande mottaget",
|
||||
"contacts_ShareContactZeroHop": "Dela kontakt via annons",
|
||||
"settings_gpxExportAll": "Exportera alla kontakter till GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Exporterar repeater / roomserver med plats till GPX-fil.",
|
||||
"settings_gpxExportSuccess": "Har exporterat GPX-fil med framgång",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareSubject": "meshcore-open export av GPX-kartdata",
|
||||
"settings_gpxExportShareText": "Kartdata exporterad från meshcore-open",
|
||||
"pathTrace_someHopsNoLocation": "En eller flera av humlen saknar en plats!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_zeroHopContactAdvertSent": "Відправлено контакт за оголошенням",
|
||||
"contacts_addContactFromClipboard": "Додати контакт з буфера обміну",
|
||||
"contacts_ShareContactZeroHop": "Поділитися контактом за оголошенням",
|
||||
"notification_activityTitle": "Активність MeshCore",
|
||||
"notification_messagesCount": "{count} {count, plural, =1{повідомлення} few{повідомлення} many{повідомлень} other{повідомлень}}",
|
||||
"notification_channelMessagesCount": "{count} {count, plural, =1{повідомлення каналу} few{повідомлення каналу} many{повідомлень каналу} other{повідомлень каналу}}",
|
||||
"notification_newNodesCount": "{count} {count, plural, =1{новий вузол} few{нових вузли} many{нових вузлів} other{нових вузлів}}",
|
||||
"notification_newTypeDiscovered": "Виявлено новий {contactType}",
|
||||
"notification_receivedNewMessage": "Отримано нове повідомлення",
|
||||
"contacts_ShareContactZeroHop": "Поділитися контактом за оголошенням",
|
||||
"settings_gpxExportRepeaters": "Експортувати ретранслятори / сервер кімнати до GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "Експортує ретранслятори / сервер кімнати з місцезнаходженням у файл GPX.",
|
||||
"settings_gpxExportSuccess": "Успішно експортовано файл GPX.",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportAllContacts": "Усі місця контактів",
|
||||
"settings_gpxExportShareSubject": "експорт даних карти meshcore-open у форматі GPX",
|
||||
"pathTrace_someHopsNoLocation": "Одне або більше хмелів відсутнє місце розташування!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1569,6 +1569,13 @@
|
|||
"contacts_zeroHopContactAdvertFailed": "发送联系方式失败。",
|
||||
"contacts_contactAdvertCopied": "广告内容已复制到剪贴板。",
|
||||
"contacts_contactAdvertCopyFailed": "将广告复制到剪贴板操作失败。",
|
||||
"notification_activityTitle": "MeshCore 活动",
|
||||
"notification_messagesCount": "{count} 条消息",
|
||||
"notification_channelMessagesCount": "{count} 条频道消息",
|
||||
"notification_newNodesCount": "{count} 个新节点",
|
||||
"notification_newTypeDiscovered": "发现新 {contactType}",
|
||||
"notification_receivedNewMessage": "收到新消息",
|
||||
"contacts_contactAdvertCopyFailed": "将广告复制到剪贴板操作失败。",
|
||||
"settings_gpxExportRepeaters": "导出重复器/房间服务器到GPX",
|
||||
"settings_gpxExportRepeatersSubtitle": "导出带有位置的重复器/房间服务器到GPX文件。",
|
||||
"settings_gpxExportContactsSubtitle": "导出带有位置的伙伴到GPX文件。",
|
||||
|
|
@ -1585,4 +1592,5 @@
|
|||
"settings_gpxExportShareText": "来自meshcore-open的导出地图数据",
|
||||
"settings_gpxExportShareSubject": "meshcore-open GPX 地图数据导出",
|
||||
"pathTrace_someHopsNoLocation": "其中一个或多个啤酒花缺少位置!"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,6 +150,12 @@ class MeshCoreApp extends StatelessWidget {
|
|||
themeMode: _themeModeFromSetting(
|
||||
settingsService.settings.themeMode,
|
||||
),
|
||||
builder: (context, child) {
|
||||
// Update notification service with resolved locale
|
||||
final locale = Localizations.localeOf(context);
|
||||
NotificationService().setLocale(locale);
|
||||
return child ?? const SizedBox.shrink();
|
||||
},
|
||||
home: const ScannerScreen(),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class NotificationService {
|
||||
static final NotificationService _instance = NotificationService._internal();
|
||||
factory NotificationService() => _instance;
|
||||
|
|
@ -10,6 +14,34 @@ class NotificationService {
|
|||
FlutterLocalNotificationsPlugin();
|
||||
bool _isInitialized = false;
|
||||
|
||||
// Locale for localized notification strings
|
||||
Locale _locale = const Locale('en');
|
||||
|
||||
/// Set the locale for notification strings (call when app locale changes)
|
||||
void setLocale(Locale locale) {
|
||||
_locale = locale;
|
||||
}
|
||||
|
||||
AppLocalizations get _l10n => lookupAppLocalizations(_locale);
|
||||
|
||||
// Rate limiting to prevent notification storms
|
||||
// (Added after getting notification-flooded while evaluating RF flood management. The irony.)
|
||||
static const _minNotificationInterval = Duration(seconds: 3);
|
||||
static const _batchWindow = Duration(seconds: 5);
|
||||
|
||||
DateTime? _lastNotificationTime;
|
||||
final List<_PendingNotification> _pendingNotifications = [];
|
||||
bool _isBatchingActive = false;
|
||||
bool _suppressNotifications = false;
|
||||
|
||||
/// Temporarily suppress all notifications (e.g., during sync)
|
||||
void suppressNotifications(bool suppress) {
|
||||
_suppressNotifications = suppress;
|
||||
if (suppress) {
|
||||
_pendingNotifications.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> initialize() async {
|
||||
if (_isInitialized) return;
|
||||
|
||||
|
|
@ -76,7 +108,7 @@ class NotificationService {
|
|||
return true;
|
||||
}
|
||||
|
||||
Future<void> showMessageNotification({
|
||||
Future<void> _showMessageNotificationImpl({
|
||||
required String contactName,
|
||||
required String message,
|
||||
String? contactId,
|
||||
|
|
@ -125,7 +157,7 @@ class NotificationService {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> showAdvertNotification({
|
||||
Future<void> _showAdvertNotificationImpl({
|
||||
required String contactName,
|
||||
required String contactType,
|
||||
String? contactId,
|
||||
|
|
@ -163,14 +195,14 @@ class NotificationService {
|
|||
|
||||
await _notifications.show(
|
||||
contactId?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
|
||||
'New $contactType discovered',
|
||||
_l10n.notification_newTypeDiscovered(contactType),
|
||||
contactName,
|
||||
notificationDetails,
|
||||
payload: 'advert:$contactId',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showChannelMessageNotification({
|
||||
Future<void> _showChannelMessageNotificationImpl({
|
||||
required String channelName,
|
||||
required String message,
|
||||
int? channelIndex,
|
||||
|
|
@ -211,7 +243,9 @@ class NotificationService {
|
|||
);
|
||||
|
||||
final preview = message.trim();
|
||||
final body = preview.isEmpty ? 'Received new message' : preview;
|
||||
final body = preview.isEmpty
|
||||
? _l10n.notification_receivedNewMessage
|
||||
: preview;
|
||||
|
||||
await _notifications.show(
|
||||
channelIndex?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
|
||||
|
|
@ -222,6 +256,21 @@ class NotificationService {
|
|||
);
|
||||
}
|
||||
|
||||
/// Returns a privacy-safe identifier for debug logging.
|
||||
/// - advert: shows device name (body contains contactName)
|
||||
/// - message: shows "from: sender" (avoids logging message content)
|
||||
/// - channelMessage: shows "in: channel" (avoids logging message content)
|
||||
String _getNotificationIdentifier(_PendingNotification n) {
|
||||
switch (n.type) {
|
||||
case _NotificationType.advert:
|
||||
return n.body;
|
||||
case _NotificationType.message:
|
||||
return 'from: ${n.title}';
|
||||
case _NotificationType.channelMessage:
|
||||
return 'in: ${n.title}';
|
||||
}
|
||||
}
|
||||
|
||||
void _onNotificationTapped(NotificationResponse response) {
|
||||
final payload = response.payload;
|
||||
if (payload != null) {
|
||||
|
|
@ -238,4 +287,212 @@ class NotificationService {
|
|||
Future<void> cancel(int id) async {
|
||||
await _notifications.cancel(id);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// Public notification methods (rate limiting is enforced automatically)
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
Future<void> showMessageNotification({
|
||||
required String contactName,
|
||||
required String message,
|
||||
String? contactId,
|
||||
int? badgeCount,
|
||||
}) async {
|
||||
if (_suppressNotifications) return;
|
||||
|
||||
_queueNotification(
|
||||
_PendingNotification(
|
||||
type: _NotificationType.message,
|
||||
title: contactName,
|
||||
body: message,
|
||||
id: contactId,
|
||||
badgeCount: badgeCount,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showAdvertNotification({
|
||||
required String contactName,
|
||||
required String contactType,
|
||||
String? contactId,
|
||||
}) async {
|
||||
if (_suppressNotifications) return;
|
||||
|
||||
_queueNotification(
|
||||
_PendingNotification(
|
||||
type: _NotificationType.advert,
|
||||
title: contactType,
|
||||
body: contactName,
|
||||
id: contactId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showChannelMessageNotification({
|
||||
required String channelName,
|
||||
required String message,
|
||||
int? channelIndex,
|
||||
int? badgeCount,
|
||||
}) async {
|
||||
if (_suppressNotifications) return;
|
||||
|
||||
_queueNotification(
|
||||
_PendingNotification(
|
||||
type: _NotificationType.channelMessage,
|
||||
title: channelName,
|
||||
body: message,
|
||||
id: channelIndex?.toString(),
|
||||
badgeCount: badgeCount,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _queueNotification(_PendingNotification notification) {
|
||||
final now = DateTime.now();
|
||||
|
||||
// If we recently showed a notification, start batching
|
||||
if (_lastNotificationTime != null &&
|
||||
now.difference(_lastNotificationTime!) < _minNotificationInterval) {
|
||||
_pendingNotifications.add(notification);
|
||||
debugPrint(
|
||||
'[Notification] queued: ${notification.type.name} (${_getNotificationIdentifier(notification)})',
|
||||
);
|
||||
|
||||
// Start batch timer if not already running
|
||||
if (!_isBatchingActive) {
|
||||
_isBatchingActive = true;
|
||||
Future.delayed(_batchWindow, _processBatch);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Show immediately if enough time has passed
|
||||
debugPrint(
|
||||
'[Notification] sent immediately: ${notification.type.name} (${_getNotificationIdentifier(notification)})',
|
||||
);
|
||||
_showNotificationImmediately(notification);
|
||||
_lastNotificationTime = now;
|
||||
}
|
||||
|
||||
Future<void> _processBatch() async {
|
||||
_isBatchingActive = false;
|
||||
|
||||
if (_pendingNotifications.isEmpty) return;
|
||||
|
||||
final batch = List<_PendingNotification>.from(_pendingNotifications);
|
||||
_pendingNotifications.clear();
|
||||
|
||||
if (batch.length == 1) {
|
||||
// Single notification, show normally
|
||||
_showNotificationImmediately(batch.first);
|
||||
} else {
|
||||
// Multiple notifications, show summary
|
||||
await _showBatchSummary(batch);
|
||||
}
|
||||
|
||||
_lastNotificationTime = DateTime.now();
|
||||
}
|
||||
|
||||
Future<void> _showNotificationImmediately(
|
||||
_PendingNotification notification,
|
||||
) async {
|
||||
switch (notification.type) {
|
||||
case _NotificationType.message:
|
||||
await _showMessageNotificationImpl(
|
||||
contactName: notification.title,
|
||||
message: notification.body,
|
||||
contactId: notification.id,
|
||||
badgeCount: notification.badgeCount,
|
||||
);
|
||||
break;
|
||||
case _NotificationType.advert:
|
||||
await _showAdvertNotificationImpl(
|
||||
contactName: notification.body,
|
||||
contactType: notification.title,
|
||||
contactId: notification.id,
|
||||
);
|
||||
break;
|
||||
case _NotificationType.channelMessage:
|
||||
await _showChannelMessageNotificationImpl(
|
||||
channelName: notification.title,
|
||||
message: notification.body,
|
||||
channelIndex: int.tryParse(notification.id ?? ''),
|
||||
badgeCount: notification.badgeCount,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showBatchSummary(List<_PendingNotification> batch) async {
|
||||
if (!_isInitialized) await initialize();
|
||||
|
||||
// Group by type
|
||||
final messages = batch
|
||||
.where((n) => n.type == _NotificationType.message)
|
||||
.toList();
|
||||
final adverts = batch
|
||||
.where((n) => n.type == _NotificationType.advert)
|
||||
.toList();
|
||||
final channelMsgs = batch
|
||||
.where((n) => n.type == _NotificationType.channelMessage)
|
||||
.toList();
|
||||
|
||||
// Build summary text using localized plurals
|
||||
final parts = <String>[];
|
||||
if (messages.isNotEmpty) {
|
||||
parts.add(_l10n.notification_messagesCount(messages.length));
|
||||
}
|
||||
if (channelMsgs.isNotEmpty) {
|
||||
parts.add(_l10n.notification_channelMessagesCount(channelMsgs.length));
|
||||
}
|
||||
if (adverts.isNotEmpty) {
|
||||
parts.add(_l10n.notification_newNodesCount(adverts.length));
|
||||
}
|
||||
|
||||
if (parts.isEmpty) return;
|
||||
|
||||
// Show first few device names in batch summary for debugging (only if adverts exist)
|
||||
final deviceInfo = adverts.isNotEmpty
|
||||
? ' (${adverts.take(5).map((n) => n.body).join(', ')}${adverts.length > 5 ? ', ...' : ''})'
|
||||
: '';
|
||||
debugPrint('[Notification] batch summary: ${parts.join(", ")}$deviceInfo');
|
||||
|
||||
const androidDetails = AndroidNotificationDetails(
|
||||
'batch_summary',
|
||||
'Activity Summary',
|
||||
channelDescription: 'Batched notification summaries',
|
||||
importance: Importance.defaultImportance,
|
||||
priority: Priority.defaultPriority,
|
||||
icon: '@mipmap/ic_launcher',
|
||||
);
|
||||
|
||||
const notificationDetails = NotificationDetails(android: androidDetails);
|
||||
|
||||
await _notifications.show(
|
||||
'batch_summary'.hashCode,
|
||||
_l10n.notification_activityTitle,
|
||||
parts.join(', '),
|
||||
notificationDetails,
|
||||
payload: 'batch',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper class for pending notifications
|
||||
enum _NotificationType { message, advert, channelMessage }
|
||||
|
||||
class _PendingNotification {
|
||||
final _NotificationType type;
|
||||
final String title;
|
||||
final String body;
|
||||
final String? id;
|
||||
final int? badgeCount;
|
||||
|
||||
_PendingNotification({
|
||||
required this.type,
|
||||
required this.title,
|
||||
required this.body,
|
||||
this.id,
|
||||
this.badgeCount,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,127 @@
|
|||
{}
|
||||
{
|
||||
"bg": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"de": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"nl": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"pl": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"sk": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"sl": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"sv": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"uk": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
"notification_activityTitle",
|
||||
"notification_messagesCount",
|
||||
"notification_channelMessagesCount",
|
||||
"notification_newNodesCount",
|
||||
"notification_newTypeDiscovered",
|
||||
"notification_receivedNewMessage"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue