diff --git a/assets/l10n/intl_ar.arb b/assets/l10n/intl_ar.arb index c3fbde038..6ad065889 100644 --- a/assets/l10n/intl_ar.arb +++ b/assets/l10n/intl_ar.arb @@ -2772,5 +2772,22 @@ "sendRoomNotifications": "إرسال إشعارات @room", "@sendRoomNotifications": {}, "changeTheDescriptionOfTheGroup": "تغيير وصف الدردشة", - "@changeTheDescriptionOfTheGroup": {} + "@changeTheDescriptionOfTheGroup": {}, + "invitedBy": "📩 تمت دعوته من قبل {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "chatPermissionsDescription": "‪حدد مستوى الصلاحية الضروري لإجراءات معينة في هذه الدردشة. عادة ما تمثل مستويات الصلاحية 0 و 50 و 100 المستخدمين والمشرفين ولكن أي تدرج ممكن.", + "@chatPermissionsDescription": {}, + "changelog": "سجل التغييرات", + "@changelog": {}, + "updateInstalled": "تم تثبيت🎉 تحديث {version}!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + } } diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 54aa417b3..cc05a4ddc 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -558,7 +558,7 @@ "type": "text", "placeholders": {} }, - "defaultPermissionLevel": "Standardberechtigungsstufe", + "defaultPermissionLevel": "Standardberechtigungsstufe für neue Benutzer", "@defaultPermissionLevel": { "type": "text", "placeholders": {} @@ -2737,5 +2737,57 @@ "chats": {}, "participants": {} } - } + }, + "changeGeneralChatSettings": "Allgemeine Chat-Einstellungen ändern", + "@changeGeneralChatSettings": {}, + "userLevel": "{level} - Benutzer", + "@userLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "moderatorLevel": "{level} - Moderator", + "@moderatorLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "changeTheChatPermissions": "Ändere die Chat-Berechtigungen", + "@changeTheChatPermissions": {}, + "changeTheVisibilityOfChatHistory": "Wechsele die Sichtbarkeit der Chat-Historie", + "@changeTheVisibilityOfChatHistory": {}, + "chatPermissionsDescription": "Definieren Sie, welche Befugnisstufe für bestimmte Aktionen in diesem Chat erforderlich ist. Die Befugnisstufen 0, 50 und 100 stehen üblicherweise für Benutzer, Moderatoren und Admins, aber jede Abstufung ist möglich.", + "@chatPermissionsDescription": {}, + "invitedBy": "📩 Eingeladen von {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "adminLevel": "{level} - Administrator", + "@adminLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "inviteOtherUsers": "Lade andere Benutzer in diesen Chat ein", + "@inviteOtherUsers": {}, + "changeTheCanonicalRoomAlias": "Ändern der Hauptadresse für den öffentlichen Chat", + "@changeTheCanonicalRoomAlias": {}, + "sendRoomNotifications": "Senden Sie eine @room-Benachrichtigung", + "@sendRoomNotifications": {}, + "changeTheDescriptionOfTheGroup": "Ändern Sie die Beschreibung des Chats", + "@changeTheDescriptionOfTheGroup": {}, + "updateInstalled": "🎉 Update {version} installiert!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + }, + "changelog": "Änderungsprotokoll", + "@changelog": {} } diff --git a/assets/l10n/intl_et.arb b/assets/l10n/intl_et.arb index c94ad9938..5c686459e 100644 --- a/assets/l10n/intl_et.arb +++ b/assets/l10n/intl_et.arb @@ -2772,5 +2772,22 @@ "changeTheChatPermissions": "Muuda vestluse õigusi", "@changeTheChatPermissions": {}, "changeTheDescriptionOfTheGroup": "Muuda vestluse kirjeldust", - "@changeTheDescriptionOfTheGroup": {} + "@changeTheDescriptionOfTheGroup": {}, + "chatPermissionsDescription": "Määra erinevatele kasutajatele selles vestluses vajalikud õigused. Tüüpiliselt on need 0, 50 ja 100 (vastavalt kasutajad, moderaatorid ja peakasutajad), kuid igasugused vahepealsed variatsioonid on ka võimalikud.", + "@chatPermissionsDescription": {}, + "invitedBy": "📩 Kutsujaks {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "updateInstalled": "🎉 Versiooniuuendus {version} on paigaldatud!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + }, + "changelog": "Muudatuste logi", + "@changelog": {} } diff --git a/assets/l10n/intl_eu.arb b/assets/l10n/intl_eu.arb index d810bcc95..64765054d 100644 --- a/assets/l10n/intl_eu.arb +++ b/assets/l10n/intl_eu.arb @@ -2679,7 +2679,7 @@ "@noPublicLinkHasBeenCreatedYet": {}, "userRole": "Erabiltzailearen rola", "@userRole": {}, - "minimumPowerLevel": "{level} da gutxieneko botere maila.", + "minimumPowerLevel": "{level} da gutxieneko botere-maila.", "@minimumPowerLevel": { "type": "text", "placeholders": { @@ -2772,5 +2772,22 @@ "changeTheVisibilityOfChatHistory": "Aldatu txataren historiaren ikusgaitasuna", "@changeTheVisibilityOfChatHistory": {}, "changeTheCanonicalRoomAlias": "Aldatu txataren helbide publiko nagusia", - "@changeTheCanonicalRoomAlias": {} + "@changeTheCanonicalRoomAlias": {}, + "invitedBy": "📩 {user}(e)k gonbidatua", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "updateInstalled": "🎉 {version} bertsioa instalatu da!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + }, + "changelog": "Aldaketak", + "@changelog": {}, + "chatPermissionsDescription": "Definitu zer botere-maila behar den txat honetako ekintza jakinetarako. 0, 50 eta 100 botere-mailek erabiltzaileak, moderatzaileak eta administratzaileak ordezkatzen dituzte, baina edozein graduazio posible da.", + "@chatPermissionsDescription": {} } diff --git a/assets/l10n/intl_gl.arb b/assets/l10n/intl_gl.arb index 94f76ff12..4d2d6cfaa 100644 --- a/assets/l10n/intl_gl.arb +++ b/assets/l10n/intl_gl.arb @@ -2772,5 +2772,22 @@ "sendRoomNotifications": "Enviar notificacións a @room", "@sendRoomNotifications": {}, "changeTheDescriptionOfTheGroup": "Cambiar a descrición do chat", - "@changeTheDescriptionOfTheGroup": {} + "@changeTheDescriptionOfTheGroup": {}, + "invitedBy": "📩 Convidada por {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "changelog": "Novidades na versión", + "@changelog": {}, + "chatPermissionsDescription": "Define que nivel de permisos son necesarios para realizar certas accións neste chat. Os niveis de permiso 0, 50 e 100 normalmente representan, usuarias, moderadoras e administradoras, pero son posibles outras escalas.", + "@chatPermissionsDescription": {}, + "updateInstalled": "🎉 Instalouse a actualización a {version}!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + } } diff --git a/assets/l10n/intl_nl.arb b/assets/l10n/intl_nl.arb index 8453e47db..9bc789c00 100644 --- a/assets/l10n/intl_nl.arb +++ b/assets/l10n/intl_nl.arb @@ -562,7 +562,7 @@ "type": "text", "placeholders": {} }, - "defaultPermissionLevel": "Standaardmachtigingsniveau", + "defaultPermissionLevel": "Standaard machtigingsniveau voor nieuwe personen", "@defaultPermissionLevel": { "type": "text", "placeholders": {} @@ -2313,7 +2313,7 @@ "@replace": {}, "report": "rapporteer", "@report": {}, - "reportErrorDescription": "Oh nee. Er is iets misgegaan. Probeer het later nog eens. Als je wilt, kun je de bug rapporteren aan de ontwikkelaars.", + "reportErrorDescription": "😭 Oh nee. Er is iets misgegaan. Probeer het later nog eens. Als je wilt, kun je de bug rapporteren aan de ontwikkelaars.", "@reportErrorDescription": {}, "sendTypingNotifications": "Typemeldingen verzenden", "@sendTypingNotifications": {}, @@ -2349,7 +2349,7 @@ "@inviteContactToGroupQuestion": {}, "optionalRedactReason": "(Optioneel) Reden voor aanpassing van dit bericht...", "@optionalRedactReason": {}, - "addChatDescription": "Voeg een chatbeschrijving toe", + "addChatDescription": "Voeg een chatbeschrijving toe...", "@addChatDescription": {}, "invalidServerName": "Foute servernaam", "@invalidServerName": {}, @@ -2404,7 +2404,7 @@ "@makeAdminDescription": {}, "archiveRoomDescription": "De chat zal naar het archief worden verplaatst. Andere personen zullen in staat zijn te zien dat je de chat hebt verlaten.", "@archiveRoomDescription": {}, - "hasKnocked": "{user} heeft geklopt", + "hasKnocked": "🚪 {user} heeft geklopt", "@hasKnocked": { "placeholders": { "user": {} @@ -2421,5 +2421,175 @@ "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." - } + }, + "joinSpace": "Deelname aan space", + "@joinSpace": {}, + "block": "Blokkeren", + "@block": {}, + "blockedUsers": "Geblokkeerde personen", + "@blockedUsers": {}, + "presenceStyle": "Aanwezigheid:", + "@presenceStyle": { + "type": "text", + "placeholders": {} + }, + "searchChatsRooms": "Zoek naar #chats, @personen...", + "@searchChatsRooms": {}, + "swipeRightToLeftToReply": "Veeg van rechts naar links om te reageren", + "@swipeRightToLeftToReply": {}, + "calls": "Gesprekken", + "@calls": {}, + "customEmojisAndStickers": "Aangepaste emojis and stickers", + "@customEmojisAndStickers": {}, + "accessAndVisibilityDescription": "Wie mag meedoen in deze chat en hoe de chat ontdekt kan worden.", + "@accessAndVisibilityDescription": {}, + "customEmojisAndStickersBody": "Voeg toe of deel aangepaste emojis of stickers die gebruikt kunnen worden in elke chat.", + "@customEmojisAndStickersBody": {}, + "hideRedactedMessages": "Verberg verwijderde berichten", + "@hideRedactedMessages": {}, + "hideRedactedMessagesBody": "Als iemand een bericht verwijdert, is dit bericht niet meer zichtbaar in de chat.", + "@hideRedactedMessagesBody": {}, + "hideInvalidOrUnknownMessageFormats": "Verberg ongeldige of onbekende berichtformaten", + "@hideInvalidOrUnknownMessageFormats": {}, + "passwordRecoverySettings": "Wachtwoord herstel instellingen", + "@passwordRecoverySettings": {}, + "youInvitedToBy": "📩 Je bent uitgenodigd via een link voor:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": {} + } + }, + "knock": "Kloppen", + "@knock": {}, + "overview": "Overzicht", + "@overview": {}, + "hidePresences": "Verberg Status Lijst?", + "@hidePresences": {}, + "noOneCanJoin": "Niemand kan deelnemen", + "@noOneCanJoin": {}, + "yourGlobalUserIdIs": "Je globale gebruikers-ID is: ", + "@yourGlobalUserIdIs": {}, + "appLockDescription": "Vergendel de app wanneer het niet gebruikt wordt met een pincode", + "@appLockDescription": {}, + "globalChatId": "Globale chat ID", + "@globalChatId": {}, + "accessAndVisibility": "Toegang en zichtbaarheid", + "@accessAndVisibility": {}, + "invitedBy": "📩 Uitgenodigd door: {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "publicSpaces": "Publieke spaces", + "@publicSpaces": {}, + "blockUsername": "Negeer gebruikersnaam", + "@blockUsername": {}, + "publicChatAddresses": "Publieke chat adressen", + "@publicChatAddresses": {}, + "createNewAddress": "Creëer nieuw adres", + "@createNewAddress": {}, + "countChatsAndCountParticipants": "{chats} chats en {participants} deelnemers", + "@countChatsAndCountParticipants": { + "type": "text", + "placeholders": { + "chats": {}, + "participants": {} + } + }, + "noMoreChatsFound": "Geen chats gevonden...", + "@noMoreChatsFound": {}, + "joinedChats": "Deelnemende chats", + "@joinedChats": {}, + "knocking": "Kloppen", + "@knocking": {}, + "space": "Space", + "@space": {}, + "spaces": "Spaces", + "@spaces": {}, + "unread": "Zet als ongelezen", + "@unread": {}, + "databaseBuildErrorBody": "Het aanmaken van de SQlite database is mislukt. De app probeert nu een traditionele database te gebruiken. Meldt alsjeblieft deze fout aan de ontwikkelaars via deze {url}. De foutmelding is: {error}", + "@databaseBuildErrorBody": { + "type": "text", + "placeholders": { + "url": {}, + "error": {} + } + }, + "groupName": "Groepsnaam", + "@groupName": {}, + "changeGeneralChatSettings": "Wijzig algemene chat instellingen", + "@changeGeneralChatSettings": {}, + "restricted": "Beperkt", + "@restricted": {}, + "searchForUsers": "Zoek naar @personen...", + "@searchForUsers": {}, + "searchMore": "Zoek meer...", + "@searchMore": {}, + "noPublicLinkHasBeenCreatedYet": "Publieke link is nog niet gecreëerd", + "@noPublicLinkHasBeenCreatedYet": {}, + "groupCanBeFoundViaSearch": "Groep kan gevonden worden via zoeken", + "@groupCanBeFoundViaSearch": {}, + "searchIn": "Zoek in chat \"{chat}\"...", + "@searchIn": { + "type": "text", + "placeholders": { + "chat": {} + } + }, + "files": "Bestanden", + "@files": {}, + "unreadChatsInApp": "{appname}: {unread} ongelezen chats", + "@unreadChatsInApp": { + "type": "text", + "placeholders": { + "appname": {}, + "unread": {} + } + }, + "noDatabaseEncryption": "Database versleuteling is niet ondersteund op dit platform", + "@noDatabaseEncryption": {}, + "thereAreCountUsersBlocked": "Nu zijn er {count} personen geblokkeerd.", + "@thereAreCountUsersBlocked": { + "type": "text", + "count": {} + }, + "markAsUnread": "Markeer als ongelezen", + "@markAsUnread": {}, + "userLevel": "{level} - Persoon", + "@userLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "moderatorLevel": "{level} - Moderator", + "@moderatorLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "adminLevel": "{level} - Administrator", + "@adminLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "stickers": "Stickers", + "@stickers": {}, + "nothingFound": "Niets gevonden...", + "@nothingFound": {}, + "gallery": "Gallerij", + "@gallery": {}, + "transparent": "Transparant", + "@transparent": {}, + "incomingMessages": "Inkomende berichten", + "@incomingMessages": {}, + "discover": "Ontdek", + "@discover": {}, + "commandHint_ignore": "Negeer de gegeven matrix ID", + "@commandHint_ignore": {} } diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index 7cc58523a..ea5c7bbc1 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -2369,7 +2369,7 @@ "reason": {} } }, - "setChatDescription": "Изменить описание чата", + "setChatDescription": "Установить описание чата", "@setChatDescription": {}, "setColorTheme": "Цветовая тема:", "@setColorTheme": {}, @@ -2708,5 +2708,57 @@ "files": "Файлы", "@files": {}, "swipeRightToLeftToReply": "Для ответа проведите с права на лево", - "@swipeRightToLeftToReply": {} + "@swipeRightToLeftToReply": {}, + "userLevel": "{level} - Пользователь", + "@userLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "moderatorLevel": "{level} - Модератор", + "@moderatorLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "adminLevel": "{level} - Администратор", + "@adminLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "changeGeneralChatSettings": "Изменить общие настройки чата", + "@changeGeneralChatSettings": {}, + "changeTheChatPermissions": "Изменить права доступа к чату", + "@changeTheChatPermissions": {}, + "changeTheDescriptionOfTheGroup": "Изменить описание чата", + "@changeTheDescriptionOfTheGroup": {}, + "inviteOtherUsers": "Пригласить других пользователей в этот чат", + "@inviteOtherUsers": {}, + "changeTheVisibilityOfChatHistory": "Изменить видимость истории чата", + "@changeTheVisibilityOfChatHistory": {}, + "countChatsAndCountParticipants": "{chats} чатов и {participants} участников", + "@countChatsAndCountParticipants": { + "type": "text", + "placeholders": { + "chats": {}, + "participants": {} + } + }, + "unread": "Непрочитанные", + "@unread": {}, + "space": "Пространство", + "@space": {}, + "spaces": "Пространства", + "@spaces": {}, + "markAsUnread": "Отметить как непрочитанное", + "@markAsUnread": {}, + "goToSpace": "Перейти к пространству: {space}", + "@goToSpace": { + "type": "text", + "space": {} + } } diff --git a/assets/l10n/intl_tr.arb b/assets/l10n/intl_tr.arb index a0bf35457..dbb60c3d8 100644 --- a/assets/l10n/intl_tr.arb +++ b/assets/l10n/intl_tr.arb @@ -2772,5 +2772,22 @@ "changeGeneralChatSettings": "Genel sohbet ayarlarını değiştir", "@changeGeneralChatSettings": {}, "changeTheVisibilityOfChatHistory": "Sohbet geçmişinin görünürlüğünü değiştir", - "@changeTheVisibilityOfChatHistory": {} + "@changeTheVisibilityOfChatHistory": {}, + "invitedBy": "📩 {user} davet etti", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "chatPermissionsDescription": "Bu sohbette belirli eylemler için hangi güç düzeyinin gerekli olduğunu tanımlayın. 0, 50 ve 100 güç düzeyleri genellikle kullanıcıları, moderatörleri ve yöneticileri temsil eder, ancak herhangi bir derecelendirme mümkündür.", + "@chatPermissionsDescription": {}, + "changelog": "Değişiklik günlüğü", + "@changelog": {}, + "updateInstalled": "🎉 Güncelleme {version} kuruldu!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + } } diff --git a/assets/l10n/intl_uk.arb b/assets/l10n/intl_uk.arb index 9af791b25..4a70f11ae 100644 --- a/assets/l10n/intl_uk.arb +++ b/assets/l10n/intl_uk.arb @@ -1557,7 +1557,7 @@ "type": "text", "placeholders": {} }, - "defaultPermissionLevel": "Типовий рівень дозволів", + "defaultPermissionLevel": "Типовий рівень дозволів для нових користувачів", "@defaultPermissionLevel": { "type": "text", "placeholders": {} @@ -1913,12 +1913,12 @@ }, "unverified": "Неперевірений", "@unverified": {}, - "locationDisabledNotice": "Служби визначення місцеположення вимкнені. Увімкніть їх, щоб могти надавати доступ до вашого місцеположення.", + "locationDisabledNotice": "Служби визначення розташування вимкнені. Увімкніть їх, щоб мати змогу ділитися своїм розташуванням.", "@locationDisabledNotice": { "type": "text", "placeholders": {} }, - "locationPermissionDeniedNotice": "Дозвіл на розташування відхилено. Надайте можливість ділитися своїм місцеперебуванням.", + "locationPermissionDeniedNotice": "Дозвіл на розташування відхилено. Надайте можливість ділитися своїм розташуванням.", "@locationPermissionDeniedNotice": { "type": "text", "placeholders": {} @@ -2708,5 +2708,86 @@ "@thereAreCountUsersBlocked": { "type": "text", "count": {} - } + }, + "moderatorLevel": "{level} - Модератор", + "@moderatorLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "adminLevel": "{level} - Адміністратор", + "@adminLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "userLevel": "{level} - Користувач", + "@userLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "changeGeneralChatSettings": "Змінити загальні налаштування чату", + "@changeGeneralChatSettings": {}, + "inviteOtherUsers": "Запросити інших користувачів до цього чату", + "@inviteOtherUsers": {}, + "changeTheChatPermissions": "Змінити права доступу до чату", + "@changeTheChatPermissions": {}, + "changeTheVisibilityOfChatHistory": "Змінити видимість історії чату", + "@changeTheVisibilityOfChatHistory": {}, + "changeTheCanonicalRoomAlias": "Змінити основну адресу загальнодоступного чату", + "@changeTheCanonicalRoomAlias": {}, + "sendRoomNotifications": "Надсилати сповіщення @room", + "@sendRoomNotifications": {}, + "space": "Простір", + "@space": {}, + "spaces": "Простори", + "@spaces": {}, + "goToSpace": "Перейти до простору: {space}", + "@goToSpace": { + "type": "text", + "space": {} + }, + "markAsUnread": "Позначити непрочитаним", + "@markAsUnread": {}, + "alwaysUse24HourFormat": "ні", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "invitedBy": "📩 Запрошений {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "changeTheDescriptionOfTheGroup": "Змінити опис чату", + "@changeTheDescriptionOfTheGroup": {}, + "updateInstalled": "🎉 Оновлення {version} встановлено!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + }, + "changelog": "Зміни", + "@changelog": {}, + "chatPermissionsDescription": "Визначте, який рівень повноважень необхідний для певних дій у цьому чаті. Рівні повноважень 0, 50 і 100 зазвичай представляють користувачів, модераторів та адміністраторів, але можливі будь-які градації.", + "@chatPermissionsDescription": {}, + "countChatsAndCountParticipants": "{chats} чати та {participants} учасників", + "@countChatsAndCountParticipants": { + "type": "text", + "placeholders": { + "chats": {}, + "participants": {} + } + }, + "noMoreChatsFound": "Більше чатів не знайдено...", + "@noMoreChatsFound": {}, + "joinedChats": "Приєднані чати", + "@joinedChats": {}, + "unread": "Непрочитані", + "@unread": {} } diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index 67b1e96aa..a188872b4 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -2772,5 +2772,22 @@ "changeTheDescriptionOfTheGroup": "更改聊天描述", "@changeTheDescriptionOfTheGroup": {}, "changeGeneralChatSettings": "更改常规聊天设置", - "@changeGeneralChatSettings": {} + "@changeGeneralChatSettings": {}, + "invitedBy": "📩 邀请人 {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "chatPermissionsDescription": "定义此聊天中哪个权限等级对特定操作是必需的。权限等级 0、50 和 100 通常代表用户、主持人和管理员,但你可以自定义任何等级。", + "@chatPermissionsDescription": {}, + "changelog": "更新记录", + "@changelog": {}, + "updateInstalled": "🎉 已安装更新 {version} !", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + } } diff --git a/assets/l10n/intl_zh_Hant.arb b/assets/l10n/intl_zh_Hant.arb index 164003394..9570c7214 100644 --- a/assets/l10n/intl_zh_Hant.arb +++ b/assets/l10n/intl_zh_Hant.arb @@ -2736,5 +2736,32 @@ "appname": {}, "unread": {} } + }, + "adminLevel": "{level} - 管理員", + "@adminLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "userLevel": "{level} - 用戶", + "@userLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "moderatorLevel": "{level} - 管理員", + "@moderatorLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "invitedBy": "📩 由 {user} 邀請", + "@invitedBy": { + "placeholders": { + "user": {} + } } } diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 1bc6f76ce..20676baf6 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'dart:io'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:collection/collection.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:file_picker/file_picker.dart'; @@ -254,23 +255,22 @@ class ChatController extends State // void requestHistory([_]) async { Future requestHistory() async { if (timeline == null) return; - // Pangea# if (!timeline!.canRequestHistory) return; + // Pangea# Logs().v('Requesting history...'); - await timeline!.requestHistory(historyCount: _loadHistoryCount); + await timeline?.requestHistory(historyCount: _loadHistoryCount); } void requestFuture() async { final timeline = this.timeline; if (timeline == null) return; - if (!timeline.canRequestFuture) return; Logs().v('Requesting future...'); final mostRecentEventId = timeline.events.first.eventId; await timeline.requestFuture(historyCount: _loadHistoryCount); setReadMarker(eventId: mostRecentEventId); } - void _updateScrollController() { + void updateScrollController() { if (!mounted) { return; } @@ -289,7 +289,7 @@ class ChatController extends State } } - void _loadDraft() async { + void loadDraft() async { final prefs = await SharedPreferences.getInstance(); final draft = widget.shareText ?? prefs.getString('draft_$roomId'); if (draft != null && draft.isNotEmpty) { @@ -299,12 +299,12 @@ class ChatController extends State @override void initState() { - scrollController.addListener(_updateScrollController); - inputFocus.addListener(_inputFocusListener); + scrollController.addListener(updateScrollController); + inputFocus.addListener(inputFocusListener); - _loadDraft(); + loadDraft(); super.initState(); - _displayChatDetailsColumn = ValueNotifier( + displayChatDetailsColumn = ValueNotifier( Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ?? false, ); @@ -333,15 +333,15 @@ class ChatController extends State await Matrix.of(context).client.roomsLoading; }); // Pangea# - _tryLoadTimeline(); + tryLoadTimeline(); if (kIsWeb) { onFocusSub = html.window.onFocus.listen((_) => setReadMarker()); } } - void _tryLoadTimeline() async { + void tryLoadTimeline() async { final initialEventId = widget.eventId; - loadTimelineFuture = _getTimeline(); + loadTimelineFuture = getTimeline(); try { await loadTimelineFuture; if (initialEventId != null) scrollToEventId(initialEventId); @@ -358,7 +358,7 @@ class ChatController extends State return; } if (!mounted) return; - _showScrollUpMaterialBanner(fullyRead); + showScrollUpMaterialBanner(fullyRead); } catch (e, s) { ErrorReporter(context, 'Unable to load timeline').onErrorCallback(e, s); rethrow; @@ -371,7 +371,7 @@ class ChatController extends State scrollUpBannerEventId = null; }); - void _showScrollUpMaterialBanner(String eventId) => setState(() { + void showScrollUpMaterialBanner(String eventId) => setState(() { scrollUpBannerEventId = eventId; }); @@ -404,7 +404,7 @@ class ChatController extends State []; // Pangea# - Future _getTimeline({ + Future getTimeline({ String? eventContextId, }) async { await Matrix.of(context).client.roomsLoading; @@ -414,6 +414,7 @@ class ChatController extends State eventContextId = null; } try { + timeline?.cancelSubscriptions(); timeline = await room.getTimeline( onUpdate: updateView, eventContextId: eventContextId, @@ -442,7 +443,7 @@ class ChatController extends State ); if (!mounted) return; if (e is TimeoutException || e is IOException) { - _showScrollUpMaterialBanner(eventContextId!); + showScrollUpMaterialBanner(eventContextId!); } } timeline!.requestKeys(onlineKeyBackupOnly: false); @@ -459,10 +460,10 @@ class ChatController extends State setReadMarker(); } - Future? _setReadMarkerFuture; + Future? setReadMarkerFuture; void setReadMarker({String? eventId}) { - if (_setReadMarkerFuture != null) return; + if (setReadMarkerFuture != null) return; if (_scrolledUp) return; if (scrollUpBannerEventId != null) return; if (eventId == null && @@ -493,13 +494,13 @@ class ChatController extends State Logs().d('Set read marker...', eventId); // ignore: unawaited_futures - _setReadMarkerFuture = timeline + setReadMarkerFuture = timeline .setReadMarker( eventId: eventId, public: AppConfig.sendPublicReadReceipts, ) .then((_) { - _setReadMarkerFuture = null; + setReadMarkerFuture = null; }) // #Pangea .catchError((e, s) { @@ -531,7 +532,7 @@ class ChatController extends State void dispose() { timeline?.cancelSubscriptions(); timeline = null; - inputFocus.removeListener(_inputFocusListener); + inputFocus.removeListener(inputFocusListener); onFocusSub?.cancel(); //#Pangea choreographer.stateListener.close(); @@ -556,7 +557,7 @@ class ChatController extends State } // then cancel the old timeline // fixes bug with read reciepts and quick switching - loadTimelineFuture = _getTimeline(eventContextId: room.fullyRead).onError( + loadTimelineFuture = getTimeline(eventContextId: room.fullyRead).onError( ErrorReporter( context, 'Unable to load timeline after changing sending Client', @@ -590,7 +591,7 @@ class ChatController extends State }) async { // Pangea# if (sendController.text.trim().isEmpty) return; - _storeInputTimeoutTimer?.cancel(); + storeInputTimeoutTimer?.cancel(); final prefs = await SharedPreferences.getInstance(); prefs.remove('draft_$roomId'); var parseCommands = true; @@ -661,7 +662,7 @@ class ChatController extends State setState(() { sendController.text = pendingText; - _inputTextIsEmpty = pendingText.isEmpty; + inputTextIsEmpty = pendingText.isEmpty; replyEvent = null; editEvent = null; pendingText = ''; @@ -861,7 +862,7 @@ class ChatController extends State setState(() => showEmojiPicker = !showEmojiPicker); } - void _inputFocusListener() { + void inputFocusListener() { if (showEmojiPicker && inputFocus.hasFocus) { emojiPickerType = EmojiPickerType.keyboard; setState(() => showEmojiPicker = false); @@ -875,7 +876,7 @@ class ChatController extends State ); } - String _getSelectedEventString() { + String getSelectedEventString() { var copyString = ''; if (selectedEvents.length == 1) { return selectedEvents.first @@ -893,7 +894,7 @@ class ChatController extends State } void copyEventsAction() { - Clipboard.setData(ClipboardData(text: _getSelectedEventString())); + Clipboard.setData(ClipboardData(text: getSelectedEventString())); setState(() { showEmojiPicker = false; // #Pangea @@ -1101,7 +1102,7 @@ class ChatController extends State } else { Matrix.of(context).shareContent = { 'msgtype': 'm.text', - 'body': _getSelectedEventString(), + 'body': getSelectedEventString(), }; } setState(() => selectedEvents.clear()); @@ -1137,15 +1138,21 @@ class ChatController extends State String eventId, { bool highlightEvent = true, }) async { - final eventIndex = timeline!.events - .where((event) => event.isVisibleInGui) - .toList() - .indexWhere((e) => e.eventId == eventId); + final foundEvent = + timeline!.events.firstWhereOrNull((event) => event.eventId == eventId); + + final eventIndex = foundEvent == null + ? -1 + : timeline!.events + .where((event) => event.isVisibleInGui || event.eventId == eventId) + .toList() + .indexOf(foundEvent); + if (eventIndex == -1) { setState(() { timeline = null; _scrolledUp = false; - loadTimelineFuture = _getTimeline(eventContextId: eventId).onError( + loadTimelineFuture = getTimeline(eventContextId: eventId).onError( ErrorReporter(context, 'Unable to load timeline after scroll to ID') .onErrorCallback, ); @@ -1166,7 +1173,7 @@ class ChatController extends State duration: FluffyThemes.animationDuration, preferPosition: AutoScrollPosition.middle, ); - _updateScrollController(); + updateScrollController(); } void scrollDown() async { @@ -1174,7 +1181,7 @@ class ChatController extends State setState(() { timeline = null; _scrolledUp = false; - loadTimelineFuture = _getTimeline().onError( + loadTimelineFuture = getTimeline().onError( ErrorReporter(context, 'Unable to load timeline after scroll down') .onErrorCallback, ); @@ -1200,7 +1207,7 @@ class ChatController extends State setState(() => showEmojiPicker = false); if (emoji == null) return; // make sure we don't send the same emoji twice - if (_allReactionEvents.any( + if (allReactionEvents.any( (e) => e.content.tryGetMap('m.relates_to')?['key'] == emoji.emoji, )) { return; @@ -1224,7 +1231,7 @@ class ChatController extends State ); } - late Iterable _allReactionEvents; + late Iterable allReactionEvents; void emojiPickerBackspace() { switch (emojiPickerType) { @@ -1245,7 +1252,7 @@ class ChatController extends State // #Pangea closeSelectionOverlay(); // Pangea# - _allReactionEvents = allReactionEvents; + allReactionEvents = allReactionEvents; emojiPickerType = EmojiPickerType.reaction; setState(() => showEmojiPicker = true); } @@ -1459,18 +1466,18 @@ class ChatController extends State ); } - Timer? _storeInputTimeoutTimer; - static const Duration _storeInputTimeout = Duration(milliseconds: 500); + Timer? storeInputTimeoutTimer; + static const storeInputTimeout = Duration(milliseconds: 500); void onInputBarChanged(String text) { - if (_inputTextIsEmpty != text.isEmpty) { + if (inputTextIsEmpty != text.isEmpty) { setState(() { - _inputTextIsEmpty = text.isEmpty; + inputTextIsEmpty = text.isEmpty; }); } - _storeInputTimeoutTimer?.cancel(); - _storeInputTimeoutTimer = Timer(_storeInputTimeout, () async { + storeInputTimeoutTimer?.cancel(); + storeInputTimeoutTimer = Timer(storeInputTimeout, () async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('draft_$roomId', text); }); @@ -1509,7 +1516,7 @@ class ChatController extends State } } - bool _inputTextIsEmpty = true; + var inputTextIsEmpty = true; bool get isArchived => {Membership.leave, Membership.ban}.contains(room.membership); @@ -1576,7 +1583,7 @@ class ChatController extends State }); // #Pangea - MessageTextSelection textSelection = MessageTextSelection(); + final textSelection = MessageTextSelection(); void showToolbar( PangeaMessageEvent pangeaMessageEvent, { @@ -1627,14 +1634,14 @@ class ChatController extends State } // Pangea# - late final ValueNotifier _displayChatDetailsColumn; + late final ValueNotifier displayChatDetailsColumn; void toggleDisplayChatDetailsColumn() async { await Matrix.of(context).store.setBool( SettingKeys.displayChatDetailsColumn, - !_displayChatDetailsColumn.value, + !displayChatDetailsColumn.value, ); - _displayChatDetailsColumn.value = !_displayChatDetailsColumn.value; + displayChatDetailsColumn.value = !displayChatDetailsColumn.value; } @override @@ -1649,7 +1656,7 @@ class ChatController extends State duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, child: ValueListenableBuilder( - valueListenable: _displayChatDetailsColumn, + valueListenable: displayChatDetailsColumn, builder: (context, displayChatDetailsColumn, _) { if (!FluffyThemes.isThreeColumnMode(context) || room.membership != Membership.join || diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 06073f47f..2248abb90 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -678,6 +678,15 @@ class ChatListController extends State final displayname = room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)); + final spacesWithPowerLevels = room.client.rooms + .where( + (space) => + space.isSpace && + space.canChangeStateEvent(EventTypes.SpaceChild) && + !space.spaceChildren.any((c) => c.roomId == room.id), + ) + .toList(); + final action = await showMenu( context: posContext, position: position, @@ -776,6 +785,18 @@ class ChatListController extends State ], ), ), + if (spacesWithPowerLevels.isNotEmpty) + PopupMenuItem( + value: ChatContextAction.addToSpace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group_work_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.addToSpace), + ], + ), + ), PopupMenuItem( value: ChatContextAction.leave, child: Row( @@ -838,6 +859,25 @@ class ChatListController extends State await showFutureLoadingDialog(context: context, future: room.leave); return; + case ChatContextAction.addToSpace: + final space = await showConfirmationDialog( + context: context, + title: L10n.of(context)!.space, + actions: spacesWithPowerLevels + .map( + (space) => AlertDialogAction( + key: space, + label: space + .getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)), + ), + ) + .toList(), + ); + if (space == null) return; + await showFutureLoadingDialog( + context: context, + future: () => space.setSpaceChild(room.id), + ); } } @@ -1072,6 +1112,7 @@ enum ChatContextAction { markUnread, mute, leave, + addToSpace, }