From b5db1fc42657845847c10891daecd287524644a9 Mon Sep 17 00:00:00 2001 From: Ayush7614 Date: Sun, 2 Aug 2026 01:02:52 +0530 Subject: [PATCH 1/2] Localize non-redacted notification titles and nearby bodies. Redacted lock-screen copy was already localized; the preview-on path still hardcoded English for mentions, DMs, and nearby peer alerts. --- bitchat/Localizable.xcstrings | 935 ++++++++++++++++++ bitchat/Services/NotificationService.swift | 55 +- .../Services/NotificationServiceTests.swift | 26 +- 3 files changed, 1010 insertions(+), 6 deletions(-) diff --git a/bitchat/Localizable.xcstrings b/bitchat/Localizable.xcstrings index f869ab85..f66e8ca8 100644 --- a/bitchat/Localizable.xcstrings +++ b/bitchat/Localizable.xcstrings @@ -65869,6 +65869,941 @@ } } }, + "notification.mention.title" : { + "comment" : "Lock-screen notification title when someone is mentioned; %@ is the sender nickname", + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 du wurdest von %@ erwähnt" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 te ha mencionado %@" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 %@ t'a mentionné" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 %@ ने आपका उल्लेख किया" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 sei stato menzionato da %@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 %@さんにメンションされました" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 %@님이 회원님을 언급했습니다" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 je bent genoemd door %@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 wspomniał o tobie %@" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 você foi mencionado por %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 вас упомянул(а) %@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 du nämndes av %@" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 %@ senden bahsetti" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 you were mentioned by %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 %@ 提到了你" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "🫵 %@ 提到了你" + } + } + } + }, + + "notification.dm.title" : { + "comment" : "Lock-screen notification title for a direct message; %@ is the sender nickname", + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM von %@" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 MD de %@" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM de %@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 %@ से DM" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM da %@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 %@さんからのDM" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 %@님의 DM" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM van %@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 PW od %@" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM de %@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 ЛС от %@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM från %@" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 %@ kişisinden DM" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 DM from %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 来自 %@ 的私信" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "🔒 來自 %@ 的私訊" + } + } + } + }, + + "notification.nearby.title" : { + "comment" : "Lock-screen notification title when mesh peers are nearby", + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatter in der Nähe!" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 ¡bitchatters cerca!" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 des bitchatters à proximité !" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 पास में bitchatter हैं!" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatter nelle vicinanze!" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 近くにbitchatterがいます!" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 근처에 bitchatter가 있어요!" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters in de buurt!" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatterzy w pobliżu!" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters por perto!" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 рядом есть bitchatters!" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters i närheten!" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 yakında bitchatter'lar var!" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 bitchatters nearby!" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 附近有 bitchatter!" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "👥 附近有 bitchatter!" + } + } + } + }, + + "notification.nearby.body.one" : { + "comment" : "Lock-screen notification body when exactly one mesh peer is nearby", + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 Person in der Nähe" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 persona alrededor" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 personne à proximité" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आसपास 1 व्यक्ति" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 persona intorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "周囲に1人" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "주변에 1명" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 persoon in de buurt" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 osoba w pobliżu" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 pessoa por perto" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 человек рядом" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person i närheten" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "etrafta 1 kişi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 person around" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 1 人" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 1 人" + } + } + } + }, + + "notification.nearby.body.other" : { + "comment" : "Lock-screen notification body when multiple mesh peers are nearby; %lld is the peer count", + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Personen in der Nähe" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personas alrededor" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personnes à proximité" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आसपास %lld व्यक्ति" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld persone intorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "周囲に%lld人" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "주변에 %lld명" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personen in de buurt" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld osób w pobliżu" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pessoas por perto" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld человек рядом" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personer i närheten" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "etrafta %lld kişi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 %lld 人" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 %lld 人" + } + } + } + }, + "notification.redacted.body" : { "comment" : "Lock-screen notification body shown in place of the message text when message previews are hidden", "extractionState" : "manual", diff --git a/bitchat/Services/NotificationService.swift b/bitchat/Services/NotificationService.swift index dc8ef193..2c99e2ed 100644 --- a/bitchat/Services/NotificationService.swift +++ b/bitchat/Services/NotificationService.swift @@ -229,7 +229,20 @@ final class NotificationService { } func sendMentionNotification(from sender: String, message: String) { - let title = hidePreviews ? Redacted.mentionTitle : "🫵 you were mentioned by \(sender)" + let title: String + if hidePreviews { + title = Redacted.mentionTitle + } else { + title = String( + format: String( + localized: "notification.mention.title", + defaultValue: "🫵 you were mentioned by %@", + comment: "Lock-screen notification title when someone is mentioned; %@ is the sender nickname" + ), + locale: .current, + sender + ) + } let body = hidePreviews ? Redacted.body : message let identifier = "mention-\(UUID().uuidString)" @@ -237,7 +250,20 @@ final class NotificationService { } func sendPrivateMessageNotification(from sender: String, message: String, peerID: PeerID) { - let title = hidePreviews ? Redacted.directMessageTitle : "🔒 DM from \(sender)" + let title: String + if hidePreviews { + title = Redacted.directMessageTitle + } else { + title = String( + format: String( + localized: "notification.dm.title", + defaultValue: "🔒 DM from %@", + comment: "Lock-screen notification title for a direct message; %@ is the sender nickname" + ), + locale: .current, + sender + ) + } let body = hidePreviews ? Redacted.body : message let identifier = "private-\(UUID().uuidString)" // Routing payload, not display copy: `userInfo` never reaches the lock @@ -260,8 +286,29 @@ final class NotificationService { } func sendNetworkAvailableNotification(peerCount: Int) { - let title = "👥 bitchatters nearby!" - let body = peerCount == 1 ? "1 person around" : "\(peerCount) people around" + let title = String( + localized: "notification.nearby.title", + defaultValue: "👥 bitchatters nearby!", + comment: "Lock-screen notification title when mesh peers are nearby" + ) + let body: String + if peerCount == 1 { + body = String( + localized: "notification.nearby.body.one", + defaultValue: "1 person around", + comment: "Lock-screen notification body when exactly one mesh peer is nearby" + ) + } else { + body = String( + format: String( + localized: "notification.nearby.body.other", + defaultValue: "%lld people around", + comment: "Lock-screen notification body when multiple mesh peers are nearby; %lld is the peer count" + ), + locale: .current, + peerCount + ) + } // Fixed identifier so iOS updates the existing notification instead of creating new ones let identifier = "network-available" diff --git a/bitchatTests/Services/NotificationServiceTests.swift b/bitchatTests/Services/NotificationServiceTests.swift index 384f0075..da7f4ee7 100644 --- a/bitchatTests/Services/NotificationServiceTests.swift +++ b/bitchatTests/Services/NotificationServiceTests.swift @@ -71,7 +71,18 @@ final class NotificationServiceTests: XCTestCase { service.sendPrivateMessageNotification(from: "Alice", message: "hi", peerID: peerID) let request = deliverer.requests.singleValue - XCTAssertEqual(request?.content.title, "🔒 DM from Alice") + XCTAssertEqual( + request?.content.title, + String( + format: String( + localized: "notification.dm.title", + defaultValue: "🔒 DM from %@", + comment: "Lock-screen notification title for a direct message; %@ is the sender nickname" + ), + locale: .current, + "Alice" + ) + ) XCTAssertEqual(request?.content.body, "hi") XCTAssertEqual(request?.content.userInfo["peerID"] as? String, peerID.id) XCTAssertEqual(request?.content.userInfo["senderName"] as? String, "Alice") @@ -114,7 +125,18 @@ final class NotificationServiceTests: XCTestCase { XCTAssertTrue(deliverer.requests[0].identifier.hasPrefix("geo-activity-87yv-")) XCTAssertEqual(deliverer.requests[1].identifier, "network-available") XCTAssertEqual(deliverer.requests[1].content.interruptionLevel, .timeSensitive) - XCTAssertEqual(deliverer.requests[1].content.body, "2 people around") + XCTAssertEqual( + deliverer.requests[1].content.body, + String( + format: String( + localized: "notification.nearby.body.other", + defaultValue: "%lld people around", + comment: "Lock-screen notification body when multiple mesh peers are nearby; %lld is the peer count" + ), + locale: .current, + 2 + ) + ) } } From 64613895c5add4b7385584e50e1019c8f2574299 Mon Sep 17 00:00:00 2001 From: Ayush7614 Date: Mon, 10 Aug 2026 16:28:08 +0530 Subject: [PATCH 2/2] Use plural variations for nearby notification body. Replace hand-split notification.nearby.body.one/other with a single %#@people@ catalog key aligned to people_count plural categories, and route sendNetworkAvailableNotification through nearbyBody(peerCount:). --- bitchat/Localizable.xcstrings | 1272 ++++++++++++----- bitchat/Services/NotificationService.swift | 31 +- .../Services/NotificationServiceTests.swift | 23 +- 3 files changed, 927 insertions(+), 399 deletions(-) diff --git a/bitchat/Localizable.xcstrings b/bitchat/Localizable.xcstrings index f66e8ca8..81402fec 100644 --- a/bitchat/Localizable.xcstrings +++ b/bitchat/Localizable.xcstrings @@ -66243,6 +66243,906 @@ } }, + "notification.nearby.body" : { + "comment" : "Lock-screen notification body for mesh peers nearby; %#@people@ is the peer count with plural variations", + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Person in der Nähe" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Personen in der Nähe" + } + } + } + } + } + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld persona alrededor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personas alrededor" + } + } + } + } + } + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personne à proximité" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personnes à proximité" + } + } + } + } + } + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "आसपास 1 व्यक्ति" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "आसपास %lld व्यक्ति" + } + } + } + } + } + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld persona intorno" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld persone intorno" + } + } + } + } + } + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "周囲に%lld人" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "周囲に%lld人" + } + } + } + } + } + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "주변에 %lld명" + } + } + } + } + } + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld persoon in de buurt" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personen in de buurt" + } + } + } + } + } + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld osoba w pobliżu" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld osób w pobliżu" + } + } + } + } + } + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pessoa por perto" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pessoas por perto" + } + } + } + } + } + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld человек рядом" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld человек рядом" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld человек рядом" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld человек рядом" + } + } + } + } + } + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person i närheten" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld personer i närheten" + } + } + } + } + } + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "etrafta 1 kişi" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "etrafta %lld kişi" + } + } + } + } + } + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld person around" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld people around" + } + } + } + } + } + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 %lld 人" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 %lld 人" + } + } + } + } + } + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "%#@people@" + }, + "substitutions" : { + "people" : { + "argNum" : 1, + "formatSpecifier" : "lld", + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 %lld 人" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "附近有 %lld 人" + } + } + } + } + } + } + } + } + }, "notification.nearby.title" : { "comment" : "Lock-screen notification title when mesh peers are nearby", "extractionState" : "manual", @@ -66430,379 +67330,7 @@ } }, - "notification.nearby.body.one" : { - "comment" : "Lock-screen notification body when exactly one mesh peer is nearby", - "extractionState" : "manual", - "localizations" : { - "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "bn" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 Person in der Nähe" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 persona alrededor" - } - }, - "fa" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "fil" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 personne à proximité" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "आसपास 1 व्यक्ति" - } - }, - "id" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 persona intorno" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "周囲に1人" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "주변에 1명" - } - }, - "ms" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "ne" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 persoon in de buurt" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 osoba w pobliżu" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 pessoa por perto" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 человек рядом" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person i närheten" - } - }, - "ta" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "etrafta 1 kişi" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "ur" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "vi" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 person around" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "附近有 1 人" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "附近有 1 人" - } - } - } - }, - "notification.nearby.body.other" : { - "comment" : "Lock-screen notification body when multiple mesh peers are nearby; %lld is the peer count", - "extractionState" : "manual", - "localizations" : { - "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "bn" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Personen in der Nähe" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld personas alrededor" - } - }, - "fa" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "fil" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld personnes à proximité" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "आसपास %lld व्यक्ति" - } - }, - "id" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld persone intorno" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "周囲に%lld人" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "주변에 %lld명" - } - }, - "ms" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "ne" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld personen in de buurt" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld osób w pobliżu" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld pessoas por perto" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld человек рядом" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld personer i närheten" - } - }, - "ta" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "etrafta %lld kişi" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "ur" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "vi" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld people around" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "附近有 %lld 人" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "附近有 %lld 人" - } - } - } - }, "notification.redacted.body" : { "comment" : "Lock-screen notification body shown in place of the message text when message previews are hidden", diff --git a/bitchat/Services/NotificationService.swift b/bitchat/Services/NotificationService.swift index 2c99e2ed..015dcaac 100644 --- a/bitchat/Services/NotificationService.swift +++ b/bitchat/Services/NotificationService.swift @@ -291,24 +291,7 @@ final class NotificationService { defaultValue: "👥 bitchatters nearby!", comment: "Lock-screen notification title when mesh peers are nearby" ) - let body: String - if peerCount == 1 { - body = String( - localized: "notification.nearby.body.one", - defaultValue: "1 person around", - comment: "Lock-screen notification body when exactly one mesh peer is nearby" - ) - } else { - body = String( - format: String( - localized: "notification.nearby.body.other", - defaultValue: "%lld people around", - comment: "Lock-screen notification body when multiple mesh peers are nearby; %lld is the peer count" - ), - locale: .current, - peerCount - ) - } + let body = Self.nearbyBody(peerCount: peerCount) // Fixed identifier so iOS updates the existing notification instead of creating new ones let identifier = "network-available" @@ -320,4 +303,16 @@ final class NotificationService { categoryIdentifier: Self.nearbyCategoryID ) } + + static func nearbyBody(peerCount: Int) -> String { + String( + format: String( + localized: "notification.nearby.body", + defaultValue: "%#@people@", + comment: "Lock-screen notification body for mesh peers nearby; %#@people@ is the peer count with plural variations" + ), + locale: .current, + peerCount + ) + } } diff --git a/bitchatTests/Services/NotificationServiceTests.swift b/bitchatTests/Services/NotificationServiceTests.swift index da7f4ee7..c27b83f3 100644 --- a/bitchatTests/Services/NotificationServiceTests.swift +++ b/bitchatTests/Services/NotificationServiceTests.swift @@ -127,17 +127,22 @@ final class NotificationServiceTests: XCTestCase { XCTAssertEqual(deliverer.requests[1].content.interruptionLevel, .timeSensitive) XCTAssertEqual( deliverer.requests[1].content.body, - String( - format: String( - localized: "notification.nearby.body.other", - defaultValue: "%lld people around", - comment: "Lock-screen notification body when multiple mesh peers are nearby; %lld is the peer count" - ), - locale: .current, - 2 - ) + NotificationService.nearbyBody(peerCount: 2) ) } + + func test_sendNetworkAvailableNotification_onePeerUsesPluralBody() { + let deliverer = RecordingNotificationRequestDeliverer() + let service = NotificationService( + isRunningTestsProvider: { false }, + authorizer: RecordingNotificationAuthorizer(), + requestDeliverer: deliverer + ) + + service.sendNetworkAvailableNotification(peerCount: 1) + + XCTAssertEqual(deliverer.requests.singleValue?.content.body, NotificationService.nearbyBody(peerCount: 1)) + } } private final class RecordingNotificationAuthorizer: NotificationAuthorizing {