@@ -74,6 +74,17 @@ class NotificationService: UNNotificationServiceExtension {
7474 private static let backgroundServiceInitQueue = DispatchQueue ( label: " io.element.NotificationService.backgroundServiceInitQueue " )
7575 // MARK: - Method Overrides
7676
77+ override init ( ) {
78+ super. init ( )
79+
80+ // Set up runtime language and fallback by considering the userDefaults object shared within the application group.
81+ let sharedUserDefaults = MXKAppSettings . standard ( ) . sharedUserDefaults
82+ if let language = sharedUserDefaults? . string ( forKey: " appLanguage " ) {
83+ Bundle . mxk_setLanguage ( language)
84+ }
85+ Bundle . mxk_setFallbackLanguage ( " en " )
86+ }
87+
7788 override func didReceive( _ request: UNNotificationRequest , withContentHandler contentHandler: @escaping ( UNNotificationContent ) -> Void ) {
7889 let userInfo = request. content. userInfo
7990
@@ -341,9 +352,9 @@ class NotificationService: UNNotificationServiceExtension {
341352 let isVideoCall = sdp? . contains ( " m=video " ) ?? false
342353
343354 if isVideoCall {
344- notificationBody = NSString . localizedUserNotificationString ( forKey: " VIDEO_CALL_FROM_USER " , arguments : [ eventSenderName as Any ] )
355+ notificationBody = NotificationService . localizedString ( forKey: " VIDEO_CALL_FROM_USER " , eventSenderName)
345356 } else {
346- notificationBody = NSString . localizedUserNotificationString ( forKey: " VOICE_CALL_FROM_USER " , arguments : [ eventSenderName as Any ] )
357+ notificationBody = NotificationService . localizedString ( forKey: " VOICE_CALL_FROM_USER " , eventSenderName)
347358 }
348359
349360 // call notifications should stand out from normal messages, so we don't stack them
@@ -405,7 +416,7 @@ class NotificationService: UNNotificationServiceExtension {
405416 }
406417
407418 let msgType = event. content [ kMXMessageTypeKey] as? String
408- let messageContent = event. content [ kMXMessageBodyKey] as? String
419+ let messageContent = event. content [ kMXMessageBodyKey] as? String ?? " "
409420 let isReply = event. isReply ( )
410421
411422 if isReply {
@@ -416,30 +427,30 @@ class NotificationService: UNNotificationServiceExtension {
416427
417428 if event. isEncrypted && !self . showDecryptedContentInNotifications {
418429 // Hide the content
419- notificationBody = NSString . localizedUserNotificationString ( forKey: " MESSAGE " , arguments : [ ] )
430+ notificationBody = NotificationService . localizedString ( forKey: " MESSAGE " )
420431 break
421432 }
422433
423434 if event. location != nil {
424- notificationBody = NSString . localizedUserNotificationString ( forKey: " LOCATION_FROM_USER " , arguments : [ eventSenderName] )
435+ notificationBody = NotificationService . localizedString ( forKey: " LOCATION_FROM_USER " , eventSenderName)
425436 break
426437 }
427438
428439 switch msgType {
429440 case kMXMessageTypeEmote:
430- notificationBody = NSString . localizedUserNotificationString ( forKey: " ACTION_FROM_USER " , arguments : [ eventSenderName, messageContent as Any ] )
441+ notificationBody = NotificationService . localizedString ( forKey: " ACTION_FROM_USER " , eventSenderName, messageContent)
431442 case kMXMessageTypeImage:
432- notificationBody = NSString . localizedUserNotificationString ( forKey: " PICTURE_FROM_USER " , arguments : [ eventSenderName] )
443+ notificationBody = NotificationService . localizedString ( forKey: " PICTURE_FROM_USER " , eventSenderName)
433444 case kMXMessageTypeVideo:
434- notificationBody = NSString . localizedUserNotificationString ( forKey: " VIDEO_FROM_USER " , arguments : [ eventSenderName] )
445+ notificationBody = NotificationService . localizedString ( forKey: " VIDEO_FROM_USER " , eventSenderName)
435446 case kMXMessageTypeAudio:
436447 if event. isVoiceMessage ( ) {
437- notificationBody = NSString . localizedUserNotificationString ( forKey: " VOICE_MESSAGE_FROM_USER " , arguments : [ eventSenderName] )
448+ notificationBody = NotificationService . localizedString ( forKey: " VOICE_MESSAGE_FROM_USER " , eventSenderName)
438449 } else {
439- notificationBody = NSString . localizedUserNotificationString ( forKey: " AUDIO_FROM_USER " , arguments : [ eventSenderName, messageContent as Any ] )
450+ notificationBody = NotificationService . localizedString ( forKey: " AUDIO_FROM_USER " , eventSenderName, messageContent)
440451 }
441452 case kMXMessageTypeFile:
442- notificationBody = NSString . localizedUserNotificationString ( forKey: " FILE_FROM_USER " , arguments : [ eventSenderName, messageContent as Any ] )
453+ notificationBody = NotificationService . localizedString ( forKey: " FILE_FROM_USER " , eventSenderName, messageContent)
443454
444455 // All other message types such as text, notice, server notice etc
445456 default :
@@ -469,50 +480,50 @@ class NotificationService: UNNotificationServiceExtension {
469480 // If there was a change, use the sender's userID if one was blank and show the change.
470481 if let oldDisplayname = oldContent. displayname ?? event. sender,
471482 let displayname = newContent. displayname ?? event. sender {
472- notificationBody = NSString . localizedUserNotificationString ( forKey: " USER_UPDATED_DISPLAYNAME " , arguments : [ oldDisplayname, displayname] )
483+ notificationBody = NotificationService . localizedString ( forKey: " USER_UPDATED_DISPLAYNAME " , oldDisplayname, displayname)
473484 } else {
474485 // Should never be reached as the event should always have a sender.
475- notificationBody = NSString . localizedUserNotificationString ( forKey: " GENERIC_USER_UPDATED_DISPLAYNAME " , arguments : [ eventSenderName] )
486+ notificationBody = NotificationService . localizedString ( forKey: " GENERIC_USER_UPDATED_DISPLAYNAME " , eventSenderName)
476487 }
477488 } else {
478489 // If the display name hasn't changed, handle as an avatar change.
479- notificationBody = NSString . localizedUserNotificationString ( forKey: " USER_UPDATED_AVATAR " , arguments : [ eventSenderName] )
490+ notificationBody = NotificationService . localizedString ( forKey: " USER_UPDATED_AVATAR " , eventSenderName)
480491 }
481492 } else {
482493 // No known reports of having reached this situation for a membership notification
483494 // So use a generic membership updated fallback.
484- notificationBody = NSString . localizedUserNotificationString ( forKey: " USER_MEMBERSHIP_UPDATED " , arguments : [ eventSenderName] )
495+ notificationBody = NotificationService . localizedString ( forKey: " USER_MEMBERSHIP_UPDATED " , eventSenderName)
485496 }
486497 // Otherwise treat the notification as an invite.
487498 // This is the expected notification content for a membership event.
488499 } else {
489- if roomDisplayName != nil && roomDisplayName != eventSenderName {
490- notificationBody = NSString . localizedUserNotificationString ( forKey: " USER_INVITE_TO_NAMED_ROOM " , arguments : [ eventSenderName, roomDisplayName as Any ] )
500+ if let roomDisplayName = roomDisplayName , roomDisplayName != eventSenderName {
501+ notificationBody = NotificationService . localizedString ( forKey: " USER_INVITE_TO_NAMED_ROOM " , eventSenderName, roomDisplayName)
491502 } else {
492- notificationBody = NSString . localizedUserNotificationString ( forKey: " USER_INVITE_TO_CHAT " , arguments : [ eventSenderName] )
503+ notificationBody = NotificationService . localizedString ( forKey: " USER_INVITE_TO_CHAT " , eventSenderName)
493504 }
494505 }
495506
496507 case . sticker:
497508 notificationTitle = self . messageTitle ( for: eventSenderName, in: roomDisplayName)
498- notificationBody = NSString . localizedUserNotificationString ( forKey: " STICKER_FROM_USER " , arguments : [ eventSenderName as Any ] )
509+ notificationBody = NotificationService . localizedString ( forKey: " STICKER_FROM_USER " , eventSenderName)
499510
500511 // Reactions are unexpected notification types, but have been seen in some circumstances.
501512 case . reaction:
502513 notificationTitle = self . messageTitle ( for: eventSenderName, in: roomDisplayName)
503514 if let reactionKey = event. relatesTo? . key {
504515 // Try to show the reaction key in the notification.
505- notificationBody = NSString . localizedUserNotificationString ( forKey: " REACTION_FROM_USER " , arguments : [ eventSenderName, reactionKey] )
516+ notificationBody = NotificationService . localizedString ( forKey: " REACTION_FROM_USER " , eventSenderName, reactionKey)
506517 } else {
507518 // Otherwise show a generic reaction.
508- notificationBody = NSString . localizedUserNotificationString ( forKey: " GENERIC_REACTION_FROM_USER " , arguments : [ eventSenderName] )
519+ notificationBody = NotificationService . localizedString ( forKey: " GENERIC_REACTION_FROM_USER " , eventSenderName)
509520 }
510521
511522 case . custom:
512523 if ( event. type == kWidgetMatrixEventTypeString || event. type == kWidgetModularEventTypeString) ,
513524 let type = event. content ? [ " type " ] as? String ,
514525 ( type == kWidgetTypeJitsiV1 || type == kWidgetTypeJitsiV2) {
515- notificationBody = NSString . localizedUserNotificationString ( forKey: " GROUP_CALL_STARTED " , arguments : nil )
526+ notificationBody = NotificationService . localizedString ( forKey: " GROUP_CALL_STARTED " )
516527 notificationTitle = roomDisplayName
517528
518529 // call notifications should stand out from normal messages, so we don't stack them
@@ -566,7 +577,7 @@ class NotificationService: UNNotificationServiceExtension {
566577 var validatedNotificationTitle : String ? = notificationTitle
567578 if self . localAuthenticationService. isProtectionSet {
568579 MXLog . debug ( " [NotificationService] validateNotificationContentAndComplete: Resetting title and body because app protection is set " )
569- validatedNotificationBody = NSString . localizedUserNotificationString ( forKey: " MESSAGE_PROTECTED " , arguments : [ ] )
580+ validatedNotificationBody = NotificationService . localizedString ( forKey: " MESSAGE_PROTECTED " )
570581 validatedNotificationTitle = nil
571582 }
572583
@@ -596,7 +607,7 @@ class NotificationService: UNNotificationServiceExtension {
596607 private func messageTitle( for eventSenderName: String , in roomDisplayName: String ? ) -> String {
597608 // Display the room name only if it is different than the sender name
598609 if let roomDisplayName = roomDisplayName, roomDisplayName != eventSenderName {
599- return NSString . localizedUserNotificationString ( forKey: " MSG_FROM_USER_IN_ROOM_TITLE " , arguments : [ eventSenderName, roomDisplayName] )
610+ return NotificationService . localizedString ( forKey: " MSG_FROM_USER_IN_ROOM_TITLE " , eventSenderName, roomDisplayName)
600611 } else {
601612 return eventSenderName
602613 }
@@ -605,9 +616,9 @@ class NotificationService: UNNotificationServiceExtension {
605616 private func replyTitle( for eventSenderName: String , in roomDisplayName: String ? ) -> String {
606617 // Display the room name only if it is different than the sender name
607618 if let roomDisplayName = roomDisplayName, roomDisplayName != eventSenderName {
608- return NSString . localizedUserNotificationString ( forKey: " REPLY_FROM_USER_IN_ROOM_TITLE " , arguments : [ eventSenderName, roomDisplayName] )
619+ return NotificationService . localizedString ( forKey: " REPLY_FROM_USER_IN_ROOM_TITLE " , eventSenderName, roomDisplayName)
609620 } else {
610- return NSString . localizedUserNotificationString ( forKey: " REPLY_FROM_USER_TITLE " , arguments : [ eventSenderName] )
621+ return NotificationService . localizedString ( forKey: " REPLY_FROM_USER_TITLE " , eventSenderName)
611622 }
612623 }
613624
@@ -816,4 +827,13 @@ class NotificationService: UNNotificationServiceExtension {
816827 }
817828 }
818829 }
830+
831+ private static func localizedString( forKey key: String , _ args: CVarArg ... ) -> String {
832+ // The bundle needs to be an MXKLanguageBundle and contain the lproj files.
833+ // MatrixKit now sets the app bundle as the MXKLanguageBundle
834+ let format = NSLocalizedString ( key, bundle: Bundle . app, comment: " " )
835+ let locale = LocaleProvider . locale ?? Locale . current
836+
837+ return String ( format: format, locale: locale, arguments: args)
838+ }
819839}
0 commit comments