{ _t('The information being sent to us to help make Riot.im better includes:') }
@@ -212,19 +245,14 @@ class Analytics {
{ _t(customVariables[row[0]].expl) }
{ row[1] }
) }
-
-
-
- { _t('We also record each page you use in the app (currently ), your User Agent'
- + ' () and your device resolution ().',
- {},
- {
- CurrentPageHash: { getRedactedHash() },
- CurrentUserAgent: { navigator.userAgent },
- CurrentDeviceResolution: { resolution },
- },
+ { otherVariables.map((item, index) =>
+
+
{ _t(item.expl) }
+
{ item.value }
+
,
) }
-
+
+
{ _t('Where this page includes identifiable information, such as a room, '
+ 'user or group ID, that data is removed before being sent to the server.') }
diff --git a/src/GroupAddressPicker.js b/src/GroupAddressPicker.js
index c45a335ab6..91380b6eed 100644
--- a/src/GroupAddressPicker.js
+++ b/src/GroupAddressPicker.js
@@ -19,7 +19,7 @@ import sdk from './';
import MultiInviter from './utils/MultiInviter';
import { _t } from './languageHandler';
import MatrixClientPeg from './MatrixClientPeg';
-import GroupStoreCache from './stores/GroupStoreCache';
+import GroupStore from './stores/GroupStore';
export function showGroupInviteDialog(groupId) {
return new Promise((resolve, reject) => {
@@ -116,11 +116,10 @@ function _onGroupInviteFinished(groupId, addrs) {
function _onGroupAddRoomFinished(groupId, addrs, addRoomsPublicly) {
const matrixClient = MatrixClientPeg.get();
- const groupStore = GroupStoreCache.getGroupStore(groupId);
const errorList = [];
return Promise.all(addrs.map((addr) => {
- return groupStore
- .addRoomToGroup(addr.address, addRoomsPublicly)
+ return GroupStore
+ .addRoomToGroup(groupId, addr.address, addRoomsPublicly)
.catch(() => { errorList.push(addr.address); })
.then(() => {
const roomId = addr.address;
diff --git a/src/Lifecycle.js b/src/Lifecycle.js
index ec1fca2bc6..7378e982ef 100644
--- a/src/Lifecycle.js
+++ b/src/Lifecycle.js
@@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
+Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -64,33 +65,33 @@ import sdk from './index';
* Resolves to `true` if we ended up starting a session, or `false` if we
* failed.
*/
-export function loadSession(opts) {
- const fragmentQueryParams = opts.fragmentQueryParams || {};
- let enableGuest = opts.enableGuest || false;
- const guestHsUrl = opts.guestHsUrl;
- const guestIsUrl = opts.guestIsUrl;
- const defaultDeviceDisplayName = opts.defaultDeviceDisplayName;
+export async function loadSession(opts) {
+ try {
+ let enableGuest = opts.enableGuest || false;
+ const guestHsUrl = opts.guestHsUrl;
+ const guestIsUrl = opts.guestIsUrl;
+ const fragmentQueryParams = opts.fragmentQueryParams || {};
+ const defaultDeviceDisplayName = opts.defaultDeviceDisplayName;
- if (!guestHsUrl) {
- console.warn("Cannot enable guest access: can't determine HS URL to use");
- enableGuest = false;
- }
+ if (!guestHsUrl) {
+ console.warn("Cannot enable guest access: can't determine HS URL to use");
+ enableGuest = false;
+ }
- if (enableGuest &&
- fragmentQueryParams.guest_user_id &&
- fragmentQueryParams.guest_access_token
- ) {
- console.log("Using guest access credentials");
- return _doSetLoggedIn({
- userId: fragmentQueryParams.guest_user_id,
- accessToken: fragmentQueryParams.guest_access_token,
- homeserverUrl: guestHsUrl,
- identityServerUrl: guestIsUrl,
- guest: true,
- }, true).then(() => true);
- }
-
- return _restoreFromLocalStorage().then((success) => {
+ if (enableGuest &&
+ fragmentQueryParams.guest_user_id &&
+ fragmentQueryParams.guest_access_token
+ ) {
+ console.log("Using guest access credentials");
+ return _doSetLoggedIn({
+ userId: fragmentQueryParams.guest_user_id,
+ accessToken: fragmentQueryParams.guest_access_token,
+ homeserverUrl: guestHsUrl,
+ identityServerUrl: guestIsUrl,
+ guest: true,
+ }, true).then(() => true);
+ }
+ const success = await _restoreFromLocalStorage();
if (success) {
return true;
}
@@ -101,7 +102,9 @@ export function loadSession(opts) {
// fall back to login screen
return false;
- });
+ } catch (e) {
+ return _handleLoadSessionFailure(e);
+ }
}
/**
@@ -195,9 +198,9 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
// The plan is to gradually move the localStorage access done here into
// SessionStore to avoid bugs where the view becomes out-of-sync with
// localStorage (e.g. teamToken, isGuest etc.)
-function _restoreFromLocalStorage() {
+async function _restoreFromLocalStorage() {
if (!localStorage) {
- return Promise.resolve(false);
+ return false;
}
const hsUrl = localStorage.getItem("mx_hs_url");
const isUrl = localStorage.getItem("mx_is_url") || 'https://matrix.org';
@@ -215,26 +218,23 @@ function _restoreFromLocalStorage() {
if (accessToken && userId && hsUrl) {
console.log(`Restoring session for ${userId}`);
- try {
- return _doSetLoggedIn({
- userId: userId,
- deviceId: deviceId,
- accessToken: accessToken,
- homeserverUrl: hsUrl,
- identityServerUrl: isUrl,
- guest: isGuest,
- }, false).then(() => true);
- } catch (e) {
- return _handleRestoreFailure(e);
- }
+ await _doSetLoggedIn({
+ userId: userId,
+ deviceId: deviceId,
+ accessToken: accessToken,
+ homeserverUrl: hsUrl,
+ identityServerUrl: isUrl,
+ guest: isGuest,
+ }, false);
+ return true;
} else {
console.log("No previous session found.");
- return Promise.resolve(false);
+ return false;
}
}
-function _handleRestoreFailure(e) {
- console.log("Unable to restore session", e);
+function _handleLoadSessionFailure(e) {
+ console.log("Unable to load session", e);
const def = Promise.defer();
const SessionRestoreErrorDialog =
@@ -255,7 +255,7 @@ function _handleRestoreFailure(e) {
}
// try, try again
- return _restoreFromLocalStorage();
+ return loadSession();
});
}
diff --git a/src/SdkConfig.js b/src/SdkConfig.js
index 64bf21ecf8..8df725a913 100644
--- a/src/SdkConfig.js
+++ b/src/SdkConfig.js
@@ -21,13 +21,6 @@ const DEFAULTS = {
integrations_rest_url: "https://scalar.vector.im/api",
// Where to send bug reports. If not specified, bugs cannot be sent.
bug_report_endpoint_url: null,
-
- piwik: {
- url: "https://piwik.riot.im/",
- whitelistedHSUrls: ["https://matrix.org"],
- whitelistedISUrls: ["https://vector.im", "https://matrix.org"],
- siteId: 1,
- },
};
class SdkConfig {
@@ -52,4 +45,3 @@ class SdkConfig {
}
module.exports = SdkConfig;
-module.exports.DEFAULTS = DEFAULTS;
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index e60bde4094..712150af4d 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -58,7 +58,7 @@ function textForMemberEvent(ev) {
});
} else if (!prevContent.displayname && content.displayname) {
return _t('%(senderName)s set their display name to %(displayName)s.', {
- senderName,
+ senderName: ev.getSender(),
displayName: content.displayname,
});
} else if (prevContent.displayname && !content.displayname) {
diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js
index e636f95751..ce8f1020a1 100644
--- a/src/autocomplete/UserProvider.js
+++ b/src/autocomplete/UserProvider.js
@@ -101,8 +101,13 @@ export default class UserProvider extends AutocompleteProvider {
let completions = [];
const {command, range} = this.getCurrentCommand(query, selection, force);
- if (command) {
- completions = this.matcher.match(command[0]).map((user) => {
+
+ if (!command) return completions;
+
+ const fullMatch = command[0];
+ // Don't search if the query is a single "@"
+ if (fullMatch && fullMatch !== '@') {
+ completions = this.matcher.match(fullMatch).map((user) => {
const displayName = (user.name || user.userId || '').replace(' (IRC)', ''); // FIXME when groups are done
return {
// Length of completion should equal length of text in decorator. draft-js
diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js
index 62fdb1070a..ce79ccadfa 100644
--- a/src/components/structures/GroupView.js
+++ b/src/components/structures/GroupView.js
@@ -27,7 +27,6 @@ import AccessibleButton from '../views/elements/AccessibleButton';
import Modal from '../../Modal';
import classnames from 'classnames';
-import GroupStoreCache from '../../stores/GroupStoreCache';
import GroupStore from '../../stores/GroupStore';
import FlairStore from '../../stores/FlairStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
@@ -93,8 +92,8 @@ const CategoryRoomList = React.createClass({
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
- return this.context.groupStore
- .addRoomToGroupSummary(addr.address)
+ return GroupStore
+ .addRoomToGroupSummary(this.props.groupId, addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
})).then(() => {
@@ -174,7 +173,8 @@ const FeaturedRoom = React.createClass({
onDeleteClicked: function(e) {
e.preventDefault();
e.stopPropagation();
- this.context.groupStore.removeRoomFromGroupSummary(
+ GroupStore.removeRoomFromGroupSummary(
+ this.props.groupId,
this.props.summaryInfo.room_id,
).catch((err) => {
console.error('Error whilst removing room from group summary', err);
@@ -269,7 +269,7 @@ const RoleUserList = React.createClass({
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
- return this.context.groupStore
+ return GroupStore
.addUserToGroupSummary(addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
@@ -344,7 +344,8 @@ const FeaturedUser = React.createClass({
onDeleteClicked: function(e) {
e.preventDefault();
e.stopPropagation();
- this.context.groupStore.removeUserFromGroupSummary(
+ GroupStore.removeUserFromGroupSummary(
+ this.props.groupId,
this.props.summaryInfo.user_id,
).catch((err) => {
console.error('Error whilst removing user from group summary', err);
@@ -390,15 +391,6 @@ const FeaturedUser = React.createClass({
},
});
-const GroupContext = {
- groupStore: PropTypes.instanceOf(GroupStore).isRequired,
-};
-
-CategoryRoomList.contextTypes = GroupContext;
-FeaturedRoom.contextTypes = GroupContext;
-RoleUserList.contextTypes = GroupContext;
-FeaturedUser.contextTypes = GroupContext;
-
const GROUP_JOINPOLICY_OPEN = "open";
const GROUP_JOINPOLICY_INVITE = "invite";
@@ -415,12 +407,6 @@ export default React.createClass({
groupStore: PropTypes.instanceOf(GroupStore),
},
- getChildContext: function() {
- return {
- groupStore: this._groupStore,
- };
- },
-
getInitialState: function() {
return {
summary: null,
@@ -440,6 +426,7 @@ export default React.createClass({
},
componentWillMount: function() {
+ this._unmounted = false;
this._matrixClient = MatrixClientPeg.get();
this._matrixClient.on("Group.myMembership", this._onGroupMyMembership);
@@ -448,8 +435,8 @@ export default React.createClass({
},
componentWillUnmount: function() {
+ this._unmounted = true;
this._matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership);
- this._groupStore.removeAllListeners();
},
componentWillReceiveProps: function(newProps) {
@@ -464,8 +451,7 @@ export default React.createClass({
},
_onGroupMyMembership: function(group) {
- if (group.groupId !== this.props.groupId) return;
-
+ if (this._unmounted || group.groupId !== this.props.groupId) return;
if (group.myMembership === 'leave') {
// Leave settings - the user might have clicked the "Leave" button
this._closeSettings();
@@ -478,34 +464,11 @@ export default React.createClass({
if (group && group.inviter && group.inviter.userId) {
this._fetchInviterProfile(group.inviter.userId);
}
- this._groupStore = GroupStoreCache.getGroupStore(groupId);
- this._groupStore.registerListener(() => {
- const summary = this._groupStore.getSummary();
- if (summary.profile) {
- // Default profile fields should be "" for later sending to the server (which
- // requires that the fields are strings, not null)
- ["avatar_url", "long_description", "name", "short_description"].forEach((k) => {
- summary.profile[k] = summary.profile[k] || "";
- });
- }
- this.setState({
- summary,
- summaryLoading: !this._groupStore.isStateReady(GroupStore.STATE_KEY.Summary),
- isGroupPublicised: this._groupStore.getGroupPublicity(),
- isUserPrivileged: this._groupStore.isUserPrivileged(),
- groupRooms: this._groupStore.getGroupRooms(),
- groupRoomsLoading: !this._groupStore.isStateReady(GroupStore.STATE_KEY.GroupRooms),
- isUserMember: this._groupStore.getGroupMembers().some(
- (m) => m.userId === this._matrixClient.credentials.userId,
- ),
- error: null,
- });
- if (this.props.groupIsNew && firstInit) {
- this._onEditClick();
- }
- });
+ GroupStore.registerListener(groupId, this.onGroupStoreUpdated.bind(this, firstInit));
let willDoOnboarding = false;
- this._groupStore.on('error', (err) => {
+ // XXX: This should be more fluxy - let's get the error from GroupStore .getError or something
+ GroupStore.on('error', (err, errorGroupId) => {
+ if (this._unmounted || groupId !== errorGroupId) return;
if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN' && !willDoOnboarding) {
dis.dispatch({
action: 'do_after_sync_prepared',
@@ -520,15 +483,45 @@ export default React.createClass({
this.setState({
summary: null,
error: err,
+ editing: false,
});
});
},
+ onGroupStoreUpdated(firstInit) {
+ if (this._unmounted) return;
+ const summary = GroupStore.getSummary(this.props.groupId);
+ if (summary.profile) {
+ // Default profile fields should be "" for later sending to the server (which
+ // requires that the fields are strings, not null)
+ ["avatar_url", "long_description", "name", "short_description"].forEach((k) => {
+ summary.profile[k] = summary.profile[k] || "";
+ });
+ }
+ this.setState({
+ summary,
+ summaryLoading: !GroupStore.isStateReady(this.props.groupId, GroupStore.STATE_KEY.Summary),
+ isGroupPublicised: GroupStore.getGroupPublicity(this.props.groupId),
+ isUserPrivileged: GroupStore.isUserPrivileged(this.props.groupId),
+ groupRooms: GroupStore.getGroupRooms(this.props.groupId),
+ groupRoomsLoading: !GroupStore.isStateReady(this.props.groupId, GroupStore.STATE_KEY.GroupRooms),
+ isUserMember: GroupStore.getGroupMembers(this.props.groupId).some(
+ (m) => m.userId === this._matrixClient.credentials.userId,
+ ),
+ error: null,
+ });
+ // XXX: This might not work but this.props.groupIsNew unused anyway
+ if (this.props.groupIsNew && firstInit) {
+ this._onEditClick();
+ }
+ },
+
_fetchInviterProfile(userId) {
this.setState({
inviterProfileBusy: true,
});
this._matrixClient.getProfileInfo(userId).then((resp) => {
+ if (this._unmounted) return;
this.setState({
inviterProfile: {
avatarUrl: resp.avatar_url,
@@ -538,6 +531,7 @@ export default React.createClass({
}).catch((e) => {
console.error('Error getting group inviter profile', e);
}).finally(() => {
+ if (this._unmounted) return;
this.setState({
inviterProfileBusy: false,
});
@@ -677,7 +671,7 @@ export default React.createClass({
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
- this._groupStore.acceptGroupInvite().then(() => {
+ GroupStore.acceptGroupInvite(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
@@ -696,7 +690,7 @@ export default React.createClass({
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
- this._groupStore.leaveGroup().then(() => {
+ GroupStore.leaveGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
@@ -715,7 +709,7 @@ export default React.createClass({
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
- this._groupStore.joinGroup().then(() => {
+ GroupStore.joinGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
@@ -743,7 +737,7 @@ export default React.createClass({
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
- this._groupStore.leaveGroup().then(() => {
+ GroupStore.leaveGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 92baecb787..8df46d2f7c 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -1,7 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
-Copyright 2017 New Vector Ltd
+Copyright 2017, 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -351,16 +351,16 @@ export default React.createClass({
guestIsUrl: this.getCurrentIsUrl(),
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
});
- }).catch((e) => {
- console.error(`Error attempting to load session: ${e}`);
- return false;
}).then((loadedSession) => {
if (!loadedSession) {
// fall back to showing the login screen
dis.dispatch({action: "start_login"});
}
});
- }).done();
+ // Note we don't catch errors from this: we catch everything within
+ // loadSession as there's logic there to ask the user if they want
+ // to try logging out.
+ });
},
componentWillUnmount: function() {
diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js
index ca1e331d15..18523ceb59 100644
--- a/src/components/structures/RightPanel.js
+++ b/src/components/structures/RightPanel.js
@@ -27,7 +27,7 @@ import Analytics from '../../Analytics';
import RateLimitedFunc from '../../ratelimitedfunc';
import AccessibleButton from '../../components/views/elements/AccessibleButton';
import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker';
-import GroupStoreCache from '../../stores/GroupStoreCache';
+import GroupStore from '../../stores/GroupStore';
import { formatCount } from '../../utils/FormattingUtils';
@@ -120,7 +120,7 @@ module.exports = React.createClass({
if (this.context.matrixClient) {
this.context.matrixClient.removeListener("RoomState.members", this.onRoomStateMember);
}
- this._unregisterGroupStore();
+ this._unregisterGroupStore(this.props.groupId);
},
getInitialState: function() {
@@ -132,26 +132,23 @@ module.exports = React.createClass({
componentWillReceiveProps(newProps) {
if (newProps.groupId !== this.props.groupId) {
- this._unregisterGroupStore();
+ this._unregisterGroupStore(this.props.groupId);
this._initGroupStore(newProps.groupId);
}
},
_initGroupStore(groupId) {
if (!groupId) return;
- this._groupStore = GroupStoreCache.getGroupStore(groupId);
- this._groupStore.registerListener(this.onGroupStoreUpdated);
+ GroupStore.registerListener(groupId, this.onGroupStoreUpdated);
},
_unregisterGroupStore() {
- if (this._groupStore) {
- this._groupStore.unregisterListener(this.onGroupStoreUpdated);
- }
+ GroupStore.unregisterListener(this.onGroupStoreUpdated);
},
onGroupStoreUpdated: function() {
this.setState({
- isUserPrivilegedInGroup: this._groupStore.isUserPrivileged(),
+ isUserPrivilegedInGroup: GroupStore.isUserPrivileged(this.props.groupId),
});
},
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 85223c4eef..3bef4e41bb 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -1,7 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
-Copyright 2017 New Vector Ltd
+Copyright 2017, 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -63,6 +63,7 @@ const gHVersionLabel = function(repo, token='') {
const SIMPLE_SETTINGS = [
{ id: "urlPreviewsEnabled" },
{ id: "autoplayGifsAndVideos" },
+ { id: "alwaysShowEncryptionIcons" },
{ id: "hideReadReceipts" },
{ id: "dontSendTypingNotifications" },
{ id: "alwaysShowTimestamps" },
@@ -801,10 +802,10 @@ module.exports = React.createClass({
"us track down the problem. Debug logs contain application " +
"usage data including your username, the IDs or aliases of " +
"the rooms or groups you have visited and the usernames of " +
- "other users. They do not contian messages.",
+ "other users. They do not contain messages.",
)
}
-
diff --git a/src/components/views/dialogs/AddressPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js
index 685c4fcde3..0d0b7456b5 100644
--- a/src/components/views/dialogs/AddressPickerDialog.js
+++ b/src/components/views/dialogs/AddressPickerDialog.js
@@ -22,7 +22,7 @@ import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Promise from 'bluebird';
import { addressTypes, getAddressType } from '../../../UserAddress.js';
-import GroupStoreCache from '../../../stores/GroupStoreCache';
+import GroupStore from '../../../stores/GroupStore';
const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
@@ -243,9 +243,8 @@ module.exports = React.createClass({
_doNaiveGroupRoomSearch: function(query) {
const lowerCaseQuery = query.toLowerCase();
- const groupStore = GroupStoreCache.getGroupStore(this.props.groupId);
const results = [];
- groupStore.getGroupRooms().forEach((r) => {
+ GroupStore.getGroupRooms(this.props.groupId).forEach((r) => {
const nameMatch = (r.name || '').toLowerCase().includes(lowerCaseQuery);
const topicMatch = (r.topic || '').toLowerCase().includes(lowerCaseQuery);
const aliasMatch = (r.canonical_alias || '').toLowerCase().includes(lowerCaseQuery);
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index 21a2477c37..71a5da224c 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -1,5 +1,6 @@
/*
Copyright 2017 Vector Creations Ltd
+Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -36,8 +37,18 @@ export default React.createClass({
propTypes: {
// onFinished callback to call when Escape is pressed
+ // Take a boolean which is true if the dialog was dismissed
+ // with a positive / confirm action or false if it was
+ // cancelled (BaseDialog itself only calls this with false).
onFinished: PropTypes.func.isRequired,
+ // Whether the dialog should have a 'close' button that will
+ // cause the dialog to be cancelled. This should only be set
+ // to false if there is nothing the app can sensibly do if the
+ // dialog is cancelled, eg. "We can't restore your session and
+ // the app cannot work". Default: true.
+ hasCancel: PropTypes.bool,
+
// called when a key is pressed
onKeyDown: PropTypes.func,
@@ -56,6 +67,12 @@ export default React.createClass({
contentId: React.PropTypes.string,
},
+ getDefaultProps: function() {
+ return {
+ hasCancel: true,
+ };
+ },
+
childContextTypes: {
matrixClient: PropTypes.instanceOf(MatrixClient),
},
@@ -74,15 +91,15 @@ export default React.createClass({
if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}
- if (e.keyCode === KeyCode.ESCAPE) {
+ if (this.props.hasCancel && e.keyCode === KeyCode.ESCAPE) {
e.stopPropagation();
e.preventDefault();
- this.props.onFinished();
+ this.props.onFinished(false);
}
},
_onCancelClick: function(e) {
- this.props.onFinished();
+ this.props.onFinished(false);
},
render: function() {
@@ -101,11 +118,11 @@ export default React.createClass({
// AT users can skip its presentation.
aria-describedby={this.props.contentId}
>
-
-
+ : null }
{ this.props.title }
diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js
index 2025b6fc81..83cdb1f07f 100644
--- a/src/components/views/dialogs/BugReportDialog.js
+++ b/src/components/views/dialogs/BugReportDialog.js
@@ -1,5 +1,6 @@
/*
Copyright 2017 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -105,6 +106,8 @@ export default class BugReportDialog extends React.Component {
render() {
const Loader = sdk.getComponent("elements.Spinner");
+ const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
+ const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
let error = null;
if (this.state.err) {
@@ -113,13 +116,6 @@ export default class BugReportDialog extends React.Component {
;
}
- let cancelButton = null;
- if (!this.state.busy) {
- cancelButton = ;
- }
-
let progress = null;
if (this.state.busy) {
progress = (
@@ -131,11 +127,11 @@ export default class BugReportDialog extends React.Component {
}
return (
-
-
- { _t("Submit debug logs") }
-
-
+
+
{ _t(
"Debug logs contain application usage data including your " +
@@ -146,7 +142,7 @@ export default class BugReportDialog extends React.Component {
{ _t("We encountered an error trying to restore your previous session. If " +
- "you continue, you will need to log in again, and encrypted chat " +
- "history will be unreadable.") }
+
{ _t("We encountered an error trying to restore your previous session.") }
-
{ _t("If you have previously used a more recent version of Riot, your session " +
- "may be incompatible with this version. Close this window and return " +
- "to the more recent version.") }
+
{ _t(
+ "If you have previously used a more recent version of Riot, your session " +
+ "may be incompatible with this version. Close this window and return " +
+ "to the more recent version.",
+ ) }
- { bugreport }
+
{ _t(
+ "Clearing your browser's storage may fix the problem, but will sign you " +
+ "out and cause any encrypted chat history to become unreadable.",
+ ) }
diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js
index 6a95b3c16e..a42e45c55d 100644
--- a/src/components/views/messages/MImageBody.js
+++ b/src/components/views/messages/MImageBody.js
@@ -25,7 +25,7 @@ import ImageUtils from '../../../ImageUtils';
import Modal from '../../../Modal';
import sdk from '../../../index';
import dis from '../../../dispatcher';
-import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
+import { decryptFile } from '../../../utils/DecryptFile';
import Promise from 'bluebird';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
@@ -49,6 +49,8 @@ export default class extends React.Component {
super(props);
this.onAction = this.onAction.bind(this);
+ this.onImageError = this.onImageError.bind(this);
+ this.onImageLoad = this.onImageLoad.bind(this);
this.onImageEnter = this.onImageEnter.bind(this);
this.onImageLeave = this.onImageLeave.bind(this);
this.onClientSync = this.onClientSync.bind(this);
@@ -70,6 +72,7 @@ export default class extends React.Component {
this.context.matrixClient.on('sync', this.onClientSync);
}
+ // FIXME: factor this out and aplpy it to MVideoBody and MAudioBody too!
onClientSync(syncState, prevState) {
if (this.unmounted) return;
// Consider the client reconnected if there is no error with syncing.
@@ -136,6 +139,11 @@ export default class extends React.Component {
});
}
+ onImageLoad() {
+ this.fixupHeight();
+ this.props.onWidgetLoad();
+ }
+
_getContentUrl() {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined) {
@@ -153,6 +161,13 @@ export default class extends React.Component {
return this.state.decryptedThumbnailUrl;
}
return this.state.decryptedUrl;
+ } else if (content.info.mimetype == "image/svg+xml" && content.info.thumbnail_url) {
+ // special case to return client-generated thumbnails for SVGs, if any,
+ // given we deliberately don't thumbnail them serverside to prevent
+ // billion lol attacks and similar
+ return this.context.matrixClient.mxcUrlToHttp(
+ content.info.thumbnail_url, 800, 600,
+ );
} else {
return this.context.matrixClient.mxcUrlToHttp(content.url, 800, 600);
}
@@ -160,7 +175,6 @@ export default class extends React.Component {
componentDidMount() {
this.dispatcherRef = dis.register(this.onAction);
- this.fixupHeight();
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
let thumbnailPromise = Promise.resolve(null);
@@ -168,21 +182,20 @@ export default class extends React.Component {
thumbnailPromise = decryptFile(
content.info.thumbnail_file,
).then(function(blob) {
- return readBlobAsDataUri(blob);
+ return URL.createObjectURL(blob);
});
}
let decryptedBlob;
thumbnailPromise.then((thumbnailUrl) => {
return decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
- return readBlobAsDataUri(blob);
+ return URL.createObjectURL(blob);
}).then((contentUrl) => {
this.setState({
decryptedUrl: contentUrl,
decryptedThumbnailUrl: thumbnailUrl,
decryptedBlob: decryptedBlob,
});
- this.props.onWidgetLoad();
});
}).catch((err) => {
console.warn("Unable to decrypt attachment: ", err);
@@ -205,6 +218,13 @@ export default class extends React.Component {
dis.unregister(this.dispatcherRef);
this.context.matrixClient.removeListener('sync', this.onClientSync);
this._afterComponentWillUnmount();
+
+ if (this.state.decryptedUrl) {
+ URL.revokeObjectURL(this.state.decryptedUrl);
+ }
+ if (this.state.decryptedThumbnailUrl) {
+ URL.revokeObjectURL(this.state.decryptedThumbnailUrl);
+ }
}
// To be overridden by subclasses (e.g. MStickerBody) for further
@@ -229,7 +249,16 @@ export default class extends React.Component {
const maxHeight = 600; // let images take up as much width as they can so long as the height doesn't exceed 600px.
// the alternative here would be 600*timelineWidth/800; to scale them down to fit inside a 4:3 bounding box
- //console.log("trying to fit image into timelineWidth of " + this.refs.body.offsetWidth + " or " + this.refs.body.clientWidth);
+ // FIXME: this will break on clientside generated thumbnails (as per e2e rooms)
+ // which may well be much smaller than the 800x600 bounding box.
+
+ // FIXME: It will also break really badly for images with broken or missing thumbnails
+
+ // FIXME: Because we don't know what size of thumbnail the server's actually going to send
+ // us, we can't even really layout the page nicely for it. Instead we have to assume
+ // it'll target 800x600 and we'll downsize if needed to make things fit.
+
+ // console.log("trying to fit image into timelineWidth of " + this.refs.body.offsetWidth + " or " + this.refs.body.clientWidth);
let thumbHeight = null;
if (content.info) {
thumbHeight = ImageUtils.thumbHeight(content.info.w, content.info.h, timelineWidth, maxHeight);
@@ -239,18 +268,22 @@ export default class extends React.Component {
}
_messageContent(contentUrl, thumbUrl, content) {
+ const thumbnail = (
+
+
+
+ );
+
return (
-
-
-
+ { thumbUrl && !this.state.imgError ? thumbnail : '' }
-
+
);
}
@@ -285,14 +318,6 @@ export default class extends React.Component {
);
}
- if (this.state.imgError) {
- return (
-
- { _t("This image cannot be displayed.") }
-
- );
- }
-
const contentUrl = this._getContentUrl();
let thumbUrl;
if (this._isGif() && SettingsStore.getValue("autoplayGifsAndVideos")) {
@@ -301,20 +326,6 @@ export default class extends React.Component {
thumbUrl = this._getThumbUrl();
}
- if (thumbUrl) {
- return this._messageContent(contentUrl, thumbUrl, content);
- } else if (content.body) {
- return (
-
- { _t("Image '%(Body)s' cannot be displayed.", {Body: content.body}) }
-
- );
- } else {
- return (
-
- { _t("This image cannot be displayed.") }
-
- );
- }
+ return this._messageContent(contentUrl, thumbUrl, content);
}
}
diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js
index 21edbae9a2..5365daee03 100644
--- a/src/components/views/messages/MVideoBody.js
+++ b/src/components/views/messages/MVideoBody.js
@@ -20,7 +20,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import MFileBody from './MFileBody';
import MatrixClientPeg from '../../../MatrixClientPeg';
-import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
+import { decryptFile } from '../../../utils/DecryptFile';
import Promise from 'bluebird';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
@@ -94,14 +94,14 @@ module.exports = React.createClass({
thumbnailPromise = decryptFile(
content.info.thumbnail_file,
).then(function(blob) {
- return readBlobAsDataUri(blob);
+ return URL.createObjectURL(blob);
});
}
let decryptedBlob;
thumbnailPromise.then((thumbnailUrl) => {
return decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
- return readBlobAsDataUri(blob);
+ return URL.createObjectURL(blob);
}).then((contentUrl) => {
this.setState({
decryptedUrl: contentUrl,
@@ -120,6 +120,15 @@ module.exports = React.createClass({
}
},
+ componentWillUnmount: function() {
+ if (this.state.decryptedUrl) {
+ URL.revokeObjectURL(this.state.decryptedUrl);
+ }
+ if (this.state.decryptedThumbnailUrl) {
+ URL.revokeObjectURL(this.state.decryptedThumbnailUrl);
+ }
+ },
+
render: function() {
const content = this.props.mxEvent.getContent();
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index c2e51d878b..c7150da67c 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -33,6 +33,7 @@ import withMatrixClient from '../../../wrappers/withMatrixClient';
const ContextualMenu = require('../../structures/ContextualMenu');
import dis from '../../../dispatcher';
import {makeEventPermalink} from "../../../matrix-to";
+import SettingsStore from "../../../settings/SettingsStore";
const ObjectUtils = require('../../../ObjectUtils');
@@ -759,7 +760,11 @@ function E2ePadlockUnencrypted(props) {
}
function E2ePadlock(props) {
- return ;
+ if (SettingsStore.getValue("alwaysShowEncryptionIcons")) {
+ return ;
+ } else {
+ return ;
+ }
}
module.exports.getHandlerTile = getHandlerTile;
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index acf04831e8..fc1872249f 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -30,7 +30,7 @@ import DMRoomMap from '../../../utils/DMRoomMap';
const Receipt = require('../../../utils/Receipt');
import TagOrderStore from '../../../stores/TagOrderStore';
import RoomListStore from '../../../stores/RoomListStore';
-import GroupStoreCache from '../../../stores/GroupStoreCache';
+import GroupStore from '../../../stores/GroupStore';
const HIDE_CONFERENCE_CHANS = true;
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
@@ -83,8 +83,6 @@ module.exports = React.createClass({
cli.on("Group.myMembership", this._onGroupMyMembership);
const dmRoomMap = DMRoomMap.shared();
- this._groupStores = {};
- this._groupStoreTokens = [];
// A map between tags which are group IDs and the room IDs of rooms that should be kept
// in the room list when filtering by that tag.
this._visibleRoomsForGroup = {
@@ -93,22 +91,22 @@ module.exports = React.createClass({
// All rooms that should be kept in the room list when filtering.
// By default, show all rooms.
this._visibleRooms = MatrixClientPeg.get().getRooms();
- // When the selected tags are changed, initialise a group store if necessary
- this._tagStoreToken = TagOrderStore.addListener(() => {
+
+ // Listen to updates to group data. RoomList cares about members and rooms in order
+ // to filter the room list when group tags are selected.
+ this._groupStoreToken = GroupStore.registerListener(null, () => {
(TagOrderStore.getOrderedTags() || []).forEach((tag) => {
- if (tag[0] !== '+' || this._groupStores[tag]) {
+ if (tag[0] !== '+') {
return;
}
- this._groupStores[tag] = GroupStoreCache.getGroupStore(tag);
- this._groupStoreTokens.push(
- this._groupStores[tag].registerListener(() => {
- // This group's rooms or members may have updated, update rooms for its tag
- this.updateVisibleRoomsForTag(dmRoomMap, tag);
- this.updateVisibleRooms();
- }),
- );
+ // This group's rooms or members may have updated, update rooms for its tag
+ this.updateVisibleRoomsForTag(dmRoomMap, tag);
+ this.updateVisibleRooms();
});
- // Filters themselves have changed, refresh the selected tags
+ });
+
+ this._tagStoreToken = TagOrderStore.addListener(() => {
+ // Filters themselves have changed
this.updateVisibleRooms();
});
@@ -183,9 +181,9 @@ module.exports = React.createClass({
this._roomListStoreToken.remove();
}
- if (this._groupStoreTokens.length > 0) {
- // NB: GroupStore is not a Flux.Store
- this._groupStoreTokens.forEach((token) => token.unregister());
+ // NB: GroupStore is not a Flux.Store
+ if (this._groupStoreToken) {
+ this._groupStoreToken.unregister();
}
// cancel any pending calls to the rate_limited_funcs
@@ -259,12 +257,11 @@ module.exports = React.createClass({
updateVisibleRoomsForTag: function(dmRoomMap, tag) {
if (!this.mounted) return;
// For now, only handle group tags
- const store = this._groupStores[tag];
- if (!store) return;
+ if (tag[0] !== '+') return;
this._visibleRoomsForGroup[tag] = [];
- store.getGroupRooms().forEach((room) => this._visibleRoomsForGroup[tag].push(room.roomId));
- store.getGroupMembers().forEach((member) => {
+ GroupStore.getGroupRooms(tag).forEach((room) => this._visibleRoomsForGroup[tag].push(room.roomId));
+ GroupStore.getGroupMembers(tag).forEach((member) => {
if (member.userId === MatrixClientPeg.get().credentials.userId) return;
dmRoomMap.getDMRoomsForUserId(member.userId).forEach(
(roomId) => this._visibleRoomsForGroup[tag].push(roomId),
diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js
index 929e0b0d83..c055c67cd3 100644
--- a/src/components/views/rooms/Stickerpicker.js
+++ b/src/components/views/rooms/Stickerpicker.js
@@ -174,6 +174,7 @@ export default class Stickerpicker extends React.Component {
showTitle={false}
showMinimise={true}
showDelete={false}
+ showPopout={false}
onMinimiseClick={this._onHideStickersClick}
handleMinimisePointerEvents={true}
whitelistCapabilities={['m.sticker']}
diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json
index 88d70777f1..dc48b0ba59 100644
--- a/src/i18n/strings/bg.json
+++ b/src/i18n/strings/bg.json
@@ -950,7 +950,6 @@
"Import room keys": "Импортиране на ключове за стая",
"File to import": "Файл за импортиране",
"Import": "Импортирай",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Също така записваме всяка страница, която използвате в приложението (в момента ), браузъра, който използвате () и резолюцията на устройството ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Когато тази страница съдържа информация идентифицираща Вас (като например стая, потребител или идентификатор на група), тези данни биват премахнати преди да бъдат изпратени до сървъра.",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Има непознати устройства в тази стая. Ако продължите без да ги потвърдите, ще бъде възможно за някого да подслушва Вашия разговор.",
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "ВНИМАНИЕ: НЕУСПЕШНО ПОТВЪРЖДАВАНЕ НА КЛЮЧА! Ключът за подписване за %(userId)s и устройството %(deviceId)s е \"%(fprint)s\", което не съвпада с предоставения ключ \"%(fingerprint)s\". Това може да означава, че Вашата комуникация е прихваната!",
@@ -987,7 +986,7 @@
"%(user)s is a %(userRole)s": "%(user)s е %(userRole)s",
"Code": "Код",
"Debug Logs Submission": "Изпращане на логове за дебъгване",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Ако сте изпратили грешка чрез GitHub, логовете за дебъгване могат да ни помогнат да проследим проблема. Логовете за дебъгване съдържат данни за използване на приложението, включващи потребителското Ви име, идентификаторите или псевдонимите на стаите или групите, които сте посетили, и потребителските имена на други потребители. Те не съдържат съобщения.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Ако сте изпратили грешка чрез GitHub, логовете за дебъгване могат да ни помогнат да проследим проблема. Логовете за дебъгване съдържат данни за използване на приложението, включващи потребителското Ви име, идентификаторите или псевдонимите на стаите или групите, които сте посетили, и потребителските имена на други потребители. Те не съдържат съобщения.",
"Submit debug logs": "Изпрати логове за дебъгване",
"Opens the Developer Tools dialog": "Отваря прозорец с инструменти на разработчика",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Видяно от %(displayName)s (%(userName)s) в %(dateTime)s",
@@ -1157,5 +1156,7 @@
"Collapse panel": "Свий панела",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "С текущия Ви браузър, изглеждането и усещането на приложението може да бъде неточно, и някои или всички от функциите може да не функционират,работят......... Ако искате може да продължите така или иначе, но сте сами по отношение на евентуалните проблеми, които може да срещнете!",
"Checking for an update...": "Проверяване за нова версия...",
- "There are advanced notifications which are not shown here": "Съществуват разширени настройки за известия, които не са показани тук"
+ "There are advanced notifications which are not shown here": "Съществуват разширени настройки за известия, които не са показани тук",
+ "Missing roomId.": "Липсва идентификатор на стая.",
+ "Picture": "Изображение"
}
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 2030b4285a..278d915229 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -952,7 +952,6 @@
"%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|one": "Nachricht jetzt erneut senden oder senden abbrechen now.",
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privatsphäre ist uns wichtig, deshalb sammeln wir keine persönlichen oder identifizierbaren Daten für unsere Analysen.",
"The information being sent to us to help make Riot.im better includes:": "Die Informationen, die an uns gesendet werden um Riot.im zu verbessern enthalten:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Wir speichern auch jede Seite, die du in der App benutzt (currently ), deinen User Agent () und die Bildschirmauflösung deines Gerätes ().",
"The platform you're on": "Benutzte Plattform",
"The version of Riot.im": "Riot.im Version",
"Your language of choice": "Deine ausgewählte Sprache",
@@ -986,7 +985,7 @@
"Re-request encryption keys from your other devices.": "Verschlüsselungs-Schlüssel von deinen anderen Geräten erneut anfragen.",
"%(user)s is a %(userRole)s": "%(user)s ist ein %(userRole)s",
"Debug Logs Submission": "Einsenden des Fehlerprotokolls",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Wenn du einen Fehler via GitHub gemeldet hast, können Fehlerberichte uns helfen um das Problem zu finden. Sie enthalten Anwendungsdaten wie deinen Nutzernamen, Raum- und Gruppen-ID's und Aliase die du besucht hast und Nutzernamen anderer Nutzer. Sie enthalten keine Nachrichten.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Wenn du einen Fehler via GitHub gemeldet hast, können Fehlerberichte uns helfen um das Problem zu finden. Sie enthalten Anwendungsdaten wie deinen Nutzernamen, Raum- und Gruppen-ID's und Aliase die du besucht hast und Nutzernamen anderer Nutzer. Sie enthalten keine Nachrichten.",
"Submit debug logs": "Fehlerberichte einreichen",
"Code": "Code",
"Opens the Developer Tools dialog": "Öffnet den Entwicklerwerkzeugkasten",
@@ -1157,5 +1156,7 @@
"Collapse panel": "Panel einklappen",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "In deinem aktuell verwendeten Browser können Aussehen und Handhabung der Anwendung unter Umständen noch komplett fehlerhaft sein, so dass einige bzw. im Extremfall alle Funktionen nicht zur Verfügung stehen. Du kannst es trotzdem versuchen und fortfahren, bist dabei aber bezüglich aller auftretenden Probleme auf dich allein gestellt!",
"Checking for an update...": "Nach Updates suchen...",
- "There are advanced notifications which are not shown here": "Es existieren erweiterte Benachrichtigungen, welche hier nicht angezeigt werden"
+ "There are advanced notifications which are not shown here": "Es existieren erweiterte Benachrichtigungen, welche hier nicht angezeigt werden",
+ "Missing roomId.": "Fehlende Raum-ID.",
+ "Picture": "Bild"
}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 1e2f51f513..4adca0cc72 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -10,9 +10,12 @@
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Whether or not you're using the Richtext mode of the Rich Text Editor",
"Your homeserver's URL": "Your homeserver's URL",
"Your identity server's URL": "Your identity server's URL",
+ "Every page you use in the app": "Every page you use in the app",
+ "e.g. ": "e.g. ",
+ "Your User Agent": "Your User Agent",
+ "Your device resolution": "Your device resolution",
"Analytics": "Analytics",
"The information being sent to us to help make Riot.im better includes:": "The information being sent to us to help make Riot.im better includes:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.",
"Call Failed": "Call Failed",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.",
@@ -198,6 +201,7 @@
"Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)",
"Always show message timestamps": "Always show message timestamps",
"Autoplay GIFs and videos": "Autoplay GIFs and videos",
+ "Always show encryption icons": "Always show encryption icons",
"Enable automatic language detection for syntax highlighting": "Enable automatic language detection for syntax highlighting",
"Hide avatars in user and room mentions": "Hide avatars in user and room mentions",
"Disable big emoji in chat": "Disable big emoji in chat",
@@ -654,6 +658,7 @@
"Delete widget": "Delete widget",
"Revoke widget access": "Revoke widget access",
"Minimize apps": "Minimize apps",
+ "Popout widget": "Popout widget",
"Picture": "Picture",
"Edit": "Edit",
"Create new room": "Create new room",
@@ -745,7 +750,7 @@
"Failed to send logs: ": "Failed to send logs: ",
"Submit debug logs": "Submit debug logs",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.",
- "Click here to create a GitHub issue.": "Click here to create a GitHub issue.",
+ "Riot bugs are tracked on GitHub: create a GitHub issue.": "Riot bugs are tracked on GitHub: create a GitHub issue.",
"GitHub issue link:": "GitHub issue link:",
"Notes:": "Notes:",
"Send logs": "Send logs",
@@ -807,11 +812,15 @@
"Ignore request": "Ignore request",
"Loading device info...": "Loading device info...",
"Encryption key request": "Encryption key request",
- "Otherwise, click here to send a bug report.": "Otherwise, click here to send a bug report.",
+ "Sign out": "Sign out",
+ "Log out and remove encryption keys?": "Log out and remove encryption keys?",
+ "Send Logs": "Send Logs",
+ "Clear Storage and Sign Out": "Clear Storage and Sign Out",
+ "Refresh": "Refresh",
"Unable to restore session": "Unable to restore session",
- "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.": "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.",
+ "We encountered an error trying to restore your previous session.": "We encountered an error trying to restore your previous session.",
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.",
- "Continue anyway": "Continue anyway",
+ "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.",
"Invalid Email Address": "Invalid Email Address",
"This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address",
"Verification Pending": "Verification Pending",
@@ -1016,7 +1025,6 @@
"Status.im theme": "Status.im theme",
"Can't load user settings": "Can't load user settings",
"Server may be unavailable or overloaded": "Server may be unavailable or overloaded",
- "Sign out": "Sign out",
"For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.",
"Success": "Success",
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them",
@@ -1034,7 +1042,7 @@
"Device key:": "Device key:",
"Ignored Users": "Ignored Users",
"Debug Logs Submission": "Debug Logs Submission",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.",
"Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.",
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.",
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json
index 500d20e0f4..cc8de81fa9 100644
--- a/src/i18n/strings/eo.json
+++ b/src/i18n/strings/eo.json
@@ -952,7 +952,6 @@
"The platform you're on": "Via sistemtipo",
"Which officially provided instance you are using, if any": "Kiun oficiale disponeblan aperon vi uzas, se iun ajn",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Ĉu vi uzas la riĉtekstan reĝimon de la riĉteksta redaktilo aŭ ne",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Ni ankaŭ registras ĉiun paĝon, kiun vi uzas en la programo (nun ), vian klientan aplikaĵon () kaj vian aparatan distingon ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Kiam ĉi tiu paĝo enhavas identigeblajn informojn, ekzemple ĉambron, uzantan aŭ grupan identigilon, ĝi sendiĝas al la servilo sen tiuj.",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s",
"Disable Community Filter Panel": "Malŝalti komunuman filtran breton",
diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json
index 853a2de6c0..5a7a1312a9 100644
--- a/src/i18n/strings/eu.json
+++ b/src/i18n/strings/eu.json
@@ -959,7 +959,6 @@
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Pribatutasuna garrantzitsua da guretzat, beraz ez dugu datu pertsonalik edo identifikagarririk jasotzen gure estatistiketan.",
"Learn more about how we use analytics.": "Ikasi gehiago estatistikei ematen diegun erabileraz.",
"The information being sent to us to help make Riot.im better includes:": "Riot.im hobetzeko bidaltzen zaigun informazioan hau dago:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Aplikazioan erabiltzen duzun orri bakoitza jasotzen dugu (orain ), erabiltzaile-agentea () eta gailuaren bereizmena ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Orri honek informazio identifikagarria badu ere, esaterako gela, erabiltzailea edo talde ID-a, datu hauek ezabatu egiten dira zerbitzarira bidali aurretik.",
"Whether or not you're logged in (we don't record your user name)": "Saioa hasita dagoen ala ez (ez dugu erabiltzaile-izena gordetzen)",
"Which officially provided instance you are using, if any": "Erabiltzen ari zaren instantzia ofiziala, balego",
@@ -987,7 +986,7 @@
"%(user)s is a %(userRole)s": "%(user)s %(userRole)s da",
"Code": "Kodea",
"Debug Logs Submission": "Arazte-egunkarien bidalketak",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Akats bat bidali baduzu GitHub bidez, arazte-egunkariek arazoa aurkitzen lagundu gaitzakete. Arazte-egunkariek aplikazioak darabilen datuak dauzkate, zure erabiltzaile izena barne, bisitatu dituzun gelen ID-ak edo ezizenak eta beste erabiltzaileen izenak. Ez dute mezurik.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Akats bat bidali baduzu GitHub bidez, arazte-egunkariek arazoa aurkitzen lagundu gaitzakete. Arazte-egunkariek aplikazioak darabilen datuak dauzkate, zure erabiltzaile izena barne, bisitatu dituzun gelen ID-ak edo ezizenak eta beste erabiltzaileen izenak. Ez dute mezurik.",
"Submit debug logs": "Bidali arazte-txostenak",
"Opens the Developer Tools dialog": "Garatzailearen tresnen elkarrizketa-koadroa irekitzen du",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s (%(userName)s)(e)k ikusita %(dateTime)s(e)tan",
@@ -1157,5 +1156,7 @@
"Collapse panel": "Tolestu panela",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Zure oraingo nabigatzailearekin aplikazioaren itxura eta portaera guztiz okerra izan daiteke, eta funtzio batzuk ez dira ibiliko. Hala ere aurrera jarraitu dezakezu saiatu nahi baduzu, baina zure erantzukizunaren menpe geratzen dira aurkitu ditzakezun arazoak!",
"Checking for an update...": "Eguneraketarik dagoen egiaztatzen...",
- "There are advanced notifications which are not shown here": "Hemen erakusten ez diren jakinarazpen aurreratuak daude"
+ "There are advanced notifications which are not shown here": "Hemen erakusten ez diren jakinarazpen aurreratuak daude",
+ "Missing roomId.": "Gelaren ID-a falta da.",
+ "Picture": "Irudia"
}
diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json
index d9fc51ce28..50e0a38f8a 100644
--- a/src/i18n/strings/fr.json
+++ b/src/i18n/strings/fr.json
@@ -956,7 +956,6 @@
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Le respect de votre vie privée est important pour nous, donc nous ne collectons aucune donnée personnelle ou permettant de vous identifier pour nos statistiques.",
"Learn more about how we use analytics.": "En savoir plus sur notre utilisation des statistiques.",
"The information being sent to us to help make Riot.im better includes:": "Les informations qui nous sont envoyées pour nous aider à améliorer Riot.im comprennent :",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Nous enregistrons aussi chaque page que vous utilisez dans l'application (en ce moment ), votre User Agent () et la résolution de votre appareil ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Si la page contient des informations permettant de vous identifier, comme un salon, un identifiant d'utilisateur ou de groupe, ces données sont enlevées avant qu'elle ne soit envoyée au serveur.",
"The platform you're on": "La plateforme que vous utilisez",
"The version of Riot.im": "La version de Riot.im",
@@ -988,7 +987,7 @@
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Vu par %(displayName)s (%(userName)s) à %(dateTime)s",
"Code": "Code",
"Debug Logs Submission": "Envoi des journaux de débogage",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Si vous avez signalé un bug via GitHub, les journaux de débogage peuvent nous aider à identifier le problème. Les journaux de débogage contiennent des données d'utilisation de l'application dont votre nom d'utilisateur, les identifiants ou alias des salons ou groupes que vous avez visité et les noms d'utilisateur des autres participants. Ils ne contiennent pas les messages.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Si vous avez signalé un bug via GitHub, les journaux de débogage peuvent nous aider à identifier le problème. Les journaux de débogage contiennent des données d'utilisation de l'application dont votre nom d'utilisateur, les identifiants ou alias des salons ou groupes que vous avez visité et les noms d'utilisateur des autres participants. Ils ne contiennent pas les messages.",
"Submit debug logs": "Envoyer les journaux de débogage",
"Opens the Developer Tools dialog": "Ouvre la fenêtre des Outils de développeur",
"Unable to join community": "Impossible de rejoindre la communauté",
@@ -1156,5 +1155,8 @@
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Les rapports de débogage contiennent des données d'usage de l'application qui incluent votre nom d'utilisateur, les identifiants ou alias des salons ou groupes auxquels vous avez rendu visite ainsi que les noms des autres utilisateurs. Ils ne contiennent aucun message.",
"Failed to send logs: ": "Échec lors de l'envoi des rapports : ",
"Notes:": "Notes :",
- "Preparing to send logs": "Préparation d'envoi des rapports"
+ "Preparing to send logs": "Préparation d'envoi des rapports",
+ "Missing roomId.": "Identifiant de salon manquant.",
+ "Picture": "Image",
+ "Click here to create a GitHub issue.": "Cliquez ici pour créer un signalement sur GitHub."
}
diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json
index 95363334fb..06bff9eb76 100644
--- a/src/i18n/strings/gl.json
+++ b/src/i18n/strings/gl.json
@@ -951,7 +951,6 @@
"File to import": "Ficheiro a importar",
"Import": "Importar",
"The information being sent to us to help make Riot.im better includes:": "A información enviada a Riot.im para axudarnos a mellorar inclúe:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Tamén rexistramos cada páxina que vostede utiliza no aplicativo (actualmente ), o User Agent () e a resolución do dispositivo ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Si esta páxina inclúe información identificable como ID de grupo, usuario ou sala, estes datos son eliminados antes de ser enviados ao servidor.",
"The platform you're on": "A plataforma na que está",
"The version of Riot.im": "A versión de Riot.im",
@@ -993,7 +992,7 @@
"Join this community": "Únase a esta comunidade",
"Leave this community": "Deixar esta comunidade",
"Debug Logs Submission": "Envío de rexistro de depuración",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Si enviou un reporte de fallo a través de GitHub, os informes poden axudarnos a examinar o problema. Os informes de fallo conteñen datos do uso do aplicativo incluíndo o seu nome de usuaria, os IDs ou alcumes das salas e grupos que visitou e os nomes de usuaria de outras personas. Non conteñen mensaxes.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Si enviou un reporte de fallo a través de GitHub, os informes poden axudarnos a examinar o problema. Os informes de fallo conteñen datos do uso do aplicativo incluíndo o seu nome de usuaria, os IDs ou alcumes das salas e grupos que visitou e os nomes de usuaria de outras personas. Non conteñen mensaxes.",
"Submit debug logs": "Enviar informes de depuración",
"Opens the Developer Tools dialog": "Abre o cadro de Ferramentas de Desenvolvedoras",
"Stickerpack": "Peganitas",
diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json
index 5fe1e90163..f00dacab29 100644
--- a/src/i18n/strings/hu.json
+++ b/src/i18n/strings/hu.json
@@ -362,7 +362,7 @@
"This email address was not found": "Az e-mail cím nem található",
"The email address linked to your account must be entered.": "A fiókodhoz kötött e-mail címet add meg.",
"Press to start a chat with someone": "Nyomd meg a gombot ha szeretnél csevegni valakivel",
- "Privacy warning": "Magánéleti figyelmeztetés",
+ "Privacy warning": "Adatvédelmi figyelmeztetés",
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "'%(fileName)s' fájl túllépte a Saját szerverben beállított feltöltési méret határt",
"The file '%(fileName)s' failed to upload": "'%(fileName)s' fájl feltöltése sikertelen",
"The remote side failed to pick up": "A hívott fél nem vette fel",
@@ -956,7 +956,6 @@
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "A személyes adatok védelme fontos számunkra, így mi nem gyűjtünk személyes és személyhez köthető adatokat az analitikánkhoz.",
"Learn more about how we use analytics.": "Tudj meg többet arról hogyan használjuk az analitikai adatokat.",
"The information being sent to us to help make Riot.im better includes:": "Az adatok amiket a Riot.im javításához felhasználunk az alábbiak:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Felvesszük az összes oldalt amit az alkalmazásban használsz (jelenleg ), a \"User Agent\"-et () és az eszköz felbontását ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Minden azonosításra alkalmas adatot mint a szoba, felhasználó vagy csoport azonosítót mielőtt az adatokat elküldenénk eltávolításra kerülnek.",
"The platform you're on": "A platform amit használsz",
"The version of Riot.im": "Riot.im verziója",
@@ -987,7 +986,7 @@
"%(user)s is a %(userRole)s": "%(user)s egy %(userRole)s",
"Code": "Kód",
"Debug Logs Submission": "Hibakeresési napló elküldése",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Ha a GitHubon keresztül küldted be a hibát, a hibakeresési napló segíthet nekünk a javításban. A napló felhasználási adatokat tartalmaz mint a felhasználói neved, az általad meglátogatott szobák vagy csoportok azonosítóját vagy alternatív nevét és mások felhasználói nevét. De nem tartalmazzák az üzeneteket.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Ha a GitHubon keresztül küldted be a hibát, a hibakeresési napló segíthet nekünk a javításban. A napló felhasználási adatokat tartalmaz mint a felhasználói neved, az általad meglátogatott szobák vagy csoportok azonosítóját vagy alternatív nevét és mások felhasználói nevét. De nem tartalmazzák az üzeneteket.",
"Submit debug logs": "Hibakeresési napló küldése",
"Opens the Developer Tools dialog": "Megnyitja a fejlesztői eszközök ablakát",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s (%(userName)s) az alábbi időpontban látta: %(dateTime)s",
@@ -1157,5 +1156,7 @@
"Collapse panel": "Panel becsukása",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Ebben a böngészőben az alkalmazás felülete tele lehet hibával, és az is lehet, hogy egyáltalán nem működik. Ha így is ki szeretnéd próbálni, megteheted, de ha valami gondod van, nem tudunk segíteni!",
"Checking for an update...": "Frissítés keresése...",
- "There are advanced notifications which are not shown here": "Vannak itt nem látható, haladó értesítések"
+ "There are advanced notifications which are not shown here": "Vannak itt nem látható, haladó értesítések",
+ "Missing roomId.": "Hiányzó szoba azonosító.",
+ "Picture": "Kép"
}
diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json
index 27d1cde38d..ba7417bcc8 100644
--- a/src/i18n/strings/it.json
+++ b/src/i18n/strings/it.json
@@ -101,7 +101,6 @@
"Your identity server's URL": "L'URL del tuo server di identità",
"Analytics": "Statistiche",
"The information being sent to us to help make Riot.im better includes:": "Le informazioni inviate per aiutarci a migliorare Riot.im includono:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Registriamo anche ogni pagina che usi nell'app (attualmente ), il tuo User Agent () e la risoluzione del dispositivo ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Se questa pagina include informazioni identificabili, come una stanza, utente o ID di gruppo, questi dati sono rimossi prima che vengano inviati al server.",
"Call Failed": "Chiamata fallita",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Ci sono dispositivi sconosciuti in questa stanza: se procedi senza verificarli, qualcuno avrà la possibilità di intercettare la tua chiamata.",
@@ -865,7 +864,7 @@
"Device key:": "Chiave dispositivo:",
"Ignored Users": "Utenti ignorati",
"Debug Logs Submission": "Invio dei log di debug",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Se hai segnalato un errore via Github, i log di debug possono aiutarci a identificare il problema. I log di debug contengono dati di utilizzo dell'applicazione inclusi il nome utente, gli ID o alias delle stanze o gruppi visitati e i nomi degli altri utenti. Non contengono messaggi.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Se hai segnalato un errore via Github, i log di debug possono aiutarci a identificare il problema. I log di debug contengono dati di utilizzo dell'applicazione inclusi il nome utente, gli ID o alias delle stanze o gruppi visitati e i nomi degli altri utenti. Non contengono messaggi.",
"Submit debug logs": "Invia log di debug",
"Riot collects anonymous analytics to allow us to improve the application.": "Riot raccoglie statistiche anonime per permetterci di migliorare l'applicazione.",
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Diamo importanza alla privacy, perciò non raccogliamo dati personali o identificabili per le nostre statistiche.",
diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json
index cb386cc409..013790aa80 100644
--- a/src/i18n/strings/ja.json
+++ b/src/i18n/strings/ja.json
@@ -87,7 +87,6 @@
"Your identity server's URL": "あなたのアイデンティティサーバのURL",
"Analytics": "分析",
"The information being sent to us to help make Riot.im better includes:": "Riot.imをよりよくするために私達に送信される情報は以下を含みます:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "私達はこのアプリであなたが利用したページ(現在は)、あなたのユーザエージェント(現在は)、並びにあなたの端末の解像度(現在の端末では)も記録します。",
"Thursday": "木曜日",
"Messages in one-to-one chats": "一対一のチャットでのメッセージ",
"A new version of Riot is available.": "新しいバージョンのRiotが利用可能です。",
@@ -242,5 +241,7 @@
"Collapse panel": "パネルを折りたたむ",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "現在ご使用のブラウザでは、アプリの外見や使い心地が正常でない可能性があります。また、一部または全部の機能がご使用いただけない可能性があります。このままご使用いただけますが、問題が発生した場合は対応しかねます!",
"Checking for an update...": "アップデートを確認しています…",
- "There are advanced notifications which are not shown here": "ここに表示されない詳細な通知があります"
+ "There are advanced notifications which are not shown here": "ここに表示されない詳細な通知があります",
+ "Call": "通話",
+ "Answer": "応答"
}
diff --git a/src/i18n/strings/lt.json b/src/i18n/strings/lt.json
index efa5cf9d5e..bd46c25ed8 100644
--- a/src/i18n/strings/lt.json
+++ b/src/i18n/strings/lt.json
@@ -12,7 +12,6 @@
"Your identity server's URL": "Jūsų identifikavimo serverio URL adresas",
"Analytics": "Statistika",
"The information being sent to us to help make Riot.im better includes:": "Informacijoje, kuri yra siunčiama Riot.im tobulinimui yra:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Mes taip pat saugome kiekvieną puslapį, kurį jūs naudojate programėlėje (dabartinis ), jūsų paskyros agentas () ir jūsų įrenginio rezoliucija ().",
"Fetching third party location failed": "Nepavyko gauti trečios šalies vietos",
"A new version of Riot is available.": "Yra nauja Riot versija.",
"I understand the risks and wish to continue": "Aš suprantu riziką ir noriu tęsti",
diff --git a/src/i18n/strings/lv.json b/src/i18n/strings/lv.json
index 5c10bec7b9..6282d40daf 100644
--- a/src/i18n/strings/lv.json
+++ b/src/i18n/strings/lv.json
@@ -695,7 +695,6 @@
"Your homeserver's URL": "Bāzes servera URL adrese",
"Your identity server's URL": "Tava Identitātes servera URL adrese",
"The information being sent to us to help make Riot.im better includes:": "Informācija, kura mums tiek nosūtīta, lai ļautu padarīt Riot.im labāku, ietver:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Mēs arī fiksējam katru lapu, kuru tu izmanto programmā (currently ), Tavu lietotāja aģentu () un Tavas ierīces ekrāna izšķirtspēju ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Ja šī lapa ietver identificējamu informāciju, tādu kā istaba, lietotājs, grupas Id, šie dati tiek noņemti pirms nosūtīšanas uz serveri.",
"Call Failed": "Zvans neizdevās",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Šajā istabā ir nepazīstamas ierīces: ja Tu turpināsi bez to pārbaudes, ir iespējams, ka kāda nepiederoša persona var noklausīties Tavas sarunas.",
@@ -987,7 +986,7 @@
"%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|other": "Tagadvisas atkārtoti sūtīt vai visas atcelt. Tu vari atzīmēt arī individuālas ziņas, kuras atkārtoti sūtīt vai atcelt.",
"Clear filter": "Attīrīt filtru",
"Debug Logs Submission": "Iesniegt atutošanas logfailus",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Ja esi paziņojis par kļūdu caur GitHub, atutošanas logfaili var mums palīdzēt identificēt problēmu. Atutošanas logfaili satur programmas lietošanas datus, tostarp Tavu lietotājvārdu, istabu/grupu Id vai aliases, kuras esi apmeklējis un citu lietotāju lietotājvārdus. Tie nesatur pašas ziņas.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Ja esi paziņojis par kļūdu caur GitHub, atutošanas logfaili var mums palīdzēt identificēt problēmu. Atutošanas logfaili satur programmas lietošanas datus, tostarp Tavu lietotājvārdu, istabu/grupu Id vai aliases, kuras esi apmeklējis un citu lietotāju lietotājvārdus. Tie nesatur pašas ziņas.",
"Submit debug logs": "Iesniegt atutošanas logfailus",
"Opens the Developer Tools dialog": "Atver Izstrādātāja instrumentus",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Skatījis %(displayName)s (%(userName)s) %(dateTime)s",
diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json
index 6c78265a4d..e96280f2e4 100644
--- a/src/i18n/strings/nl.json
+++ b/src/i18n/strings/nl.json
@@ -959,7 +959,6 @@
"Notify the whole room": "Notificeer de gehele ruimte",
"Room Notification": "Ruimte Notificatie",
"The information being sent to us to help make Riot.im better includes:": "De informatie dat naar ons wordt verstuurd om Riot.im beter te maken betrekt:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "We nemen ook elke pagina die je in de applicatie gebruikt (momenteel ), je User Agent () en de resolutie van je apparaat () op.",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Waar deze pagina identificeerbare informatie bevat, zoals een ruimte, gebruiker of groep ID, zal deze data verwijderd worden voordat het naar de server gestuurd wordt.",
"The platform you're on": "Het platform waar je je op bevindt",
"The version of Riot.im": "De versie van Riot.im",
@@ -1002,7 +1001,7 @@
"Everyone": "Iedereen",
"Leave this community": "Deze gemeenschap verlaten",
"Debug Logs Submission": "Debug Logs Indienen",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Als je een bug via Github hebt ingediend kunnen debug logs ons helpen om het probleem te vinden. Debug logs bevatten applicatie-gebruik data inclusief je gebruikersnaam, de ID's of namen van de ruimtes en groepen die je hebt bezocht en de gebruikersnamen van andere gebruikers. Ze bevatten geen berichten.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Als je een bug via Github hebt ingediend kunnen debug logs ons helpen om het probleem te vinden. Debug logs bevatten applicatie-gebruik data inclusief je gebruikersnaam, de ID's of namen van de ruimtes en groepen die je hebt bezocht en de gebruikersnamen van andere gebruikers. Ze bevatten geen berichten.",
"Submit debug logs": "Debug logs indienen",
"Opens the Developer Tools dialog": "Opent het Ontwikkelaars Gereedschappen dialoog",
"Fetching third party location failed": "Het ophalen van de locatie van de derde partij is mislukt",
diff --git a/src/i18n/strings/pl.json b/src/i18n/strings/pl.json
index 3d6d026945..445d3ad025 100644
--- a/src/i18n/strings/pl.json
+++ b/src/i18n/strings/pl.json
@@ -693,7 +693,6 @@
"Your homeserver's URL": "Adres URL twojego serwera domowego",
"Your identity server's URL": "Adres URL twojego serwera tożsamości",
"The information being sent to us to help make Riot.im better includes:": "Oto informacje przesyłane do nas, służące do poprawy Riot.im:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Zapisujemy również każdą stronę, z której korzystasz w aplikacji (obecnie ), twój User Agent () oraz rozdzielczość ekranu twojego urządzenia ().",
"The platform you're on": "Platforma na której jesteś",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "W tym pokoju są nieznane urządzenia: jeżeli będziesz kontynuować bez ich weryfikacji, możliwe będzie podsłuchiwanie Twojego połączenia.",
"Answer": "Odbierz",
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
index 631eed1c98..9a4739357e 100644
--- a/src/i18n/strings/pt_BR.json
+++ b/src/i18n/strings/pt_BR.json
@@ -664,7 +664,6 @@
"Your homeserver's URL": "A URL do seu Servidor de Base (homeserver)",
"Your identity server's URL": "A URL do seu servidor de identidade",
"The information being sent to us to help make Riot.im better includes:": "As informações que estão sendo usadas para ajudar a melhorar o Riot.im incluem:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Nós também gravamos cada página que você usa no app (atualmente ), o seu User Agent () e a resolução do seu dispositivo ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Quando esta página tem informação de identificação, como uma sala, ID de usuária/o ou de grupo, estes dados são removidos antes de serem enviados ao servidor.",
"Call Failed": "A chamada falhou",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Há dispositivos desconhecidos nesta sala: se você continuar sem verificá-los, será possível alguém espiar sua chamada.",
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
index 8de9983f22..10d6bb02dd 100644
--- a/src/i18n/strings/ru.json
+++ b/src/i18n/strings/ru.json
@@ -3,24 +3,24 @@
"Add email address": "Добавить адрес email",
"Add phone number": "Добавить номер телефона",
"Admin": "Администратор",
- "Advanced": "Дополнительно",
+ "Advanced": "Подробности",
"Algorithm": "Алгоритм",
"A new password must be entered.": "Введите новый пароль.",
- "Anyone who knows the room's link, apart from guests": "Любой, кто знает ссылку на комнату, кроме гостей",
- "Anyone who knows the room's link, including guests": "Любой, кто знает ссылку на комнату, включая гостей",
+ "Anyone who knows the room's link, apart from guests": "Все, у кого есть ссылка на эту комнату, кроме гостей",
+ "Anyone who knows the room's link, including guests": "Все, у кого есть ссылка на эту комнату, включая гостей",
"Are you sure you want to reject the invitation?": "Вы уверены что вы хотите отклонить приглашение?",
- "Are you sure you want to upload the following files?": "Вы уверены что вы хотите отправить следующие файлы?",
+ "Are you sure you want to upload the following files?": "Вы уверены, что вы хотите отправить эти файлы?",
"Banned users": "Заблокированные пользователи",
"Bans user with given id": "Блокирует пользователя с заданным ID",
"Blacklisted": "В черном списке",
"Bulk Options": "Групповые параметры",
"Can't load user settings": "Невозможно загрузить пользовательские настройки",
- "Changes to who can read history will only apply to future messages in this room": "Изменения того, кто может прочитать историю, будут применяться только к будущим сообщениям в этой комнате",
+ "Changes to who can read history will only apply to future messages in this room": "Изменение правил доступа к истории будет применено только к будущим сообщениям в этой комнате",
"Changes your display nickname": "Изменяет ваш псевдоним",
"Claimed Ed25519 fingerprint key": "Требуемый ключ цифрового отпечатка Ed25519",
"Clear Cache and Reload": "Очистить кэш и перезагрузить",
"Clear Cache": "Очистить кэш",
- "Click here to fix": "Щелкните здесь, чтобы исправить",
+ "Click here to fix": "Нажмите здесь, чтобы исправить это",
"Commands": "Команды",
"Confirm your new password": "Подтвердите новый пароль",
"Continue": "Продолжить",
@@ -32,19 +32,19 @@
"Deactivate Account": "Деактивировать учетную запись",
"Deactivate my account": "Деактивировать мою учетную запись",
"Decryption error": "Ошибка расшифровки",
- "Default": "По умолчанию",
+ "Default": "Обычный пользователь",
"Deops user with given id": "Снимает полномочия оператора с пользователя с заданным ID",
"Device ID": "ID устройства",
- "Devices will not yet be able to decrypt history from before they joined the room": "Устройства пока не могут дешифровать историю до их входа в комнату",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Устройства не смогут расшифровать историю сообщений до момента их входа в комнату (временно)",
"Display name": "Отображаемое имя",
"Displays action": "Отображение действий",
"Ed25519 fingerprint": "Ed25519 отпечаток",
- "Email, name or matrix ID": "Email, имя или matrix ID",
+ "Email, name or matrix ID": "Email-адрес, имя или идентификатор",
"Emoji": "Смайлы",
- "Encrypted messages will not be visible on clients that do not yet implement encryption": "Зашифрованные сообщения не будут видны в клиентах, которые еще не подключили шифрование",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Зашифрованные сообщения не будут видны в клиентах, еще не поддерживающих сквозное шифрование",
"Encrypted room": "Зашифрованная комната",
"End-to-end encryption information": "Сведения о сквозном шифровании",
- "End-to-end encryption is in beta and may not be reliable": "Сквозное шифрование находится в бета-версии и может быть ненадежным",
+ "End-to-end encryption is in beta and may not be reliable": "Сквозное шифрование сейчас в бета-тестировании и может не работать",
"Error": "Ошибка",
"Event information": "Информация о событии",
"Export E2E room keys": "Экспорт ключей сквозного шифрования",
@@ -56,64 +56,64 @@
"Failed to upload file": "Не удалось отправить файл",
"Favourite": "Избранное",
"Favourites": "Избранные",
- "Filter room members": "Фильтр участников комнаты",
+ "Filter room members": "Поиск по участникам",
"Forget room": "Забыть комнату",
"Forgot your password?": "Забыли пароль?",
"For security, this session has been signed out. Please sign in again.": "Для обеспечения безопасности ваша сессия была завершена. Пожалуйста, войдите снова.",
- "Hangup": "Закончить",
+ "Hangup": "Повесить трубку",
"Historical": "Архив",
"Homeserver is": "Домашний сервер это",
"Identity Server is": "Сервер идентификации это",
"I have verified my email address": "Я подтвердил свой адрес email",
"Import E2E room keys": "Импорт ключей сквозного шифрования",
"Invalid Email Address": "Недопустимый адрес email",
- "Invite new room members": "Пригласить новых участников в комнату",
- "Invites": "Приглашает",
+ "Invite new room members": "Пригласить в комнату новых участников",
+ "Invites": "Приглашения",
"Invites user with given id to current room": "Приглашает пользователя с заданным ID в текущую комнату",
- "Sign in with": "Войти, используя",
+ "Sign in with": "Войти с помощью",
"Joins room with given alias": "Входит в комнату с заданным псевдонимом",
"Kicks user with given id": "Выкидывает пользователя с заданным ID",
"Labs": "Лаборатория",
"Leave room": "Покинуть комнату",
"Login as guest": "Войти как гость",
"Logout": "Выйти",
- "Low priority": "Низкий приоритет",
+ "Low priority": "Неважные",
"Manage Integrations": "Управление интеграциями",
"Mobile phone number": "Номер мобильного телефона",
"Moderator": "Модератор",
- "%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
+ "%(serverName)s Matrix ID": "Matrix ID на %(serverName)s",
"Name": "Имя",
- "Never send encrypted messages to unverified devices from this device": "Никогда не отправлять зашифрованные сообщения на непроверенные устройства с этого устройства",
- "Never send encrypted messages to unverified devices in this room from this device": "Никогда не отправлять зашифрованные сообщения на непроверенные устройства в этой комнате с этого устройства",
+ "Never send encrypted messages to unverified devices from this device": "Никогда не отправлять зашифрованные сообщения на непроверенные устройства (с этого устройства)",
+ "Never send encrypted messages to unverified devices in this room from this device": "Никогда не отправлять зашифрованные сообщения на непроверенные устройства (в этой комнате, с этого устройства)",
"New password": "Новый пароль",
"New passwords must match each other.": "Новые пароли должны совпадать.",
"none": "никто",
"Notifications": "Уведомления",
"": "<не поддерживается>",
"NOT verified": "НЕ проверено",
- "No users have specific privileges in this room": "Ни один пользователь не имеет специальных полномочий в этой комнате",
- "Once encryption is enabled for a room it cannot be turned off again (for now)": "После включения шифрования в комнате, оно не может быть деактивировано (на данный момент)",
+ "No users have specific privileges in this room": "Ни один пользователь не имеет особых прав в этой комнате",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "После включения шифрования в комнате вы не сможете его снова выключить (временно)",
"Password": "Пароль",
"People": "Люди",
- "Permissions": "Разрешения",
+ "Permissions": "Права доступа",
"Phone": "Телефон",
"Remove": "Удалить",
"Return to login screen": "Вернуться к экрану входа",
"Send Reset Email": "Отправить письмо со ссылкой для сброса пароля",
"Settings": "Настройки",
- "Start a chat": "Начать чат",
- "Start Chat": "Начать чат",
+ "Start a chat": "Начать разговор",
+ "Start Chat": "Начать разговор",
"Unable to add email address": "Не удается добавить адрес email",
"Unable to remove contact information": "Не удалось удалить контактную информацию",
"Unable to verify email address.": "Не удалось проверить адрес email.",
"Unban": "Разблокировать",
- "Unencrypted room": "Незашифрованная комната",
+ "Unencrypted room": "Нешифрованная комната",
"unencrypted": "без шифрования",
"unknown device": "неизвестное устройство",
"unknown error code": "неизвестный код ошибки",
"Upload avatar": "Загрузить аватар",
"Upload Files": "Отправка файлов",
- "Upload file": "Отправка файла",
+ "Upload file": "Отправить файл",
"User ID": "ID пользователя",
"User Interface": "Пользовательский интерфейс",
"User name": "Имя пользователя",
@@ -121,61 +121,61 @@
"Verification Pending": "В ожидании подтверждения",
"Verification": "Проверка",
"verified": "проверенный",
- "Video call": "Видеозвонок",
+ "Video call": "Видеовызов",
"Voice call": "Голосовой вызов",
- "VoIP conference finished.": "VoIP-конференция закончилась.",
- "VoIP conference started.": "VoIP-конференция началась.",
+ "VoIP conference finished.": "Конференц-звонок окончен.",
+ "VoIP conference started.": "Конференц-звонок начался.",
"(warning: cannot be disabled again!)": "(предупреждение: отключить будет невозможно!)",
"Warning!": "Внимание!",
- "Who can access this room?": "Кто может получить доступ к этой комнате?",
+ "Who can access this room?": "Кто может войти в эту комнату?",
"Who can read history?": "Кто может читать историю?",
- "Who would you like to add to this room?": "Кого бы вы хотели добавить в эту комнату?",
+ "Who would you like to add to this room?": "Кого бы вы хотели пригласить в эту комнату?",
"Who would you like to communicate with?": "С кем бы вы хотели связаться?",
"You do not have permission to post to this room": "Вы не можете писать в эту комнату",
"You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Вы вышли из всех устройств и больше не будете получать push-уведомления. Чтобы повторно активировать уведомления, войдите снова на каждом из устройств",
"You have no visible notifications": "Нет видимых уведомлений",
"Your password has been reset": "Ваш пароль был сброшен",
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Пароль успешно изменен. До повторной авторизации вы не будете получать push-уведомления на других устройствах",
- "You should not yet trust it to secure data": "На сегодняшний день не следует полностью полагаться на то, что ваши данные будут надежно зашифрованы",
+ "You should not yet trust it to secure data": "На данный момент не следует полагаться на то, что ваша переписка будет надежно зашифрована",
"%(targetName)s accepted an invitation.": "%(targetName)s принял приглашение.",
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s принял приглашение от %(displayName)s.",
"Active call": "Активный вызов",
- "%(names)s and %(lastPerson)s are typing": "%(names)s и %(lastPerson)s печатает",
- "%(senderName)s answered the call.": "%(senderName)s ответил на звонок.",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s и %(lastPerson)s печатают",
+ "%(senderName)s answered the call.": "%(senderName)s ответил(а) на звонок.",
"%(senderName)s banned %(targetName)s.": "%(senderName)s заблокировал(а) %(targetName)s.",
- "Call Timeout": "Время ожидания вызова",
- "%(senderName)s changed their profile picture.": "%(senderName)s изменил изображение профиля.",
- "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s изменил(а) уровень доступа для %(powerLevelDiffText)s.",
+ "Call Timeout": "Нет ответа",
+ "%(senderName)s changed their profile picture.": "%(senderName)s изменил(а) свой аватар.",
+ "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s изменил(а) уровни прав %(powerLevelDiffText)s.",
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s изменил(а) название комнаты на %(roomName)s.",
- "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s изменил тему на %(topic)s.",
- "Conference call failed.": "Не удалось выполнить групповой вызов.",
- "Conference calling is in development and may not be reliable.": "Групповые вызовы находятся в разработке и могут быть нестабильны.",
- "Conference calls are not supported in encrypted rooms": "Групповые вызовы не поддерживаются в зашифрованных комнатах",
- "Conference calls are not supported in this client": "Групповые вызовы в этом клиенте не поддерживаются",
- "/ddg is not a command": "/ddg не команда",
- "Drop here to tag %(section)s": "Перетащите сюда для тега %(section)s",
- "%(senderName)s ended the call.": "%(senderName)s завершил звонок.",
+ "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s изменил(а) тему комнаты на \"%(topic)s\".",
+ "Conference call failed.": "Сбой конференц-звонка.",
+ "Conference calling is in development and may not be reliable.": "Конференц-связь находится в разработке и может не работать.",
+ "Conference calls are not supported in encrypted rooms": "Конференц-связь не поддерживается в зашифрованных комнатах",
+ "Conference calls are not supported in this client": "Конференц-связь в этом клиенте не поддерживается",
+ "/ddg is not a command": "/ddg — это не команда",
+ "Drop here to tag %(section)s": "Перетащите сюда, чтобы пометить как %(section)s",
+ "%(senderName)s ended the call.": "%(senderName)s завершил(а) звонок.",
"Existing Call": "Текущий вызов",
- "Failed to lookup current room": "Не удалось выполнить поиск текущий комнаты",
+ "Failed to lookup current room": "Не удалось найти текущую комнату",
"Failed to send request.": "Не удалось отправить запрос.",
- "Failed to set up conference call": "Не удалось настроить групповой вызов",
- "Failed to verify email address: make sure you clicked the link in the email": "Не удалось проверить адрес email: убедитесь, что вы перешли по ссылке в письме",
+ "Failed to set up conference call": "Не удалось сделать конференц-звонок",
+ "Failed to verify email address: make sure you clicked the link in the email": "Не удалось проверить email-адрес: убедитесь, что вы перешли по ссылке в письме",
"Failure to create room": "Не удалось создать комнату",
- "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s с %(fromPowerLevel)s на %(toPowerLevel)s",
+ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "для %(userId)s с %(fromPowerLevel)s на %(toPowerLevel)s",
"click to reveal": "нажмите для открытия",
"%(senderName)s invited %(targetName)s.": "%(senderName)s приглашает %(targetName)s.",
"%(displayName)s is typing": "%(displayName)s печатает",
- "%(targetName)s joined the room.": "%(targetName)s вошел(ла) в комнату.",
- "%(senderName)s kicked %(targetName)s.": "%(senderName)s выкинул %(targetName)s.",
- "%(targetName)s left the room.": "%(targetName)s покинул комнату.",
- "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s сделал(а) историю комнаты видимой для всех участников комнаты с момента их приглашения.",
- "%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s сделал(а) историю комнаты видимой для всех участников комнаты с момента их входа.",
- "%(senderName)s made future room history visible to all room members.": "%(senderName)s сделал(а) историю комнаты видимой для всех участников комнаты.",
- "%(senderName)s made future room history visible to anyone.": "%(senderName)s сделал(а) историю комнаты видимой для всех.",
- "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s сделал(а) историю комнаты видимой для неизвестного (%(visibility)s).",
+ "%(targetName)s joined the room.": "%(targetName)s вошел(-ла) в комнату.",
+ "%(senderName)s kicked %(targetName)s.": "%(senderName)s выгнал(а) %(targetName)s.",
+ "%(targetName)s left the room.": "%(targetName)s покинул(а) комнату.",
+ "%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s сделал(а) историю разговора видимой для всех собеседников с момента их приглашения.",
+ "%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s сделал(а) историю разговора видимой для всех собеседников с момента их входа в комнату.",
+ "%(senderName)s made future room history visible to all room members.": "%(senderName)s сделал(а) историю разговора видимой для всех собеседников.",
+ "%(senderName)s made future room history visible to anyone.": "%(senderName)s сделал(а) историю разговора видимой для всех.",
+ "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s сделал(а) историю комнаты видимой в неизвестном режиме (%(visibility)s).",
"Missing room_id in request": "Отсутствует room_id в запросе",
"Missing user_id in request": "Отсутствует user_id в запросе",
- "Must be viewing a room": "Необходимо посмотреть комнату",
+ "Must be viewing a room": "Вы должны просматривать комнату",
"(not supported by this browser)": "(не поддерживается этим браузером)",
"Connectivity to the server has been lost.": "Связь с сервером потеряна.",
"Sent messages will be stored until your connection has returned.": "Отправленные сообщения будут сохранены, пока соединение не восстановится.",
@@ -197,25 +197,25 @@
"Encrypt room": "Шифрование комнаты",
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
"Upload an avatar:": "Загрузите аватар:",
- "You need to be logged in.": "Вы должны быть авторизованы.",
+ "You need to be logged in.": "Вы должны войти в систему.",
"You need to be able to invite users to do that.": "Для этого вы должны иметь возможность приглашать пользователей.",
- "You cannot place VoIP calls in this browser.": "VoIP звонки не поддерживаются в этом браузере.",
- "You are already in a call.": "Вы уже совершаете вызов.",
- "You are trying to access %(roomName)s.": "Вы пытаетесь получить доступ к %(roomName)s.",
- "You cannot place a call with yourself.": "Вы не можете сделать вызов самому себе.",
- "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s отозвал %(targetName)s's приглашение.",
- "Sep": "Сен.",
- "Jan": "Янв.",
- "Feb": "Фев.",
- "Mar": "Мар.",
- "Apr": "Апр.",
+ "You cannot place VoIP calls in this browser.": "Звонки не поддерживаются в этом браузере.",
+ "You are already in a call.": "Вы уже сделали звонок.",
+ "You are trying to access %(roomName)s.": "Вы пытаетесь войти в %(roomName)s.",
+ "You cannot place a call with yourself.": "Вы не можете позвонить самому себе.",
+ "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s отозвал(а) свое приглашение %(targetName)s.",
+ "Sep": "Сен",
+ "Jan": "Янв",
+ "Feb": "Фев",
+ "Mar": "Мар",
+ "Apr": "Апр",
"May": "Май",
- "Jun": "Июн.",
- "Jul": "Июл.",
- "Aug": "Авг.",
- "Oct": "Окт.",
- "Nov": "Ноя.",
- "Dec": "Дек.",
+ "Jun": "Июн",
+ "Jul": "Июл",
+ "Aug": "Авг",
+ "Oct": "Окт",
+ "Nov": "Ноя",
+ "Dec": "Дек",
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(time)s",
"Mon": "Пн",
"Sun": "Вс",
@@ -224,21 +224,21 @@
"Thu": "Чт",
"Fri": "Пт",
"Sat": "Сб",
- "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Ваш адрес email, кажется, не связан с Matrix ID на этом домашнем сервере.",
- "To use it, just wait for autocomplete results to load and tab through them.": "Для того, чтобы использовать эту функцию, просто подождите автозаполнения результатов, а затем используйте клавишу TAB для прокрутки.",
- "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s включено сквозное шифрование (algorithm %(algorithm)s).",
+ "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Ваш email-адрес не связан ни с одним пользователем на этом сервере.",
+ "To use it, just wait for autocomplete results to load and tab through them.": "Чтобы воспользоваться этой функцией, дождитесь загрузки результатов в окне автодополнения, а затем используйте Tab для прокрутки.",
+ "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s включил(а) в комнате сквозное шифрование (алгоритм %(algorithm)s).",
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s разблокировал(а) %(targetName)s.",
"Unable to capture screen": "Не удается сделать снимок экрана",
"Unable to enable Notifications": "Не удалось включить уведомления",
- "Upload Failed": "Сбой при отправке",
+ "Upload Failed": "Сбой отправки файла",
"Usage": "Использование",
"Use with caution": "Использовать с осторожностью",
- "VoIP is unsupported": "VoIP не поддерживается",
- "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Текстовое сообщение было отправлено на +%(msisdn)s. Введите проверочный код, который оно содержит",
+ "VoIP is unsupported": "Звонки не поддерживаются",
+ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Текстовое сообщение было отправлено на +%(msisdn)s. Введите проверочный код из сообщения",
"and %(count)s others...|other": "и %(count)s других...",
"and %(count)s others...|one": "и еще один...",
"Are you sure?": "Вы уверены?",
- "Autoplay GIFs and videos": "Автовоспроизведение GIF и видео",
+ "Autoplay GIFs and videos": "Автоматически воспроизводить GIF-анимации и видео",
"Click to mute audio": "Щелкните, чтобы выключить звук",
"Click to mute video": "Щелкните, чтобы выключить видео",
"Click to unmute video": "Щелкните, чтобы включить видео",
@@ -246,18 +246,18 @@
"Decrypt %(text)s": "Расшифровать %(text)s",
"Delete": "Удалить",
"Devices": "Устройства",
- "Direct chats": "Прямые чаты",
+ "Direct chats": "Личные чаты",
"Disinvite": "Отозвать приглашение",
- "Don't send typing notifications": "Не оповещать, когда я печатаю",
- "Download %(text)s": "Загрузить %(text)s",
+ "Don't send typing notifications": "Не отправлять оповещения о том, когда я печатаю",
+ "Download %(text)s": "Скачать %(text)s",
"Enable encryption": "Включить шифрование",
"Enter Code": "Ввести код",
"Failed to ban user": "Не удалось заблокировать пользователя",
- "Failed to change power level": "Не удалось изменить уровень привилегий",
- "Failed to forget room %(errCode)s": "Не удалось удалить комнату %(errCode)s",
+ "Failed to change power level": "Не удалось изменить уровень прав",
+ "Failed to forget room %(errCode)s": "Не удалось забыть комнату: %(errCode)s",
"Failed to join room": "Не удалось войти в комнату",
"Access Token:": "Токен доступа:",
- "Always show message timestamps": "Всегда показывать временные метки сообщений",
+ "Always show message timestamps": "Всегда показывать время отправки сообщений",
"Authentication": "Аутентификация",
"olm version:": "Версия olm:",
"%(items)s and %(lastItem)s": "%(items)s и %(lastItem)s",
@@ -266,7 +266,7 @@
"Ban": "Заблокировать",
"Change Password": "Сменить пароль",
"Command error": "Ошибка команды",
- "Confirm password": "Подтвердите пароль",
+ "Confirm password": "Подтвердите новый пароль",
"Current password": "Текущий пароль",
"Email": "Электронная почта",
"Failed to kick": "Не удалось выгнать",
@@ -275,45 +275,45 @@
"Failed to reject invite": "Не удалось отклонить приглашение",
"Failed to save settings": "Не удалось сохранить настройки",
"Failed to set display name": "Не удалось задать отображаемое имя",
- "Failed to toggle moderator status": "Не удалось изменить статус модератора",
+ "Failed to toggle moderator status": "Не удалось переключить статус модератора",
"Fill screen": "Заполнить экран",
- "Hide read receipts": "Скрыть отметки о прочтении",
- "Hide Text Formatting Toolbar": "Скрыть панель форматирования текста",
+ "Hide read receipts": "Скрывать отметки о прочтении",
+ "Hide Text Formatting Toolbar": "Скрыть инструменты форматирования текста",
"Incorrect verification code": "Неверный код подтверждения",
"Interface Language": "Язык интерфейса",
- "Invalid alias format": "Недопустимый формат псевдонима",
+ "Invalid alias format": "Недопустимый формат имени",
"Invalid address format": "Недопустимый формат адреса",
- "'%(alias)s' is not a valid format for an address": "'%(alias)s' недопустимый формат адреса",
- "'%(alias)s' is not a valid format for an alias": "'%(alias)s' недопустимый формат псевдонима",
+ "'%(alias)s' is not a valid format for an address": "Адрес '%(alias)s' имеет недопустимый формат",
+ "'%(alias)s' is not a valid format for an alias": "Имя '%(alias)s' имеет недопустимый формат",
"Join Room": "Войти в комнату",
"Kick": "Выгнать",
- "Local addresses for this room:": "Локальные адреса этой комнаты:",
+ "Local addresses for this room:": "Адреса этой комнаты на вашем сервере:",
"Markdown is disabled": "Markdown отключен",
"Markdown is enabled": "Markdown включен",
"matrix-react-sdk version:": "версия matrix-react-sdk:",
- "New address (e.g. #foo:%(localDomain)s)": "Новый адрес (например, #foo:%(localDomain)s)",
+ "New address (e.g. #foo:%(localDomain)s)": "Новый адрес (например, #чтонибудь:%(localDomain)s)",
"New passwords don't match": "Новые пароли не совпадают",
- "not set": "не задано",
- "not specified": "не определен",
+ "not set": "не указан",
+ "not specified": "не задан",
"No devices with registered encryption keys": "Нет устройств с зарегистрированными ключами шифрования",
"No more results": "Больше никаких результатов",
"No results": "Нет результатов",
"OK": "OK",
- "Only people who have been invited": "Только приглашенные люди",
+ "Only people who have been invited": "Только приглашенные участники",
"Passwords can't be empty": "Пароли не могут быть пустыми",
- "%(senderName)s placed a %(callType)s call.": "%(senderName)s выполнил %(callType)s вызов.",
+ "%(senderName)s placed a %(callType)s call.": "%(senderName)s начал(а) %(callType)s-звонок.",
"Please check your email and click on the link it contains. Once this is done, click continue.": "Проверьте свою электронную почту и нажмите на содержащуюся ссылку. После этого нажмите кнопку Продолжить.",
- "Power level must be positive integer.": "Уровень авторизации должен быть положительным целым числом.",
+ "Power level must be positive integer.": "Уровень прав должен быть положительным целым числом.",
"Profile": "Профиль",
"Reason": "Причина",
- "%(targetName)s rejected the invitation.": "%(targetName)s отклонил приглашение.",
+ "%(targetName)s rejected the invitation.": "%(targetName)s отклонил(а) приглашение.",
"Reject invitation": "Отклонить приглашение",
"Remove Contact Information?": "Удалить контактную информацию?",
- "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s удалил свое отображаемое имя (%(oldDisplayName)s).",
- "%(senderName)s removed their profile picture.": "%(senderName)s удалил свое изображение профиля.",
- "%(senderName)s requested a VoIP conference.": "%(senderName)s хочет начать VoIP-конференцию.",
+ "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s удалил(а) свое отображаемое имя (%(oldDisplayName)s).",
+ "%(senderName)s removed their profile picture.": "%(senderName)s удалил(а) свой аватар.",
+ "%(senderName)s requested a VoIP conference.": "%(senderName)s хочет начать конференц-звонок.",
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Сброс пароля на данный момент сбрасывает ключи шифрования на всех устройствах, делая зашифрованную историю чатов нечитаемой. Чтобы избежать этого, экспортируйте ключи комнат и импортируйте их после сброса пароля. В будущем это будет исправлено.",
- "Riot does not have permission to send you notifications - please check your browser settings": "У Riot нет разрешений на отправку уведомлений - проверьте настройки браузера",
+ "Riot does not have permission to send you notifications - please check your browser settings": "У Riot нет разрешения на отправку уведомлений — проверьте настройки браузера",
"Riot was not given permission to send notifications - please try again": "Riot не получил разрешение на отправку уведомлений, пожалуйста, попробуйте снова",
"riot-web version:": "версия riot-web:",
"Room %(roomId)s not visible": "Комната %(roomId)s невидима",
@@ -321,13 +321,13 @@
"Room name (optional)": "Имя комнаты (необязательно)",
"Rooms": "Комнаты",
"Scroll to bottom of page": "Перейти к нижней части страницы",
- "Scroll to unread messages": "Прокрутка к непрочитанным сообщениям",
+ "Scroll to unread messages": "Прокрутить до непрочитанных сообщений",
"Search": "Поиск",
"Search failed": "Поиск не удался",
"Sender device information": "Информация об устройстве отправителя",
"Send Invites": "Отправить приглашения",
- "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s отправил изображение.",
- "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s отправил(а) приглашение для %(targetDisplayName)s войти в комнату.",
+ "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s отправил(а) изображение.",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s пригласил(а) %(targetDisplayName)s в комнату.",
"Show panel": "Показать панель",
"Sign in": "Войти",
"Sign out": "Выйти",
@@ -335,14 +335,14 @@
"Someone": "Кто-то",
"Submit": "Отправить",
"Success": "Успех",
- "The default role for new room members is": "Роль по умолчанию для новых участников комнаты",
- "The main address for this room is": "Основной адрес для этой комнаты",
- "This email address is already in use": "Этот адрес email уже используется",
+ "The default role for new room members is": "Права по умолчанию для новых участников комнаты",
+ "The main address for this room is": "Основной адрес этой комнаты",
+ "This email address is already in use": "Этот email-адрес уже используется",
"This email address was not found": "Этот адрес электронной почты не найден",
"The email address linked to your account must be entered.": "Необходимо ввести адрес электронной почты, связанный с вашей учетной записью.",
"The file '%(fileName)s' failed to upload": "Не удалось отправить файл '%(fileName)s'",
- "The remote side failed to pick up": "Вызываемый абонент не ответил",
- "This room has no local addresses": "В этой комнате нет локальных адресов",
+ "The remote side failed to pick up": "Собеседник не ответил на ваш звонок",
+ "This room has no local addresses": "Эта комната не имеет адресов на вашем сервере",
"This room is not recognised.": "Эта комната не опознана.",
"These are experimental features that may break in unexpected ways": "Это экспериментальные функции, которые могут себя вести неожиданным образом",
"This doesn't appear to be a valid email address": "Похоже, это недействительный адрес email",
@@ -352,30 +352,30 @@
"Turn Markdown off": "Выключить Markdown",
"Turn Markdown on": "Включить Markdown",
"Unknown room %(roomId)s": "Неизвестная комната %(roomId)s",
- "You have been invited to join this room by %(inviterName)s": "%(inviterName)s приглашает вас в комнату",
+ "You have been invited to join this room by %(inviterName)s": "%(inviterName)s приглашает вас в эту комнату",
"You seem to be uploading files, are you sure you want to quit?": "Похоже, вы отправляете файлы, вы уверены, что хотите выйти?",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s %(time)s",
"Make Moderator": "Сделать модератором",
"Room": "Комната",
"Cancel": "Отмена",
"bold": "жирный",
- "italic": "курсивный",
+ "italic": "курсив",
"strike": "перечеркнутый",
"underline": "подчеркнутый",
"code": "код",
"quote": "цитата",
- "bullet": "список",
- "numbullet": "нумерованный список",
+ "bullet": "элемент списка",
+ "numbullet": "элемент нумерованного списка",
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Не удается подключиться к домашнему серверу через HTTP, так как в адресной строке браузера указан URL HTTPS. Используйте HTTPS или либо включите небезопасные сценарии.",
"Dismiss": "Отклонить",
- "Custom Server Options": "Настраиваемые параметры сервера",
- "Mute": "Беззвучный",
+ "Custom Server Options": "Выбор другого сервера",
+ "Mute": "Заглушить",
"Operation failed": "Сбой операции",
"powered by Matrix": "Основано на Matrix",
- "Add a topic": "Добавить тему",
- "Show timestamps in 12 hour format (e.g. 2:30pm)": "Отображать время в 12-часовом формате (напр. 2:30pm)",
- "Use compact timeline layout": "Использовать компактный макет временной шкалы",
- "Hide removed messages": "Скрыть удаленные сообщения",
+ "Add a topic": "Задать тему",
+ "Show timestamps in 12 hour format (e.g. 2:30pm)": "Отображать время в 12-часовом формате (напр. 2:30 ПП)",
+ "Use compact timeline layout": "Использовать компактный вид списка сообщений",
+ "Hide removed messages": "Скрывать удаленные сообщения",
"No Microphones detected": "Микрофоны не обнаружены",
"Unknown devices": "Неизвестное устройство",
"Camera": "Камера",
@@ -384,29 +384,29 @@
"Start automatically after system login": "Автозапуск при входе в систему",
"Analytics": "Аналитика",
"Riot collects anonymous analytics to allow us to improve the application.": "Riot собирает анонимные данные, позволяющие нам улучшить приложение.",
- "Opt out of analytics": "Не собирать аналитические данные",
+ "Opt out of analytics": "Не отправлять данные для аналитики",
"Logged in as:": "Вы вошли как:",
"Default Device": "Устройство по умолчанию",
"No Webcams detected": "Веб-камера не обнаружена",
"VoIP": "VoIP",
"For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Для обеспечения безопасности при выходе будут удалены все ключи сквозного шифрования из этого браузера. Если вы хотите иметь возможность расшифровать историю сообщений в будущем, необходимо экспортировать ключи комнат вручную.",
"Guest access is disabled on this Home Server.": "Гостевой доступ отключен на этом сервере.",
- "Guests cannot join this room even if explicitly invited.": "Гости не могут войти в эту комнату, даже если они приглашены.",
+ "Guests cannot join this room even if explicitly invited.": "Посторонние не смогут войти в эту комнату, даже если они будут приглашены.",
"Missing Media Permissions, click here to request.": "Отсутствуют разрешения, нажмите для запроса.",
"No media permissions": "Нет разрешенных носителей",
"You may need to manually permit Riot to access your microphone/webcam": "Вам необходимо предоставить Riot доступ к микрофону или веб-камере вручную",
"Anyone": "Все",
"Are you sure you want to leave the room '%(roomName)s'?": "Вы уверены, что хотите покинуть '%(roomName)s'?",
- "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s удалил имя комнаты.",
- "Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Смена пароля на данный момент сбрасывает ключи сквозного шифрования на всех устройствах, делая зашифрованную историю чата нечитаемой. Чтобы избежать этого, экспортируйте ключи комнат и импортируйте их после смены пароля. В будущем это будет исправлено.",
+ "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s удалил(а) имя комнаты.",
+ "Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Смена пароля на данный момент сбрасывает ключи сквозного шифрования на всех устройствах, делая историю зашифрованных чатов нечитаемой. Чтобы избежать этого, экспортируйте ключи сквозного шифрования и импортируйте их после смены пароля. В будущем это будет исправлено.",
"Custom level": "Пользовательский уровень",
"Device already verified!": "Устройство уже проверено!",
"Device ID:": "ID устройства:",
"device id: ": "ID устройства: ",
"Device key:": "Ключ устройства:",
- "Email address": "Адрес email",
- "Email address (optional)": "Адрес email (необязательно)",
- "Error decrypting attachment": "Ошибка при расшифровке вложения",
+ "Email address": "Email-адрес",
+ "Email address (optional)": "Email-адрес (необязательно)",
+ "Error decrypting attachment": "Ошибка расшифровки вложения",
"Export": "Экспорт",
"Failed to set avatar.": "Не удалось установить аватар.",
"Import": "Импорт",
@@ -415,14 +415,14 @@
"Invited": "Приглашен",
"Jump to first unread message.": "Перейти к первому непрочитанному сообщению.",
"Message not sent due to unknown devices being present": "Сообщение не отправлено из-за присутствия неизвестных устройств",
- "Mobile phone number (optional)": "Номер мобильного телефона (не обязательно)",
+ "Mobile phone number (optional)": "Номер мобильного телефона (необязательно)",
"Password:": "Пароль:",
"Privacy warning": "Предупреждение о конфиденциальности",
"Privileged Users": "Привилегированные пользователи",
"Revoke Moderator": "Отозвать права модератора",
"Refer a friend to Riot:": "Расскажите другу о Riot:",
- "Register": "Регистрация",
- "Remote addresses for this room:": "Удаленные адреса для этой комнаты:",
+ "Register": "Зарегистрироваться",
+ "Remote addresses for this room:": "Адреса этой комнаты на других серверах:",
"Remove %(threePid)s?": "Удалить %(threePid)s?",
"Results from DuckDuckGo": "Результаты от DuckDuckGo",
"Save": "Сохранить",
@@ -431,35 +431,35 @@
"Server may be unavailable or overloaded": "Сервер может быть недоступен или перегружен",
"Server may be unavailable, overloaded, or search timed out :(": "Сервер может быть недоступен, перегружен или поиск прекращен по тайм-ауту :(",
"Server may be unavailable, overloaded, or the file too big": "Сервер может быть недоступен, перегружен или размер файла слишком большой",
- "Server may be unavailable, overloaded, or you hit a bug.": "Сервер может быть недоступен, перегружен или возникла ошибка.",
- "Server unavailable, overloaded, or something else went wrong.": "Сервер может быть недоступен, перегружен или что-то пошло не так.",
+ "Server may be unavailable, overloaded, or you hit a bug.": "Возможно, сервер недоступен, перегружен или случилась ошибка.",
+ "Server unavailable, overloaded, or something else went wrong.": "Возможно, сервер недоступен, перегружен или что-то еще пошло не так.",
"Session ID": "ID сессии",
- "%(senderName)s set a profile picture.": "%(senderName)s установил изображение профиля.",
- "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s изменил отображаемое имя на %(displayName)s.",
+ "%(senderName)s set a profile picture.": "%(senderName)s установил(а) себе аватар.",
+ "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s изменил(а) отображаемое имя на %(displayName)s.",
"Signed Out": "Выполнен выход",
- "Tagged as: ": "Теги: ",
- "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Предоставленный ключ подписи соответствует ключу, полученному от %(userId)s с устройства %(deviceId)s. Устройство помечено как проверенное.",
- "The file '%(fileName)s' exceeds this home server's size limit for uploads": "Файл '%(fileName)s' превышает предельный размер, допустимый к отправке на этом домашнем сервере",
+ "Tagged as: ": "Метки: ",
+ "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Предоставленный вами ключ совпадает с ключом, полученным от %(userId)s с устройства %(deviceId)s. Это устройство помечено как проверенное.",
+ "The file '%(fileName)s' exceeds this home server's size limit for uploads": "Файл '%(fileName)s' слишком большой для отправки на этот сервер",
"This Home Server does not support login using email address.": "Этот домашний сервер не поддерживает авторизацию с использованием адреса электронной почты.",
- "The visibility of existing history will be unchanged": "Видимость существующей истории не изменится",
- "This room is not accessible by remote Matrix servers": "Это комната недоступна с удаленных серверов Matrix",
+ "The visibility of existing history will be unchanged": "Правила доступа к существующей истории не изменятся",
+ "This room is not accessible by remote Matrix servers": "Это комната недоступна из других серверов Matrix",
"To reset your password, enter the email address linked to your account": "Чтобы сбросить пароль, введите адрес электронной почты, связанный с вашей учетной записью",
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Попытка загрузить выбранный интервал истории чата этой комнаты не удалась, так как у вас нет разрешений на просмотр.",
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Попытка загрузить выбранный интервал истории чата этой комнаты не удалась, так как запрошенный элемент не найден.",
"Unable to load device list": "Не удалось загрузить список устройств",
- "Unknown (user, device) pair:": "Неизвестная пара (пользователь, устройство):",
- "Unmute": "Включить звук",
+ "Unknown (user, device) pair:": "Неизвестная пара пользователь-устройство:",
+ "Unmute": "Вернуть право речи",
"Unrecognised command:": "Нераспознанная команда:",
- "Unrecognised room alias:": "Нераспознанный псевдоним комнаты:",
- "Verified key": "Проверенный ключ",
+ "Unrecognised room alias:": "Нераспознанное имя комнаты:",
+ "Verified key": "Ключ проверен",
"WARNING: Device already verified, but keys do NOT MATCH!": "ВНИМАНИЕ: устройство уже было проверено, однако ключи НЕ СОВПАДАЮТ!",
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "ВНИМАНИЕ: ОШИБКА ПРОВЕРКИ КЛЮЧЕЙ! Ключ подписи пользователя %(userId)s на устройстве %(deviceId)s — \"%(fprint)s\", и он не соответствует предоставленному ключу \"%(fingerprint)s\". Это может означать, что ваше общение перехватывается!",
- "You have disabled URL previews by default.": "Предварительный просмотр ссылок отключен по-умолчанию.",
- "You have enabled URL previews by default.": "Предварительный просмотр ссылок включен по-умолчанию.",
+ "You have disabled URL previews by default.": "Предпросмотр ссылок по умолчанию выключен для вас.",
+ "You have enabled URL previews by default.": "Предпросмотр ссылок по умолчанию включен для вас.",
"You need to enter a user name.": "Необходимо ввести имя пользователя.",
"You seem to be in a call, are you sure you want to quit?": "Звонок не завершен, вы уверены, что хотите выйти?",
- "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Вы не сможете отменить это изменение, так как этот пользователь получит уровень доступа, аналогичный вашему.",
- "Please select the destination room for this message": "Выберите комнату для отправки этого сообщения",
+ "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Вы не сможете отменить это действие, так как этот пользователь получит уровень прав, равный вашему.",
+ "Please select the destination room for this message": "Выберите, куда отправить это сообщение",
"Options": "Настройки",
"Passphrases must match": "Пароли должны совпадать",
"Passphrase must not be empty": "Пароль не должен быть пустым",
@@ -477,14 +477,14 @@
"Start new chat": "Начать новый чат",
"Failed to invite": "Пригласить не удалось",
"Failed to invite user": "Не удалось пригласить пользователя",
- "Failed to invite the following users to the %(roomName)s room:": "Не удалось пригласить следующих пользователей в %(roomName)s:",
+ "Failed to invite the following users to the %(roomName)s room:": "Не удалось пригласить этих пользователей в %(roomName)s:",
"Confirm Removal": "Подтвердите удаление",
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Вы действительно хотите удалить это событие? Обратите внимание, что если это смена названия комнаты или темы, то удаление отменит это изменение.",
"Unknown error": "Неизвестная ошибка",
"Incorrect password": "Неверный пароль",
"This will make your account permanently unusable. You will not be able to re-register the same user ID.": "Ваша учетная запись будет заблокирована навсегда. Вы не сможете повторно зарегистрировать тот же идентификатор пользователя.",
"This action is irreversible.": "Это действие необратимо.",
- "To continue, please enter your password.": "Для продолжения введите ваш пароль.",
+ "To continue, please enter your password.": "Чтобы продолжить, введите ваш пароль.",
"To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "Чтобы удостовериться, что этому устройству можно доверять, пожалуйста, свяжитесь с владельцем другим способом (например, лично или по телефону) и спросите его, совпадает ли ключ, указанный у него в настройках для этого устройства, с ключом ниже:",
"Device name": "Имя устройства",
"Device key": "Ключ устройства",
@@ -500,46 +500,46 @@
"We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Мы рекомендуем вам выполнить процедуру проверки каждого устройства, чтобы удостовериться, что они принадлежат их законному владельцу, но вы можете переотправить сообщение без проверки, если хотите.",
"\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" содержит неподтвержденные устройства.",
"Unknown Address": "Неизвестный адрес",
- "Unblacklist": "Удалить из черного списка",
- "Blacklist": "Черный список",
- "Unverify": "Отозвать статус проверенного",
- "Verify...": "Проверить...",
+ "Unblacklist": "Разблокировать",
+ "Blacklist": "Заблокировать",
+ "Unverify": "Отозвать верификацию",
+ "Verify...": "Верифицировать...",
"ex. @bob:example.com": "например @bob:example.com",
"Add User": "Добавить пользователя",
- "This Home Server would like to make sure you are not a robot": "Этот домашний сервер хочет убедиться, что вы не робот",
- "Sign in with CAS": "Войти, используя CAS",
- "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Вы можете использовать настраиваемые параметры сервера для входа на другие серверы Matrix, указав другой URL-адрес домашнего сервера.",
- "This allows you to use this app with an existing Matrix account on a different home server.": "Это позволяет использовать это приложение с существующей учетной записью Matrix на другом домашнем сервере.",
- "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "Вы также можете установить другой сервер идентификации, но это, как правило, будет препятствовать взаимодействию с пользователями на основе адреса email.",
- "Please check your email to continue registration.": "Проверьте электронную почту, чтобы продолжить регистрацию.",
- "Token incorrect": "Неверный токен",
+ "This Home Server would like to make sure you are not a robot": "Этот сервер хочет убедиться, что вы не робот",
+ "Sign in with CAS": "Войти с помощью CAS",
+ "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Вы можете войти на другой сервер Matrix, указав его URL-адрес.",
+ "This allows you to use this app with an existing Matrix account on a different home server.": "Это позволяет использовать приложение с учетной записью Matrix на другом сервере.",
+ "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "Кроме того, можно выбрать другой сервер идентификации, однако в таком случае вы, скорее всего, не сможете взаимодействовать с пользователями посредством их email-адресов.",
+ "Please check your email to continue registration.": "Чтобы продолжить регистрацию, проверьте электронную почту.",
+ "Token incorrect": "Неверный код проверки",
"Please enter the code it contains:": "Введите полученный код:",
- "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Если адрес email не указан, сброс пароля будет невозможным. Уверены?",
- "You are registering with %(SelectedTeamName)s": "Вы регистрируетесь на %(SelectedTeamName)s",
+ "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Если не указать email-адрес, вы не сможете при необходимости сбросить свой пароль. Уверены?",
+ "You are registering with %(SelectedTeamName)s": "Вы регистрируетесь в %(SelectedTeamName)s",
"Default server": "Сервер по умолчанию",
- "Custom server": "Пользовательский сервер",
- "Home server URL": "URL-адрес домашнего сервера",
+ "Custom server": "Другой сервер",
+ "Home server URL": "URL-адрес сервера",
"Identity server URL": "URL-адрес сервера идентификации",
"What does this mean?": "Что это значит?",
- "Error decrypting audio": "Ошибка расшифровки аудио",
+ "Error decrypting audio": "Ошибка расшифровки аудиозаписи",
"Error decrypting image": "Ошибка расшифровки изображения",
"Image '%(Body)s' cannot be displayed.": "Изображение '%(Body)s' не может быть отображено.",
- "This image cannot be displayed.": "Не удается отобразить изображение.",
+ "This image cannot be displayed.": "Не удается показать изображение.",
"Error decrypting video": "Ошибка расшифровки видео",
"Add an Integration": "Добавить интеграцию",
- "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "Вы будете пернаправлены на внешний сайт, где сможете аутентифицировать свою учетную запись для использования с %(integrationsUrl)s. Продолжить?",
- "Removed or unknown message type": "Удалено или неизвестный тип сообщения",
- "URL Previews": "Предварительный просмотр URL-адресов",
+ "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "Вы будете перенаправлены на внешний сайт, чтобы войти в свою учетную запись для использования с %(integrationsUrl)s. Продолжить?",
+ "Removed or unknown message type": "Сообщение удалено или имеет неизвестный тип",
+ "URL Previews": "Предпросмотр содержимого ссылок",
"Drop file here to upload": "Перетащите файл сюда для отправки",
" (unsupported)": " (не поддерживается)",
- "Ongoing conference call%(supportedText)s.": "Установлен групповой вызов %(supportedText)s.",
+ "Ongoing conference call%(supportedText)s.": "Идет конференц-звонок%(supportedText)s.",
"Online": "Онлайн",
"Idle": "Неактивен",
"Offline": "Не в сети",
- "%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s сменил аватар комнаты на ",
- "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s удалил аватар комнаты.",
- "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s",
- "Create new room": "Создать новую комнату",
+ "%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s сменил(а) аватар комнаты на ",
+ "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s удалил(а) аватар комнаты.",
+ "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил(а) аватар %(roomName)s",
+ "Create new room": "Создать комнату",
"Room directory": "Каталог комнат",
"Start chat": "Начать чат",
"Add": "Добавить",
@@ -564,12 +564,12 @@
"Something went wrong!": "Что-то пошло не так!",
"This will be your account name on the homeserver, or you can pick a different server.": "Это будет имя вашей учетной записи на домашнем сервере, или вы можете выбрать другой сервер.",
"If you already have a Matrix account you can log in instead.": "Если у вас уже есть учетная запись Matrix, вы можете войти.",
- "Home": "Старт",
+ "Home": "Начало",
"Accept": "Принять",
- "Active call (%(roomName)s)": "Активный вызов (%(roomName)s)",
+ "Active call (%(roomName)s)": "Текущий вызов (%(roomName)s)",
"Admin Tools": "Инструменты администратора",
"Alias (optional)": "Псевдоним (опционально)",
- "Click here to join the discussion!": "Нажмите здесь, чтобы присоединиться к обсуждению!",
+ "Click here to join the discussion!": "Нажмите здесь, чтобы присоединиться!",
"Close": "Закрыть",
"Disable Notifications": "Отключить уведомления",
"Drop File Here": "Перетащите файл сюда",
@@ -578,12 +578,12 @@
"Encrypted by an unverified device": "Зашифровано непроверенным устройством",
"Encryption is enabled in this room": "Шифрование в этой комнате включено",
"Encryption is not enabled in this room": "Шифрование в этой комнате не включено",
- "Failed to upload profile picture!": "Не удалось отправить изображение профиля!",
+ "Failed to upload profile picture!": "Не удалось загрузить аватар!",
"Incoming call from %(name)s": "Входящий вызов от %(name)s",
"Incoming video call from %(name)s": "Входящий видеовызов от %(name)s",
"Incoming voice call from %(name)s": "Входящий голосовой вызов от %(name)s",
- "Join as voice or video.": "Войти как голос или видео.",
- "Last seen": "Последний визит",
+ "Join as voice or video.": "Присоединиться с голосом или с видео.",
+ "Last seen": "Последний вход",
"Level:": "Уровень:",
"No display name": "Нет отображаемого имени",
"Otherwise, click here to send a bug report.": "В противном случае, нажмите 2 для отправки отчета об ошибке.",
@@ -591,47 +591,47 @@
"Public Chat": "Публичный чат",
"Reason: %(reasonText)s": "Причина: %(reasonText)s",
"Rejoin": "Войти повторно",
- "Start authentication": "Начать проверку подлинности",
- "This room": "В этой комнате",
- "(~%(count)s results)|other": "(~%(count)s результаты)",
+ "Start authentication": "Начать аутентификацию",
+ "This room": "Эта комната",
+ "(~%(count)s results)|other": "(~%(count)s результатов)",
"Device Name": "Имя устройства",
"Custom": "Пользовательские",
"Decline": "Отклонить",
"Room contains unknown devices": "Комната содержит непроверенные устройства",
"%(roomName)s does not exist.": "%(roomName)s не существует.",
"%(roomName)s is not accessible at this time.": "%(roomName)s на данный момент недоступна.",
- "Seen by %(userName)s at %(dateTime)s": "Просмотрено %(userName)s в %(dateTime)s",
+ "Seen by %(userName)s at %(dateTime)s": "Прочитано %(userName)s в %(dateTime)s",
"Send anyway": "Отправить в любом случае",
- "Show Text Formatting Toolbar": "Показать панель инструментов форматирования текста",
- "This invitation was sent to an email address which is not associated with this account:": "Это приглашение было отправлено на адрес email, не связанный с этой учетной записью:",
- "To link to a room it must have an address.": "Чтобы связаться с комнатой, она должна иметь адрес.",
- "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Не удалось установить соответствует ли адрес, по которому этому приглашение было послано, вашей учетной записи.",
+ "Show Text Formatting Toolbar": "Показать инструменты форматирования текста",
+ "This invitation was sent to an email address which is not associated with this account:": "Это приглашение было отправлено на email-адрес, не связанный с вашей учетной записью:",
+ "To link to a room it must have an address.": "Чтобы иметь возможность ссылаться на комнату, ей нужно присвоить адрес.",
+ "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Не удалось установить, что адрес в этом приглашении соответствует вашей учетной записи.",
"Undecryptable": "Невозможно расшифровать",
- "Unencrypted message": "Незашифрованное сообщение",
+ "Unencrypted message": "Нешифрованное сообщение",
"unknown caller": "неизвестный абонент",
- "Unnamed Room": "Комната без имени",
+ "Unnamed Room": "Комната без названия",
"Unverified": "Не проверено",
- "Upload new:": "Отправить новый:",
- "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (уровень доступа %(powerLevelNumber)s)",
+ "Upload new:": "Загрузить новый:",
+ "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (уровень прав %(powerLevelNumber)s)",
"Verified": "Проверено",
- "Would you like to accept or decline this invitation?": "Вы хотели бы подтвердить или отклонить это приглашение?",
+ "Would you like to accept or decline this invitation?": "Вы хотите подтвердить или отклонить это приглашение?",
"(~%(count)s results)|one": "(~%(count)s результат)",
"Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Не удается подключиться к домашнему серверу - проверьте подключение, убедитесь, что ваш SSL-сертификат домашнего сервера является доверенным и что расширение браузера не блокирует запросы.",
- "You have been banned from %(roomName)s by %(userName)s.": "%(userName)s заблокировал вас в %(roomName)s.",
- "You have been kicked from %(roomName)s by %(userName)s.": "%(userName)s выгнал вас из %(roomName)s.",
- "You may wish to login with a different account, or add this email to this account.": "При желании вы можете войти в систему с другой учетной записью или добавить этот адрес email в эту учетную запись.",
- "Your home server does not support device management.": "Ваш домашний сервер не поддерживает управление устройствами.",
- "(could not connect media)": "(подключение к СМИ не может быть установлено)",
+ "You have been banned from %(roomName)s by %(userName)s.": "%(userName)s заблокировал(а) вас в %(roomName)s.",
+ "You have been kicked from %(roomName)s by %(userName)s.": "%(userName)s выгнал(а) вас из %(roomName)s.",
+ "You may wish to login with a different account, or add this email to this account.": "При желании вы можете войти в систему под другим именем или привязать этот email-адрес к вашей учетной записи.",
+ "Your home server does not support device management.": "Ваш сервер не поддерживает управление устройствами.",
+ "(could not connect media)": "(сбой подключения)",
"(no answer)": "(нет ответа)",
- "(unknown failure: %(reason)s)": "(неизвестная ошибка: %(reason)s",
+ "(unknown failure: %(reason)s)": "(неизвестная ошибка: %(reason)s)",
"Disable Peer-to-Peer for 1:1 calls": "Отключить Peer-to-Peer для 1:1 звонков",
- "Not a valid Riot keyfile": "Недействительный файл ключа Riot",
+ "Not a valid Riot keyfile": "Недействительный файл ключей Riot",
"Your browser does not support the required cryptography extensions": "Ваш браузер не поддерживает требуемые криптографические расширения",
- "Authentication check failed: incorrect password?": "Ошибка аутентификации: неправильный пароль?",
- "Do you want to set an email address?": "Хотите указать адрес email?",
+ "Authentication check failed: incorrect password?": "Ошибка аутентификации: возможно, неправильный пароль?",
+ "Do you want to set an email address?": "Хотите указать email-адрес?",
"This will allow you to reset your password and receive notifications.": "Это позволит при необходимости сбросить пароль и получать уведомления.",
- "Press to start a chat with someone": "Нажмите для начала чата с кем-либо",
- "You're not in any rooms yet! Press to make a room or to browse the directory": "Вы еще не вошли ни в одну из комнат! Нажмите , чтобы создать комнату или для просмотра каталога",
+ "Press to start a chat with someone": "Нажмите , чтобы начать разговор с кем-либо",
+ "You're not in any rooms yet! Press to make a room or to browse the directory": "Вы еще не вошли ни в одну из комнат! Нажмите , чтобы создать комнату, или , чтобы посмотреть каталог комнат",
"To return to your account in future you need to set a password": "Чтобы вернуться к учетной записи в будущем, необходимо задать пароль",
"Skip": "Пропустить",
"Start verification": "Начать проверку",
@@ -648,22 +648,22 @@
"Changes colour scheme of current room": "Изменяет цветовую схему текущей комнаты",
"Delete widget": "Удалить виджет",
"Define the power level of a user": "Определить уровень доступа пользователя",
- "Do you want to load widget from URL:": "Загрузить виджет из URL-адреса:",
- "Edit": "Редактировать",
- "Enable automatic language detection for syntax highlighting": "Включить автоматическое определение языка для подсветки синтаксиса",
- "Hide join/leave messages (invites/kicks/bans unaffected)": "Скрыть сообщения о входе/выходе (не применяется к приглашениям/выкидываниям/банам)",
+ "Do you want to load widget from URL:": "Вы собираетесь загрузить виджет по URL-адресу:",
+ "Edit": "Изменить",
+ "Enable automatic language detection for syntax highlighting": "Автоматически определять язык подсветки синтаксиса",
+ "Hide join/leave messages (invites/kicks/bans unaffected)": "Скрывать уведомления о входе/выходе из комнаты (не применяется к приглашениям/выкидываниям/банам)",
"Integrations Error": "Ошибка интеграции",
- "AM": "AM",
- "PM": "PM",
+ "AM": "ДП",
+ "PM": "ПП",
"NOTE: Apps are not end-to-end encrypted": "ПРИМЕЧАНИЕ: приложения не защищены сквозным шифрованием",
- "Revoke widget access": "Отозвать доступ к виджетам",
+ "Revoke widget access": "Отключить виджет",
"Sets the room topic": "Задать тему комнаты",
"The maximum permitted number of widgets have already been added to this room.": "Максимально допустимое количество виджетов уже добавлено в эту комнату.",
"To get started, please pick a username!": "Чтобы начать, выберите имя пользователя!",
"Unable to create widget.": "Не удалось создать виджет.",
"Unbans user with given id": "Разбанить пользователя с заданным ID",
- "You are not in this room.": "Вас нет в этой комнате.",
- "You do not have permission to do that in this room.": "У вас нет разрешения на это в этой комнате.",
+ "You are not in this room.": "Вас сейчас нет в этой комнате.",
+ "You do not have permission to do that in this room.": "У вас нет разрешения на это в данной комнате.",
"Verifies a user, device, and pubkey tuple": "Проверка пользователя, устройства и открытого ключа",
"Autocomplete Delay (ms):": "Задержка автозаполнения (мс):",
"Loading device info...": "Загрузка информации об устройстве...",
@@ -671,14 +671,14 @@
"Create": "Создать",
"Featured Rooms:": "Рекомендуемые комнаты:",
"Featured Users:": "Избранные пользователи:",
- "Automatically replace plain text Emoji": "Автоматически заменять обычный текст на Emoji",
+ "Automatically replace plain text Emoji": "Автоматически заменять текстовые смайлики на Emoji",
"Failed to upload image": "Не удалось загрузить изображение",
- "Hide avatars in user and room mentions": "Скрыть аватары в упоминаниях пользователей и комнат",
- "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s виджет, добавленный %(senderName)s",
- "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s виджет, удаленный %(senderName)s",
- "Robot check is currently unavailable on desktop - please use a web browser": "Проверка робота в настоящее время недоступна на компьютере - пожалуйста, используйте браузер",
- "Publish this room to the public in %(domain)s's room directory?": "Опубликовать эту комнату для пользователей в %(domain)s каталоге комнат?",
- "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s виджет, измененный %(senderName)s",
+ "Hide avatars in user and room mentions": "Скрывать аватары в упоминаниях пользователей и комнат",
+ "%(widgetName)s widget added by %(senderName)s": "Виджет %(widgetName)s был добавлен %(senderName)s",
+ "%(widgetName)s widget removed by %(senderName)s": "Виджет %(widgetName)s был удален %(senderName)s",
+ "Robot check is currently unavailable on desktop - please use a web browser": "CAPTCHA-тест в настоящее время недоступен в Desktop-клиенте - пожалуйста, используйте браузер",
+ "Publish this room to the public in %(domain)s's room directory?": "Опубликовать эту комнату в каталоге комнат %(domain)s?",
+ "%(widgetName)s widget modified by %(senderName)s": "Виджет %(widgetName)s был изменен %(senderName)s",
"Copied!": "Скопировано!",
"Failed to copy": "Не удалось скопировать",
"Advanced options": "Дополнительные параметры",
@@ -687,40 +687,40 @@
"Ignored Users": "Игнорируемые пользователи",
"Ignore": "Игнорировать",
"Unignore": "Перестать игнорировать",
- "User Options": "Параметры пользователя",
+ "User Options": "Действия",
"You are now ignoring %(userId)s": "Теперь вы игнорируете %(userId)s",
"You are no longer ignoring %(userId)s": "Вы больше не игнорируете %(userId)s",
- "Unignored user": "Неигнорируемый пользователь",
- "Ignored user": "Игнорируемый пользователь",
+ "Unignored user": "Пользователь убран из списка игнорирования",
+ "Ignored user": "Пользователь добавлен в список игнорирования",
"Stops ignoring a user, showing their messages going forward": "Прекращает игнорирование пользователя, показывая их будущие сообщения",
"Ignores a user, hiding their messages from you": "Игнорирует пользователя, скрывая сообщения от вас",
- "Disable Emoji suggestions while typing": "Отключить предложения Emoji при наборе текста",
- "Banned by %(displayName)s": "Запрещено %(displayName)s",
+ "Disable Emoji suggestions while typing": "Не предлагать Emoji при наборе текста",
+ "Banned by %(displayName)s": "Заблокирован(а) %(displayName)s",
"Message removed by %(userId)s": "Сообщение удалено %(userId)s",
"To send messages, you must be a": "Для отправки сообщений необходимо быть",
- "To invite users into the room, you must be a": "Чтобы пригласить пользователей в комнату, необходимо быть",
- "To configure the room, you must be a": "Чтобы настроить комнату, необходимо быть",
- "To kick users, you must be a": "Чтобы выкидывать пользователей, необходимо быть",
- "To ban users, you must be a": "Чтобы банить пользователей, необходимо быть",
- "To remove other users' messages, you must be a": "Чтобы удалять сообщения других пользователей, необходимо быть",
+ "To invite users into the room, you must be a": "Чтобы приглашать участников в комнату, необходимо быть",
+ "To configure the room, you must be a": "Чтобы настраивать комнату, необходимо быть",
+ "To kick users, you must be a": "Чтобы выгонять участников, необходимо быть",
+ "To ban users, you must be a": "Чтобы блокировать участников, необходимо быть",
+ "To remove other users' messages, you must be a": "Чтобы удалять сообщения других участников, необходимо быть",
"To send events of type , you must be a": "Для отправки событий типа , необходимо быть",
"To change the room's avatar, you must be a": "Чтобы изменить аватар комнаты, необходимо быть",
- "To change the room's name, you must be a": "Чтобы изменить имя комнаты, необходимо быть",
- "To change the room's main address, you must be a": "Чтобы изменить основной адрес комнаты, необходимо быть",
- "To change the room's history visibility, you must be a": "Чтобы изменить видимость истории комнаты, необходимо быть",
- "To change the permissions in the room, you must be a": "Чтобы изменить разрешения в комнате, необходимо быть",
- "To change the topic, you must be a": "Чтобы изменить тему, необходимо быть",
- "To modify widgets in the room, you must be a": "Чтобы изменить виджеты в комнате, необходимо быть",
+ "To change the room's name, you must be a": "Чтобы переименовывать комнату, необходимо быть",
+ "To change the room's main address, you must be a": "Чтобы изменять основной адрес комнаты, необходимо быть",
+ "To change the room's history visibility, you must be a": "Чтобы изменять настройки доступа к истории комнаты, необходимо быть",
+ "To change the permissions in the room, you must be a": "Чтобы изменять разрешения в комнате, необходимо быть",
+ "To change the topic, you must be a": "Чтобы изменять тему комнаты, необходимо быть",
+ "To modify widgets in the room, you must be a": "Чтобы изменять виджеты в комнате, необходимо быть",
"Description": "Описание",
- "Name or matrix ID": "Имя или matrix ID",
+ "Name or matrix ID": "Имя или идентификатор Matrix",
"Unable to accept invite": "Невозможно принять приглашение",
"Leave": "Покинуть",
- "Failed to invite the following users to %(groupId)s:": "Не удалось пригласить следующих пользователей в %(groupId)s:",
- "Failed to remove '%(roomName)s' from %(groupId)s": "Не удалось удалить '%(roomName)s' из %(groupId)s",
- "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Вы действительно хотите удалить '%(roomName)s' из %(groupId)s?",
- "Jump to read receipt": "Перейти к подтверждению о прочтении",
- "Disable big emoji in chat": "Отключить большие emoji в чате",
- "Message Pinning": "Закрепление сообщений",
+ "Failed to invite the following users to %(groupId)s:": "Не удалось пригласить этих пользователей в %(groupId)s:",
+ "Failed to remove '%(roomName)s' from %(groupId)s": "Не удалось убрать '%(roomName)s' из %(groupId)s",
+ "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Вы действительно хотите убрать '%(roomName)s' из %(groupId)s?",
+ "Jump to read receipt": "Перейти к последнему прочтенному им сообщению",
+ "Disable big emoji in chat": "Отключить большие Emoji в чате",
+ "Message Pinning": "Закрепленные сообщения",
"Remove avatar": "Удалить аватар",
"Failed to invite users to %(groupId)s": "Не удалось пригласить пользователей в %(groupId)s",
"Unable to reject invite": "Невозможно отклонить приглашение",
@@ -731,9 +731,9 @@
"Add to summary": "Добавить в сводку",
"Failed to add the following users to the summary of %(groupId)s:": "Не удалось добавить следующих пользователей в сводку %(groupId)s:",
"Which rooms would you like to add to this summary?": "Какие комнаты вы хотите добавить в эту сводку?",
- "Room name or alias": "Название комнаты или псевдоним",
+ "Room name or alias": "Название или идентификатор комнаты",
"Pinned Messages": "Закрепленные сообщения",
- "%(senderName)s changed the pinned messages for the room.": "%(senderName)s изменил закрепленные сообщения для этой комнаты.",
+ "%(senderName)s changed the pinned messages for the room.": "%(senderName)s изменил(а) закрепленные в этой комнате сообщения.",
"Failed to add the following rooms to the summary of %(groupId)s:": "Не удалось добавить следующие комнаты в сводку %(groupId)s:",
"Failed to remove the room from the summary of %(groupId)s": "Не удалось удалить комнату из сводки %(groupId)s",
"The room '%(roomName)s' could not be removed from the summary.": "Комнату '%(roomName)s' не удалось удалить из сводки.",
@@ -742,7 +742,7 @@
"Light theme": "Светлая тема",
"Dark theme": "Темная тема",
"Unknown": "Неизвестно",
- "Failed to add the following rooms to %(groupId)s:": "Не удалось добавить следующие комнаты в %(groupId)s:",
+ "Failed to add the following rooms to %(groupId)s:": "Не удалось добавить эти комнаты в %(groupId)s:",
"Matrix ID": "Matrix ID",
"Matrix Room ID": "Matrix ID комнаты",
"email address": "адрес email",
@@ -753,30 +753,30 @@
"No pinned messages.": "Нет прикрепленных сообщений.",
"Loading...": "Загрузка...",
"Unnamed room": "Комната без названия",
- "World readable": "Доступно всем",
- "Guests can join": "Гости могут присоединиться",
- "No rooms to show": "Нет комнат для отображения",
+ "World readable": "Открыта для чтения",
+ "Guests can join": "Открыта для участия",
+ "No rooms to show": "Нет комнат, нечего показывать",
"Long Description (HTML)": "Длинное описание (HTML)",
"Community Settings": "Настройки сообщества",
"Invite to Community": "Пригласить в сообщество",
"Add to community": "Добавить в сообщество",
- "Add rooms to the community": "Добавление комнат в сообщество",
+ "Add rooms to the community": "Добавить комнаты в сообщество",
"Which rooms would you like to add to this community?": "Какие комнаты вы хотите добавить в это сообщество?",
"Who would you like to add to this community?": "Кого бы вы хотели добавить в это сообщество?",
- "Invite new community members": "Пригласить новых членов сообщества",
- "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Предупреждение: любой, кого вы добавляете в сообщество, будет виден всем, кто знает ID сообщества",
+ "Invite new community members": "Пригласить в сообщество новых участников",
+ "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Предупреждение: любой, кого вы добавляете в сообщество, будет виден всем, кто знает ID этого сообщества",
"Add rooms to this community": "Добавить комнаты в это сообщество",
"Failed to invite users to community": "Не удалось пригласить пользователей в сообщество",
"Communities": "Сообщества",
- "Invalid community ID": "Недопустимый ID сообщества",
- "'%(groupId)s' is not a valid community ID": "'%(groupId)s' - недействительный ID сообщества",
- "New community ID (e.g. +foo:%(localDomain)s)": "Новый ID сообщества (напр. +foo:%(localDomain)s)",
- "Remove from community": "Удалить из сообщества",
- "Failed to remove user from community": "Не удалось удалить пользователя из сообщества",
- "Filter community members": "Фильтр участников сообщества",
- "Filter community rooms": "Фильтр комнат сообщества",
- "Failed to remove room from community": "Не удалось удалить комнату из сообщества",
- "Removing a room from the community will also remove it from the community page.": "Удаление комнаты из сообщества также удалит ее со страницы сообщества.",
+ "Invalid community ID": "Недопустимый идентификатор сообщества",
+ "'%(groupId)s' is not a valid community ID": "Идентификатор сообщества '%(groupId)s' недопустим",
+ "New community ID (e.g. +foo:%(localDomain)s)": "Новый идентификатор сообщества (напр. +чтонибудь:%(localDomain)s)",
+ "Remove from community": "Исключить из сообщества",
+ "Failed to remove user from community": "Не удалось исключить участника из сообщества",
+ "Filter community members": "Поиск по участникам сообщества",
+ "Filter community rooms": "Поиск по комнатам сообщества",
+ "Failed to remove room from community": "Не удалось убрать комнату из сообщества",
+ "Removing a room from the community will also remove it from the community page.": "Исключение комнаты из сообщества также уберет ее со страницы сообщества.",
"Create Community": "Создать сообщество",
"Community Name": "Имя сообщества",
"Community ID": "ID сообщества",
@@ -792,42 +792,42 @@
"This Home server does not support communities": "Этот домашний сервер не поддерживает сообщества",
"Failed to load %(groupId)s": "Ошибка загрузки %(groupId)s",
"Your Communities": "Ваши сообщества",
- "You're not currently a member of any communities.": "В настоящее время вы не являетесь членом каких-либо сообществ.",
+ "You're not currently a member of any communities.": "Вы не состоите в каких-либо сообществах.",
"Error whilst fetching joined communities": "Ошибка при загрузке сообществ",
"Create a new community": "Создать новое сообщество",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Создайте сообщество для объединения пользователей и комнат! Создайте собственную домашнюю страницу, чтобы выделить свое пространство во вселенной Matrix.",
"Join an existing community": "Присоединиться к существующему сообществу",
"To join an existing community you'll have to know its community identifier; this will look something like +example:matrix.org.": "Чтобы присоединиться к существующему сообществу, вам нужно знать его ID; это будет выглядеть примерно так+primer:matrix.org.",
"Something went wrong whilst creating your community": "При создании сообщества что-то пошло не так",
- "%(names)s and %(count)s others are typing|other": "%(names)s и %(count)s другие печатают",
+ "%(names)s and %(count)s others are typing|other": "%(names)s и еще %(count)s печатают",
"And %(count)s more...|other": "И более %(count)s...",
"Delete Widget": "Удалить виджет",
- "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Удаление виджета удаляет его для всех пользователей этой комнаты. Вы действительно хотите удалить этот виджет?",
+ "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Удаление виджета действует для всех участников этой комнаты. Вы действительно хотите удалить этот виджет?",
"Message removed": "Сообщение удалено",
- "Mirror local video feed": "Зеркальное отображение видео",
+ "Mirror local video feed": "Зеркально отражать видео со своей камеры",
"Invite": "Пригласить",
- "Mention": "Упоминание",
+ "Mention": "Упомянуть",
"Failed to withdraw invitation": "Не удалось отозвать приглашение",
"Community IDs may only contain characters a-z, 0-9, or '=_-./'": "ID сообществ могут содержать только символы a-z, 0-9, или '=_-./'",
- "%(names)s and %(count)s others are typing|one": "%(names)s и еще кто-то печатает",
+ "%(names)s and %(count)s others are typing|one": "%(names)s и еще один собеседник печатают",
"%(senderName)s sent an image": "%(senderName)s отправил(а) изображение",
"%(senderName)s sent a video": "%(senderName)s отправил(а) видео",
- "%(senderName)s uploaded a file": "%(senderName)s загрузил(а) файл",
- "Disinvite this user?": "Отменить приглашение этого пользователя?",
+ "%(senderName)s uploaded a file": "%(senderName)s отправил(а) файл",
+ "Disinvite this user?": "Отозвать приглашение этого пользователя?",
"Kick this user?": "Выгнать этого пользователя?",
"Unban this user?": "Разблокировать этого пользователя?",
"Ban this user?": "Заблокировать этого пользователя?",
"Drop here to favourite": "Перетащите сюда для добавления в избранные",
"You have been kicked from this room by %(userName)s.": "%(userName)s выгнал(а) вас из этой комнаты.",
"You have been banned from this room by %(userName)s.": "%(userName)s заблокировал(а) вас в этой комнате.",
- "You are trying to access a room.": "Вы пытаетесь получить доступ к комнате.",
+ "You are trying to access a room.": "Вы пытаетесь войти в комнату.",
"Members only (since the point in time of selecting this option)": "Только участники (с момента выбора этого параметра)",
"Members only (since they were invited)": "Только участники (с момента их приглашения)",
- "Members only (since they joined)": "Только участники (с момента их присоединения)",
+ "Members only (since they joined)": "Только участники (с момента их входа)",
"An email has been sent to %(emailAddress)s": "Письмо было отправлено на %(emailAddress)s",
"A text message has been sent to %(msisdn)s": "Текстовое сообщение отправлено на %(msisdn)s",
- "Disinvite this user from community?": "Отозвать приглашение этого пользователя в сообщество?",
- "Remove this user from community?": "Удалить этого пользователя из сообщества?",
+ "Disinvite this user from community?": "Отозвать приглашение в сообщество этого участника?",
+ "Remove this user from community?": "Исключить этого участника из сообщества?",
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s присоединились %(count)s раз",
"%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s присоединились",
@@ -873,30 +873,30 @@
"%(items)s and %(count)s others|one": "%(items)s и один другой",
"An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Сообщение отправлено на %(emailAddress)s. После перехода по ссылке в отправленном вам письме, щелкните ниже.",
"Room Notification": "Уведомления комнаты",
- "Drop here to tag direct chat": "Перетащите сюда, чтобы отметить как прямой чат",
- "Drop here to restore": "Перетащиет сюда для восстановления",
- "Drop here to demote": "Перетащите сюда для понижения",
+ "Drop here to tag direct chat": "Перетащите сюда, чтобы пометить как личный чат",
+ "Drop here to restore": "Перетащиет сюда, чтобы вернуть",
+ "Drop here to demote": "Перетащите сюда, чтобы понизить",
"Community Invites": "Приглашения в сообщества",
"Notify the whole room": "Уведомить всю комнату",
"These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Эти комнаты отображаются для участников сообщества на странице сообщества. Участники сообщества могут присоединиться к комнатам, щелкнув на них.",
- "Show these rooms to non-members on the community page and room list?": "Следует ли показывать эти комнаты посторонним на странице сообщества и в комнате?",
+ "Show these rooms to non-members on the community page and room list?": "Следует ли показывать эти комнаты посторонним на странице сообщества и в списке комнат?",
"Sign in to get started": "Войдите, чтобы начать",
"Visibility in Room List": "Видимость в списке комнат",
- "Visible to everyone": "Видимый для всех",
+ "Visible to everyone": "Всем",
"Only visible to community members": "Только участникам сообщества",
- "Hide avatar changes": "Скрыть изменения аватара",
- "Hide display name changes": "Скрыть изменения отображаемого имени",
- "Enable inline URL previews by default": "Включить просмотр URL-адресов по умолчанию",
- "Enable URL previews for this room (only affects you)": "Включить просмотр URL-адресов для этой комнаты (влияет только на вас)",
- "Enable URL previews by default for participants in this room": "Включить просмотр URL-адресов по умолчанию для участников этой комнаты",
+ "Hide avatar changes": "Скрывать уведомления об изменении аватаров",
+ "Hide display name changes": "Скрывать уведомления об изменениях имен",
+ "Enable inline URL previews by default": "Включить предпросмотр ссылок по умолчанию",
+ "Enable URL previews for this room (only affects you)": "Включить предпросмотр ссылок в этой комнате (влияет только на вас)",
+ "Enable URL previews by default for participants in this room": "Включить предпросмотр ссылок для участников этой комнаты по умолчанию",
"Status.im theme": "Тема status.im",
- "Restricted": "Ограничен",
+ "Restricted": "Ограниченный пользователь",
"Username on %(hs)s": "Имя пользователя на %(hs)s",
- "The visibility of '%(roomName)s' in %(groupId)s could not be updated.": "Видимость '%(roomName)s' в %(groupId)s не удалось обновить.",
+ "The visibility of '%(roomName)s' in %(groupId)s could not be updated.": "Не удалось изменить видимость '%(roomName)s' в %(groupId)s.",
"%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s отклонили приглашения %(count)s раз",
"%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)s отклонили приглашения",
- "URL previews are enabled by default for participants in this room.": "Предварительный просмотр URL-адресов для участников этой комнаты по умолчанию.",
- "URL previews are disabled by default for participants in this room.": "Предварительный просмотр URL-адресов для участников этой комнаты по умолчанию.",
+ "URL previews are enabled by default for participants in this room.": "Предпросмотр ссылок по умолчанию включен для участников этой комнаты.",
+ "URL previews are disabled by default for participants in this room.": "Предпросмотр ссылок по умолчанию выключен для участников этой комнаты.",
"%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)sотклонил их приглашение %(count)s раз",
"%(oneUser)srejected their invitation %(count)s times|one": "%(oneUser)sотклонил приглашение",
"%(severalUsers)shad their invitations withdrawn %(count)s times|other": "%(severalUsers)sотозвали приглашения %(count)s раз",
@@ -906,28 +906,28 @@
"Please note you are logging into the %(hs)s server, not matrix.org.": "Обратите внимание, что вы заходите на сервер %(hs)s, а не на matrix.org.",
"Custom of %(powerLevel)s": "Пользовательский уровень %(powerLevel)s",
"
HTML for your community's page
\n
\n Use the long description to introduce new members to the community, or distribute\n some important links\n
\n
\n You can even use 'img' tags\n
\n": "
HTML для страницы вашего сообщества
\n
\n Используйте подробное описание для представления вашего сообщества новым участникам, или\n поделитесь чем-нибудь важным, например ссылками\n
\n
\n Также вы можете использовать теги 'img'\n
\n",
- "%(duration)ss": "%(duration)sсек",
- "%(duration)sm": "%(duration)sмин",
- "%(duration)sh": "%(duration)sчас",
- "%(duration)sd": "%(duration)sдн",
+ "%(duration)ss": "%(duration)s сек",
+ "%(duration)sm": "%(duration)s мин",
+ "%(duration)sh": "%(duration)s ч",
+ "%(duration)sd": "%(duration)s дн",
"Your community hasn't got a Long Description, a HTML page to show to community members. Click here to open settings and give it one!": "У вашего сообщества нет подробного описания HTML-страницы для показа участникам. Щелкните здесь, чтобы открыть параметры и добавить его!",
"Online for %(duration)s": "В сети %(duration)s",
"Offline for %(duration)s": "Не в сети %(duration)s",
"Idle for %(duration)s": "Неактивен %(duration)s",
"Unknown for %(duration)s": "Неизвестно %(duration)s",
"There's no one else here! Would you like to invite others or stop warning about the empty room?": "Здесь никого нет! Хотите пригласить кого-нибудь или выключить предупреждение о пустой комнате?",
- "Something went wrong when trying to get your communities.": "Что-то пошло не так, во время отображения ваших сообществ.",
+ "Something went wrong when trying to get your communities.": "Что-то пошло не так при попытке получить список ваших сообществ.",
"Tag Panel": "Панель тегов",
"Delete %(count)s devices|other": "Удалить %(count)s устройств",
"Delete %(count)s devices|one": "Удалить устройство",
"Select devices": "Выбрать устройства",
"This homeserver doesn't offer any login flows which are supported by this client.": "Этот домашний сервер не поддерживает метод входа, поддерживаемый клиентом.",
"Call Failed": "Звонок не удался",
- "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "В этой комнате есть неизвестные устройства: если вы продолжите без их проверки, имейте в виду, что кто-то посторонний может вас прослушать.",
- "Review Devices": "Обзор устройств",
+ "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "В этой комнате есть неизвестные устройства: если вы решите их не проверять, имейте в виду, что кто-то, возможно, вас прослушивает.",
+ "Review Devices": "Проверка устройств",
"Call Anyway": "Позвонить в любом случае",
"Answer Anyway": "Ответить в любом случае",
- "Call": "Вызов",
+ "Call": "Позвонить",
"Answer": "Ответить",
"Send": "Отправить",
"Addresses": "Адреса",
@@ -935,62 +935,61 @@
"expand": "развернуть",
"Old cryptography data detected": "Обнаружены старые криптографические данные",
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Обнаружены данные из более старой версии Riot. Это приведет к сбою криптографии в более ранней версии. В этой версии не могут быть расшифрованы сообщения, которые использовались недавно при использовании старой версии. Это также может привести к сбою обмена сообщениями с этой версией. Если возникают неполадки, выйдите и снова войдите в систему. Чтобы сохранить журнал сообщений, экспортируйте и повторно импортируйте ключи.",
- "Warning": "Предупреждение",
- "Showing flair for these communities:": "Показ таланта в следующих сообществах:",
- "This room is not showing flair for any communities": "В этой комнате не отображается талант для любых сообществ",
- "Flair will appear if enabled in room settings": "Талант появится, если он активирован в настройках комнаты",
- "Flair": "Талант",
- "Flair will not appear": "Талант не отображается",
- "Display your community flair in rooms configured to show it.": "Покажите свой талант в комнатах, которых это разрешено.",
- "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Вы не сможете отменить это изменение после понижения себя, в случае если вы являетесь последним привилегированным пользователем в этой комнате.",
+ "Warning": "Внимание",
+ "Showing flair for these communities:": "Комната принадлежит следующим сообществам:",
+ "This room is not showing flair for any communities": "Эта комната не принадлежит каким-либо сообществам",
+ "Flair will appear if enabled in room settings": "Значок появится, если он включен в настройках комнаты",
+ "Flair": "Значки сообществ",
+ "Flair will not appear": "Значок не будет отображаться",
+ "Display your community flair in rooms configured to show it.": "Вы можете показывать значки своих сообществ в комнатах, в которых это разрешено.",
+ "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "После понижения своих привилегий вы не сможете это отменить. Если вы являетесь последним привилегированным пользователем в этой комнате, выдать права кому-либо заново будет невозможно.",
"%(count)s of your messages have not been sent.|one": "Ваше сообщение не было отправлено.",
"%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|other": "Отправить все или отменить все сейчас. Можно также выбрать отдельные сообщения для отправки или отмены.",
"%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|one": "Отправить или отменить сообщение сейчас.",
- "Message Replies": "Ответы на сообщения",
+ "Message Replies": "Сообщения-ответы",
"Send an encrypted reply…": "Отправить зашифрованный ответ…",
- "Send a reply (unencrypted)…": "Отправить ответ (незашифрованный)…",
+ "Send a reply (unencrypted)…": "Отправить ответ (нешифрованный)…",
"Send an encrypted message…": "Отправить зашифрованное сообщение…",
- "Send a message (unencrypted)…": "Отправить сообщение (незашифрованное)…",
+ "Send a message (unencrypted)…": "Отправить сообщение (нешифрованное)…",
"Replying": "Отвечает",
"Minimize apps": "Свернуть приложения",
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Конфиденциальность важна для нас, поэтому мы не собираем никаких личных или идентифицируемых данных для нашей аналитики.",
"Learn more about how we use analytics.": "Подробнее о том, как мы используем аналитику.",
- "The information being sent to us to help make Riot.im better includes:": "Информация направляемая нам, чтобы помочь сделать Riot.im лучше включает в себя:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Мы также записываем каждую страницу, которую вы используете в приложении (в данный момент ), ваш пользовательский агент () и разрешение экрана вашего устройства ().",
- "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Если на этой странице содержатся идентифицируемые сведения, например номер, идентификатор пользователя или группы, эти данные удаляются перед отправкой на сервер.",
+ "The information being sent to us to help make Riot.im better includes:": "Информация, отправляемая нам, чтобы помочь нам сделать Riot.im лучше, включает в себя:",
+ "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Если на этой странице встречаются сведения личного характера, например имя комнаты, имя пользователя или группы, они удаляются перед отправкой на сервер.",
"The platform you're on": "Используемая платформа",
"The version of Riot.im": "Версия Riot.im",
- "Whether or not you're logged in (we don't record your user name)": "Независимо от того, вошли вы или нет (мы не записываем ваше имя пользователя)",
- "Your language of choice": "Выбранный вами язык",
- "Your homeserver's URL": "URL-адрес домашнего сервера",
- "Your identity server's URL": "URL-адрес вашего идентификационного сервера",
- "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
- "Which officially provided instance you are using, if any": "Какую официально выпущенную версию вы используете",
- "Whether or not you're using the Richtext mode of the Rich Text Editor": "Независимо от того, используете ли вы режим Richtext в редакторе Rich Text Editor",
+ "Whether or not you're logged in (we don't record your user name)": "Вошли вы в систему или нет (мы не храним ваше имя пользователя)",
+ "Your language of choice": "Выбранный язык",
+ "Your homeserver's URL": "URL-адрес сервера",
+ "Your identity server's URL": "URL-адрес сервера идентификации",
+ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s",
+ "Which officially provided instance you are using, if any": "Каким официально поддерживаемым клиентом вы пользуетесь (если пользуетесь)",
+ "Whether or not you're using the Richtext mode of the Rich Text Editor": "Используете ли вы режим Richtext в редакторе Rich Text Editor",
"This room is not public. You will not be able to rejoin without an invite.": "Эта комната не является публичной. Вы не сможете войти без приглашения.",
"Show devices, send anyway or cancel.": "Показать устройства, отправить в любом случае или отменить.",
"Community IDs cannot not be empty.": "ID сообществ не могут быть пустыми.",
"In reply to": "В ответ на",
- "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s изменил отображаемое имя на %(displayName)s.",
+ "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s изменил(а) отображаемое имя на %(displayName)s.",
"Failed to set direct chat tag": "Не удалось установить тег прямого чата",
"Failed to remove tag %(tagName)s from room": "Не удалось удалить тег %(tagName)s из комнаты",
"Failed to add tag %(tagName)s to room": "Не удалось добавить тег %(tagName)s в комнату",
"Clear filter": "Очистить фильтр",
"To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Чтобы настроить фильтр, перетащите аватар сообщества на панель фильтров в левой части экрана. Вы можете нажать на аватар в панели фильтров в любое время, чтобы увидеть только комнаты и людей, связанных с этим сообществом.",
"Did you know: you can use communities to filter your Riot.im experience!": "Знаете ли вы: вы можете использовать сообщества, чтобы фильтровать в Riot.im!",
- "Disable Community Filter Panel": "Отключить панель фильтра сообщества",
- "If your other devices do not have the key for this message you will not be able to decrypt them.": "Если у других устройств нет ключа для этого сообщения, вы не сможете его расшифровать.",
+ "Disable Community Filter Panel": "Отключить панель сообществ",
+ "If your other devices do not have the key for this message you will not be able to decrypt them.": "Если на других устройствах тоже нет ключа для этого сообщения, вы не сможете его расшифровать.",
"Key request sent.": "Запрос ключа отправлен.",
"Re-request encryption keys from your other devices.": "Повторно запросить ключи шифрования с других устройств.",
"%(user)s is a %(userRole)s": "%(user)s является %(userRole)s",
- "Your key share request has been sent - please check your other devices for key share requests.": "Ваш запрос на передачу ключей отправлен - пожалуйста, проверьте другие ваши устройства на запросы передачи ключей.",
- "Key share requests are sent to your other devices automatically. If you rejected or dismissed the key share request on your other devices, click here to request the keys for this session again.": "Запросы передачи ключей автоматически отправляются на другие устройства. Если вы отклонили или отменили запрос на передачу ключей на других устройствах, нажмите здесь, чтобы запросить ключи для этого сеанса повторно.",
+ "Your key share request has been sent - please check your other devices for key share requests.": "Запрос на передачу ключей отправлен — проверьте остальные устройства.",
+ "Key share requests are sent to your other devices automatically. If you rejected or dismissed the key share request on your other devices, click here to request the keys for this session again.": "Запросы на передачу ключей автоматически отправляются на другие устройства. Если вы отклонили или отменили запрос на другом устройстве, нажмите, чтобы запросить ключи повторно.",
"Code": "Код",
"Debug Logs Submission": "Отправка журналов отладки",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Если вы отправили ошибку через GitHub, журналы отладки могут помочь нам выявить проблему. Журналы отладки содержат данные об использовании приложения, включая ваше имя пользователя, идентификаторы или псевдонимы комнат или групп, которые вы посетили, а также имена других пользователей. Они не содержат сообщений.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Если вы отправили ошибку через GitHub, журналы отладки могут помочь нам выявить проблему. Журналы отладки содержат данные об использовании приложения, включая ваше имя пользователя, идентификаторы или псевдонимы комнат или групп, которые вы посетили, а также имена других пользователей. Они не содержат сообщений.",
"Submit debug logs": "Отправка журналов отладки",
"Opens the Developer Tools dialog": "Открывает Инструменты разработчика",
- "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Просмотрено %(displayName)s (%(userName)s) в %(dateTime)s",
+ "Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Прочитано %(displayName)s (%(userName)s) в %(dateTime)s",
"Unable to join community": "Не удалось присоединиться к сообществу",
"Unable to leave community": "Не удалось покинуть сообщество",
"Changes made to your community name and avatar might not be seen by other users for up to 30 minutes.": "Изменения имени и аватара, внесенные в ваше сообщество, могут не отображаться другим пользователям в течение 30 минут.",
@@ -998,11 +997,11 @@
"Leave this community": "Покинуть это сообщество",
"Who can join this community?": "Кто может присоединиться к этому сообществу?",
"Everyone": "Все",
- "Stickerpack": "Этикетки",
- "Sticker Messages": "Сообщения этикеткой",
- "Add a stickerpack": "Добавить этикетки",
- "Hide Stickers": "Скрыть этикетки",
- "Show Stickers": "Показать этикетки",
+ "Stickerpack": "Стикеры",
+ "Sticker Messages": "Стикеры",
+ "Add a stickerpack": "Добавить стикеры",
+ "Hide Stickers": "Скрыть стикеры",
+ "Show Stickers": "Показать стикеры",
"Fetching third party location failed": "Не удалось извлечь местоположение третьей стороны",
"A new version of Riot is available.": "Доступна новая версия Riot.",
"I understand the risks and wish to continue": "Я понимаю риски и желаю продолжить",
@@ -1011,51 +1010,51 @@
"All notifications are currently disabled for all targets.": "Все оповещения для всех устройств отключены.",
"Uploading report": "Отправка отчета",
"Sunday": "Воскресенье",
- "Notification targets": "Цели уведомления",
+ "Notification targets": "Устройства для уведомлений",
"Today": "Сегодня",
"Files": "Файлы",
- "You are not receiving desktop notifications": "Вы не получаете уведомления на рабочем столе",
+ "You are not receiving desktop notifications": "Вы не получаете системные уведомления",
"Friday": "Пятница",
- "Update": "Обновление",
- "What's New": "Что нового",
- "Add an email address above to configure email notifications": "Добавьте email адрес для оповещений",
+ "Update": "Обновить",
+ "What's New": "Что изменилось",
+ "Add an email address above to configure email notifications": "Добавьте email-адрес выше для настройки email-уведомлений",
"Expand panel": "Развернуть панель",
"On": "Включить",
"%(count)s Members|other": "%(count)s членов",
"Filter room names": "Фильтр по названию комнат",
"Changelog": "История изменений",
"Waiting for response from server": "Ожидание ответа от сервера",
- "Uploaded on %(date)s by %(user)s": "Отправлено %(date)s для %(user)s",
+ "Uploaded on %(date)s by %(user)s": "Загружено %(user)s в %(date)s",
"Send Custom Event": "Отправить индивидуальное мероприятие",
"Advanced notification settings": "Дополнительные параметры уведомлений",
"Failed to send logs: ": "Не удалось отправить журналы: ",
"delete the alias.": "удалить псевдоним.",
- "To return to your account in future you need to set a password": "Чтобы вернуться к учетной записи в будущем, необходимо задать пароль",
+ "To return to your account in future you need to set a password": "Чтобы вы могли вернуться в свою учетную запись в будущем, вам необходимо задать пароль",
"Forget": "Забыть",
"#example": "#пример",
"Hide panel": "Скрыть панель",
- "You cannot delete this image. (%(code)s)": "Это изображение нельзя удалить. (%(code)s)",
+ "You cannot delete this image. (%(code)s)": "Вы не можете удалить это изображение. (%(code)s)",
"Cancel Sending": "Отменить отправку",
- "This Room": "Эта комната",
+ "This Room": "В этой комнате",
"The Home Server may be too old to support third party networks": "Домашний сервер может быть слишком старым для поддержки сетей сторонних производителей",
- "Noisy": "Со звуком",
+ "Noisy": "Вкл. (со звуком)",
"Room not found": "Комната не найдена",
- "Messages containing my display name": "Сообщения, содержащие мое имя",
- "Messages in one-to-one chats": "Сообщения в индивидуальных чатах",
+ "Messages containing my display name": "Сообщения, упоминающие мое имя",
+ "Messages in one-to-one chats": "Сообщения в 1:1 чатах",
"Unavailable": "Недоступен",
- "Error saving email notification preferences": "Ошибка при сохранении настроек уведомлений по email",
+ "Error saving email notification preferences": "Ошибка сохранения настроек email-уведомлений",
"View Decrypted Source": "Просмотр расшифрованного источника",
"Failed to update keywords": "Не удалось обновить ключевые слова",
"Notes:": "Заметки:",
"remove %(name)s from the directory.": "удалить %(name)s из каталога.",
- "Notifications on the following keywords follow rules which can’t be displayed here:": "Уведомления по следующим ключевым словам соответствуют правилам, которые нельзя отобразить здесь:",
+ "Notifications on the following keywords follow rules which can’t be displayed here:": "Уведомления по этим ключевым словам соответствуют правилам, которые нельзя отобразить здесь:",
"Safari and Opera work too.": "Safari и Opera работают тоже.",
"Please set a password!": "Пожалуйста, установите пароль!",
"You have successfully set a password!": "Вы успешно установили пароль!",
- "An error occurred whilst saving your email notification preferences.": "Возникла ошибка при сохранении настроек оповещения по email.",
+ "An error occurred whilst saving your email notification preferences.": "Возникла ошибка при сохранении настроек email-уведомлений.",
"Explore Room State": "Просмотр статуса комнаты",
"Source URL": "Исходный URL-адрес",
- "Messages sent by bot": "Сообщения, отправленные ботом",
+ "Messages sent by bot": "Сообщения от ботов",
"Filter results": "Фильтрация результатов",
"Members": "Участники",
"No update available.": "Нет доступных обновлений.",
@@ -1073,7 +1072,7 @@
"View Source": "Просмотр источника",
"Tuesday": "Вторник",
"Enter keywords separated by a comma:": "Введите ключевые слова, разделенные запятой:",
- "Search…": "Поиск.…",
+ "Search…": "Поиск…",
"You have successfully set a password and an email address!": "Вы успешно установили пароль и адрес email!",
"Remove %(name)s from the directory?": "Удалить %(name)s из каталога?",
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot использует многие передовые возможности браузера, некоторые из которых недоступны или являются экспериментальным в вашем текущем браузере.",
@@ -1090,8 +1089,8 @@
"Failed to set Direct Message status of room": "Не удалось установить статус прямого сообщения в комнате",
"Monday": "Понедельник",
"Remove from Directory": "Удалить из каталога",
- "Enable them now": "Включить сейчас",
- "Messages containing my user name": "Сообщение, содержащие мое имя пользователя",
+ "Enable them now": "Включить их сейчас",
+ "Messages containing my user name": "Сообщение, упоминающие мое имя пользователя",
"Toolbox": "Панель инструментов",
"Collecting logs": "Сбор журналов",
"more": "больше",
@@ -1103,37 +1102,37 @@
"Failed to get public room list": "Не удалось получить список общедоступных комнат",
"Send logs": "Отправка журналов",
"All messages": "Все сообщения",
- "Call invitation": "Пригласительный звонок",
- "Downloading update...": "Загрузка обновления...",
+ "Call invitation": "Приглашения на звонки",
+ "Downloading update...": "Загрузка обновления…",
"State Key": "Ключ состояния",
"Failed to send custom event.": "Не удалось отправить индивидуальное мероприятие.",
"What's new?": "Что нового?",
"Notify me for anything else": "Уведомлять во всех остальных случаях",
- "When I'm invited to a room": "Когда меня приглашают в комнату",
+ "When I'm invited to a room": "Приглашения в комнаты",
"Click here to create a GitHub issue.": "Нажмите здесь для создания запроса о проблеме на GitHub.",
- "Can't update user notification settings": "Не удается обновить пользовательские настройки оповещения",
- "Notify for all other messages/rooms": "Уведомлять обо всех других сообщениях/комнатах",
+ "Can't update user notification settings": "Не удалось обновить пользовательские настройки оповещения",
+ "Notify for all other messages/rooms": "Уведомлять обо всех остальных сообщениях и комнатах",
"Unable to look up room ID from server": "Не удалось найти ID комнаты на сервере",
"Couldn't find a matching Matrix room": "Не удалось найти подходящую комнату Matrix",
- "All Rooms": "Все комнаты",
+ "All Rooms": "Везде",
"You cannot delete this message. (%(code)s)": "Это сообщение нельзя удалить. (%(code)s)",
"Thursday": "Четверг",
"Forward Message": "Переслать сообщение",
"Logs sent": "Журналы отправлены",
"Back": "Назад",
"Reply": "Ответить",
- "Show message in desktop notification": "Показывать сообщение в уведомлении на рабочем столе",
+ "Show message in desktop notification": "Показывать текст сообщения в уведомлениях на рабочем столе",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Журналы отладки содержат данные об использовании приложения, включая ваше имя пользователя, идентификаторы или псевдонимы комнат или групп, которые вы посетили, а также имена других пользователей. Они не содержат сообщений.",
"Unhide Preview": "Показать предварительный просмотр",
"Unable to join network": "Не удается подключиться к сети",
- "You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "Возможно вы настроили их не в Riot, а в другом Matrix-клиенте. Настроить их в Riot не удастся, но они будут в нем применяться",
+ "You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "Возможно, вы настроили их не в Riot, а в другом Matrix-клиенте. Настроить их в Riot не удастся, но они будут продолжать работать и здесь",
"Sorry, your browser is not able to run Riot.": "К сожалению, ваш браузер не способен запустить Riot.",
- "Messages in group chats": "Сообщения в групповых чатах",
+ "Messages in group chats": "Сообщения в конференциях",
"Yesterday": "Вчера",
"Error encountered (%(errorDetail)s).": "Обнаружена ошибка (%(errorDetail)s).",
"Login": "Войти",
"Low Priority": "Низкий приоритет",
- "Unable to fetch notification target list": "Не удалось получить список целей уведомления",
+ "Unable to fetch notification target list": "Не удалось получить список устройств для уведомлений",
"Set Password": "Задать пароль",
"Enable audible notifications in web client": "Включить звуковые уведомления в веб-клиенте",
"Permalink": "Постоянная ссылка",
@@ -1144,7 +1143,7 @@
"You can now return to your account after signing out, and sign in on other devices.": "Теперь вы сможете вернуться к своей учетной записи после выхода из системы и войти на других устройствах.",
"Enable email notifications": "Включить уведомления по email",
"Event Type": "Тип мероприятия",
- "Download this file": "Скачать этот файл",
+ "Download this file": "Скачать файл",
"Pin Message": "Закрепить сообщение",
"Failed to change settings": "Не удалось изменить настройки",
"View Community": "Просмотр сообщества",
@@ -1155,6 +1154,9 @@
"Quote": "Цитата",
"Collapse panel": "Свернуть панель",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "В текущем браузере внешний вид приложения может быть полностью неверным, а некоторые или все функции могут не работать. Если вы хотите попробовать в любом случае, то можете продолжить, но с теми проблемами, с которыми вы можете столкнуться вам придется разбираться самостоятельно!",
- "Checking for an update...": "Проверка обновлений...",
- "There are advanced notifications which are not shown here": "Существуют дополнительные уведомления, которые не показаны здесь"
+ "Checking for an update...": "Проверка обновлений…",
+ "There are advanced notifications which are not shown here": "Существуют дополнительные уведомления, которые не показаны здесь",
+ "Missing roomId.": "Отсутствует идентификатор комнаты.",
+ "You don't currently have any stickerpacks enabled": "У вас нет стикеров",
+ "Picture": "Снимок"
}
diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json
index c915ae04b4..13cc6ac9aa 100644
--- a/src/i18n/strings/sk.json
+++ b/src/i18n/strings/sk.json
@@ -957,7 +957,6 @@
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Vaše súkromie je pre nás dôležité, preto nezhromažďujeme žiadne osobné údaje alebo údaje, na základe ktorých je možné vás identifikovať.",
"Learn more about how we use analytics.": "Zistite viac o tom, ako spracúvame analytické údaje.",
"The information being sent to us to help make Riot.im better includes:": "S cieľom vylepšovať aplikáciu Riot.im zbierame nasledujúce údaje:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Zaznamenávame tiež každú stránku aplikácie Riot.im, ktorú otvoríte (momentálne ), reťazec user agent () a rozlíšenie obrazovky vašeho zariadenia ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Ak sa na stránke vyskytujú identifikujúce údaje, akými sú napríklad názov miestnosti, ID používateľa, miestnosti alebo skupiny, tieto sú pred odoslaním na server odstránené.",
"The platform you're on": "Vami používaná platforma",
"The version of Riot.im": "Verzia Riot.im",
@@ -993,7 +992,7 @@
"To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Ak si chcete nastaviť filter, pretiahnite obrázok komunity na panel filtrovania úplne na ľavej strane obrazovky. Potom môžete kedykoľvek kliknúť na obrázok komunity na tomto panely a Riot.im vám bude zobrazovať len miestnosti a ľudí z komunity, na ktorej obrázok ste klikli.",
"Clear filter": "Zrušiť filter",
"Debug Logs Submission": "Odoslanie ladiacich záznamov",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "Ak ste nám poslali hlásenie o chybe cez Github, ladiace záznamy nám môžu pomôcť lepšie identifikovať chybu. Ladiace záznamy obsahujú údaje o používaní aplikácii, vrátane vašeho používateľského mena, názvy a aliasy miestností a komunít, ku ktorým ste sa pripojili a mená ostatných používateľov. Tieto záznamy neobsahujú samotný obsah vašich správ.",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Ak ste nám poslali hlásenie o chybe cez Github, ladiace záznamy nám môžu pomôcť lepšie identifikovať chybu. Ladiace záznamy obsahujú údaje o používaní aplikácii, vrátane vašeho používateľského mena, názvy a aliasy miestností a komunít, ku ktorým ste sa pripojili a mená ostatných používateľov. Tieto záznamy neobsahujú samotný obsah vašich správ.",
"Submit debug logs": "Odoslať ladiace záznamy",
"Opens the Developer Tools dialog": "Otvorí dialóg nástroje pre vývojárov",
"Stickerpack": "Balíček nálepiek",
diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json
index d36995bc5b..9dbb76396e 100644
--- a/src/i18n/strings/sq.json
+++ b/src/i18n/strings/sq.json
@@ -12,7 +12,6 @@
"Your identity server's URL": "URL-ja e server-it identiteti tëndë",
"Analytics": "Analiza",
"The information being sent to us to help make Riot.im better includes:": "Informacionet që dërgohen për t'i ndihmuar Riot.im-it të përmirësohet përmbajnë:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Ne gjithashtu inçizojmë çdo faqe që përdorë në aplikacion (në këtë çast ), agjentin e përdoruesit tëndë () dhe rezolucionin e pajisjes tënde ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Kur kjo faqe pëmban informacione që mund të të identifikojnë, sikur një dhomë, përdorues apo identifikatues grupi, këto të dhëna do të mënjanohen para se t‘i dërgohën një server-it.",
"Call Failed": "Thirrja nuk mundej të realizohet",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Pajisje të panjohura ndodhen në këtë dhomë: nësë vazhdon pa i vërtetuar, është e mundshme që dikush të jua përgjon thirrjen.",
diff --git a/src/i18n/strings/sr.json b/src/i18n/strings/sr.json
index 87d5ec782e..5571d98c87 100644
--- a/src/i18n/strings/sr.json
+++ b/src/i18n/strings/sr.json
@@ -773,7 +773,6 @@
"Your identity server's URL": "Адреса вашег идентитеског сервера",
"Analytics": "Аналитика",
"The information being sent to us to help make Riot.im better includes:": "У податке које нам шаљете зарад побољшавања Riot.im-а спадају:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "Такође бележимо сваку страницу коју користите у апликацији (тренутно ), ваш кориснички агент () и резолуцију вашег уређаја ().",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Ако страница садржи поверљиве податке (као што је назив собе, корисника или ИБ-ја групе), ти подаци се уклањају пре слања на сервер.",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "Корисник %(oldDisplayName)s је променио приказно име у %(displayName)s.",
"Failed to set direct chat tag": "Нисам успео да поставим ознаку директног ћаскања",
diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json
index fe8ef8c16f..37fb480d98 100644
--- a/src/i18n/strings/zh_Hans.json
+++ b/src/i18n/strings/zh_Hans.json
@@ -10,7 +10,7 @@
"Decryption error": "解密出错",
"Delete": "删除",
"Default": "默认",
- "Device ID": "设备识别码",
+ "Device ID": "设备 ID",
"Devices": "设备列表",
"Devices will not yet be able to decrypt history from before they joined the room": "新加入聊天室的设备不能解密加入之前的聊天记录",
"Direct chats": "私聊",
@@ -20,8 +20,8 @@
"Don't send typing notifications": "不要发送我的打字状态",
"Download %(text)s": "下载 %(text)s",
"Email": "电子邮箱",
- "Email address": "电子邮箱地址",
- "Email, name or matrix ID": "电子邮箱,姓名或者matrix ID",
+ "Email address": "邮箱地址",
+ "Email, name or matrix ID": "邮箱地址,名称或者Matrix ID",
"Emoji": "表情",
"Enable encryption": "启用加密",
"Encrypted messages will not be visible on clients that do not yet implement encryption": "不支持加密的客户端将看不到加密的消息",
@@ -29,7 +29,7 @@
"%(senderName)s ended the call.": "%(senderName)s 结束了通话。.",
"End-to-end encryption information": "端到端加密信息",
"End-to-end encryption is in beta and may not be reliable": "端到端加密现为 beta 版,不一定可靠",
- "Enter Code": "输入代码",
+ "Enter Code": "输入验证码",
"Error": "错误",
"Error decrypting attachment": "解密附件时出错",
"Event information": "事件信息",
@@ -39,8 +39,8 @@
"Failed to change password. Is your password correct?": "修改密码失败。确认原密码输入正确吗?",
"Failed to forget room %(errCode)s": "忘记聊天室失败,错误代码: %(errCode)s",
"Failed to join room": "无法加入聊天室",
- "Failed to kick": "踢人失败",
- "Failed to leave room": "无法离开聊天室",
+ "Failed to kick": "移除失败",
+ "Failed to leave room": "无法退出聊天室",
"Failed to load timeline position": "无法加载时间轴位置",
"Failed to lookup current room": "找不到当前聊天室",
"Failed to mute user": "禁言用户失败",
@@ -48,7 +48,7 @@
"Failed to reject invitation": "拒绝邀请失败",
"Failed to save settings": "保存设置失败",
"Failed to send email": "发送邮件失败",
- "Failed to send request.": "发送请求失败。",
+ "Failed to send request.": "请求发送失败。",
"Failed to set avatar.": "设置头像失败。.",
"Failed to set display name": "设置昵称失败",
"Failed to set up conference call": "无法启动群组通话",
@@ -104,7 +104,7 @@
"Server may be unavailable or overloaded": "服务器可能不可用或者超载",
"Server may be unavailable, overloaded, or search timed out :(": "服务器可能不可用、超载,或者搜索超时 :(",
"Server may be unavailable, overloaded, or the file too big": "服务器可能不可用、超载,或者文件过大",
- "Server may be unavailable, overloaded, or you hit a bug.": "服务器可能不可用、超载,或者你遇到了一个漏洞.",
+ "Server may be unavailable, overloaded, or you hit a bug.": "服务器可能不可用、超载,或者你遇到了一个 bug。",
"Server unavailable, overloaded, or something else went wrong.": "服务器可能不可用、超载,或者其他东西出错了.",
"Session ID": "会话 ID",
"%(senderName)s set a profile picture.": "%(senderName)s 设置了头像。.",
@@ -129,11 +129,11 @@
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "文件 '%(fileName)s' 超过了此主服务器的上传大小限制",
"The file '%(fileName)s' failed to upload": "文件 '%(fileName)s' 上传失败",
"Add email address": "添加邮件地址",
- "Add phone number": "添加电话号码",
+ "Add phone number": "添加手机号码",
"Advanced": "高级",
"Algorithm": "算法",
"Always show message timestamps": "总是显示消息时间戳",
- "%(names)s and %(lastPerson)s are typing": "%(names)s 和 %(lastPerson)s 正在打字",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s 和 %(lastPerson)s 正在输入",
"A new password must be entered.": "一个新的密码必须被输入。.",
"%(senderName)s answered the call.": "%(senderName)s 接了通话。.",
"An error has occurred.": "一个错误出现了。",
@@ -152,7 +152,7 @@
"%(targetName)s joined the room.": "%(targetName)s 已加入聊天室。",
"Jump to first unread message.": "跳到第一条未读消息。",
"%(senderName)s kicked %(targetName)s.": "%(senderName)s 把 %(targetName)s 踢出了聊天室。.",
- "Leave room": "离开聊天室",
+ "Leave room": "退出聊天室",
"Login as guest": "以游客的身份登录",
"New password": "新密码",
"Add a topic": "添加一个主题",
@@ -177,10 +177,10 @@
"Anyone who knows the room's link, apart from guests": "任何知道聊天室链接的人,游客除外",
"Anyone who knows the room's link, including guests": "任何知道聊天室链接的人,包括游客",
"Are you sure?": "你确定吗?",
- "Are you sure you want to leave the room '%(roomName)s'?": "你确定要离开聊天室 “%(roomName)s” 吗?",
+ "Are you sure you want to leave the room '%(roomName)s'?": "你确定要退出聊天室 “%(roomName)s” 吗?",
"Are you sure you want to reject the invitation?": "你确定要拒绝邀请吗?",
"Are you sure you want to upload the following files?": "你确定要上传这些文件吗?",
- "Bans user with given id": "封禁指定 ID 的用户",
+ "Bans user with given id": "按照 ID 封禁指定的用户",
"Blacklisted": "已列入黑名单",
"Bulk Options": "批量操作",
"Call Timeout": "通话超时",
@@ -210,8 +210,8 @@
"Conference calling is in development and may not be reliable.": "视频会议功能还在开发状态,可能不稳定。",
"Conference calls are not supported in encrypted rooms": "加密聊天室不支持视频会议",
"Conference calls are not supported in this client": "此客户端不支持视频会议",
- "%(count)s new messages|one": "%(count)s 条新消息",
- "%(count)s new messages|other": "%(count)s 新消息",
+ "%(count)s new messages|one": "%(count)s 条未读消息",
+ "%(count)s new messages|other": "%(count)s 未读消息",
"Create a new chat or reuse an existing one": "创建新聊天或使用已有的聊天",
"Custom": "自定义",
"Custom level": "自定义级别",
@@ -222,7 +222,7 @@
"Device key:": "设备密钥 :",
"Disable Notifications": "关闭消息通知",
"Drop File Here": "把文件拖拽到这里",
- "Email address (optional)": "电子邮件地址 (可选)",
+ "Email address (optional)": "邮箱地址 (可选)",
"Enable Notifications": "启用消息通知",
"Encrypted by a verified device": "由一个已验证的设备加密",
"Encrypted by an unverified device": "由一个未经验证的设备加密",
@@ -232,31 +232,31 @@
"Error: Problem communicating with the given homeserver.": "错误: 与指定的主服务器通信时出错。",
"Export": "导出",
"Failed to fetch avatar URL": "获取 Avatar URL 失败",
- "Failed to upload profile picture!": "无法上传头像!",
+ "Failed to upload profile picture!": "头像上传失败!",
"Guest access is disabled on this Home Server.": "此服务器禁用了游客访问。",
"Home": "主页面",
"Import": "导入",
"Incoming call from %(name)s": "来自 %(name)s 的通话",
"Incoming video call from %(name)s": "来自 %(name)s 的视频通话",
- "Incoming voice call from %(name)s": "来自 %(name)s 的视频通话",
+ "Incoming voice call from %(name)s": "来自 %(name)s 的语音通话",
"Incorrect username and/or password.": "用户名或密码错误。",
"%(senderName)s invited %(targetName)s.": "%(senderName)s 邀请了 %(targetName)s。",
"Invited": "已邀请",
"Invites": "邀请",
- "Invites user with given id to current room": "邀请指定 ID 的用户加入当前聊天室",
- "'%(alias)s' is not a valid format for an address": "'%(alias)s' 不是一个合法的电子邮件地址格式",
+ "Invites user with given id to current room": "按照 ID 邀请指定用户加入当前聊天室",
+ "'%(alias)s' is not a valid format for an address": "'%(alias)s' 不是一个合法的邮箱地址格式",
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' 不是一个合法的昵称格式",
- "%(displayName)s is typing": "%(displayName)s 正在打字",
+ "%(displayName)s is typing": "%(displayName)s 正在输入",
"Sign in with": "第三方登录",
"Message not sent due to unknown devices being present": "消息未发送,因为有未知的设备存在",
- "Missing room_id in request": "请求中没有 room_id",
+ "Missing room_id in request": "请求中没有 聊天室 ID",
"Missing user_id in request": "请求中没有 user_id",
"Mobile phone number": "手机号码",
"Mobile phone number (optional)": "手机号码 (可选)",
"Moderator": "协管员",
"Mute": "静音",
"Name": "姓名",
- "Never send encrypted messages to unverified devices from this device": "不要从此设备向未验证的设备发送消息",
+ "Never send encrypted messages to unverified devices from this device": "在此设备上不向未经验证的设备发送消息",
"New passwords don't match": "两次输入的新密码不符",
"none": "无",
"not set": "未设置",
@@ -274,7 +274,7 @@
"Password:": "密码:",
"Passwords can't be empty": "密码不能为空",
"Permissions": "权限",
- "Phone": "电话",
+ "Phone": "手机号码",
"Cancel": "取消",
"Create new room": "创建新聊天室",
"Custom Server Options": "自定义服务器选项",
@@ -293,7 +293,7 @@
"Edit": "编辑",
"Joins room with given alias": "以指定的别名加入聊天室",
"Labs": "实验室",
- "%(targetName)s left the room.": "%(targetName)s 离开了聊天室。",
+ "%(targetName)s left the room.": "%(targetName)s 退出了聊天室。",
"Logged in as:": "登录为:",
"Logout": "登出",
"Low priority": "低优先级",
@@ -365,11 +365,11 @@
"Unverify": "取消验证",
"ex. @bob:example.com": "例如 @bob:example.com",
"Add User": "添加用户",
- "This Home Server would like to make sure you are not a robot": "这个Home Server想要确认你不是一个机器人",
+ "This Home Server would like to make sure you are not a robot": "此主服务器想确认你不是机器人",
"Token incorrect": "令牌错误",
"Default server": "默认服务器",
"Custom server": "自定义服务器",
- "URL Previews": "URL 预览",
+ "URL Previews": "链接预览",
"Drop file here to upload": "把文件拖到这里以上传",
"Online": "在线",
"Idle": "空闲",
@@ -395,8 +395,8 @@
"Drop here to tag %(section)s": "拖拽到这里标记 %(section)s",
"Enable automatic language detection for syntax highlighting": "启用自动语言检测用于语法高亮",
"Failed to change power level": "修改特权级别失败",
- "Kick": "踢出",
- "Kicks user with given id": "踢出指定 ID 的用户",
+ "Kick": "移除",
+ "Kicks user with given id": "按照 ID 移除特定的用户",
"Last seen": "上次看见",
"Level:": "级别:",
"Local addresses for this room:": "这个聊天室的本地地址:",
@@ -416,9 +416,9 @@
"Sets the room topic": "设置聊天室主题",
"Show Text Formatting Toolbar": "显示文字格式工具栏",
"This room has no local addresses": "这个聊天室没有本地地址",
- "This doesn't appear to be a valid email address": "这看起来不是一个合法的电子邮件地址",
+ "This doesn't appear to be a valid email address": "这看起来不是一个合法的邮箱地址",
"This is a preview of this room. Room interactions have been disabled": "这是这个聊天室的一个预览。聊天室交互已禁用",
- "This phone number is already in use": "此电话号码已被使用",
+ "This phone number is already in use": "此手机号码已被使用",
"This room": "这个聊天室",
"This room is not accessible by remote Matrix servers": "这个聊天室无法被远程 Matrix 服务器访问",
"This room's internal ID is": "这个聊天室的内部 ID 是",
@@ -433,7 +433,7 @@
"Unencrypted room": "未加密的聊天室",
"unencrypted": "未加密的",
"Unencrypted message": "未加密的消息",
- "unknown caller": "未知的呼叫者",
+ "unknown caller": "未知呼叫者",
"unknown device": "未知设备",
"Unnamed Room": "未命名的聊天室",
"Unverified": "未验证",
@@ -449,10 +449,10 @@
"Passwords don't match.": "密码不匹配。",
"I already have an account": "我已经有一个帐号",
"Unblacklist": "移出黑名单",
- "Not a valid Riot keyfile": "不是一个合法的 Riot 密钥文件",
+ "Not a valid Riot keyfile": "不是一个有效的 Riot 密钥文件",
"%(targetName)s accepted an invitation.": "%(targetName)s 接受了一个邀请。",
"Do you want to load widget from URL:": "你想从此 URL 加载小组件吗:",
- "Hide join/leave messages (invites/kicks/bans unaffected)": "隐藏加入/离开消息(邀请/踢出/封禁不受影响)",
+ "Hide join/leave messages (invites/kicks/bans unaffected)": "隐藏加入/退出消息(邀请/踢出/封禁不受影响)",
"Integrations Error": "集成错误",
"Publish this room to the public in %(domain)s's room directory?": "把这个聊天室发布到 %(domain)s 的聊天室目录吗?",
"Manage Integrations": "管理集成",
@@ -464,12 +464,12 @@
"%(senderName)s requested a VoIP conference.": "%(senderName)s 请求一个 VoIP 会议。",
"Seen by %(userName)s at %(dateTime)s": "在 %(dateTime)s 被 %(userName)s 看到",
"Tagged as: ": "标记为: ",
- "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "验证码将发送到+%(msisdn)s,请输入接收到的验证码",
+ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "验证码将发送至 +%(msisdn)s,请输入收到的验证码",
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s 接受了 %(displayName)s 的邀请。",
- "Active call (%(roomName)s)": "%(roomName)s 的呼叫",
+ "Active call (%(roomName)s)": "当前通话 (来自聊天室 %(roomName)s)",
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s 将级别调整到%(powerLevelDiffText)s 。",
"Changes colour scheme of current room": "修改了样式",
- "Deops user with given id": "Deops user",
+ "Deops user with given id": "按照 ID 取消特定用户的管理员权限",
"Join as voice or video.": "通过 语言 或者 视频加入.",
"%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s 设定历史浏览功能为 所有聊天室成员,从他们被邀请开始.",
"%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s 设定历史浏览功能为 所有聊天室成员,从他们加入开始.",
@@ -486,16 +486,16 @@
"%(roomName)s is not accessible at this time.": "%(roomName)s 此时无法访问。",
"Start authentication": "开始认证",
"The maximum permitted number of widgets have already been added to this room.": "小部件的最大允许数量已经添加到这个聊天室了。",
- "The phone number entered looks invalid": "输入的电话号码看起来无效",
+ "The phone number entered looks invalid": "输入的手机号码看起来无效",
"The remote side failed to pick up": "远端未能接收到",
- "This Home Server does not support login using email address.": "HS不支持使用电子邮件地址登陆。",
- "This invitation was sent to an email address which is not associated with this account:": "此邀请被发送到与此帐户不相关的电子邮件地址:",
+ "This Home Server does not support login using email address.": "HS不支持使用邮箱地址登陆。",
+ "This invitation was sent to an email address which is not associated with this account:": "此邀请被发送到与此帐户不相关的邮箱地址:",
"This room is not recognised.": "无法识别此聊天室。",
"To get started, please pick a username!": "请点击用户名!",
- "Unable to add email address": "无法添加电子邮件地址",
+ "Unable to add email address": "无法添加邮箱地址",
"Automatically replace plain text Emoji": "文字、表情自动转换",
- "To reset your password, enter the email address linked to your account": "要重置你的密码,请输入关联你的帐号的电子邮箱地址",
- "Unable to verify email address.": "无法验证电子邮箱地址。",
+ "To reset your password, enter the email address linked to your account": "要重置你的密码,请输入关联你的帐号的邮箱地址",
+ "Unable to verify email address.": "无法验证邮箱地址。",
"Unknown room %(roomId)s": "未知聊天室 %(roomId)s",
"Unknown (user, device) pair:": "未知(用户,设备)对:",
"Unrecognised command:": "无法识别的命令:",
@@ -515,18 +515,18 @@
"You cannot place VoIP calls in this browser.": "你不能在这个浏览器中发起 VoIP 通话。",
"You do not have permission to post to this room": "你没有发送到这个聊天室的权限",
"You have been invited to join this room by %(inviterName)s": "你已经被 %(inviterName)s 邀请加入这个聊天室",
- "You seem to be in a call, are you sure you want to quit?": "你好像在一个通话中,你确定要退出吗?",
- "You seem to be uploading files, are you sure you want to quit?": "你好像正在上传文件,你确定要退出吗?",
+ "You seem to be in a call, are you sure you want to quit?": "您似乎正在进行通话,确定要退出吗?",
+ "You seem to be uploading files, are you sure you want to quit?": "您似乎正在上传文件,确定要退出吗?",
"You should not yet trust it to secure data": "你不应该相信它来保护你的数据",
"Upload an avatar:": "上传一个头像:",
- "This doesn't look like a valid email address.": "这看起来不是一个合法的电子邮件地址。",
- "This doesn't look like a valid phone number.": "这看起来不是一个合法的电话号码。",
+ "This doesn't look like a valid email address.": "这看起来不是一个合法的邮箱地址。",
+ "This doesn't look like a valid phone number.": "这看起来不是一个合法的手机号码。",
"User names may only contain letters, numbers, dots, hyphens and underscores.": "用户名只可以包含字母、数字、点、连字号和下划线。",
"An unknown error occurred.": "一个未知错误出现了。",
"An error occurred: %(error_string)s": "一个错误出现了: %(error_string)s",
"Encrypt room": "加密聊天室",
"There are no visible files in this room": "这个聊天室里面没有可见的文件",
- "Active call": "活跃的通话",
+ "Active call": "当前通话",
"Verify...": "验证...",
"Error decrypting audio": "解密音频时出错",
"Error decrypting image": "解密图像时出错",
@@ -537,7 +537,7 @@
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s 移除了聊天室头像。",
"Something went wrong!": "出了点问题!",
"If you already have a Matrix account you can log in instead.": "如果你已经有一个 Matrix 帐号,你可以登录。",
- "Do you want to set an email address?": "你要设置一个电子邮箱地址吗?",
+ "Do you want to set an email address?": "你要设置一个邮箱地址吗?",
"New address (e.g. #foo:%(localDomain)s)": "新的地址(例如 #foo:%(localDomain)s)",
"Upload new:": "上传新的:",
"User ID": "用户 ID",
@@ -547,16 +547,16 @@
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "警告:密钥验证失败!%(userId)s 和 device %(deviceId)s 的签名密钥是 \"%(fprint)s\",和提供的咪呀 \"%(fingerprint)s\" 不匹配。这可能意味着你的通信正在被窃听!",
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s 收回了 %(targetName)s 的邀请。",
"Would you like to accept or decline this invitation?": "你想要 接受 还是 拒绝 这个邀请?",
- "You already have existing direct chats with this user:": "你已经有和这个用户的直接聊天:",
+ "You already have existing direct chats with this user:": "你已经有和此用户的直接聊天:",
"You're not in any rooms yet! Press to make a room or to browse the directory": "你现在还不再任何聊天室!按下 来创建一个聊天室或者 来浏览目录",
"You cannot place a call with yourself.": "你不能和你自己发起一个通话。",
"You have been kicked from %(roomName)s by %(userName)s.": "你已经被 %(userName)s 踢出了 %(roomName)s.",
"You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "你已经登出了所有的设备并不再接收推送通知。要重新启用通知,请再在每个设备上登录",
- "You have disabled URL previews by default.": "你已经默认 禁用 URL 预览。",
- "You have enabled URL previews by default.": "你已经默认 启用 URL 预览。",
+ "You have disabled URL previews by default.": "你已经默认 禁用 链接预览。",
+ "You have enabled URL previews by default.": "你已经默认 启用 链接预览。",
"Your home server does not support device management.": "你的 home server 不支持设备管理。",
"Set a display name:": "设置一个昵称:",
- "This server does not support authentication with a phone number.": "这个服务器不支持用电话号码认证。",
+ "This server does not support authentication with a phone number.": "这个服务器不支持用手机号码认证。",
"Password too short (min %(MIN_PASSWORD_LENGTH)s).": "密码过短(最短为 %(MIN_PASSWORD_LENGTH)s)。",
"Make this room private": "使这个聊天室私密",
"Share message history with new users": "和新用户共享消息历史",
@@ -593,7 +593,7 @@
"You must join the room to see its files": "你必须加入聊天室以看到它的文件",
"Failed to invite the following users to the %(roomName)s room:": "邀请以下用户到 %(roomName)s 聊天室失败:",
"Confirm Removal": "确认移除",
- "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "这会让你的账户永远不可用。你无法重新注册同一个用户 ID.",
+ "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "这将会导致您的账户永远无法使用。你将无法重新注册同样的用户 ID。",
"Verifies a user, device, and pubkey tuple": "验证一个用户、设备和密钥元组",
"We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.": "我们在尝试恢复你之前的会话时遇到了一个错误。如果你继续,你将需要重新登录,加密的聊天历史将会不可读。",
"Unknown devices": "未知设备",
@@ -609,9 +609,9 @@
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "你可以使用自定义的服务器选项来通过指定一个不同的主服务器 URL 来登录其他 Matrix 服务器。",
"This allows you to use this app with an existing Matrix account on a different home server.": "这允许你用一个已有在不同主服务器的 Matrix 账户使用这个应用。",
"Please check your email to continue registration.": "请查看你的电子邮件以继续注册。",
- "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "如果你不指定一个电子邮箱地址,你将不能重置你的密码。你确定吗?",
+ "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "如果你不指定一个邮箱地址,你将不能重置你的密码。你确定吗?",
"Home server URL": "主服务器 URL",
- "Identity server URL": "身份服务器 URL",
+ "Identity server URL": "身份认证服务器 URL",
"What does this mean?": "这是什么意思?",
"Image '%(Body)s' cannot be displayed.": "图像 '%(Body)s' 无法显示。",
"This image cannot be displayed.": "图像无法显示。",
@@ -620,12 +620,12 @@
"Ongoing conference call%(supportedText)s.": "正在进行的会议通话 %(supportedText)s.",
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s 修改了 %(roomName)s 的头像",
"This will be your account name on the homeserver, or you can pick a different server.": "这将会成为你在 主服务器上的账户名,或者你可以选择一个 不同的服务器。",
- "Your browser does not support the required cryptography extensions": "你的浏览器不支持所需的密码学扩展",
+ "Your browser does not support the required cryptography extensions": "你的浏览器不支持 Riot 所需的密码学特性",
"Authentication check failed: incorrect password?": "身份验证失败:密码错误?",
"This will allow you to reset your password and receive notifications.": "这将允许你重置你的密码和接收通知。",
"Share without verifying": "不验证就分享",
"You added a new device '%(displayName)s', which is requesting encryption keys.": "你添加了一个新的设备 '%(displayName)s',它正在请求加密密钥。",
- "Your unverified device '%(displayName)s' is requesting encryption keys.": "你的未验证的设备 '%(displayName)s' 正在请求加密密钥。",
+ "Your unverified device '%(displayName)s' is requesting encryption keys.": "你的未经验证的设备 '%(displayName)s' 正在请求加密密钥。",
"Encryption key request": "加密密钥请求",
"Autocomplete Delay (ms):": "自动补全延迟(毫秒):",
"%(widgetName)s widget added by %(senderName)s": "%(widgetName)s 小组建被 %(senderName)s 添加",
@@ -650,18 +650,18 @@
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s 解除了 %(targetName)s 的封禁。",
"(could not connect media)": "(无法连接媒体)",
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s 更改了聊天室的置顶消息。",
- "%(names)s and %(count)s others are typing|other": "%(names)s 和另外 %(count)s 个人正在打字",
- "%(names)s and %(count)s others are typing|one": "%(names)s 和另一个人正在打字",
+ "%(names)s and %(count)s others are typing|other": "%(names)s 和另外 %(count)s 个人正在输入",
+ "%(names)s and %(count)s others are typing|one": "%(names)s 和另一个人正在输入",
"Send": "发送",
"Message Pinning": "消息置顶",
- "Disable Emoji suggestions while typing": "禁用打字时Emoji建议",
+ "Disable Emoji suggestions while typing": "输入时禁用 Emoji 建议",
"Use compact timeline layout": "使用紧凑的时间线布局",
"Hide avatar changes": "隐藏头像修改",
- "Hide display name changes": "隐藏昵称的修改",
+ "Hide display name changes": "隐藏昵称修改",
"Disable big emoji in chat": "禁用聊天中的大Emoji",
- "Never send encrypted messages to unverified devices in this room from this device": "在这个聊天室永不从这个设备发送加密消息到未验证的设备",
- "Enable URL previews for this room (only affects you)": "在这个聊天室启用 URL 预览(只影响你)",
- "Enable URL previews by default for participants in this room": "对这个聊天室的参与者默认启用 URL 预览",
+ "Never send encrypted messages to unverified devices in this room from this device": "在此设备上,在此聊天室中不向未经验证的设备发送加密的消息",
+ "Enable URL previews for this room (only affects you)": "在此聊天室启用链接预览(只影响你)",
+ "Enable URL previews by default for participants in this room": "对这个聊天室的参与者默认启用 链接预览",
"Delete %(count)s devices|other": "删除了 %(count)s 个设备",
"Delete %(count)s devices|one": "删除设备",
"Select devices": "选择设备",
@@ -706,10 +706,10 @@
"were kicked %(count)s times|one": "被踢出",
"was kicked %(count)s times|other": "被踢出 %(count)s 次",
"was kicked %(count)s times|one": "被踢出",
- "%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s 改了他们的名字 %(count)s 次",
- "%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s 改了他们的名字",
- "%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s 改了他们的名字 %(count)s 次",
- "%(oneUser)schanged their name %(count)s times|one": "%(oneUser)s 改了他们的名字",
+ "%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s 改了他们的名称 %(count)s 次",
+ "%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s 改了他们的名称",
+ "%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s 改了他们的名称 %(count)s 次",
+ "%(oneUser)schanged their name %(count)s times|one": "%(oneUser)s 改了他们的名称",
"%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)s 更换了他们的的头像 %(count)s 次",
"%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)s 更换了他们的头像",
"%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)s 更换了他们的头像 %(count)s 次",
@@ -718,10 +718,10 @@
"%(items)s and %(count)s others|one": "%(items)s 和另一个人",
"collapse": "折叠",
"expand": "展开",
- "email address": "电子邮箱地址",
+ "email address": "邮箱地址",
"You have entered an invalid address.": "你输入了一个无效地址。",
"Advanced options": "高级选项",
- "Leave": "离开",
+ "Leave": "退出",
"Description": "描述",
"Warning": "警告",
"Light theme": "浅色主题",
@@ -738,14 +738,13 @@
"Your homeserver's URL": "您的主服务器的链接",
"Your identity server's URL": "您的身份认证服务器的链接",
"The information being sent to us to help make Riot.im better includes:": "将要为帮助 Riot.im 发展而发送的信息包含:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "我们也记录了您在本应用中使用的页面(目前为 ), User Agent()和设备的分辨率()。",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "这个页面中含有可能能用于识别您身份的信息,比如聊天室、用户或群组 ID,在它们发送到服务器上之前,这些数据会被移除。",
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s,%(monthName)s %(day)s %(time)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s,%(monthName)s %(day)s %(fullYear)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s,%(monthName)s %(day)s %(fullYear)s %(time)s",
"Who would you like to add to this community?": "您想把谁添加到这个社区内?",
- "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "警告:您添加的用户对一切知道这个社区的 ID 的人公开",
+ "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "警告:您添加的一切用户都将会对一切知道此社区的 ID 的人公开",
"Name or matrix ID": "名称或 Matrix ID",
"Which rooms would you like to add to this community?": "您想把哪个聊天室添加到这个社区中?",
"Add rooms to the community": "添加聊天室到社区",
@@ -753,11 +752,11 @@
"Failed to invite users to community": "邀请用户到社区失败",
"Message Replies": "消息回复",
"Disable Peer-to-Peer for 1:1 calls": "在1:1通话中禁用点到点",
- "Enable inline URL previews by default": "默认启用自动网址预览",
- "Disinvite this user?": "取消邀请这个用户?",
- "Kick this user?": "踢出这个用户?",
- "Unban this user?": "解除这个用户的封禁?",
- "Ban this user?": "封紧这个用户?",
+ "Enable inline URL previews by default": "默认启用网址预览",
+ "Disinvite this user?": "取消邀请此用户?",
+ "Kick this user?": "移除此用户?",
+ "Unban this user?": "解除此用户的封禁?",
+ "Ban this user?": "封紧此用户?",
"Send an encrypted reply…": "发送加密的回复…",
"Send a reply (unencrypted)…": "发送回复(未加密)…",
"Send an encrypted message…": "发送加密消息…",
@@ -790,7 +789,7 @@
"Add a User": "添加一个用户",
"Unable to accept invite": "无法接受邀请",
"Unable to reject invite": "无法拒绝邀请",
- "Leave Community": "离开社区",
+ "Leave Community": "退出社区",
"Community Settings": "社区设置",
"Community %(groupId)s not found": "找不到社区 %(groupId)s",
"Your Communities": "你的社区",
@@ -802,13 +801,13 @@
"Failed to invite users to %(groupId)s": "邀请用户到 %(groupId)s 失败",
"Failed to invite the following users to %(groupId)s:": "邀请下列用户到 %(groupId)s 失败:",
"Failed to add the following rooms to %(groupId)s:": "添加以下聊天室到 %(groupId)s 失败:",
- "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "你似乎没有将此邮箱地址同在此主服务器上的任何一个 Matrix 账号相关联。",
+ "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "你似乎没有将此邮箱地址同在此主服务器上的任何一个 Matrix 账号绑定。",
"Restricted": "受限用户",
"To use it, just wait for autocomplete results to load and tab through them.": "若要使用自动补全,只要等待自动补全结果加载完成,按 Tab 键切换即可。",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s 将他们的昵称修改成了 %(displayName)s 。",
"Hide avatars in user and room mentions": "隐藏头像",
"Disable Community Filter Panel": "停用社区面板",
- "Opt out of analytics": "禁用开发数据上传",
+ "Opt out of analytics": "退出统计分析服务",
"Stickerpack": "贴图集",
"Sticker Messages": "贴图消息",
"You don't currently have any stickerpacks enabled": "您目前没有启用任何贴纸包",
@@ -849,13 +848,13 @@
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
"You are registering with %(SelectedTeamName)s": "你将注册为 %(SelectedTeamName)s",
"Remove from community": "从社区中移除",
- "Disinvite this user from community?": "是否要取消邀请此用户加入社区?",
+ "Disinvite this user from community?": "是否不再邀请此用户加入本社区?",
"Remove this user from community?": "是否要从社区中移除此用户?",
"Failed to withdraw invitation": "撤回邀请失败",
"Failed to remove user from community": "移除用户失败",
"Filter community members": "过滤社区成员",
"Flair will not appear": "将不会显示 Flair",
- "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "你确定要从 %(groupId)s 中删除1吗?",
+ "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "你确定要从 %(groupId)s 中移除 %(roomName)s 吗?",
"Removing a room from the community will also remove it from the community page.": "从社区中移除房间时,同时也会将其从社区页面中移除。",
"Failed to remove room from community": "从社区中移除聊天室失败",
"Failed to remove '%(roomName)s' from %(groupId)s": "从 %(groupId)s 中移除 “%(roomName)s” 失败",
@@ -868,18 +867,18 @@
"%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s 已加入",
"%(oneUser)sjoined %(count)s times|other": "%(oneUser)s 已加入 %(count)s 次",
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)s 已加入",
- "%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s 已离开 %(count)s 次",
- "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s 已离开",
- "%(oneUser)sleft %(count)s times|other": "%(oneUser)s 已离开 %(count)s 次",
- "%(oneUser)sleft %(count)s times|one": "%(oneUser)s 已离开",
- "%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s 已加入&已离开 %(count)s 次",
- "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s 已加入&已离开",
- "%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s 已加入&已离开 %(count)s 次",
- "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s 已加入&已离开",
- "%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s 离开并重新加入了 %(count)s 次",
- "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s 离开并重新加入了",
- "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s 离开并重新加入了 %(count)s 次",
- "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s 离开并重新加入了",
+ "%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s 已退出 %(count)s 次",
+ "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s 已退出",
+ "%(oneUser)sleft %(count)s times|other": "%(oneUser)s 已退出 %(count)s 次",
+ "%(oneUser)sleft %(count)s times|one": "%(oneUser)s 已退出",
+ "%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s 已加入&已退出 %(count)s 次",
+ "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s 已加入&已退出",
+ "%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s 已加入&已退出 %(count)s 次",
+ "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s 已加入&已退出",
+ "%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s 退出并重新加入了 %(count)s 次",
+ "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s 退出并重新加入了",
+ "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s 退出并重新加入了 %(count)s 次",
+ "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s 退出并重新加入了",
"%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)s 拒绝了他们的邀请",
"%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s 拒绝了他们的邀请共 %(count)s 次",
"%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)s 拒绝了他们的邀请共 %(count)s 次",
@@ -893,7 +892,7 @@
"Community IDs cannot not be empty.": "社区 ID 不能为空。",
"Community IDs may only contain characters a-z, 0-9, or '=_-./'": "社区 ID 只能包含 a-z、0-9 或 “=_-./” 等字符",
"Something went wrong whilst creating your community": "创建社区时出现问题",
- "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "您目前默认将未验证的设备列入黑名单;在发送消息到这些设备上之前,您必须先验证它们。",
+ "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "您目前默认将未经验证的设备列入黑名单;在发送消息到这些设备上之前,您必须先验证它们。",
"If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "如果您之前使用过较新版本的 Riot,则您的会话可能与当前版本不兼容。请关闭此窗口并使用最新版本。",
"To change the room's avatar, you must be a": "无法修改此聊天室的头像,因为您不是此聊天室的",
"To change the room's name, you must be a": "无法修改此聊天室的名称,因为您不是此聊天室的",
@@ -906,7 +905,7 @@
"URL previews are enabled by default for participants in this room.": "此聊天室默认启用链接预览。",
"URL previews are disabled by default for participants in this room.": "此聊天室默认禁用链接预览。",
"%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s 将聊天室的头像更改为 ",
- "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "您也可以自定义身份验证服务器,但这通常会阻止基于电子邮件地址的与用户的交互。",
+ "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "您也可以自定义身份认证服务器,但这通常会阻止基于邮箱地址的与用户的交互。",
"Please enter the code it contains:": "请输入它包含的代码:",
"Flair will appear if enabled in room settings": "如果在聊天室设置中启用, flair 将会显示",
"Matrix ID": "Matrix ID",
@@ -956,7 +955,7 @@
"Please note you are logging into the %(hs)s server, not matrix.org.": "请注意,您正在登录的服务器是 %(hs)s,不是 matrix.org。",
"This homeserver doesn't offer any login flows which are supported by this client.": "此主服务器不兼容本客户端支持的任何登录方式。",
"Sign in to get started": "登录以开始使用",
- "Unbans user with given id": "使用 ID 解封特定的用户",
+ "Unbans user with given id": "按照 ID 解封特定的用户",
"Opens the Developer Tools dialog": "打开开发者工具窗口",
"Notify the whole room": "通知聊天室全体成员",
"This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "此操作允许您将加密聊天室中收到的消息的密钥导出为本地文件。您可以将文件导入其他 Matrix 客户端,以便让别的客户端在未收到密钥的情况下解密这些消息。",
@@ -966,7 +965,7 @@
"Ignores a user, hiding their messages from you": "忽略用户,隐藏他们的消息",
"Stops ignoring a user, showing their messages going forward": "解除忽略用户,显示他们的消息",
"To return to your account in future you need to set a password": "如果你想再次使用账号,您得为它设置一个密码",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "如果你在 GitHub 提交了一个 bug,调试日志可以帮助我们追踪这个问题。 调试日志包含应用程序使用数据,这包括您的用户名、您访问的房间或社区的 ID 或名称以及其他用户的用户名,不包扩聊天记录。",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "如果你在 GitHub 提交了一个 bug,调试日志可以帮助我们追踪这个问题。 调试日志包含应用程序使用数据,这包括您的用户名、您访问的房间或社区的 ID 或名称以及其他用户的用户名,不包扩聊天记录。",
"Debug Logs Submission": "发送调试日志",
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "密码修改成功。在您在其他设备上重新登录之前,其他设备不会收到推送通知",
"Tried to load a specific point in this room's timeline, but was unable to find it.": "尝试加载此房间的时间线的特定时间点,但是无法找到。",
@@ -990,7 +989,7 @@
"Friday": "星期五",
"Update": "更新",
"What's New": "新鲜事",
- "Add an email address above to configure email notifications": "请在上方输入电子邮件地址以接收邮件通知",
+ "Add an email address above to configure email notifications": "请在上方输入邮箱地址以接收邮件通知",
"Expand panel": "展开面板",
"On": "打开",
"%(count)s Members|other": "%(count)s 位成员",
@@ -1044,7 +1043,7 @@
"Tuesday": "星期二",
"Enter keywords separated by a comma:": "输入以逗号间隔的关键字:",
"Forward Message": "转发消息",
- "You have successfully set a password and an email address!": "您已经成功设置了密码和电子邮件地址!",
+ "You have successfully set a password and an email address!": "您已经成功设置了密码和邮箱地址!",
"Remove %(name)s from the directory?": "从目录中移除 %(name)s 吗?",
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot 使用了许多先进的浏览器功能,有些在你目前所用的浏览器上无法使用或仅为实验性的功能。",
"Developer Tools": "开发者工具",
@@ -1129,5 +1128,9 @@
"Collapse panel": "折叠面板",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "您目前的浏览器,应用程序的外观和感觉完全不正确,有些或全部功能可能无法使用。如果您仍想继续尝试,可以继续,但请自行负担其后果!",
"Checking for an update...": "正在检查更新…",
- "There are advanced notifications which are not shown here": "更多的通知并没有在此显示出来"
+ "There are advanced notifications which are not shown here": "更多的通知并没有在此显示出来",
+ "There's no one else here! Would you like to invite others or stop warning about the empty room?": "这里没有其他人了!你是想 邀请用户 还是 不再提示?",
+ "You need to be able to invite users to do that.": "你需要有邀请用户的权限才能进行此操作。",
+ "Missing roomId.": "找不到此聊天室 ID 所对应的聊天室。",
+ "Tag Panel": "标签面板"
}
diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json
index 10844783cc..883252821d 100644
--- a/src/i18n/strings/zh_Hant.json
+++ b/src/i18n/strings/zh_Hant.json
@@ -956,7 +956,6 @@
"Notify the whole room": "通知整個聊天室",
"Room Notification": "聊天室通知",
"The information being sent to us to help make Riot.im better includes:": "協助讓 Riot.im 變得更好的傳送給我們的資訊包含了:",
- "We also record each page you use in the app (currently ), your User Agent () and your device resolution ().": "我們也紀錄了每個您在應用程式中使用的頁面(目前 ),您的使用者代理()與您的裝置解析度()。",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "這個頁面包含了可識別的資訊,如聊天室、使用者或群組 ID,這些資料會在傳到伺服器前被刪除。",
"The platform you're on": "您使用的平臺是",
"The version of Riot.im": "Riot.im 的版本",
@@ -987,7 +986,7 @@
"%(user)s is a %(userRole)s": "%(user)s 是 %(userRole)s",
"Code": "代碼",
"Debug Logs Submission": "除錯訊息傳送",
- "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contian messages.": "如果您透過 GitHub 來回報錯誤,除錯訊息可以用來追蹤問題。除錯訊息包含應用程式的使用資料,包括您的使用者名稱、您所造訪的房間/群組的 ID 或別名、其他使用者的使用者名稱等,其中不包含訊息本身。",
+ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "如果您透過 GitHub 來回報錯誤,除錯訊息可以用來追蹤問題。除錯訊息包含應用程式的使用資料,包括您的使用者名稱、您所造訪的房間/群組的 ID 或別名、其他使用者的使用者名稱等,其中不包含訊息本身。",
"Submit debug logs": "傳送除錯訊息",
"Opens the Developer Tools dialog": "開啟開發者工具對話視窗",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "被 %(displayName)s (%(userName)s) 於 %(dateTime)s 看過",
@@ -1157,5 +1156,7 @@
"Collapse panel": "摺疊面板",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "您目前的瀏覽器,其應用程式的外觀和感覺可能完全不正確,有些或全部功能可以無法使用。如果您仍想要繼續嘗試,可以繼續,但必須自行承擔後果!",
"Checking for an update...": "正在檢查更新...",
- "There are advanced notifications which are not shown here": "有些進階的通知並未在此顯示"
+ "There are advanced notifications which are not shown here": "有些進階的通知並未在此顯示",
+ "Missing roomId.": "缺少 roomid。",
+ "Picture": "圖片"
}
diff --git a/src/rageshake/rageshake.js b/src/rageshake/rageshake.js
index 11e19a709e..93a52ba1aa 100644
--- a/src/rageshake/rageshake.js
+++ b/src/rageshake/rageshake.js
@@ -395,9 +395,6 @@ function selectQuery(store, keyRange, resultMapper) {
}
-let store = null;
-let logger = null;
-let initPromise = null;
module.exports = {
/**
@@ -406,11 +403,11 @@ module.exports = {
* @return {Promise} Resolves when set up.
*/
init: function() {
- if (initPromise) {
- return initPromise;
+ if (global.mx_rage_initPromise) {
+ return global.mx_rage_initPromise;
}
- logger = new ConsoleLogger();
- logger.monkeyPatch(window.console);
+ global.mx_rage_logger = new ConsoleLogger();
+ global.mx_rage_logger.monkeyPatch(window.console);
// just *accessing* indexedDB throws an exception in firefox with
// indexeddb disabled.
@@ -420,19 +417,19 @@ module.exports = {
} catch(e) {}
if (indexedDB) {
- store = new IndexedDBLogStore(indexedDB, logger);
- initPromise = store.connect();
- return initPromise;
+ global.mx_rage_store = new IndexedDBLogStore(indexedDB, global.mx_rage_logger);
+ global.mx_rage_initPromise = global.mx_rage_store.connect();
+ return global.mx_rage_initPromise;
}
- initPromise = Promise.resolve();
- return initPromise;
+ global.mx_rage_initPromise = Promise.resolve();
+ return global.mx_rage_initPromise;
},
flush: function() {
- if (!store) {
+ if (!global.mx_rage_store) {
return;
}
- store.flush();
+ global.mx_rage_store.flush();
},
/**
@@ -440,10 +437,10 @@ module.exports = {
* @return Promise Resolves if cleaned logs.
*/
cleanup: async function() {
- if (!store) {
+ if (!global.mx_rage_store) {
return;
}
- await store.consume();
+ await global.mx_rage_store.consume();
},
/**
@@ -452,21 +449,21 @@ module.exports = {
* @return {Array<{lines: string, id, string}>} list of log data
*/
getLogsForReport: async function() {
- if (!logger) {
+ if (!global.mx_rage_logger) {
throw new Error(
"No console logger, did you forget to call init()?"
);
}
// If in incognito mode, store is null, but we still want bug report
// sending to work going off the in-memory console logs.
- if (store) {
+ if (global.mx_rage_store) {
// flush most recent logs
- await store.flush();
- return await store.consume();
+ await global.mx_rage_store.flush();
+ return await global.mx_rage_store.consume();
}
else {
return [{
- lines: logger.flush(true),
+ lines: global.mx_rage_logger.flush(true),
id: "-",
}];
}
diff --git a/src/settings/Settings.js b/src/settings/Settings.js
index 8e94be3be1..d214d5417f 100644
--- a/src/settings/Settings.js
+++ b/src/settings/Settings.js
@@ -150,6 +150,11 @@ export const SETTINGS = {
displayName: _td('Autoplay GIFs and videos'),
default: false,
},
+ "alwaysShowEncryptionIcons": {
+ supportedLevels: LEVELS_ACCOUNT_SETTINGS,
+ displayName: _td('Always show encryption icons'),
+ default: true,
+ },
"enableSyntaxHighlightLanguageDetection": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
displayName: _td('Enable automatic language detection for syntax highlighting'),
diff --git a/src/stores/GroupStore.js b/src/stores/GroupStore.js
index d4f0b09ff9..bc2be37f51 100644
--- a/src/stores/GroupStore.js
+++ b/src/stores/GroupStore.js
@@ -48,106 +48,110 @@ function checkBacklog() {
// Limit the maximum number of ongoing promises returned by fn to LIMIT and
// use a FIFO queue to handle the backlog.
-function limitConcurrency(fn) {
- return new Promise((resolve, reject) => {
- const item = () => {
- ongoingRequestCount++;
- resolve();
- };
- if (ongoingRequestCount >= LIMIT) {
- // Enqueue this request for later execution
- backlogQueue.push(item);
- } else {
- item();
- }
- })
- .then(fn)
- .then((result) => {
+async function limitConcurrency(fn) {
+ if (ongoingRequestCount >= LIMIT) {
+ // Enqueue this request for later execution
+ await new Promise((resolve, reject) => {
+ backlogQueue.push(resolve);
+ });
+ }
+
+ ongoingRequestCount++;
+ try {
+ return await fn();
+ } catch (err) {
+ // We explicitly do not handle the error here, but let it propogate.
+ throw err;
+ } finally {
ongoingRequestCount--;
checkBacklog();
- return result;
- });
+ }
}
/**
- * Stores the group summary for a room and provides an API to change it and
- * other useful group APIs that may have an effect on the group summary.
+ * Global store for tracking group summary, members, invited members and rooms.
*/
-export default class GroupStore extends EventEmitter {
-
- static STATE_KEY = {
+class GroupStore extends EventEmitter {
+ STATE_KEY = {
GroupMembers: 'GroupMembers',
GroupInvitedMembers: 'GroupInvitedMembers',
Summary: 'Summary',
GroupRooms: 'GroupRooms',
};
- constructor(groupId) {
+ constructor() {
super();
- if (!groupId) {
- throw new Error('GroupStore needs a valid groupId to be created');
- }
- this.groupId = groupId;
this._state = {};
- this._state[GroupStore.STATE_KEY.Summary] = {};
- this._state[GroupStore.STATE_KEY.GroupRooms] = [];
- this._state[GroupStore.STATE_KEY.GroupMembers] = [];
- this._state[GroupStore.STATE_KEY.GroupInvitedMembers] = [];
- this._ready = {};
+ this._state[this.STATE_KEY.Summary] = {};
+ this._state[this.STATE_KEY.GroupRooms] = {};
+ this._state[this.STATE_KEY.GroupMembers] = {};
+ this._state[this.STATE_KEY.GroupInvitedMembers] = {};
+
+ this._ready = {};
+ this._ready[this.STATE_KEY.Summary] = {};
+ this._ready[this.STATE_KEY.GroupRooms] = {};
+ this._ready[this.STATE_KEY.GroupMembers] = {};
+ this._ready[this.STATE_KEY.GroupInvitedMembers] = {};
+
+ this._fetchResourcePromise = {
+ [this.STATE_KEY.Summary]: {},
+ [this.STATE_KEY.GroupRooms]: {},
+ [this.STATE_KEY.GroupMembers]: {},
+ [this.STATE_KEY.GroupInvitedMembers]: {},
+ };
- this._fetchResourcePromise = {};
this._resourceFetcher = {
- [GroupStore.STATE_KEY.Summary]: () => {
+ [this.STATE_KEY.Summary]: (groupId) => {
return limitConcurrency(
- () => MatrixClientPeg.get().getGroupSummary(this.groupId),
+ () => MatrixClientPeg.get().getGroupSummary(groupId),
);
},
- [GroupStore.STATE_KEY.GroupRooms]: () => {
+ [this.STATE_KEY.GroupRooms]: (groupId) => {
return limitConcurrency(
- () => MatrixClientPeg.get().getGroupRooms(this.groupId).then(parseRoomsResponse),
+ () => MatrixClientPeg.get().getGroupRooms(groupId).then(parseRoomsResponse),
);
},
- [GroupStore.STATE_KEY.GroupMembers]: () => {
+ [this.STATE_KEY.GroupMembers]: (groupId) => {
return limitConcurrency(
- () => MatrixClientPeg.get().getGroupUsers(this.groupId).then(parseMembersResponse),
+ () => MatrixClientPeg.get().getGroupUsers(groupId).then(parseMembersResponse),
);
},
- [GroupStore.STATE_KEY.GroupInvitedMembers]: () => {
+ [this.STATE_KEY.GroupInvitedMembers]: (groupId) => {
return limitConcurrency(
- () => MatrixClientPeg.get().getGroupInvitedUsers(this.groupId).then(parseMembersResponse),
+ () => MatrixClientPeg.get().getGroupInvitedUsers(groupId).then(parseMembersResponse),
);
},
};
- this.on('error', (err) => {
- console.error(`GroupStore for ${this.groupId} encountered error`, err);
+ this.on('error', (err, groupId) => {
+ console.error(`GroupStore encountered error whilst fetching data for ${groupId}`, err);
});
}
- _fetchResource(stateKey) {
+ _fetchResource(stateKey, groupId) {
// Ongoing request, ignore
- if (this._fetchResourcePromise[stateKey]) return;
+ if (this._fetchResourcePromise[stateKey][groupId]) return;
- const clientPromise = this._resourceFetcher[stateKey]();
+ const clientPromise = this._resourceFetcher[stateKey](groupId);
// Indicate ongoing request
- this._fetchResourcePromise[stateKey] = clientPromise;
+ this._fetchResourcePromise[stateKey][groupId] = clientPromise;
clientPromise.then((result) => {
- this._state[stateKey] = result;
- this._ready[stateKey] = true;
+ this._state[stateKey][groupId] = result;
+ this._ready[stateKey][groupId] = true;
this._notifyListeners();
}).catch((err) => {
// Invited users not visible to non-members
- if (stateKey === GroupStore.STATE_KEY.GroupInvitedMembers && err.httpStatus === 403) {
+ if (stateKey === this.STATE_KEY.GroupInvitedMembers && err.httpStatus === 403) {
return;
}
- console.error("Failed to get resource " + stateKey + ":" + err);
- this.emit('error', err);
+ console.error(`Failed to get resource ${stateKey} for ${groupId}`, err);
+ this.emit('error', err, groupId);
}).finally(() => {
// Indicate finished request, allow for future fetches
- delete this._fetchResourcePromise[stateKey];
+ delete this._fetchResourcePromise[stateKey][groupId];
});
return clientPromise;
@@ -162,25 +166,29 @@ export default class GroupStore extends EventEmitter {
* immediately triggers an update to send the current state of the
* store (which could be the initial state).
*
- * This also causes a fetch of all group data, which might cause
- * 4 separate HTTP requests, but only said requests aren't already
- * ongoing.
+ * If a group ID is specified, this also causes a fetch of all data
+ * of the specified group, which might cause 4 separate HTTP
+ * requests, but only if said requests aren't already ongoing.
*
+ * @param {string?} groupId the ID of the group to fetch data for.
+ * Optional.
* @param {function} fn the function to call when the store updates.
* @return {Object} tok a registration "token" with a single
* property `unregister`, a function that can
* be called to unregister the listener such
* that it won't be called any more.
*/
- registerListener(fn) {
+ registerListener(groupId, fn) {
this.on('update', fn);
// Call to set initial state (before fetching starts)
this.emit('update');
- this._fetchResource(GroupStore.STATE_KEY.Summary);
- this._fetchResource(GroupStore.STATE_KEY.GroupRooms);
- this._fetchResource(GroupStore.STATE_KEY.GroupMembers);
- this._fetchResource(GroupStore.STATE_KEY.GroupInvitedMembers);
+ if (groupId) {
+ this._fetchResource(this.STATE_KEY.Summary, groupId);
+ this._fetchResource(this.STATE_KEY.GroupRooms, groupId);
+ this._fetchResource(this.STATE_KEY.GroupMembers, groupId);
+ this._fetchResource(this.STATE_KEY.GroupInvitedMembers, groupId);
+ }
// Similar to the Store of flux/utils, we return a "token" that
// can be used to unregister the listener.
@@ -195,123 +203,137 @@ export default class GroupStore extends EventEmitter {
this.removeListener('update', fn);
}
- isStateReady(id) {
- return this._ready[id];
+ isStateReady(groupId, id) {
+ return this._ready[id][groupId];
}
- getSummary() {
- return this._state[GroupStore.STATE_KEY.Summary];
+ getSummary(groupId) {
+ return this._state[this.STATE_KEY.Summary][groupId] || {};
}
- getGroupRooms() {
- return this._state[GroupStore.STATE_KEY.GroupRooms];
+ getGroupRooms(groupId) {
+ return this._state[this.STATE_KEY.GroupRooms][groupId] || [];
}
- getGroupMembers() {
- return this._state[GroupStore.STATE_KEY.GroupMembers];
+ getGroupMembers(groupId) {
+ return this._state[this.STATE_KEY.GroupMembers][groupId] || [];
}
- getGroupInvitedMembers() {
- return this._state[GroupStore.STATE_KEY.GroupInvitedMembers];
+ getGroupInvitedMembers(groupId) {
+ return this._state[this.STATE_KEY.GroupInvitedMembers][groupId] || [];
}
- getGroupPublicity() {
- return this._state[GroupStore.STATE_KEY.Summary].user ?
- this._state[GroupStore.STATE_KEY.Summary].user.is_publicised : null;
+ getGroupPublicity(groupId) {
+ return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
+ (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_publicised : null;
}
- isUserPrivileged() {
- return this._state[GroupStore.STATE_KEY.Summary].user ?
- this._state[GroupStore.STATE_KEY.Summary].user.is_privileged : null;
+ isUserPrivileged(groupId) {
+ return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
+ (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_privileged : null;
}
- addRoomToGroup(roomId, isPublic) {
+ refreshGroupRooms(groupId) {
+ return this._fetchResource(this.STATE_KEY.GroupRooms, groupId);
+ }
+
+ refreshGroupMembers(groupId) {
+ return this._fetchResource(this.STATE_KEY.GroupMembers, groupId);
+ }
+
+ addRoomToGroup(groupId, roomId, isPublic) {
return MatrixClientPeg.get()
- .addRoomToGroup(this.groupId, roomId, isPublic)
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms));
+ .addRoomToGroup(groupId, roomId, isPublic)
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
}
- updateGroupRoomVisibility(roomId, isPublic) {
+ updateGroupRoomVisibility(groupId, roomId, isPublic) {
return MatrixClientPeg.get()
- .updateGroupRoomVisibility(this.groupId, roomId, isPublic)
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms));
+ .updateGroupRoomVisibility(groupId, roomId, isPublic)
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
}
- removeRoomFromGroup(roomId) {
+ removeRoomFromGroup(groupId, roomId) {
return MatrixClientPeg.get()
- .removeRoomFromGroup(this.groupId, roomId)
+ .removeRoomFromGroup(groupId, roomId)
// Room might be in the summary, refresh just in case
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary))
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms));
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
}
- inviteUserToGroup(userId) {
- return MatrixClientPeg.get().inviteUserToGroup(this.groupId, userId)
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupInvitedMembers));
+ inviteUserToGroup(groupId, userId) {
+ return MatrixClientPeg.get().inviteUserToGroup(groupId, userId)
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
}
- acceptGroupInvite() {
- return MatrixClientPeg.get().acceptGroupInvite(this.groupId)
+ acceptGroupInvite(groupId) {
+ return MatrixClientPeg.get().acceptGroupInvite(groupId)
// The user should now be able to access (personal) group settings
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
// The user might be able to see more rooms now
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
// The user should now appear as a member
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupMembers))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
// The user should now not appear as an invited member
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupInvitedMembers));
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
}
- joinGroup() {
- return MatrixClientPeg.get().joinGroup(this.groupId)
+ joinGroup(groupId) {
+ return MatrixClientPeg.get().joinGroup(groupId)
// The user should now be able to access (personal) group settings
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
// The user might be able to see more rooms now
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
// The user should now appear as a member
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupMembers))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
// The user should now not appear as an invited member
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupInvitedMembers));
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
}
- leaveGroup() {
- return MatrixClientPeg.get().leaveGroup(this.groupId)
+ leaveGroup(groupId) {
+ return MatrixClientPeg.get().leaveGroup(groupId)
// The user should now not be able to access group settings
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
// The user might only be able to see a subset of rooms now
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupRooms))
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
// The user should now not appear as a member
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.GroupMembers));
+ .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId));
}
- addRoomToGroupSummary(roomId, categoryId) {
+ addRoomToGroupSummary(groupId, roomId, categoryId) {
return MatrixClientPeg.get()
- .addRoomToGroupSummary(this.groupId, roomId, categoryId)
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
+ .addRoomToGroupSummary(groupId, roomId, categoryId)
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
}
- addUserToGroupSummary(userId, roleId) {
+ addUserToGroupSummary(groupId, userId, roleId) {
return MatrixClientPeg.get()
- .addUserToGroupSummary(this.groupId, userId, roleId)
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
+ .addUserToGroupSummary(groupId, userId, roleId)
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
}
- removeRoomFromGroupSummary(roomId) {
+ removeRoomFromGroupSummary(groupId, roomId) {
return MatrixClientPeg.get()
- .removeRoomFromGroupSummary(this.groupId, roomId)
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
+ .removeRoomFromGroupSummary(groupId, roomId)
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
}
- removeUserFromGroupSummary(userId) {
+ removeUserFromGroupSummary(groupId, userId) {
return MatrixClientPeg.get()
- .removeUserFromGroupSummary(this.groupId, userId)
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
+ .removeUserFromGroupSummary(groupId, userId)
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
}
- setGroupPublicity(isPublished) {
+ setGroupPublicity(groupId, isPublished) {
return MatrixClientPeg.get()
- .setGroupPublicity(this.groupId, isPublished)
+ .setGroupPublicity(groupId, isPublished)
.then(() => { FlairStore.invalidatePublicisedGroups(MatrixClientPeg.get().credentials.userId); })
- .then(this._fetchResource.bind(this, GroupStore.STATE_KEY.Summary));
+ .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
}
}
+
+let singletonGroupStore = null;
+if (!singletonGroupStore) {
+ singletonGroupStore = new GroupStore();
+}
+module.exports = singletonGroupStore;
diff --git a/src/stores/GroupStoreCache.js b/src/stores/GroupStoreCache.js
deleted file mode 100644
index 8b4286831b..0000000000
--- a/src/stores/GroupStoreCache.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 New Vector Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import GroupStore from './GroupStore';
-
-class GroupStoreCache {
- constructor() {
- this.groupStore = null;
- }
-
- getGroupStore(groupId) {
- if (!this.groupStore || this.groupStore.groupId !== groupId) {
- // This effectively throws away the reference to any previous GroupStore,
- // allowing it to be GCd once the components referencing it have stopped
- // referencing it.
- this.groupStore = new GroupStore(groupId);
- }
- return this.groupStore;
- }
-}
-
-if (global.singletonGroupStoreCache === undefined) {
- global.singletonGroupStoreCache = new GroupStoreCache();
-}
-export default global.singletonGroupStoreCache;
diff --git a/src/utils/DecryptFile.js b/src/utils/DecryptFile.js
index cb5e407407..92c2e3644d 100644
--- a/src/utils/DecryptFile.js
+++ b/src/utils/DecryptFile.js
@@ -1,5 +1,6 @@
/*
Copyright 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -22,25 +23,62 @@ import 'isomorphic-fetch';
import MatrixClientPeg from '../MatrixClientPeg';
import Promise from 'bluebird';
+// WARNING: We have to be very careful about what mime-types we allow into blobs,
+// as for performance reasons these are now rendered via URL.createObjectURL()
+// rather than by converting into data: URIs.
+//
+// This means that the content is rendered using the origin of the script which
+// called createObjectURL(), and so if the content contains any scripting then it
+// will pose a XSS vulnerability when the browser renders it. This is particularly
+// bad if the user right-clicks the URI and pastes it into a new window or tab,
+// as the blob will then execute with access to Riot's full JS environment(!)
+//
+// See https://github.com/matrix-org/matrix-react-sdk/pull/1820#issuecomment-385210647
+// for details.
+//
+// We mitigate this by only allowing mime-types into blobs which we know don't
+// contain any scripting, and instantiate all others as application/octet-stream
+// regardless of what mime-type the event claimed. Even if the payload itself
+// is some malicious HTML, the fact we instantiate it with a media mimetype or
+// application/octet-stream means the browser doesn't try to render it as such.
+//
+// One interesting edge case is image/svg+xml, which empirically *is* rendered
+// correctly if the blob is set to the src attribute of an img tag (for thumbnails)
+// *even if the mimetype is application/octet-stream*. However, empirically JS
+// in the SVG isn't executed in this scenario, so we seem to be okay.
+//
+// Tested on Chrome 65 and Firefox 60
+//
+// The list below is taken mainly from
+// https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats
+// N.B. Matrix doesn't currently specify which mimetypes are valid in given
+// events, so we pick the ones which HTML5 browsers should be able to display
+//
+// For the record, mime-types which must NEVER enter this list below include:
+// text/html, text/xhtml, image/svg, image/svg+xml, image/pdf, and similar.
-/**
- * Read blob as a data:// URI.
- * @return {Promise} A promise that resolves with the data:// URI.
- */
-export function readBlobAsDataUri(file) {
- const deferred = Promise.defer();
- const reader = new FileReader();
- reader.onload = function(e) {
- deferred.resolve(e.target.result);
- };
- reader.onerror = function(e) {
- deferred.reject(e);
- };
- reader.readAsDataURL(file);
- return deferred.promise;
+const ALLOWED_BLOB_MIMETYPES = {
+ 'image/jpeg': true,
+ 'image/gif': true,
+ 'image/png': true,
+
+ 'video/mp4': true,
+ 'video/webm': true,
+ 'video/ogg': true,
+
+ 'audio/mp4': true,
+ 'audio/webm': true,
+ 'audio/aac': true,
+ 'audio/mpeg': true,
+ 'audio/ogg': true,
+ 'audio/wave': true,
+ 'audio/wav': true,
+ 'audio/x-wav': true,
+ 'audio/x-pn-wav': true,
+ 'audio/flac': true,
+ 'audio/x-flac': true,
}
-
/**
* Decrypt a file attached to a matrix event.
* @param file {Object} The json taken from the matrix event.
@@ -61,7 +99,17 @@ export function decryptFile(file) {
return encrypt.decryptAttachment(responseData, file);
}).then(function(dataArray) {
// Turn the array into a Blob and give it the correct MIME-type.
- const blob = new Blob([dataArray], {type: file.mimetype});
+
+ // IMPORTANT: we must not allow scriptable mime-types into Blobs otherwise
+ // they introduce XSS attacks if the Blob URI is viewed directly in the
+ // browser (e.g. by copying the URI into a new tab or window.)
+ // See warning at top of file.
+ let mimetype = file.mimetype ? file.mimetype.split(";")[0].trim() : '';
+ if (!ALLOWED_BLOB_MIMETYPES[mimetype]) {
+ mimetype = 'application/octet-stream';
+ }
+
+ const blob = new Blob([dataArray], {type: mimetype});
return blob;
});
}
diff --git a/src/utils/MultiInviter.js b/src/utils/MultiInviter.js
index a0f33f5c39..b3e7fc495a 100644
--- a/src/utils/MultiInviter.js
+++ b/src/utils/MultiInviter.js
@@ -18,7 +18,7 @@ limitations under the License.
import MatrixClientPeg from '../MatrixClientPeg';
import {getAddressType} from '../UserAddress';
import {inviteToRoom} from '../RoomInvite';
-import GroupStoreCache from '../stores/GroupStoreCache';
+import GroupStore from '../stores/GroupStore';
import Promise from 'bluebird';
/**
@@ -118,9 +118,7 @@ export default class MultiInviter {
let doInvite;
if (this.groupId !== null) {
- doInvite = GroupStoreCache
- .getGroupStore(this.groupId)
- .inviteUserToGroup(addr);
+ doInvite = GroupStore.inviteUserToGroup(this.groupId, addr);
} else {
doInvite = inviteToRoom(this.roomId, addr);
}
diff --git a/test/components/structures/GroupView-test.js b/test/components/structures/GroupView-test.js
new file mode 100644
index 0000000000..71df26da46
--- /dev/null
+++ b/test/components/structures/GroupView-test.js
@@ -0,0 +1,378 @@
+/*
+Copyright 2018 New Vector Ltd.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ReactTestUtils from 'react-dom/test-utils';
+import expect from 'expect';
+import Promise from 'bluebird';
+
+import MockHttpBackend from 'matrix-mock-request';
+import MatrixClientPeg from '../../../src/MatrixClientPeg';
+import sdk from 'matrix-react-sdk';
+import Matrix from 'matrix-js-sdk';
+
+import * as TestUtils from 'test-utils';
+
+const GroupView = sdk.getComponent('structures.GroupView');
+const WrappedGroupView = TestUtils.wrapInMatrixClientContext(GroupView);
+
+const Spinner = sdk.getComponent('elements.Spinner');
+
+/**
+ * Call fn before calling componentDidUpdate on a react component instance, inst.
+ * @param {React.Component} inst an instance of a React component.
+ * @returns {Promise} promise that resolves when componentDidUpdate is called on
+ * given component instance.
+ */
+function waitForUpdate(inst) {
+ return new Promise((resolve, reject) => {
+ const cdu = inst.componentDidUpdate;
+
+ inst.componentDidUpdate = (prevProps, prevState, snapshot) => {
+ resolve();
+
+ if (cdu) cdu(prevProps, prevState, snapshot);
+
+ inst.componentDidUpdate = cdu;
+ };
+ });
+}
+
+describe('GroupView', function() {
+ let root;
+ let rootElement;
+ let httpBackend;
+ let summaryResponse;
+ let summaryResponseWithComplicatedLongDesc;
+ let summaryResponseWithNoLongDesc;
+ let summaryResponseWithBadImg;
+ let groupId;
+ let groupIdEncoded;
+
+ // Summary response fields
+ const user = {
+ is_privileged: true, // can edit the group
+ is_public: true, // appear as a member to non-members
+ is_publicised: true, // display flair
+ };
+ const usersSection = {
+ roles: {},
+ total_user_count_estimate: 0,
+ users: [],
+ };
+ const roomsSection = {
+ categories: {},
+ rooms: [],
+ total_room_count_estimate: 0,
+ };
+
+ beforeEach(function() {
+ TestUtils.beforeEach(this);
+
+ httpBackend = new MockHttpBackend();
+
+ Matrix.request(httpBackend.requestFn);
+
+ MatrixClientPeg.get = () => Matrix.createClient({
+ baseUrl: 'https://my.home.server',
+ userId: '@me:here',
+ accessToken: '123456789',
+ });
+
+ summaryResponse = {
+ profile: {
+ avatar_url: "mxc://someavatarurl",
+ is_openly_joinable: true,
+ is_public: true,
+ long_description: "This is a LONG description.",
+ name: "The name of a community",
+ short_description: "This is a community",
+ },
+ user,
+ users_section: usersSection,
+ rooms_section: roomsSection,
+ };
+ summaryResponseWithNoLongDesc = {
+ profile: {
+ avatar_url: "mxc://someavatarurl",
+ is_openly_joinable: true,
+ is_public: true,
+ long_description: null,
+ name: "The name of a community",
+ short_description: "This is a community",
+ },
+ user,
+ users_section: usersSection,
+ rooms_section: roomsSection,
+ };
+ summaryResponseWithComplicatedLongDesc = {
+ profile: {
+ avatar_url: "mxc://someavatarurl",
+ is_openly_joinable: true,
+ is_public: true,
+ long_description: `
+
This is a more complicated group page
+
With paragraphs
+
+
And lists!
+
With list items.
+
+
And also images:
`,
+ name: "The name of a community",
+ short_description: "This is a community",
+ },
+ user,
+ users_section: usersSection,
+ rooms_section: roomsSection,
+ };
+
+ summaryResponseWithBadImg = {
+ profile: {
+ avatar_url: "mxc://someavatarurl",
+ is_openly_joinable: true,
+ is_public: true,
+ long_description: '
Evil image:
',
+ name: "The name of a community",
+ short_description: "This is a community",
+ },
+ user,
+ users_section: usersSection,
+ rooms_section: roomsSection,
+ };
+
+ groupId = "+" + Math.random().toString(16).slice(2) + ':domain';
+ groupIdEncoded = encodeURIComponent(groupId);
+
+ rootElement = document.createElement('div');
+ root = ReactDOM.render(, rootElement);
+ });
+
+ afterEach(function() {
+ ReactDOM.unmountComponentAtNode(rootElement);
+ });
+
+ it('should show a spinner when first displayed', function() {
+ ReactTestUtils.findRenderedComponentWithType(root, Spinner);
+
+ // If we don't respond here, the rate limiting done to ensure a maximum of
+ // 3 concurrent network requests for GroupStore will block subsequent requests
+ // in other tests.
+ //
+ // This is a good case for doing the rate limiting somewhere other than the module
+ // scope of GroupStore.js
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
+
+ return httpBackend.flush(undefined, undefined, 0);
+ });
+
+ it('should indicate failure after failed /summary', function() {
+ const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
+ const prom = waitForUpdate(groupView).then(() => {
+ ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_error');
+ });
+
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(500, {});
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
+
+ httpBackend.flush(undefined, undefined, 0);
+ return prom;
+ });
+
+ it('should show a group avatar, name, id and short description after successful /summary', function() {
+ const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
+ const prom = waitForUpdate(groupView).then(() => {
+ ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView');
+
+ const avatar = ReactTestUtils.findRenderedComponentWithType(root, sdk.getComponent('avatars.GroupAvatar'));
+ const img = ReactTestUtils.findRenderedDOMComponentWithTag(avatar, 'img');
+ const avatarImgElement = ReactDOM.findDOMNode(img);
+ expect(avatarImgElement).toExist();
+ expect(avatarImgElement.src).toInclude(
+ 'https://my.home.server/_matrix/media/v1/thumbnail/' +
+ 'someavatarurl?width=48&height=48&method=crop',
+ );
+
+ const name = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_name');
+ const nameElement = ReactDOM.findDOMNode(name);
+ expect(nameElement).toExist();
+ expect(nameElement.innerText).toInclude('The name of a community');
+ expect(nameElement.innerText).toInclude(groupId);
+
+ const shortDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_header_shortDesc');
+ const shortDescElement = ReactDOM.findDOMNode(shortDesc);
+ expect(shortDescElement).toExist();
+ expect(shortDescElement.innerText).toBe('This is a community');
+ });
+
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/summary').respond(200, summaryResponse);
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/users').respond(200, { chunk: [] });
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/invited_users').respond(200, { chunk: [] });
+ httpBackend.when('GET', '/groups/' + groupIdEncoded + '/rooms').respond(200, { chunk: [] });
+
+ httpBackend.flush(undefined, undefined, 0);
+ return prom;
+ });
+
+ it('should show a simple long description after successful /summary', function() {
+ const groupView = ReactTestUtils.findRenderedComponentWithType(root, GroupView);
+ const prom = waitForUpdate(groupView).then(() => {
+ ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView');
+
+ const longDesc = ReactTestUtils.findRenderedDOMComponentWithClass(root, 'mx_GroupView_groupDesc');
+ const longDescElement = ReactDOM.findDOMNode(longDesc);
+ expect(longDescElement).toExist();
+ expect(longDescElement.innerText).toBe('This is a LONG description.');
+ expect(longDescElement.innerHTML).toBe('