Line data Source code
1 : /*
2 : * Famedly Matrix SDK
3 : * Copyright (C) 2019, 2020, 2021 Famedly GmbH
4 : *
5 : * This program is free software: you can redistribute it and/or modify
6 : * it under the terms of the GNU Affero General Public License as
7 : * published by the Free Software Foundation, either version 3 of the
8 : * License, or (at your option) any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU Affero General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Affero General Public License
16 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 : */
18 :
19 : import 'package:collection/collection.dart';
20 :
21 : import 'package:matrix/encryption.dart';
22 : import 'package:matrix/matrix.dart';
23 :
24 : abstract class EventLocalizations {
25 : // As we need to create the localized body off of a different set of parameters, we
26 : // might create it with `event.plaintextBody`, maybe with `event.body`, maybe with the
27 : // reply fallback stripped, and maybe with the new body in `event.content['m.new_content']`.
28 : // Thus, it seems easier to offload that logic into `Event.getLocalizedBody()` and pass the
29 : // `body` variable around here.
30 1 : static String _localizedBodyNormalMessage(
31 : Event event, MatrixLocalizations i18n, String body) {
32 1 : switch (event.messageType) {
33 1 : case MessageTypes.Image:
34 1 : return i18n.sentAPicture(
35 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
36 1 : case MessageTypes.File:
37 1 : return i18n.sentAFile(
38 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
39 1 : case MessageTypes.Audio:
40 1 : return i18n.sentAnAudio(
41 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
42 1 : case MessageTypes.Video:
43 1 : return i18n.sentAVideo(
44 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
45 1 : case MessageTypes.Location:
46 1 : return i18n.sharedTheLocation(
47 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
48 1 : case MessageTypes.Sticker:
49 0 : return i18n.sentASticker(
50 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
51 1 : case MessageTypes.Emote:
52 1 : return '* $body';
53 1 : case EventTypes.KeyVerificationRequest:
54 1 : return i18n.requestedKeyVerification(
55 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
56 1 : case EventTypes.KeyVerificationCancel:
57 1 : return i18n.canceledKeyVerification(
58 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
59 1 : case EventTypes.KeyVerificationDone:
60 1 : return i18n.completedKeyVerification(
61 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
62 1 : case EventTypes.KeyVerificationReady:
63 1 : return i18n.isReadyForKeyVerification(
64 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
65 1 : case EventTypes.KeyVerificationAccept:
66 1 : return i18n.acceptedKeyVerification(
67 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
68 1 : case EventTypes.KeyVerificationStart:
69 1 : return i18n.startedKeyVerification(
70 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
71 1 : case MessageTypes.BadEncrypted:
72 : String errorText;
73 0 : switch (event.body) {
74 0 : case DecryptException.channelCorrupted:
75 0 : errorText = '${i18n.channelCorruptedDecryptError}.';
76 : break;
77 0 : case DecryptException.notEnabled:
78 0 : errorText = '${i18n.encryptionNotEnabled}.';
79 : break;
80 0 : case DecryptException.unknownAlgorithm:
81 0 : errorText = '${i18n.unknownEncryptionAlgorithm}.';
82 : break;
83 0 : case DecryptException.unknownSession:
84 0 : errorText = '${i18n.noPermission}.';
85 : break;
86 : default:
87 : errorText = body;
88 : break;
89 : }
90 0 : return i18n.couldNotDecryptMessage(errorText);
91 : case MessageTypes.Text:
92 : case MessageTypes.Notice:
93 : case MessageTypes.None:
94 : default:
95 : return body;
96 : }
97 : }
98 :
99 : // This map holds how to localize event types, and thus which event types exist.
100 : // If an event exists but it does not have a localized body, set its callback to null
101 : static final Map<String,
102 : String Function(Event event, MatrixLocalizations i18n, String body)?>
103 3 : localizationsMap = {
104 2 : EventTypes.Sticker: (event, i18n, body) => i18n.sentASticker(
105 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
106 2 : EventTypes.Redaction: (event, i18n, body) => i18n.redactedAnEvent(event),
107 2 : EventTypes.RoomAliases: (event, i18n, body) => i18n.changedTheRoomAliases(
108 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
109 1 : EventTypes.RoomCanonicalAlias: (event, i18n, body) =>
110 1 : i18n.changedTheRoomInvitationLink(
111 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
112 2 : EventTypes.RoomCreate: (event, i18n, body) => i18n.createdTheChat(
113 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
114 2 : EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded,
115 1 : EventTypes.RoomJoinRules: (event, i18n, body) {
116 2 : final joinRules = JoinRules.values.firstWhereOrNull((r) =>
117 3 : r.toString().replaceAll('JoinRules.', '') ==
118 2 : event.content['join_rule']);
119 : if (joinRules == null) {
120 0 : return i18n.changedTheJoinRules(
121 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
122 : } else {
123 1 : return i18n.changedTheJoinRulesTo(
124 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
125 1 : joinRules.getLocalizedString(i18n));
126 : }
127 : },
128 1 : EventTypes.RoomMember: (event, i18n, body) {
129 2 : final targetName = event.stateKeyUser?.calcDisplayname(i18n: i18n) ?? '';
130 : final senderName =
131 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
132 5 : final userIsTarget = event.stateKey == event.room.client.userID;
133 5 : final userIsSender = event.senderId == event.room.client.userID;
134 :
135 1 : switch (event.roomMemberChangeType) {
136 1 : case RoomMemberChangeType.avatar:
137 0 : return i18n.changedTheProfileAvatar(targetName);
138 1 : case RoomMemberChangeType.displayname:
139 : final newDisplayname =
140 0 : event.content.tryGet<String>('displayname') ?? '';
141 : final oldDisplayname =
142 0 : event.prevContent?.tryGet<String>('displayname') ?? '';
143 0 : return i18n.changedTheDisplaynameTo(oldDisplayname, newDisplayname);
144 1 : case RoomMemberChangeType.join:
145 : return userIsTarget
146 0 : ? i18n.youJoinedTheChat
147 1 : : i18n.joinedTheChat(targetName);
148 1 : case RoomMemberChangeType.acceptInvite:
149 : return userIsTarget
150 0 : ? i18n.youAcceptedTheInvitation
151 1 : : i18n.acceptedTheInvitation(targetName);
152 1 : case RoomMemberChangeType.rejectInvite:
153 : return userIsTarget
154 0 : ? i18n.youRejectedTheInvitation
155 1 : : i18n.rejectedTheInvitation(targetName);
156 1 : case RoomMemberChangeType.withdrawInvitation:
157 : return userIsSender
158 0 : ? i18n.youHaveWithdrawnTheInvitationFor(targetName)
159 1 : : i18n.hasWithdrawnTheInvitationFor(senderName, targetName);
160 1 : case RoomMemberChangeType.leave:
161 0 : return i18n.userLeftTheChat(targetName);
162 1 : case RoomMemberChangeType.kick:
163 : return userIsSender
164 0 : ? i18n.youKicked(targetName)
165 1 : : i18n.kicked(senderName, targetName);
166 1 : case RoomMemberChangeType.invite:
167 : return userIsSender
168 0 : ? i18n.youInvitedUser(targetName)
169 : : userIsTarget
170 0 : ? i18n.youInvitedBy(senderName)
171 1 : : i18n.invitedUser(senderName, targetName);
172 1 : case RoomMemberChangeType.ban:
173 : return userIsSender
174 0 : ? i18n.youBannedUser(targetName)
175 1 : : i18n.bannedUser(senderName, targetName);
176 0 : case RoomMemberChangeType.unban:
177 : return userIsSender
178 0 : ? i18n.youUnbannedUser(targetName)
179 0 : : i18n.unbannedUser(senderName, targetName);
180 0 : case RoomMemberChangeType.knock:
181 0 : return i18n.hasKnocked(targetName);
182 : case RoomMemberChangeType.other:
183 : default:
184 : return userIsTarget
185 0 : ? i18n.youJoinedTheChat
186 0 : : i18n.joinedTheChat(targetName);
187 : }
188 : },
189 1 : EventTypes.RoomPowerLevels: (event, i18n, body) =>
190 1 : i18n.changedTheChatPermissions(
191 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
192 2 : EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo(
193 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
194 2 : event.content.tryGet<String>('name') ?? ''),
195 1 : EventTypes.RoomTopic: (event, i18n, body) =>
196 1 : i18n.changedTheChatDescriptionTo(
197 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
198 2 : event.content.tryGet<String>('topic') ?? ''),
199 2 : EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar(
200 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
201 0 : EventTypes.GuestAccess: (event, i18n, body) {
202 0 : final guestAccess = GuestAccess.values.firstWhereOrNull((r) =>
203 0 : r.toString().replaceAll('GuestAccess.', '') ==
204 0 : event.content['guest_access']);
205 : if (guestAccess == null) {
206 0 : return i18n.changedTheGuestAccessRules(
207 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
208 : } else {
209 0 : return i18n.changedTheGuestAccessRulesTo(
210 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
211 0 : guestAccess.getLocalizedString(i18n));
212 : }
213 : },
214 1 : EventTypes.HistoryVisibility: (event, i18n, body) {
215 2 : final historyVisibility = HistoryVisibility.values.firstWhereOrNull((r) =>
216 3 : r.toString().replaceAll('HistoryVisibility.', '') ==
217 2 : event.content['history_visibility']);
218 : if (historyVisibility == null) {
219 0 : return i18n.changedTheHistoryVisibility(
220 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
221 : } else {
222 1 : return i18n.changedTheHistoryVisibilityTo(
223 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
224 1 : historyVisibility.getLocalizedString(i18n));
225 : }
226 : },
227 1 : EventTypes.Encryption: (event, i18n, body) {
228 1 : var localizedBody = i18n.activatedEndToEndEncryption(
229 2 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
230 4 : if (event.room.client.encryptionEnabled == false) {
231 3 : localizedBody += '. ${i18n.needPantalaimonWarning}';
232 : }
233 : return localizedBody;
234 : },
235 0 : EventTypes.CallAnswer: (event, i18n, body) => i18n.answeredTheCall(
236 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
237 0 : EventTypes.CallHangup: (event, i18n, body) => i18n.endedTheCall(
238 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
239 0 : EventTypes.CallInvite: (event, i18n, body) => i18n.startedACall(
240 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
241 0 : EventTypes.CallCandidates: (event, i18n, body) => i18n.sentCallInformations(
242 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
243 0 : EventTypes.Encrypted: (event, i18n, body) =>
244 0 : _localizedBodyNormalMessage(event, i18n, body),
245 1 : EventTypes.Message: (event, i18n, body) =>
246 1 : _localizedBodyNormalMessage(event, i18n, body),
247 0 : EventTypes.Reaction: (event, i18n, body) => i18n.sentReaction(
248 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
249 0 : event.content
250 0 : .tryGetMap<String, Object?>('m.relates_to')
251 0 : ?.tryGet<String>('key') ??
252 : body,
253 : ),
254 : };
255 : }
|