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 2 : static String _localizedBodyNormalMessage(
31 : Event event,
32 : MatrixLocalizations i18n,
33 : String body,
34 : ) {
35 2 : switch (event.messageType) {
36 2 : case MessageTypes.Image:
37 2 : return i18n.sentAPicture(
38 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
39 : );
40 2 : case MessageTypes.File:
41 2 : return i18n.sentAFile(
42 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
43 : );
44 2 : case MessageTypes.Audio:
45 2 : return i18n.sentAnAudio(
46 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
47 : );
48 2 : case MessageTypes.Video:
49 2 : return i18n.sentAVideo(
50 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
51 : );
52 2 : case MessageTypes.Location:
53 2 : return i18n.sharedTheLocation(
54 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
55 : );
56 2 : case MessageTypes.Sticker:
57 0 : return i18n.sentASticker(
58 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
59 : );
60 2 : case MessageTypes.Emote:
61 2 : return '* $body';
62 2 : case EventTypes.KeyVerificationRequest:
63 2 : return i18n.requestedKeyVerification(
64 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
65 : );
66 2 : case EventTypes.KeyVerificationCancel:
67 2 : return i18n.canceledKeyVerification(
68 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
69 : );
70 2 : case EventTypes.KeyVerificationDone:
71 2 : return i18n.completedKeyVerification(
72 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
73 : );
74 2 : case EventTypes.KeyVerificationReady:
75 2 : return i18n.isReadyForKeyVerification(
76 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
77 : );
78 2 : case EventTypes.KeyVerificationAccept:
79 2 : return i18n.acceptedKeyVerification(
80 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
81 : );
82 2 : case EventTypes.KeyVerificationStart:
83 2 : return i18n.startedKeyVerification(
84 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
85 : );
86 2 : case MessageTypes.BadEncrypted:
87 : String errorText;
88 0 : switch (event.body) {
89 0 : case DecryptException.channelCorrupted:
90 0 : errorText = '${i18n.channelCorruptedDecryptError}.';
91 : break;
92 0 : case DecryptException.notEnabled:
93 0 : errorText = '${i18n.encryptionNotEnabled}.';
94 : break;
95 0 : case DecryptException.unknownAlgorithm:
96 0 : errorText = '${i18n.unknownEncryptionAlgorithm}.';
97 : break;
98 0 : case DecryptException.unknownSession:
99 0 : errorText = '${i18n.noPermission}.';
100 : break;
101 : default:
102 : errorText = body;
103 : break;
104 : }
105 0 : return i18n.couldNotDecryptMessage(errorText);
106 : case MessageTypes.Text:
107 : case MessageTypes.Notice:
108 : case MessageTypes.None:
109 : default:
110 : return body;
111 : }
112 : }
113 :
114 : // This map holds how to localize event types, and thus which event types exist.
115 : // If an event exists but it does not have a localized body, set its callback to null
116 : static final Map<String,
117 : String Function(Event event, MatrixLocalizations i18n, String body)?>
118 6 : localizationsMap = {
119 4 : EventTypes.Sticker: (event, i18n, body) => i18n.sentASticker(
120 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
121 : ),
122 0 : EventTypes.encryptionInfo: (event, i18n, body) {
123 0 : return i18n.userCanNowReadAlong(
124 0 : event.content.tryGetList<String>('users') ?? [i18n.unknownUser],
125 0 : event.content.tryGetList<String>('devices'),
126 : );
127 : },
128 4 : EventTypes.Redaction: (event, i18n, body) => i18n.redactedAnEvent(event),
129 4 : EventTypes.RoomAliases: (event, i18n, body) => i18n.changedTheRoomAliases(
130 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
131 : ),
132 2 : EventTypes.RoomCanonicalAlias: (event, i18n, body) =>
133 2 : i18n.changedTheRoomInvitationLink(
134 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
135 : ),
136 4 : EventTypes.RoomCreate: (event, i18n, body) => i18n.createdTheChat(
137 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
138 : ),
139 4 : EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded,
140 2 : EventTypes.RoomJoinRules: (event, i18n, body) {
141 2 : final joinRules = JoinRules.values.firstWhereOrNull(
142 2 : (r) =>
143 6 : r.toString().replaceAll('JoinRules.', '') ==
144 4 : event.content['join_rule'],
145 : );
146 : if (joinRules == null) {
147 0 : return i18n.changedTheJoinRules(
148 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
149 : );
150 : } else {
151 2 : return i18n.changedTheJoinRulesTo(
152 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
153 2 : joinRules.getLocalizedString(i18n),
154 : );
155 : }
156 : },
157 2 : EventTypes.RoomMember: (event, i18n, body) {
158 4 : final targetName = event.stateKeyUser?.calcDisplayname(i18n: i18n) ?? '';
159 : final senderName =
160 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
161 10 : final userIsTarget = event.stateKey == event.room.client.userID;
162 10 : final userIsSender = event.senderId == event.room.client.userID;
163 :
164 2 : switch (event.roomMemberChangeType) {
165 2 : case RoomMemberChangeType.avatar:
166 0 : return i18n.changedTheProfileAvatar(targetName);
167 2 : case RoomMemberChangeType.displayname:
168 : final newDisplayname =
169 0 : event.content.tryGet<String>('displayname') ?? '';
170 : final oldDisplayname =
171 0 : event.prevContent?.tryGet<String>('displayname') ?? '';
172 0 : return i18n.changedTheDisplaynameTo(oldDisplayname, newDisplayname);
173 2 : case RoomMemberChangeType.join:
174 : return userIsTarget
175 0 : ? i18n.youJoinedTheChat
176 2 : : i18n.joinedTheChat(targetName);
177 2 : case RoomMemberChangeType.acceptInvite:
178 : return userIsTarget
179 0 : ? i18n.youAcceptedTheInvitation
180 2 : : i18n.acceptedTheInvitation(targetName);
181 2 : case RoomMemberChangeType.rejectInvite:
182 : return userIsTarget
183 0 : ? i18n.youRejectedTheInvitation
184 2 : : i18n.rejectedTheInvitation(targetName);
185 2 : case RoomMemberChangeType.withdrawInvitation:
186 : return userIsSender
187 0 : ? i18n.youHaveWithdrawnTheInvitationFor(targetName)
188 2 : : i18n.hasWithdrawnTheInvitationFor(senderName, targetName);
189 2 : case RoomMemberChangeType.leave:
190 0 : return i18n.userLeftTheChat(targetName);
191 2 : case RoomMemberChangeType.kick:
192 : return userIsSender
193 0 : ? i18n.youKicked(targetName)
194 2 : : i18n.kicked(senderName, targetName);
195 2 : case RoomMemberChangeType.invite:
196 : return userIsSender
197 0 : ? i18n.youInvitedUser(targetName)
198 : : userIsTarget
199 0 : ? i18n.youInvitedBy(senderName)
200 2 : : i18n.invitedUser(senderName, targetName);
201 2 : case RoomMemberChangeType.ban:
202 : return userIsSender
203 0 : ? i18n.youBannedUser(targetName)
204 2 : : i18n.bannedUser(senderName, targetName);
205 0 : case RoomMemberChangeType.unban:
206 : return userIsSender
207 0 : ? i18n.youUnbannedUser(targetName)
208 0 : : i18n.unbannedUser(senderName, targetName);
209 0 : case RoomMemberChangeType.knock:
210 0 : return i18n.hasKnocked(targetName);
211 0 : case RoomMemberChangeType.other:
212 : return userIsTarget
213 0 : ? i18n.youJoinedTheChat
214 0 : : i18n.joinedTheChat(targetName);
215 : }
216 : },
217 2 : EventTypes.RoomPowerLevels: (event, i18n, body) =>
218 2 : i18n.changedTheChatPermissions(
219 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
220 : ),
221 4 : EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo(
222 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
223 4 : event.content.tryGet<String>('name') ?? '',
224 : ),
225 2 : EventTypes.RoomTopic: (event, i18n, body) =>
226 2 : i18n.changedTheChatDescriptionTo(
227 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
228 4 : event.content.tryGet<String>('topic') ?? '',
229 : ),
230 4 : EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar(
231 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
232 : ),
233 0 : EventTypes.GuestAccess: (event, i18n, body) {
234 0 : final guestAccess = GuestAccess.values.firstWhereOrNull(
235 0 : (r) =>
236 0 : r.toString().replaceAll('GuestAccess.', '') ==
237 0 : event.content['guest_access'],
238 : );
239 : if (guestAccess == null) {
240 0 : return i18n.changedTheGuestAccessRules(
241 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
242 : );
243 : } else {
244 0 : return i18n.changedTheGuestAccessRulesTo(
245 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
246 0 : guestAccess.getLocalizedString(i18n),
247 : );
248 : }
249 : },
250 2 : EventTypes.HistoryVisibility: (event, i18n, body) {
251 2 : final historyVisibility = HistoryVisibility.values.firstWhereOrNull(
252 2 : (r) =>
253 6 : r.toString().replaceAll('HistoryVisibility.', '') ==
254 4 : event.content['history_visibility'],
255 : );
256 : if (historyVisibility == null) {
257 0 : return i18n.changedTheHistoryVisibility(
258 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
259 : );
260 : } else {
261 2 : return i18n.changedTheHistoryVisibilityTo(
262 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
263 2 : historyVisibility.getLocalizedString(i18n),
264 : );
265 : }
266 : },
267 2 : EventTypes.Encryption: (event, i18n, body) {
268 2 : var localizedBody = i18n.activatedEndToEndEncryption(
269 4 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
270 : );
271 8 : if (event.room.client.encryptionEnabled == false) {
272 6 : localizedBody += '. ${i18n.needPantalaimonWarning}';
273 : }
274 : return localizedBody;
275 : },
276 0 : EventTypes.CallAnswer: (event, i18n, body) => i18n.answeredTheCall(
277 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
278 : ),
279 0 : EventTypes.CallHangup: (event, i18n, body) => i18n.endedTheCall(
280 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
281 : ),
282 0 : EventTypes.CallInvite: (event, i18n, body) => i18n.startedACall(
283 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
284 : ),
285 0 : EventTypes.CallCandidates: (event, i18n, body) => i18n.sentCallInformations(
286 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
287 : ),
288 0 : EventTypes.Encrypted: (event, i18n, body) =>
289 0 : _localizedBodyNormalMessage(event, i18n, body),
290 2 : EventTypes.Message: (event, i18n, body) =>
291 2 : _localizedBodyNormalMessage(event, i18n, body),
292 0 : EventTypes.Reaction: (event, i18n, body) => i18n.sentReaction(
293 0 : event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
294 0 : event.content
295 0 : .tryGetMap<String, Object?>('m.relates_to')
296 0 : ?.tryGet<String>('key') ??
297 : body,
298 : ),
299 : };
300 : }
|