Merge branch 'krille/add-localization-stuff' into 'master'
Implement localized String represantions See merge request famedly/famedlysdk!278
This commit is contained in:
commit
d2f2690dd4
|
@ -14,10 +14,10 @@ coverage:
|
|||
script:
|
||||
- apt update
|
||||
- apt install -y curl gnupg2 git
|
||||
- curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
- curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list
|
||||
- curl https://storage.googleapis.com/dart-archive/channels/stable/release/2.7.2/linux_packages/dart_2.7.2-1_amd64.deb > dart.deb
|
||||
- apt install -y ./dart.deb
|
||||
- apt update
|
||||
- apt install -y dart chromium lcov libolm3
|
||||
- apt install -y chromium lcov libolm3
|
||||
- ln -s /usr/lib/dart/bin/pub /usr/bin/
|
||||
- useradd -m test
|
||||
- chown -R 'test:' '.'
|
||||
|
|
|
@ -31,6 +31,7 @@ export 'package:famedlysdk/src/utils/matrix_exception.dart';
|
|||
export 'package:famedlysdk/src/utils/matrix_file.dart';
|
||||
export 'package:famedlysdk/src/utils/matrix_id_string_extension.dart';
|
||||
export 'package:famedlysdk/src/utils/uri_extension.dart';
|
||||
export 'package:famedlysdk/src/utils/matrix_localizations.dart';
|
||||
export 'package:famedlysdk/src/utils/open_id_credentials.dart';
|
||||
export 'package:famedlysdk/src/utils/profile.dart';
|
||||
export 'package:famedlysdk/src/utils/public_rooms_response.dart';
|
||||
|
|
|
@ -28,6 +28,7 @@ import 'package:famedlysdk/src/utils/receipt.dart';
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:matrix_file_e2ee/matrix_file_e2ee.dart';
|
||||
import './room.dart';
|
||||
import 'utils/matrix_localizations.dart';
|
||||
|
||||
/// All data exchanged over Matrix is expressed as an "event". Typically each client action (e.g. sending a message) correlates with exactly one event.
|
||||
class Event {
|
||||
|
@ -503,6 +504,236 @@ class Event {
|
|||
}
|
||||
return MatrixFile(bytes: uint8list, path: '/$body');
|
||||
}
|
||||
|
||||
/// Returns a localized String representation of this event. For a
|
||||
/// room list you may find [withSenderNamePrefix] useful. Set [hideReply] to
|
||||
/// crop all lines starting with '>'.
|
||||
String getLocalizedBody(MatrixLocalizations i18n,
|
||||
{bool withSenderNamePrefix = false, bool hideReply = false}) {
|
||||
if (redacted) {
|
||||
return i18n.removedBy(redactedBecause.sender.calcDisplayname());
|
||||
}
|
||||
var localizedBody = body;
|
||||
final senderName = sender.calcDisplayname();
|
||||
switch (type) {
|
||||
case EventTypes.Sticker:
|
||||
localizedBody = i18n.sentASticker(senderName);
|
||||
break;
|
||||
case EventTypes.Redaction:
|
||||
localizedBody = i18n.redactedAnEvent(senderName);
|
||||
break;
|
||||
case EventTypes.RoomAliases:
|
||||
localizedBody = i18n.changedTheRoomAliases(senderName);
|
||||
break;
|
||||
case EventTypes.RoomCanonicalAlias:
|
||||
localizedBody = i18n.changedTheRoomInvitationLink(senderName);
|
||||
break;
|
||||
case EventTypes.RoomCreate:
|
||||
localizedBody = i18n.createdTheChat(senderName);
|
||||
break;
|
||||
case EventTypes.RoomJoinRules:
|
||||
var joinRules = JoinRules.values.firstWhere(
|
||||
(r) =>
|
||||
r.toString().replaceAll('JoinRules.', '') ==
|
||||
content['join_rule'],
|
||||
orElse: () => null);
|
||||
if (joinRules == null) {
|
||||
localizedBody = i18n.changedTheJoinRules(senderName);
|
||||
} else {
|
||||
localizedBody = i18n.changedTheJoinRulesTo(
|
||||
senderName, joinRules.getLocalizedString(i18n));
|
||||
}
|
||||
break;
|
||||
case EventTypes.RoomMember:
|
||||
var text = 'Failed to parse member event';
|
||||
final targetName = stateKeyUser.calcDisplayname();
|
||||
// Has the membership changed?
|
||||
final newMembership = content['membership'] ?? '';
|
||||
final oldMembership = unsigned['prev_content'] is Map<String, dynamic>
|
||||
? unsigned['prev_content']['membership'] ?? ''
|
||||
: '';
|
||||
if (newMembership != oldMembership) {
|
||||
if (oldMembership == 'invite' && newMembership == 'join') {
|
||||
text = i18n.acceptedTheInvitation(targetName);
|
||||
} else if (oldMembership == 'invite' && newMembership == 'leave') {
|
||||
if (stateKey == senderId) {
|
||||
text = i18n.rejectedTheInvitation(targetName);
|
||||
} else {
|
||||
text = i18n.hasWithdrawnTheInvitationFor(senderName, targetName);
|
||||
}
|
||||
} else if (oldMembership == 'leave' && newMembership == 'join') {
|
||||
text = i18n.joinedTheChat(targetName);
|
||||
} else if (oldMembership == 'join' && newMembership == 'ban') {
|
||||
text = i18n.kickedAndBanned(senderName, targetName);
|
||||
} else if (oldMembership == 'join' &&
|
||||
newMembership == 'leave' &&
|
||||
stateKey != senderId) {
|
||||
text = i18n.kicked(senderName, targetName);
|
||||
} else if (oldMembership == 'join' &&
|
||||
newMembership == 'leave' &&
|
||||
stateKey == senderId) {
|
||||
text = i18n.userLeftTheChat(targetName);
|
||||
} else if (oldMembership == 'invite' && newMembership == 'ban') {
|
||||
text = i18n.bannedUser(senderName, targetName);
|
||||
} else if (oldMembership == 'leave' && newMembership == 'ban') {
|
||||
text = i18n.bannedUser(senderName, targetName);
|
||||
} else if (oldMembership == 'ban' && newMembership == 'leave') {
|
||||
text = i18n.unbannedUser(senderName, targetName);
|
||||
} else if (newMembership == 'invite') {
|
||||
text = i18n.invitedUser(senderName, targetName);
|
||||
} else if (newMembership == 'join') {
|
||||
text = i18n.joinedTheChat(targetName);
|
||||
}
|
||||
} else if (newMembership == 'join') {
|
||||
final newAvatar = content['avatar_url'] ?? '';
|
||||
final oldAvatar = unsigned['prev_content'] is Map<String, dynamic>
|
||||
? unsigned['prev_content']['avatar_url'] ?? ''
|
||||
: '';
|
||||
|
||||
final newDisplayname = content['displayname'] ?? '';
|
||||
final oldDisplayname =
|
||||
unsigned['prev_content'] is Map<String, dynamic>
|
||||
? unsigned['prev_content']['displayname'] ?? ''
|
||||
: '';
|
||||
|
||||
// Has the user avatar changed?
|
||||
if (newAvatar != oldAvatar) {
|
||||
text = i18n.changedTheProfileAvatar(targetName);
|
||||
}
|
||||
// Has the user avatar changed?
|
||||
else if (newDisplayname != oldDisplayname) {
|
||||
text = i18n.changedTheDisplaynameTo(targetName, newDisplayname);
|
||||
}
|
||||
}
|
||||
localizedBody = text;
|
||||
break;
|
||||
case EventTypes.RoomPowerLevels:
|
||||
localizedBody = i18n.changedTheChatPermissions(senderName);
|
||||
break;
|
||||
case EventTypes.RoomName:
|
||||
localizedBody = i18n.changedTheChatNameTo(senderName, content['name']);
|
||||
break;
|
||||
case EventTypes.RoomTopic:
|
||||
localizedBody =
|
||||
i18n.changedTheChatDescriptionTo(senderName, content['topic']);
|
||||
break;
|
||||
case EventTypes.RoomAvatar:
|
||||
localizedBody = i18n.changedTheChatAvatar(senderName);
|
||||
break;
|
||||
case EventTypes.GuestAccess:
|
||||
var guestAccess = GuestAccess.values.firstWhere(
|
||||
(r) =>
|
||||
r.toString().replaceAll('GuestAccess.', '') ==
|
||||
content['guest_access'],
|
||||
orElse: () => null);
|
||||
if (guestAccess == null) {
|
||||
localizedBody = i18n.changedTheGuestAccessRules(senderName);
|
||||
} else {
|
||||
localizedBody = i18n.changedTheGuestAccessRulesTo(
|
||||
senderName, guestAccess.getLocalizedString(i18n));
|
||||
}
|
||||
break;
|
||||
case EventTypes.HistoryVisibility:
|
||||
var historyVisibility = HistoryVisibility.values.firstWhere(
|
||||
(r) =>
|
||||
r.toString().replaceAll('HistoryVisibility.', '') ==
|
||||
content['history_visibility'],
|
||||
orElse: () => null);
|
||||
if (historyVisibility == null) {
|
||||
localizedBody = i18n.changedTheHistoryVisibility(senderName);
|
||||
} else {
|
||||
localizedBody = i18n.changedTheHistoryVisibilityTo(
|
||||
senderName, historyVisibility.getLocalizedString(i18n));
|
||||
}
|
||||
break;
|
||||
case EventTypes.Encryption:
|
||||
localizedBody = i18n.activatedEndToEndEncryption(senderName);
|
||||
if (!room.client.encryptionEnabled) {
|
||||
localizedBody += '. ' + i18n.needPantalaimonWarning;
|
||||
}
|
||||
break;
|
||||
case EventTypes.Encrypted:
|
||||
case EventTypes.Message:
|
||||
switch (messageType) {
|
||||
case MessageTypes.Image:
|
||||
localizedBody = i18n.sentAPicture(senderName);
|
||||
break;
|
||||
case MessageTypes.File:
|
||||
localizedBody = i18n.sentAFile(senderName);
|
||||
break;
|
||||
case MessageTypes.Audio:
|
||||
localizedBody = i18n.sentAnAudio(senderName);
|
||||
break;
|
||||
case MessageTypes.Video:
|
||||
localizedBody = i18n.sentAVideo(senderName);
|
||||
break;
|
||||
case MessageTypes.Location:
|
||||
localizedBody = i18n.sharedTheLocation(senderName);
|
||||
break;
|
||||
case MessageTypes.Sticker:
|
||||
localizedBody = i18n.sentASticker(senderName);
|
||||
break;
|
||||
case MessageTypes.Emote:
|
||||
localizedBody = '* $body';
|
||||
break;
|
||||
case MessageTypes.BadEncrypted:
|
||||
String errorText;
|
||||
switch (body) {
|
||||
case DecryptError.CHANNEL_CORRUPTED:
|
||||
errorText = i18n.channelCorruptedDecryptError + '.';
|
||||
break;
|
||||
case DecryptError.NOT_ENABLED:
|
||||
errorText = i18n.encryptionNotEnabled + '.';
|
||||
break;
|
||||
case DecryptError.UNKNOWN_ALGORITHM:
|
||||
errorText = i18n.unknownEncryptionAlgorithm + '.';
|
||||
break;
|
||||
case DecryptError.UNKNOWN_SESSION:
|
||||
errorText = i18n.noPermission + '.';
|
||||
break;
|
||||
default:
|
||||
errorText = body;
|
||||
break;
|
||||
}
|
||||
localizedBody = i18n.couldNotDecryptMessage(errorText);
|
||||
break;
|
||||
case MessageTypes.Text:
|
||||
case MessageTypes.Notice:
|
||||
case MessageTypes.None:
|
||||
case MessageTypes.Reply:
|
||||
localizedBody = body;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
localizedBody = i18n.unknownEvent(typeKey);
|
||||
}
|
||||
|
||||
// Hide reply fallback
|
||||
if (hideReply) {
|
||||
localizedBody = localizedBody.replaceFirst(
|
||||
RegExp(r'^>( \*)? <[^>]+>[^\n\r]+\r?\n(> [^\n]*\r?\n)*\r?\n'), '');
|
||||
}
|
||||
|
||||
// Add the sender name prefix
|
||||
if (withSenderNamePrefix &&
|
||||
type == EventTypes.Message &&
|
||||
textOnlyMessageTypes.contains(messageType)) {
|
||||
final senderNameOrYou =
|
||||
senderId == room.client.userID ? i18n.you : senderName;
|
||||
localizedBody = '$senderNameOrYou: $localizedBody';
|
||||
}
|
||||
|
||||
return localizedBody;
|
||||
}
|
||||
|
||||
static const Set<MessageTypes> textOnlyMessageTypes = {
|
||||
MessageTypes.Text,
|
||||
MessageTypes.Reply,
|
||||
MessageTypes.Notice,
|
||||
MessageTypes.Emote,
|
||||
MessageTypes.None,
|
||||
};
|
||||
}
|
||||
|
||||
enum MessageTypes {
|
||||
|
|
|
@ -40,6 +40,7 @@ import 'package:olm/olm.dart' as olm;
|
|||
|
||||
import './user.dart';
|
||||
import 'timeline.dart';
|
||||
import 'utils/matrix_localizations.dart';
|
||||
import 'utils/states_map.dart';
|
||||
|
||||
enum PushRuleState { notify, mentions_only, dont_notify }
|
||||
|
@ -279,6 +280,27 @@ class Room {
|
|||
? states['m.room.name'].content['name']
|
||||
: '';
|
||||
|
||||
/// Returns a localized displayname for this server. If the room is a groupchat
|
||||
/// without a name, then it will return the localized version of 'Group with Alice' instead
|
||||
/// of just 'Alice' to make it different to a direct chat.
|
||||
/// Empty chats will become the localized version of 'Empty Chat'.
|
||||
/// This method requires a localization class which implements [MatrixLocalizations]
|
||||
String getLocalizedDisplayname(MatrixLocalizations i18n) {
|
||||
if ((name?.isEmpty ?? true) &&
|
||||
(canonicalAlias?.isEmpty ?? true) &&
|
||||
!isDirectChat &&
|
||||
(mHeroes != null && mHeroes.isNotEmpty)) {
|
||||
return i18n.groupWith(displayname);
|
||||
}
|
||||
if ((name?.isEmpty ?? true) &&
|
||||
(canonicalAlias?.isEmpty ?? true) &&
|
||||
!isDirectChat &&
|
||||
(mHeroes?.isEmpty ?? true)) {
|
||||
return i18n.emptyChat;
|
||||
}
|
||||
return displayname;
|
||||
}
|
||||
|
||||
/// The topic of the room if set by a participant.
|
||||
String get topic => states['m.room.topic'] != null
|
||||
? states['m.room.topic'].content['topic']
|
||||
|
|
152
lib/src/utils/matrix_localizations.dart
Normal file
152
lib/src/utils/matrix_localizations.dart
Normal file
|
@ -0,0 +1,152 @@
|
|||
import '../room.dart';
|
||||
|
||||
abstract class MatrixLocalizations {
|
||||
String get emptyChat;
|
||||
|
||||
String get invitedUsersOnly;
|
||||
|
||||
String get fromTheInvitation;
|
||||
|
||||
String get fromJoining;
|
||||
|
||||
String get visibleForAllParticipants;
|
||||
|
||||
String get visibleForEveryone;
|
||||
|
||||
String get guestsCanJoin;
|
||||
|
||||
String get guestsAreForbidden;
|
||||
|
||||
String get anyoneCanJoin;
|
||||
|
||||
String get needPantalaimonWarning;
|
||||
|
||||
String get channelCorruptedDecryptError;
|
||||
|
||||
String get encryptionNotEnabled;
|
||||
|
||||
String get unknownEncryptionAlgorithm;
|
||||
|
||||
String get noPermission;
|
||||
|
||||
String get you;
|
||||
|
||||
String groupWith(String displayname);
|
||||
|
||||
String removedBy(String calcDisplayname);
|
||||
|
||||
String sentASticker(String senderName);
|
||||
|
||||
String redactedAnEvent(String senderName);
|
||||
|
||||
String changedTheRoomAliases(String senderName);
|
||||
|
||||
String changedTheRoomInvitationLink(String senderName);
|
||||
|
||||
String createdTheChat(String senderName);
|
||||
|
||||
String changedTheJoinRules(String senderName);
|
||||
|
||||
String changedTheJoinRulesTo(String senderName, String localizedString);
|
||||
|
||||
String acceptedTheInvitation(String targetName);
|
||||
|
||||
String rejectedTheInvitation(String targetName);
|
||||
|
||||
String hasWithdrawnTheInvitationFor(String senderName, String targetName);
|
||||
|
||||
String joinedTheChat(String targetName);
|
||||
|
||||
String kickedAndBanned(String senderName, String targetName);
|
||||
|
||||
String kicked(String senderName, String targetName);
|
||||
|
||||
String userLeftTheChat(String targetName);
|
||||
|
||||
String bannedUser(String senderName, String targetName);
|
||||
|
||||
String unbannedUser(String senderName, String targetName);
|
||||
|
||||
String invitedUser(String senderName, String targetName);
|
||||
|
||||
String changedTheProfileAvatar(String targetName);
|
||||
|
||||
String changedTheDisplaynameTo(String targetName, String newDisplayname);
|
||||
|
||||
String changedTheChatPermissions(String senderName);
|
||||
|
||||
String changedTheChatNameTo(String senderName, String content);
|
||||
|
||||
String changedTheChatDescriptionTo(String senderName, String content);
|
||||
|
||||
String changedTheChatAvatar(String senderName);
|
||||
|
||||
String changedTheGuestAccessRules(String senderName);
|
||||
|
||||
String changedTheGuestAccessRulesTo(
|
||||
String senderName, String localizedString);
|
||||
|
||||
String changedTheHistoryVisibility(String senderName);
|
||||
|
||||
String changedTheHistoryVisibilityTo(
|
||||
String senderName, String localizedString);
|
||||
|
||||
String activatedEndToEndEncryption(String senderName);
|
||||
|
||||
String sentAPicture(String senderName);
|
||||
|
||||
String sentAFile(String senderName);
|
||||
|
||||
String sentAnAudio(String senderName);
|
||||
|
||||
String sentAVideo(String senderName);
|
||||
|
||||
String sharedTheLocation(String senderName);
|
||||
|
||||
String couldNotDecryptMessage(String errorText);
|
||||
|
||||
String unknownEvent(String typeKey);
|
||||
}
|
||||
|
||||
extension HistoryVisibilityDisplayString on HistoryVisibility {
|
||||
String getLocalizedString(MatrixLocalizations i18n) {
|
||||
switch (this) {
|
||||
case HistoryVisibility.invited:
|
||||
return i18n.fromTheInvitation;
|
||||
case HistoryVisibility.joined:
|
||||
return i18n.fromJoining;
|
||||
case HistoryVisibility.shared:
|
||||
return i18n.visibleForAllParticipants;
|
||||
case HistoryVisibility.world_readable:
|
||||
return i18n.visibleForEveryone;
|
||||
default:
|
||||
return toString().replaceAll('HistoryVisibility.', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension GuestAccessDisplayString on GuestAccess {
|
||||
String getLocalizedString(MatrixLocalizations i18n) {
|
||||
switch (this) {
|
||||
case GuestAccess.can_join:
|
||||
return i18n.guestsCanJoin;
|
||||
case GuestAccess.forbidden:
|
||||
return i18n.guestsAreForbidden;
|
||||
default:
|
||||
return toString().replaceAll('GuestAccess.', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension JoinRulesDisplayString on JoinRules {
|
||||
String getLocalizedString(MatrixLocalizations i18n) {
|
||||
switch (this) {
|
||||
case JoinRules.public:
|
||||
return i18n.anyoneCanJoin;
|
||||
case JoinRules.invite:
|
||||
return i18n.invitedUsersOnly;
|
||||
default:
|
||||
return toString().replaceAll('JoinRules.', '');
|
||||
}
|
||||
}
|
||||
}
|
23
pubspec.lock
23
pubspec.lock
|
@ -133,7 +133,7 @@ packages:
|
|||
name: coverage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.13.3"
|
||||
version: "0.13.9"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -203,7 +203,7 @@ packages:
|
|||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.0+4"
|
||||
version: "0.12.1"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -296,7 +296,7 @@ packages:
|
|||
name: mime_type
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.4"
|
||||
version: "0.3.0"
|
||||
multi_server_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -326,7 +326,7 @@ packages:
|
|||
name: package_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
version: "1.9.3"
|
||||
package_resolver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -424,7 +424,7 @@ packages:
|
|||
name: source_map_stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.5"
|
||||
version: "2.0.0"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -480,21 +480,21 @@ packages:
|
|||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
version: "1.14.3"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.13"
|
||||
version: "0.2.15"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.18"
|
||||
version: "0.3.4"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -537,6 +537,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.13"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.2"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -8,10 +8,10 @@ environment:
|
|||
sdk: ">=2.7.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
http: ^0.12.0+4
|
||||
mime_type: ^0.2.4
|
||||
http: ^0.12.1
|
||||
mime_type: ^0.3.0
|
||||
canonical_json: ^1.0.0
|
||||
image: ^2.1.4
|
||||
image: ^2.1.12
|
||||
|
||||
olm:
|
||||
git:
|
||||
|
|
Loading…
Reference in a new issue