+ {
+ _t('The signing key you provided matches the signing key you received ' +
+ 'from %(userId)s\'s device %(deviceId)s. Device marked as verified.',
+ {userId, deviceId})
+ }
+
+
,
+ });
+ })());
}
}
return reject(this.getUsage());
diff --git a/src/SlateComposerHistoryManager.js b/src/SlateComposerHistoryManager.js
deleted file mode 100644
index 948dcf64ff..0000000000
--- a/src/SlateComposerHistoryManager.js
+++ /dev/null
@@ -1,86 +0,0 @@
-//@flow
-/*
-Copyright 2017 Aviral Dasgupta
-
-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 {Value} from 'slate';
-
-import _clamp from 'lodash/clamp';
-
-type MessageFormat = 'rich' | 'markdown';
-
-class HistoryItem {
- // We store history items in their native format to ensure history is accurate
- // and then convert them if our RTE has subsequently changed format.
- value: Value;
- format: MessageFormat = 'rich';
-
- constructor(value: ?Value, format: ?MessageFormat) {
- this.value = value;
- this.format = format;
- }
-
- static fromJSON(obj: Object): HistoryItem {
- return new HistoryItem(
- Value.fromJSON(obj.value),
- obj.format,
- );
- }
-
- toJSON(): Object {
- return {
- value: this.value.toJSON(),
- format: this.format,
- };
- }
-}
-
-export default class SlateComposerHistoryManager {
- history: Array = [];
- prefix: string;
- lastIndex: number = 0; // used for indexing the storage
- currentIndex: number = 0; // used for indexing the loaded validated history Array
-
- constructor(roomId: string, prefix: string = 'mx_composer_history_') {
- this.prefix = prefix + roomId;
-
- // TODO: Performance issues?
- let item;
- for (; item = sessionStorage.getItem(`${this.prefix}[${this.currentIndex}]`); this.currentIndex++) {
- try {
- this.history.push(
- HistoryItem.fromJSON(JSON.parse(item)),
- );
- } catch (e) {
- console.warn("Throwing away unserialisable history", e);
- }
- }
- this.lastIndex = this.currentIndex;
- // reset currentIndex to account for any unserialisable history
- this.currentIndex = this.history.length;
- }
-
- save(value: Value, format: MessageFormat) {
- const item = new HistoryItem(value, format);
- this.history.push(item);
- this.currentIndex = this.history.length;
- sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item.toJSON()));
- }
-
- getItem(offset: number): ?HistoryItem {
- this.currentIndex = _clamp(this.currentIndex + offset, 0, this.history.length - 1);
- return this.history[this.currentIndex];
- }
-}
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index 8290e17555..a0d088affb 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -473,7 +473,7 @@ function textForPowerEvent(event) {
}
function textForPinnedEvent(event) {
- const senderName = event.getSender();
+ const senderName = event.sender ? event.sender.name : event.getSender();
return _t("%(senderName)s changed the pinned messages for the room.", {senderName});
}
diff --git a/src/async-components/views/dialogs/EncryptedEventDialog.js b/src/async-components/views/dialogs/EncryptedEventDialog.js
index 1309efd811..28dd802082 100644
--- a/src/async-components/views/dialogs/EncryptedEventDialog.js
+++ b/src/async-components/views/dialogs/EncryptedEventDialog.js
@@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+<<<<<<< HEAD
import React from "react";
+=======
+import {Key} from "../../../Keyboard";
+
+const React = require("react");
+>>>>>>> develop
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
@@ -22,7 +28,14 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg";
const sdk = require('../../../index');
+<<<<<<< HEAD
export default createReactClass({
+=======
+// XXX: This component is not cross-signing aware.
+// https://github.com/vector-im/riot-web/issues/11752 tracks either updating this
+// component or taking it out to pasture.
+module.exports = createReactClass({
+>>>>>>> develop
displayName: 'EncryptedEventDialog',
propTypes: {
@@ -84,7 +97,7 @@ export default createReactClass({
},
onKeyDown: function(e) {
- if (e.keyCode === 27) { // escape
+ if (e.key === Key.ESCAPE) {
e.stopPropagation();
e.preventDefault();
this.props.onFinished(false);
diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js
index aec9162c97..af07ce3fb7 100644
--- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js
+++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js
@@ -1,6 +1,6 @@
/*
Copyright 2018, 2019 New Vector Ltd
-Copyright 2019 The Matrix.org Foundation C.I.C.
+Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,10 +17,19 @@ limitations under the License.
import React from 'react';
import FileSaver from 'file-saver';
+<<<<<<< HEAD
import * as sdk from '../../../../index';
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
+=======
+import PropTypes from 'prop-types';
+
+import sdk from '../../../../index';
+import MatrixClientPeg from '../../../../MatrixClientPeg';
+>>>>>>> develop
import { scorePassword } from '../../../../utils/PasswordScorer';
import { _t } from '../../../../languageHandler';
+import { accessSecretStorage } from '../../../../CrossSigningManager';
+import SettingsStore from '../../../../settings/SettingsStore';
const PHASE_PASSPHRASE = 0;
const PHASE_PASSPHRASE_CONFIRM = 1;
@@ -48,10 +57,20 @@ function selectText(target) {
* on the server.
*/
export default class CreateKeyBackupDialog extends React.PureComponent {
+ static propTypes = {
+ secureSecretStorage: PropTypes.bool,
+ onFinished: PropTypes.func.isRequired,
+ }
+
constructor(props) {
super(props);
+ this._recoveryKeyNode = null;
+ this._keyBackupInfo = null;
+ this._setZxcvbnResultTimeout = null;
+
this.state = {
+ secureSecretStorage: props.secureSecretStorage,
phase: PHASE_PASSPHRASE,
passPhrase: '',
passPhraseConfirm: '',
@@ -60,12 +79,25 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
zxcvbnResult: null,
setPassPhrase: false,
};
+
+ if (this.state.secureSecretStorage === undefined) {
+ this.state.secureSecretStorage =
+ SettingsStore.isFeatureEnabled("feature_cross_signing");
+ }
+
+ // If we're using secret storage, skip ahead to the backing up step, as
+ // `accessSecretStorage` will handle passphrases as needed.
+ if (this.state.secureSecretStorage) {
+ this.state.phase = PHASE_BACKINGUP;
+ }
}
- componentWillMount() {
- this._recoveryKeyNode = null;
- this._keyBackupInfo = null;
- this._setZxcvbnResultTimeout = null;
+ componentDidMount() {
+ // If we're using secret storage, skip ahead to the backing up step, as
+ // `accessSecretStorage` will handle passphrases as needed.
+ if (this.state.secureSecretStorage) {
+ this._createBackup();
+ }
}
componentWillUnmount() {
@@ -102,15 +134,26 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
}
_createBackup = async () => {
+ const { secureSecretStorage } = this.state;
this.setState({
phase: PHASE_BACKINGUP,
error: null,
});
let info;
try {
- info = await MatrixClientPeg.get().createKeyBackupVersion(
- this._keyBackupInfo,
- );
+ if (secureSecretStorage) {
+ await accessSecretStorage(async () => {
+ info = await MatrixClientPeg.get().prepareKeyBackupVersion(
+ null /* random key */,
+ { secureSecretStorage: true },
+ );
+ info = await MatrixClientPeg.get().createKeyBackupVersion(info);
+ });
+ } else {
+ info = await MatrixClientPeg.get().createKeyBackupVersion(
+ this._keyBackupInfo,
+ );
+ }
await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup();
this.setState({
phase: PHASE_DONE,
diff --git a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js
index 7a7d130dbe..559d972f24 100644
--- a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js
+++ b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js
@@ -1,5 +1,6 @@
/*
-Copyright 2018-2019 New Vector Ltd
+Copyright 2018, 2019 New Vector Ltd
+Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -40,9 +41,11 @@ export default class NewRecoveryMethodDialog extends React.PureComponent {
onSetupClick = async () => {
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
- Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, {
- onFinished: this.props.onFinished,
- });
+ Modal.createTrackedDialog(
+ 'Restore Backup', '', RestoreKeyBackupDialog, {
+ onFinished: this.props.onFinished,
+ }, null, /* priority = */ false, /* static = */ true,
+ );
}
render() {
diff --git a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js
index 2ca595fdc9..9dfc3e73ed 100644
--- a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js
+++ b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js
@@ -1,5 +1,6 @@
/*
Copyright 2019 New Vector Ltd
+Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -35,6 +36,7 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent {
this.props.onFinished();
Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
import("./CreateKeyBackupDialog"),
+ null, null, /* priority = */ false, /* static = */ true,
);
}
diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js
index df51650719..daea3224b7 100644
--- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js
+++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js
@@ -23,13 +23,15 @@ import FileSaver from 'file-saver';
import { _t } from '../../../../languageHandler';
import Modal from '../../../../Modal';
-const PHASE_PASSPHRASE = 0;
-const PHASE_PASSPHRASE_CONFIRM = 1;
-const PHASE_SHOWKEY = 2;
-const PHASE_KEEPITSAFE = 3;
-const PHASE_STORING = 4;
-const PHASE_DONE = 5;
-const PHASE_OPTOUT_CONFIRM = 6;
+const PHASE_LOADING = 0;
+const PHASE_MIGRATE = 1;
+const PHASE_PASSPHRASE = 2;
+const PHASE_PASSPHRASE_CONFIRM = 3;
+const PHASE_SHOWKEY = 4;
+const PHASE_KEEPITSAFE = 5;
+const PHASE_STORING = 6;
+const PHASE_DONE = 7;
+const PHASE_OPTOUT_CONFIRM = 8;
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms.
@@ -58,7 +60,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
this._setZxcvbnResultTimeout = null;
this.state = {
- phase: PHASE_PASSPHRASE,
+ phase: PHASE_LOADING,
passPhrase: '',
passPhraseConfirm: '',
copied: false,
@@ -66,6 +68,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
zxcvbnResult: null,
setPassPhrase: false,
};
+
+ this._fetchBackupInfo();
}
componentWillUnmount() {
@@ -74,10 +78,23 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
}
}
+ async _fetchBackupInfo() {
+ const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
+
+ this.setState({
+ phase: backupInfo ? PHASE_MIGRATE: PHASE_PASSPHRASE,
+ backupInfo,
+ });
+ }
+
_collectRecoveryKeyNode = (n) => {
this._recoveryKeyNode = n;
}
+ _onMigrateNextClick = () => {
+ this._bootstrapSecretStorage();
+ }
+
_onCopyClick = () => {
selectText(this._recoveryKeyNode);
const successful = document.execCommand('copy');
@@ -125,6 +142,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
}
},
createSecretStorageKey: async () => this._keyInfo,
+ keyBackupInfo: this.state.backupInfo,
});
this.setState({
phase: PHASE_DONE,
@@ -250,6 +268,27 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE;
}
+ _renderPhaseMigrate() {
+ // TODO: This is a temporary screen so people who have the labs flag turned on and
+ // click the button are aware they're making a change to their account.
+ // Once we're confident enough in this (and it's supported enough) we can do
+ // it automatically.
+ // https://github.com/vector-im/riot-web/issues/11696
+ const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
+ return
+
{_t(
+ "Secret Storage will be set up using your existing key backup details." +
+ "Your secret storage passphrase and recovery key will be the same as " +
+ " they were for your key backup",
+ )}
-
+
);
}
}
diff --git a/src/components/structures/EmbeddedPage.js b/src/components/structures/EmbeddedPage.js
index 85ae414a0a..276baccef1 100644
--- a/src/components/structures/EmbeddedPage.js
+++ b/src/components/structures/EmbeddedPage.js
@@ -25,9 +25,14 @@ import { _t } from '../../languageHandler';
import sanitizeHtml from 'sanitize-html';
import * as sdk from '../../index';
import dis from '../../dispatcher';
+<<<<<<< HEAD
import {MatrixClientPeg} from '../../MatrixClientPeg';
import { MatrixClient } from 'matrix-js-sdk';
+=======
+import MatrixClientPeg from '../../MatrixClientPeg';
+>>>>>>> develop
import classnames from 'classnames';
+import MatrixClientContext from "../../contexts/MatrixClientContext";
export default class EmbeddedPage extends React.PureComponent {
static propTypes = {
@@ -39,9 +44,7 @@ export default class EmbeddedPage extends React.PureComponent {
scrollbar: PropTypes.bool,
};
- static contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient),
- };
+ static contextType = MatrixClientContext;
constructor(props) {
super(props);
@@ -104,7 +107,7 @@ export default class EmbeddedPage extends React.PureComponent {
render() {
// HACK: Workaround for the context's MatrixClient not updating.
- const client = this.context.matrixClient || MatrixClientPeg.get();
+ const client = this.context || MatrixClientPeg.get();
const isGuest = client ? client.isGuest() : true;
const className = this.props.className;
const classes = classnames({
diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js
index 4f70dc2a9a..ec52afba19 100644
--- a/src/components/structures/LeftPanel.js
+++ b/src/components/structures/LeftPanel.js
@@ -19,12 +19,15 @@ import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-import { MatrixClient } from 'matrix-js-sdk';
import { Key } from '../../Keyboard';
import * as sdk from '../../index';
import dis from '../../dispatcher';
+<<<<<<< HEAD
import * as VectorConferenceHandler from '../../VectorConferenceHandler';
import TagPanelButtons from './TagPanelButtons';
+=======
+import VectorConferenceHandler from '../../VectorConferenceHandler';
+>>>>>>> develop
import SettingsStore from '../../settings/SettingsStore';
import {_t} from "../../languageHandler";
import Analytics from "../../Analytics";
@@ -39,10 +42,6 @@ const LeftPanel = createReactClass({
collapsed: PropTypes.bool.isRequired,
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
- },
-
getInitialState: function() {
return {
searchFilter: '',
@@ -243,7 +242,6 @@ const LeftPanel = createReactClass({
tagPanelContainer = (
{ isCustomTagsEnabled ? : undefined }
-
);
}
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 2cc30a5b39..9597f99cd2 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -38,6 +38,7 @@ import TagOrderActions from '../../actions/TagOrderActions';
import RoomListActions from '../../actions/RoomListActions';
import ResizeHandle from '../views/elements/ResizeHandle';
import {Resizer, CollapseDistributor} from '../../resizer';
+import MatrixClientContext from "../../contexts/MatrixClientContext";
// We need to fetch each pinned message individually (if we don't already have it)
// so each pinned message may trigger a request. Limit the number per room for sanity.
// NB. this is just for server notices rather than pinned messages in general.
@@ -77,21 +78,6 @@ const LoggedInView = createReactClass({
// and lots and lots of other stuff.
},
- childContextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
- authCache: PropTypes.object,
- },
-
- getChildContext: function() {
- return {
- matrixClient: this._matrixClient,
- authCache: {
- auth: {},
- lastUpdate: 0,
- },
- };
- },
-
getInitialState: function() {
return {
// use compact timeline view
@@ -407,13 +393,6 @@ const LoggedInView = createReactClass({
return;
}
- // XXX: Remove after CIDER replaces Slate completely: https://github.com/vector-im/riot-web/issues/11036
- // If using Slate, consume the Backspace without first focusing as it causes an implosion
- if (ev.key === Key.BACKSPACE && !SettingsStore.getValue("useCiderComposer")) {
- ev.stopPropagation();
- return;
- }
-
if (!isClickShortcut && ev.key !== Key.TAB && !canElementReceiveInput(ev.target)) {
// synchronous dispatch so we focus before key generates input
dis.dispatch({action: 'focus_composer'}, true);
@@ -631,21 +610,30 @@ const LoggedInView = createReactClass({
}
return (
-
- { topBar }
-
-
-
-
-
- { pageElement }
-
-
-
+
+
+ { topBar }
+
+
+
+
+
+ { pageElement }
+
+
+
+
);
},
});
diff --git a/src/components/structures/MainSplit.js b/src/components/structures/MainSplit.js
index bd7bfd8780..772be358cf 100644
--- a/src/components/structures/MainSplit.js
+++ b/src/components/structures/MainSplit.js
@@ -74,6 +74,21 @@ export default class MainSplit extends React.Component {
}
}
+ componentDidUpdate(prevProps) {
+ const wasPanelSet = this.props.panel && !prevProps.panel;
+ const wasPanelCleared = !this.props.panel && prevProps.panel;
+
+ if (this.resizeContainer && wasPanelSet) {
+ // The resizer can only be created when **both** expanded and the panel is
+ // set. Once both are true, the container ref will mount, which is required
+ // for the resizer to work.
+ this._createResizer();
+ } else if (this.resizer && wasPanelCleared) {
+ this.resizer.detach();
+ this.resizer = null;
+ }
+ }
+
render() {
const bodyView = React.Children.only(this.props.children);
const panelView = this.props.panel;
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 0713290a26..e88c1bfa16 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -150,16 +150,6 @@ export default createReactClass({
makeRegistrationUrl: PropTypes.func.isRequired,
},
- childContextTypes: {
- appConfig: PropTypes.object,
- },
-
- getChildContext: function() {
- return {
- appConfig: this.props.config,
- };
- },
-
getInitialState: function() {
const s = {
// the master view we are showing.
@@ -1466,7 +1456,7 @@ export default createReactClass({
}
});
- if (SettingsStore.isFeatureEnabled("feature_dm_verification")) {
+ if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
cli.on("crypto.verification.request", request => {
let requestObserver;
if (request.event.getRoomId()) {
@@ -1492,7 +1482,7 @@ export default createReactClass({
const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog");
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
verifier,
- });
+ }, null, /* priority = */ false, /* static = */ true);
});
}
// Fire the tinter right on startup to ensure the default theme is applied
@@ -1579,9 +1569,17 @@ export default createReactClass({
action: 'start_post_registration',
});
} else if (screen.indexOf('room/') == 0) {
- const segments = screen.substring(5).split('/');
- const roomString = segments[0];
- let eventId = segments.splice(1).join("/"); // empty string if no event id given
+ // Rooms can have the following formats:
+ // #room_alias:domain or !opaque_id:domain
+ const room = screen.substring(5);
+ const domainOffset = room.indexOf(':') + 1; // 0 in case room does not contain a :
+ let eventOffset = room.length;
+ // room aliases can contain slashes only look for slash after domain
+ if (room.substring(domainOffset).indexOf('/') > -1) {
+ eventOffset = domainOffset + room.substring(domainOffset).indexOf('/');
+ }
+ const roomString = room.substring(0, eventOffset);
+ let eventId = room.substring(eventOffset + 1); // empty string if no event id given
// Previously we pulled the eventID from the segments in such a way
// where if there was no eventId then we'd get undefined. However, we
diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js
index 3c01c3d6a3..7d7b19d979 100644
--- a/src/components/structures/MyGroups.js
+++ b/src/components/structures/MyGroups.js
@@ -17,12 +17,17 @@ limitations under the License.
import React from 'react';
import createReactClass from 'create-react-class';
+<<<<<<< HEAD
import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk';
import * as sdk from '../../index';
+=======
+import sdk from '../../index';
+>>>>>>> develop
import { _t } from '../../languageHandler';
import dis from '../../dispatcher';
import AccessibleButton from '../views/elements/AccessibleButton';
+import MatrixClientContext from "../../contexts/MatrixClientContext";
export default createReactClass({
displayName: 'MyGroups',
@@ -34,8 +39,8 @@ export default createReactClass({
};
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+ statics: {
+ contextType: MatrixClientContext,
},
componentWillMount: function() {
@@ -47,7 +52,7 @@ export default createReactClass({
},
_fetch: function() {
- this.context.matrixClient.getJoinedGroups().then((result) => {
+ this.context.getJoinedGroups().then((result) => {
this.setState({groups: result.groups, error: null});
}, (err) => {
if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN') {
diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js
index bb62699ecf..a725b73d9a 100644
--- a/src/components/structures/RightPanel.js
+++ b/src/components/structures/RightPanel.js
@@ -23,13 +23,13 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import * as sdk from '../../index';
import dis from '../../dispatcher';
-import { MatrixClient } from 'matrix-js-sdk';
import RateLimitedFunc from '../../ratelimitedfunc';
import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker';
import GroupStore from '../../stores/GroupStore';
import SettingsStore from "../../settings/SettingsStore";
import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases";
import RightPanelStore from "../../stores/RightPanelStore";
+import MatrixClientContext from "../../contexts/MatrixClientContext";
export default class RightPanel extends React.Component {
static get propTypes() {
@@ -40,14 +40,10 @@ export default class RightPanel extends React.Component {
};
}
- static get contextTypes() {
- return {
- matrixClient: PropTypes.instanceOf(MatrixClient),
- };
- }
+ static contextType = MatrixClientContext;
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.state = {
phase: this._getPhaseFromProps(),
isUserPrivilegedInGroup: null,
@@ -93,15 +89,15 @@ export default class RightPanel extends React.Component {
componentWillMount() {
this.dispatcherRef = dis.register(this.onAction);
- const cli = this.context.matrixClient;
+ const cli = this.context;
cli.on("RoomState.members", this.onRoomStateMember);
this._initGroupStore(this.props.groupId);
}
componentWillUnmount() {
dis.unregister(this.dispatcherRef);
- if (this.context.matrixClient) {
- this.context.matrixClient.removeListener("RoomState.members", this.onRoomStateMember);
+ if (this.context) {
+ this.context.removeListener("RoomState.members", this.onRoomStateMember);
}
this._unregisterGroupStore(this.props.groupId);
}
@@ -190,7 +186,7 @@ export default class RightPanel extends React.Component {
} else if (this.state.phase === RIGHT_PANEL_PHASES.GroupRoomList) {
panel = ;
} else if (this.state.phase === RIGHT_PANEL_PHASES.RoomMemberInfo) {
- if (SettingsStore.isFeatureEnabled("feature_dm_verification")) {
+ if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
const onClose = () => {
dis.dispatch({
action: "view_user",
@@ -209,7 +205,7 @@ export default class RightPanel extends React.Component {
} else if (this.state.phase === RIGHT_PANEL_PHASES.Room3pidMemberInfo) {
panel = ;
} else if (this.state.phase === RIGHT_PANEL_PHASES.GroupMemberInfo) {
- if (SettingsStore.isFeatureEnabled("feature_dm_verification")) {
+ if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
const onClose = () => {
dis.dispatch({
action: "view_user",
diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index d783c8cc3f..87b9d076e2 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -27,7 +27,11 @@ import PropTypes from 'prop-types';
import { _t } from '../../languageHandler';
import { instanceForInstanceId, protocolNameForInstanceId } from '../../utils/DirectoryUtils';
import Analytics from '../../Analytics';
+<<<<<<< HEAD
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
+=======
+import MatrixClientContext from "../../contexts/MatrixClientContext";
+>>>>>>> develop
const MAX_NAME_LENGTH = 80;
const MAX_TOPIC_LENGTH = 160;
@@ -63,16 +67,6 @@ export default createReactClass({
};
},
- childContextTypes: {
- matrixClient: PropTypes.object,
- },
-
- getChildContext: function() {
- return {
- matrixClient: MatrixClientPeg.get(),
- };
- },
-
componentWillMount: function() {
this._unmounted = false;
this.nextBatch = null;
@@ -268,6 +262,7 @@ export default createReactClass({
roomServer: server,
instanceId: instanceId,
includeAll: includeAll,
+ error: null,
}, this.refreshRoomList);
// We also refresh the room list each time even though this
// filtering is client-side. It hopefully won't be client side
diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js
index 0839c364e5..d9ce032ba8 100644
--- a/src/components/structures/RoomStatusBar.js
+++ b/src/components/structures/RoomStatusBar.js
@@ -26,7 +26,7 @@ import {MatrixClientPeg} from '../../MatrixClientPeg';
import Resend from '../../Resend';
import * as cryptodevices from '../../cryptodevices';
import dis from '../../dispatcher';
-import { messageForResourceLimitError } from '../../utils/ErrorUtils';
+import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils';
const STATUS_BAR_HIDDEN = 0;
const STATUS_BAR_EXPANDED = 1;
@@ -273,7 +273,7 @@ export default createReactClass({
unsentMessages[0].error.data &&
unsentMessages[0].error.data.error
) {
- title = unsentMessages[0].error.data.error;
+ title = messageForSendError(unsentMessages[0].error.data) || unsentMessages[0].error.data.error;
} else {
title = _t('%(count)s of your messages have not been sent.', { count: unsentMessages.length });
}
diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js
index e16fee64a5..c2a644287d 100644
--- a/src/components/structures/RoomSubList.js
+++ b/src/components/structures/RoomSubList.js
@@ -25,7 +25,7 @@ import * as Unread from '../../Unread';
import * as RoomNotifs from '../../RoomNotifs';
import * as FormattingUtils from '../../utils/FormattingUtils';
import IndicatorScrollbar from './IndicatorScrollbar';
-import {Key, KeyCode} from '../../Keyboard';
+import {Key} from '../../Keyboard';
import { Group } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
import RoomTile from "../views/rooms/RoomTile";
@@ -186,7 +186,7 @@ export default class RoomSubList extends React.PureComponent {
dis.dispatch({
action: 'view_room',
room_id: roomId,
- clear_search: (ev && (ev.keyCode === KeyCode.ENTER || ev.keyCode === KeyCode.SPACE)),
+ clear_search: (ev && (ev.key === Key.ENTER || ev.key === Key.SPACE)),
});
};
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index d3309a1f43..83ef6441f6 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -25,10 +25,8 @@ import shouldHideEvent from '../../shouldHideEvent';
import React, {createRef} from 'react';
import createReactClass from 'create-react-class';
-import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-import {Room} from "matrix-js-sdk";
import { _t } from '../../languageHandler';
import {RoomPermalinkCreator} from '../../utils/permalinks/Permalinks';
@@ -44,7 +42,7 @@ import * as ObjectUtils from '../../ObjectUtils';
import * as Rooms from '../../Rooms';
import eventSearch from '../../Searching';
-import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
+import {isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard';
import MainSplit from './MainSplit';
import RightPanel from './RightPanel';
@@ -55,7 +53,11 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
import WidgetUtils from '../../utils/WidgetUtils';
import AccessibleButton from "../views/elements/AccessibleButton";
import RightPanelStore from "../../stores/RightPanelStore";
+<<<<<<< HEAD
import {haveTileForEvent} from "../views/rooms/EventTile";
+=======
+import RoomContext from "../../contexts/RoomContext";
+>>>>>>> develop
const DEBUG = false;
let debuglog = function() {};
@@ -67,6 +69,7 @@ if (DEBUG) {
debuglog = console.log.bind(console);
}
+<<<<<<< HEAD
export const RoomContext = PropTypes.shape({
canReact: PropTypes.bool.isRequired,
canReply: PropTypes.bool.isRequired,
@@ -74,6 +77,9 @@ export const RoomContext = PropTypes.shape({
});
export default createReactClass({
+=======
+module.exports = createReactClass({
+>>>>>>> develop
displayName: 'RoomView',
propTypes: {
ConferenceHandler: PropTypes.any,
@@ -165,23 +171,6 @@ export default createReactClass({
canReact: false,
canReply: false,
-
- useCider: false,
- };
- },
-
- childContextTypes: {
- room: RoomContext,
- },
-
- getChildContext: function() {
- const {canReact, canReply, room} = this.state;
- return {
- room: {
- canReact,
- canReply,
- room,
- },
};
},
@@ -203,18 +192,10 @@ export default createReactClass({
WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate);
- this._onCiderUpdated();
- this._ciderWatcherRef = SettingsStore.watchSetting(
- "useCiderComposer", null, this._onCiderUpdated);
-
this._roomView = createRef();
this._searchResultsPanel = createRef();
},
- _onCiderUpdated: function() {
- this.setState({useCider: SettingsStore.getValue("useCiderComposer")});
- },
-
_onRoomViewStoreUpdate: function(initial) {
if (this.unmounted) {
return;
@@ -462,7 +443,7 @@ export default createReactClass({
componentDidUpdate: function() {
if (this._roomView.current) {
- const roomView = ReactDOM.findDOMNode(this._roomView.current);
+ const roomView = this._roomView.current;
if (!roomView.ondrop) {
roomView.addEventListener('drop', this.onDrop);
roomView.addEventListener('dragover', this.onDragOver);
@@ -506,7 +487,7 @@ export default createReactClass({
// is really just for hygiene - we're going to be
// deleted anyway, so it doesn't matter if the event listeners
// don't get cleaned up.
- const roomView = ReactDOM.findDOMNode(this._roomView.current);
+ const roomView = this._roomView.current;
roomView.removeEventListener('drop', this.onDrop);
roomView.removeEventListener('dragover', this.onDragOver);
roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd);
@@ -562,15 +543,15 @@ export default createReactClass({
let handled = false;
const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev);
- switch (ev.keyCode) {
- case KeyCode.KEY_D:
+ switch (ev.key) {
+ case Key.D:
if (ctrlCmdOnly) {
this.onMuteAudioClick();
handled = true;
}
break;
- case KeyCode.KEY_E:
+ case Key.E:
if (ctrlCmdOnly) {
this.onMuteVideoClick();
handled = true;
@@ -793,11 +774,12 @@ export default createReactClass({
this._updateE2EStatus(room);
},
- _updateE2EStatus: function(room) {
- if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
+ _updateE2EStatus: async function(room) {
+ const cli = MatrixClientPeg.get();
+ if (!cli.isRoomEncrypted(room.roomId)) {
return;
}
- if (!MatrixClientPeg.get().isCryptoEnabled()) {
+ if (!cli.isCryptoEnabled()) {
// If crypto is not currently enabled, we aren't tracking devices at all,
// so we don't know what the answer is. Let's error on the safe side and show
// a warning for this case.
@@ -806,10 +788,38 @@ export default createReactClass({
});
return;
}
- room.hasUnverifiedDevices().then((hasUnverifiedDevices) => {
- this.setState({
- e2eStatus: hasUnverifiedDevices ? "warning" : "verified",
+ if (!SettingsStore.isFeatureEnabled("feature_cross_signing")) {
+ room.hasUnverifiedDevices().then((hasUnverifiedDevices) => {
+ this.setState({
+ e2eStatus: hasUnverifiedDevices ? "warning" : "verified",
+ });
});
+ return;
+ }
+ const e2eMembers = await room.getEncryptionTargetMembers();
+ for (const member of e2eMembers) {
+ const { userId } = member;
+ const userVerified = cli.checkUserTrust(userId).isCrossSigningVerified();
+ if (!userVerified) {
+ this.setState({
+ e2eStatus: "warning",
+ });
+ return;
+ }
+ const devices = await cli.getStoredDevicesForUser(userId);
+ const allDevicesVerified = devices.every(device => {
+ const { deviceId } = device;
+ return cli.checkDeviceTrust(userId, deviceId).isCrossSigningVerified();
+ });
+ if (!allDevicesVerified) {
+ this.setState({
+ e2eStatus: "warning",
+ });
+ return;
+ }
+ }
+ this.setState({
+ e2eStatus: "verified",
});
},
@@ -1800,29 +1810,16 @@ export default createReactClass({
myMembership === 'join' && !this.state.searchResults
);
if (canSpeak) {
- if (this.state.useCider) {
- const MessageComposer = sdk.getComponent('rooms.MessageComposer');
- messageComposer =
- ;
- } else {
- const SlateMessageComposer = sdk.getComponent('rooms.SlateMessageComposer');
- messageComposer =
- ;
- }
+ const MessageComposer = sdk.getComponent('rooms.MessageComposer');
+ messageComposer =
+ ;
}
// TODO: Why aren't we storing the term/scope/count in this format
@@ -1925,7 +1922,8 @@ export default createReactClass({
/>);
let topUnreadMessagesBar = null;
- if (this.state.showTopUnreadMessagesBar) {
+ // Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
+ if (this.state.showTopUnreadMessagesBar && !this.state.searchResults) {
const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
topUnreadMessagesBar = ();
}
let jumpToBottom;
- if (!this.state.atEndOfLiveTimeline) {
+ // Do not show JumpToBottomButton if we have search results showing, it makes no sense
+ if (!this.state.atEndOfLiveTimeline && !this.state.searchResults) {
const JumpToBottomButton = sdk.getComponent('rooms.JumpToBottomButton');
jumpToBottom = (
-
-
-
-
);
} else {
diff --git a/src/components/views/dialogs/DMInviteDialog.js b/src/components/views/dialogs/DMInviteDialog.js
new file mode 100644
index 0000000000..371768eb4e
--- /dev/null
+++ b/src/components/views/dialogs/DMInviteDialog.js
@@ -0,0 +1,773 @@
+/*
+Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
+
+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, {createRef} from 'react';
+import PropTypes from 'prop-types';
+import {_t} from "../../../languageHandler";
+import sdk from "../../../index";
+import MatrixClientPeg from "../../../MatrixClientPeg";
+import {makeUserPermalink} from "../../../utils/permalinks/Permalinks";
+import DMRoomMap from "../../../utils/DMRoomMap";
+import {RoomMember} from "matrix-js-sdk/lib/matrix";
+import * as humanize from "humanize";
+import SdkConfig from "../../../SdkConfig";
+import {getHttpUriForMxc} from "matrix-js-sdk/lib/content-repo";
+import * as Email from "../../../email";
+import {getDefaultIdentityServerUrl, useDefaultIdentityServer} from "../../../utils/IdentityServerUtils";
+import {abbreviateUrl} from "../../../utils/UrlUtils";
+import dis from "../../../dispatcher";
+import IdentityAuthClient from "../../../IdentityAuthClient";
+import Modal from "../../../Modal";
+
+// TODO: [TravisR] Make this generic for all kinds of invites
+
+const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first
+const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked
+
+// This is the interface that is expected by various components in this file. It is a bit
+// awkward because it also matches the RoomMember class from the js-sdk with some extra support
+// for 3PIDs/email addresses.
+//
+// XXX: We should use TypeScript interfaces instead of this weird "abstract" class.
+class Member {
+ /**
+ * The display name of this Member. For users this should be their profile's display
+ * name or user ID if none set. For 3PIDs this should be the 3PID address (email).
+ */
+ get name(): string { throw new Error("Member class not implemented"); }
+
+ /**
+ * The ID of this Member. For users this should be their user ID. For 3PIDs this should
+ * be the 3PID address (email).
+ */
+ get userId(): string { throw new Error("Member class not implemented"); }
+
+ /**
+ * Gets the MXC URL of this Member's avatar. For users this should be their profile's
+ * avatar MXC URL or null if none set. For 3PIDs this should always be null.
+ */
+ getMxcAvatarUrl(): string { throw new Error("Member class not implemented"); }
+}
+
+class DirectoryMember extends Member {
+ _userId: string;
+ _displayName: string;
+ _avatarUrl: string;
+
+ constructor(userDirResult: {user_id: string, display_name: string, avatar_url: string}) {
+ super();
+ this._userId = userDirResult.user_id;
+ this._displayName = userDirResult.display_name;
+ this._avatarUrl = userDirResult.avatar_url;
+ }
+
+ // These next class members are for the Member interface
+ get name(): string {
+ return this._displayName || this._userId;
+ }
+
+ get userId(): string {
+ return this._userId;
+ }
+
+ getMxcAvatarUrl(): string {
+ return this._avatarUrl;
+ }
+}
+
+class ThreepidMember extends Member {
+ _id: string;
+
+ constructor(id: string) {
+ super();
+ this._id = id;
+ }
+
+ // This is a getter that would be falsey on all other implementations. Until we have
+ // better type support in the react-sdk we can use this trick to determine the kind
+ // of 3PID we're dealing with, if any.
+ get isEmail(): boolean {
+ return this._id.includes('@');
+ }
+
+ // These next class members are for the Member interface
+ get name(): string {
+ return this._id;
+ }
+
+ get userId(): string {
+ return this._id;
+ }
+
+ getMxcAvatarUrl(): string {
+ return null;
+ }
+}
+
+class DMUserTile extends React.PureComponent {
+ static propTypes = {
+ member: PropTypes.object.isRequired, // Should be a Member (see interface above)
+ onRemove: PropTypes.func.isRequired, // takes 1 argument, the member being removed
+ };
+
+ _onRemove = (e) => {
+ // Stop the browser from highlighting text
+ e.preventDefault();
+ e.stopPropagation();
+
+ this.props.onRemove(this.props.member);
+ };
+
+ render() {
+ const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar");
+ const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
+
+ const avatarSize = 20;
+ const avatar = this.props.member.isEmail
+ ?
+ : ;
+
+ return (
+
+
+ {avatar}
+ {this.props.member.name}
+
+
+
+
+
+ );
+ }
+}
+
+class DMRoomTile extends React.PureComponent {
+ static propTypes = {
+ member: PropTypes.object.isRequired, // Should be a Member (see interface above)
+ lastActiveTs: PropTypes.number,
+ onToggle: PropTypes.func.isRequired, // takes 1 argument, the member being toggled
+ highlightWord: PropTypes.string,
+ isSelected: PropTypes.bool,
+ };
+
+ _onClick = (e) => {
+ // Stop the browser from highlighting text
+ e.preventDefault();
+ e.stopPropagation();
+
+ this.props.onToggle(this.props.member);
+ };
+
+ _highlightName(str: string) {
+ if (!this.props.highlightWord) return str;
+
+ // We convert things to lowercase for index searching, but pull substrings from
+ // the submitted text to preserve case. Note: we don't need to htmlEntities the
+ // string because React will safely encode the text for us.
+ const lowerStr = str.toLowerCase();
+ const filterStr = this.props.highlightWord.toLowerCase();
+
+ const result = [];
+
+ let i = 0;
+ let ii;
+ while ((ii = lowerStr.indexOf(filterStr, i)) >= 0) {
+ // Push any text we missed (first bit/middle of text)
+ if (ii > i) {
+ // Push any text we aren't highlighting (middle of text match, or beginning of text)
+ result.push({str.substring(i, ii)});
+ }
+
+ i = ii; // copy over ii only if we have a match (to preserve i for end-of-text matching)
+
+ // Highlight the word the user entered
+ const substr = str.substring(i, filterStr.length + i);
+ result.push({substr});
+ i += substr.length;
+ }
+
+ // Push any text we missed (end of text)
+ if (i < (str.length - 1)) {
+ result.push({str.substring(i)});
+ }
+
+ return result;
+ }
+
+ render() {
+ const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar");
+
+ let timestamp = null;
+ if (this.props.lastActiveTs) {
+ // TODO: [TravisR] Figure out how to i18n this
+ // `humanize` wants seconds for a timestamp, so divide by 1000
+ const humanTs = humanize.relativeTime(this.props.lastActiveTs / 1000);
+ timestamp = {humanTs};
+ }
+
+ const avatarSize = 36;
+ const avatar = this.props.member.isEmail
+ ?
+ : ;
+
+ let checkmark = null;
+ if (this.props.isSelected) {
+ // To reduce flickering we put the 'selected' room tile above the real avatar
+ checkmark = ;
+ }
+
+ // To reduce flickering we put the checkmark on top of the actual avatar (prevents
+ // the browser from reloading the image source when the avatar remounts).
+ const stackedAvatar = (
+
+ {avatar}
+ {checkmark}
+
+ );
+
+ return (
+
+ );
+ }
+}
+
+export default class DMInviteDialog extends React.PureComponent {
+ static propTypes = {
+ // Takes an array of user IDs/emails to invite.
+ onFinished: PropTypes.func.isRequired,
+ };
+
+ _debounceTimer: number = null;
+ _editorRef: any = null;
+
+ constructor() {
+ super();
+
+ this.state = {
+ targets: [], // array of Member objects (see interface above)
+ filterText: "",
+ recents: this._buildRecents(),
+ numRecentsShown: INITIAL_ROOMS_SHOWN,
+ suggestions: this._buildSuggestions(),
+ numSuggestionsShown: INITIAL_ROOMS_SHOWN,
+ serverResultsMixin: [], // { user: DirectoryMember, userId: string }[], like recents and suggestions
+ threepidResultsMixin: [], // { user: ThreepidMember, userId: string}[], like recents and suggestions
+ canUseIdentityServer: !!MatrixClientPeg.get().getIdentityServerUrl(),
+ tryingIdentityServer: false,
+ };
+
+ this._editorRef = createRef();
+ }
+
+ _buildRecents(): {userId: string, user: RoomMember, lastActive: number} {
+ const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals();
+ const recents = [];
+ for (const userId in rooms) {
+ const room = rooms[userId];
+ const member = room.getMember(userId);
+ if (!member) continue; // just skip people who don't have memberships for some reason
+
+ const lastEventTs = room.timeline && room.timeline.length
+ ? room.timeline[room.timeline.length - 1].getTs()
+ : 0;
+ if (!lastEventTs) continue; // something weird is going on with this room
+
+ recents.push({userId, user: member, lastActive: lastEventTs});
+ }
+
+ // Sort the recents by last active to save us time later
+ recents.sort((a, b) => b.lastActive - a.lastActive);
+
+ return recents;
+ }
+
+ _buildSuggestions(): {userId: string, user: RoomMember} {
+ const maxConsideredMembers = 200;
+ const client = MatrixClientPeg.get();
+ const excludedUserIds = [client.getUserId(), SdkConfig.get()['welcomeUserId']];
+ const joinedRooms = client.getRooms()
+ .filter(r => r.getMyMembership() === 'join')
+ .filter(r => r.getJoinedMemberCount() <= maxConsideredMembers);
+
+ // Generates { userId: {member, rooms[]} }
+ const memberRooms = joinedRooms.reduce((members, room) => {
+ const joinedMembers = room.getJoinedMembers().filter(u => !excludedUserIds.includes(u.userId));
+ for (const member of joinedMembers) {
+ if (!members[member.userId]) {
+ members[member.userId] = {
+ member: member,
+ // Track the room size of the 'picked' member so we can use the profile of
+ // the smallest room (likely a DM).
+ pickedMemberRoomSize: room.getJoinedMemberCount(),
+ rooms: [],
+ };
+ }
+
+ members[member.userId].rooms.push(room);
+
+ if (room.getJoinedMemberCount() < members[member.userId].pickedMemberRoomSize) {
+ members[member.userId].member = member;
+ members[member.userId].pickedMemberRoomSize = room.getJoinedMemberCount();
+ }
+ }
+ return members;
+ }, {});
+
+ // Generates { userId: {member, numRooms, score} }
+ const memberScores = Object.values(memberRooms).reduce((scores, entry) => {
+ const numMembersTotal = entry.rooms.reduce((c, r) => c + r.getJoinedMemberCount(), 0);
+ const maxRange = maxConsideredMembers * entry.rooms.length;
+ scores[entry.member.userId] = {
+ member: entry.member,
+ numRooms: entry.rooms.length,
+ score: Math.max(0, Math.pow(1 - (numMembersTotal / maxRange), 5)),
+ };
+ return scores;
+ }, {});
+
+ const members = Object.values(memberScores);
+ members.sort((a, b) => {
+ if (a.score === b.score) {
+ if (a.numRooms === b.numRooms) {
+ return a.member.userId.localeCompare(b.member.userId);
+ }
+
+ return b.numRooms - a.numRooms;
+ }
+ return b.score - a.score;
+ });
+ return members.map(m => ({userId: m.member.userId, user: m.member}));
+ }
+
+ _startDm = () => {
+ this.props.onFinished(this.state.targets.map(t => t.userId));
+ };
+
+ _cancel = () => {
+ this.props.onFinished([]);
+ };
+
+ _updateFilter = (e) => {
+ const term = e.target.value;
+ this.setState({filterText: term});
+
+ // Debounce server lookups to reduce spam. We don't clear the existing server
+ // results because they might still be vaguely accurate, likewise for races which
+ // could happen here.
+ if (this._debounceTimer) {
+ clearTimeout(this._debounceTimer);
+ }
+ this._debounceTimer = setTimeout(async () => {
+ MatrixClientPeg.get().searchUserDirectory({term}).then(r => {
+ if (term !== this.state.filterText) {
+ // Discard the results - we were probably too slow on the server-side to make
+ // these results useful. This is a race we want to avoid because we could overwrite
+ // more accurate results.
+ return;
+ }
+ this.setState({
+ serverResultsMixin: r.results.map(u => ({
+ userId: u.user_id,
+ user: new DirectoryMember(u),
+ })),
+ });
+ }).catch(e => {
+ console.error("Error searching user directory:");
+ console.error(e);
+ this.setState({serverResultsMixin: []}); // clear results because it's moderately fatal
+ });
+
+ // Whenever we search the directory, also try to search the identity server. It's
+ // all debounced the same anyways.
+ if (!this.state.canUseIdentityServer) {
+ // The user doesn't have an identity server set - warn them of that.
+ this.setState({tryingIdentityServer: true});
+ return;
+ }
+ if (term.indexOf('@') > 0 && Email.looksValid(term)) {
+ // Start off by suggesting the plain email while we try and resolve it
+ // to a real account.
+ this.setState({
+ // per above: the userId is a lie here - it's just a regular identifier
+ threepidResultsMixin: [{user: new ThreepidMember(term), userId: term}],
+ });
+ try {
+ const authClient = new IdentityAuthClient();
+ const token = await authClient.getAccessToken();
+ if (term !== this.state.filterText) return; // abandon hope
+
+ const lookup = await MatrixClientPeg.get().lookupThreePid(
+ 'email',
+ term,
+ undefined, // callback
+ token,
+ );
+ if (term !== this.state.filterText) return; // abandon hope
+
+ if (!lookup || !lookup.mxid) {
+ // We weren't able to find anyone - we're already suggesting the plain email
+ // as an alternative, so do nothing.
+ return;
+ }
+
+ // We append the user suggestion to give the user an option to click
+ // the email anyways, and so we don't cause things to jump around. In
+ // theory, the user would see the user pop up and think "ah yes, that
+ // person!"
+ const profile = await MatrixClientPeg.get().getProfileInfo(lookup.mxid);
+ if (term !== this.state.filterText || !profile) return; // abandon hope
+ this.setState({
+ threepidResultsMixin: [...this.state.threepidResultsMixin, {
+ user: new DirectoryMember({
+ user_id: lookup.mxid,
+ display_name: profile.displayname,
+ avatar_url: profile.avatar_url,
+ }),
+ userId: lookup.mxid,
+ }],
+ });
+ } catch (e) {
+ console.error("Error searching identity server:");
+ console.error(e);
+ this.setState({threepidResultsMixin: []}); // clear results because it's moderately fatal
+ }
+ }
+ }, 150); // 150ms debounce (human reaction time + some)
+ };
+
+ _showMoreRecents = () => {
+ this.setState({numRecentsShown: this.state.numRecentsShown + INCREMENT_ROOMS_SHOWN});
+ };
+
+ _showMoreSuggestions = () => {
+ this.setState({numSuggestionsShown: this.state.numSuggestionsShown + INCREMENT_ROOMS_SHOWN});
+ };
+
+ _toggleMember = (member: Member) => {
+ const targets = this.state.targets.map(t => t); // cheap clone for mutation
+ const idx = targets.indexOf(member);
+ if (idx >= 0) targets.splice(idx, 1);
+ else targets.push(member);
+ this.setState({targets});
+ };
+
+ _removeMember = (member: Member) => {
+ const targets = this.state.targets.map(t => t); // cheap clone for mutation
+ const idx = targets.indexOf(member);
+ if (idx >= 0) {
+ targets.splice(idx, 1);
+ this.setState({targets});
+ }
+ };
+
+ _onPaste = async (e) => {
+ // Prevent the text being pasted into the textarea
+ e.preventDefault();
+
+ // Process it as a list of addresses to add instead
+ const text = e.clipboardData.getData("text");
+ const possibleMembers = [
+ // If we can avoid hitting the profile endpoint, we should.
+ ...this.state.recents,
+ ...this.state.suggestions,
+ ...this.state.serverResultsMixin,
+ ...this.state.threepidResultsMixin,
+ ];
+ const toAdd = [];
+ const failed = [];
+ const potentialAddresses = text.split(/[\s,]+/);
+ for (const address of potentialAddresses) {
+ const member = possibleMembers.find(m => m.userId === address);
+ if (member) {
+ toAdd.push(member.user);
+ continue;
+ }
+
+ if (address.indexOf('@') > 0 && Email.looksValid(address)) {
+ toAdd.push(new ThreepidMember(address));
+ continue;
+ }
+
+ if (address[0] !== '@') {
+ failed.push(address); // not a user ID
+ continue;
+ }
+
+ try {
+ const profile = await MatrixClientPeg.get().getProfileInfo(address);
+ const displayName = profile ? profile.displayname : null;
+ const avatarUrl = profile ? profile.avatar_url : null;
+ toAdd.push(new DirectoryMember({
+ user_id: address,
+ display_name: displayName,
+ avatar_url: avatarUrl,
+ }));
+ } catch (e) {
+ console.error("Error looking up profile for " + address);
+ console.error(e);
+ failed.push(address);
+ }
+ }
+
+ if (failed.length > 0) {
+ const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
+ Modal.createTrackedDialog('Invite Paste Fail', '', QuestionDialog, {
+ title: _t('Failed to find the following users'),
+ description: _t(
+ "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s",
+ {csvNames: failed.join(", ")},
+ ),
+ button: _t('OK'),
+ });
+ }
+
+ this.setState({targets: [...this.state.targets, ...toAdd]});
+ };
+
+ _onClickInputArea = (e) => {
+ // Stop the browser from highlighting text
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (this._editorRef && this._editorRef.current) {
+ this._editorRef.current.focus();
+ }
+ };
+
+ _onUseDefaultIdentityServerClick = (e) => {
+ e.preventDefault();
+
+ // Update the IS in account data. Actually using it may trigger terms.
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ useDefaultIdentityServer();
+ this.setState({canUseIdentityServer: true, tryingIdentityServer: false});
+ };
+
+ _onManageSettingsClick = (e) => {
+ e.preventDefault();
+ dis.dispatch({ action: 'view_user_settings' });
+ this._cancel();
+ };
+
+ _renderSection(kind: "recents"|"suggestions") {
+ let sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions;
+ let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown;
+ const showMoreFn = kind === 'recents' ? this._showMoreRecents.bind(this) : this._showMoreSuggestions.bind(this);
+ const lastActive = (m) => kind === 'recents' ? m.lastActive : null;
+ const sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions");
+
+ // Mix in the server results if we have any, but only if we're searching. We track the additional
+ // members separately because we want to filter sourceMembers but trust the mixin arrays to have
+ // the right members in them.
+ let additionalMembers = [];
+ const hasMixins = this.state.serverResultsMixin || this.state.threepidResultsMixin;
+ if (this.state.filterText && hasMixins && kind === 'suggestions') {
+ // We don't want to duplicate members though, so just exclude anyone we've already seen.
+ const notAlreadyExists = (u: Member): boolean => {
+ return !sourceMembers.some(m => m.userId === u.userId)
+ && !additionalMembers.some(m => m.userId === u.userId);
+ };
+
+ const uniqueServerResults = this.state.serverResultsMixin.filter(notAlreadyExists);
+ additionalMembers = additionalMembers.concat(...uniqueServerResults);
+
+ const uniqueThreepidResults = this.state.threepidResultsMixin.filter(notAlreadyExists);
+ additionalMembers = additionalMembers.concat(...uniqueThreepidResults);
+ }
+
+ // Hide the section if there's nothing to filter by
+ if (sourceMembers.length === 0 && additionalMembers.length === 0) return null;
+
+ // Do some simple filtering on the input before going much further. If we get no results, say so.
+ if (this.state.filterText) {
+ const filterBy = this.state.filterText.toLowerCase();
+ sourceMembers = sourceMembers
+ .filter(m => m.user.name.toLowerCase().includes(filterBy) || m.userId.toLowerCase().includes(filterBy));
+
+ if (sourceMembers.length === 0 && additionalMembers.length === 0) {
+ return (
+
+
{sectionName}
+
{_t("No results")}
+
+ );
+ }
+ }
+
+ // Now we mix in the additional members. Again, we presume these have already been filtered. We
+ // also assume they are more relevant than our suggestions and prepend them to the list.
+ sourceMembers = [...additionalMembers, ...sourceMembers];
+
+ // If we're going to hide one member behind 'show more', just use up the space of the button
+ // with the member's tile instead.
+ if (showNum === sourceMembers.length - 1) showNum++;
+
+ // .slice() will return an incomplete array but won't error on us if we go too far
+ const toRender = sourceMembers.slice(0, showNum);
+ const hasMore = toRender.length < sourceMembers.length;
+
+ const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
+ let showMore = null;
+ if (hasMore) {
+ showMore = (
+
+ {_t("Show more")}
+
+ );
+ }
+
+ const tiles = toRender.map(r => (
+ t.userId === r.userId)}
+ />
+ ));
+ return (
+
+ {_t(
+ "If you can't find someone, ask them for their username, or share your " +
+ "username (%(userId)s) or profile link.",
+ {userId},
+ {a: (sub) => {sub}},
+ )}
+
;
}
@@ -311,9 +353,18 @@ export default class Dropdown extends React.Component {
// Note the menu sits inside the AccessibleButton div so it's anchored
// to the input, but overflows below it. The root contains both.
return
-
+
{ currentValue }
-
+
{ menu }
;
@@ -321,6 +372,7 @@ export default class Dropdown extends React.Component {
}
Dropdown.propTypes = {
+ id: PropTypes.string.isRequired,
// The width that the dropdown should be. If specified,
// the dropped-down part of the menu will be set to this
// width.
@@ -340,4 +392,6 @@ Dropdown.propTypes = {
value: PropTypes.string,
// negative for consistency with HTML
disabled: PropTypes.bool,
+ // ARIA label
+ label: PropTypes.string.isRequired,
};
diff --git a/src/components/views/elements/EditableTextContainer.js b/src/components/views/elements/EditableTextContainer.js
index 53b3a454d2..57e1b3d2cd 100644
--- a/src/components/views/elements/EditableTextContainer.js
+++ b/src/components/views/elements/EditableTextContainer.js
@@ -25,13 +25,13 @@ import * as sdk from '../../../index';
* Parent components should supply an 'onSubmit' callback which returns a
* promise; a spinner is shown until the promise resolves.
*
- * The parent can also supply a 'getIntialValue' callback, which works in a
+ * The parent can also supply a 'getInitialValue' callback, which works in a
* similarly asynchronous way. If this is not provided, the initial value is
* taken from the 'initialValue' property.
*/
export default class EditableTextContainer extends React.Component {
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this._unmounted = false;
this.state = {
diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js
index 86f73b68e4..7d5ccbb72d 100644
--- a/src/components/views/elements/Field.js
+++ b/src/components/views/elements/Field.js
@@ -66,10 +66,14 @@ export default class Field extends React.PureComponent {
this.state = {
valid: undefined,
feedback: undefined,
+ focused: false,
};
}
onFocus = (ev) => {
+ this.setState({
+ focused: true,
+ });
this.validate({
focused: true,
});
@@ -88,6 +92,9 @@ export default class Field extends React.PureComponent {
};
onBlur = (ev) => {
+ this.setState({
+ focused: false,
+ });
this.validate({
focused: false,
});
@@ -112,7 +119,9 @@ export default class Field extends React.PureComponent {
allowEmpty,
});
- if (feedback) {
+ // this method is async and so we may have been blurred since the method was called
+ // if we have then hide the feedback as withValidation does
+ if (this.state.focused && feedback) {
this.setState({
valid,
feedback,
diff --git a/src/components/views/elements/Flair.js b/src/components/views/elements/Flair.js
index 0b9dabeae6..0af772466b 100644
--- a/src/components/views/elements/Flair.js
+++ b/src/components/views/elements/Flair.js
@@ -18,9 +18,9 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {MatrixClient} from 'matrix-js-sdk';
import FlairStore from '../../../stores/FlairStore';
import dis from '../../../dispatcher';
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
class FlairAvatar extends React.Component {
@@ -40,7 +40,7 @@ class FlairAvatar extends React.Component {
}
render() {
- const httpUrl = this.context.matrixClient.mxcUrlToHttp(
+ const httpUrl = this.context.mxcUrlToHttp(
this.props.groupProfile.avatarUrl, 16, 16, 'scale', false);
const tooltip = this.props.groupProfile.name ?
`${this.props.groupProfile.name} (${this.props.groupProfile.groupId})`:
@@ -62,9 +62,7 @@ FlairAvatar.propTypes = {
}),
};
-FlairAvatar.contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
-};
+FlairAvatar.contextType = MatrixClientContext;
export default class Flair extends React.Component {
constructor() {
@@ -92,7 +90,7 @@ export default class Flair extends React.Component {
for (const groupId of groups) {
let groupProfile = null;
try {
- groupProfile = await FlairStore.getGroupProfileCached(this.context.matrixClient, groupId);
+ groupProfile = await FlairStore.getGroupProfileCached(this.context, groupId);
} catch (err) {
console.error('Could not get profile for group', groupId, err);
}
@@ -134,6 +132,4 @@ Flair.propTypes = {
groups: PropTypes.arrayOf(PropTypes.string),
};
-Flair.contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
-};
+Flair.contextType = MatrixClientContext;
diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js
index e12a15766e..449eaad7a7 100644
--- a/src/components/views/elements/ImageView.js
+++ b/src/components/views/elements/ImageView.js
@@ -22,10 +22,14 @@ import PropTypes from 'prop-types';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {formatDate} from '../../../DateUtils';
import { _t } from '../../../languageHandler';
+<<<<<<< HEAD
import filesize from "filesize";
import AccessibleButton from "./AccessibleButton";
import Modal from "../../../Modal";
import * as sdk from "../../../index";
+=======
+import {Key} from "../../../Keyboard";
+>>>>>>> develop
export default class ImageView extends React.Component {
static propTypes = {
@@ -60,7 +64,7 @@ export default class ImageView extends React.Component {
}
onKeyDown = (ev) => {
- if (ev.keyCode === 27) { // escape
+ if (ev.key === Key.ESCAPE) {
ev.stopPropagation();
ev.preventDefault();
this.props.onFinished();
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
index 3ebde884bb..cb4e2e4da6 100644
--- a/src/components/views/elements/LanguageDropdown.js
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -21,6 +21,7 @@ import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import * as languageHandler from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
+import { _t } from "../../../languageHandler";
function languageMatchesSearchQuery(query, language) {
if (language.label.toUpperCase().indexOf(query.toUpperCase()) == 0) return true;
@@ -105,9 +106,14 @@ export default class LanguageDropdown extends React.Component {
value = this.props.value || language;
}
- return
{ options }
;
diff --git a/src/components/views/elements/LazyRenderList.js b/src/components/views/elements/LazyRenderList.js
index 0fc0ef6733..7572dced0b 100644
--- a/src/components/views/elements/LazyRenderList.js
+++ b/src/components/views/elements/LazyRenderList.js
@@ -56,14 +56,20 @@ class ItemRange {
}
export default class LazyRenderList extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {};
+ }
+
static getDerivedStateFromProps(props, state) {
const range = LazyRenderList.getVisibleRangeFromProps(props);
const intersectRange = range.expand(props.overflowMargin);
const renderRange = range.expand(props.overflowItems);
- const listHasChangedSize = !!state && renderRange.totalSize() !== state.renderRange.totalSize();
+ const listHasChangedSize = !!state.renderRange && renderRange.totalSize() !== state.renderRange.totalSize();
// only update render Range if the list has shrunk/grown and we need to adjust padding OR
// if the new range + overflowMargin isn't contained by the old anymore
- if (listHasChangedSize || !state || !state.renderRange.contains(intersectRange)) {
+ if (listHasChangedSize || !state.renderRange || !state.renderRange.contains(intersectRange)) {
return {renderRange};
}
return null;
diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js
index d2d7434709..1de857e7fe 100644
--- a/src/components/views/elements/Pill.js
+++ b/src/components/views/elements/Pill.js
@@ -20,12 +20,13 @@ import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import classNames from 'classnames';
-import { Room, RoomMember, MatrixClient } from 'matrix-js-sdk';
+import { Room, RoomMember } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { getDisplayAliasForRoom } from '../../../Rooms';
import FlairStore from "../../../stores/FlairStore";
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
// For URLs of matrix.to links in the timeline which have been reformatted by
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
@@ -66,17 +67,6 @@ const Pill = createReactClass({
isSelected: PropTypes.bool,
},
-
- childContextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient),
- },
-
- getChildContext() {
- return {
- matrixClient: this._matrixClient,
- };
- },
-
getInitialState() {
return {
// ID/alias of the room/user
@@ -127,7 +117,7 @@ const Pill = createReactClass({
}
break;
case Pill.TYPE_USER_MENTION: {
- const localMember = nextProps.room.getMember(resourceId);
+ const localMember = nextProps.room ? nextProps.room.getMember(resourceId) : undefined;
member = localMember;
if (!localMember) {
member = new RoomMember(null, resourceId);
@@ -271,20 +261,22 @@ const Pill = createReactClass({
}
const classes = classNames("mx_Pill", pillClass, {
- "mx_UserPill_me": userId === MatrixClientPeg.get().credentials.userId,
+ "mx_UserPill_me": userId === MatrixClientPeg.get().getUserId(),
"mx_UserPill_selected": this.props.isSelected,
});
if (this.state.pillType) {
- return this.props.inMessage ?
-
- { avatar }
- { linkText }
- :
-
- { avatar }
- { linkText }
- ;
+ return
+ { this.props.inMessage ?
+
+ { avatar }
+ { linkText }
+ :
+
+ { avatar }
+ { linkText }
+ }
+ ;
} else {
// Deliberately render nothing if the URL isn't recognised
return null;
diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js
index 0b766807d5..0e1f230e57 100644
--- a/src/components/views/elements/ReplyThread.js
+++ b/src/components/views/elements/ReplyThread.js
@@ -21,10 +21,11 @@ import {_t} from '../../../languageHandler';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import {wantsDateSeparator} from '../../../DateUtils';
-import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
+import {MatrixEvent} from 'matrix-js-sdk';
import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
import SettingsStore from "../../../settings/SettingsStore";
import escapeHtml from "escape-html";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
// This component does no cycle detection, simply because the only way to make such a cycle would be to
// craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would
@@ -38,12 +39,10 @@ export default class ReplyThread extends React.Component {
permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired,
};
- static contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
- };
+ static contextType = MatrixClientContext;
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.state = {
// The loaded events to be rendered as linear-replies
@@ -187,7 +186,7 @@ export default class ReplyThread extends React.Component {
componentWillMount() {
this.unmounted = false;
- this.room = this.context.matrixClient.getRoom(this.props.parentEv.getRoomId());
+ this.room = this.context.getRoom(this.props.parentEv.getRoomId());
this.room.on("Room.redaction", this.onRoomRedaction);
// same event handler as Room.redaction as for both we just do forceUpdate
this.room.on("Room.redactionCancelled", this.onRoomRedaction);
@@ -259,7 +258,7 @@ export default class ReplyThread extends React.Component {
try {
// ask the client to fetch the event we want using the context API, only interface to do so is to ask
// for a timeline with that event, but once it is loaded we can use findEventById to look up the ev map
- await this.context.matrixClient.getEventTimeline(this.room.getUnfilteredTimelineSet(), eventId);
+ await this.context.getEventTimeline(this.room.getUnfilteredTimelineSet(), eventId);
} catch (e) {
// if it fails catch the error and return early, there's no point trying to find the event in this case.
// Return null as it is falsey and thus should be treated as an error (as the event cannot be resolved).
@@ -300,7 +299,7 @@ export default class ReplyThread extends React.Component {
} else if (this.state.loadedEv) {
const ev = this.state.loadedEv;
const Pill = sdk.getComponent('elements.Pill');
- const room = this.context.matrixClient.getRoom(ev.getRoomId());
+ const room = this.context.getRoom(ev.getRoomId());
header =
{
_t('In reply to', {}, {
diff --git a/src/components/views/elements/RoomAliasField.js b/src/components/views/elements/RoomAliasField.js
index cacecb5005..007bd07a47 100644
--- a/src/components/views/elements/RoomAliasField.js
+++ b/src/components/views/elements/RoomAliasField.js
@@ -20,11 +20,13 @@ import * as sdk from '../../../index';
import withValidation from './Validation';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
+// Controlled form component wrapping Field for inputting a room alias scoped to a given domain
export default class RoomAliasField extends React.PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
domain: PropTypes.string.isRequired,
onChange: PropTypes.func,
+ value: PropTypes.string.isRequired,
};
constructor(props) {
@@ -53,6 +55,7 @@ export default class RoomAliasField extends React.PureComponent {
onValidate={this._onValidate}
placeholder={_t("e.g. my-room")}
onChange={this._onChange}
+ value={this.props.value.substring(1, this.props.value.length - this.props.domain.length - 1)}
maxLength={maxlength} />
);
}
@@ -61,7 +64,7 @@ export default class RoomAliasField extends React.PureComponent {
if (this.props.onChange) {
this.props.onChange(this._asFullAlias(ev.target.value));
}
- }
+ };
_onValidate = async (fieldState) => {
const result = await this._validationRules(fieldState);
diff --git a/src/components/views/elements/SyntaxHighlight.js b/src/components/views/elements/SyntaxHighlight.js
index 82b5ae572c..bce65cf1a9 100644
--- a/src/components/views/elements/SyntaxHighlight.js
+++ b/src/components/views/elements/SyntaxHighlight.js
@@ -24,8 +24,8 @@ export default class SyntaxHighlight extends React.Component {
children: PropTypes.node,
};
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this._ref = this._ref.bind(this);
}
diff --git a/src/components/views/elements/TagTile.js b/src/components/views/elements/TagTile.js
index 28930bbabe..d4a9d836aa 100644
--- a/src/components/views/elements/TagTile.js
+++ b/src/components/views/elements/TagTile.js
@@ -20,17 +20,21 @@ import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
+<<<<<<< HEAD
import { MatrixClient } from 'matrix-js-sdk';
import * as sdk from '../../../index';
+=======
+import sdk from '../../../index';
+>>>>>>> develop
import dis from '../../../dispatcher';
-import {_t} from '../../../languageHandler';
import { isOnlyCtrlOrCmdIgnoreShiftKeyEvent } from '../../../Keyboard';
import * as FormattingUtils from '../../../utils/FormattingUtils';
import FlairStore from '../../../stores/FlairStore';
import GroupStore from '../../../stores/GroupStore';
import TagOrderStore from '../../../stores/TagOrderStore';
-import {ContextMenu, ContextMenuButton, toRightOf} from "../../structures/ContextMenu";
+import {ContextMenu, toRightOf} from "../../structures/ContextMenu";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
// A class for a child of TagPanel (possibly wrapped in a DNDTagTile) that represents
// a thing to click on for the user to filter the visible rooms in the RoomList to:
@@ -46,8 +50,8 @@ export default createReactClass({
tag: PropTypes.string,
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+ statics: {
+ contextType: MatrixClientContext,
},
getInitialState() {
@@ -56,6 +60,8 @@ export default createReactClass({
hover: false,
// The profile data of the group if this.props.tag is a group ID
profile: null,
+ // Whether or not the context menu is open
+ menuDisplayed: false,
};
},
@@ -81,7 +87,7 @@ export default createReactClass({
_onFlairStoreUpdated() {
if (this.unmounted) return;
FlairStore.getGroupProfileCached(
- this.context.matrixClient,
+ this.context,
this.props.tag,
).then((profile) => {
if (this.unmounted) return;
@@ -112,12 +118,10 @@ export default createReactClass({
},
onMouseOver: function() {
- console.log("DEBUG onMouseOver");
this.setState({hover: true});
},
onMouseOut: function() {
- console.log("DEBUG onMouseOut");
this.setState({hover: false});
},
@@ -140,12 +144,11 @@ export default createReactClass({
render: function() {
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
- const Tooltip = sdk.getComponent('elements.Tooltip');
const profile = this.state.profile || {};
const name = profile.name || this.props.tag;
const avatarHeight = 40;
- const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
+ const httpUrl = profile.avatarUrl ? this.context.mxcUrlToHttp(
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
) : null;
@@ -164,9 +167,6 @@ export default createReactClass({
badgeElement = (
{FormattingUtils.formatCount(badge.count)}
);
}
- const tip = this.state.hover ?
- :
- ;
// FIXME: this ought to use AccessibleButton for a11y but that causes onMouseOut/onMouseOver to fire too much
const contextButton = this.state.hover || this.state.menuDisplayed ?
diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js
index 9638fbe230..4eacbed6a9 100644
--- a/src/components/views/rooms/BasicMessageComposer.js
+++ b/src/components/views/rooms/BasicMessageComposer.js
@@ -34,11 +34,11 @@ import {parsePlainTextMessage} from '../../../editor/deserialize';
import {renderModel} from '../../../editor/render';
import {Room} from 'matrix-js-sdk';
import TypingStore from "../../../stores/TypingStore";
-import EMOJIBASE from 'emojibase-data/en/compact.json';
import SettingsStore from "../../../settings/SettingsStore";
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
import * as sdk from '../../../index';
import {Key} from "../../../Keyboard";
+import {EMOTICON_TO_EMOJI} from "../../../emoji";
const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$');
@@ -80,8 +80,8 @@ export default class BasicMessageEditor extends React.Component {
initialCaret: PropTypes.object, // See DocumentPosition in editor/model.js
};
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.state = {
autoComplete: null,
};
@@ -108,7 +108,8 @@ export default class BasicMessageEditor extends React.Component {
const emoticonMatch = REGEX_EMOTICON_WHITESPACE.exec(range.text);
if (emoticonMatch) {
const query = emoticonMatch[1].toLowerCase().replace("-", "");
- const data = EMOJIBASE.find(e => e.emoticon ? e.emoticon.toLowerCase() === query : false);
+ const data = EMOTICON_TO_EMOJI.get(query);
+
if (data) {
const {partCreator} = model;
const hasPrecedingSpace = emoticonMatch[0][0] === " ";
diff --git a/src/components/views/rooms/E2EIcon.js b/src/components/views/rooms/E2EIcon.js
index d6baa30c8e..545d1fd7ed 100644
--- a/src/components/views/rooms/E2EIcon.js
+++ b/src/components/views/rooms/E2EIcon.js
@@ -17,24 +17,62 @@ limitations under the License.
import classNames from 'classnames';
import { _t } from '../../../languageHandler';
import AccessibleButton from '../elements/AccessibleButton';
+import SettingsStore from '../../../settings/SettingsStore';
export default function(props) {
+ const { isUser } = props;
+ const isNormal = props.status === "normal";
const isWarning = props.status === "warning";
const isVerified = props.status === "verified";
const e2eIconClasses = classNames({
mx_E2EIcon: true,
mx_E2EIcon_warning: isWarning,
+ mx_E2EIcon_normal: isNormal,
mx_E2EIcon_verified: isVerified,
}, props.className);
let e2eTitle;
- if (isWarning) {
- e2eTitle = props.isUser ?
- _t("Some devices for this user are not trusted") :
- _t("Some devices in this encrypted room are not trusted");
- } else if (isVerified) {
- e2eTitle = props.isUser ?
- _t("All devices for this user are trusted") :
- _t("All devices in this encrypted room are trusted");
+
+ const crossSigning = SettingsStore.isFeatureEnabled("feature_cross_signing");
+ if (crossSigning && isUser) {
+ if (isWarning) {
+ e2eTitle = _t(
+ "This user has not verified all of their devices.",
+ );
+ } else if (isNormal) {
+ e2eTitle = _t(
+ "You have not verified this user. " +
+ "This user has verified all of their devices.",
+ );
+ } else if (isVerified) {
+ e2eTitle = _t(
+ "You have verified this user. " +
+ "This user has verified all of their devices.",
+ );
+ }
+ } else if (crossSigning && !isUser) {
+ if (isWarning) {
+ e2eTitle = _t(
+ "Some users in this encrypted room are not verified by you or " +
+ "they have not verified their own devices.",
+ );
+ } else if (isVerified) {
+ e2eTitle = _t(
+ "All users in this encrypted room are verified by you and " +
+ "they have verified their own devices.",
+ );
+ }
+ } else if (!crossSigning && isUser) {
+ if (isWarning) {
+ e2eTitle = _t("Some devices for this user are not trusted");
+ } else if (isVerified) {
+ e2eTitle = _t("All devices for this user are trusted");
+ }
+ } else if (!crossSigning && !isUser) {
+ if (isWarning) {
+ e2eTitle = _t("Some devices in this encrypted room are not trusted");
+ } else if (isVerified) {
+ e2eTitle = _t("All devices in this encrypted room are trusted");
+ }
}
let style = null;
diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js
index d2cd3cc4d3..1d01f68551 100644
--- a/src/components/views/rooms/EditMessageComposer.js
+++ b/src/components/views/rooms/EditMessageComposer.js
@@ -26,11 +26,11 @@ import {findEditableEvent} from '../../../utils/EventUtils';
import {parseEvent} from '../../../editor/deserialize';
import {PartCreator} from '../../../editor/parts';
import EditorStateTransfer from '../../../utils/EditorStateTransfer';
-import {MatrixClient} from 'matrix-js-sdk';
import classNames from 'classnames';
import {EventStatus} from 'matrix-js-sdk';
import BasicMessageComposer from "./BasicMessageComposer";
import {Key} from "../../../Keyboard";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
function _isReply(mxEvent) {
const relatesTo = mxEvent.getContent()["m.relates_to"];
@@ -105,12 +105,10 @@ export default class EditMessageComposer extends React.Component {
editState: PropTypes.instanceOf(EditorStateTransfer).isRequired,
};
- static contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
- };
+ static contextType = MatrixClientContext;
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.model = null;
this._editorRef = null;
@@ -124,7 +122,7 @@ export default class EditMessageComposer extends React.Component {
};
_getRoom() {
- return this.context.matrixClient.getRoom(this.props.editState.getEvent().getRoomId());
+ return this.context.getRoom(this.props.editState.getEvent().getRoomId());
}
_onKeyDown = (event) => {
@@ -190,7 +188,7 @@ export default class EditMessageComposer extends React.Component {
if (this._isContentModified(newContent)) {
const roomId = editedEvent.getRoomId();
this._cancelPreviousPendingEdit();
- this.context.matrixClient.sendMessage(roomId, editContent);
+ this.context.sendMessage(roomId, editContent);
}
// close the event editing and focus composer
@@ -205,7 +203,7 @@ export default class EditMessageComposer extends React.Component {
previousEdit.status === EventStatus.QUEUED ||
previousEdit.status === EventStatus.NOT_SENT
)) {
- this.context.matrixClient.cancelPendingEvent(previousEdit);
+ this.context.cancelPendingEvent(previousEdit);
}
}
@@ -232,7 +230,7 @@ export default class EditMessageComposer extends React.Component {
_createEditorModel() {
const {editState} = this.props;
const room = this._getRoom();
- const partCreator = new PartCreator(room, this.context.matrixClient);
+ const partCreator = new PartCreator(room, this.context);
let parts;
if (editState.hasEditorState()) {
// if restoring state from a previous editor,
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 73b2877775..cc00810188 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -1,8 +1,8 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd
-Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -23,16 +23,29 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from "classnames";
import { _t, _td } from '../../../languageHandler';
+<<<<<<< HEAD
import * as TextForEvent from "../../../TextForEvent";
import Modal from "../../../Modal";
import * as sdk from "../../../index";
+=======
+
+const sdk = require('../../../index');
+const TextForEvent = require('../../../TextForEvent');
+
+>>>>>>> develop
import dis from '../../../dispatcher';
import SettingsStore from "../../../settings/SettingsStore";
-import {EventStatus, MatrixClient} from 'matrix-js-sdk';
+import {EventStatus} from 'matrix-js-sdk';
import {formatTime} from "../../../DateUtils";
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import {ALL_RULE_TYPES} from "../../../mjolnir/BanList";
+<<<<<<< HEAD
import * as ObjectUtils from "../../../ObjectUtils";
+=======
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
+
+const ObjectUtils = require('../../../ObjectUtils');
+>>>>>>> develop
const eventTileTypes = {
'm.room.message': 'messages.MessageEvent',
@@ -218,8 +231,8 @@ export default createReactClass({
};
},
- contextTypes: {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+ statics: {
+ contextType: MatrixClientContext,
},
componentWillMount: function() {
@@ -233,7 +246,7 @@ export default createReactClass({
componentDidMount: function() {
this._suppressReadReceiptAnimation = false;
- const client = this.context.matrixClient;
+ const client = this.context;
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
this.props.mxEvent.on("Event.decrypted", this._onDecrypted);
if (this.props.showReactions) {
@@ -258,7 +271,7 @@ export default createReactClass({
},
componentWillUnmount: function() {
- const client = this.context.matrixClient;
+ const client = this.context;
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted);
if (this.props.showReactions) {
@@ -287,7 +300,7 @@ export default createReactClass({
return;
}
- const verified = await this.context.matrixClient.isEventSenderVerified(mxEvent);
+ const verified = await this.context.isEventSenderVerified(mxEvent);
this.setState({
verified: verified,
}, () => {
@@ -345,11 +358,11 @@ export default createReactClass({
},
shouldHighlight: function() {
- const actions = this.context.matrixClient.getPushActionsForEvent(this.props.mxEvent);
+ const actions = this.context.getPushActionsForEvent(this.props.mxEvent);
if (!actions || !actions.tweaks) { return false; }
// don't show self-highlights from another of our clients
- if (this.props.mxEvent.getSender() === this.context.matrixClient.credentials.userId) {
+ if (this.props.mxEvent.getSender() === this.context.credentials.userId) {
return false;
}
@@ -438,15 +451,6 @@ export default createReactClass({
});
},
- onCryptoClick: function(e) {
- const event = this.props.mxEvent;
-
- Modal.createTrackedDialogAsync('Encrypted Event Dialog', '',
- import('../../../async-components/views/dialogs/EncryptedEventDialog'),
- {event},
- );
- },
-
onRequestKeysClick: function() {
this.setState({
// Indicate in the UI that the keys have been requested (this is expected to
@@ -457,7 +461,7 @@ export default createReactClass({
// Cancel any outgoing key request for this event and resend it. If a response
// is received for the request with the required keys, the event could be
// decrypted successfully.
- this.context.matrixClient.cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
+ this.context.cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
},
onPermalinkClicked: function(e) {
@@ -474,11 +478,10 @@ export default createReactClass({
_renderE2EPadlock: function() {
const ev = this.props.mxEvent;
- const props = {onClick: this.onCryptoClick};
// event could not be decrypted
if (ev.getContent().msgtype === 'm.bad.encrypted') {
- return ;
+ return ;
}
// event is encrypted, display padlock corresponding to whether or not it is verified
@@ -486,11 +489,11 @@ export default createReactClass({
if (this.state.verified) {
return; // no icon for verified
} else {
- return ();
+ return ();
}
}
- if (this.context.matrixClient.isRoomEncrypted(ev.getRoomId())) {
+ if (this.context.isRoomEncrypted(ev.getRoomId())) {
// else if room is encrypted
// and event is being encrypted or is not_sent (Unknown Devices/Network Error)
if (ev.status === EventStatus.ENCRYPTING) {
@@ -503,7 +506,7 @@ export default createReactClass({
return; // we expect this to be unencrypted
}
// if the event is not encrypted, but it's an e2e room, show the open padlock
- return ;
+ return ;
}
// no padlock needed
@@ -737,7 +740,7 @@ export default createReactClass({
switch (this.props.tileShape) {
case 'notif': {
- const room = this.context.matrixClient.getRoom(this.props.mxEvent.getRoomId());
+ const room = this.context.getRoom(this.props.mxEvent.getRoomId());
return (
@@ -915,7 +918,6 @@ class E2ePadlock extends React.Component {
static propTypes = {
icon: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
- onClick: PropTypes.func,
};
constructor() {
@@ -926,10 +928,6 @@ class E2ePadlock extends React.Component {
};
}
- onClick = (e) => {
- if (this.props.onClick) this.props.onClick(e);
- };
-
onHoverStart = () => {
this.setState({hover: true});
};
diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js
index 7e48071fe5..c7c954dc9f 100644
--- a/src/components/views/rooms/ForwardMessage.js
+++ b/src/components/views/rooms/ForwardMessage.js
@@ -20,7 +20,7 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher';
-import { KeyCode } from '../../../Keyboard';
+import {Key} from '../../../Keyboard';
export default createReactClass({
@@ -52,8 +52,8 @@ export default createReactClass({
},
_onKeyDown: function(ev) {
- switch (ev.keyCode) {
- case KeyCode.ESCAPE:
+ switch (ev.key) {
+ case Key.ESCAPE:
this.props.onCancelClick();
break;
}
diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js
index 548c722630..3c9118aacb 100644
--- a/src/components/views/rooms/LinkPreviewWidget.js
+++ b/src/components/views/rooms/LinkPreviewWidget.js
@@ -18,12 +18,17 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
-import { linkifyElement } from '../../../HtmlUtils';
+import { AllHtmlEntities } from 'html-entities';
+import {linkifyElement} from '../../../HtmlUtils';
import SettingsStore from "../../../settings/SettingsStore";
+<<<<<<< HEAD
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import * as sdk from "../../../index";
import Modal from "../../../Modal";
import * as ImageUtils from "../../../ImageUtils";
+=======
+import { _t } from "../../../languageHandler";
+>>>>>>> develop
export default createReactClass({
displayName: 'LinkPreviewWidget',
@@ -125,6 +130,11 @@ export default createReactClass({
;
}
+ // The description includes &-encoded HTML entities, we decode those as React treats the thing as an
+ // opaque string. This does not allow any HTML to be injected into the DOM.
+ const description = AllHtmlEntities.decode(p["og:description"] || "");
+
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return (
;
diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js
index 32025187d1..86199d7535 100644
--- a/src/components/views/rooms/MemberList.js
+++ b/src/components/views/rooms/MemberList.js
@@ -32,7 +32,15 @@ const INITIAL_LOAD_NUM_MEMBERS = 30;
const INITIAL_LOAD_NUM_INVITED = 5;
const SHOW_MORE_INCREMENT = 100;
+<<<<<<< HEAD
export default createReactClass({
+=======
+// Regex applied to filter our punctuation in member names before applying sort, to fuzzy it a little
+// matches all ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
+const SORT_REGEX = /[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/g;
+
+module.exports = createReactClass({
+>>>>>>> develop
displayName: 'MemberList',
getInitialState: function() {
@@ -336,10 +344,13 @@ export default createReactClass({
}
// Fourth by name (alphabetical)
- const nameA = memberA.name[0] === '@' ? memberA.name.substr(1) : memberA.name;
- const nameB = memberB.name[0] === '@' ? memberB.name.substr(1) : memberB.name;
+ const nameA = (memberA.name[0] === '@' ? memberA.name.substr(1) : memberA.name).replace(SORT_REGEX, "");
+ const nameB = (memberB.name[0] === '@' ? memberB.name.substr(1) : memberB.name).replace(SORT_REGEX, "");
// console.log(`Comparing userA_name=${nameA} against userB_name=${nameB} - returning`);
- return nameA.localeCompare(nameB);
+ return nameA.localeCompare(nameB, {
+ ignorePunctuation: true,
+ sensitivity: "base",
+ });
},
onSearchQueryChanged: function(searchQuery) {
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index 8a92175f77..8d36f02d02 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -107,8 +107,8 @@ class UploadButton extends React.Component {
roomId: PropTypes.string.isRequired,
}
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.onUploadClick = this.onUploadClick.bind(this);
this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
@@ -165,8 +165,8 @@ class UploadButton extends React.Component {
}
export default class MessageComposer extends React.Component {
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.onInputStateChanged = this.onInputStateChanged.bind(this);
this.onEvent = this.onEvent.bind(this);
this._onRoomStateEvents = this._onRoomStateEvents.bind(this);
diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js
index 5c80fbb737..7704291631 100644
--- a/src/components/views/rooms/ReadReceiptMarker.js
+++ b/src/components/views/rooms/ReadReceiptMarker.js
@@ -15,8 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React from 'react';
-import ReactDOM from 'react-dom';
+import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import('../../../VelocityBounce');
@@ -88,6 +87,10 @@ export default createReactClass({
};
},
+ UNSAFE_componentWillMount: function() {
+ this._avatar = createRef();
+ },
+
componentWillUnmount: function() {
// before we remove the rr, store its location in the map, so that if
// it reappears, it can be animated from the right place.
@@ -103,7 +106,7 @@ export default createReactClass({
return;
}
- const avatarNode = ReactDOM.findDOMNode(this);
+ const avatarNode = this._avatar.current;
rrInfo.top = avatarNode.offsetTop;
rrInfo.left = avatarNode.offsetLeft;
rrInfo.parent = avatarNode.offsetParent;
@@ -123,7 +126,7 @@ export default createReactClass({
oldTop = oldInfo.top + oldInfo.parent.getBoundingClientRect().top;
}
- const newElement = ReactDOM.findDOMNode(this);
+ const newElement = this._avatar.current;
let startTopOffset;
if (!newElement.offsetParent) {
// this seems to happen sometimes for reasons I don't understand
@@ -173,7 +176,7 @@ export default createReactClass({
render: function() {
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
if (this.state.suppressDisplay) {
- return ;
+ return ;
}
const style = {
@@ -213,6 +216,7 @@ export default createReactClass({
style={style}
title={title}
onClick={this.props.onClick}
+ inputRef={this._avatar}
/>
);
diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js
index a0d56b1116..b28494c65a 100644
--- a/src/components/views/rooms/ReplyPreview.js
+++ b/src/components/views/rooms/ReplyPreview.js
@@ -35,8 +35,8 @@ export default class ReplyPreview extends React.Component {
permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired,
};
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.unmounted = false;
this.state = {
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index dd9258f1bf..b33e3daf77 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -21,6 +21,7 @@ import React from "react";
import ReactDOM from "react-dom";
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
+import utils from "matrix-js-sdk/lib/utils";
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import rate_limited_func from "../../../ratelimitedfunc";
@@ -588,10 +589,17 @@ export default createReactClass({
_applySearchFilter: function(list, filter) {
if (filter === "") return list;
const lcFilter = filter.toLowerCase();
+ // apply toLowerCase before and after removeHiddenChars because different rules get applied
+ // e.g M -> M but m -> n, yet some unicode homoglyphs come out as uppercase, e.g 𝚮 -> H
+ const fuzzyFilter = utils.removeHiddenChars(lcFilter).toLowerCase();
// case insensitive if room name includes filter,
// or if starts with `#` and one of room's aliases starts with filter
- return list.filter((room) => (room.name && room.name.toLowerCase().includes(lcFilter)) ||
- (filter[0] === '#' && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter))));
+ return list.filter((room) => {
+ if (filter[0] === "#" && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter))) {
+ return true;
+ }
+ return room.name && utils.removeHiddenChars(room.name.toLowerCase()).toLowerCase().includes(fuzzyFilter);
+ });
},
_handleCollapsedState: function(key, collapsed) {
@@ -627,7 +635,6 @@ export default createReactClass({
const defaultProps = {
collapsed: this.props.collapsed,
isFiltered: !!this.props.searchFilter,
- incomingCall: this.state.incomingCall,
};
subListsProps.forEach((p) => {
@@ -640,7 +647,7 @@ export default createReactClass({
}));
return subListsProps.reduce((components, props, i) => {
- props = Object.assign({}, defaultProps, props);
+ props = {...defaultProps, ...props};
const isLast = i === subListsProps.length - 1;
const len = props.list.length + (props.extraTiles ? props.extraTiles.length : 0);
const {key, label, onHeaderClick, ...otherProps} = props;
@@ -651,12 +658,12 @@ export default createReactClass({
onHeaderClick(collapsed);
}
};
- let startAsHidden = props.startAsHidden || this.collapsedState[chosenKey];
+ const startAsHidden = props.startAsHidden || this.collapsedState[chosenKey];
this._layoutSections.push({
id: chosenKey,
count: len,
});
- let subList = (
{setupCaption}
-
{ _t("Not now") }
-
-
+
{ _t("Don't ask me again") }
-
+
);
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js
index 16db17a9b7..77a2301296 100644
--- a/src/components/views/rooms/RoomTile.js
+++ b/src/components/views/rooms/RoomTile.js
@@ -68,11 +68,6 @@ export default createReactClass({
});
},
- _isDirectMessageRoom: function(roomId) {
- const dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId);
- return Boolean(dmRooms);
- },
-
_shouldShowStatusMessage() {
if (!SettingsStore.isFeatureEnabled("feature_custom_status")) {
return false;
@@ -371,8 +366,11 @@ export default createReactClass({
let ariaLabel = name;
+ const dmUserId = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId);
+
let dmIndicator;
- if (this._isDirectMessageRoom(this.props.room.roomId)) {
+ let dmOnline;
+ if (dmUserId) {
dmIndicator = ;
+
+ const { room } = this.props;
+ const member = room.getMember(dmUserId);
+ if (member && member.membership === "join" && room.getJoinedMemberCount() === 2) {
+ const UserOnlineDot = sdk.getComponent('rooms.UserOnlineDot');
+ dmOnline = ;
+ }
}
// The following labels are written in such a fashion to increase screen reader efficiency (speed).
@@ -428,6 +433,7 @@ export default createReactClass({
{ label }
{ subtextLabel }
+ { dmOnline }
{ contextMenuButton }
{ badge }
diff --git a/src/components/views/rooms/SearchBar.js b/src/components/views/rooms/SearchBar.js
index e89a5c72c1..1f9ec87163 100644
--- a/src/components/views/rooms/SearchBar.js
+++ b/src/components/views/rooms/SearchBar.js
@@ -19,6 +19,7 @@ import createReactClass from 'create-react-class';
import AccessibleButton from "../elements/AccessibleButton";
import classNames from "classnames";
import { _t } from '../../../languageHandler';
+import {Key} from "../../../Keyboard";
export default createReactClass({
displayName: 'SearchBar',
@@ -42,11 +43,13 @@ export default createReactClass({
},
onSearchChange: function(e) {
- if (e.keyCode === 13) { // on enter...
- this.onSearch();
- }
- if (e.keyCode === 27) { // escape...
- this.props.onCancelClick();
+ switch (e.key) {
+ case Key.ENTER:
+ this.onSearch();
+ break;
+ case Key.ESCAPE:
+ this.props.onCancelClick();
+ break;
}
},
diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js
index 3b4f3122c0..c11d940331 100644
--- a/src/components/views/rooms/SendMessageComposer.js
+++ b/src/components/views/rooms/SendMessageComposer.js
@@ -26,7 +26,6 @@ import {
unescapeMessage,
} from '../../../editor/serialize';
import {CommandPartCreator} from '../../../editor/parts';
-import {MatrixClient} from 'matrix-js-sdk';
import BasicMessageComposer from "./BasicMessageComposer";
import ReplyPreview from "./ReplyPreview";
import RoomViewStore from '../../../stores/RoomViewStore';
@@ -40,6 +39,7 @@ import Modal from '../../../Modal';
import {_t, _td} from '../../../languageHandler';
import ContentMessages from '../../../ContentMessages';
import {Key} from "../../../Keyboard";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent);
@@ -89,12 +89,10 @@ export default class SendMessageComposer extends React.Component {
permalinkCreator: PropTypes.object.isRequired,
};
- static contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
- };
+ static contextType = MatrixClientContext;
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.model = null;
this._editorRef = null;
this.currentlyComposedEditorState = null;
@@ -245,7 +243,7 @@ export default class SendMessageComposer extends React.Component {
const isReply = !!RoomViewStore.getQuotingEvent();
const {roomId} = this.props.room;
const content = createMessageContent(this.model, this.props.permalinkCreator);
- this.context.matrixClient.sendMessage(roomId, content);
+ this.context.sendMessage(roomId, content);
if (isReply) {
// Clear reply_to_event as we put the message into the queue
// if the send fails, retry will handle resending.
@@ -273,7 +271,7 @@ export default class SendMessageComposer extends React.Component {
}
componentWillMount() {
- const partCreator = new CommandPartCreator(this.props.room, this.context.matrixClient);
+ const partCreator = new CommandPartCreator(this.props.room, this.context);
const parts = this._restoreStoredEditorState(partCreator) || [];
this.model = new EditorModel(parts, partCreator);
this.dispatcherRef = dis.register(this.onAction);
@@ -361,7 +359,7 @@ export default class SendMessageComposer extends React.Component {
// from Finder) but more images copied from a different website
// / word processor etc.
ContentMessages.sharedInstance().sendContentListToRoom(
- Array.from(clipboardData.files), this.props.room.roomId, this.context.matrixClient,
+ Array.from(clipboardData.files), this.props.room.roomId, this.context,
);
}
}
diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js
index 67ee0d183c..07e9be7a9a 100644
--- a/src/components/views/rooms/Stickerpicker.js
+++ b/src/components/views/rooms/Stickerpicker.js
@@ -315,8 +315,8 @@ export default class Stickerpicker extends React.Component {
// Offset the chevron location, which is relative to the left of the context menu
// (10 = offset when context menu would not be displayed off viewport)
- // (8 = value required in practice (possibly 10 - 2 where the 2 = context menu borders)
- const stickerPickerChevronOffset = Math.max(10, 8 + window.pageXOffset + buttonRect.left - x);
+ // (2 = context menu borders)
+ const stickerPickerChevronOffset = Math.max(10, 2 + window.pageXOffset + buttonRect.left - x);
const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
diff --git a/src/components/views/rooms/UserOnlineDot.js b/src/components/views/rooms/UserOnlineDot.js
new file mode 100644
index 0000000000..426dd1bf64
--- /dev/null
+++ b/src/components/views/rooms/UserOnlineDot.js
@@ -0,0 +1,48 @@
+/*
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+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, {useContext, useEffect, useMemo, useState, useCallback} from "react";
+import PropTypes from "prop-types";
+
+import {useEventEmitter} from "../../../hooks/useEventEmitter";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
+
+const UserOnlineDot = ({userId}) => {
+ const cli = useContext(MatrixClientContext);
+ const user = useMemo(() => cli.getUser(userId), [cli, userId]);
+
+ const [isOnline, setIsOnline] = useState(false);
+
+ // Recheck if the user or client changes
+ useEffect(() => {
+ setIsOnline(user && (user.currentlyActive || user.presence === "online"));
+ }, [cli, user]);
+ // Recheck also if we receive a User.currentlyActive event
+ const currentlyActiveHandler = useCallback((ev) => {
+ const content = ev.getContent();
+ setIsOnline(content.currently_active || content.presence === "online");
+ }, []);
+ useEventEmitter(user, "User.currentlyActive", currentlyActiveHandler);
+ useEventEmitter(user, "User.presence", currentlyActiveHandler);
+
+ return isOnline ? : null;
+};
+
+UserOnlineDot.propTypes = {
+ userId: PropTypes.string.isRequired,
+};
+
+export default UserOnlineDot;
diff --git a/src/components/views/settings/AvatarSetting.js b/src/components/views/settings/AvatarSetting.js
new file mode 100644
index 0000000000..51ef3a4302
--- /dev/null
+++ b/src/components/views/settings/AvatarSetting.js
@@ -0,0 +1,78 @@
+/*
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+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, {useCallback} from "react";
+import PropTypes from "prop-types";
+
+import sdk from "../../../index";
+import {_t} from "../../../languageHandler";
+import Modal from "../../../Modal";
+
+const AvatarSetting = ({avatarUrl, avatarAltText, avatarName, uploadAvatar, removeAvatar}) => {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+
+ const openImageView = useCallback(() => {
+ const ImageView = sdk.getComponent("elements.ImageView");
+ Modal.createDialog(ImageView, {
+ src: avatarUrl,
+ name: avatarName,
+ }, "mx_Dialog_lightbox");
+ }, [avatarUrl, avatarName]);
+
+ let avatarElement = ;
+ if (avatarUrl) {
+ avatarElement = (
+
+ );
+ }
+
+ let uploadAvatarBtn;
+ if (uploadAvatar) {
+ // insert an empty div to be the host for a css mask containing the upload.svg
+ uploadAvatarBtn =
+
+
diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js
new file mode 100644
index 0000000000..3022885701
--- /dev/null
+++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js
@@ -0,0 +1,166 @@
+/*
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+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 PropTypes from 'prop-types';
+import {_t} from "../../../../../languageHandler";
+import MatrixClientPeg from "../../../../../MatrixClientPeg";
+import Pill from "../../../elements/Pill";
+import {makeUserPermalink} from "../../../../../utils/permalinks/Permalinks";
+import BaseAvatar from "../../../avatars/BaseAvatar";
+import { ContentRepo } from "matrix-js-sdk";
+
+const BRIDGE_EVENT_TYPES = [
+ "uk.half-shot.bridge",
+ // m.bridge
+];
+
+export default class BridgeSettingsTab extends React.Component {
+ static propTypes = {
+ roomId: PropTypes.string.isRequired,
+ };
+
+ _renderBridgeCard(event, room) {
+ const content = event.getContent();
+ if (!content || !content.channel || !content.protocol) {
+ return null;
+ }
+ const { channel, network } = content;
+ const protocolName = content.protocol.displayname || content.protocol.id;
+ const channelName = channel.displayname || channel.id;
+ const networkName = network ? network.displayname || network.id : protocolName;
+
+ let creator = null;
+ if (content.creator) {
+ creator =
{ _t("This bridge was provisioned by ", {}, {
+ user: ,
+ })}
;
+ }
+
+ const bot = (
{_t("This bridge is managed by .", {}, {
+ user: ,
+ })}
);
}
diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.js
index f6923025d7..6d53c23743 100644
--- a/src/components/views/toasts/VerificationRequestToast.js
+++ b/src/components/views/toasts/VerificationRequestToast.js
@@ -90,7 +90,9 @@ export default class VerificationRequestToast extends React.PureComponent {
const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS);
const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog');
- Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {verifier});
+ Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
+ verifier,
+ }, null, /* priority = */ false, /* static = */ true);
};
render() {
diff --git a/src/utils/withLegacyMatrixClient.js b/src/contexts/MatrixClientContext.js
similarity index 51%
rename from src/utils/withLegacyMatrixClient.js
rename to src/contexts/MatrixClientContext.js
index af6a930a88..54a23ca132 100644
--- a/src/utils/withLegacyMatrixClient.js
+++ b/src/contexts/MatrixClientContext.js
@@ -14,18 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React from "react";
-import PropTypes from "prop-types";
-import {MatrixClient} from "matrix-js-sdk";
+import { createContext } from "react";
-// Higher Order Component to allow use of legacy MatrixClient React Context
-// in Functional Components which do not otherwise support legacy React Contexts
-export default (Component) => class extends React.PureComponent {
- static contextTypes = {
- matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
- };
-
- render() {
- return ;
- }
-};
+const MatrixClientContext = createContext(undefined);
+MatrixClientContext.displayName = "MatrixClientContext";
+export default MatrixClientContext;
diff --git a/src/contexts/RoomContext.js b/src/contexts/RoomContext.js
new file mode 100644
index 0000000000..8613be195c
--- /dev/null
+++ b/src/contexts/RoomContext.js
@@ -0,0 +1,25 @@
+/*
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+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 { createContext } from "react";
+
+const RoomContext = createContext({
+ canReact: undefined,
+ canReply: undefined,
+ room: undefined,
+});
+RoomContext.displayName = "RoomContext";
+export default RoomContext;
diff --git a/src/emoji.js b/src/emoji.js
new file mode 100644
index 0000000000..125864e381
--- /dev/null
+++ b/src/emoji.js
@@ -0,0 +1,91 @@
+/*
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+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 EMOJIBASE from 'emojibase-data/en/compact.json';
+
+// The unicode is stored without the variant selector
+const UNICODE_TO_EMOJI = new Map(); // not exported as gets for it are handled by getEmojiFromUnicode
+export const EMOTICON_TO_EMOJI = new Map();
+export const SHORTCODE_TO_EMOJI = new Map();
+
+export const getEmojiFromUnicode = unicode => UNICODE_TO_EMOJI.get(stripVariation(unicode));
+
+const EMOJIBASE_GROUP_ID_TO_CATEGORY = [
+ "people", // smileys
+ "people", // actually people
+ "control", // modifiers and such, not displayed in picker
+ "nature",
+ "foods",
+ "places",
+ "activity",
+ "objects",
+ "symbols",
+ "flags",
+];
+
+export const DATA_BY_CATEGORY = {
+ "people": [],
+ "nature": [],
+ "foods": [],
+ "places": [],
+ "activity": [],
+ "objects": [],
+ "symbols": [],
+ "flags": [],
+};
+
+// Store various mappings from unicode/emoticon/shortcode to the Emoji objects
+EMOJIBASE.forEach(emoji => {
+ const categoryId = EMOJIBASE_GROUP_ID_TO_CATEGORY[emoji.group];
+ if (DATA_BY_CATEGORY.hasOwnProperty(categoryId)) {
+ DATA_BY_CATEGORY[categoryId].push(emoji);
+ }
+ // This is used as the string to match the query against when filtering emojis
+ emoji.filterString = `${emoji.annotation}\n${emoji.shortcodes.join('\n')}}\n${emoji.emoticon || ''}`.toLowerCase();
+
+ // Add mapping from unicode to Emoji object
+ // The 'unicode' field that we use in emojibase has either
+ // VS15 or VS16 appended to any characters that can take
+ // variation selectors. Which one it appends depends
+ // on whether emojibase considers their type to be 'text' or
+ // 'emoji'. We therefore strip any variation chars from strings
+ // both when building the map and when looking up.
+ UNICODE_TO_EMOJI.set(stripVariation(emoji.unicode), emoji);
+
+ if (emoji.emoticon) {
+ // Add mapping from emoticon to Emoji object
+ EMOTICON_TO_EMOJI.set(emoji.emoticon, emoji);
+ }
+
+ if (emoji.shortcodes) {
+ // Add mapping from each shortcode to Emoji object
+ emoji.shortcodes.forEach(shortcode => {
+ SHORTCODE_TO_EMOJI.set(shortcode, emoji);
+ });
+ }
+});
+
+/**
+ * Strips variation selectors from a string
+ * NB. Skin tone modifers are not variation selectors:
+ * this function does not touch them. (Should it?)
+ *
+ * @param {string} str string to strip
+ * @returns {string} stripped string
+ */
+function stripVariation(str) {
+ return str.replace(/[\uFE00-\uFE0F]/, "");
+}
diff --git a/src/hooks/useEventEmitter.js b/src/hooks/useEventEmitter.js
index 56676bf871..7adc6ef2e3 100644
--- a/src/hooks/useEventEmitter.js
+++ b/src/hooks/useEventEmitter.js
@@ -28,6 +28,9 @@ export const useEventEmitter = (emitter, eventName, handler) => {
useEffect(
() => {
+ // allow disabling this hook by passing a falsy emitter
+ if (!emitter) return;
+
// Create event listener that calls handler function stored in ref
const eventListener = event => savedHandler.current(event);
diff --git a/src/i18n/strings/az.json b/src/i18n/strings/az.json
index c53258a28f..538403f164 100644
--- a/src/i18n/strings/az.json
+++ b/src/i18n/strings/az.json
@@ -70,7 +70,7 @@
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(time)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s",
"Unable to enable Notifications": "Xəbərdarlıqları daxil qoşmağı bacarmadı",
- "Default": "Default",
+ "Default": "Varsayılan olaraq",
"Moderator": "Moderator",
"Admin": "Administrator",
"Start a chat": "Danışığa başlamaq",
@@ -350,7 +350,7 @@
"Failed to invite users to community": "İstifadəçiləri cəmiyyətə dəvət etmək alınmadı",
"Unnamed Room": "Adı açıqlanmayan otaq",
"Unable to load! Check your network connectivity and try again.": "Yükləmək olmur! Şəbəkə bağlantınızı yoxlayın və yenidən cəhd edin.",
- "Dismiss": "Rədd et",
+ "Dismiss": "Nəzərə almayın",
"Riot does not have permission to send you notifications - please check your browser settings": "Riot-un sizə bildiriş göndərmək icazəsi yoxdur - brauzerinizin parametrlərini yoxlayın",
"Riot was not given permission to send notifications - please try again": "Riot bildiriş göndərmək üçün icazə verilmədi - lütfən yenidən cəhd edin",
"This email address was not found": "Bu e-poçt ünvanı tapılmadı",
@@ -389,5 +389,64 @@
"Verified key": "Təsdiqlənmiş açar",
"Sends the given message coloured as a rainbow": "Verilən mesajı göy qurşağı kimi rəngli göndərir",
"Sends the given emote coloured as a rainbow": "Göndərilmiş emote rəngini göy qurşağı kimi göndərir",
- "Unrecognised command:": "Tanınmayan əmr:"
+ "Unrecognised command:": "Tanınmayan əmr:",
+ "Add Email Address": "Emal ünvan əlavə etmək",
+ "Add Phone Number": "Telefon nömrəsi əlavə etmək",
+ "e.g. %(exampleValue)s": "e.g. %(exampleValue)s",
+ "Call failed due to misconfigured server": "Düzgün qurulmamış server səbəbindən zəng alınmadı",
+ "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Xahiş edirik, baş serverin administratoruna müraciət edin (%(homeserverDomain)s) ki zənglərin etibarlı işləməsi üçün dönüş serverini konfiqurasiya etsin.",
+ "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Alternativ olaraq, ümumi serveri turn.matrix.org istifadə etməyə cəhd edə bilərsiniz, lakin bu qədər etibarlı olmayacaq və IP ünvanınızı bu serverlə bölüşəcəkdir. Bunu Ayarlarda da idarə edə bilərsiniz.",
+ "Try using turn.matrix.org": "Turn.matrix.org istifadə edin",
+ "The file '%(fileName)s' failed to upload.": "'%(fileName)s' faylı yüklənə bilmədi.",
+ "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "'%(fileName)s' faylı yükləmə üçün bu server ölçü həddini aşmışdır",
+ "Send cross-signing keys to homeserver": "Ev serveri üçün çarpaz imzalı açarları göndərin",
+ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
+ "Add rooms to the community": "Icmaya otaqlar əlavə edin",
+ "Failed to invite the following users to %(groupId)s:": "Aşağıdakı istifadəçiləri %(groupId)s - ə dəvət etmək alınmadı:",
+ "Failed to invite users to %(groupId)s": "İstifadəçiləri %(groupId)s - a dəvət etmək alınmadı",
+ "Failed to add the following rooms to %(groupId)s:": "Aşağıdakı otaqları %(groupId)s - a əlavə etmək alınmadı:",
+ "Identity server has no terms of service": "Şəxsiyyət serverinin xidmət şərtləri yoxdur",
+ "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Bu hərəkət e-poçt ünvanı və ya telefon nömrəsini təsdiqləmək üçün standart şəxsiyyət serverinə girməyi tələb edir, lakin serverdə heç bir xidmət şəraiti yoxdur.",
+ "Only continue if you trust the owner of the server.": "Yalnız server sahibinə etibar etsəniz davam edin.",
+ "Trust": "Etibar",
+ "Custom (%(level)s)": "Xüsusi (%(level)s)",
+ "Failed to start chat": "Söhbətə başlamaq olmur",
+ "Failed to invite the following users to the %(roomName)s room:": "Aşağıdakı istifadəçiləri %(roomName)s otağına dəvət etmək alınmadı:",
+ "Room %(roomId)s not visible": "Otaq %(roomId)s görünmür",
+ "Messages": "Mesajlar",
+ "Actions": "Tədbirlər",
+ "Other": "Digər",
+ "Sends a message as plain text, without interpreting it as markdown": "Bir mesajı qeyd kimi şərh etmədən, düz mətn şəklində göndərir",
+ "You do not have the required permissions to use this command.": "Bu komandadan (əmrdən) istifadə etmək üçün tələb olunan icazəniz yoxdur.",
+ "Error upgrading room": "Otaq yeniləmə xətası",
+ "Double check that your server supports the room version chosen and try again.": "Serverinizin seçilmiş otaq versiyasını dəstəklədiyini bir daha yoxlayın və yenidən cəhd edin.",
+ "Changes the avatar of the current room": "Cari otağın avatarını dəyişdirir",
+ "Use an identity server": "Şəxsiyyət serverindən istifadə edin",
+ "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "E-poçtla dəvət etmək üçün şəxsiyyət serverindən istifadə edin. Defolt şəxsiyyət serverini (%(defaultIdentityServerName)s) istifadə etməyə və ya Parametrlərdə idarə etməyə davam edin.",
+ "Use an identity server to invite by email. Manage in Settings.": "E-poçtla dəvət etmək üçün şəxsiyyət serverindən istifadə edin. Parametrlərdə idarə edin.",
+ "Please supply a https:// or http:// widget URL": "Zəhmət olmasa https:// və ya http:// widget URL təmin edin",
+ "Device already verified!": "Cihaz artıq təsdiqləndi!",
+ "WARNING: Device already verified, but keys do NOT MATCH!": "XƏBƏRDARLIQ: Cihaz artıq təsdiqləndi, lakin açarlar uyğun gəlmir!",
+ "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!": "XƏBƏRDARLIQ: ƏSAS VERIFİKASİYA VERİLİR! %(userId)s və cihaz %(deviceId)s üçün imza açarı \"%(fprint)s\" ilə təmin olunmayan \"%(fingerprint)s\". Bu, ünsiyyətlərinizin tutulduğunu ifadə edə bilər!",
+ "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Təqdim etdiyiniz imza açarı %(userId)s cihazının %(deviceId)s cihazından aldığınız imza açarına uyğundur. Cihaz təsdiqlənmiş kimi qeyd edildi.",
+ "Forces the current outbound group session in an encrypted room to be discarded": "Şifrəli bir otaqda mövcud qrup sessiyasını ləğv etməyə məcbur edir",
+ "Displays list of commands with usages and descriptions": "İstifadə qaydaları və təsvirləri ilə komanda siyahısını göstərir",
+ "%(senderName)s requested a VoIP conference.": "%(senderName)s VoIP konfrans istədi.",
+ "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s göstərilən adlarını %(displayName)s olaraq dəyişdirdi.",
+ "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s öz adlarını %(displayName)s olaraq təyin etdilər.",
+ "%(senderName)s set a profile picture.": "%(senderName)s profil şəkli təyin etdi.",
+ "%(senderName)s made no change.": "%(senderName)s dəyişiklik etməyib.",
+ "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s otaq otağını sildi.",
+ "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s bu otağı təkmilləşdirdi.",
+ "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s linki olanlara otağı açıq etdi.",
+ "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s otağı yalnız dəvətlə açıq etdi.",
+ "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s qoşulma qaydasını %(rule)s olaraq dəyişdirdi",
+ "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s qonaq otağa qoşulmasına icazə verdi.",
+ "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s qonaqların otağa daxil olmasının qarşısını aldı.",
+ "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s %(rule)s-a qonaq girişi dəyişdirildi.",
+ "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "Bu otaqda %(qruplar)s üçün %(senderDisplayName)s aktiv oldu.",
+ "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "Bu otaqda %(groups)s üçün %(senderDisplayName)s aktiv oldu.",
+ "powered by Matrix": "Matrix tərəfindən təchiz edilmişdir",
+ "Custom Server Options": "Fərdi Server Seçimləri",
+ "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "Bu otaqda %(newGroups)s üçün aktiv və %(oldGroups)s üçün %(senderDisplayName)s deaktiv oldu."
}
diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json
index 3115ca140a..e179c9d61b 100644
--- a/src/i18n/strings/bg.json
+++ b/src/i18n/strings/bg.json
@@ -1851,5 +1851,169 @@
"Connecting to integration manager...": "Свързане с мениджъра на интеграции...",
"Cannot connect to integration manager": "Неуспешна връзка с мениджъра на интеграции",
"The integration manager is offline or it cannot reach your homeserver.": "Мениджъра на интеграции е офлайн или не може да се свърже със сървъра ви.",
- "Clear notifications": "Изчисти уведомленията"
+ "Clear notifications": "Изчисти уведомленията",
+ "Send cross-signing keys to homeserver": "Изпрати ключове за кръстосано-подписване към сървъра",
+ "Error upgrading room": "Грешка при обновяване на стаята",
+ "Double check that your server supports the room version chosen and try again.": "Проверете дали сървъра поддържа тази версия на стаята и опитайте пак.",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа на потребители отговарящи на %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа до стаи отговарящи на %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа до сървъри отговарящи на %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа неща отговарящи на %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s обнови невалидно правило за блокиране",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа на потребители отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа до стаи отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа до сървъри отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа неща отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа на потребители отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа до стаи отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа до сървъри отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа до неща отговарящи на %(glob)s поради %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа на потребители отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа до стаи отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа до сървъри отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа до неща отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s",
+ "The message you are trying to send is too large.": "Съобщението, което се опитвате да изпратите е прекалено голямо.",
+ "Cross-signing and secret storage are enabled.": "Кръстосано-подписване и секретно складиране са включени.",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "За вашия профил има самоличност за кръстосано-подписване в секретно складиране, но все още не е доверена от това устройство.",
+ "Cross-signing and secret storage are not yet set up.": "Кръстосаното-подписване и секретно складиране все още не са настроени.",
+ "Bootstrap cross-signing and secret storage": "Инициализирай кръстосано-подписване и секретно складиране",
+ "Cross-signing public keys:": "Публични ключове за кръстосано-подписване:",
+ "on device": "на устройството",
+ "not found": "не са намерени",
+ "Cross-signing private keys:": "Private ключове за кръстосано подписване:",
+ "in secret storage": "в секретно складиране",
+ "Secret storage public key:": "Публичен ключ за секретно складиране:",
+ "in account data": "в данни за акаунта",
+ "not stored": "не е складиран",
+ "Backup has a valid signature from this user": "Резервното копие има валиден подпис за този потребител",
+ "Backup has a invalid signature from this user": "Резервното копие има невалиден подпис за този потребител",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "Резервното копие има подпис от непознат потребител с идентификатор %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "Резервното копие има подпис от непознато устройство с идентификатор %(deviceId)s",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Резервния ключ е съхранен в секретно складиране, но тази функция не е включена на това устройство. Включете кръстосано-подписване от Labs за да промените състоянието на резервното копие на ключа.",
+ "Backup key stored: ": "Резервният ключ е съхранен: ",
+ "Start using Key Backup with Secure Secret Storage": "Започни използване на резервно копие на ключ в защитено секретно складиране",
+ "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Използвай мениджър на интеграции %(serverName)s за управление на ботове, приспособления и стикери.",
+ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Използвай мениджър на интеграции за управление на ботове, приспособления и стикери.",
+ "Manage integrations": "Управление на интеграциите",
+ "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Мениджърът на интеграции получава конфигурационни данни, може да модифицира приспособления, да изпраща покани за стаи и да настройва нива на достъп от ваше име.",
+ "Customise your experience with experimental labs features. Learn more.": "Настройте изживяването си с експериментални функции. Научи повече.",
+ "Ignored/Blocked": "Игнорирани/блокирани",
+ "Error adding ignored user/server": "Грешка при добавяне на игнориран потребител/сървър",
+ "Something went wrong. Please try again or view your console for hints.": "Нещо се обърка. Опитайте пак или вижте конзолата за информация какво не е наред.",
+ "Error subscribing to list": "Грешка при абониране за списък",
+ "Please verify the room ID or alias and try again.": "Потвърдете идентификатора или адреса на стаята и опитайте пак.",
+ "Error removing ignored user/server": "Грешка при премахване на игнориран потребител/сървър",
+ "Error unsubscribing from list": "Грешка при отписването от списък",
+ "Please try again or view your console for hints.": "Опитайте пак или вижте конзолата за информация какво не е наред.",
+ "None": "Няма нищо",
+ "Ban list rules - %(roomName)s": "Списък с правила за блокиране - %(roomName)s",
+ "Server rules": "Сървърни правила",
+ "User rules": "Потребителски правила",
+ "You have not ignored anyone.": "Не сте игнорирали никой.",
+ "You are currently ignoring:": "В момента игнорирате:",
+ "You are not subscribed to any lists": "Не сте абонирани към списъци",
+ "Unsubscribe": "Отпиши",
+ "View rules": "Виж правилата",
+ "You are currently subscribed to:": "В момента сте абонирани към:",
+ "⚠ These settings are meant for advanced users.": "⚠ Тези настройки са за напреднали потребители.",
+ "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Добавете тук потребители или сървъри, които искате да игнорирате. Използвайте звездички за да кажете на Riot да търси съвпадения с всеки символ. Например: @bot:* ще игнорира всички потребители с име 'bot' на кой да е сървър.",
+ "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Игнорирането на хора става чрез списъци за блокиране, които съдържат правила кой да бъде блокиран. Абонирането към списък за блокиране означава, че сървърите/потребителите блокирани от този списък ще бъдат скрити от вас.",
+ "Personal ban list": "Персонален списък за блокиране",
+ "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Персоналния ви списък за блокиране съдържа потребители/сървъри, от които не искате да виждате съобщения. След игнориране на първия потребител/сървър, ще се появи нова стая в списъка със стаи, наречена 'My Ban List' - останете в тази стая за да работи списъкът с блокиране.",
+ "Server or user ID to ignore": "Сървър или потребителски идентификатор за игнориране",
+ "eg: @bot:* or example.org": "напр.: @bot:* или example.org",
+ "Subscribed lists": "Абонирани списъци",
+ "Subscribing to a ban list will cause you to join it!": "Абонирането към списък ще направи така, че да се присъедините към него!",
+ "If this isn't what you want, please use a different tool to ignore users.": "Ако това не е каквото искате, използвайте друг инструмент за игнориране на потребители.",
+ "Room ID or alias of ban list": "Идентификатор или име на стая списък за блокиране",
+ "Subscribe": "Абонирай ме",
+ "Cross-signing": "Кръстосано-подписване",
+ "This user has not verified all of their devices.": "Този потребител не е потвърдил всичките си устройства.",
+ "You have not verified this user. This user has verified all of their devices.": "Не сте потвърдили този потребител. Потребителят е потвърдил всичките си устройства.",
+ "You have verified this user. This user has verified all of their devices.": "Потвърдили сте този потребител. Потребителят е потвърдил всичките си устройства.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Някои потребители в тази стая не са потвърдени от вас или не са потвърдили собствените си устройства.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Всички потребители в тази стая са потвърдени от вас и са потвърдили всичките си устройства.",
+ "This message cannot be decrypted": "Съобщението не може да бъде дешифровано",
+ "Unencrypted": "Нешифровано",
+ "Close preview": "Затвори прегледа",
+ " wants to chat": " иска да чати",
+ "Start chatting": "Започни чат",
+ "Failed to connect to integration manager": "Неуспешна връзка с мениджъра на интеграции",
+ "Trusted": "Доверени",
+ "Not trusted": "Недоверени",
+ "Hide verified sessions": "Скрий потвърдените сесии",
+ "%(count)s verified sessions|other": "%(count)s потвърдени сесии",
+ "%(count)s verified sessions|one": "1 потвърдена сесия",
+ "Direct message": "Директно съобщение",
+ "Unverify user": "Отпотвърди потребители",
+ "%(role)s in %(roomName)s": "%(role)s в%(roomName)s",
+ "Messages in this room are end-to-end encrypted.": "Съобщенията в тази стая са шифровани от край-до-край.",
+ "Verify": "Потвърди",
+ "Security": "Сигурност",
+ "You have ignored this user, so their message is hidden. Show anyways.": "Игнорирали сте този потребител, така че съобщението им е скрито. Покажи така или иначе.",
+ "Reactions": "Реакции",
+ " reacted with %(content)s": " реагира с %(content)s",
+ "Any of the following data may be shared:": "Следните данни може да бъдат споделени:",
+ "Your display name": "Вашето име",
+ "Your avatar URL": "Адреса на профилната ви снимка",
+ "Your user ID": "Потребителския ви идентификатор",
+ "Your theme": "Вашата тема",
+ "Riot URL": "Riot URL адрес",
+ "Room ID": "Идентификатор на стаята",
+ "Widget ID": "Идентификатор на приспособлението",
+ "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Използването на това приспособление може да сподели данни с %(widgetDomain)s и с мениджъра на интеграции.",
+ "Using this widget may share data with %(widgetDomain)s.": "Използването на това приспособление може да сподели данни с %(widgetDomain)s.",
+ "Widgets do not use message encryption.": "Приспособленията не използваш шифроване на съобщенията.",
+ "Widget added by": "Приспособлението е добавено от",
+ "This widget may use cookies.": "Това приспособление може да използва бисквитки.",
+ "More options": "Още опции",
+ "Language Dropdown": "Падащо меню за избор на език",
+ "Integrations are disabled": "Интеграциите са изключени",
+ "Enable 'Manage Integrations' in Settings to do this.": "Включете 'Управление на интеграции' от настройките за направите това.",
+ "Integrations not allowed": "Интеграциите не са разрешени",
+ "Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Вашият Riot не позволява да използвате мениджъра на интеграции за да направите това. Свържете се с администратор.",
+ "Automatically invite users": "Автоматично кани потребители",
+ "Upgrade private room": "Обнови лична стая",
+ "Upgrade public room": "Обнови публична стая",
+ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Обновяването на стая е действие за напреднали и обикновено се препоръчва когато стаята е нестабилна поради бъгове, липсващи функции или проблеми със сигурността.",
+ "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Това обикновено влия само на това как стаята се обработва на сървъра. Ако имате проблеми с Riot, съобщете за проблем.",
+ "You'll upgrade this room from to .": "Ще обновите стаята от до .",
+ "Upgrade": "Обнови",
+ "Enter secret storage passphrase": "Въведете парола за секретно складиране",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Неуспешен достъп до секретно складиране. Уверете се, че сте въвели правилната парола.",
+ "Warning: You should only access secret storage from a trusted computer.": "Внимание: Трябва да достъпвате секретно складиране само от доверен компютър.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "Въведете парола за да достъпите защитената история на съобщенията и самоличността за кръстосано-подписване и потвърждение на други устройства.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Ако сте забравили паролата си, може да използвате ключ за възстановяване или да настройте опции за възстановяване.",
+ "Enter secret storage recovery key": "Въведете ключ за възстановяване на секретно складиране",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Неуспешен достъп до секретно складиране. Подсигурете се, че сте въвели правилния ключ за възстановяване.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "Въведете ключа за възстановяване за да достъпите защитената история на съобщенията и самоличността за кръстосано-подписване и потвърждение на други устройства.",
+ "If you've forgotten your recovery key you can .": "Ако сте забравили ключа си за възстановяване, може да .",
+ "Warning: You should only set up key backup from a trusted computer.": "Внимание: Трябва да настройвате резервно копие на ключове само от доверен компютър.",
+ "If you've forgotten your recovery key you can ": "Ако сте забравили ключа за възстановяване, може да ",
+ "Notification settings": "Настройки на уведомленията",
+ "Help": "Помощ",
+ "Reload": "Презареди",
+ "Take picture": "Направи снимка",
+ "Remove for everyone": "Премахни за всички",
+ "Remove for me": "Премахни за мен",
+ "User Status": "Потребителски статус",
+ "Country Dropdown": "Падащо меню за избор на държава",
+ "Verification Request": "Заявка за потвърждение",
+ " (1/%(totalCount)s)": " (1/%(totalCount)s)",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Секретно Складиране ще бъде настроено със съществуващия ви резервен ключ. Паролата и ключа за секретно складиране ще бъдат същите каквито са за резервно складиране",
+ "Warning: You should only set up secret storage from a trusted computer.": "Внимание: Желателно е да настройвате секретно складиране само от доверен компютър.",
+ "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Ще използваме секретно складиране за да предоставим опцията да се съхрани шифровано копие на идентичността ви за кръстосано-подписване, за потвърждаване на други устройства и ключове. Защитете достъпа си до шифровани съобщения с парола за да е защитено.",
+ "Set up with a recovery key": "Настрой с ключ за възстановяване",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Като предпазна мярка, ако забравите паролата, може да го използвате за да възстановите достъпа до шифрованите съобщения.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Като предпазна мярка, може да го използвате за да възстановите достъпа до шифрованите съобщения.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Съхранявайте ключа за възстановяване на сигурно място, като в password manager (или сейф).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "Ключа за възстановяване беше копиран в клиборд, поставете го в:",
+ "Your recovery key is in your Downloads folder.": "Ключа за възстановяване е във вашата папка Изтегляния.",
+ "Your access to encrypted messages is now protected.": "Достъпът ви до шифровани съобщения вече е защитен.",
+ "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Без настройка на секретно складиране, ако излезе от профила или използвате друго устройство, няма да можете да възстановите достъпа до шифровани съобщения или идентичността за кръстосано-подписване за потвърждаване на други устройства.",
+ "Set up secret storage": "Настрой секретно складиране",
+ "Migrate from Key Backup": "Мигрирай от резервно копие на ключове",
+ "Secure your encrypted messages with a passphrase": "Защитете шифрованите съобщения с парола",
+ "Storing secrets...": "Складиране на тайни...",
+ "Unable to set up secret storage": "Неуспешна настройка на секретно складиране"
}
diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json
index f552c117e5..5aacb27c80 100644
--- a/src/i18n/strings/cs.json
+++ b/src/i18n/strings/cs.json
@@ -510,8 +510,8 @@
"Flair": "Vztah ke komunitě",
"Showing flair for these communities:": "Místnost má vztah k těmto komunitám:",
"This room is not showing flair for any communities": "Tato místnost nemá vztah k žádné komunitě",
- "URL previews are enabled by default for participants in this room.": "Náhledy URL adres jsou defaultně nastavené jako povolené pro členy této místnosti.",
- "URL previews are disabled by default for participants in this room.": "Náhledy URL adres jsou defaultně nastavené jako zakázané pro členy této místnosti.",
+ "URL previews are enabled by default for participants in this room.": "Ve výchozím nastavení jsou náhledy URL adres povolené pro členy této místnosti.",
+ "URL previews are disabled by default for participants in this room.": "Ve výchozím nastavení jsou náhledy URL adres zakázané pro členy této místnosti.",
"Invalid file%(extra)s": "Neplatný soubor%(extra)s",
"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?": "Budete přesměrováni na stránku třetí strany k ověření svého účtu pro používání s %(integrationsUrl)s. Chcete pokračovat?",
"To continue, please enter your password.": "Aby jste mohli pokračovat, zadejte prosím své heslo.",
@@ -587,10 +587,10 @@
"%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)ssi změnili jméno",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)ssi %(count)s krát změnili jméno",
"%(oneUser)schanged their name %(count)s times|one": "%(oneUser)ssi změnili jméno",
- "%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)ssi %(count)s krát změnili avatara",
- "%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)ssi změnili avatara",
- "%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)ssi %(count)s krát změnil avatara",
- "%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)ssi změnil avatara",
+ "%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)ssi %(count)s krát změnili avatary",
+ "%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)ssi změnili avatary",
+ "%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)ssi %(count)s krát změnil avatar",
+ "%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)ssi změnil avatar",
"%(items)s and %(count)s others|other": "%(items)s a %(count)s další",
"%(items)s and %(count)s others|one": "%(items)s a jeden další",
"%(items)s and %(lastItem)s": "%(items)s a také %(lastItem)s",
@@ -604,8 +604,8 @@
"You have entered an invalid address.": "Zadali jste neplatnou adresu.",
"Confirm Removal": "Potvrdit odstranění",
"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.": "Opravdu chcete odstranit (smazat) tuto událost? V případě, že smažete název místnosti anebo změníte téma, je možné, že se změny neprovedou.",
- "Community IDs may only contain characters a-z, 0-9, or '=_-./'": "ID skupiny může obsahovat pouze znaky a-z, 0-9, or '=_-./'",
- "Something went wrong whilst creating your community": "Něco se pokazilo v během vytváření vaší skupiny",
+ "Community IDs may only contain characters a-z, 0-9, or '=_-./'": "ID skupiny může obsahovat pouze znaky a-z, 0-9, nebo '=_-./'",
+ "Something went wrong whilst creating your community": "Něco se pokazilo během vytváření vaší skupiny",
"Unknown error": "Neznámá chyba",
"Incorrect password": "Nesprávné heslo",
"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:": "Pokud si chcete ověřit, zda je zařízení skutečně důvěryhodné, kontaktujte vlastníka jiným způsobem (např. osobně anebo telefonicky) a zeptejte se ho na klíč, který má pro toto zařízení zobrazený v nastavení a zda se shoduje s klíčem zobrazeným níže:",
@@ -1019,7 +1019,7 @@
"Room Topic": "Téma místnosti",
"No room avatar": "Žádný avatar místnosti",
"Room avatar": "Avatar místnosti",
- "Upload room avatar": "Nahrát avatara místnosti",
+ "Upload room avatar": "Nahrát avatar místnosti",
"Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Změny toho kdo smí číst historické zprávy se aplikují jenom na další zprávy v této místosti. Viditelnost už poslaných zpráv zůstane jaká byla.",
"To link to this room, please add an alias.": "K vytvoření odkazu je potřeba vyrobit místnosti alias.",
"Roles & Permissions": "Funkce & Práva",
@@ -1047,7 +1047,7 @@
"Enable big emoji in chat": "Povolit velké emotikony v chatu",
"Show avatars in user and room mentions": "Zobrazovat ikony uživatelů ve zmínkách o nich",
"Prompt before sending invites to potentially invalid matrix IDs": "Zeptat před odesláním pozvánky potenciálně neplatným Matrixovým identifikátorům",
- "Show avatar changes": "Zobrazovat změny ikony",
+ "Show avatar changes": "Zobrazovat změny avatarů",
"Show join/leave messages (invites/kicks/bans unaffected)": "Zobrazovat zprávy o připojení/opuštění (netýká se pozvánek/vykopnutí/vykázání)",
"Show a placeholder for removed messages": "Zobrazovat zamazání místo smazané zprávy",
"Show display name changes": "Zobrazovat změny jména",
@@ -1406,7 +1406,7 @@
"Order rooms in the room list by most important first instead of most recent": "Seřadit místosti v seznamu podle důležitosti místo podle posledního použití",
"Scissors": "Nůžky",
"Accept all %(invitedRooms)s invites": "Přijmout všechny pozvánky: %(invitedRooms)s",
- "Change room avatar": "Změnit avatara místnosti",
+ "Change room avatar": "Změnit avatar místnosti",
"Change room name": "Změnit jméno místnosti",
"Change main address for the room": "Změnit hlavní adresu místnosti",
"Change history visibility": "Změnit viditelnost historie",
@@ -1451,7 +1451,7 @@
"Room upgrades usually only affect server-side processing of the room. If you're having problems with your Riot client, please file an issue with .": "Upgrade místnosti se většinou týká zpracování serverovem. Pokud máte problém s klientem Riot, nahlašte nám prosím chybu na GitHub: .",
"Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Varování: Upgrade místnosti automaticky převede všechny členy na novou verzi místnosti. Do staré místnosti pošleme odkaz na novou místnost - všichni členové na něj budou muset kliknout, aby se přidali do nové místnosti.",
"Please confirm that you'd like to go forward with upgrading this room from to .": "Potvrďte prosím, že chcete pokračovat a opravdu provést upgrade z verze na verzi .",
- "Changes your avatar in this current room only": "Změní vaší ikonu jen v této místnosti",
+ "Changes your avatar in this current room only": "Změní váš avatar jen v této místnosti",
"Unbans user with given ID": "Přijmout zpět uživatele s daným identifikátorem",
"Adds a custom widget by URL to the room": "Přidá do místnosti vlastní widget podle adresy URL",
"Please supply a https:// or http:// widget URL": "Zadejte webovou adresu widgetu (začínající na https:// nebo http://)",
@@ -1500,7 +1500,7 @@
"Do you want to join %(roomName)s?": "Chcete se přidat do místnosti %(roomName)s?",
" invited you": " vás pozval",
"You're previewing %(roomName)s. Want to join it?": "Prohlížíte si místnost %(roomName)s. Chcete se do ní přidat?",
- "%(roomName)s can't be previewed. Do you want to join it?": "Místnost %(roomName)s si nelze jen tak prohlížet. Chcete se do ní přidat?",
+ "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s si nelze jen tak prohlížet. Chcete se do ní přidat?",
"This room doesn't exist. Are you sure you're at the right place?": "Tato místnost neexistuje. Jste si jistí, že jste na správném místě?",
"Try again later, or ask a room admin to check if you have access.": "Zkuste to znovu nebo se zeptejte administrátora aby zkontrolovat jestli máte přístup.",
"%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.": "Při pokusu o připojení došlo k chybě: %(errcode)s. Pokud si myslíte, že je to bug, můžete ho nahlásit.",
@@ -1602,8 +1602,8 @@
"Actions": "Akce",
"Sends a message as plain text, without interpreting it as markdown": "Pošle zprávu jako prostý text, neinterpretuje jí jako Markdown",
"You do not have the required permissions to use this command.": "Na provedení tohoto příkazu nemáte dostatečná oprávnění.",
- "Changes the avatar of the current room": "Změní vašeho avatara pro tuto místnost",
- "Changes your avatar in all rooms": "Změní vašeho avatara pro všechny místnosti",
+ "Changes the avatar of the current room": "Změní váš avatar pro tuto místnost",
+ "Changes your avatar in all rooms": "Změní váš avatar pro všechny místnosti",
"Use an identity server": "Používat server identit",
"Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Použít server identit na odeslání emailové pozvánky. Pokračováním použijete výchozí server identit (%(defaultIdentityServerName)s) nebo ho můžete změnit v Nastavení.",
"Use an identity server to invite by email. Manage in Settings.": "Použít server identit na odeslání emailové pozvánky. Můžete spravovat v Nastavení.",
@@ -1807,7 +1807,7 @@
"View": "Zobrazit",
"Find a room…": "Najít místnost…",
"Find a room… (e.g. %(exampleRoom)s)": "Najít místnost… (například %(exampleRoom)s)",
- "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Pokud nemůžete nelézt místnost, kterou hledáte, napište o pozvánku nebo si jí Vytvořte.",
+ "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Pokud nemůžete nelézt místnost, kterou hledáte, napište si o pozvánku nebo Vytvořte novou.",
"Explore rooms": "Prohlížet místnosti",
"Jump to first unread room.": "Skočit na první nepřečtenou místnost.",
"Jump to first invite.": "Skočit na první pozvánku.",
@@ -1834,5 +1834,165 @@
"Emoji Autocomplete": "Automatické doplňování Emodži",
"Notification Autocomplete": "Automatické doplňování upozornění",
"Room Autocomplete": "Automatické doplňování místností",
- "User Autocomplete": "Automatické doplňování uživatelů"
+ "User Autocomplete": "Automatické doplňování uživatelů",
+ "Custom (%(level)s)": "Vlastní (%(level)s)",
+ "Error upgrading room": "Chyba při upgrade místnosti",
+ "Double check that your server supports the room version chosen and try again.": "Zkontrolujte, že váš server opravdu podporuje zvolenou verzi místnosti.",
+ "%(senderName)s placed a voice call.": "%(senderName)s spustil hovor.",
+ "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s spustil hovor. (není podporováno tímto prohlížečem)",
+ "%(senderName)s placed a video call.": "%(senderName)s spustil videohovor.",
+ "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s spustil videohovor. (není podporováno tímto prohlížečem)",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s odstranit pravidlo blokující uživatele odpovídající %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s odstranil pravidlo blokující místnosti odpovídající %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s odstranil pravidlo blokující servery odpovídající %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s odstranil blokující pravidlo %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s aktualizoval neplatné pravidlo blokování",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo blokující uživatele odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo blokující místnosti odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo blokující servery odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval blokovací pravidlo odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s vytvořil pravidlo blokující uživatele odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s vytvořil pravidlo blokující místnosti odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s vytvořil pravidlo blokující servery odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s vytvořil blokovací pravidlo odpovídající %(glob)s z důvodu %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s změnil pravidlo blokující uživatele odpovídající %(oldGlob)s na uživatele odpovídající %(newGlob)s z důvodu %(reason)s.",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s změnil pravidlo blokující místnosti odpovídající %(oldGlob)s na místnosti odpovídající %(newGlob)s z důvodu %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s změnil pravidlo blokující servery odpovídající %(oldGlob)s na servery odpovídající %(newGlob)s z důvodu %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s změnil blokovací pravidlo odpovídající %(oldGlob)s na odpovídající %(newGlob)s z důvodu %(reason)s",
+ "Try out new ways to ignore people (experimental)": "Vyzkošejte nové metody ignorování lidí (experimentální)",
+ "Send verification requests in direct message, including a new verification UX in the member panel.": "Poslat požadavek na ověření v přímé zprávě včetně nového verifikačního rozhraní v panelu.",
+ "Enable local event indexing and E2EE search (requires restart)": "Povolit lokální indexování a vyhledávání v E2E šifrovaných zprávách (vyžaduje restart)",
+ "Match system theme": "Přizpůsobit se systémovému vzhledu",
+ "My Ban List": "Můj seznam zablokovaných",
+ "This is your list of users/servers you have blocked - don't leave the room!": "Toto je váš seznam blokovaných uživatelů/serverů - neopouštějte tuto místnost!",
+ "Decline (%(counter)s)": "Odmítnout (%(counter)s)",
+ "on device": "na zařízení",
+ "Connecting to integration manager...": "Připojuji se ke správci integrací...",
+ "Cannot connect to integration manager": "Nepovedlo se připojení ke správci integrací",
+ "The integration manager is offline or it cannot reach your homeserver.": "Správce integrací neběží nebo se nemůže připojit k vašemu domovskému serveru.",
+ "Clear notifications": "Odstranit notifikace",
+ "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Použít správce integrací (%(serverName)s) na správu botů, widgetů a samolepek.",
+ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Použít správce integrací na správu botů, widgetů a samolepek.",
+ "Manage integrations": "Spravovat integrace",
+ "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Správce integrací dostává konfigurační data a může za vás modifikovat widgety, posílat pozvánky a nastavovat úrovně oprávnění.",
+ "Customise your experience with experimental labs features. Learn more.": "Přizpůsobte si aplikaci s experimentálními funkcemi. Více informací.",
+ "Ignored/Blocked": "Ignorováno/Blokováno",
+ "Error adding ignored user/server": "Chyba při přidávání ignorovaného uživatele/serveru",
+ "Something went wrong. Please try again or view your console for hints.": "Něco se nepovedlo. Zkuste pro prosím znovu nebo se podívejte na detaily do konzole.",
+ "Error subscribing to list": "Nepovedlo se založit odběr",
+ "Please verify the room ID or alias and try again.": "Zkontrolujte prosím ID místnosti a zkuste to znovu.",
+ "Error removing ignored user/server": "Ignorovaný uživatel/server nejde odebrat",
+ "Error unsubscribing from list": "Nepovedlo se zrušit odběr",
+ "Please try again or view your console for hints.": "Zkuste to prosím znovu a nebo se podívejte na detailní chybu do konzole.",
+ "None": "Žádné",
+ "Ban list rules - %(roomName)s": "Pravidla blokování - %(roomName)s",
+ "Server rules": "Pravidla serveru",
+ "User rules": "Pravidla uživatele",
+ "You have not ignored anyone.": "Nikoho neignorujete.",
+ "You are currently ignoring:": "Ignorujete:",
+ "You are not subscribed to any lists": "Neodebíráte žádné seznamy",
+ "Unsubscribe": "Přestat odebírat",
+ "View rules": "Zobrazit pravidla",
+ "You are currently subscribed to:": "Odebíráte:",
+ "⚠ These settings are meant for advanced users.": "⚠ Tato nastavení jsou pro pokročilé uživatele.",
+ "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Sem přidejte uživatele a servery, které chcete ignorovat. Můžete použít hvězdičku místo libovolných znaků. Například pravidlo @bot:* bude blokovat všechny uživatele se jménem 'bot' na libovolném serveru.",
+ "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Lidé a servery jsou blokováni pomocí seznamů obsahující pravidla koho blokovat. Odebírání blokovacího seznamu znamená, že neuvidíte uživatele a servery na něm uvedené.",
+ "Personal ban list": "Osobní seznam blokací",
+ "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Váš osobní seznam blokací obsahuje všechny uživatele a server, které nechcete vidět. Po ignorování prvního uživatele/server se vytvoří nová místnost 'Můj seznam blokací' - zůstaňte v ní aby seznam platil.",
+ "Server or user ID to ignore": "Server nebo ID uživatele",
+ "eg: @bot:* or example.org": "např.: @bot:* nebo example.org",
+ "Subscribed lists": "Odebírané seznamy",
+ "Room ID or alias of ban list": "ID místnosti nebo alias seznamu blokování",
+ "Subscribe": "Odebírat",
+ "This message cannot be decrypted": "Zprávu nelze rozšifrovat",
+ "Unencrypted": "Nešifrované",
+ " wants to chat": " chce chatovat",
+ "Start chatting": "Začít chatovat",
+ "Failed to connect to integration manager": "Nepovedlo se připojit ke správci integrací",
+ "Trusted": "Důvěryhodné",
+ "Not trusted": "Nedůvěryhodné",
+ "Hide verified Sign-In's": "Skrýt důvěryhodná přihlášení",
+ "%(count)s verified Sign-In's|other": "%(count)s důvěryhodných přihlášení",
+ "%(count)s verified Sign-In's|one": "1 důvěryhodné přihlášení",
+ "Direct message": "Přímá zpráva",
+ "Unverify user": "Nedůvěřovat uživateli",
+ "%(role)s in %(roomName)s": "%(role)s v %(roomName)s",
+ "Messages in this room are end-to-end encrypted.": "V této místosti jsou zprávy E2E šifrované.",
+ "Security": "Bezpečnost",
+ "Verify": "Ověřit",
+ "You have ignored this user, so their message is hidden. Show anyways.": "Tohoto uživatele ignorujete, takže jsou jeho/její zprávy skryté. Přesto zobrazit.",
+ "Reactions": "Reakce",
+ " reacted with %(content)s": " reagoval %(content)s",
+ "Any of the following data may be shared:": "Následující data můžou být sdílena:",
+ "Your display name": "Vaše zobrazované jméno",
+ "Your avatar URL": "URL vašeho avataru",
+ "Your user ID": "Vaše ID",
+ "Your theme": "Váš motiv",
+ "Riot URL": "URL Riotu",
+ "Room ID": "ID místnosti",
+ "Widget ID": "ID widgetu",
+ "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Použití tohoto widgetu může sdílet data s %(widgetDomain)s a vaším správcem integrací.",
+ "Using this widget may share data with %(widgetDomain)s.": "Použití tohoto widgetu může sdílet data s %(widgetDomain)s.",
+ "Widgets do not use message encryption.": "Widgety nepoužívají šifrování zpráv.",
+ "Widget added by": "Widget přidal",
+ "This widget may use cookies.": "Widget může používat cookies.",
+ "More options": "Více možností",
+ "Integrations are disabled": "Integrace jsou zakázané",
+ "Enable 'Manage Integrations' in Settings to do this.": "Abyste mohli akci provést, povolte 'Spravovat integrace' v Nastavení.",
+ "Integrations not allowed": "Integrace nejsou povolené",
+ "Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Váš Riot neumožňuje použít správce integrací. Kontaktujte prosím administrátora.",
+ "Automatically invite users": "Automaticky zvát uživatele",
+ "Upgrade private room": "Upgradovat soukromou místnost",
+ "Upgrade public room": "Upgradovat veřejnou místnost",
+ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgradování místnosti je pokročilá operace a je doporučeno jí provést pokud je místnost nestabilní kvůli chybám, chybějícím funkcím nebo zranitelnostem.",
+ "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Toto běžně ovlivňuje jak je místnost zpracovávána na serveru. Pokud máte problém s Riotem, nahlaste nám prosím bug.",
+ "You'll upgrade this room from to .": "Upgradujeme tuto místnost z na .",
+ "Upgrade": "Upgradovat",
+ "Enter secret storage passphrase": "Zadejte tajné heslo k úložišti",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Nepovedlo se přistoupit k bezpečnému úložišti. Zkontrolujte prosím, že je zadané správné heslo.",
+ "Warning: You should only access secret storage from a trusted computer.": "Varování: Přistupujte k bezpečnému úložišti pouze z důvěryhodných počítačů.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Pokud si nepamatujete heslo, můžete použít váš obnovovací klíč nebo si nastavte nové možnosti obnovení.",
+ "Enter secret storage recovery key": "Zadejte klíč k bezpečnému úložišti",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Nepovedlo se dostat k bezpečnému úložišti. Ověřte prosím, že je zadaný správný klíč.",
+ "If you've forgotten your recovery key you can .": "Pokud si nepamatujete obnovovací klíč, můžete si .",
+ "Warning: You should only set up key backup from a trusted computer.": "Varování: Nastavujte zálohu jen z důvěryhodných počítačů.",
+ "If you've forgotten your recovery key you can ": "Pokud si nepamatujete obnovovací klíč, můžete si ",
+ "Notification settings": "Nastavení notifikací",
+ "Reload": "Načíst znovu",
+ "Take picture": "Udělat fotku",
+ "Remove for everyone": "Odstranit pro všechny",
+ "Remove for me": "Odstranit pro mě",
+ "User Status": "Stav uživatele",
+ "Verification Request": "Požadavek na ověření",
+ " (1/%(totalCount)s)": " (1/%(totalCount)s)",
+ "Warning: You should only set up secret storage from a trusted computer.": "Varování: Nastavujte bezpečné úložiště pouze z důvěryhodného počítače.",
+ "Set up with a recovery key": "Nastavit obnovovací klíč",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Můžete jej použít jako záchranou síť na obnovení šifrovaných zpráv když zapomenete heslo.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Můžete jej použít jako záchranou síť na obnovení šifrovaných zpráv.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Uschovejte svůj klíč na velmi bezpečném místě, například ve správci hesel (nebo v trezoru).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "Váš obnovovací klíč byl zkopírován do schránky, vložte jej:",
+ "Your recovery key is in your Downloads folder.": "Váš obnovací klíč je ve složce Stažené.",
+ "Your access to encrypted messages is now protected.": "Přístup k šifrovaným zprávám je teď chráněn.",
+ "Set up secret storage": "Nastavit bezpečné úložiště",
+ "Secure your encrypted messages with a passphrase": "Zabezpečte vaše šifrované zprávy heslem",
+ "Storing secrets...": "Ukládám tajná data...",
+ "Unable to set up secret storage": "Nepovedlo se nastavit bezpečné úložiště",
+ "The message you are trying to send is too large.": "Zpráva kterou se snažíte odeslat je příliš velká.",
+ "not found": "nenalezeno",
+ "Backup has a valid signature from this user": "Záloha má platný podpis od tohoto uživatele",
+ "Backup has a invalid signature from this user": "Záloha má neplatný podpis od tohoto uživatele",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "Záloha je podepsaná neznámým uživatelem %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "Záloha je podepsaná neznámým zařízením %(deviceId)s",
+ "This user has not verified all of their devices.": "Uživatel neověřil všechna svá zařízení.",
+ "You have not verified this user. This user has verified all of their devices.": "Tohoto uživatele jste neověřili. Uživatel má ověřená všechna svá zařízení.",
+ "You have verified this user. This user has verified all of their devices.": "Tohoto uživatele jste ověřili. Uživatel má ověřená všechna svá zařízení.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Někteřé uživatele v této šifrované místnosti jste neověřili nebo nemají ověřená některá svá zařízení.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Všichni uživatelé v této šifrované místnosti jsou ověření a mají ověřená všechna svá zařízení.",
+ "Close preview": "Zavřít náhled",
+ "Hide verified sessions": "Schovat ověřené relace",
+ "%(count)s verified sessions|other": "%(count)s ověřených relací",
+ "%(count)s verified sessions|one": "1 ověřená relace",
+ "Language Dropdown": "Menu jazyků",
+ "Help": "Pomoc",
+ "Country Dropdown": "Menu států"
}
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 45ba96ec62..4eb038ec33 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -1471,7 +1471,7 @@
"Please supply a https:// or http:// widget URL": "Bitte gib eine https:// oder http:// Widget-URL an",
"Sends the given emote coloured as a rainbow": "Sended das Emoji in Regenbogenfarben",
"%(senderName)s made no change.": "%(senderName)s hat keine Änderung vorgenommen.",
- "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s hat die Einladung zum Betreten des Raumes für %(targetDisplayName)s zurückgezogen.",
+ "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s hat die Einladung zum Raumbeitritt für %(targetDisplayName)s zurückgezogen.",
"Cannot reach homeserver": "Der Heimserver ist nicht erreichbar",
"Ensure you have a stable internet connection, or get in touch with the server admin": "Stelle sicher, dass du eine stabile Internetverbindung hast oder wende dich an deinen Server-Administrator",
"Ask your Riot admin to check your config for incorrect or duplicate entries.": "Wende dich an deinen Riot Admin um deine Konfiguration auf ungültige oder doppelte Einträge zu überprüfen.",
@@ -1579,5 +1579,11 @@
"Change identity server": "Wechsle den Identitätsserver",
"You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Du solltest deine persönlichen Daten vom Identitätsserver entfernen, bevor du die Verbindung trennst. Leider ist der Identitätsserver derzeit offline oder kann nicht erreicht werden.",
"You should:": "Du solltest:",
- "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "Überprüfe deinen Browser auf Erweiterungen, die den Identitätsserver blockieren könnten (z.B. Privacy Badger)"
+ "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "Überprüfe deinen Browser auf Erweiterungen, die den Identitätsserver blockieren könnten (z.B. Privacy Badger)",
+ "Error upgrading room": "Fehler beim Raum-Aufrüsten",
+ "Double check that your server supports the room version chosen and try again.": "Überprüfe nochmal ob dein Server die ausgewählte Raumversion unterstützt und versuche es nochmal.",
+ "%(senderName)s placed a voice call.": "%(senderName)s tätigte einen Sprachanruf.",
+ "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s tätigte einen Sprachanruf (Nicht von diesem Browser unterstützt)",
+ "%(senderName)s placed a video call.": "%(senderName)s tätigte einen Videoanruf.",
+ "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s tätigte einen Videoanruf (Nicht von diesem Browser unterstützt)"
}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 80604e9090..4311733b51 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -301,6 +301,7 @@
"No homeserver URL provided": "No homeserver URL provided",
"Unexpected error resolving homeserver configuration": "Unexpected error resolving homeserver configuration",
"Unexpected error resolving identity server configuration": "Unexpected error resolving identity server configuration",
+ "The message you are trying to send is too large.": "The message you are trying to send is too large.",
"This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.",
"This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.",
"Please contact your service administrator to continue using the service.": "Please contact your service administrator to continue using the service.",
@@ -357,10 +358,10 @@
"Render simple counters in room header": "Render simple counters in room header",
"Multiple integration managers": "Multiple integration managers",
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
- "Send verification requests in direct message, including a new verification UX in the member panel.": "Send verification requests in direct message, including a new verification UX in the member panel.",
+ "New DM invite dialog (under development)": "New DM invite dialog (under development)",
"Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)",
"Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)",
- "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages",
+ "Show info about bridges in room settings": "Show info about bridges in room settings",
"Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing",
"Use compact timeline layout": "Use compact timeline layout",
"Show a placeholder for removed messages": "Show a placeholder for removed messages",
@@ -499,6 +500,8 @@
"Pin": "Pin",
"Decline (%(counter)s)": "Decline (%(counter)s)",
"Accept to continue:": "Accept to continue:",
+ "Upload": "Upload",
+ "Remove": "Remove",
"Failed to upload profile picture!": "Failed to upload profile picture!",
"Upload new:": "Upload new:",
"No display name": "No display name",
@@ -597,10 +600,9 @@
"Off": "Off",
"On": "On",
"Noisy": "Noisy",
- "Profile picture": "Profile picture",
- "Upload profile picture": "Upload profile picture",
"Upgrade to your own domain": "Upgrade to your own domain",
"Display Name": "Display Name",
+ "Profile picture": "Profile picture",
"Save": "Save",
"Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS",
"Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)",
@@ -691,7 +693,6 @@
"User rules": "User rules",
"Close": "Close",
"You have not ignored anyone.": "You have not ignored anyone.",
- "Remove": "Remove",
"You are currently ignoring:": "You are currently ignoring:",
"You are not subscribed to any lists": "You are not subscribed to any lists",
"Unsubscribe": "Unsubscribe",
@@ -762,6 +763,13 @@
"Room version:": "Room version:",
"Developer options": "Developer options",
"Open Devtools": "Open Devtools",
+ "This bridge was provisioned by ": "This bridge was provisioned by ",
+ "This bridge is managed by .": "This bridge is managed by .",
+ "Bridged into , on ": "Bridged into , on ",
+ "Connected to on ": "Connected to on ",
+ "Connected via %(protocolName)s": "Connected via %(protocolName)s",
+ "Bridge Info": "Bridge Info",
+ "Below is a list of bridges connected to this room.": "Below is a list of bridges connected to this room.",
"Room Addresses": "Room Addresses",
"Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?",
"URL Previews": "URL Previews",
@@ -857,9 +865,14 @@
" (unsupported)": " (unsupported)",
"Join as voice or video.": "Join as voice or video.",
"Ongoing conference call%(supportedText)s.": "Ongoing conference call%(supportedText)s.",
+ "This user has not verified all of their devices.": "This user has not verified all of their devices.",
+ "You have not verified this user. This user has verified all of their devices.": "You have not verified this user. This user has verified all of their devices.",
+ "You have verified this user. This user has verified all of their devices.": "You have verified this user. This user has verified all of their devices.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Some users in this encrypted room are not verified by you or they have not verified their own devices.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "All users in this encrypted room are verified by you and they have verified their own devices.",
"Some devices for this user are not trusted": "Some devices for this user are not trusted",
- "Some devices in this encrypted room are not trusted": "Some devices in this encrypted room are not trusted",
"All devices for this user are trusted": "All devices for this user are trusted",
+ "Some devices in this encrypted room are not trusted": "Some devices in this encrypted room are not trusted",
"All devices in this encrypted room are trusted": "All devices in this encrypted room are trusted",
"Edit message": "Edit message",
"This event could not be displayed": "This event could not be displayed",
@@ -876,6 +889,7 @@
"Unencrypted": "Unencrypted",
"Please select the destination room for this message": "Please select the destination room for this message",
"Scroll to bottom of page": "Scroll to bottom of page",
+ "Close preview": "Close preview",
"device id: ": "device id: ",
"Disinvite": "Disinvite",
"Kick": "Kick",
@@ -941,13 +955,6 @@
"Strikethrough": "Strikethrough",
"Code block": "Code block",
"Quote": "Quote",
- "Server error": "Server error",
- "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
- "Command error": "Command error",
- "Unable to reply": "Unable to reply",
- "At this time it is not possible to reply with an emote.": "At this time it is not possible to reply with an emote.",
- "Markdown is disabled": "Markdown is disabled",
- "Markdown is enabled": "Markdown is enabled",
"No pinned messages.": "No pinned messages.",
"Loading...": "Loading...",
"Pinned Messages": "Pinned Messages",
@@ -1049,16 +1056,9 @@
"This Room": "This Room",
"All Rooms": "All Rooms",
"Search…": "Search…",
- "bold": "bold",
- "italic": "italic",
- "deleted": "deleted",
- "underlined": "underlined",
- "inline-code": "inline-code",
- "block-quote": "block-quote",
- "bulleted-list": "bulleted-list",
- "numbered-list": "numbered-list",
- "Show Text Formatting Toolbar": "Show Text Formatting Toolbar",
- "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar",
+ "Server error": "Server error",
+ "Command error": "Command error",
+ "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
"Failed to connect to integration manager": "Failed to connect to integration manager",
"You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled",
"Add some now": "Add some now",
@@ -1074,8 +1074,6 @@
"There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.",
"Error creating alias": "Error creating alias",
"There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.",
- "Invalid alias format": "Invalid alias format",
- "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias",
"Error removing alias": "Error removing alias",
"There was an error removing that alias. It may no longer exist or a temporary error occurred.": "There was an error removing that alias. It may no longer exist or a temporary error occurred.",
"Main address": "Main address",
@@ -1091,11 +1089,9 @@
"Showing flair for these communities:": "Showing flair for these communities:",
"This room is not showing flair for any communities": "This room is not showing flair for any communities",
"New community ID (e.g. +foo:%(localDomain)s)": "New community ID (e.g. +foo:%(localDomain)s)",
- "Room avatar": "Room avatar",
- "Upload room avatar": "Upload room avatar",
- "No room avatar": "No room avatar",
"Room Name": "Room Name",
"Room Topic": "Room Topic",
+ "Room avatar": "Room avatar",
"You have enabled URL previews by default.": "You have enabled URL previews by default.",
"You have disabled URL previews by default.": "You have disabled URL previews by default.",
"URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.",
@@ -1110,7 +1106,6 @@
"%(count)s verified sessions|other": "%(count)s verified sessions",
"%(count)s verified sessions|one": "1 verified session",
"Direct message": "Direct message",
- "Unverify user": "Unverify user",
"Remove from community": "Remove from community",
"Disinvite this user from community?": "Disinvite this user from community?",
"Remove this user from community?": "Remove this user from community?",
@@ -1120,8 +1115,8 @@
"This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.",
"Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.",
"Messages in this room are end-to-end encrypted.": "Messages in this room are end-to-end encrypted.",
- "Security": "Security",
"Verify": "Verify",
+ "Security": "Security",
"Sunday": "Sunday",
"Monday": "Monday",
"Tuesday": "Tuesday",
@@ -1254,7 +1249,6 @@
"Please create a new issue on GitHub so that we can investigate this bug.": "Please create a new issue on GitHub so that we can investigate this bug.",
"collapse": "collapse",
"expand": "expand",
- "Communities": "Communities",
"You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)",
"Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s",
"Rotate Left": "Rotate Left",
@@ -1262,6 +1256,7 @@
"Rotate Right": "Rotate Right",
"Rotate clockwise": "Rotate clockwise",
"Download this file": "Download this file",
+ "Language Dropdown": "Language Dropdown",
"Manage Integrations": "Manage Integrations",
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)sjoined %(count)s times",
@@ -1427,6 +1422,14 @@
"View Servers in Room": "View Servers in Room",
"Toolbox": "Toolbox",
"Developer Tools": "Developer Tools",
+ "Failed to find the following users": "Failed to find the following users",
+ "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s",
+ "Recent Conversations": "Recent Conversations",
+ "Suggestions": "Suggestions",
+ "Show more": "Show more",
+ "Direct Messages": "Direct Messages",
+ "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.",
+ "Go": "Go",
"An error has occurred.": "An error has occurred.",
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.",
"Verifying this user will mark their device as trusted, and also mark your device as trusted to them.": "Verifying this user will mark their device as trusted, and also mark your device as trusted to them.",
@@ -1538,7 +1541,6 @@
"Upload files (%(current)s of %(total)s)": "Upload files (%(current)s of %(total)s)",
"Upload files": "Upload files",
"Upload all": "Upload all",
- "Upload": "Upload",
"This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.",
"These files are too large to upload. The file size limit is %(limit)s.": "These files are too large to upload. The file size limit is %(limit)s.",
"Some files are too large to be uploaded. The file size limit is %(limit)s.": "Some files are too large to be uploaded. The file size limit is %(limit)s.",
@@ -1620,6 +1622,7 @@
"Hide": "Hide",
"Home": "Home",
"Sign in": "Sign in",
+ "Help": "Help",
"Reload": "Reload",
"Take picture": "Take picture",
"Remove for everyone": "Remove for everyone",
@@ -1627,6 +1630,7 @@
"User Status": "User Status",
"powered by Matrix": "powered by Matrix",
"This homeserver would like to make sure you are not a robot.": "This homeserver would like to make sure you are not a robot.",
+ "Country Dropdown": "Country Dropdown",
"Custom Server Options": "Custom Server Options",
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.",
"To continue, please enter your password.": "To continue, please enter your password.",
@@ -1762,6 +1766,7 @@
"Did you know: you can use communities to filter your Riot.im experience!": "Did you know: you can use communities to filter your Riot.im experience!",
"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.": "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.",
"Error whilst fetching joined communities": "Error whilst fetching joined communities",
+ "Communities": "Communities",
"Create a new community": "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.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.",
"You have no visible notifications": "You have no visible notifications",
@@ -1930,6 +1935,7 @@
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.",
"File to import": "File to import",
"Import": "Import",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup",
"Great! This passphrase looks strong enough.": "Great! This passphrase looks strong enough.",
"Warning: You should only set up secret storage from a trusted computer.": "Warning: You should only set up secret storage from a trusted computer.",
"We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.",
@@ -1956,6 +1962,7 @@
"Your access to encrypted messages is now protected.": "Your access to encrypted messages is now protected.",
"Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.",
"Set up secret storage": "Set up secret storage",
+ "Migrate from Key Backup": "Migrate from Key Backup",
"Secure your encrypted messages with a passphrase": "Secure your encrypted messages with a passphrase",
"Confirm your passphrase": "Confirm your passphrase",
"Recovery key": "Recovery key",
diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json
index 453f09bb25..e0e667ecce 100644
--- a/src/i18n/strings/eu.json
+++ b/src/i18n/strings/eu.json
@@ -1842,7 +1842,7 @@
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s erabiltzaileak bideo-dei bat abiatu du. (Nabigatzaile honek ez du onartzen)",
"Try out new ways to ignore people (experimental)": "Probatu jendea ez entzuteko modu berriak (esperimentala)",
"Send verification requests in direct message, including a new verification UX in the member panel.": "Bidali egiaztatze eskariak mezu zuzenetan, kide paneleko egiaztatze interfaze berria barne.",
- "Enable cross-signing to verify per-user instead of per-device (in development)": "Gaitu sinatze gurutzatua erabiltzaileko eta ez gailuko egiaztatzeko (garapenean)",
+ "Enable cross-signing to verify per-user instead of per-device (in development)": "Gaitu zeharkako sinadura erabiltzaileko eta ez gailuko egiaztatzeko (garapenean)",
"Enable local event indexing and E2EE search (requires restart)": "Gaitu gertaera lokalen indexazioa eta E2EE bilaketa (berrabiarazi behar da)",
"Match system theme": "Bat egin sistemako gaiarekin",
"My Ban List": "Nire debeku-zerrenda",
@@ -1923,5 +1923,114 @@
"Remove for everyone": "Kendu denentzat",
"Remove for me": "Kendu niretzat",
"Verification Request": "Egiaztaketa eskaria",
- " (1/%(totalCount)s)": " (1/%(totalCount)s)"
+ " (1/%(totalCount)s)": " (1/%(totalCount)s)",
+ "Send cross-signing keys to homeserver": "Bidali zeharkako sinaduraren gakoak hasiera-zerbitzarira",
+ "Error upgrading room": "Errorea gela eguneratzean",
+ "Double check that your server supports the room version chosen and try again.": "Egiaztatu zure zerbitzariak aukeratutako gela bertsioa onartzen duela eta saiatu berriro.",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen erabiltzaileak debekatzen zituen araua kendu du",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen gelak debekatzen zituen araua kendu du",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen zerbitzariak debekatzen zituen araua kendu du",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datorren debeku arau bat kendu du",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s erabiltzaileak baliogabeko debeku arau bat eguneratu du",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen erabiltzaileak debekatzen dituen araua eguneratu du, arrazoia: %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen gelak debekatzen dituen araua eguneratu du, arrazoia: %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen zerbitzariak debekatzen dituen araua eguneratu du, arrazoia: %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datorren debeku arau bat eguneratu du, arrazoia: %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen erabiltzaileak debekatzen dituen araua sortu du, arrazoia: %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen gelak debekatzen dituen araua sortu du, arrazoia: %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datozen zerbitzariak debekatzen dituen araua sortu du, arrazoia: %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s erabiltzaileak %(glob)s adierazpenarekin bat datorren debeku arau bat sortu du, arrazoia: %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s erabiltzaileak erabiltzaileak debekatzen dituen araua aldatu du %(oldGlob)s adierazpenetik %(newGlob)s adierazpenera, arrazoia: %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s erabiltzaileak gelak debekatzen dituen araua aldatu du %(oldGlob)s adierazpenetik %(newGlob)s adierazpenera, arrazoia: %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s erabiltzaileak zerbitzariak debekatzen dituen araua aldatu du %(oldGlob)s adierazpenetik %(newGlob)s adierazpenera, arrazoia: %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s erabiltzaileak debeku arau bat aldatu du %(oldGlob)s adierazpenetik %(newGlob)s adierazpenera, arrazoia: %(reason)s",
+ "Cross-signing and secret storage are enabled.": "Zeharkako sinadura eta biltegi sekretua gaituta daude.",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "Zure kontuak zeharkako sinaduraren identitatea biltegi sekretuan du, baina gailu honek oraindik ez du fidagarritzat.",
+ "Cross-signing and secret storage are not yet set up.": "Zeharkako sinadura eta biltegi sekretua ez daude oraindik ezarrita.",
+ "Bootstrap cross-signing and secret storage": "Abiatu zeharkako sinadura eta biltegi sekretua",
+ "Cross-signing public keys:": "Zeharkako sinaduraren gako publikoak:",
+ "on device": "gailuan",
+ "not found": "ez da aurkitu",
+ "Cross-signing private keys:": "Zeharkako sinaduraren gako pribatuak:",
+ "in secret storage": "biltegi sekretuan",
+ "Secret storage public key:": "Biltegi sekretuko gako publikoa:",
+ "in account data": "kontuaren datuetan",
+ "not stored": "gorde gabe",
+ "Backup has a valid signature from this user": "Babes-kopiak erabiltzaile honen baliozko sinadura bat du",
+ "Backup has a invalid signature from this user": "Babes-kopiak erabiltzaile honen baliogabeko sinadura bat du",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "Babes-kopiak %(deviceId)s ID-a duen erabiltzaile ezezagun baten sinadura du",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "Babes-kopiak %(deviceId)s ID-a duen gailu ezezagun baten sinadura du",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Babes-kopiaren gakoa biltegi sekretuan gorde da, gaina ezaugarri hau ez dago aktibatuta gailu honetan. Gaitu zeharkako sinadura laborategian babes-kopiaren egoera aldatzeko.",
+ "Backup key stored: ": "Babes-kopiaren gakoa gordeta: ",
+ "Start using Key Backup with Secure Secret Storage": "Hasi gakoen babes-kopia erabiltzen biltegi sekretu seguruarekin",
+ "Cross-signing": "Zeharkako sinadura",
+ "This message cannot be decrypted": "Mezu hau ezin da deszifratu",
+ "Unencrypted": "Zifratu gabe",
+ "Close preview": "Itxi aurrebista",
+ " wants to chat": " erabiltzaileak txateatu nahi du",
+ "Start chatting": "Hasi txateatzen",
+ "Hide verified sessions": "Ezkutatu egiaztatutako saioak",
+ "%(count)s verified sessions|other": "%(count)s egiaztatutako saio",
+ "%(count)s verified sessions|one": "Egiaztatutako saio 1",
+ "Reactions": "Erreakzioak",
+ " reacted with %(content)s": " erabiltzaileak %(content)s batekin erreakzionatu du",
+ "Automatically invite users": "Gonbidatu erabiltzaileak automatikoki",
+ "Upgrade private room": "Eguneratu gela pribatua",
+ "Upgrade public room": "Eguneratu gela publikoa",
+ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Gela eguneratzea ekintza aurreratu bat da eta akatsen, falta diren ezaugarrien, edo segurtasun arazoen erruz gela ezegonkorra denean aholkatzen da.",
+ "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Orokorrean honek zerbitzariak gela nola prozesatzen duen da duen eragin bakarra. RIot-ekin arazoak badituzu, eman akats baten berri.",
+ "You'll upgrade this room from to .": "Gela hau bertsiotik bertsiora eguneratuko duzu.",
+ "Upgrade": "Eguneratu",
+ "Enter secret storage passphrase": "Sartu biltegi sekretuko pasaesaldia",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Ezin izan da biltegi sekretura sartu. Egiaztatu pasaesaldi zuzena idatzi duzula.",
+ "Warning: You should only access secret storage from a trusted computer.": "Abisua: Biltegi sekretura ordenagailu fidagarri batetik konektatu beharko zinateke beti.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "Atzitu zure mezu seguruen historiala eta zeharkako sinaduraren identitatea beste gailuak egiaztatzeko zure pasa-esaldia sartuz.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Zure pasa-esaldia ahaztu baduzu berreskuratze gakoa erabili dezakezu edo berreskuratze aukera berriak ezarri ditzakezu.",
+ "Enter secret storage recovery key": "Sartu biltegi sekretuko berreskuratze-gakoa",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Ezin izan da biltegi sekretura sartu. Egiaztatu berreskuratze-gako zuzena sartu duzula.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "Atzitu zure mezu seguruen historiala eta zeharkako sinaduraren identitatea beste gailuak egiaztatzeko zure pasa-esaldia sartuz.",
+ "If you've forgotten your recovery key you can .": "Zure berreskuratze-gakoa ahaztu baduzu ditzakezu.",
+ "Warning: You should only set up key backup from a trusted computer.": "Abisua:: Gakoen babes-kopia fidagarria den gailu batetik egin beharko zenuke beti.",
+ "If you've forgotten your recovery key you can ": "Zure berreskuratze-gakoa ahaztu baduzu ditzakezu",
+ "Notification settings": "Jakinarazpenen ezarpenak",
+ "User Status": "Erabiltzaile-egoera",
+ "Warning: You should only set up secret storage from a trusted computer.": "Abisua: Biltegi sekretura ordenagailu fidagarri batetik konektatu beharko zinateke beti.",
+ "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Biltegi sekretua erabiliko dugu, aukeran, beste gailuak egiaztatzeko zure zeharkako sinaduraren identitatearen eta mezuen gakoen kopia zifratu bat zure zerbitzarian gordetzeko. Babestu zure mezu zifratuetara sarbidea pasa.esaldi batekin seguru mantentzeko.",
+ "Set up with a recovery key": "Ezarri berreskuratze gakoarekin",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Aukeran, berreskuratze pasa-esaldia ahazten baduzu, zure zifratutako mezuak berreskuratzeko erabili dezakezu.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Badaezpada, zure zifratutako mezuen historiala berreskuratzeko erabili dezakezu.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Gorde zure berreskuratze gakoa toki oso seguruan, esaterako pasahitz kudeatzaile batean (edo gordailu kutxan).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "Zure berreskuratze gakoa arbelera kopiatu da, itsatsi hemen:",
+ "Your recovery key is in your Downloads folder.": "Zure berreskuratze gakoa zure Deskargak karpetan dago.",
+ "Your access to encrypted messages is now protected.": "Zure zifratutako mezuetara sarbidea babestuta dago.",
+ "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Mezu seguruen berreskuratzea ezartzen ez bada, ezin izango duzu zure zifratutako mezuen historiala edo beste gailuak egiaztatzeko zeharkako sinadura berreskuratu saioa amaitzen baduzu edo beste gailu bat erabiltzen baduzu.",
+ "Set up secret storage": "Ezarri biltegi sekretua",
+ "Secure your encrypted messages with a passphrase": "Babestu zure zifratutako mezuak pasa-esaldi batekin",
+ "Storing secrets...": "Sekretuak gordetzen...",
+ "Unable to set up secret storage": "Ezin izan da biltegi sekretua ezarri",
+ "The message you are trying to send is too large.": "Bidali nahi duzun mezua handiegia da.",
+ "This user has not verified all of their devices.": "Erabiltzaileak ez ditu bere gailu guztiak egiaztatu.",
+ "You have not verified this user. This user has verified all of their devices.": "Ez duzu erabiltzaile hau egiaztatu. Erabiltzaile honek bere gailu guztiak egiaztatu ditu.",
+ "You have verified this user. This user has verified all of their devices.": "Erabiltzaile hau egiaztatu duzu. Erabiltzaile honek bere gailu guztiak egiaztatu ditu.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Ez dituzu zifratutako gela honetako erabiltzaile batzuk egiaztatu, edo hauek ez dituzte bere gailu guztiak egiaztatu.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Zifratutako gela honetako erabiltzaile guztiak egiaztatu dituzu, eta hauek bere gailu guztiak egiaztatu dituzte.",
+ "Language Dropdown": "Hizkuntza menua",
+ "Help": "Laguntza",
+ "Country Dropdown": "Herrialde menua",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Biltegi sekretua zure oraingo gakoen babes-kopiaren xehetasunak erabiliz ezarriko da. Zure biltegi sekretuaren pasa-esaldia eta berreskuratze gakoa lehen gakoen babes-kopiarako ziren berdinak izango dira",
+ "Migrate from Key Backup": "Migratu gakoen babes-kopiatik",
+ "New DM invite dialog (under development)": "Mezu zuzen bidezko gonbidapen elkarrizketa-koadro berria (garapenean)",
+ "Show info about bridges in room settings": "Erakutsi zubiei buruzko informazioa gelaren ezarpenetan",
+ "This bridge was provisioned by ": "Zubi hau erabiltzaileak hornitu du",
+ "This bridge is managed by .": "Zubi hau erabiltzaileak kudeatzen du.",
+ "Bridged into , on ": " kanalera zubia, protokoloan",
+ "Connected to on ": " kanalera konektatuta, sarean",
+ "Connected via %(protocolName)s": "%(protocolName)s bidez konektatuta",
+ "Bridge Info": "Zubiaren informazioa",
+ "Below is a list of bridges connected to this room.": "Behean gela honetara konektatutako zubien informazioa dago.",
+ "Show more": "Erakutsi gehiago",
+ "Recent Conversations": "Azken elkarrizketak",
+ "Direct Messages": "Mezu zuzenak",
+ "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Ez baduzu baten bat aurkitzen, eska egiezu bere erabiltzaile-izena, edo eman egiezu zurea (%(userId)s) edo profilera esteka.",
+ "Go": "Joan"
}
diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json
index 6f941b5d4a..e53afb2598 100644
--- a/src/i18n/strings/fi.json
+++ b/src/i18n/strings/fi.json
@@ -1078,7 +1078,7 @@
"This room is a continuation of another conversation.": "Tämä huone on jatkumo toisesta keskustelusta.",
"Don't ask me again": "Älä kysy uudelleen",
"Not now": "Ei nyt",
- "The conversation continues here.": "Keskustelu jatkuu tässä.",
+ "The conversation continues here.": "Keskustelu jatkuu täällä.",
"Share Link to User": "Jaa linkki käyttäjälle",
"Muted Users": "Mykistetyt käyttäjät",
"Timeline": "Aikajana",
@@ -1343,7 +1343,7 @@
"Failed to decrypt %(failedCount)s sessions!": "%(failedCount)s istunnon purkaminen epäonnistui!",
"Restored %(sessionCount)s session keys": "%(sessionCount)s istunnon avainta palautettu",
"Enter Recovery Passphrase": "Syötä palautuksen salalause",
- "Warning: you should only set up key backup from a trusted computer.": "Varoitus: sinun pitäisi ottaa avainvarmuuskopio käyttöön vain luotetulta tietokoneelta.",
+ "Warning: you should only set up key backup from a trusted computer.": "Varoitus: sinun pitäisi ottaa avainvarmuuskopio käyttöön vain luotetulla tietokoneella.",
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Pääse turvattuun viestihistoriaasi ja ota käyttöön turvallinen viestintä syöttämällä palautuksen salalauseesi.",
"If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Jos olet unohtanut palautuksen salalauseesi, voit käyttää palautusavaintasi tai ottaa käyttöön uuden palautustavan",
"Access your secure message history and set up secure messaging by entering your recovery key.": "Pääse turvattuun viestihistoriaasi ja ota käyttöön turvallinen viestintä syöttämällä palautusavaimesi.",
@@ -1926,5 +1926,104 @@
"Customise your experience with experimental labs features. Learn more.": "Muokkaa kokemustasi kokeellisilla laboratio-ominaisuuksia. Tutki vaihtoehtoja.",
"Error upgrading room": "Virhe päivitettäessä huonetta",
"Double check that your server supports the room version chosen and try again.": "Tarkista, että palvelimesi tukee valittua huoneversiota ja yritä uudelleen.",
- "Invite joined members to the new room automatically": "Kutsu huoneen jäsenet liittymään uuteen huoneeseen automaattisesti"
+ "Invite joined members to the new room automatically": "Kutsu huoneen jäsenet liittymään uuteen huoneeseen automaattisesti",
+ "Send cross-signing keys to homeserver": "Lähetä ristivarmennuksen tarvitsemat avaimet kotipalvelimelle",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s poisti porttikiellon käyttäjiltä, jotka täsmäsivät sääntöön %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s poisti huoneita estävän säännön %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s poisti palvelimia estävän säännön %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s poisti estosäännön %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s muokkasi epäkelpoa estosääntöä",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s muokkasi käyttäjiä estävää sääntöä %(glob)s seuraavasta syystä: %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s muokkasi huoneita estävää sääntöä %(glob)s seuraavasta syystä: %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s muokkasi palvelimia estävää sääntöä %(glob)s seuraavasta syystä: %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s muokkasi estosääntöä %(glob)s seuraavasta syystä: %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s loi porttikiellonsäännön %(glob)s, syy: %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s loi huoneita estävän säännön %(glob)s, syy: %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s loi palvelimia estävän säännön %(glob)s, syy: %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s loi estosäännön %(glob)s, syy: %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti sääntöä, joka esti käyttäjiä säännöllä %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti sääntöä, joka esti huoneita säännöllä %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti sääntöä, joka esti palvelimia säännöllä %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti estosääntöä muodosta %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s",
+ "The message you are trying to send is too large.": "Lähettämäsi viesti on liian suuri.",
+ "Cross-signing and secret storage are enabled.": "Ristivarmennus ja salavarasto on käytössä.",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "Tunnuksellasi on ristivarmennusidentiteetti salavarastossa, mutta tämä laite ei luota siihen.",
+ "Cross-signing and secret storage are not yet set up.": "Ristivarmennusta ja salavarastoa ei ole vielä otettu käyttöön.",
+ "Bootstrap cross-signing and secret storage": "Ota käyttöön ristivarmennus ja salavarasto",
+ "Cross-signing public keys:": "Ristivarmennuksen julkiset avaimet:",
+ "on device": "laitteella",
+ "not found": "ei löydetty",
+ "Cross-signing private keys:": "Ristivarmennuksen salaiset avaimet:",
+ "in secret storage": "salavarastossa",
+ "Secret storage public key:": "Salavaraston julkinen avain:",
+ "in account data": "tunnuksen tiedoissa",
+ "not stored": "ei tallennettu",
+ "Cross-signing": "Ristivarmennus",
+ "Backup has a valid signature from this user": "Varmuuskopiossa on kelvollinen allekirjoitus tältä käyttäjältä",
+ "Backup has a invalid signature from this user": "Varmuuskopiossa on epäkelpo allekirjoitus tältä käyttäjältä",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "Varmuuskopiossa on tuntematon allekirjoitus käyttäjältä, jonka ID on %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "Varmuuskopiossa on tuntematon allekirjoitus laitteelta, jonka ID on %(deviceId)s",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Vara-avain on tallennettu salavarastoon, mutta salavarasto ei ole käytössä tällä laitteella. Ota käyttöön ristivarmennus Laboratoriosta, jotta voi muokata avainvarmuuskopion tilaa.",
+ "Backup key stored: ": "Vara-avain on tallennettu: ",
+ "Start using Key Backup with Secure Secret Storage": "Aloita avainten varmuuskopiointi turvalliseen salavarastoon",
+ "This user has not verified all of their devices.": "Tämä käyttäjä ei ole varmentanut kaikkia laitteitaan.",
+ "You have not verified this user. This user has verified all of their devices.": "Et ole varmentanut tätä käyttäjää. Tämä käyttäjä on varmentanut kaikki laitteensa.",
+ "You have verified this user. This user has verified all of their devices.": "Olet varmentanut tämän käyttäjän. Tämä käyttäjä on varmentanut kaikki laitteensa.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Et ole varmentanut osaa tämän salausta käyttävän huoneen käyttäjistä tai he eivät ole varmentaneet omia laitteitaan.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Olet varmentanut kaikki käyttäjät tässä salausta käyttävässä huoneessa ja he ovat varmentaneet omat laitteensa.",
+ "This message cannot be decrypted": "Tätä viestiä ei voida avata luettavaksi",
+ "Unencrypted": "Suojaamaton",
+ "Close preview": "Sulje esikatselu",
+ " wants to chat": " haluaa keskustella",
+ "Start chatting": "Aloita keskustelu",
+ "Hide verified sessions": "Piilota varmennetut istunnot",
+ "%(count)s verified sessions|other": "%(count)s varmennettua istuntoa",
+ "%(count)s verified sessions|one": "1 varmennettu istunto",
+ "Reactions": "Reaktiot",
+ " reacted with %(content)s": " reagoi: %(content)s",
+ "Language Dropdown": "Kielipudotusvalikko",
+ "Automatically invite users": "Kutsu käyttäjät automaattisesti",
+ "Upgrade private room": "Päivitä yksityinen huone",
+ "Upgrade public room": "Päivitä julkinen huone",
+ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Huoneen päivittäminen on monimutkainen toimenpide ja yleensä sitä suositellaan, kun huone on epävakaa bugien, puuttuvien ominaisuuksien tai tietoturvaongelmien takia.",
+ "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Tämä yleensä vaikuttaa siihen, miten huonetta käsitellään palvelimella. Jos sinulla on ongelmia Riottisi kanssa, ilmoita virheestä.",
+ "You'll upgrade this room from to .": "Olat päivittämässä tätä huonetta versiosta versioon .",
+ "Upgrade": "Päivitä",
+ "Enter secret storage passphrase": "Syötä salavaraston salalause",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Salavavaraston avaaminen epäonnistui. Varmista, että syötit oikean salalauseen.",
+ "Warning: You should only access secret storage from a trusted computer.": "Varoitus: sinun pitäisi käyttää salavarastoa vain luotetulta tietokoneelta.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "Käytä turvattua viestihistoriaasi ja ristivarmennuksen identiteettiäsi muiden laitteiden varmentamiseen syöttämällä salalauseesi.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Jos olet unohtanut salalauseesi, voit käyttää palautusavaintasi tai asettaa uusia palautusvaihtoehtoja.",
+ "Enter secret storage recovery key": "Syötä salavaraston palautusavain",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Salavaraston käyttö epäonnistui. Varmista, että syötit oikean palautusavaimen.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "Käytä turvattua viestihistoriaasi ja ristivarmennuksen identiteettiäsi muiden laitteiden varmentamiseen syöttämällä palautusavaimesi.",
+ "If you've forgotten your recovery key you can .": "Jos olet unohtanut palautusavaimesi, voit .",
+ "Warning: You should only set up key backup from a trusted computer.": "Varoitus: sinun pitäisi ottaa avainten varmuuskopiointi käyttöön vain luotetulla tietokoneella.",
+ "If you've forgotten your recovery key you can ": "Jos olet unohtanut palautusavaimesi, voit ",
+ "Notification settings": "Ilmoitusasetukset",
+ "Help": "Ohje",
+ "User Status": "Käyttäjän tila",
+ "Country Dropdown": "Maapudotusvalikko",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Salavarasto otetaan käyttöön nykyisen avainten varmuuskopiointimenetelmäsi tiedoilla. Salavaraston salalause ja palautusavain tulee olemaan samat kuin ne olivat avainten varmuuskopioinnissasi",
+ "Warning: You should only set up secret storage from a trusted computer.": "Varoitus: sinun pitäisi ottaa salavarasto käyttöön vain luotetulla tietokoneella.",
+ "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Voimme vaihtoehtoisesti tallentaa salavarastoon salatun kopion ristivarmennuksen identiteetistäsi muiden laitteiden varmentamiseen ja lähettääksesi avaimia meidän palvelimelle. Suojaa pääsysi salattuihin viesteihisi pitämällä salalauseesi turvassa.",
+ "Set up with a recovery key": "Ota käyttöön palautusavaimella",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Turvaverkkona, voit käyttää sitä palauttamaan pääsysi salattuihin viesteihin, jos unohdat salalauseesi.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Turvaverkkona, voit käyttää sitä palauttamaan pääsysi salattuihin viesteihisi.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Pidä palautusavaimesi jossain hyvin turvallisessa paikassa, kuten salasananhallintasovelluksessa (tai kassakaapissa).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "Palautusavaimesi on kopioitu leikepöydällesi. Liitä se:",
+ "Your recovery key is in your Downloads folder.": "Palautusavaimesi on Lataukset-kansiossasi.",
+ "Your access to encrypted messages is now protected.": "Pääsysi salattuihin viesteihisi on nyt turvattu.",
+ "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Ottamatta käyttöön salavarastoa et voi palauttaa pääsyäsi salattuihin viesteihisi tai ristivarmennuksen identiteettiisi, jos kirjaudut ulos tai käytät toista laitetta.",
+ "Set up secret storage": "Ota salavarasto käyttöön",
+ "Migrate from Key Backup": "Siirrä tiedot vanhasta avainten varmuuskopiointijärjestelmästä",
+ "Secure your encrypted messages with a passphrase": "Turvaa salatut viestisi salalauseella",
+ "Storing secrets...": "Tallennetaan salaisuuksia...",
+ "Unable to set up secret storage": "Salavaraston käyttöönotto epäonnistui",
+ "New DM invite dialog (under development)": "Uusi dialogi kutsuille yksityiskeskusteluun (kehitysversio)",
+ "Show more": "Näytä lisää",
+ "Recent Conversations": "Viimeaikaiset keskustelut",
+ "Direct Messages": "Yksityisviestit",
+ "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Jos et löydä jotakuta, kysy hänen käyttäjätunnusta, tai anna oma käyttäjätunnuksesi (%(userId)s) tai linkin profiiliisi hänelle.",
+ "Go": "Mene"
}
diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json
index bed99fed86..c2ee7c575b 100644
--- a/src/i18n/strings/fr.json
+++ b/src/i18n/strings/fr.json
@@ -122,7 +122,7 @@
"%(senderName)s kicked %(targetName)s.": "%(senderName)s a expulsé %(targetName)s.",
"Kick": "Expulser",
"Kicks user with given id": "Expulse l'utilisateur à partir de son identifiant",
- "Labs": "Laboratoire",
+ "Labs": "Labo",
"Leave room": "Quitter le salon",
"%(targetName)s left the room.": "%(targetName)s a quitté le salon.",
"Local addresses for this room:": "Adresses locales pour ce salon :",
@@ -1938,5 +1938,100 @@
"Notification settings": "Paramètres de notification",
"User Status": "Statut de l’utilisateur",
"Reactions": "Réactions",
- " reacted with %(content)s": " ont réagi avec %(content)s"
+ " reacted with %(content)s": " ont réagi avec %(content)s",
+ " wants to chat": " veut discuter",
+ "Start chatting": "Commencer à discuter",
+ "Send cross-signing keys to homeserver": "Envoyer les clés de signature croisée au serveur d’accueil",
+ "Cross-signing public keys:": "Clés publiques de signature croisée :",
+ "on device": "sur l’appareil",
+ "not found": "non trouvé",
+ "Cross-signing private keys:": "Clés privées de signature croisée :",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s a supprimé la règle qui bannit les utilisateurs correspondant à %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s a supprimé la règle qui bannit les salons correspondant à %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s a supprimé la règle qui bannit les serveurs correspondant à %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s a supprimé une règle de bannissement correspondant à %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s a mis à jour une règle de bannissement non valide",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s a mis à jour la règle qui bannit les utilisateurs correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s a supprimé la règle qui bannit les salons correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s a supprimé la règle qui bannit les serveurs correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s a mis à jour la règle de bannissement correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s a créé une règle qui bannit les utilisateurs correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s a créé une règle qui bannit les salons correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s a créé une règle qui bannit les serveurs correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s a créé une règle de bannissement correspondant à %(glob)s pour %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s a changé une règle qui bannit les utilisateurs correspondant à %(oldGlob)s vers une règle correspondant à %(newGlob)s pour %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s a changé une règle qui bannit les salons correspondant à %(oldGlob)s vers une règle correspondant à %(newGlob)s pour %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s a changé une règle qui bannit les serveurs correspondant à %(oldGlob)s vers une règle correspondant à %(newGlob)s pour %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s a mis à jour une règle de bannissement correspondant à %(oldGlob)s vers une règle correspondant à %(newGlob)s pour %(reason)s",
+ "in secret storage": "dans le coffre secret",
+ "Secret storage public key:": "Clé publique du coffre secret :",
+ "in account data": "dans les données du compte",
+ "Bootstrap Secure Secret Storage": "Créer le coffre secret sécurisé",
+ "Cross-signing": "Signature croisée",
+ "Enter secret storage passphrase": "Saisir la phrase de passe du coffre secret",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Impossible d’accéder au coffre secret. Vérifiez que vous avez saisi la bonne phrase de passe.",
+ "Warning: You should only access secret storage from a trusted computer.": "Attention : Vous devriez uniquement accéder au coffre secret depuis un ordinateur de confiance.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "Accédez à l’historique de vos messages sécurisés et votre identité de signature croisée pour vérifier d’autres appareils en saisissant votre phrase de passe.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Si vous avez oublié votre phrase de passe, vous pouvez utiliser votre clé de récupération ou définir de nouvelles options de récupération.",
+ "Enter secret storage recovery key": "Saisir la clé de récupération du coffre secret",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Impossible d’accéder au coffre secret. Vérifiez que vous avez saisi la bonne clé de récupération.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "Accédez à l’historique de vos messages secrets et à votre identité de signature croisée pour vérifier d’autres appareils en saisissant votre clé de récupération.",
+ "If you've forgotten your recovery key you can .": "Si vous avez oublié votre clé de récupération vous pouvez .",
+ "Warning: You should only set up key backup from a trusted computer.": "Attention : Vous devriez uniquement configurer une sauvegarde de clés depuis un ordinateur de confiance.",
+ "If you've forgotten your recovery key you can ": "Si vous avez oublié votre clé de récupération, vous pouvez ",
+ "Warning: You should only set up secret storage from a trusted computer.": "Attention : Vous devriez uniquement configurer le coffre secret depuis un ordinateur de confiance.",
+ "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Nous utiliserons le coffre secret pour conserver optionnellement une copie chiffrée de votre identité de signature croisée pour vérifier d’autres appareils et des clés de messages sur notre serveur. Protégez votre accès aux messages chiffrés avec une phrase de passe pour la sécuriser.",
+ "Set up with a recovery key": "Configurer avec une clé de récupération",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Par mesure de sécurité, vous pouvez l’utiliser pour récupérer l’accès aux messages chiffrés si vous oubliez votre phrase de passe.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Par mesure de sécurité, vous pouvez l’utiliser pour récupérer l’accès à vos messages chiffrés.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Conservez votre clé de récupération dans un endroit très sécurisé, comme un gestionnaire de mots de passe (ou un coffre-fort).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "Votre clé de récupération a été copiée dans votre presse-papiers, collez-la pour :",
+ "Your recovery key is in your Downloads folder.": "Votre clé de récupération est dans votre dossier de Téléchargements.",
+ "Your access to encrypted messages is now protected.": "Votre accès aux messages chiffrés est maintenant protégé.",
+ "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Si vous ne configurez pas le coffre secret, vous ne pourrez pas récupérer l’accès à vos messages chiffrés ou à votre identité de signature croisée pour vérifier d’autres appareils si vous vous déconnectez ou si vous utilisez un autre appareil.",
+ "Set up secret storage": "Configurer le coffre secret",
+ "Secure your encrypted messages with a passphrase": "Sécuriser vos messages chiffrés avec une phrase de passe",
+ "Storing secrets...": "Sauvegarde des secrets…",
+ "Unable to set up secret storage": "Impossible de configurer le coffre secret",
+ "Cross-signing and secret storage are enabled.": "La signature croisée et le coffre secret sont activés.",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "Votre compte a une identité de signature croisée dans le coffre secret, mais cet appareil ne lui fait pas encore confiance.",
+ "Cross-signing and secret storage are not yet set up.": "La signature croisée et le coffre secret ne sont pas encore configurés.",
+ "Bootstrap cross-signing and secret storage": "Initialiser la signature croisée et le coffre secret",
+ "not stored": "non sauvegardé",
+ "Backup has a valid signature from this user": "La sauvegarde a une signature valide de cet utilisateur",
+ "Backup has a invalid signature from this user": "La sauvegarde a une signature non valide de cet utilisateur",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "La sauvegarde a une signature de l’utilisateur inconnu ayant pour identifiant %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "La sauvegarde a une signature de l’appareil inconnu ayant pour identifiant %(deviceId)s",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "La clé de sauvegarde est stockée dans le coffre secret, mais cette fonctionnalité n’est pas activée sur cet appareil. Activez la signature croisée dans le Labo pour modifier l’état de la sauvegarde de clé.",
+ "Backup key stored: ": "Clé de sauvegarde stockée : ",
+ "Start using Key Backup with Secure Secret Storage": "Commencer à utiliser la sauvegarde de clés avec le coffre secret sécurisé",
+ "Hide verified sessions": "Masquer les sessions vérifiées",
+ "%(count)s verified sessions|other": "%(count)s sessions vérifiées",
+ "%(count)s verified sessions|one": "1 session vérifiée",
+ "Close preview": "Fermer l’aperçu",
+ "This user has not verified all of their devices.": "Cet utilisateur n'a pas vérifié tous ses appareils.",
+ "You have not verified this user. This user has verified all of their devices.": "Vous n'avez pas vérifié cet utilisateur. Cet utilisateur a vérifié tous ses appareils.",
+ "You have verified this user. This user has verified all of their devices.": "Vous avez vérifié cet utilisateur. Cet utilisateur a vérifié tous ses appareils.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Certains utilisateurs dans ce salon chiffré n’ont pas été vérifiés par vous ou n’ont pas vérifié leurs propres appareils.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Tous les utilisateurs de ce salon chiffré ont été vérifiés par vous et ont vérifié leurs propres appareils.",
+ "Language Dropdown": "Sélection de la langue",
+ "Country Dropdown": "Sélection du pays",
+ "The message you are trying to send is too large.": "Le message que vous essayez d’envoyer est trop gros.",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Le coffre secret sera configuré en utilisant vos informations de sauvegarde de clés existantes. Votre phrase de passe et votre clé de récupération seront les mêmes que pour la sauvegarde de clés",
+ "Migrate from Key Backup": "Migrer depuis la sauvegarde de clés",
+ "Help": "Aide",
+ "New DM invite dialog (under development)": "Nouveau dialogue d’invitation aux MD (en développement)",
+ "Show more": "En savoir plus",
+ "Recent Conversations": "Conversations récentes",
+ "Direct Messages": "Messages directs",
+ "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Si vous n’arrivez pas à trouver quelqu’un, demandez-lui son nom d’utilisateur ou partagez votre nom d’utilisateur (%(userId)s) ou votre lien de profil.",
+ "Go": "C’est parti",
+ "Show info about bridges in room settings": "Afficher des informations à propos des passerelles dans les paramètres du salon",
+ "This bridge was provisioned by ": "Cette passerelle est fournie par ",
+ "This bridge is managed by .": "Cette passerelle est gérée par .",
+ "Bridged into , on ": "Relié à , sur ",
+ "Connected to on ": "Connecté à sur ",
+ "Connected via %(protocolName)s": "Connecté via %(protocolName)s",
+ "Bridge Info": "Informations de la passerelle",
+ "Below is a list of bridges connected to this room.": "Vous trouverez ci-dessous la liste des passerelles connectées à ce salon."
}
diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json
index 04b5b993ee..92113c3238 100644
--- a/src/i18n/strings/hu.json
+++ b/src/i18n/strings/hu.json
@@ -1939,5 +1939,100 @@
"Notification settings": "Értesítések beállítása",
"User Status": "Felhasználó állapota",
"Reactions": "Reakciók",
- " reacted with %(content)s": " ezzel reagáltak: %(content)s"
+ " reacted with %(content)s": " ezzel reagáltak: %(content)s",
+ " wants to chat": " csevegni szeretne",
+ "Start chatting": "Beszélgetés elkezdése",
+ "Send cross-signing keys to homeserver": "Kereszt-aláírás kulcsok küldése a matrix szervernek",
+ "Cross-signing public keys:": "Kereszt-aláírás nyilvános kulcsok:",
+ "on device": "eszközön",
+ "not found": "nem található",
+ "Cross-signing private keys:": "Kereszt-aláírás privát kulcsok:",
+ "in secret storage": "biztonsági tárolóban",
+ "Secret storage public key:": "Biztonsági tároló nyilvános kulcs:",
+ "in account data": "fiók adatokban",
+ "Bootstrap Secure Secret Storage": "Biztonsági Titok Tároló megnyitása",
+ "Cross-signing": "Kereszt-aláírás",
+ "Enter secret storage passphrase": "Add meg a jelmondatot a biztonsági tárolóhoz",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "A biztonsági tárolóhoz nem lehet hozzáférni. Kérlek ellenőrizd, hogy jó jelmondatot adtál-e meg.",
+ "Warning: You should only access secret storage from a trusted computer.": "Figyelem: Csak biztonságos eszközről férj hozzá a biztonságos tárolóhoz.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Ha elfelejtetted a jelmondatodat használd a visszaállítási kulcsod vagy állíts be új visszaállítási lehetőséget.",
+ "Enter secret storage recovery key": "Add meg a visszaállítási kulcsot a biztonsági tárolóhoz",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "A biztonsági tárolóhoz nem lehet hozzáférni. Kérlek ellenőrizd, hogy jó visszaállítási kulcsot adtál-e meg.",
+ "If you've forgotten your recovery key you can .": "Ha elfelejtetted a visszaállítási kulcsot .",
+ "Warning: You should only set up key backup from a trusted computer.": "Figyelmeztetés: Csak biztonságos számítógépről állíts be kulcs mentést.",
+ "If you've forgotten your recovery key you can ": "Ha elfelejtetted a visszaállítási kulcsot ",
+ "Warning: You should only set up secret storage from a trusted computer.": "Figyelem: Csak biztonságos eszközről állíts be biztonságos tárolót.",
+ "Set up with a recovery key": "Beállítás visszaállítási kulccsal",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Mint egy biztonsági háló, ha elfelejted a jelmondatot akkor felhasználhatod ahhoz, hogy hozzáférj a titkosított üzeneteidhez.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Használhatod egy biztonsági hálóként a titkosított üzenetekhez való hozzáférés visszaállításához.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókezelő (vagy széf).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "A visszaállítási kulcsod a vágólapra lett másolva, illeszd be ide:",
+ "Your recovery key is in your Downloads folder.": "A visszaállítási kulcsod a Letöltések mappában van.",
+ "Your access to encrypted messages is now protected.": "A titkosított üzenetekhez való hozzáférésed védve van.",
+ "Set up secret storage": "Biztonságos tároló beállítása",
+ "Secure your encrypted messages with a passphrase": "Biztosítsd a titkosított üzeneteidet jelmondattal",
+ "Storing secrets...": "Titkok tárolása...",
+ "Unable to set up secret storage": "A biztonsági tárolót nem sikerült beállítani",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s törölte azt a szabályt amivel ilyen felhasználók voltak kitiltva: %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s törölte azt a szabályt amivel ilyen szobák voltak kitiltva: %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s törölte azt a szabályt amivel ilyen szerverek voltak kitiltva: %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s törölte ezt a kitiltó szabályt: %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s frissített egy érvénytelen kitiltó szabályt",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s frissítette azt a szabályt amivel ilyen felhasználók voltak kitiltva: %(glob)s ezért: %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s frissítette azt a szabályt amivel ilyen szobák voltak kitiltva: %(glob)s ezért: %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s frissítette azt a szabályt amivel ilyen szerverek voltak kitiltva: %(glob)s ezért: %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s frissítette ezt a kitiltó szabályt: %(glob)s ezért: %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s hozzáadta azt a szabályt amivel ilyen felhasználók lesznek kitiltva: %(glob)s ezért: %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s hozzáadta azt a szabályt amivel ilyen szobák lesznek kitiltva: %(glob)s ezért: %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s hozzáadta azt a szabályt amivel ilyen szerverek lesznek kitiltva: %(glob)s ezért: %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s hozzáadta ezt a kitiltó szabályt: %(glob)s ezért: %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s megváltoztatta a szabályt amivel felhasználók voltak kitiltva erről: %(oldGlob)s erre: %(newGlob)s ezért: %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s megváltoztatta a szabályt amivel szobák voltak kitiltva erről: %(oldGlob)s erre: %(newGlob)s ezért: %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s megváltoztatta a szabályt amivel szerverek voltak kitiltva erről: %(oldGlob)s erre: %(newGlob)s ezért: %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s megváltoztatta a kitiltó szabályt erről: %(oldGlob)s erre: %(newGlob)s ezért: %(reason)s",
+ "Cross-signing and secret storage are enabled.": "Kereszt-aláírás és a biztonsági tároló engedélyezve van.",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "A fiókod a biztonsági tárolóban tartalmaz egy személyazonosságot a kereszt-aláíráshoz de egyenlőre nincs megbízhatónak jelölve ezen az eszközön.",
+ "Cross-signing and secret storage are not yet set up.": "Kereszt-aláírás és a biztonsági tároló egyenlőre nincs beállítva.",
+ "Bootstrap cross-signing and secret storage": "Kereszt-aláírás és biztonsági tároló beállítása",
+ "not stored": "nincs mentve",
+ "Backup has a valid signature from this user": "A mentés érvényes aláírást tartalmaz a felhasználótól",
+ "Backup has a invalid signature from this user": "A mentés érvénytelen aláírást tartalmaz a felhasználótól",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "A mentésnek ismeretlen felhasználótól származó aláírása van ezzel az azonosítóval: %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "A mentésnek ismeretlen eszköztől származó aláírása van ezzel az azonosítóval: %(deviceId)s",
+ "Backup key stored: ": "Visszaállítási kulcs tárolva: ",
+ "Start using Key Backup with Secure Secret Storage": "Kulcs Mentés és Biztonsági Titok Tároló használatának megkezdése",
+ "Hide verified sessions": "Ellenőrzött munkamenetek eltakarása",
+ "%(count)s verified sessions|other": "%(count)s ellenőrzött munkamenet",
+ "%(count)s verified sessions|one": "1 ellenőrzött munkamenet",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Visszaállítási kulcs a biztonsági tárolóban van elmentve, de ezen az eszközön ez a lehetőség nincs engedélyezve. Engedélyezd a kulcs mentés állapotának módosításához a kereszt-aláírást a Laborban.",
+ "Close preview": "Előnézet bezárása",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "A jelmondat megadásával hozzáférhetsz a titkosított üzeneteidhez és a kereszt-aláíráshoz tartozó azonosítódhoz amivel más eszközöket ellenőrizhetsz.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "A visszaállítási kulcs megadásával hozzáférhetsz a titkosított üzeneteidhez és a kereszt-aláíráshoz tartozó azonosítódhoz amivel más eszközöket ellenőrizhetsz.",
+ "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Biztonsági tárolót fogunk használni, hogy tárolhassuk a kereszt-aláíráshoz tartozó azonosítót titkosított formában, amivel más eszközöket és üzenet kulcsokat lehet ellenőrizni. Védd a titkosított üzenetekhez való hozzáférést jelmondattal és azt tartsd titokban.",
+ "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "A biztonsági tároló beállítása nélkül ha kijelentkezel és egy másik eszközön lépsz be nem fogsz hozzáférni a titkosított üzeneteidhez és a kereszt-aláíráshoz tartozó azonosítódhoz amivel más eszközöket ellenőrizhetsz.",
+ "This user has not verified all of their devices.": "Ez a felhasználó még ellenőrizte az összes eszközét.",
+ "You have not verified this user. This user has verified all of their devices.": "Ezt a felhasználót még nem ellenőrizted. Minden eszközét ellenőrizte ez a felhasználó.",
+ "You have verified this user. This user has verified all of their devices.": "Ezt a felhasználót ellenőrizted. Minden eszközét ellenőrizte ez a felhasználó.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Néhány felhasználót még nem ellenőriztél ebben a titkosított szobában vagy még ők nem ellenőrizték az összes eszközüket.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Minden felhasználót ellenőriztél ebben a titkosított szobában és ők ellenőrizték az összes eszközüket.",
+ "Language Dropdown": "Nyelvválasztó lenyíló menü",
+ "Country Dropdown": "Ország lenyíló menü",
+ "The message you are trying to send is too large.": "Túl nagy képet próbálsz elküldeni.",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "A Biztonsági Tároló a már létező kulcs mentés adatai felhasználásával lesz létrehozva. A biztonsági tároló jelmondata és a visszaállítási kulcs meg fog egyezni a kulcs mentésénél használttal",
+ "Migrate from Key Backup": "Mozgatás a Kulcs Mentésből",
+ "Help": "Segítség",
+ "New DM invite dialog (under development)": "Új KB (DM) meghívó párbeszédablak (fejlesztés alatt)",
+ "Show more": "Mutass többet",
+ "Recent Conversations": "Legújabb Beszélgetések",
+ "Direct Messages": "Közvetlen Beszélgetések",
+ "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Ha nem találsz meg valakit, kérdezd meg a felhasználói nevét vagy oszd meg a te felhasználói nevedet (%(userId)s) vagy a profil hivatkozást.",
+ "Go": "Menj",
+ "Show info about bridges in room settings": "Híd információk megmutatása a szoba beállításoknál",
+ "This bridge was provisioned by ": "Ezt a hidat ez a felhasználó hozta létre: ",
+ "This bridge is managed by .": "Ezt a hidat ez a felhasználó kezeli: .",
+ "Bridged into , on ": "Híd ide: , ezzel a protokollal: ",
+ "Connected to on ": "Ide csatlakozva: itt: ",
+ "Connected via %(protocolName)s": "Ezzel a protokollal csatlakozva: %(protocolName)s",
+ "Bridge Info": "Híd információ",
+ "Below is a list of bridges connected to this room.": "Alább látható a lista a szobához kapcsolódó hidakról."
}
diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json
index 6910de79fc..390729950b 100644
--- a/src/i18n/strings/it.json
+++ b/src/i18n/strings/it.json
@@ -271,9 +271,9 @@
"Blacklisted": "In lista nera",
"device id: ": "ID dispositivo: ",
"Disinvite": "Revoca invito",
- "Kick": "Caccia fuori",
+ "Kick": "Butta fuori",
"Disinvite this user?": "Revocare l'invito a questo utente?",
- "Kick this user?": "Cacciare questo utente?",
+ "Kick this user?": "Buttare fuori questo utente?",
"Failed to kick": "Espulsione fallita",
"Unban": "Togli ban",
"Ban": "Bandisci",
@@ -858,7 +858,7 @@
"All messages": "Tutti i messaggi",
"Call invitation": "Invito ad una chiamata",
"Downloading update...": "Scaricamento aggiornamento...",
- "State Key": "Chiave dello Stato",
+ "State Key": "Chiave dello stato",
"Failed to send custom event.": "Impossibile inviare evento personalizzato.",
"What's new?": "Cosa c'è di nuovo?",
"Notify me for anything else": "Notificami per qualsiasi altra cosa",
@@ -1923,5 +1923,99 @@
"%(senderName)s placed a video call.": "%(senderName)s ha effettuato una videochiamata.",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s ha effettuato una videochiamata. (non supportata da questo browser)",
"Clear notifications": "Cancella le notifiche",
- "Customise your experience with experimental labs features. Learn more.": "Personalizza la tua esperienza con funzionalità sperimentali. Maggiori informazioni."
+ "Customise your experience with experimental labs features. Learn more.": "Personalizza la tua esperienza con funzionalità sperimentali. Maggiori informazioni.",
+ "Error upgrading room": "Errore di aggiornamento stanza",
+ "Double check that your server supports the room version chosen and try again.": "Controlla che il tuo server supporti la versione di stanza scelta e riprova.",
+ "This message cannot be decrypted": "Questo messaggio non può essere decifrato",
+ "Unencrypted": "Non criptato",
+ "Reactions": "Reazioni",
+ " reacted with %(content)s": " ha reagito con %(content)s",
+ "Automatically invite users": "Invita utenti automaticamente",
+ "Upgrade private room": "Aggiorna stanza privata",
+ "Upgrade public room": "Aggiorna stanza pubblica",
+ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Aggiornare una stanza è un'azione avanzata ed è consigliabile quando una stanza non è stabile a causa di errori, funzioni mancanti o vulnerabilità di sicurezza.",
+ "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Solitamente ciò influisce solo come la stanza viene elaborata sul server. Se stai riscontrando problemi con il tuo Riot, segnala un errore.",
+ "You'll upgrade this room from to .": "Aggiornerai questa stanza dalla alla .",
+ "Upgrade": "Aggiorna",
+ "Notification settings": "Impostazioni di notifica",
+ "User Status": "Stato utente",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s ha rimosso la regola che bandisce utenti corrispondenti a %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s ha rimosso la regola che bandisce stanze corrispondenti a %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s ha rimosso la regola che bandisce server corrispondenti a %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s ha rimosso una regola di ban corrispondente a %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s ha aggiornato una regola di ban non valida",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s ha aggiornato la regola che bandisce utenti corrispondenti a %(glob)s perchè %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s ha aggiornato la regola che bandisce stanze corrispondenti a %(glob)s perchè %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s ha aggiornato la regola che bandisce server corrispondenti a %(glob)s perchè %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s ha aggiornato una regola di ban corrispondente a %(glob)s perchè %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s ha creato una regola che bandisce utenti corrispondenti a %(glob)s perchè %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s ha creato una regola che bandisce stanze corrispondenti a %(glob)s perchè %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s ha creato una regola che bandisce server corrispondenti a %(glob)s perchè %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s ha creato una regola di ban corrispondente a %(glob)s perchè %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ha modificato una regola che bandiva utenti corrispondenti a %(oldGlob)s per corrispondere a %(newGlob)s perchè %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ha modificato una regola che bandiva stanze corrispondenti a %(oldGlob)s per corrispondere a %(newGlob)s perchè %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ha modificato una regola che bandiva server corrispondenti a %(oldGlob)s per corrispondere a %(newGlob)s perchè %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ha modificato una regola di ban che corrispondeva a %(oldGlob)s per corrispondere a %(newGlob)s perchè %(reason)s",
+ "Send cross-signing keys to homeserver": "Invia chiavi di firma incrociata all'homeserver",
+ "Cross-signing public keys:": "Chiavi pubbliche di firma incrociata:",
+ "on device": "sul dispositivo",
+ "not found": "non trovato",
+ "Cross-signing private keys:": "Chiavi private di firma incrociata:",
+ "in secret storage": "in un archivio segreto",
+ "Secret storage public key:": "Chiave pubblica dell'archivio segreto:",
+ "in account data": "nei dati dell'account",
+ "Cross-signing": "Firma incrociata",
+ " wants to chat": " vuole chattare",
+ "Start chatting": "Inizia a chattare",
+ "Enter secret storage passphrase": "Inserisci la password dell'archivio segreto",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Impossibile accedere all'archivio segreto. Controlla di avere inserito la password corretta.",
+ "Warning: You should only access secret storage from a trusted computer.": "Attenzione: dovresti accedere all'archivio segreto solo da un computer fidato.",
+ "Cross-signing and secret storage are enabled.": "La firma incrociata e l'archivio segreto sono attivi.",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "Il tuo account ha un'identità a firma incrociata in archivio segreto, ma non è ancora fidata da questo dispositivo.",
+ "Cross-signing and secret storage are not yet set up.": "La firma incrociata e l'archivio segreto non sono ancora impostati.",
+ "not stored": "non salvato",
+ "Backup has a valid signature from this user": "Il backup ha una firma valida da questo utente",
+ "Backup has a invalid signature from this user": "Il backup ha una firma non valida da questo utente",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "Il backup ha una firma dall'utente sconosciuto con ID %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "Il backup ha una firma dal dispositivo sconosciuto con ID %(deviceId)s",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Backup chiavi presente nell'archivio segreto, ma questa funzione non è attiva sul dispositivo. Attiva la firma incrociata in Labs per modificare lo stato del backup.",
+ "Backup key stored: ": "Backup chiavi salvato: ",
+ "Start using Key Backup with Secure Secret Storage": "Inizia ad usare il Backup Chiavi con l'Archivio Segreto",
+ "Hide verified sessions": "Nascondi sessioni verificate",
+ "%(count)s verified sessions|other": "%(count)s sessioni verificate",
+ "%(count)s verified sessions|one": "1 sessione verificata",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "Accedi alla cronologia sicura dei tuoi messaggi e all'identità a firma incrociata per verificare altri dispositivi inserendo la tua password.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Se hai dimenticato la password puoi usare la tua chiave di recupero o impostare nuove opzioni di recupero.",
+ "Enter secret storage recovery key": "Inserisci la chiave di recupero dell'archivio segreto",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Impossibile accedere all'archivio segreto. Controlla di avere inserito la chiave di recupero corretta.",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "Accedi alla cronologia sicura dei tuoi messaggi e all'identità a firma incrociata per verificare altri dispositivi inserendo la tua chiave di recupero.",
+ "If you've forgotten your recovery key you can .": "Se hai dimenticato la tua chiave di recupero puoi .",
+ "Warning: You should only set up key backup from a trusted computer.": "Attenzione: dovresti impostare il backup chiavi solo da un computer fidato.",
+ "If you've forgotten your recovery key you can ": "Se hai dimenticato la tua chiave di recupero puoi ",
+ "Warning: You should only set up secret storage from a trusted computer.": "Attenzione: dovresti impostare l'archivio segreto solo da un computer fidato.",
+ "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Utilizzeremo l'archivio segreto per memorizzare facoltativamente una copia criptata della tua identità a firma incrociata per verificare altri dispositivi e chiavi dei messaggi sul nostro server. Proteggi il tuo accesso ai messaggi cifrati con una password per tenerlo al sicuro.",
+ "Set up with a recovery key": "Imposta con una chiave di recupero",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Come àncora di salvezza, puoi usarla per recuperare l'accesso ai tuoi messaggi cifrati se dimentichi la password.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Come àncora di salvezza, puoi usarla per recuperare l'accesso ai tuoi messaggi cifrati.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Tieni la chiave di recupero in un posto molto sicuro, come un gestore di password (o una cassaforte).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "La tua chiave di recupero è stata copiata negli appunti, incollala in:",
+ "Your recovery key is in your Downloads folder.": "La chiave di recupero è nella tua cartella Scaricati.",
+ "Your access to encrypted messages is now protected.": "L'accesso ai tuoi messaggi cifrati è ora protetto.",
+ "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Senza l'impostazione dell'archivio segreto, non sarà possibile recuperare l'accesso ai messaggi cifrati o l'identità a firma incrociata per verificare altri dispositivi se ti disconnetti o utilizzi un altro dispositivo.",
+ "Set up secret storage": "Imposta l'archivio segreto",
+ "Secure your encrypted messages with a passphrase": "Proteggi i tuoi messaggi cifrati con una password",
+ "Storing secrets...": "Memorizzo i segreti...",
+ "Unable to set up secret storage": "Impossibile impostare un archivio segreto",
+ "Close preview": "Chiudi anteprima",
+ "This user has not verified all of their devices.": "Questo utente non ha verificato tutti i suoi dispositivi.",
+ "You have not verified this user. This user has verified all of their devices.": "Non hai verificato questo utente. Questo utente ha verificato tutti i suoi dispositivi.",
+ "You have verified this user. This user has verified all of their devices.": "Hai verificato questo utente. Questo utente ha verificato tutti i suoi dispositivi.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Non hai verificato alcuni utenti in questa stanza criptata o essi non hanno verificato i loro dispositivi.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Hai verificato tutti gli utenti in questa stanza criptata ed essi hanno verificato i loro dispositivi.",
+ "Language Dropdown": "Lingua a tendina",
+ "Country Dropdown": "Nazione a tendina",
+ "The message you are trying to send is too large.": "Il messaggio che stai tentando di inviare è troppo grande.",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "L'archivio segreto verrà impostato usando i dettagli del tuo backup chiavi. La password dell'archivio segreto e la chiave di ripristino saranno le stesse del tuo backup chiavi",
+ "Migrate from Key Backup": "Migra dal backup chiavi",
+ "Help": "Aiuto"
}
diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json
index 14a1b6d429..cce9a81188 100644
--- a/src/i18n/strings/ja.json
+++ b/src/i18n/strings/ja.json
@@ -446,7 +446,7 @@
"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.": "共有キーリクエストは、他の端末に自動的に送信されます。 他の端末での共有キーリクエストを拒否または却下した場合は、ここをクリックしてこのセッションのキーを再度要求してください。",
"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.": "他の端末から暗号化キーを再リクエスト requestLink>します。",
+ "Re-request encryption keys from your other devices.": "他の端末から暗号化キーを再リクエストします。",
"Undecryptable": "解読不能",
"Encrypted by an unverified device": "未検証の端末によって暗号化されました",
"Unencrypted message": "暗号化されていないメッセージ",
diff --git a/src/i18n/strings/mn.json b/src/i18n/strings/mn.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/src/i18n/strings/mn.json
@@ -0,0 +1 @@
+{}
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
index c71e4236b9..fc8b37140e 100644
--- a/src/i18n/strings/ru.json
+++ b/src/i18n/strings/ru.json
@@ -1830,5 +1830,7 @@
"My Ban List": "Мой список запрещенных",
"Ignored/Blocked": "Игнорируемые/Заблокированные",
"Error adding ignored user/server": "Ошибка добавления игнорируемого пользователя/сервера",
- "Error subscribing to list": "Ошибка при подписке на список"
+ "Error subscribing to list": "Ошибка при подписке на список",
+ "Send cross-signing keys to homeserver": "Отправка ключей перекрестной подписи на домашний сервер",
+ "Error upgrading room": "Ошибка обновления комнаты"
}
diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json
index 1782059252..8befc3c18c 100644
--- a/src/i18n/strings/sk.json
+++ b/src/i18n/strings/sk.json
@@ -1187,70 +1187,70 @@
"Verify this user by confirming the following number appears on their screen.": "Overte tohoto používateľa tým, že zistíte, či sa na jeho obrazovke objaví nasledujúce číslo.",
"Unable to find a supported verification method.": "Nie je možné nájsť podporovanú metódu overenia.",
"For maximum security, we recommend you do this in person or use another trusted means of communication.": "Pre zachovanie maximálnej bezpečnosti odporúčame, aby ste toto vykonali osobne, alebo použili iný dôverihodný komunikačný kanál.",
- "Dog": "Pes",
- "Cat": "Mačka",
- "Lion": "Lev",
+ "Dog": "Hlava psa",
+ "Cat": "Hlava mačky",
+ "Lion": "Hlava leva",
"Horse": "Kôň",
- "Unicorn": "Jednorožec",
- "Pig": "Prasa",
+ "Unicorn": "Hlava jednorožca",
+ "Pig": "Hlava Prasaťa",
"Elephant": "Slon",
- "Rabbit": "Zajac",
- "Panda": "Panda",
+ "Rabbit": "Hlava Zajaca",
+ "Panda": "Hlava Pandy",
"Rooster": "Kohút",
"Penguin": "Tučniak",
"Turtle": "Korytnačka",
"Fish": "Ryba",
"Octopus": "Chobotnica",
"Butterfly": "Motýľ",
- "Flower": "Kvetina",
- "Tree": "Strom",
+ "Flower": "Tulipán",
+ "Tree": "Listnatý strom",
"Cactus": "Kaktus",
- "Mushroom": "Hríb",
+ "Mushroom": "Huba",
"Globe": "Zemeguľa",
- "Moon": "Mesiac",
+ "Moon": "Polmesiac",
"Cloud": "Oblak",
"Fire": "Oheň",
"Banana": "Banán",
- "Apple": "Jablko",
+ "Apple": "Červené jablko",
"Strawberry": "Jahoda",
- "Corn": "Kukurica",
+ "Corn": "Kukuričný klas",
"Pizza": "Pizza",
- "Cake": "Koláč",
- "Heart": "Srdce",
- "Smiley": "Úsmev",
+ "Cake": "Narodeninová torta",
+ "Heart": "Červené srdce",
+ "Smiley": "Škeriaca sa tvár",
"Robot": "Robot",
- "Hat": "Klobúk",
+ "Hat": "Cylinder",
"Glasses": "Okuliare",
- "Spanner": "Skrutkovač",
- "Santa": "Mikuláš",
+ "Spanner": "Francúzsky kľúč",
+ "Santa": "Santa Claus",
"Thumbs up": "palec nahor",
"Umbrella": "Dáždnik",
"Hourglass": "Presýpacie hodiny",
- "Clock": "Hodiny",
- "Gift": "Darček",
+ "Clock": "Budík",
+ "Gift": "Zabalený darček",
"Light bulb": "Žiarovka",
- "Book": "Kniha",
+ "Book": "Zatvorená kniha",
"Pencil": "Ceruzka",
- "Paperclip": "Kancelárska sponka",
+ "Paperclip": "Sponka na papier",
"Scissors": "Nožnice",
- "Padlock": "Visiaci zámok",
+ "Padlock": "Zatvorená zámka",
"Key": "Kľúč",
"Hammer": "Kladivo",
"Telephone": "Telefón",
- "Flag": "Vlajka",
- "Train": "Vlak",
+ "Flag": "Kockovaná zástava",
+ "Train": "Rušeň",
"Bicycle": "Bicykel",
"Aeroplane": "Lietadlo",
"Rocket": "Raketa",
"Trophy": "Trofej",
- "Ball": "Lopta",
+ "Ball": "Futbal",
"Guitar": "Gitara",
"Trumpet": "Trúbka",
- "Bell": "Zvonček",
+ "Bell": "Zvon",
"Anchor": "Kotva",
"Headphones": "Slúchadlá",
- "Folder": "Priečinok",
- "Pin": "Pin",
+ "Folder": "Fascikel",
+ "Pin": "Špendlík",
"Your homeserver does not support device management.": "Váš domovský server nepodporuje správu zariadení.",
"Yes": "Áno",
"No": "Nie",
@@ -1461,7 +1461,7 @@
"The server does not support the room version specified.": "Server nepodporuje zadanú verziu miestnosti.",
"Name or Matrix ID": "Meno alebo Matrix ID",
"Email, name or Matrix ID": "Email, meno alebo Matrix ID",
- "Failed to start chat": "Nepodarilo sa spustiť konverzáciu",
+ "Failed to start chat": "Nepodarilo sa začať konverzáciu",
"Messages": "Správy",
"Actions": "Akcie",
"Room upgrade confirmation": "Potvrdenie aktualizácii miestnosti",
@@ -1514,5 +1514,115 @@
"Terms of service not accepted or the identity server is invalid.": "Neprijali ste Podmienky poskytovania služby alebo to nie je správny server.",
"Identity server has no terms of service": "Server totožností nemá žiadne podmienky poskytovania služieb",
"The identity server you have chosen does not have any terms of service.": "Zadaný server totožností nezverejňuje žiadne podmienky poskytovania služieb.",
- "Only continue if you trust the owner of the server.": "Pokračujte len v prípade, že dôverujete prevádzkovateľovi servera."
+ "Only continue if you trust the owner of the server.": "Pokračujte len v prípade, že dôverujete prevádzkovateľovi servera.",
+ "Add Email Address": "Pridať emailovú adresu",
+ "Add Phone Number": "Pridať telefónne číslo",
+ "Send cross-signing keys to homeserver": "Poslať kľúče pre podpisovanie naprieč zariadeniami na domovský server",
+ "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Táto akcia si vyžaduje mať overenú emailovú adresu alebo telefónne číslo cez predvolený server totožností , ale server nezverejnil podmienky používania.",
+ "Trust": "Dôverovať",
+ "Custom (%(level)s)": "Vlastná (%(level)s)",
+ "Sends a message as plain text, without interpreting it as markdown": "Odošle správu vo formáte obyčajný text, bez prekladu markdown",
+ "You do not have the required permissions to use this command.": "Na použitie tohoto príkazu nemáte dostatočné povolenia.",
+ "Error upgrading room": "Chyba pri aktualizácii miestnosti",
+ "Double check that your server supports the room version chosen and try again.": "Uistite sa, že domovský server podporuje zvolenú verziu miestnosti a skúste znovu.",
+ "Changes the avatar of the current room": "Zmení obrázok miestnosti",
+ "Use an identity server": "Použiť server totožností",
+ "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Aby ste mohli používateľov pozývať zadaním emailovej adresy, je potrebné nastaviť adresu servera totožností. Klepnutím na tlačidlo pokračovať použijete predvolený server (%(defaultIdentityServerName)s) a zmeniť to môžete v nastaveniach.",
+ "Use an identity server to invite by email. Manage in Settings.": "Server totožností sa použije na pozývanie používateľov zadaním emailovej adresy. Spravujte v nastaveniach.",
+ "%(senderName)s placed a voice call.": "%(senderName)s uskutočnil telefonát.",
+ "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s uskutočnil telefonát. (Nepodporované týmto prehliadačom)",
+ "%(senderName)s placed a video call.": "%(senderName)s uskutočnil video hovor.",
+ "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s uskutočnil video hovor. (Nepodporované týmto prehliadačom)",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s odstránil pravidlo zákazu vstúpiť používateľom zhodujúcich sa s %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s odstránil pravidlo zákaz vstúpiť do miestností zhodujúcich sa s %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s odstránil pravidlo zakázať vstúpiť z domovského servera zhodnými s %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s odstránil pravidlo zákazu vstupu zhodné s %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s aktualizoval neplatné pravidlo zákazu vstúpiť",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zákazu vstúpiť používateľom zhodujúcim sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť do miestností shodujúcich sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť z domovských serverov zhodujúcich sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť zhodujúce sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť používateľom zhodujúcim sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť do miestností zhodujúcich sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť z domovských serverov zhodujúcich sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť zhodujúce sa s %(glob)s, dôvod: %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s zmenil pravidlo zakázať vstúpiť používateľom pôvodne zhodujúcich sa s %(oldGlob)s na používateľov zhodujúcich sa s %(newGlob)s, dôvod: %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s zmenil pravidlo zakázať vstúpiť do miestností pôvodne zhodujúcich sa s %(oldGlob)s na miestnosti zhodujúce sa s %(newGlob)s, dôvod: %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s zmenil pravidlo zakázať vstúpiť z domovských serverov pôvodne sa zhodujúcich s %(oldGlob)s na servery zhodujúce sa s %(newGlob)s, dôvod: %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť pôvodne sa zhodujúce s %(oldGlob)s na %(newGlob)s, dôvod: %(reason)s",
+ "%(name)s (%(userId)s)": "%(name)s (%(userId)s)",
+ "Multiple integration managers": "Viacej integračných serverov",
+ "Try out new ways to ignore people (experimental)": "Vyskúšajte si nový spôsob ignorovania používateľov (experiment)",
+ "Send verification requests in direct message, including a new verification UX in the member panel.": "Požiadavky na overenie používateľov posielať v priamych konverzáciách, zahŕňa tiež nové rozhranie overenia v zozname používateľov.",
+ "Enable cross-signing to verify per-user instead of per-device (in development)": "Povoliť podpisovanie naprieč zariadeniami, umožňuje overovanie používateľov namiesto ich zariadení (vo vývoji)",
+ "Enable local event indexing and E2EE search (requires restart)": "Povoliť lokálne indexovanie udalostí a vyhľadávanie v šifrovaných miestnostiach",
+ "Use the new, faster, composer for writing messages": "Používať nový, rýchly, editor pri písaní správ",
+ "Match system theme": "Prispôsobiť sa vzhľadu systému",
+ "Send read receipts for messages (requires compatible homeserver to disable)": "Odosielať potvrdenia o prečítaní správ (na zakázanie je vyžadovaný kompatibilný domovský server)",
+ "Show previews/thumbnails for images": "Zobrazovať ukážky/náhľady obrázkov",
+ "My Ban List": "Môj zoznam zakázať vstúpiť",
+ "Decline (%(counter)s)": "Zamietnuť (%(counter)s)",
+ "The message you are trying to send is too large.": "Správa, ktorú sa usilujete odoslať, je príliš veľká.",
+ "This is your list of users/servers you have blocked - don't leave the room!": "Toto je zoznam používateľov / serverov, ktorých ste zablokovali - neopúšťajte miestnosť!",
+ "Upload": "Nahrať",
+ "Cross-signing and secret storage are enabled.": "Podpisovanie naprieč zariadeniami a bezpečné úložisko sú aktívne.",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "Na bezpečnom úložisku vo vašom účte máte uloženú totožnosť podpisovania naprieč zariadeniami, ale v tomto zariadení zatiaľ tejto totožnosti nedôverujete.",
+ "Cross-signing and secret storage are not yet set up.": "Podpisovanie naprieč zariadeniami a bezpečné úložisko zatiaľ nie sú nastavené.",
+ "Bootstrap cross-signing and secret storage": "Zaviesť podpisovanie naprieč zariadeniami a bezpečné úložisko",
+ "Cross-signing public keys:": "Verejné kľúče podpisovania naprieč zariadeniami:",
+ "on device": "na zariadení",
+ "not found": "nenájdené",
+ "Cross-signing private keys:": "Súkromné kľúče podpisovania naprieč zariadeniami:",
+ "in secret storage": "na bezpečnom úložisku",
+ "Secret storage public key:": "Verejný kľúč bezpečného úložiska:",
+ "in account data": "v údajoch účtu",
+ "Connecting to integration manager...": "Pripájanie k integračnému serveru…",
+ "Cannot connect to integration manager": "Nie je možné sa pripojiť k integračnému serveru",
+ "The integration manager is offline or it cannot reach your homeserver.": "Integračný server je offline, alebo nemôže pristupovať k domovskému serveru.",
+ "not stored": "neuložené",
+ "Backup has a valid signature from this user": "Záloha je podpísaná platným kľúčom od tohoto používateľa",
+ "Backup has a invalid signature from this user": "Záloha je podpísaná neplatným kľúčom od tohoto používateľa",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "Podpis zálohy pochádza od neznámeho používateľa ID %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "Podpis zálohy pochádza z neznámeho zariadenia ID %(deviceId)s",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Záloha kľúčov je uložená na bezpečnom úložisku, ale funkcia podpisovanie naprieč zariadeniami nie je na tomto zariadení aktívna. Ak chcete zmeniť stav zálohy kľúčov, zapnite podpisovanie naprieč zariadeniami v časti experimenty.",
+ "Backup key stored: ": "Záloha kľúčov uložená: ",
+ "Start using Key Backup with Secure Secret Storage": "Začnite používať zálohu kľúčov na bezpečnom úložisku",
+ "Clear notifications": "Vymazať oznámenia",
+ "Change identity server": "Zmeniť server totožností",
+ "Disconnect from the identity server and connect to instead?": "Naozaj si želáte odpojiť od servera totožností a pripojiť sa namiesto toho k serveru ?",
+ "Disconnect identity server": "Odpojiť server totožností",
+ "Disconnect from the identity server ?": "Naozaj sa chcete odpojiť od servera totožností ?",
+ "Disconnect": "Odpojiť",
+ "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Pred odpojením zo servera totožností by ste mali z neho odstrániť vaše osobné údaje. Žiaľ, server momentálne nie je dostupný a nie je možné sa k nemu pripojiť.",
+ "You should:": "Mali by ste:",
+ "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "Skontrolovať rozšírenia inštalované vo webovom prehliadači, ktoré by mohli blokovať prístup k serveru totožností (napr. rozšírenie Privacy Badger)",
+ "contact the administrators of identity server ": "Kontaktovať správcu servera totožností ",
+ "wait and try again later": "Počkať a skúsiť znovu neskôr",
+ "Disconnect anyway": "Napriek tomu sa odpojiť",
+ "You are still sharing your personal data on the identity server .": "na servery máte stále uložené vaše osobné údaje.",
+ "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Odporúčame, aby ste ešte pred odpojením sa zo servera totožností odstránili vašu emailovú adresu a telefónne číslo.",
+ "Identity Server (%(server)s)": "Server totožností (%(server)s)",
+ "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Momentálne na vyhľadávanie kontaktov a na možnosť byť nájdení kontaktmi ktorých poznáte používate . Zmeniť server totožností môžete nižšie.",
+ "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Ak nechcete na vyhľadávanie kontaktov a možnosť byť nájdení používať , zadajte adresu servera totožností nižšie.",
+ "Identity Server": "Server totožností",
+ "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Momentálne nepoužívate žiaden server totožností. Ak chcete vyhľadávať kontakty a zároveň umožniť ostatným vašim kontaktom, aby mohli nájsť vás, nastavte si server totožností nižšie.",
+ "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Ak sa odpojíte od servera totožností, vaše kontakty vás nebudú môcť nájsť a ani vy nebudete môcť pozývať používateľov zadaním emailovej adresy a telefónneho čísla.",
+ "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Používanie servera totožností je voliteľné. Ak sa rozhodnete, že nebudete používať server totožností, nebudú vás vaši známi môcť nájsť a ani vy nebudete môcť pozývať používateľov zadaním emailovej adresy alebo telefónneho čísla.",
+ "Do not use an identity server": "Nepoužívať server totožností",
+ "Enter a new identity server": "Zadať nový server totožností",
+ "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Použiť integračný server (%(serverName)s) na správu botov, widgetov a balíčkov s nálepkami.",
+ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Použiť integračný server na správu botov, widgetov a balíčkov s nálepkami.",
+ "Manage integrations": "Spravovať integrácie",
+ "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integračné servery zhromažďujú údaje nastavení, môžu spravovať widgety, odosielať vo vašom mene pozvánky alebo meniť úroveň moci.",
+ "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Súhlas s podmienkami používania servera totožností (%(serverName)s), aby ste mohli byť nájdení zadaním emailovej adresy alebo telefónneho čísla.",
+ "Discovery": "Objaviť",
+ "Deactivate account": "Deaktivovať účet",
+ "Clear cache and reload": "Vymazať vyrovnávaciu pamäť a načítať znovu",
+ "Customise your experience with experimental labs features. Learn more.": "Prispôsobte si zážitok s používania aktivovaním experimentálnych vlastností. Zistiť viac.",
+ "Ignored/Blocked": "Ignorovaní / Blokovaní",
+ "Error adding ignored user/server": "Chyba pri pridávaní ignorovaného používateľa / servera",
+ "Something went wrong. Please try again or view your console for hints.": "Niečo sa nepodarilo. Prosím, skúste znovu neskôr alebo si prečítajte ďalšie usmernenia zobrazením konzoly.",
+ "Error subscribing to list": "Chyba pri prihlasovaní sa do zoznamu",
+ "Please verify the room ID or alias and try again.": "Prosím, overte platnosť ID miestnosti alebo alias a skúste znovu.\nPlease verify the room ID or alias and try again.",
+ "Error removing ignored user/server": "Chyba pri odstraňovaní ignorovaného používateľa / servera"
}
diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json
index d04fe2b5e1..e833009d59 100644
--- a/src/i18n/strings/sq.json
+++ b/src/i18n/strings/sq.json
@@ -865,7 +865,7 @@
"%(oneUser)shad their invitation withdrawn %(count)s times|other": "Për %(oneUser)s përdorues ftesa u tërhoq mbrapsht %(count)s herë",
"%(oneUser)shad their invitation withdrawn %(count)s times|one": "U tërhoq mbrapsht ftesa për %(oneUser)s",
"Community IDs cannot be empty.": "ID-të e bashkësisë s’mund të jenë të zbrazëta.",
- "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. This action is irreversible.": "Kjo do ta bëjë llogarinë tuaj përgjithmonë të papërdorshme. S’do të jeni në gjendje të hyni në llogarinë tuaj, dhe askush s’do të jetë në gjendje të riregjistrojë të njëjtën ID përdoruesi. Kjo do të shkaktojë daljen e llogarisë tuaj nga krejt dhomat ku merrni pjesë, dhe do të heqë hollësitë e llogarisë tuaj nga shërbyesi juaj i identiteteve. Ky veprim është i paprapakthyeshëm.",
+ "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. This action is irreversible.": "Kjo do ta bëjë llogarinë tuaj përgjithmonë të papërdorshme. S’do të jeni në gjendje të hyni në llogarinë tuaj, dhe askush s’do të jetë në gjendje të riregjistrojë të njëjtën ID përdoruesi. Kjo do të shkaktojë daljen e llogarisë tuaj nga krejt dhomat ku merrni pjesë, dhe do të heqë hollësitë e llogarisë tuaj nga shërbyesi juaj i identiteteve. Ky veprim është i paprapakthyeshëm.",
"Deactivating your account does not by default cause us to forget messages you have sent. If you would like us to forget your messages, please tick the box below.": "Çaktivizimi i llogarisë tuaj nuk shkakton, si parazgjedhje, harrimin nga ne të mesazheve që keni dërguar. Nëse do të donit të harrojmë mesazhet tuaja, ju lutemi, i vini shenjë kutizës më poshtë.",
"Upgrade this room to version %(version)s": "Përmirësojeni këtë dhomë me versionin %(version)s",
"Share Room": "Ndani Dhomë Me të Tjerë",
@@ -1037,7 +1037,7 @@
"That doesn't match.": "S’përputhen.",
"Go back to set it again.": "Shkoni mbrapsht që ta ricaktoni.",
"Repeat your passphrase...": "Përsëritni frazëkalimin tuaj…",
- "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "Si rrjet i parrezikuar, mund ta përdoreni për të rikthyer historikun e mesazheve tuaj të fshehtëzuar, nëse harroni Frazëkalimin e Rimarrjeve.",
+ "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "Si masë sigurie, mund ta përdoreni për të rikthyer historikun e mesazheve tuaj të fshehtëzuar, nëse harroni Frazëkalimin e Rimarrjeve.",
"Your Recovery Key": "Kyçi Juaj i Rimarrjeve",
"Copy to clipboard": "Kopjoje në të papastër",
"Download": "Shkarkoje",
@@ -1046,8 +1046,8 @@
"Print it and store it somewhere safe": "Shtypeni dhe ruajeni diku pa rrezik",
"Save it on a USB key or backup drive": "Ruajeni në një diskth USB ose disk kopjeruajtjesh",
"Copy it to your personal cloud storage": "Kopjojeni te depoja juaj personale në re",
- "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Pa rregulluar Rikthim Mesazhesh të Sigurt, s’do të jeni në gjendje të riktheni historikun e mesazheve tuaj të fshehtëzuar, nëse bëni daljen ose përdorni një pajisje tjetër.",
- "Set up Secure Message Recovery": "Rregulloni Rikthim Mesazhesh të Sigurt",
+ "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Pa ujdisur Rimarrje të Sigurt Mesazhesh, s’do të jeni në gjendje të riktheni historikun e mesazheve tuaj të fshehtëzuar, nëse bëni daljen ose përdorni një pajisje tjetër.",
+ "Set up Secure Message Recovery": "Rregulloni Rimarrje të Sigurt Mesazhesh",
"Keep it safe": "Mbajeni të parrezikuar",
"Create Key Backup": "Krijo Kopjeruajtje Kyçesh",
"Unable to create key backup": "S’arrihet të krijojhet kopjeruajtje kyçesh",
@@ -1059,12 +1059,12 @@
"Failed to decrypt %(failedCount)s sessions!": "S’u arrit të shfshehtëzohet sesioni %(failedCount)s!",
"Restored %(sessionCount)s session keys": "U rikthyen kyçet e sesionit %(sessionCount)s",
"Enter Recovery Passphrase": "Jepni Frazëkalim Rimarrjeje",
- "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Hyni te historiku i mesazheve tuaj të siguruar dhe rregulloni shkëmbim mesazhesh të sigurt duke dhënë frazëkalimin tuaj të rikthimeve.",
- "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Nëse keni harruar frazëkalimin tuaj të rikthimeve, mund të përdorni kyçin tuaj të rikthimeve ose rregulloni mundësi të reja rikthimesh",
+ "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Hyni te historiku i mesazheve tuaj të siguruar dhe rregulloni shkëmbim mesazhesh të sigurt duke dhënë frazëkalimin tuaj të rimarrjeve.",
+ "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Nëse keni harruar frazëkalimin tuaj të rimarrjeve, mund të përdorni kyçin tuaj të rimarrjeve ose rregulloni mundësi të reja rimarrjesh",
"Enter Recovery Key": "Jepni Kyç Rimarrjeje",
- "This looks like a valid recovery key!": "Ky duket si kyç i vlefshëm rikthimesh!",
- "Not a valid recovery key": "Kyç rikthimesh jo i vlefshëm",
- "Access your secure message history and set up secure messaging by entering your recovery key.": "Hyni te historiku i mesazheve tuaj të siguruar dhe rregulloni shkëmbim mesazhesh të sigurt duke dhënë kyçin tuaj të rikthimeve.",
+ "This looks like a valid recovery key!": "Ky duket si kyç i vlefshëm rimarrjesh!",
+ "Not a valid recovery key": "Kyç rimarrjesh jo i vlefshëm",
+ "Access your secure message history and set up secure messaging by entering your recovery key.": "Hyni te historiku i mesazheve tuaj të siguruar dhe rregulloni shkëmbim mesazhesh të sigurt duke dhënë kyçin tuaj të rimarrjeve.",
"If you've forgotten your recovery passphrase you can ": "Nëse keni harruar frazëkalimin tuaj të rikthimeve, mund të ",
"Sign in with single sign-on": "Bëni hyrjen me hyrje njëshe",
"Failed to perform homeserver discovery": "S’u arrit të kryhej zbulim shërbyesi Home",
@@ -1096,16 +1096,16 @@
"Common names and surnames are easy to guess": "Emra dhe mbiemra të rëndomtë janë të kollajtë për t’u hamendësuar",
"Great! This passphrase looks strong enough.": "Bukur! Ky frazëkalim duket goxha i fuqishëm.",
"Failed to load group members": "S'u arrit të ngarkoheshin anëtarë grupi",
- "As a safety net, you can use it to restore your encrypted message history.": "Si një rrjet sigurie, mund ta përdorni për të rikthyer historikun e mesazheve tuaj të fshehtëzuar.",
+ "As a safety net, you can use it to restore your encrypted message history.": "Si masë sigurie, mund ta përdorni për të rikthyer historikun e mesazheve tuaj të fshehtëzuar.",
"Failed to invite users to the room:": "S’u arrit të ftohen përdorues te dhoma:",
"You do not have permission to invite people to this room.": "S’keni leje të ftoni njerëz në këtë dhomë.",
"User %(user_id)s does not exist": "Përdoruesi %(user_id)s nuk ekziston",
"Unknown server error": "Gabim i panjohur shërbyesi",
"There was an error joining the room": "Pati një gabim ardhjeje në dhomë",
- "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Shfaq një kujtues për aktivizim Rikthimi Mesazhesh të Sigurt në dhoma të fshehtëzuara",
+ "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Shfaq një kujtues për aktivizim Rimarrjeje Mesazhesh të Sigurt në dhoma të fshehtëzuara",
"Don't ask again": "Mos pyet sërish",
"Set up": "Rregulloje",
- "Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Po s’rregulluat Rikthim Mesazhesh të Sigurt, do të humbni historikun e mesazheve të parrezik, kur të dilni nga llogaria.",
+ "Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Pa ujdisur Rimarrje të Sigurt Mesazhesh, do të humbni historikun e mesazheve tuaj të fshehtëzuar, nëse bëni daljen nga llogaria.",
"If you don't want to set this up now, you can later in Settings.": "Nëse s’doni ta rregulloni tani këtë, mund ta bëni më vonë që nga Rregullimet.",
"Messages containing @room": "Mesazhe që përmbajnë @room",
"Encrypted messages in one-to-one chats": "Mesazhe të fshehtëzuar në fjalosje tek-për-tek",
@@ -1115,7 +1115,7 @@
"Invalid identity server discovery response": "Përgjigje e pavlefshme zbulimi identiteti shërbyesi",
"Backup has a valid signature from verified device ": "Kopjeruajtja ka një nënshkrim të vlefshëm prej pajisjes së verifikuar",
"New Recovery Method": "Metodë e Re Rimarrjesh",
- "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Nëse metodën e re të rikthimeve s’e keni caktuar ju, dikush mund të jetë duke u rrekur të hyjë në llogarinë tuaj. Ndryshoni menjëherë fjalëkalimin e llogarisë tuaj, te Rregullimet, dhe caktoni një metodë të re rikthimesh.",
+ "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Nëse metodën e re të rimarrjeve s’e keni caktuar ju, dikush mund të jetë duke u rrekur të hyjë në llogarinë tuaj. Ndryshoni menjëherë fjalëkalimin e llogarisë tuaj, te Rregullimet, dhe caktoni një metodë të re rimarrjesh.",
"Set up Secure Messages": "Rregulloni Mesazhi të Sigurt",
"Go to Settings": "Kalo te Rregullimet",
"Straight rows of keys are easy to guess": "Rreshta uniformë tastesh janë lehtësisht të hamendësueshëm",
@@ -1230,7 +1230,7 @@
"To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.": "Për të na ndihmuar të shmangim çështje të përsëdytura, ju lutemi, së pari shihni çështjet ekzistuese (dhe shtoni një +1) ose krijoni një çështje të re, nëse nuk gjeni gjë.",
"Report bugs & give feedback": "Njoftoni të meta & jepni përshtypjet",
"Go back": "Kthehu mbrapsht",
- "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "Nuk u shfshehtëzua dot kopjeruajtja me këtë kyç: ju lutemi, verifikoni që dhatë kyçin e duhur të rikthimeve.",
+ "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "Nuk u shfshehtëzua dot kopjeruajtja me këtë kyç: ju lutemi, verifikoni që dhatë kyçin e duhur të rimarrjeve.",
"Update status": "Përditëso gendjen",
"Set status": "Caktojini gjendjen",
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "Mund të përdorni mundësitë mbi shërbyes vetjak, për të bërë hyrjen në shërbyes të tjerë Matrix, duke dhënë URL-në e një tjetër shërbyesi Home. Kjo ju lejon ta përdorni këtë aplikacion në një tjetër shërbyes Home, me një llogari ekzistuese Matrix.",
@@ -1260,12 +1260,12 @@
"Create account": "Krijoni llogari",
"Keep going...": "Vazhdoni kështu…",
"Starting backup...": "Po fillohet kopjeruajtje…",
- "A new recovery passphrase and key for Secure Messages have been detected.": "Janë pikasur një frazëkalim dhe kyç i ri rikthimesh për Mesazhe të Sigurt.",
- "This device is encrypting history using the new recovery method.": "Kjo pajisje e fshehtëzon historikun duke përdorur metodë të re rikthimesh.",
+ "A new recovery passphrase and key for Secure Messages have been detected.": "Janë pikasur një frazëkalim dhe kyç i ri rimarrjesh për Mesazhe të Sigurt.",
+ "This device is encrypting history using the new recovery method.": "Kjo pajisje e fshehtëzon historikun duke përdorur metodë të re rimarrjesh.",
"Recovery Method Removed": "U hoq Metodë Rimarrje",
- "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Kjo pajisje ka pikasur se frazëkalimi dhe kyçi juaj i rikthimeve për Mesazhe të Sigurt janë hequr.",
- "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Nëse këtë e keni bërë pa dashje, mund të ujdisni Mesazhe të Sigurt në këtë pajisje, gjë që do të sjellë rifshehtëzimin e historikut të mesazheve të pajisjes me një metodë të re rikthimesh.",
- "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Nëse metodën e re të rikthimeve s’e keni hequr ju, dikush mund të jetë duke u rrekur të hyjë në llogarinë tuaj. Ndryshoni menjëherë fjalëkalimin e llogarisë tuaj, te Rregullimet, dhe caktoni një metodë të re rikthimesh.",
+ "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Kjo pajisje ka pikasur se frazëkalimi dhe kyçi juaj i rimarrjeve për Mesazhe të Sigurt janë hequr.",
+ "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Nëse këtë e keni bërë pa dashje, mund të ujdisni Mesazhe të Sigurt në këtë pajisje, gjë që do të sjellë rifshehtëzimin e historikut të mesazheve të pajisjes me një metodë të re rimarrjesh.",
+ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Nëse metodën e re të rimarrjeve s’e keni hequr ju, dikush mund të jetë duke u rrekur të hyjë në llogarinë tuaj. Ndryshoni menjëherë fjalëkalimin e llogarisë tuaj, te Rregullimet, dhe caktoni një metodë të re rimarrjesh.",
"Disinvite this user?": "T’i hiqet ftesa këtij përdoruesi?",
"The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Kartela '%(fileName)s' tejkalon kufirin e këtij shërbyesi Home për madhësinë e ngarkimeve",
"Gets or sets the room topic": "Merr ose cakton temën e dhomës",
@@ -1332,9 +1332,9 @@
"Chat with Riot Bot": "Fjalosuni me Robotin Riot",
"Some devices for this user are not trusted": "Disa pajisje për këtë përdorues nuk janë të besuara",
"All devices for this user are trusted": "Krejt pajisjet për këtë përdorues janë të besuara",
- "Recovery Key Mismatch": "Mospërputhje Kyçesh",
+ "Recovery Key Mismatch": "Mospërputhje Kyçesh Rimarrjeje",
"Incorrect Recovery Passphrase": "Frazëkalim Rimarrjeje i Pasaktë",
- "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "S’u shfshehtëzua dot kopjeruajtja me këtë frazëkalim: ju lutemi, verifikoni që dhatë frazëkalimin e duhur të rikthimeve.",
+ "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "S’u shfshehtëzua dot kopjeruajtja me këtë frazëkalim: ju lutemi, verifikoni që dhatë frazëkalimin e duhur të rimarrjeve.",
"This homeserver would like to make sure you are not a robot.": "Ky Shërbyes Home do të donte të sigurohej se s’jeni robot.",
"Change": "Ndërroje",
"Couldn't load page": "S’u ngarkua dot faqja",
@@ -1382,14 +1382,14 @@
"Hide": "Fshihe",
"We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Do të depozitojmë në shërbyesin tonë një kopje të fshehtëzuar të kyçeve tuaj. Mbrojeni kopjeruajtjen tuaj me një frazëkalim, për ta mbajtur të parrezikuar.",
"For maximum security, this should be different from your account password.": "Për maksimumin e sigurisë, ky do të duhej të ishte i ndryshëm nga fjalëkalimi juaj për llogarinë.",
- "Set up with a Recovery Key": "Rregullojeni me një Kyç Rikthimesh",
+ "Set up with a Recovery Key": "Rregullojeni me një Kyç Rimarrjesh",
"Please enter your passphrase a second time to confirm.": "Ju lutemi, që të ripohohet, rijepeni frazëkalimin tuaj.",
- "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kyçi juaj i rikthimeve është një rrjet sigurie - mund ta përdorni për rikthim hyrjeje te mesazhet tuaj të fshehtëzuar, nëse harroni frazëkalimin tuaj.",
+ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kyçi juaj i rimarrjeve është një masë sigurie - mund ta përdorni për rimarrje hyrjeje te mesazhet tuaj të fshehtëzuar, nëse harroni frazëkalimin tuaj.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Mbajeni kyçin tuaj të rikthimeve diku në një vend shumë të sigurt, bie fjala, nën një përgjegjës fjalëkalimesh (ose në një kasafortë)",
"Your keys are being backed up (the first backup could take a few minutes).": "Kyçet tuaj po kopjeruhen (kopjeruajtja e parë mund të hajë disa minuta).",
"Secure your backup with a passphrase": "Sigurojeni kopjeruajtjen tuaj me një frazëkalim",
"Confirm your passphrase": "Ripohoni frazëkalimin tuaj",
- "Recovery key": "Kyç rikthimesh",
+ "Recovery key": "Kyç rimarrjesh",
"Success!": "Sukses!",
"Allow Peer-to-Peer for 1:1 calls": "Lejo Peer-to-Peer për thirrje tek për tek",
"Credits": "Kredite",
@@ -1910,5 +1910,111 @@
"Remove for everyone": "Hiqe për këdo",
"Remove for me": "Hiqe për mua",
"Verification Request": "Kërkesë Verifikimi",
- " (1/%(totalCount)s)": " (1/%(totalCount)s)"
+ " (1/%(totalCount)s)": " (1/%(totalCount)s)",
+ "Error upgrading room": "Gabim në përditësim dhome",
+ "Double check that your server supports the room version chosen and try again.": "Rikontrolloni që shërbyesi juaj e mbulon versionin e zgjedhur për dhomën dhe riprovoni.",
+ "%(senderName)s placed a voice call.": "%(senderName)s bëri një thirrje zanore.",
+ "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s bëri një thirrje zanore. (e pambuluar nga ky shfletues)",
+ "%(senderName)s placed a video call.": "%(senderName)s bëri një thirrje video.",
+ "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s bëri një thirrje video. (e pambuluar nga ky shfletues)",
+ "Enable cross-signing to verify per-user instead of per-device (in development)": "",
+ "Enable local event indexing and E2EE search (requires restart)": "Aktivizoni indeksim aktesh vendore dhe kërkim E2EE (lyp rinisje)",
+ "Match system theme": "Përputhe me temën e sistemit",
+ "Send cross-signing keys to homeserver": "",
+ "Cross-signing public keys:": "",
+ "on device": "në pajisje",
+ "not found": "s’u gjet",
+ "in secret storage": "në depozitë të fshehtë",
+ "Secret storage public key:": "Kyç publik depozite të fshehtë:",
+ "in account data": "në të dhëna llogarie",
+ "Clear notifications": "Spastro njoftimet",
+ "Customise your experience with experimental labs features. Learn more.": "Përshtateni punimin tuaj me veçori eksperimentale. Mësoni më tepër.",
+ "This message cannot be decrypted": "Ky mesazh s'mund të shfshehtëzohet",
+ "Unencrypted": "Të pafshehtëzuara",
+ " wants to chat": " dëshiron të bisedojë",
+ "Start chatting": "Filloni të bisedoni",
+ "Reactions": "Reagime",
+ " reacted with %(content)s": " reagoi me %(content)s",
+ "Automatically invite users": "Fto përdorues automatikisht",
+ "Upgrade private room": "Përmirëso dhomë private",
+ "Upgrade public room": "Përmirëso dhomë publike",
+ "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Përmirësimi i një dhome është një veprim i thelluar dhe zakonisht rekomandohet kur një dhomë është e papërdorshme, për shkak të metash, veçorish që i mungojnë ose cenueshmëri sigurie.",
+ "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Kjo zakonisht prek vetëm mënyrën se si përpunohet dhoma te shërbyesi. Nëse keni probleme me Riot-in, ju lutemi, njoftoni një të metë.",
+ "You'll upgrade this room from to .": "Do ta përmirësoni këtë dhomë nga në .",
+ "Upgrade": "Përmirësoje",
+ "Enter secret storage passphrase": "Jepni frazëkalim për te depozitë e fshehtë",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "S’arrihet të hyhet te depozitë e fshehtë. Ju lutemi, verifikoni se dhatë frazëkalimin e saktë.",
+ "Warning: You should only access secret storage from a trusted computer.": "Kujdes: Duhet të hyni në depozitën e fshehtë vetëm nga një kompjuter i besuar.",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Nëse keni harruar frazëkalimin tuaj, mund të përdorni kyçin tuaj të rimarrjes ose të ujdisni mundësi të reja rimarrjeje.",
+ "Enter secret storage recovery key": "Jepni kyç rimarrjeje për depozitë të fshehtë",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "S’arrihet të hyhet te depozitë e fshehtë. Ju lutemi, verifikoni se dhatë kyç të saktë rimarrjeje.",
+ "If you've forgotten your recovery key you can .": "Nëse keni harruar kyçin tuaj të rimarrjeve, mund të .",
+ "Warning: You should only set up key backup from a trusted computer.": "Kujdes: duhet të ujdisni kopjeruajtje kyçesh vetëm nga një kompjuter i besuar.",
+ "If you've forgotten your recovery key you can ": "Nëse keni harruar kyçin tuaj të rimarrjeve, mund të ",
+ "Notification settings": "Rregullime njoftimesh",
+ "User Status": "Gjendje Përdoruesi",
+ "Warning: You should only set up secret storage from a trusted computer.": "Kujdes: Duhet të ujdisni një depozitë të fshehtë vetëm nga një kompjuter i besuar.",
+ "Set up with a recovery key": "Rregullojeni me një kyç rimarrjesh",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Si masë sigurie, mund ta përdorni për të rifituar hyrjen tuaj te mesazhe të fshehtëzuar, nëse harroni frazëkalimin tuaj.",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "Si një masë sigurie, mund ta përdorni për të rifituar hyrjen tuaj te mesazhe të fshehtëzuar.",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Mbajeni kyçin tuaj të rimarrjeve diku në një vend shumë të sigurt, bie fjala, nën një përgjegjës fjalëkalimesh (ose në një kasafortë).",
+ "Your recovery key has been copied to your clipboard, paste it to:": "Kyçi juaj i rimarrjeve është kopjuar te e papastra juaj, ngjiteni te:",
+ "Your recovery key is in your Downloads folder.": "Kyçi juaj i rimarrjeve gjendet te dosja juaj Shkarkime.",
+ "Your access to encrypted messages is now protected.": "Hyrja juaj te mesazhe të fshehtëzuar tani është e mbrojtur.",
+ "Set up secret storage": "Ujdisni depozitë të fshehtë",
+ "Secure your encrypted messages with a passphrase": "Sigurojini mesazhet tuaj të fshehtëzuar me një frazëkalim",
+ "Storing secrets...": "Po depozitohen të fshehta…",
+ "Unable to set up secret storage": "S’u arrit të ujdiset depozitë e fshehtë",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s hoqi rregullin për dëbim përdoruesish që kanë përputhje me %(glob)s",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s hoqi rregullin që dëbon dhoma që kanë përputhje me %(glob)s",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s hoqi rregullin që dëbon shërbyes që kanë përputhje me %(glob)s",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s hoqi një rregull dëbimi mbi përputhje me %(glob)s",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s përditësoi një rregull të pavlefshëm dëbimesh",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s përditësoi rregullin mbi dëbim përdoruesish që kanë përputhje me %(glob)s për %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s përditësoi rregullin për dëbim dhomash që kanë përputhje me %(glob)s për %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s përditësoi rregullin për dëbim shërbyesish që kanë përputhje me %(glob)s për %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s përditësoi një rregull dëbimi rreth përputhjesh me %(glob)s për %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull mbi dëbim përdoruesish që kanë përputhje me %(glob)s për %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull mbi dëbim dhomash që kanë përputhje me %(glob)s për %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull mbi dëbim shërbyesish që kanë përputhje me %(glob)s për %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull dëbimi rreth përputhjesh me %(glob)s për %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ndryshoi një rregull që dëbonte përdorues me përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ndryshoi një rregull që dëbonte dhoma me përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ndryshoi një rregull që dëbonte shërbyes me përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s përditësoi një rregull dëbimesh mbi përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s",
+ "The message you are trying to send is too large.": "Mesazhi që po rrekeni të dërgoni është shumë i madh.",
+ "New DM invite dialog (under development)": "Dialog ftese të re për MD (në zhvillim)",
+ "not stored": "e padepozituar",
+ "Backup has a valid signature from this user": "Kopjeruajtja ka një nënshkrim të vlefshëm prej këtij përdoruesi",
+ "Backup has a invalid signature from this user": "Kopjeruajtja ka një nënshkrim të pavlefshëm prej këtij përdoruesi",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "Kopjeruajtja ka një nënshkrim nga një përdorues i panjohur me ID %(deviceId)s",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "Kopjeruajtja ka një nënshkrim nga një pajisje e panjohur me ID %(deviceId)s",
+ "Backup key stored: ": "Kyç kopjeruajtjeje i depozituar: ",
+ "Start using Key Backup with Secure Secret Storage": "Fillo të përdorësh Kopejruajtje Kyçesh me Depozitim Kyçi të Fshehtë",
+ "This user has not verified all of their devices.": "Ky përdorues s’ka verifikuar krejt pajisjet e tij.",
+ "You have not verified this user. This user has verified all of their devices.": "S’e keni verifikuar këtë përdorues. Ky përdorues ka verifikuar krejt pajisjet e veta.",
+ "You have verified this user. This user has verified all of their devices.": "E keni verifikuar këtë përdorues. Ky përdorues ka verifikuar krejt pajisjet e veta.",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Disa përdorues në këtë dhomë të fshehtëzuar nuk janë verifikuar nga ju ose nuk kanë verifikuar pajisjet e tyre.",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "Krejt përdorues në këtë dhomë të fshehtëzuar janë verifikuar nga ju dhe kanë verifikuar pajisjet e tyre.",
+ "Close preview": "Mbylle paraparjen",
+ "Hide verified sessions": "Fshih sesione të verifikuar",
+ "%(count)s verified sessions|other": "%(count)s sesione të verifikuar",
+ "%(count)s verified sessions|one": "1 sesion i verifikuar",
+ "Language Dropdown": "Menu Hapmbyll Gjuhësh",
+ "Show more": "Shfaq më tepër",
+ "Recent Conversations": "Biseda Së Fundi",
+ "Direct Messages": "Mesazhe të Drejtpërdrejtë",
+ "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Nëse s’gjeni dot dikë, kërkojuni emrin e përdoruesit, ose jepuni emrin tuaj të përdoruesit (%(userId)s) ose profile link.",
+ "Go": "Shko",
+ "Help": "Ndihmë",
+ "Country Dropdown": "Menu Hapmbyll Vendesh",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Depozita e Fshehtë do të ujdiset duke përdorur hollësitë tuaja ekzistuese për kopjeruajtje kyçesh.Frazëkalimi juaj për në depozitën e fshehtë dhe kyçi i rimarrjes do të jenë të njëjtë me ata për kopjeruajtjen tuaj të kyçeve",
+ "Migrate from Key Backup": "Migroji prej Kopjeruajtje Kyçesh",
+ "Show info about bridges in room settings": "Shfaq te rregullime dhome të dhëna rreth urash",
+ "This bridge was provisioned by ": "Kjo urë është dhënë nga ",
+ "This bridge is managed by .": "Kjo urë administrohet nga .",
+ "Connected to on ": "Lidhur me në ",
+ "Connected via %(protocolName)s": "Lidhur përmes %(protocolName)s",
+ "Bridge Info": "Të dhëna Ure",
+ "Below is a list of bridges connected to this room.": "Më poshtë keni një listë urash të lidhura në këtë dhomë."
}
diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json
index 2462b5789f..159cb61965 100644
--- a/src/i18n/strings/tr.json
+++ b/src/i18n/strings/tr.json
@@ -589,5 +589,487 @@
"The platform you're on": "Bulunduğun platform",
"The version of Riot.im": "Riot.im'in sürümü",
"Your language of choice": "Seçtiginiz diliniz",
- "Which officially provided instance you are using, if any": ""
+ "Which officially provided instance you are using, if any": "Hangi resmi destekli örneği(eğer varsa) kullanmaktasınız",
+ "Add Email Address": "E-posta Adresi Ekle",
+ "Add Phone Number": "Telefon Numarası Ekle",
+ "Your identity server's URL": "Kimlik sunucunuzun linki",
+ "e.g. %(exampleValue)s": "örn.%(exampleValue)s",
+ "Every page you use in the app": "uygulamadaki kullandığınız tüm sayfalar",
+ "e.g. ": "örn. ",
+ "Your User Agent": "Kullanıcı Ajanınız",
+ "Your device resolution": "Cihazınızın çözünürlüğü",
+ "Call Failed": "Arama Başarısız",
+ "Review Devices": "Cihazları Gözden Geçir",
+ "Call Anyway": "Yinede Ara",
+ "Answer Anyway": "Yinede Cevapla",
+ "Call": "Ara",
+ "Answer": "Cevap",
+ "Call failed due to misconfigured server": "Hatalı yapılandırılmış sunucu nedeniyle arama başarısız",
+ "Call in Progress": "Arama Yapılıyor",
+ "A call is already in progress!": "Zaten bir arama devam etmekte!",
+ "Permission Required": "İzin Gerekli",
+ "Replying With Files": "Dosyalarla Cevaplanıyor",
+ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s%(monthName)s%(day)s%(fullYear)s",
+ "Invite new community members": "Yeni topluluk üyelerini davet et",
+ "Name or Matrix ID": "İsim yada Matrix ID",
+ "Invite to Community": "Topluluğa Davet",
+ "Add rooms to the community": "Topluluğa odalar ekle",
+ "Add to community": "Topluluğa ekle",
+ "Failed to invite the following users to %(groupId)s:": "%(groupId)s grubuna belirtilen kullanıcıların davet işlemi başarısız oldu:",
+ "Failed to invite users to community": "Kullanıcıların topluluğa daveti başarısız",
+ "Identity server has no terms of service": "Kimlik sunucusu hizmet kurallarına sahip değil",
+ "Only continue if you trust the owner of the server.": "Sadece sunucunun sahibine güveniyorsanız devam edin.",
+ "Trust": "Güven",
+ "Unable to load! Check your network connectivity and try again.": "Yüklenemiyor! Ağ bağlantınızı kontrol edin ve yeniden deneyin.",
+ "Registration Required": "Kayıt Zorunlu",
+ "You need to register to do this. Would you like to register now?": "Bunu yapabilmek için kayıt olmalısınız. Şimdi kayıt olmak ister misiniz?",
+ "Restricted": "Sınırlı",
+ "Email, name or Matrix ID": "E-posta, isim yada Matrix ID",
+ "Failed to start chat": "Sohbet başlatma başarısız",
+ "Failed to invite users to the room:": "Kullanıcıların odaya daveti başarısız oldu:",
+ "Missing roomId.": "roomId eksik.",
+ "You are not in this room.": "Bu odada değilsin.",
+ "You do not have permission to do that in this room.": "Bu odada bunu yapma yetkiniz yok.",
+ "Messages": "Mesajlar",
+ "Actions": "Eylemler",
+ "Other": "Diğer",
+ "Upgrades a room to a new version": "Odayı yeni bir sürüme yükseltir",
+ "You do not have the required permissions to use this command.": "Bu komutu kullanmak için gerekli izinlere sahip değilsin.",
+ "Error upgrading room": "Oda güncellenirken hata",
+ "Changes your avatar in all rooms": "Tüm odalardaki avatarlarını değiştirir",
+ "This room has no topic.": "Bu odanın başlığı yok.",
+ "Sets the room name": "Oda adını düzenler",
+ "Use an identity server": "Bir kimlik sunucusu kullan",
+ "Define the power level of a user": "Bir kullanıcının güç düzeyini tanımla",
+ "Opens the Developer Tools dialog": "Geliştirici Araçları kutucuğunu açar",
+ "%(senderDisplayName)s upgraded this room.": "Odayı güncelleyen %(senderDisplayName)s.",
+ "%(senderDisplayName)s made the room invite only.": "Odayı sadece davetle yapan %(senderDisplayName)s.",
+ "%(senderDisplayName)s has prevented guests from joining the room.": "Odaya misafirlerin girişini engelleyen %(senderDisplayName)s.",
+ "%(senderName)s removed the main address for this room.": "Bu oda için ana adresi silen %(senderName)s.",
+ "Light theme": "Açık tema",
+ "Dark theme": "Koyu tema",
+ "%(displayName)s is typing …": "%(displayName)s yazıyor…",
+ "%(names)s and %(count)s others are typing …|one": "%(names)s ve bir diğeri yazıyor…",
+ "%(names)s and %(lastPerson)s are typing …": "%(names)s ve %(lastPerson)s yazıyor…",
+ "Cannot reach homeserver": "Ana sunucuya erişilemiyor",
+ "Your Riot is misconfigured": "Rioutunuz hatalı yapılandırılmış",
+ "Cannot reach identity server": "Kimlik sunucu erişilemiyor",
+ "No homeserver URL provided": "Ana sunucu adresi belirtilmemiş",
+ "Unexpected error resolving homeserver configuration": "Ana sunucu yapılandırması çözümlenirken beklenmeyen hata",
+ "Unexpected error resolving identity server configuration": "Kimlik sunucu yapılandırması çözümlenirken beklenmeyen hata",
+ "The message you are trying to send is too large.": "Göndermeye çalıştığın mesaj çok büyük.",
+ "This homeserver has hit its Monthly Active User limit.": "Bu ana sunucu Aylık Aktif Kullanıcı limitine ulaştı.",
+ "Riot URL": "Riot Linki",
+ "Room ID": "Oda ID",
+ "More options": "Daha fazla seçenek",
+ "Join": "Katıl",
+ "Yes": "Evet",
+ "No": "Hayır",
+ "expand": "genişlet",
+ "Communities": "Topluluklar",
+ "Rotate Left": "Sola Döndür",
+ "Rotate Right": "Sağa Döndür",
+ "Rotate clockwise": "Saat yönünde döndür",
+ "%(nameList)s %(transitionList)s": "%(nameList)s%(transitionList)s",
+ "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s %(count)s kez katıldı",
+ "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s katıldı",
+ "%(oneUser)sjoined %(count)s times|other": "%(oneUser)s %(count)s kez katıldı",
+ "%(oneUser)sjoined %(count)s times|one": "%(oneUser)s katıldı",
+ "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s kullanıcı ayrıldı",
+ "%(oneUser)sleft %(count)s times|one": "%(oneUser)s ayrıldı",
+ "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s katıldı ve ayrıldı",
+ "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s katıldı ve ayrıldı",
+ "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s ayrıldı ve yeniden katıldı",
+ "were invited %(count)s times|other": "%(count)s kez davet edildi",
+ "were invited %(count)s times|one": "davet edildi",
+ "was invited %(count)s times|other": "%(count)s kez davet edildi",
+ "was invited %(count)s times|one": "davet edildi",
+ "were kicked %(count)s times|other": "%(count)s kez atıldı",
+ "were kicked %(count)s times|one": "atıldı",
+ "was kicked %(count)s times|other": "%(count)s kez atıldı",
+ "was kicked %(count)s times|one": "atıldı",
+ "%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s isimlerini değiştrtiler",
+ "%(oneUser)schanged their name %(count)s times|one": "%(oneUser)s ismini değiştirdi",
+ "Power level": "Güç düzeyi",
+ "e.g. my-room": "örn. odam",
+ "Some characters not allowed": "Bazı karakterlere izin verilmiyor",
+ "Matrix ID": "Matrix ID",
+ "Matrix Room ID": "Matrix Oda ID",
+ "email address": "e-posta adresi",
+ "That doesn't look like a valid email address": "Geçerli bir e-posta adresi gibi gözükmüyor",
+ "You have entered an invalid address.": "Geçersiz bir adres girdiniz.",
+ "Invite anyway and never warn me again": "Yinede davet et ve asla beni uyarma",
+ "Invite anyway": "Yinede davet et",
+ "Close dialog": "Kutucuğu kapat",
+ "Preparing to send logs": "Loglar gönderilmek için hazırlanıyor",
+ "Logs sent": "Loglar gönderiliyor",
+ "Thank you!": "Teşekkürler!",
+ "Failed to send logs: ": "Loglarıb gönderilmesi başarısız: ",
+ "GitHub issue": "GitHub sorunu",
+ "Notes": "Notlar",
+ "Removing…": "Siliniyor…",
+ "Clear all data on this device?": "Bu cihazdaki bütün verileri sil?",
+ "Clear all data": "Bütün verileri sil",
+ "Community IDs cannot be empty.": "Topluluk ID leri boş bırakılamaz.",
+ "Something went wrong whilst creating your community": "Topluluğunuz oluşturulurken bir şeyler yanlış gitti",
+ "Create Community": "Topluluk Oluştur",
+ "Community Name": "Topluluk Adı",
+ "Example": "Örnek",
+ "Community ID": "Topluluk ID",
+ "example": "örnek",
+ "Create": "Oluştur",
+ "Please enter a name for the room": "Lütfen oda için bir ad girin",
+ "This room is private, and can only be joined by invitation.": "Bu oda özel, sadece davet ile katılınabilir.",
+ "Create a private room": "Özel bir oda oluştur",
+ "Hide advanced": "Gelişmiş gizle",
+ "Show advanced": "Gelişmiş göster",
+ "Incompatible Database": "Uyumsuz Veritabanı",
+ "To continue, please enter your password:": "Devam etmek için lütfen şifrenizi giriniz:",
+ "Begin Verifying": "Doğrulamaya Başla",
+ "Use two-way text verification": "İki yönlü metin doğrulama kullan",
+ "Back": "Geri",
+ "You must specify an event type!": "Bir olay tipi seçmek zorundasınız!",
+ "Event sent!": "Olay gönderildi!",
+ "Event Type": "Olay Tipi",
+ "State Key": "Durum Anahtarı",
+ "Event Content": "Olay İçeriği",
+ "Send Account Data": "Hesap Verisi Gönder",
+ "Filter results": "Sonuçları filtrele",
+ "View Servers in Room": "Odadaki Sunucuları Gör",
+ "Toolbox": "Araç Kutusu",
+ "Developer Tools": "Geliştirici Araçları",
+ "Integrations are disabled": "Bütünleştirmeler kapatılmış",
+ "Integrations not allowed": "Bütünleştirmelere izin verilmiyor",
+ "Loading device info...": "Cihaz bilgileri yükleniyor...",
+ "Incompatible local cache": "Yerel geçici bellek uyumsuz",
+ "Clear cache and resync": "Geçici belleği temizle ve yeniden eşle",
+ "Updating Riot": "Riot güncelleniyor",
+ "I don't want my encrypted messages": "Şifrelenmiş mesajlarımı istemiyorum",
+ "Manually export keys": "Elle dışa aktarılmış anahtarlar",
+ "You'll lose access to your encrypted messages": "Şifrelenmiş mesajlarınıza erişiminizi kaybedeceksiniz",
+ "Are you sure you want to sign out?": "Oturumdan çıkmak istediğinize emin misiniz?",
+ "Your homeserver doesn't seem to support this feature.": "Ana sunucunuz bu özelliği desteklemiyor gözüküyor.",
+ "Message edits": "Mesajları düzenle",
+ "Report bugs & give feedback": "Hataları raporla & geri bildirim yap",
+ "Please fill why you're reporting.": "Lütfen neden raporlama yaptığınızı belirtin.",
+ "Report Content to Your Homeserver Administrator": "Ana Sunucu Yöneticinize İçeriği Raporlayın",
+ "Send report": "Rapor gönder",
+ "Room Settings - %(roomName)s": "Oda Ayarları - %(roomName)s",
+ "Failed to upgrade room": "Oda güncelleme başarısız",
+ "The room upgrade could not be completed": "Oda güncelleme tamamlanamadı",
+ "Upgrade this room to version %(version)s": "Bu odayı %(version)s versiyonuna yükselt",
+ "Upgrade Room Version": "Oda Sürümünü Yükselt",
+ "Automatically invite users": "Otomatik olarak kullanıcıları davet et",
+ "Upgrade private room": "Özel oda güncelle",
+ "Upgrade": "Yükselt",
+ "Sign out and remove encryption keys?": "Oturumu kapat ve şifreleme anahtarlarını sil?",
+ "Clear Storage and Sign Out": "Depolamayı temizle ve Oturumu Kapat",
+ "Send Logs": "Logları Gönder",
+ "Refresh": "Yenile",
+ "Checking...": "Kontrol ediliyor...",
+ "To get started, please pick a username!": "Başlamak için lütfen bir kullanıcı adı seçin!",
+ "Share Room": "Oda Paylaş",
+ "Link to most recent message": "En son mesaja bağlantı",
+ "Share User": "Kullanıcı Paylaş",
+ "Share Community": "Topluluk Paylaş",
+ "Share Room Message": "Oda Mesajı Paylaş",
+ "Link to selected message": "Seçili mesaja bağlantı",
+ "COPY": "KOPYA",
+ "Command Help": "Komut Yardımı",
+ "Missing session data": "Kayıp oturum verisi",
+ "Integration Manager": "Bütünleştirme Yöneticisi",
+ "Find others by phone or email": "Kişileri telefon yada e-posta ile bul",
+ "Be found by phone or email": "Telefon veya e-posta ile bulunun",
+ "Terms of Service": "Hizmet Şartları",
+ "Service": "Hizmet",
+ "Summary": "Özet",
+ "Document": "Belge",
+ "Next": "İleri",
+ "Upload files": "Dosyaları yükle",
+ "Upload all": "Hepsini yükle",
+ "Cancel All": "Hepsi İptal",
+ "Upload Error": "Yükleme Hatası",
+ "Allow": "İzin ver",
+ "Enter secret storage recovery key": "Depolama kurtarma anahtarı için şifre gir",
+ "This looks like a valid recovery key!": "Bu geçerli bir kurtarma anahtarına benziyor!",
+ "Not a valid recovery key": "Geçersiz bir kurtarma anahtarı",
+ "Unable to load backup status": "Yedek durumu yüklenemiyor",
+ "Recovery Key Mismatch": "Kurtarma Anahtarı Kurtarma",
+ "Unable to restore backup": "Yedek geri dönüşü yapılamıyor",
+ "No backup found!": "Yedek bulunamadı!",
+ "Backup Restored": "Yedek Geri Dönüldü",
+ "Enter Recovery Key": "Kurtarma Anahtarını Gir",
+ "Warning: You should only set up key backup from a trusted computer.": "Uyarı: Yedek anahtarı kurulumunu sadece güvenli bir bilgisayardan yapmalısınız.",
+ "Unable to reject invite": "Davet reddedilemedi",
+ "Pin Message": "Pin Mesajı",
+ "Share Message": "Mesajı Paylaş",
+ "Report Content": "İçeriği Raporla",
+ "Notification settings": "Bildirim ayarları",
+ "Clear status": "Durumu temizle",
+ "Update status": "Durumu güncelle",
+ "Set status": "Durumu ayarla",
+ "Set a new status...": "Yeni bir durum ayarla...",
+ "View Community": "Topluluğu Gör",
+ "Hide": "Gizle",
+ "Reload": "Yeniden Yükle",
+ "Remove for everyone": "Herkes için sil",
+ "Remove for me": "Benim için sil",
+ "User Status": "Kullanıcı Durumu",
+ "This homeserver would like to make sure you are not a robot.": "Bu ana sunucu sizin bir robot olup olmadığınızdan emin olmak istiyor.",
+ "Country Dropdown": "Ülke Listesi",
+ "Code": "Kod",
+ "Unable to validate homeserver/identity server": "Ana/kimlik sunucu doğrulanamıyor",
+ "Your Modular server": "Sizin Modüler sunucunuz",
+ "Server Name": "Sunucu Adı",
+ "The email field must not be blank.": "E-posta alanı boş bırakılamaz.",
+ "The username field must not be blank.": "Kullanıcı adı alanı boş bırakılamaz.",
+ "The phone number field must not be blank.": "Telefon numarası alanı boş bırakılamaz.",
+ "The password field must not be blank.": "Şifre alanı boş bırakılamaz.",
+ "Username": "Kullanıcı Adı",
+ "Use an email address to recover your account": "Hesabınızı kurtarmak için bir e-posta adresi kullanın",
+ "Enter email address (required on this homeserver)": "E-posta adresi gir ( bu ana sunucuda gerekli)",
+ "Doesn't look like a valid email address": "Geçerli bir e-posta adresine benzemiyor",
+ "Enter password": "Şifre gir",
+ "Password is allowed, but unsafe": "Şifreye izin var, fakat kullanmak güvenli değil",
+ "Nice, strong password!": "Güzel, güçlü şifre!",
+ "Keep going...": "Devam et...",
+ "Passwords don't match": "Şifreler uyuşmuyor",
+ "Enter phone number (required on this homeserver)": "Telefon numarası gir ( bu ana sunucuda gerekli)",
+ "Doesn't look like a valid phone number": "Geçerli bir telefon numarasına benzemiyor",
+ "Enter username": "Kullanıcı adı gir",
+ "Email (optional)": "E-posta (opsiyonel)",
+ "Confirm": "Doğrula",
+ "Phone (optional)": "Telefon (opsiyonel)",
+ "Create your Matrix account on %(serverName)s": "%(serverName)s üzerinde Matrix hesabınızı oluşturun",
+ "Create your Matrix account on ": " üzerinde Matrix hesabınızı oluşturun",
+ "Homeserver URL": "Ana sunucu URL",
+ "Identity Server URL": "Kimlik Sunucu URL",
+ "Other servers": "Diğer sunucular",
+ "Couldn't load page": "Sayfa yüklenemiyor",
+ "Add a Room": "Bir Oda Ekle",
+ "Add a User": "Bir Kullanıcı Ekle",
+ "Failed to upload image": "Resim yükleme başarısız",
+ "Unable to accept invite": "Davet kabul edilemiyor",
+ "Unable to join community": "Topluluğa katılınamıyor",
+ "Leave Community": "Topluluktan Ayrıl",
+ "Leave %(groupName)s?": "%(groupName)s den ayrıl?",
+ "Unable to leave community": "Topluluktan ayrılınamıyor",
+ "Community Settings": "Topluluk Ayarları",
+ "Join this community": "Bu topluluğa katıl",
+ "Leave this community": "Bu topluluktan ayrıl",
+ "You are an administrator of this community": "Bu topluluğun yöneticisi sizsiniz",
+ "You are a member of this community": "Bu topluluğun bir üyesisiniz",
+ "Who can join this community?": "Bu topluluğa kimler katılabilir?",
+ "Everyone": "Herkes",
+ "Long Description (HTML)": "Uzun Tanım (HTML)",
+ "Description": "Tanım",
+ "Community %(groupId)s not found": "%(groupId)s topluluğu bulunamıyor",
+ "This homeserver does not support communities": "Bu ana sunucu toplulukları desteklemiyor",
+ "Failed to load %(groupId)s": "%(groupId)s yükleme başarısız",
+ "Filter": "Filtre",
+ "Filter rooms…": "Odaları filtrele…",
+ "Old cryptography data detected": "Eski kriptolama verisi tespit edildi",
+ "Verification Request": "Doğrulama Talebi",
+ "Your Communities": "Topluluklarınız",
+ "Error whilst fetching joined communities": "Katılım sağlanmış topluluklar getirilirken hata oluştu",
+ "Create a new community": "Yeni bir topluluk oluştur",
+ "The homeserver may be unavailable or overloaded.": "Ana sunucunu mevcut değil yada fazla yüklü.",
+ "Preview": "Önizleme",
+ "View": "Görüntüle",
+ "Find a room…": "Bir oda bul…",
+ "Find a room… (e.g. %(exampleRoom)s)": "Bir oda bul... (örn. %(exampleRoom)s)",
+ "%(count)s of your messages have not been sent.|one": "Mesajınız gönderilmedi.",
+ "Jump to first unread room.": "Okunmamış ilk odaya zıpla.",
+ "Jump to first invite.": "İlk davete zıpla.",
+ "Add room": "Oda ekle",
+ "Clear filter": "Filtre temizle",
+ "Guest": "Misafir",
+ "Your profile": "Profiliniz",
+ "Could not load user profile": "Kullanıcı profili yüklenemedi",
+ "Your Matrix account on %(serverName)s": "%(serverName)s sunucusundaki Matrix hesabınız",
+ "Your password has been reset.": "Parolanız sıfırlandı.",
+ "Set a new password": "Yeni bir şifre belirle",
+ "General failure": "Genel başarısızlık",
+ "This homeserver does not support login using email address.": "Bu ana sunucu e-posta adresiyle oturum açmayı desteklemiyor.",
+ "This account has been deactivated.": "Bu hesap pasifleştirilmiş.",
+ "Create account": "Yeni hesap",
+ "Unable to query for supported registration methods.": "Desteklenen kayıt yöntemleri için sorgulama yapılamıyor.",
+ "Continue with previous account": "Önceki hesapla devam et",
+ "Log in to your new account.": "Yeni hesabınızla Oturum açın.",
+ "Registration Successful": "Kayıt Başarılı",
+ "Create your account": "Hesabınızı oluşturun",
+ "Forgotten your password?": "Parolanızı mı unuttunuz?",
+ "Sign in and regain access to your account.": "Oturum açın ve yeniden hesabınıza ulaşın.",
+ "Whether or not you're logged in (we don't record your username)": "İster oturum açın yasa açmayın (biz kullanıcı adınızı kaydetmiyoruz)",
+ "Whether or not you're using the Richtext mode of the Rich Text Editor": "Zengin Metin Düzenleyicinin Zengin metin modunu kullanıyor ya da kullanmıyorsunuz",
+ "Your homeserver's URL": "Ana sunucunuzun URL’i",
+ "The information being sent to us to help make Riot.im better includes:": "Riot.im i daha iyi yapmamıza yardımcı olacak bize gönderdiğiniz bilgilerin içeriği:",
+ "Try using turn.matrix.org": "turn.matrix.org i kullanarak dene",
+ "You do not have permission to start a conference call in this room": "Bu odada bir konferans başlatmak için izniniz yok",
+ "The file '%(fileName)s' failed to upload.": "%(fileName)s dosyası için yükleme başarısız.",
+ "The server does not support the room version specified.": "Belirtilen oda sürümünü sunucu desteklemiyor.",
+ "Who would you like to add to this community?": "Bu topluluğa kimi eklemek isterdiniz?",
+ "Which rooms would you like to add to this community?": "Hangi odaları bu topluluğa eklemek isterdiniz?",
+ "Unable to create widget.": "Görsel bileşen oluşturulamıyor.",
+ "Changes your display nickname in the current room only": "sadece mevcut odada görüntülenen lakabınızı değiştirir",
+ "Changes the avatar of the current room": "Mevcut odadaki avatarınızı değiştirir",
+ "Use an identity server to invite by email. Manage in Settings.": "E-posta ile davet etmek için bir kimlik sunucusu kullan. Ayarlardan Yönet.",
+ "You cannot modify widgets in this room.": "Bu odadaki görsel bileşenleri değiştiremezsiniz.",
+ "Sends the given message coloured as a rainbow": "Verilen mesajı gökkuşağı renklerinde gönderir",
+ "Displays list of commands with usages and descriptions": "Komutların listesini kullanımı ve tanımlarıyla gösterir",
+ "%(senderName)s made no change.": "%(senderName)s değişiklik yapmadı.",
+ "%(senderName)s changed the pinned messages for the room.": "Oda için sabitlenmiş mesajları %(senderName)s değiştirdi.",
+ "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s görsel bileşeni %(senderName)s tarafından düzenlendi",
+ "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s görsel bileşeni %(senderName)s tarafından eklendi",
+ "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s görsel bileşeni %(senderName)s tarafından silindi",
+ "%(names)s and %(count)s others are typing …|other": "%(names)s ve diğer %(count)s kişi yazıyor…",
+ "This homeserver has exceeded one of its resource limits.": "Bu anasunucu kaynak limitlerinden birini aştı.",
+ "Unable to connect to Homeserver. Retrying...": "Anasunucuya bağlanılamıyor. Yeniden deneniyor...",
+ "%(items)s and %(count)s others|other": "%(items)s ve diğer %(count)s",
+ "%(items)s and %(count)s others|one": "%(items)s ve bir diğeri",
+ "%(name)s (%(userId)s)": "%(name)s (%(userId)s)",
+ "Unrecognised address": "Tanınmayan adres",
+ "You do not have permission to invite people to this room.": "Bu odaya kişi davet etme izniniz yok.",
+ "User %(userId)s is already in the room": "Kullanıcı %(userId)s zaten odada",
+ "User %(user_id)s does not exist": "Kullanıcı %(user_id)s mevcut değil",
+ "User %(user_id)s may or may not exist": "Kullanıcı %(user_id)s mevcut olup olmadığı belli değil",
+ "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Hesabınıza giriş yapamazsınız. Lütfen daha fazla bilgi için ana sunucu yöneticiniz ile bağlantıya geçiniz.",
+ "You're signed out": "Çıkış yaptınız",
+ "Clear personal data": "Kişisel veri temizle",
+ "Command Autocomplete": "Oto tamamlama komutu",
+ "Community Autocomplete": "Oto tamlama topluluğu",
+ "DuckDuckGo Results": "DuckDuckGo Sonuçları",
+ "Emoji Autocomplete": "Emoji Oto Tamamlama",
+ "Notify the whole room": "Tüm odayı bilgilendir",
+ "Room Notification": "Oda Bildirimi",
+ "Set up with a recovery key": "Kurtarma anahtarı ile kur",
+ "That matches!": "Eşleşti!",
+ "That doesn't match.": "Eşleşmiyor.",
+ "Your Recovery Key": "Kurtarma Anahtarınız",
+ "Download": "İndir",
+ "Print it and store it somewhere safe": "Yazdır ve güvenli bir yerde sakla",
+ "Save it on a USB key or backup drive": "Bir USB anahtara kaydet veya sürücüye yedekle",
+ "Copy it to your personal cloud storage": "Kişisel bulut depolamaya kopyala",
+ "Your access to encrypted messages is now protected.": "Şifrelenmiş mesajlara erişiminiz şimdi korunuyor.",
+ "Migrate from Key Backup": "Anahtar Yedeğinden Göç Et",
+ "Recovery key": "Kurtarma anahtarı",
+ "Success!": "Başarılı!",
+ "Retry": "Yeniden Dene",
+ "Set up with a Recovery Key": "Bir Kurtarma Anahtarı ile Kur",
+ "Set up Secure Message Recovery": "Güvenli Mesaj Kurtarma Kurulumu",
+ "Starting backup...": "Yedeklemeye başlanıyor...",
+ "Create Key Backup": "Anahtar Yedeği Oluştur",
+ "Unable to create key backup": "Anahtar yedeği oluşturulamıyor",
+ "Don't ask again": "Yeniden sorma",
+ "New Recovery Method": "Yeni Kurtarma Yöntemi",
+ "Go to Settings": "Ayarlara Git",
+ "Recovery Method Removed": "Kurtarma Yöntemi Silindi",
+ "Robot": "Robot",
+ "Hat": "Şapka",
+ "Glasses": "Gözlük",
+ "Umbrella": "Şemsiye",
+ "Hourglass": "Kum saati",
+ "Clock": "Saat",
+ "Gift": "Hediye",
+ "Light bulb": "Ampül",
+ "Book": "Kitap",
+ "Pencil": "Kalem",
+ "Paperclip": "Ataç",
+ "Scissors": "Makas",
+ "Key": "Anahtar",
+ "Hammer": "Çekiç",
+ "Telephone": "Telefon",
+ "Flag": "Bayrak",
+ "Train": "Tren",
+ "Bicycle": "Bisiklet",
+ "Aeroplane": "Uçak",
+ "Rocket": "Füze",
+ "Ball": "Top",
+ "Guitar": "Gitar",
+ "Trumpet": "Trampet",
+ "Bell": "Zil",
+ "Anchor": "Çıpa",
+ "Headphones": "Kulaklık",
+ "Folder": "Klasör",
+ "Accept to continue:": "Devam etmek için i kabul ediniz:",
+ "Upload": "Yükle",
+ "on device": "cihaz üstünde",
+ "not found": "bulunamadı",
+ "in account data": "hesap verisinde",
+ "Your homeserver does not support device management.": "Aan sunucunuz aygıt yönetimini desteklemiyor.",
+ "Delete %(count)s devices|other": "%(count)s cihazı silin",
+ "Delete %(count)s devices|one": "Cihaz sil",
+ "ID": "ID",
+ "Connecting to integration manager...": "Entegrasyon yöneticisine bağlanın...",
+ "Cannot connect to integration manager": "Entegrasyon yöneticisine bağlanılamadı",
+ "Delete Backup": "Yedek Sil",
+ "Unable to load key backup status": "Anahtar yedek durumu yüklenemiyor",
+ "Restore from Backup": "Yedekten Geri Dön",
+ "This device is backing up your keys. ": "Bu cihaz anahtarlarınızı yedekliyor. ",
+ "Connect this device to Key Backup": "Anahtar Yedeği için bu cihazı bağlayın",
+ "not stored": "depolanmadı",
+ "Backing up %(sessionsRemaining)s keys...": "%(sessionsRemaining)s anahtar yedekleniyor...",
+ "All keys backed up": "Bütün yedekler yedeklendi",
+ "Backup is not signed by any of your devices": "Yedek hiç bir cihazınız tarafından imzalanmadı",
+ "Backup version: ": "Yedek sürümü: ",
+ "Algorithm: ": "Algoritma: ",
+ "Start using Key Backup": "Anahtar Yedekleme kullanmaya başla",
+ "Clear notifications": "Bildirimleri temizle",
+ "Enable desktop notifications for this device": "Bu cihaz için masaüstü bildirimlerini aç",
+ "Show message in desktop notification": "Masaüstü bildiriminde mesaj göster",
+ "Display Name": "Ekran Adı",
+ "Profile picture": "Profil resmi",
+ "Could not connect to Identity Server": "Kimlik Sunucusuna bağlanılamadı",
+ "Checking server": "Sunucu kontrol ediliyor",
+ "Change identity server": "Kimlik sunucu değiştir",
+ "Sorry, your homeserver is too old to participate in this room.": "Üzgünüm, ana sunucunuz bu odaya katılabilmek için oldukça eski.",
+ "Please contact your homeserver administrator.": "Lütfen anasunucu yöneticiniz ile bağlantıya geçin.",
+ "Message Pinning": "Mesaj Sabitleme",
+ "Multiple integration managers": "Çoklu entegrasyon yöneticileri",
+ "New DM invite dialog (under development)": "Yeni DM davet diyalog kutusu (geliştirilmekte)",
+ "Enable Emoji suggestions while typing": "Yazarken Emoji önerilerini aç",
+ "Show join/leave messages (invites/kicks/bans unaffected)": "Katılma/ayrılma mesajları göster (davetler/atılmalar/yasaklamalar etkilenmeyecek)",
+ "Show avatar changes": "Avatar değişikliklerini göster",
+ "Always show encryption icons": "Şifreleme ikonlarını her zaman göster",
+ "Enable big emoji in chat": "Sohbette büyük emojileri aç",
+ "Send typing notifications": "Yazma bildirimlerini gönder",
+ "Send analytics data": "Analiz verilerini gönder",
+ "Show developer tools": "Geliştirici araçlarını göster",
+ "Low bandwidth mode": "Düşük bant genişliği modu",
+ "Messages containing my username": "Kullanıcı adımı içeren mesajlar",
+ "When rooms are upgraded": "Odaların güncellenme zamanları",
+ "My Ban List": "Yasaklı Listem",
+ "Verified!": "Doğrulandı!",
+ "You've successfully verified this user.": "Bu kullanıcıyı başarılı şekilde doğruladınız.",
+ "Unable to find a supported verification method.": "Desteklenen doğrulama yöntemi bulunamadı.",
+ "Dog": "Köpek",
+ "Cat": "Kedi",
+ "Lion": "Aslan",
+ "Horse": "At",
+ "Unicorn": "Tek boynuzlu at",
+ "Pig": "Domuz",
+ "Elephant": "Fil",
+ "Rabbit": "Tavşan",
+ "Panda": "Panda",
+ "Penguin": "Penguen",
+ "Turtle": "Kaplumbağa",
+ "Fish": "Balık",
+ "Octopus": "Ahtapot",
+ "Butterfly": "Kelebek",
+ "Flower": "Çiçek",
+ "Tree": "Ağaç",
+ "Cactus": "Kaktüs",
+ "Mushroom": "Mantar",
+ "Globe": "Dünya",
+ "Moon": "Ay",
+ "Cloud": "Bulut",
+ "Fire": "Ateş",
+ "Banana": "Muz",
+ "Apple": "Elma",
+ "Strawberry": "Çilek",
+ "Corn": "Mısır",
+ "Pizza": "Pizza",
+ "Cake": "Kek",
+ "Heart": "Kalp",
+ "Trophy": "Ödül"
}
diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json
index e2df3fc774..7795060406 100644
--- a/src/i18n/strings/uk.json
+++ b/src/i18n/strings/uk.json
@@ -160,7 +160,7 @@
"The server may be unavailable or overloaded": "Сервер може бути недосяжним або перевантаженим",
"Room not found": "Кімнату не знайдено",
"Reject": "Відмовитись",
- "Failed to set Direct Message status of room": "Не вдалось встановити статус прямого спілкування в кімнаті",
+ "Failed to set Direct Message status of room": "Не вдалося встановити статус прямого спілкування в кімнаті",
"Monday": "Понеділок",
"Remove from Directory": "Прибрати з каталогу",
"Enable them now": "Увімкнути їх зараз",
@@ -405,7 +405,7 @@
"%(widgetName)s widget removed by %(senderName)s": "%(senderName)s вилучив/ла %(widgetName)s",
"Failure to create room": "Не вдалося створити кімнату",
"Server may be unavailable, overloaded, or you hit a bug.": "Сервер може бути недоступний, перевантажений, або ж ви натрапили на ваду.",
- "Send anyway": "Все-таки надіслати",
+ "Send anyway": "Надіслати хоч би там що",
"Unnamed Room": "Кімната без назви",
"This homeserver has hit its Monthly Active User limit.": "Цей домашній сервер досягнув свого ліміту щомісячних активних користувачів.",
"This homeserver has exceeded one of its resource limits.": "Цей домашній сервер досягнув одного зі своїх лімітів ресурсів.",
@@ -438,8 +438,8 @@
"Incorrect verification code": "Неправильний код перевірки",
"Submit": "Надіслати",
"Phone": "Телефон",
- "Failed to upload profile picture!": "Не вдалося завантажити світлину профілю!",
- "Upload new:": "Завантажити нову:",
+ "Failed to upload profile picture!": "Не вдалося відвантажити світлину профілю!",
+ "Upload new:": "Відвантажити нову:",
"No display name": "Немає імені для показу",
"New passwords don't match": "Нові паролі не збігаються",
"Passwords can't be empty": "Пароль не може бути пустим",
@@ -460,7 +460,7 @@
"Enable Notifications": "Увімкнути сповіщення",
"The maximum permitted number of widgets have already been added to this room.": "Максимально дозволену кількість віджетів уже додано до цієї кімнати.",
"Drop File Here": "Киньте файл сюди",
- "Drop file here to upload": "Киньте файл сюди, щоб вивантажити",
+ "Drop file here to upload": "Киньте файл сюди, щоб відвантажити",
" (unsupported)": " (не підтримується)",
"Join as voice or video.": "Приєднатися голосом або відео.",
"Ongoing conference call%(supportedText)s.": "Триває дзвінок-конференція%(supportedText)s.",
@@ -508,7 +508,7 @@
"Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Також ви можете спробувати використати публічний сервер turn.matrix.org, але це буде не настільки надійно, а також цей сервер матиме змогу бачити вашу IP-адресу. Ви можете керувати цим у налаштуваннях.",
"Try using turn.matrix.org": "Спробуйте використати turn.matrix.org",
"Replying With Files": "Відповісти файлами",
- "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Зараз неможливо відповісти файлом. Хочете завантажити цей файл без відповіді?",
+ "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Зараз неможливо відповісти файлом. Хочете відвантажити цей файл без відповіді?",
"Name or Matrix ID": "Імʼя або Matrix ID",
"Identity server has no terms of service": "Сервер ідентифікації не має умов надання послуг",
"This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Щоб підтвердити адресу е-пошту або телефон ця дія потребує доступу до типового серверу ідентифікації , але сервер не має жодних умов надання послуг.",
@@ -547,5 +547,41 @@
"Forces the current outbound group session in an encrypted room to be discarded": "Примусово відкидає поточний вихідний груповий сеанс у шифрованій кімнаті",
"Sends the given message coloured as a rainbow": "Надсилає вказане повідомлення розфарбоване веселкою",
"Your Riot is misconfigured": "Ваш Riot налаштовано неправильно",
- "Join the discussion": "Приєднатися до обговорення"
+ "Join the discussion": "Приєднатися до обговорення",
+ "Upload": "Відвантажити",
+ "Upload file": "Відвантажити файл",
+ "Send an encrypted message…": "Надіслати зашифроване повідомлення…",
+ "Send a message (unencrypted)…": "Надіслати повідомлення (незашифроване)…",
+ "The conversation continues here.": "Розмова триває тут.",
+ "This room has been replaced and is no longer active.": "Ця кімната була замінена і не є активною.",
+ "You do not have permission to post to this room": "У вас нема дозволу дописувати у цю кімнату",
+ "Sign out": "Вийти",
+ "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of Riot to do this": "Щоб уникнути втрати історії ваших листувань, ви маєте експортувати ключі кімнати перед виходом. Вам треба буде повернутися до новішої версії Riot аби зробити це",
+ "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ": "Раніше ви використовували новішу версію Riot на %(host)s. Для повторного користування цією версією з наскрізним шифруванням вам треба буде вийти та зайти знову. ",
+ "Incompatible Database": "Несумісна база даних",
+ "Continue With Encryption Disabled": "Продовжити із вимкненим шифруванням",
+ "Unknown error": "Невідома помилка",
+ "Incorrect password": "Неправильний пароль",
+ "Are you sure you want to sign out?": "Ви впевнені, що хочете вийти?",
+ "Your homeserver doesn't seem to support this feature.": "Схоже, що ваш домашній сервер не підтримує цю властивість.",
+ "Sign out and remove encryption keys?": "Вийти та видалити ключі шифрування?",
+ "Clear Storage and Sign Out": "Очистити сховище та вийти",
+ "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Очищення сховища вашого оглядача може усунути проблему, але воно виведе вас з системи і зробить непрочитною історію ваших зашифрованих листувань.",
+ "Verification Pending": "Очікується перевірка",
+ "Upload files (%(current)s of %(total)s)": "Відвантажити файли (%(current)s з %(total)s)",
+ "Upload files": "Відвантажити файли",
+ "Upload all": "Відвантажити всі",
+ "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Файл є надто великим для відвантаження. Допустимий розмір файлів — %(limit)s, але цей файл займає %(sizeOfThisFile)s.",
+ "These files are too large to upload. The file size limit is %(limit)s.": "Ці файли є надто великими для відвантаження. Допустимий розмір файлів — %(limit)s.",
+ "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Деякі файли є надто великими для відвантаження. Допустимий розмір файлів — %(limit)s.",
+ "Upload %(count)s other files|other": "Відвантажити %(count)s інших файли(ів)",
+ "Upload Error": "Помилка відвантаження",
+ "Failed to upload image": "Не вдалось відвантажити зображення",
+ "Upload avatar": "Завантажити аватар",
+ "For security, this session has been signed out. Please sign in again.": "З метою безпеки вашу сесію було завершено. Зайдіть, будь ласка, знову.",
+ "Upload an avatar:": "Завантажити аватар:",
+ "Send cross-signing keys to homeserver": "Надсилання ключей підпису на домашній сервер",
+ "Custom (%(level)s)": "Власний (%(level)s)",
+ "Error upgrading room": "Помилка оновлення кімнати",
+ "Double check that your server supports the room version chosen and try again.": "Перевірте, чи підримує ваш сервер вказану версію кімнати та спробуйте ще."
}
diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json
index 0c230734e0..db75121f07 100644
--- a/src/i18n/strings/zh_Hant.json
+++ b/src/i18n/strings/zh_Hant.json
@@ -1938,5 +1938,100 @@
"Notification settings": "通知設定",
"User Status": "使用者狀態",
"Reactions": "反應",
- " reacted with %(content)s": " 反應了 %(content)s"
+ " reacted with %(content)s": " 反應了 %(content)s",
+ " wants to chat": " 想要聊天",
+ "Start chatting": "開始聊天",
+ "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s 移除了封鎖符合 %(glob)s 使用者的規則",
+ "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s 移除了封鎖符合 %(glob)s 聊天室的規則",
+ "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s 移除了封鎖符合 %(glob)s 伺服器的規則",
+ "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s 移除了封鎖符合 %(glob)s 的規則",
+ "%(senderName)s updated an invalid ban rule": "%(senderName)s 更新無效的封鎖的規則",
+ "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s 更新了封鎖符合 %(glob)s 使用者的規則,因為 %(reason)s",
+ "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s 更新了封鎖符合 %(glob)s 聊天室的規則,因為 %(reason)s",
+ "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s 更新了封鎖符合 %(glob)s 伺服器的規則,因為 %(reason)s",
+ "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s 更新了封鎖符合 %(glob)s 的規則,因為 %(reason)s",
+ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s 建立了封鎖符合 %(glob)s 使用者的規則,因為 %(reason)s",
+ "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s 建立了封鎖符合 %(glob)s 聊天室的規則,因為 %(reason)s",
+ "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s 建立了封鎖符合 %(glob)s 伺服器的規則,因為 %(reason)s",
+ "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s 建立了封鎖符合 %(glob)s 的規則,因為 %(reason)s",
+ "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 將封鎖符合 %(oldGlob)s 使用者的規則變更為 %(newGlob)s,因為 %(reason)s",
+ "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 將封鎖符合 %(oldGlob)s 聊天室的規則變更為 %(newGlob)s,因為 %(reason)s",
+ "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 將封鎖符合 %(oldGlob)s 伺服器的規則變更為 %(newGlob)s,因為 %(reason)s",
+ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 將封鎖符合 %(oldGlob)s 的規則更新為 %(newGlob)s,因為 %(reason)s",
+ "Send cross-signing keys to homeserver": "將交叉簽章的金鑰傳送到家伺服器",
+ "Cross-signing public keys:": "交叉簽章的公開金鑰:",
+ "on device": "在裝置上",
+ "not found": "找不到",
+ "Cross-signing private keys:": "交叉簽章的私密金鑰:",
+ "in secret storage": "在秘密儲存空間中",
+ "Secret storage public key:": "秘密儲存空間公開金鑰:",
+ "in account data": "在帳號資料中",
+ "Bootstrap Secure Secret Storage": "Bootstrap 安全秘密儲存空間",
+ "Cross-signing": "交叉簽章",
+ "Enter secret storage passphrase": "輸入秘密儲存空間密碼",
+ "Unable to access secret storage. Please verify that you entered the correct passphrase.": "無法存取秘密儲存空間。請驗證您是否輸入了正確的密碼。",
+ "Warning: You should only access secret storage from a trusted computer.": "警告:您應該僅從信任的電腦存取秘密儲存空間。",
+ "Cross-signing and secret storage are enabled.": "已啟用交叉簽章與秘密儲存空間。",
+ "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "您的帳號在秘密儲存空間中有交叉簽章的身份,但尚未被此裝置信任。",
+ "Cross-signing and secret storage are not yet set up.": "尚未設定交叉簽章與秘密儲存空間。",
+ "Bootstrap cross-signing and secret storage": "啟動交叉簽章與秘密儲存空間",
+ "not stored": "未儲存",
+ "Backup has a valid signature from this user": "備份有從此使用者而來的有效簽章",
+ "Backup has a invalid signature from this user": "備份有從此使用者而來的無效簽章",
+ "Backup has a signature from unknown user with ID %(deviceId)s": "備份有從未知的使用者而來,ID 為 %(deviceId)s 的簽章",
+ "Backup has a signature from unknown device with ID %(deviceId)s": "備份有從未知的裝置而來,ID 為 %(deviceId)s 的簽章",
+ "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "備份金鑰儲存於秘密儲存空間中,但此功能未在此裝置上啟用。請在實驗室中啟用交叉簽章以修改金鑰備份狀態。",
+ "Backup key stored: ": "備份金鑰已儲存: ",
+ "Start using Key Backup with Secure Secret Storage": "開始將金鑰備份與安全秘密儲存空間一起使用",
+ "Hide verified sessions": "隱藏已驗證的工作階段",
+ "%(count)s verified sessions|other": "%(count)s 個已驗證的工作階段",
+ "%(count)s verified sessions|one": "1 個已驗證的工作階段",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "透過輸入您的密碼來存取您的安全訊息歷史與您的交叉簽章身份並驗證其他裝置。",
+ "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "如果您忘記您的密碼,您可以使用您的復原金鑰或設定新的復原選項。",
+ "Enter secret storage recovery key": "輸入秘密儲存空間復原金鑰",
+ "Unable to access secret storage. Please verify that you entered the correct recovery key.": "無法存取秘密儲存空間。請驗證您是否輸入了正確的復原金鑰。",
+ "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "透過輸入您的復原金鑰來存取您的安全訊息歷史與您的交叉簽章身份並驗證其他裝置。",
+ "If you've forgotten your recovery key you can .": "如果您忘記您的復原金鑰,您可以。",
+ "Warning: You should only set up key backup from a trusted computer.": "警告:您應該只從信任的電腦設定金鑰備份。",
+ "If you've forgotten your recovery key you can ": "如果您忘記您的復原金鑰,您可以",
+ "Warning: You should only set up secret storage from a trusted computer.": "警告:您應該只從信任的電腦設定秘密儲存空間。",
+ "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "我們將會使用秘密儲存空間來選擇性儲存您用來驗證其他裝置與訊息金鑰的交叉簽章加密副本在我們的伺服器上。使用密碼來保護您對加密訊息的存取權以使其更安全。",
+ "Set up with a recovery key": "設定復原金鑰",
+ "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "作為安全網,如果您忘記您的密碼的話,您可以使用它來恢復您對加密訊息的存取。",
+ "As a safety net, you can use it to restore your access to encrypted messages.": "作為安全網,您可以使用它來恢復您對加密訊息的存取。",
+ "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "將您的復原金鑰保留在某個非常安全的地方,如密碼管理員(或保險櫃)中。",
+ "Your recovery key has been copied to your clipboard, paste it to:": "您的復原金鑰已被複製到您的剪貼簿,請將其貼到:",
+ "Your recovery key is in your Downloads folder.": "您的復原金鑰在您的下載資料夾中。",
+ "Your access to encrypted messages is now protected.": "您對加密訊息的存取權已被保護。",
+ "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "沒有設定秘密儲存空間,您將無法恢復您的加密訊息的存取權,或是您在登出或使用其他裝置時,用以驗證其他裝置的交叉簽章身份。",
+ "Set up secret storage": "設定秘密儲存空間",
+ "Secure your encrypted messages with a passphrase": "使用密碼保護您的加密訊息",
+ "Storing secrets...": "正在儲存秘密……",
+ "Unable to set up secret storage": "無法設定秘密儲存空間",
+ "Close preview": "關閉預覽",
+ "This user has not verified all of their devices.": "這個使用者尚未驗證所有裝置。",
+ "You have not verified this user. This user has verified all of their devices.": "您尚未驗證此使用者。此使用者已驗證所有裝置。",
+ "You have verified this user. This user has verified all of their devices.": "您已驗證此使用者。此使用者已驗證所有裝置。",
+ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "在此已加密的聊天室中的某些使用者尚未被您驗證,或是他們尚未驗證他們的裝置。",
+ "All users in this encrypted room are verified by you and they have verified their own devices.": "在此已加密的聊天室中的所有使用者已被您驗證,他們也已驗證所有裝置。",
+ "Language Dropdown": "語言下拉式選單",
+ "Country Dropdown": "國家下拉式選單",
+ "The message you are trying to send is too large.": "您正試圖傳送的訊息太大了。",
+ "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "秘密儲存空間將使用你既有的金鑰備份資訊來設定。您的秘密儲存空間密碼與復原金鑰會與您的金鑰備份相同",
+ "Migrate from Key Backup": "從金鑰備份導入",
+ "Help": "說明",
+ "New DM invite dialog (under development)": "新的直接訊息邀請對話框(開發中)",
+ "Show more": "顯示更多",
+ "Recent Conversations": "最近的對話",
+ "Direct Messages": "直接訊息",
+ "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "如果您找不到某人,請向他們詢問他們的使用者名稱,或是分享您的使用者名稱 (%(userId)s) 或簡介連結。",
+ "Go": "到",
+ "Show info about bridges in room settings": "顯示關於聊天室設定中橋接相關的資訊",
+ "This bridge was provisioned by ": "此橋接由 配置",
+ "This bridge is managed by .": "此橋接由 管理。",
+ "Bridged into , on ": "橋接於 ,在 上",
+ "Connected to on ": "連線到 於 ",
+ "Connected via %(protocolName)s": "透過 %(protocolName)s 連線",
+ "Bridge Info": "橋接資訊",
+ "Below is a list of bridges connected to this room.": "以下是連線到此聊天室的橋接列表。"
}
diff --git a/src/rageshake/submit-rageshake.js b/src/rageshake/submit-rageshake.js
index 2c1dd7e16f..ed5a9e5946 100644
--- a/src/rageshake/submit-rageshake.js
+++ b/src/rageshake/submit-rageshake.js
@@ -27,6 +27,7 @@ import * as rageshake from './rageshake';
// polyfill textencoder if necessary
import * as TextEncodingUtf8 from 'text-encoding-utf-8';
+import SettingsStore from "../settings/SettingsStore";
let TextEncoder = window.TextEncoder;
if (!TextEncoder) {
TextEncoder = TextEncodingUtf8.TextEncoder;
@@ -85,6 +86,12 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
body.append('label', opts.label);
}
+ // add labs options
+ const enabledLabs = SettingsStore.getLabsFeatures().filter(SettingsStore.isFeatureEnabled);
+ if (enabledLabs.length) {
+ body.append('enabled_labs', enabledLabs.join(', '));
+ }
+
if (opts.sendLogs) {
progressCallback(_t("Collecting logs"));
const logs = await rageshake.getLogsForReport();
diff --git a/src/settings/Settings.js b/src/settings/Settings.js
index 82dd639819..14208e1f03 100644
--- a/src/settings/Settings.js
+++ b/src/settings/Settings.js
@@ -128,6 +128,12 @@ export const SETTINGS = {
supportedLevels: LEVELS_FEATURE,
default: false,
},
+ "feature_ftue_dms": {
+ isFeature: true,
+ displayName: _td("New DM invite dialog (under development)"),
+ supportedLevels: LEVELS_FEATURE,
+ default: false,
+ },
"mjolnirRooms": {
supportedLevels: ['account'],
default: [],
@@ -136,13 +142,6 @@ export const SETTINGS = {
supportedLevels: ['account'],
default: null,
},
- "feature_dm_verification": {
- isFeature: true,
- displayName: _td("Send verification requests in direct message," +
- " including a new verification UX in the member panel."),
- supportedLevels: LEVELS_FEATURE,
- default: false,
- },
"feature_cross_signing": {
isFeature: true,
displayName: _td("Enable cross-signing to verify per-user instead of per-device (in development)"),
@@ -156,10 +155,11 @@ export const SETTINGS = {
displayName: _td("Enable local event indexing and E2EE search (requires restart)"),
default: false,
},
- "useCiderComposer": {
- displayName: _td("Use the new, faster, composer for writing messages"),
- supportedLevels: LEVELS_ACCOUNT_SETTINGS,
- default: true,
+ "feature_bridge_state": {
+ isFeature: true,
+ supportedLevels: LEVELS_FEATURE,
+ displayName: _td("Show info about bridges in room settings"),
+ default: false,
},
"MessageComposerInput.suggestEmoji": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
diff --git a/src/stores/RightPanelStore.js b/src/stores/RightPanelStore.js
index 37e7498141..02775b847b 100644
--- a/src/stores/RightPanelStore.js
+++ b/src/stores/RightPanelStore.js
@@ -154,7 +154,7 @@ export default class RightPanelStore extends Store {
});
}
} else {
- if (targetPhase === this._state.lastRoomPhase) {
+ if (targetPhase === this._state.lastRoomPhase && !payload.refireParams) {
this._setState({
showRoomPanel: !this._state.showRoomPanel,
});
diff --git a/src/stores/TagOrderStore.js b/src/stores/TagOrderStore.js
index 3ca9e39560..deb6388461 100644
--- a/src/stores/TagOrderStore.js
+++ b/src/stores/TagOrderStore.js
@@ -141,8 +141,13 @@ class TagOrderStore extends Store {
newTags = [...this._state.selectedTags, payload.tag];
}
} else {
- // Select individual tag
- newTags = [payload.tag];
+ if (this._state.selectedTags.length === 1 && this._state.selectedTags.includes(payload.tag)) {
+ // Existing (only) selected tag is being normally clicked again, clear tags
+ newTags = [];
+ } else {
+ // Select individual tag
+ newTags = [payload.tag];
+ }
}
// Only set the anchor tag if the tag was previously unselected, otherwise
// the next range starts with an unselected tag.
diff --git a/src/stripped-emoji.json b/src/stripped-emoji.json
deleted file mode 100644
index 9d4308d24e..0000000000
--- a/src/stripped-emoji.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"name":"grinning face","shortname":":gleeful:","category":0,"emoji_order":1},{"name":"grinning face with big eyes","shortname":":glad:","category":0,"emoji_order":2,"aliases":[":smile:"]},{"name":"grinning face with smiling eyes","shortname":":happy:","category":0,"emoji_order":3},{"name":"beaming face with smiling eyes","shortname":":blissful:","category":0,"emoji_order":4,"aliases":[":grin:"],"aliases_ascii":[":D"]},{"name":"grinning squinting face","shortname":":amused:","category":0,"emoji_order":5,"aliases":[":laugh:",":lol:"],"aliases_ascii":["xD"]},{"name":"grinning face with sweat","shortname":":embarassed:","category":0,"emoji_order":6},{"name":"rolling on the floor laughing","shortname":":entertained:","category":0,"emoji_order":7,"aliases":[":rofl:"],"aliases_ascii":[":'D"]},{"name":"face with tears of joy","shortname":":joyful:","category":0,"emoji_order":8,"aliases":[":haha:"],"aliases_ascii":[":')"]},{"name":"slightly smiling face","shortname":":pleased:","category":0,"emoji_order":9,"aliases_ascii":[":)"]},{"name":"upside-down face","shortname":":ecstatic:","category":0,"emoji_order":10,"aliases":[":upside_down:"]},{"name":"winking face","shortname":":coy:","category":0,"emoji_order":11,"aliases":[":wink:"],"aliases_ascii":[";)"]},{"name":"smiling face with smiling eyes","shortname":":blush:","category":0,"emoji_order":12,"aliases_ascii":[":>"]},{"name":"smiling face with halo","shortname":":innocent:","category":0,"emoji_order":13,"aliases":[":halo:"],"aliases_ascii":["o:)"]},{"name":"smiling face with hearts","shortname":":love:","category":0,"emoji_order":14},{"name":"smiling face with heart-eyes","shortname":":lovestruck:","category":0,"emoji_order":15},{"name":"star-struck","shortname":":starstruck:","category":0,"emoji_order":16},{"name":"face blowing a kiss","shortname":":flirty:","category":0,"emoji_order":17,"aliases_ascii":[":x"]},{"name":"kissing face","shortname":":kiss:","category":0,"emoji_order":18},{"name":"smiling face","shortname":":relaxed:","category":0,"emoji_order":20},{"name":"kissing face with closed eyes","shortname":":loving_kiss:","category":0,"emoji_order":21,"aliases_ascii":[":*"]},{"name":"kissing face with smiling eyes","shortname":":happy_kiss:","category":0,"emoji_order":22},{"name":"face savoring food","shortname":":yum:","category":0,"emoji_order":23,"aliases":[":savour:"]},{"name":"face with tongue","shortname":":playful:","category":0,"emoji_order":24,"aliases":[":tongue_out:"],"aliases_ascii":[":p"]},{"name":"winking face with tongue","shortname":":mischievous:","category":0,"emoji_order":25,"aliases_ascii":[";p"]},{"name":"zany face","shortname":":crazy:","category":0,"emoji_order":26},{"name":"squinting face with tongue","shortname":":facetious:","category":0,"emoji_order":27,"aliases":[":lmao:"],"aliases_ascii":["xp"]},{"name":"money-mouth face","shortname":":pretentious:","category":0,"emoji_order":28,"aliases":[":money_mouth:"]},{"name":"hugging face","shortname":":hugging:","category":0,"emoji_order":29},{"name":"face with hand over mouth","shortname":":gasp:","category":0,"emoji_order":30},{"name":"shushing face","shortname":":shushing:","category":0,"emoji_order":31},{"name":"thinking face","shortname":":curious:","category":0,"emoji_order":32,"aliases":[":thinking:"],"aliases_ascii":[":l"]},{"name":"zipper-mouth face","shortname":":silenced:","category":0,"emoji_order":33,"aliases":[":zipper_mouth:"],"aliases_ascii":[":z"]},{"name":"face with raised eyebrow","shortname":":contempt:","category":0,"emoji_order":34},{"name":"neutral face","shortname":":indifferent:","category":0,"emoji_order":35,"aliases":[":neutral:"],"aliases_ascii":[":|"]},{"name":"expressionless face","shortname":":apathetic:","category":0,"emoji_order":36,"aliases":[":expressionless:"]},{"name":"face without mouth","shortname":":vacant:","category":0,"emoji_order":37,"aliases":[":no_mouth:"],"aliases_ascii":[":#"]},{"name":"smirking face","shortname":":cocky:","category":0,"emoji_order":38,"aliases":[":smirk:"],"aliases_ascii":[":j"]},{"name":"unamused face","shortname":":unamused:","category":0,"emoji_order":39,"aliases_ascii":[":?"]},{"name":"face with rolling eyes","shortname":":disbelief:","category":0,"emoji_order":40},{"name":"grimacing face","shortname":":grimaced:","category":0,"emoji_order":41,"aliases_ascii":["8D"]},{"name":"lying face","shortname":":lying:","category":0,"emoji_order":42},{"name":"relieved face","shortname":":relieved:","category":0,"emoji_order":43},{"name":"pensive face","shortname":":pensive:","category":0,"emoji_order":44},{"name":"sleepy face","shortname":":sleepy:","category":0,"emoji_order":45},{"name":"drooling face","shortname":":drooling:","category":0,"emoji_order":46},{"name":"sleeping face","shortname":":exhausted:","category":0,"emoji_order":47,"aliases":[":sleeping:"]},{"name":"face with medical mask","shortname":":ill:","category":0,"emoji_order":48,"aliases":[":mask:"]},{"name":"face with thermometer","shortname":":sick:","category":0,"emoji_order":49},{"name":"face with head-bandage","shortname":":injured:","category":0,"emoji_order":50},{"name":"nauseated face","shortname":":nauseated:","category":0,"emoji_order":51,"aliases_ascii":["%("]},{"name":"face vomiting","shortname":":vomiting:","category":0,"emoji_order":52},{"name":"sneezing face","shortname":":sneezing:","category":0,"emoji_order":53},{"name":"hot face","shortname":":overheating:","category":0,"emoji_order":54},{"name":"cold face","shortname":":freezing:","category":0,"emoji_order":55},{"name":"woozy face","shortname":":woozy:","category":0,"emoji_order":56,"aliases_ascii":[":&"]},{"name":"dizzy face","shortname":":dizzy:","category":0,"emoji_order":57,"aliases_ascii":["xo"]},{"name":"exploding head","shortname":":shocked:","category":0,"emoji_order":58,"aliases":[":exploding_head:"]},{"name":"cowboy hat face","shortname":":cowboy:","category":0,"emoji_order":59},{"name":"partying face","shortname":":partying:","category":0,"emoji_order":60,"aliases":[":celebrating:"]},{"name":"smiling face with sunglasses","shortname":":confident:","category":0,"emoji_order":61,"aliases_ascii":["8)"]},{"name":"nerd face","shortname":":nerd:","category":0,"emoji_order":62,"aliases_ascii":[":B"]},{"name":"face with monocle","shortname":":monocle:","category":0,"emoji_order":63},{"name":"confused face","shortname":":confused:","category":0,"emoji_order":64,"aliases_ascii":[":/"]},{"name":"worried face","shortname":":worried:","category":0,"emoji_order":65},{"name":"slightly frowning face","shortname":":cheerless:","category":0,"emoji_order":66},{"name":"frowning face","shortname":":sad:","category":0,"emoji_order":68,"aliases":[":frowning:"],"aliases_ascii":[":("]},{"name":"face with open mouth","shortname":":surprised:","category":0,"emoji_order":69},{"name":"hushed face","shortname":":hushed:","category":0,"emoji_order":70},{"name":"astonished face","shortname":":astonished:","category":0,"emoji_order":71,"aliases_ascii":[":o"]},{"name":"flushed face","shortname":":flushed:","category":0,"emoji_order":72,"aliases_ascii":[":$"]},{"name":"pleading face","shortname":":pleading:","category":0,"emoji_order":73},{"name":"frowning face with open mouth","shortname":":bored:","category":0,"emoji_order":74},{"name":"anguished face","shortname":":anguished:","category":0,"emoji_order":75,"aliases":[":wtf:"],"aliases_ascii":[":s"]},{"name":"fearful face","shortname":":fearful:","category":0,"emoji_order":76},{"name":"anxious face with sweat","shortname":":cold_sweat:","category":0,"emoji_order":77,"aliases":[":anxious:",":frustrated:"]},{"name":"sad but relieved face","shortname":":hopeful:","category":0,"emoji_order":78},{"name":"crying face","shortname":":upset:","category":0,"emoji_order":79,"aliases":[":cry:"],"aliases_ascii":[":'("]},{"name":"loudly crying face","shortname":":distressed:","category":0,"emoji_order":80,"aliases":[":sob:"],"aliases_ascii":[":'o"]},{"name":"face screaming in fear","shortname":":frightened:","category":0,"emoji_order":81,"aliases":[":scream:"],"aliases_ascii":["Dx"]},{"name":"confounded face","shortname":":confounded:","category":0,"emoji_order":82,"aliases_ascii":["x("]},{"name":"persevering face","shortname":":persevered:","category":0,"emoji_order":83},{"name":"disappointed face","shortname":":disappointed:","category":0,"emoji_order":84},{"name":"downcast face with sweat","shortname":":shamed:","category":0,"emoji_order":85,"aliases_ascii":[":<"]},{"name":"weary face","shortname":":weary:","category":0,"emoji_order":86,"aliases_ascii":["D:"]},{"name":"tired face","shortname":":tired:","category":0,"emoji_order":87,"aliases_ascii":[":c"]},{"name":"yawning face","shortname":":yawn:","category":0,"emoji_order":88},{"name":"face with steam from nose","shortname":":annoyed:","category":0,"emoji_order":89,"aliases":[":hrmph:"]},{"name":"pouting face","shortname":":enraged:","category":0,"emoji_order":90,"aliases":[":pout:"],"aliases_ascii":[">:/"]},{"name":"angry face","shortname":":angry:","category":0,"emoji_order":91},{"name":"face with symbols on mouth","shortname":":censored:","category":0,"emoji_order":92,"aliases_ascii":[":@"]},{"name":"smiling face with horns","shortname":":imp:","category":0,"emoji_order":93,"aliases_ascii":[">:)"]},{"name":"angry face with horns","shortname":":angry_imp:","category":0,"emoji_order":94,"aliases_ascii":[">:("]},{"name":"skull","shortname":":skull:","category":0,"emoji_order":95},{"name":"skull and crossbones","shortname":":crossbones:","category":0,"emoji_order":97},{"name":"pile of poo","shortname":":poop:","category":0,"emoji_order":98},{"name":"clown face","shortname":":clown:","category":0,"emoji_order":99},{"name":"ogre","shortname":":ogre:","category":0,"emoji_order":100,"aliases_ascii":[">0)"]},{"name":"goblin","shortname":":goblin:","category":0,"emoji_order":101},{"name":"ghost","shortname":":ghost:","category":0,"emoji_order":102},{"name":"alien","shortname":":alien:","category":0,"emoji_order":103},{"name":"alien monster","shortname":":alien_monster:","category":0,"emoji_order":104,"aliases":[":space_invader:"]},{"name":"robot","shortname":":robot:","category":0,"emoji_order":105},{"name":"grinning cat","shortname":":smiling_cat:","category":0,"emoji_order":106},{"name":"grinning cat with smiling eyes","shortname":":grinning_cat:","category":0,"emoji_order":107},{"name":"cat with tears of joy","shortname":":joyful_cat:","category":0,"emoji_order":108},{"name":"smiling cat with heart-eyes","shortname":":lovestruck_cat:","category":0,"emoji_order":109},{"name":"cat with wry smile","shortname":":smirking_cat:","category":0,"emoji_order":110},{"name":"kissing cat","shortname":":kissing_cat:","category":0,"emoji_order":111,"aliases_ascii":[":3"]},{"name":"weary cat","shortname":":weary_cat:","category":0,"emoji_order":112},{"name":"crying cat","shortname":":crying_cat:","category":0,"emoji_order":113},{"name":"pouting cat","shortname":":pouting_cat:","category":0,"emoji_order":114},{"name":"see-no-evil monkey","shortname":":see_no_evil:","category":0,"emoji_order":115},{"name":"hear-no-evil monkey","shortname":":hear_no_evil:","category":0,"emoji_order":116},{"name":"speak-no-evil monkey","shortname":":speak_no_evil:","category":0,"emoji_order":117},{"name":"kiss mark","shortname":":kiss_lips:","category":0,"emoji_order":118},{"name":"love letter","shortname":":love_letter:","category":0,"emoji_order":119},{"name":"heart with arrow","shortname":":cupid:","category":0,"emoji_order":120},{"name":"heart with ribbon","shortname":":heart_ribbon:","category":0,"emoji_order":121},{"name":"sparkling heart","shortname":":sparkling_heart:","category":0,"emoji_order":122},{"name":"growing heart","shortname":":heartpulse:","category":0,"emoji_order":123},{"name":"beating heart","shortname":":heartbeat:","category":0,"emoji_order":124},{"name":"revolving hearts","shortname":":revolving_hearts:","category":0,"emoji_order":125},{"name":"two hearts","shortname":":two_hearts:","category":0,"emoji_order":126},{"name":"heart decoration","shortname":":heart_decoration:","category":0,"emoji_order":127},{"name":"heart exclamation","shortname":":heart_exclamation:","category":0,"emoji_order":129},{"name":"broken heart","shortname":":broken_heart:","category":0,"emoji_order":130,"aliases_ascii":["3"]},{"name":"red heart","shortname":":heart:","category":0,"emoji_order":132,"aliases_ascii":["<3"]},{"name":"orange heart","shortname":":orange_heart:","category":0,"emoji_order":133},{"name":"yellow heart","shortname":":yellow_heart:","category":0,"emoji_order":134},{"name":"green heart","shortname":":green_heart:","category":0,"emoji_order":135},{"name":"blue heart","shortname":":blue_heart:","category":0,"emoji_order":136},{"name":"purple heart","shortname":":purple_heart:","category":0,"emoji_order":137},{"name":"brown heart","shortname":":brown_heart:","category":0,"emoji_order":138},{"name":"black heart","shortname":":black_heart:","category":0,"emoji_order":139},{"name":"white heart","shortname":":white_heart:","category":0,"emoji_order":140},{"name":"hundred points","shortname":":100:","category":0,"emoji_order":141},{"name":"anger symbol","shortname":":anger:","category":0,"emoji_order":142},{"name":"collision","shortname":":boom:","category":0,"emoji_order":143,"aliases":[":collision:"]},{"name":"dizzy","shortname":":dizzy_star:","category":0,"emoji_order":144},{"name":"sweat droplets","shortname":":sweat_drops:","category":0,"emoji_order":145},{"name":"dashing away","shortname":":dash:","category":0,"emoji_order":146},{"name":"hole","shortname":":hole:","category":0,"emoji_order":148},{"name":"bomb","shortname":":bomb:","category":0,"emoji_order":149},{"name":"speech balloon","shortname":":speech:","category":0,"emoji_order":150},{"name":"eye in speech bubble","shortname":":eye_bubble:","category":0,"emoji_order":151},{"name":"left speech bubble","shortname":":left_speech:","category":0,"emoji_order":156},{"name":"right anger bubble","shortname":":right_anger_speech:","category":0,"emoji_order":158},{"name":"thought balloon","shortname":":thought:","category":0,"emoji_order":159},{"name":"zzz","shortname":":zzz:","category":0,"emoji_order":160},{"name":"waving hand","shortname":":wave:","category":1,"emoji_order":161},{"name":"raised back of hand","shortname":":raised_backhand:","category":1,"emoji_order":167},{"name":"hand with fingers splayed","shortname":":splayed_hand:","category":1,"emoji_order":174},{"name":"raised hand","shortname":":raised_hand:","category":1,"emoji_order":180},{"name":"vulcan salute","shortname":":vulcan:","category":1,"emoji_order":186},{"name":"OK hand","shortname":":ok_hand:","category":1,"emoji_order":192},{"name":"pinching hand","shortname":":pinch:","category":1,"emoji_order":198},{"name":"victory hand","shortname":":victory:","category":1,"emoji_order":205},{"name":"crossed fingers","shortname":":fingers_crossed:","category":1,"emoji_order":211},{"name":"love-you gesture","shortname":":love_you_gesture:","category":1,"emoji_order":217},{"name":"sign of the horns","shortname":":metal:","category":1,"emoji_order":223,"aliases_ascii":["\\m/"]},{"name":"call me hand","shortname":":call_me:","category":1,"emoji_order":229},{"name":"backhand index pointing left","shortname":":point_left:","category":1,"emoji_order":235},{"name":"backhand index pointing right","shortname":":point_right:","category":1,"emoji_order":241},{"name":"backhand index pointing up","shortname":":backhand_point_up:","category":1,"emoji_order":247},{"name":"middle finger","shortname":":middle_finger:","category":1,"emoji_order":253},{"name":"backhand index pointing down","shortname":":point_down:","category":1,"emoji_order":259},{"name":"index pointing up","shortname":":point_up:","category":1,"emoji_order":266},{"name":"thumbs up","shortname":":thumbsup:","category":1,"emoji_order":272,"aliases":[":+1:",":yes:"]},{"name":"thumbs down","shortname":":thumbsdown:","category":1,"emoji_order":278,"aliases":[":-1:",":no:"]},{"name":"raised fist","shortname":":fist:","category":1,"emoji_order":284},{"name":"oncoming fist","shortname":":punch:","category":1,"emoji_order":290},{"name":"left-facing fist","shortname":":left_facing_fist:","category":1,"emoji_order":296},{"name":"right-facing fist","shortname":":right_facing_fist:","category":1,"emoji_order":302},{"name":"clapping hands","shortname":":clap:","category":1,"emoji_order":308},{"name":"raising hands","shortname":":raised_hands:","category":1,"emoji_order":314},{"name":"open hands","shortname":":open_hands:","category":1,"emoji_order":320},{"name":"palms up together","shortname":":palms_up:","category":1,"emoji_order":326},{"name":"handshake","shortname":":handshake:","category":1,"emoji_order":332},{"name":"folded hands","shortname":":pray:","category":1,"emoji_order":333},{"name":"writing hand","shortname":":writing_hand:","category":1,"emoji_order":340},{"name":"nail polish","shortname":":nail_care:","category":1,"emoji_order":346},{"name":"selfie","shortname":":selfie:","category":1,"emoji_order":352},{"name":"flexed biceps","shortname":":muscle:","category":1,"emoji_order":358,"aliases":[":right_bicep:"]},{"name":"mechanical arm","shortname":":mech_arm:","category":1,"emoji_order":364},{"name":"mechanical leg","shortname":":mech_leg:","category":1,"emoji_order":365},{"name":"leg","shortname":":leg:","category":1,"emoji_order":366},{"name":"foot","shortname":":foot:","category":1,"emoji_order":372},{"name":"ear","shortname":":ear:","category":1,"emoji_order":378},{"name":"ear with hearing aid","shortname":":hearing_aid:","category":1,"emoji_order":384},{"name":"nose","shortname":":nose:","category":1,"emoji_order":390},{"name":"brain","shortname":":brain:","category":1,"emoji_order":396},{"name":"tooth","shortname":":tooth:","category":1,"emoji_order":397},{"name":"bone","shortname":":bone:","category":1,"emoji_order":398},{"name":"eyes","shortname":":eyes:","category":1,"emoji_order":399},{"name":"eye","shortname":":eye:","category":1,"emoji_order":401},{"name":"tongue","shortname":":tongue:","category":1,"emoji_order":402},{"name":"mouth","shortname":":lips:","category":1,"emoji_order":403},{"name":"baby","shortname":":baby:","category":1,"emoji_order":404},{"name":"child","shortname":":child:","category":1,"emoji_order":410},{"name":"boy","shortname":":boy:","category":1,"emoji_order":416},{"name":"girl","shortname":":girl:","category":1,"emoji_order":422},{"name":"person","shortname":":adult:","category":1,"emoji_order":428},{"name":"person: blond hair","shortname":":blond_person:","category":1,"emoji_order":434},{"name":"man","shortname":":man:","category":1,"emoji_order":440},{"name":"man: beard","shortname":":bearded_person:","category":1,"emoji_order":446},{"name":"man: blond hair","shortname":":blond_man:","category":1,"emoji_order":452},{"name":"man: red hair","shortname":":red_haired_man:","category":1,"emoji_order":464},{"name":"man: curly hair","shortname":":curly_haired_man:","category":1,"emoji_order":470},{"name":"man: white hair","shortname":":white_haired_man:","category":1,"emoji_order":476},{"name":"man: bald","shortname":":bald_man:","category":1,"emoji_order":482},{"name":"woman","shortname":":woman:","category":1,"emoji_order":488},{"name":"woman: blond hair","shortname":":blond_woman:","category":1,"emoji_order":494},{"name":"woman: red hair","shortname":":red_haired_woman:","category":1,"emoji_order":506},{"name":"woman: curly hair","shortname":":curly_haired_woman:","category":1,"emoji_order":512},{"name":"woman: white hair","shortname":":white_haired_woman:","category":1,"emoji_order":518},{"name":"woman: bald","shortname":":bald_woman:","category":1,"emoji_order":524},{"name":"older person","shortname":":older_adult:","category":1,"emoji_order":530},{"name":"old man","shortname":":older_man:","category":1,"emoji_order":536},{"name":"old woman","shortname":":older_woman:","category":1,"emoji_order":542},{"name":"person frowning","shortname":":person_frowning:","category":1,"emoji_order":548},{"name":"man frowning","shortname":":man_frowning:","category":1,"emoji_order":554},{"name":"woman frowning","shortname":":woman_frowning:","category":1,"emoji_order":566},{"name":"person pouting","shortname":":person_pouting:","category":1,"emoji_order":578},{"name":"man pouting","shortname":":man_pouting:","category":1,"emoji_order":584},{"name":"woman pouting","shortname":":woman_pouting:","category":1,"emoji_order":596},{"name":"person gesturing NO","shortname":":person_gesturing_no:","category":1,"emoji_order":608},{"name":"man gesturing NO","shortname":":man_gesturing_no:","category":1,"emoji_order":614},{"name":"woman gesturing NO","shortname":":woman_gesturing_no:","category":1,"emoji_order":626},{"name":"person gesturing OK","shortname":":person_gesturing_ok:","category":1,"emoji_order":638},{"name":"man gesturing OK","shortname":":man_gesturing_ok:","category":1,"emoji_order":644},{"name":"woman gesturing OK","shortname":":woman_gesturing_ok:","category":1,"emoji_order":656},{"name":"person tipping hand","shortname":":person_tipping_hand:","category":1,"emoji_order":668},{"name":"man tipping hand","shortname":":man_tipping_hand:","category":1,"emoji_order":674},{"name":"woman tipping hand","shortname":":woman_tipping_hand:","category":1,"emoji_order":686},{"name":"person raising hand","shortname":":person_raising_hand:","category":1,"emoji_order":698},{"name":"man raising hand","shortname":":man_raising_hand:","category":1,"emoji_order":704},{"name":"woman raising hand","shortname":":woman_raising_hand:","category":1,"emoji_order":716},{"name":"deaf person","shortname":":person_deaf:","category":1,"emoji_order":728},{"name":"deaf man","shortname":":man_deaf:","category":1,"emoji_order":734},{"name":"deaf woman","shortname":":woman_deaf:","category":1,"emoji_order":746},{"name":"person bowing","shortname":":person_bowing:","category":1,"emoji_order":758},{"name":"man bowing","shortname":":man_bowing:","category":1,"emoji_order":764},{"name":"woman bowing","shortname":":woman_bowing:","category":1,"emoji_order":776},{"name":"person facepalming","shortname":":person_facepalming:","category":1,"emoji_order":788},{"name":"man facepalming","shortname":":man_facepalming:","category":1,"emoji_order":794},{"name":"woman facepalming","shortname":":woman_facepalming:","category":1,"emoji_order":806},{"name":"person shrugging","shortname":":person_shrugging:","category":1,"emoji_order":818},{"name":"man shrugging","shortname":":man_shrugging:","category":1,"emoji_order":824},{"name":"woman shrugging","shortname":":woman_shrugging:","category":1,"emoji_order":836},{"name":"man health worker","shortname":":man_health_worker:","category":1,"emoji_order":848},{"name":"woman health worker","shortname":":woman_health_worker:","category":1,"emoji_order":860},{"name":"man student","shortname":":man_student:","category":1,"emoji_order":872},{"name":"woman student","shortname":":woman_student:","category":1,"emoji_order":878},{"name":"man teacher","shortname":":man_teacher:","category":1,"emoji_order":884},{"name":"woman teacher","shortname":":woman_teacher:","category":1,"emoji_order":890},{"name":"man judge","shortname":":man_judge:","category":1,"emoji_order":896},{"name":"woman judge","shortname":":woman_judge:","category":1,"emoji_order":908},{"name":"man farmer","shortname":":man_farmer:","category":1,"emoji_order":920},{"name":"woman farmer","shortname":":woman_farmer:","category":1,"emoji_order":926},{"name":"man cook","shortname":":man_cook:","category":1,"emoji_order":932},{"name":"woman cook","shortname":":woman_cook:","category":1,"emoji_order":938},{"name":"man mechanic","shortname":":man_mechanic:","category":1,"emoji_order":944},{"name":"woman mechanic","shortname":":woman_mechanic:","category":1,"emoji_order":950},{"name":"man factory worker","shortname":":man_factory_worker:","category":1,"emoji_order":956},{"name":"woman factory worker","shortname":":woman_factory_worker:","category":1,"emoji_order":962},{"name":"man office worker","shortname":":man_office_worker:","category":1,"emoji_order":968},{"name":"woman office worker","shortname":":woman_office_worker:","category":1,"emoji_order":974},{"name":"man scientist","shortname":":man_scientist:","category":1,"emoji_order":980},{"name":"woman scientist","shortname":":woman_scientist:","category":1,"emoji_order":986},{"name":"man technologist","shortname":":man_technologist:","category":1,"emoji_order":992},{"name":"woman technologist","shortname":":woman_technologist:","category":1,"emoji_order":998},{"name":"man singer","shortname":":man_singer:","category":1,"emoji_order":1004},{"name":"woman singer","shortname":":woman_singer:","category":1,"emoji_order":1010},{"name":"man artist","shortname":":man_artist:","category":1,"emoji_order":1016},{"name":"woman artist","shortname":":woman_artist:","category":1,"emoji_order":1022},{"name":"man pilot","shortname":":man_pilot:","category":1,"emoji_order":1028},{"name":"woman pilot","shortname":":woman_pilot:","category":1,"emoji_order":1040},{"name":"man astronaut","shortname":":man_astronaut:","category":1,"emoji_order":1052},{"name":"woman astronaut","shortname":":woman_astronaut:","category":1,"emoji_order":1058},{"name":"man firefighter","shortname":":man_firefighter:","category":1,"emoji_order":1064},{"name":"woman firefighter","shortname":":woman_firefighter:","category":1,"emoji_order":1070},{"name":"police officer","shortname":":police_officer:","category":1,"emoji_order":1076},{"name":"man police officer","shortname":":man_police_officer:","category":1,"emoji_order":1082},{"name":"woman police officer","shortname":":woman_police_officer:","category":1,"emoji_order":1094},{"name":"detective","shortname":":detective:","category":1,"emoji_order":1107},{"name":"man detective","shortname":":man_detective:","category":1,"emoji_order":1113},{"name":"woman detective","shortname":":woman_detective:","category":1,"emoji_order":1127},{"name":"guard","shortname":":guard:","category":1,"emoji_order":1141},{"name":"man guard","shortname":":man_guard:","category":1,"emoji_order":1147},{"name":"woman guard","shortname":":woman_guard:","category":1,"emoji_order":1159},{"name":"construction worker","shortname":":construction_worker:","category":1,"emoji_order":1171},{"name":"man construction worker","shortname":":man_construction_worker:","category":1,"emoji_order":1177},{"name":"woman construction worker","shortname":":woman_construction_worker:","category":1,"emoji_order":1189},{"name":"prince","shortname":":prince:","category":1,"emoji_order":1201},{"name":"princess","shortname":":princess:","category":1,"emoji_order":1207},{"name":"person wearing turban","shortname":":person_turban:","category":1,"emoji_order":1213},{"name":"man wearing turban","shortname":":man_turban:","category":1,"emoji_order":1219},{"name":"woman wearing turban","shortname":":woman_turban:","category":1,"emoji_order":1231},{"name":"man with Chinese cap","shortname":":man_chinese_cap:","category":1,"emoji_order":1243},{"name":"woman with headscarf","shortname":":woman_headscarf:","category":1,"emoji_order":1249},{"name":"man in tuxedo","shortname":":man_tuxedo:","category":1,"emoji_order":1255},{"name":"bride with veil","shortname":":bride_veil:","category":1,"emoji_order":1261},{"name":"pregnant woman","shortname":":pregnant_woman:","category":1,"emoji_order":1267},{"name":"breast-feeding","shortname":":breast_feeding:","category":1,"emoji_order":1273},{"name":"baby angel","shortname":":baby_angel:","category":1,"emoji_order":1279},{"name":"Santa Claus","shortname":":santa:","category":1,"emoji_order":1285},{"name":"Mrs. Claus","shortname":":mrs_claus:","category":1,"emoji_order":1291},{"name":"superhero","shortname":":hero:","category":1,"emoji_order":1297,"aliases":[":superhero:"]},{"name":"man superhero","shortname":":man_hero:","category":1,"emoji_order":1303},{"name":"woman superhero","shortname":":woman_hero:","category":1,"emoji_order":1315},{"name":"supervillain","shortname":":villain:","category":1,"emoji_order":1327,"aliases":[":supervillain:"]},{"name":"man supervillain","shortname":":man_villain:","category":1,"emoji_order":1333},{"name":"woman supervillain","shortname":":woman_villain:","category":1,"emoji_order":1345},{"name":"mage","shortname":":mage:","category":1,"emoji_order":1357},{"name":"man mage","shortname":":man_mage:","category":1,"emoji_order":1363,"aliases_ascii":[":{>"]},{"name":"woman mage","shortname":":woman_mage:","category":1,"emoji_order":1375},{"name":"fairy","shortname":":fairy:","category":1,"emoji_order":1387},{"name":"man fairy","shortname":":man_fairy:","category":1,"emoji_order":1393},{"name":"woman fairy","shortname":":woman_fairy:","category":1,"emoji_order":1405},{"name":"vampire","shortname":":vampire:","category":1,"emoji_order":1417,"aliases_ascii":[":E"]},{"name":"man vampire","shortname":":man_vampire:","category":1,"emoji_order":1423},{"name":"woman vampire","shortname":":woman_vampire:","category":1,"emoji_order":1435},{"name":"merperson","shortname":":merperson:","category":1,"emoji_order":1447},{"name":"merman","shortname":":merman:","category":1,"emoji_order":1453},{"name":"mermaid","shortname":":mermaid:","category":1,"emoji_order":1465},{"name":"elf","shortname":":elf:","category":1,"emoji_order":1477},{"name":"man elf","shortname":":man_elf:","category":1,"emoji_order":1483},{"name":"woman elf","shortname":":woman_elf:","category":1,"emoji_order":1495},{"name":"genie","shortname":":genie:","category":1,"emoji_order":1507},{"name":"man genie","shortname":":man_genie:","category":1,"emoji_order":1508},{"name":"woman genie","shortname":":woman_genie:","category":1,"emoji_order":1510},{"name":"zombie","shortname":":zombie:","category":1,"emoji_order":1512,"aliases_ascii":["8#"]},{"name":"man zombie","shortname":":man_zombie:","category":1,"emoji_order":1513},{"name":"woman zombie","shortname":":woman_zombie:","category":1,"emoji_order":1515},{"name":"person getting massage","shortname":":person_getting_massage:","category":1,"emoji_order":1517},{"name":"man getting massage","shortname":":man_getting_face_massage:","category":1,"emoji_order":1523},{"name":"woman getting massage","shortname":":woman_getting_face_massage:","category":1,"emoji_order":1535},{"name":"person getting haircut","shortname":":person_getting_haircut:","category":1,"emoji_order":1547},{"name":"man getting haircut","shortname":":man_getting_haircut:","category":1,"emoji_order":1553},{"name":"woman getting haircut","shortname":":woman_getting_haircut:","category":1,"emoji_order":1565},{"name":"person walking","shortname":":person_walking:","category":1,"emoji_order":1577},{"name":"man walking","shortname":":man_walking:","category":1,"emoji_order":1583},{"name":"woman walking","shortname":":woman_walking:","category":1,"emoji_order":1595},{"name":"person standing","shortname":":person_standing:","category":1,"emoji_order":1607},{"name":"man standing","shortname":":man_standing:","category":1,"emoji_order":1613},{"name":"woman standing","shortname":":woman_standing:","category":1,"emoji_order":1625},{"name":"person kneeling","shortname":":person_kneeling:","category":1,"emoji_order":1637},{"name":"man kneeling","shortname":":man_kneeling:","category":1,"emoji_order":1643},{"name":"woman kneeling","shortname":":woman_kneeling:","category":1,"emoji_order":1655},{"name":"man with probing cane","shortname":":man_probing_cane:","category":1,"emoji_order":1667},{"name":"woman with probing cane","shortname":":woman_probing_cane:","category":1,"emoji_order":1673},{"name":"man in motorized wheelchair","shortname":":man_motor_wheelchair:","category":1,"emoji_order":1679},{"name":"woman in motorized wheelchair","shortname":":woman_motor_wheelchair:","category":1,"emoji_order":1685},{"name":"man in manual wheelchair","shortname":":man_wheelchair:","category":1,"emoji_order":1691},{"name":"woman in manual wheelchair","shortname":":woman_wheelchair:","category":1,"emoji_order":1697},{"name":"person running","shortname":":person_running:","category":1,"emoji_order":1703},{"name":"man running","shortname":":man_running:","category":1,"emoji_order":1709},{"name":"woman running","shortname":":woman_running:","category":1,"emoji_order":1721},{"name":"woman dancing","shortname":":dancer:","category":1,"emoji_order":1733,"aliases":[":woman_dancing:"]},{"name":"man dancing","shortname":":man_dancing:","category":1,"emoji_order":1739},{"name":"man in suit levitating","shortname":":levitate:","category":1,"emoji_order":1746},{"name":"people with bunny ears","shortname":":people_bunny_ears_partying:","category":1,"emoji_order":1752},{"name":"men with bunny ears","shortname":":men_bunny_ears_partying:","category":1,"emoji_order":1753},{"name":"women with bunny ears","shortname":":women_bunny_ears_partying:","category":1,"emoji_order":1755},{"name":"person in steamy room","shortname":":person_steamy_room:","category":1,"emoji_order":1757},{"name":"man in steamy room","shortname":":man_steamy_room:","category":1,"emoji_order":1763},{"name":"woman in steamy room","shortname":":woman_steamy_room:","category":1,"emoji_order":1775},{"name":"person climbing","shortname":":person_climbing:","category":1,"emoji_order":1787},{"name":"man climbing","shortname":":man_climbing:","category":1,"emoji_order":1793},{"name":"woman climbing","shortname":":woman_climbing:","category":1,"emoji_order":1805},{"name":"person fencing","shortname":":person_fencing:","category":1,"emoji_order":1817},{"name":"horse racing","shortname":":horse_racing:","category":1,"emoji_order":1818},{"name":"skier","shortname":":skier:","category":1,"emoji_order":1825},{"name":"snowboarder","shortname":":snowboarder:","category":1,"emoji_order":1826},{"name":"person golfing","shortname":":person_golfing:","category":1,"emoji_order":1833},{"name":"man golfing","shortname":":man_golfing:","category":1,"emoji_order":1839},{"name":"woman golfing","shortname":":woman_golfing:","category":1,"emoji_order":1853},{"name":"person surfing","shortname":":person_surfing:","category":1,"emoji_order":1867},{"name":"man surfing","shortname":":man_surfing:","category":1,"emoji_order":1873},{"name":"woman surfing","shortname":":woman_surfing:","category":1,"emoji_order":1885},{"name":"person rowing boat","shortname":":person_rowing_boat:","category":1,"emoji_order":1897},{"name":"man rowing boat","shortname":":man_rowing_boat:","category":1,"emoji_order":1903},{"name":"woman rowing boat","shortname":":woman_rowing_boat:","category":1,"emoji_order":1915},{"name":"person swimming","shortname":":person_swimming:","category":1,"emoji_order":1927},{"name":"man swimming","shortname":":man_swimming:","category":1,"emoji_order":1933},{"name":"woman swimming","shortname":":woman_swimming:","category":1,"emoji_order":1945},{"name":"person bouncing ball","shortname":":person_bouncing_ball:","category":1,"emoji_order":1958},{"name":"man bouncing ball","shortname":":man_bouncing_ball:","category":1,"emoji_order":1964},{"name":"woman bouncing ball","shortname":":woman_bouncing_ball:","category":1,"emoji_order":1978},{"name":"person lifting weights","shortname":":person_lifting_weights:","category":1,"emoji_order":1993},{"name":"man lifting weights","shortname":":man_lifting_weights:","category":1,"emoji_order":1999},{"name":"woman lifting weights","shortname":":woman_lifting_weights:","category":1,"emoji_order":2013},{"name":"person biking","shortname":":person_biking:","category":1,"emoji_order":2027},{"name":"man biking","shortname":":man_biking:","category":1,"emoji_order":2033},{"name":"woman biking","shortname":":woman_biking:","category":1,"emoji_order":2045},{"name":"person mountain biking","shortname":":person_mountain_biking:","category":1,"emoji_order":2057},{"name":"man mountain biking","shortname":":man_mountain_biking:","category":1,"emoji_order":2063},{"name":"woman mountain biking","shortname":":woman_mountain_biking:","category":1,"emoji_order":2075},{"name":"person cartwheeling","shortname":":person_cartwheel:","category":1,"emoji_order":2087},{"name":"man cartwheeling","shortname":":man_cartwheeling:","category":1,"emoji_order":2093},{"name":"woman cartwheeling","shortname":":woman_cartwheeling:","category":1,"emoji_order":2105},{"name":"people wrestling","shortname":":people_wrestling:","category":1,"emoji_order":2117},{"name":"men wrestling","shortname":":men_wrestling:","category":1,"emoji_order":2118},{"name":"women wrestling","shortname":":women_wrestling:","category":1,"emoji_order":2120},{"name":"person playing water polo","shortname":":person_water_polo:","category":1,"emoji_order":2122},{"name":"man playing water polo","shortname":":man_water_polo:","category":1,"emoji_order":2128},{"name":"woman playing water polo","shortname":":woman_water_polo:","category":1,"emoji_order":2140},{"name":"person playing handball","shortname":":person_handball:","category":1,"emoji_order":2152},{"name":"man playing handball","shortname":":man_handball:","category":1,"emoji_order":2158},{"name":"woman playing handball","shortname":":woman_handball:","category":1,"emoji_order":2170},{"name":"person juggling","shortname":":person_juggling:","category":1,"emoji_order":2182},{"name":"man juggling","shortname":":man_juggling:","category":1,"emoji_order":2188},{"name":"woman juggling","shortname":":woman_juggling:","category":1,"emoji_order":2200},{"name":"person in lotus position","shortname":":person_lotus_position:","category":1,"emoji_order":2212},{"name":"man in lotus position","shortname":":man_lotus_position:","category":1,"emoji_order":2218},{"name":"woman in lotus position","shortname":":woman_lotus_position:","category":1,"emoji_order":2230},{"name":"person taking bath","shortname":":bath:","category":1,"emoji_order":2242},{"name":"person in bed","shortname":":in_bed:","category":1,"emoji_order":2248},{"name":"people holding hands","shortname":":holding_hands_people:","category":1,"emoji_order":2254},{"name":"women holding hands","shortname":":holding_hands_ww:","category":1,"emoji_order":2270},{"name":"woman and man holding hands","shortname":":holding_hands_mw:","category":1,"emoji_order":2286,"aliases":[":holding_hands_wm:"]},{"name":"men holding hands","shortname":":holding_hands_mm:","category":1,"emoji_order":2312},{"name":"kiss","shortname":":couple:","category":1,"emoji_order":2328},{"name":"kiss: woman, man","shortname":":kiss_mw:","category":1,"emoji_order":2329,"aliases":[":kiss_wm:"]},{"name":"kiss: man, man","shortname":":kiss_mm:","category":1,"emoji_order":2331},{"name":"kiss: woman, woman","shortname":":kiss_ww:","category":1,"emoji_order":2333},{"name":"couple with heart","shortname":":couple_heart:","category":1,"emoji_order":2335},{"name":"couple with heart: woman, man","shortname":":couple_mw:","category":1,"emoji_order":2336,"aliases":[":couple_wm:"]},{"name":"couple with heart: man, man","shortname":":couple_mm:","category":1,"emoji_order":2338},{"name":"couple with heart: woman, woman","shortname":":couple_ww:","category":1,"emoji_order":2340},{"name":"family","shortname":":family:","category":1,"emoji_order":2342},{"name":"family: man, woman, boy","shortname":":family_mwb:","category":1,"emoji_order":2343},{"name":"family: man, woman, girl","shortname":":family_mwg:","category":1,"emoji_order":2344},{"name":"family: man, woman, girl, boy","shortname":":family_mwgb:","category":1,"emoji_order":2345},{"name":"family: man, woman, boy, boy","shortname":":family_mwbb:","category":1,"emoji_order":2346},{"name":"family: man, woman, girl, girl","shortname":":family_mwgg:","category":1,"emoji_order":2347},{"name":"family: man, man, boy","shortname":":family_mmb:","category":1,"emoji_order":2348},{"name":"family: man, man, girl","shortname":":family_mmg:","category":1,"emoji_order":2349},{"name":"family: man, man, girl, boy","shortname":":family_mmgb:","category":1,"emoji_order":2350},{"name":"family: man, man, boy, boy","shortname":":family_mmbb:","category":1,"emoji_order":2351},{"name":"family: man, man, girl, girl","shortname":":family_mmgg:","category":1,"emoji_order":2352},{"name":"family: woman, woman, boy","shortname":":family_wwb:","category":1,"emoji_order":2353},{"name":"family: woman, woman, girl","shortname":":family_wwg:","category":1,"emoji_order":2354},{"name":"family: woman, woman, girl, boy","shortname":":family_wwgb:","category":1,"emoji_order":2355},{"name":"family: woman, woman, boy, boy","shortname":":family_wwbb:","category":1,"emoji_order":2356},{"name":"family: woman, woman, girl, girl","shortname":":family_wwgg:","category":1,"emoji_order":2357},{"name":"family: man, boy","shortname":":family_mb:","category":1,"emoji_order":2358},{"name":"family: man, boy, boy","shortname":":family_mbb:","category":1,"emoji_order":2359},{"name":"family: man, girl","shortname":":family_mg:","category":1,"emoji_order":2360},{"name":"family: man, girl, boy","shortname":":family_mgb:","category":1,"emoji_order":2361},{"name":"family: man, girl, girl","shortname":":family_mgg:","category":1,"emoji_order":2362},{"name":"family: woman, boy","shortname":":family_wb:","category":1,"emoji_order":2363},{"name":"family: woman, boy, boy","shortname":":family_wbb:","category":1,"emoji_order":2364},{"name":"family: woman, girl","shortname":":family_wg:","category":1,"emoji_order":2365},{"name":"family: woman, girl, boy","shortname":":family_wgb:","category":1,"emoji_order":2366},{"name":"family: woman, girl, girl","shortname":":family_wgg:","category":1,"emoji_order":2367},{"name":"speaking head","shortname":":speaking_head:","category":1,"emoji_order":2369},{"name":"bust in silhouette","shortname":":bust_silhouette:","category":1,"emoji_order":2370},{"name":"busts in silhouette","shortname":":busts_silhouette:","category":1,"emoji_order":2371},{"name":"footprints","shortname":":footprints:","category":1,"emoji_order":2372},{"name":"light skin tone","shortname":":tone_light:","category":2,"emoji_order":2373,"aliases":[":tone1:"]},{"name":"medium-light skin tone","shortname":":tone_medium_light:","category":2,"emoji_order":2374,"aliases":[":tone2:"]},{"name":"medium skin tone","shortname":":tone_medium:","category":2,"emoji_order":2375,"aliases":[":tone3:"]},{"name":"medium-dark skin tone","shortname":":tone_medium_dark:","category":2,"emoji_order":2376,"aliases":[":tone4:"]},{"name":"dark skin tone","shortname":":tone_dark:","category":2,"emoji_order":2377,"aliases":[":tone5:"]},{"name":"red hair","shortname":":red_hair:","category":2,"emoji_order":2378},{"name":"curly hair","shortname":":curly_hair:","category":2,"emoji_order":2379},{"name":"white hair","shortname":":white_hair:","category":2,"emoji_order":2380},{"name":"bald","shortname":":bald:","category":2,"emoji_order":2381},{"name":"monkey face","shortname":":monkey_face:","category":3,"emoji_order":2382},{"name":"monkey","shortname":":monkey:","category":3,"emoji_order":2383},{"name":"gorilla","shortname":":gorilla:","category":3,"emoji_order":2384},{"name":"orangutan","shortname":":orangutan:","category":3,"emoji_order":2385},{"name":"dog face","shortname":":dog_face:","category":3,"emoji_order":2386},{"name":"dog","shortname":":dog:","category":3,"emoji_order":2387},{"name":"guide dog","shortname":":guide_dog:","category":3,"emoji_order":2388},{"name":"service dog","shortname":":service_dog:","category":3,"emoji_order":2389},{"name":"poodle","shortname":":poodle:","category":3,"emoji_order":2390},{"name":"wolf","shortname":":wolf_face:","category":3,"emoji_order":2391},{"name":"fox","shortname":":fox_face:","category":3,"emoji_order":2392},{"name":"raccoon","shortname":":raccoon:","category":3,"emoji_order":2393},{"name":"cat face","shortname":":cat_face:","category":3,"emoji_order":2394},{"name":"cat","shortname":":cat:","category":3,"emoji_order":2395},{"name":"lion","shortname":":lion_face:","category":3,"emoji_order":2396},{"name":"tiger face","shortname":":tiger_face:","category":3,"emoji_order":2397},{"name":"tiger","shortname":":tiger:","category":3,"emoji_order":2398},{"name":"leopard","shortname":":leopard:","category":3,"emoji_order":2399},{"name":"horse face","shortname":":horse_face:","category":3,"emoji_order":2400},{"name":"horse","shortname":":horse:","category":3,"emoji_order":2401},{"name":"unicorn","shortname":":unicorn_face:","category":3,"emoji_order":2402},{"name":"zebra","shortname":":zebra:","category":3,"emoji_order":2403},{"name":"deer","shortname":":deer:","category":3,"emoji_order":2404},{"name":"cow face","shortname":":cow_face:","category":3,"emoji_order":2405},{"name":"ox","shortname":":ox:","category":3,"emoji_order":2406},{"name":"water buffalo","shortname":":water_buffalo:","category":3,"emoji_order":2407},{"name":"cow","shortname":":cow:","category":3,"emoji_order":2408},{"name":"pig face","shortname":":pig_face:","category":3,"emoji_order":2409},{"name":"pig","shortname":":pig:","category":3,"emoji_order":2410},{"name":"boar","shortname":":boar:","category":3,"emoji_order":2411},{"name":"pig nose","shortname":":pig_nose:","category":3,"emoji_order":2412},{"name":"ram","shortname":":ram:","category":3,"emoji_order":2413},{"name":"ewe","shortname":":sheep:","category":3,"emoji_order":2414},{"name":"goat","shortname":":goat:","category":3,"emoji_order":2415},{"name":"camel","shortname":":camel:","category":3,"emoji_order":2416},{"name":"two-hump camel","shortname":":two_hump_camel:","category":3,"emoji_order":2417},{"name":"llama","shortname":":llama:","category":3,"emoji_order":2418},{"name":"giraffe","shortname":":giraffe:","category":3,"emoji_order":2419},{"name":"elephant","shortname":":elephant:","category":3,"emoji_order":2420},{"name":"rhinoceros","shortname":":rhino:","category":3,"emoji_order":2421},{"name":"hippopotamus","shortname":":hippo:","category":3,"emoji_order":2422},{"name":"mouse face","shortname":":mouse_face:","category":3,"emoji_order":2423},{"name":"mouse","shortname":":mouse:","category":3,"emoji_order":2424},{"name":"rat","shortname":":rat:","category":3,"emoji_order":2425},{"name":"hamster","shortname":":hamster_face:","category":3,"emoji_order":2426},{"name":"rabbit face","shortname":":rabbit_face:","category":3,"emoji_order":2427},{"name":"rabbit","shortname":":rabbit:","category":3,"emoji_order":2428},{"name":"chipmunk","shortname":":chipmunk:","category":3,"emoji_order":2430},{"name":"hedgehog","shortname":":hedgehog:","category":3,"emoji_order":2431},{"name":"bat","shortname":":bat:","category":3,"emoji_order":2432},{"name":"bear","shortname":":bear_face:","category":3,"emoji_order":2433},{"name":"koala","shortname":":koala_face:","category":3,"emoji_order":2434},{"name":"panda","shortname":":panda_face:","category":3,"emoji_order":2435},{"name":"sloth","shortname":":sloth:","category":3,"emoji_order":2436},{"name":"otter","shortname":":otter:","category":3,"emoji_order":2437},{"name":"skunk","shortname":":skunk:","category":3,"emoji_order":2438},{"name":"kangaroo","shortname":":kangaroo:","category":3,"emoji_order":2439},{"name":"badger","shortname":":badger:","category":3,"emoji_order":2440},{"name":"paw prints","shortname":":feet:","category":3,"emoji_order":2441},{"name":"turkey","shortname":":turkey:","category":3,"emoji_order":2442},{"name":"chicken","shortname":":chicken:","category":3,"emoji_order":2443},{"name":"rooster","shortname":":rooster:","category":3,"emoji_order":2444},{"name":"hatching chick","shortname":":hatching_chick:","category":3,"emoji_order":2445},{"name":"baby chick","shortname":":baby_chick:","category":3,"emoji_order":2446},{"name":"front-facing baby chick","shortname":":hatched_chick:","category":3,"emoji_order":2447},{"name":"bird","shortname":":bird:","category":3,"emoji_order":2448},{"name":"penguin","shortname":":penguin:","category":3,"emoji_order":2449},{"name":"dove","shortname":":dove:","category":3,"emoji_order":2451},{"name":"eagle","shortname":":eagle:","category":3,"emoji_order":2452},{"name":"duck","shortname":":duck:","category":3,"emoji_order":2453},{"name":"swan","shortname":":swan:","category":3,"emoji_order":2454},{"name":"owl","shortname":":owl:","category":3,"emoji_order":2455},{"name":"flamingo","shortname":":flamingo:","category":3,"emoji_order":2456},{"name":"peacock","shortname":":peacock:","category":3,"emoji_order":2457},{"name":"parrot","shortname":":parrot:","category":3,"emoji_order":2458},{"name":"frog","shortname":":frog_face:","category":3,"emoji_order":2459},{"name":"crocodile","shortname":":crocodile:","category":3,"emoji_order":2460},{"name":"turtle","shortname":":turtle:","category":3,"emoji_order":2461},{"name":"lizard","shortname":":lizard:","category":3,"emoji_order":2462},{"name":"snake","shortname":":snake:","category":3,"emoji_order":2463},{"name":"dragon face","shortname":":dragon_face:","category":3,"emoji_order":2464},{"name":"dragon","shortname":":dragon:","category":3,"emoji_order":2465},{"name":"sauropod","shortname":":sauropod:","category":3,"emoji_order":2466},{"name":"T-Rex","shortname":":trex:","category":3,"emoji_order":2467},{"name":"spouting whale","shortname":":spouting_whale:","category":3,"emoji_order":2468},{"name":"whale","shortname":":whale:","category":3,"emoji_order":2469},{"name":"dolphin","shortname":":dolphin:","category":3,"emoji_order":2470},{"name":"fish","shortname":":fish:","category":3,"emoji_order":2471},{"name":"tropical fish","shortname":":tropical_fish:","category":3,"emoji_order":2472},{"name":"blowfish","shortname":":blowfish:","category":3,"emoji_order":2473},{"name":"shark","shortname":":shark:","category":3,"emoji_order":2474},{"name":"octopus","shortname":":octopus:","category":3,"emoji_order":2475},{"name":"spiral shell","shortname":":shell:","category":3,"emoji_order":2476},{"name":"snail","shortname":":snail:","category":3,"emoji_order":2477},{"name":"butterfly","shortname":":butterfly:","category":3,"emoji_order":2478},{"name":"bug","shortname":":bug:","category":3,"emoji_order":2479},{"name":"ant","shortname":":ant:","category":3,"emoji_order":2480},{"name":"honeybee","shortname":":bee:","category":3,"emoji_order":2481},{"name":"lady beetle","shortname":":beetle:","category":3,"emoji_order":2482},{"name":"cricket","shortname":":cricket:","category":3,"emoji_order":2483},{"name":"spider","shortname":":spider:","category":3,"emoji_order":2485},{"name":"spider web","shortname":":spider_web:","category":3,"emoji_order":2487},{"name":"scorpion","shortname":":scorpion:","category":3,"emoji_order":2488},{"name":"mosquito","shortname":":mosquito:","category":3,"emoji_order":2489},{"name":"microbe","shortname":":microbe:","category":3,"emoji_order":2490,"aliases":[":germ:"]},{"name":"bouquet","shortname":":bouquet:","category":3,"emoji_order":2491},{"name":"cherry blossom","shortname":":cherry_blossom:","category":3,"emoji_order":2492},{"name":"white flower","shortname":":white_flower:","category":3,"emoji_order":2493},{"name":"rosette","shortname":":rosette:","category":3,"emoji_order":2495},{"name":"rose","shortname":":rose:","category":3,"emoji_order":2496},{"name":"wilted flower","shortname":":wilted_rose:","category":3,"emoji_order":2497},{"name":"hibiscus","shortname":":hibiscus:","category":3,"emoji_order":2498},{"name":"sunflower","shortname":":sunflower:","category":3,"emoji_order":2499},{"name":"blossom","shortname":":blossom:","category":3,"emoji_order":2500},{"name":"tulip","shortname":":tulip:","category":3,"emoji_order":2501},{"name":"seedling","shortname":":seedling:","category":3,"emoji_order":2502},{"name":"evergreen tree","shortname":":evergreen_tree:","category":3,"emoji_order":2503},{"name":"deciduous tree","shortname":":deciduous_tree:","category":3,"emoji_order":2504},{"name":"palm tree","shortname":":palm_tree:","category":3,"emoji_order":2505},{"name":"cactus","shortname":":cactus:","category":3,"emoji_order":2506},{"name":"sheaf of rice","shortname":":ear_of_rice:","category":3,"emoji_order":2507},{"name":"herb","shortname":":herb:","category":3,"emoji_order":2508},{"name":"shamrock","shortname":":shamrock:","category":3,"emoji_order":2510},{"name":"four leaf clover","shortname":":four_leaf_clover:","category":3,"emoji_order":2511},{"name":"maple leaf","shortname":":maple_leaf:","category":3,"emoji_order":2512},{"name":"fallen leaf","shortname":":fallen_leaf:","category":3,"emoji_order":2513},{"name":"leaf fluttering in wind","shortname":":leaves:","category":3,"emoji_order":2514},{"name":"grapes","shortname":":grapes:","category":4,"emoji_order":2515},{"name":"melon","shortname":":melon:","category":4,"emoji_order":2516},{"name":"watermelon","shortname":":watermelon:","category":4,"emoji_order":2517},{"name":"tangerine","shortname":":tangerine:","category":4,"emoji_order":2518},{"name":"lemon","shortname":":lemon:","category":4,"emoji_order":2519},{"name":"banana","shortname":":banana:","category":4,"emoji_order":2520},{"name":"pineapple","shortname":":pineapple:","category":4,"emoji_order":2521},{"name":"mango","shortname":":mango:","category":4,"emoji_order":2522},{"name":"red apple","shortname":":apple:","category":4,"emoji_order":2523},{"name":"green apple","shortname":":green_apple:","category":4,"emoji_order":2524},{"name":"pear","shortname":":pear:","category":4,"emoji_order":2525},{"name":"peach","shortname":":peach:","category":4,"emoji_order":2526},{"name":"cherries","shortname":":cherries:","category":4,"emoji_order":2527},{"name":"strawberry","shortname":":strawberry:","category":4,"emoji_order":2528},{"name":"kiwi fruit","shortname":":kiwi:","category":4,"emoji_order":2529},{"name":"tomato","shortname":":tomato:","category":4,"emoji_order":2530},{"name":"coconut","shortname":":coconut:","category":4,"emoji_order":2531},{"name":"avocado","shortname":":avocado:","category":4,"emoji_order":2532},{"name":"eggplant","shortname":":eggplant:","category":4,"emoji_order":2533},{"name":"potato","shortname":":potato:","category":4,"emoji_order":2534},{"name":"carrot","shortname":":carrot:","category":4,"emoji_order":2535},{"name":"ear of corn","shortname":":corn:","category":4,"emoji_order":2536},{"name":"hot pepper","shortname":":hot_pepper:","category":4,"emoji_order":2538},{"name":"cucumber","shortname":":cucumber:","category":4,"emoji_order":2539},{"name":"leafy green","shortname":":leafy_green:","category":4,"emoji_order":2540},{"name":"broccoli","shortname":":broccoli:","category":4,"emoji_order":2541},{"name":"garlic","shortname":":garlic:","category":4,"emoji_order":2542},{"name":"onion","shortname":":onion:","category":4,"emoji_order":2543},{"name":"mushroom","shortname":":mushroom:","category":4,"emoji_order":2544},{"name":"peanuts","shortname":":peanuts:","category":4,"emoji_order":2545},{"name":"chestnut","shortname":":chestnut:","category":4,"emoji_order":2546},{"name":"bread","shortname":":bread:","category":4,"emoji_order":2547},{"name":"croissant","shortname":":croissant:","category":4,"emoji_order":2548},{"name":"baguette bread","shortname":":french_bread:","category":4,"emoji_order":2549},{"name":"pretzel","shortname":":pretzel:","category":4,"emoji_order":2550},{"name":"bagel","shortname":":bagel:","category":4,"emoji_order":2551},{"name":"pancakes","shortname":":pancakes:","category":4,"emoji_order":2552},{"name":"waffle","shortname":":waffle:","category":4,"emoji_order":2553},{"name":"cheese wedge","shortname":":cheese:","category":4,"emoji_order":2554},{"name":"meat on bone","shortname":":meat_on_bone:","category":4,"emoji_order":2555},{"name":"poultry leg","shortname":":poultry_leg:","category":4,"emoji_order":2556},{"name":"cut of meat","shortname":":cut_of_meat:","category":4,"emoji_order":2557},{"name":"bacon","shortname":":bacon:","category":4,"emoji_order":2558},{"name":"hamburger","shortname":":hamburger:","category":4,"emoji_order":2559},{"name":"french fries","shortname":":fries:","category":4,"emoji_order":2560},{"name":"pizza","shortname":":pizza:","category":4,"emoji_order":2561},{"name":"hot dog","shortname":":hotdog:","category":4,"emoji_order":2562},{"name":"sandwich","shortname":":sandwich:","category":4,"emoji_order":2563},{"name":"taco","shortname":":taco:","category":4,"emoji_order":2564},{"name":"burrito","shortname":":burrito:","category":4,"emoji_order":2565},{"name":"stuffed flatbread","shortname":":stuffed_flatbread:","category":4,"emoji_order":2566},{"name":"falafel","shortname":":falafel:","category":4,"emoji_order":2567},{"name":"egg","shortname":":egg:","category":4,"emoji_order":2568},{"name":"cooking","shortname":":cooking:","category":4,"emoji_order":2569},{"name":"shallow pan of food","shortname":":shallow_pan_of_food:","category":4,"emoji_order":2570},{"name":"pot of food","shortname":":stew:","category":4,"emoji_order":2571},{"name":"bowl with spoon","shortname":":bowl_spoon:","category":4,"emoji_order":2572},{"name":"green salad","shortname":":salad:","category":4,"emoji_order":2573},{"name":"popcorn","shortname":":popcorn:","category":4,"emoji_order":2574},{"name":"butter","shortname":":butter:","category":4,"emoji_order":2575},{"name":"salt","shortname":":salt:","category":4,"emoji_order":2576},{"name":"canned food","shortname":":canned_food:","category":4,"emoji_order":2577},{"name":"bento box","shortname":":bento:","category":4,"emoji_order":2578},{"name":"rice cracker","shortname":":rice_cracker:","category":4,"emoji_order":2579},{"name":"rice ball","shortname":":rice_ball:","category":4,"emoji_order":2580},{"name":"cooked rice","shortname":":rice:","category":4,"emoji_order":2581},{"name":"curry rice","shortname":":curry:","category":4,"emoji_order":2582},{"name":"steaming bowl","shortname":":ramen:","category":4,"emoji_order":2583},{"name":"spaghetti","shortname":":spaghetti:","category":4,"emoji_order":2584},{"name":"roasted sweet potato","shortname":":sweet_potato:","category":4,"emoji_order":2585},{"name":"oden","shortname":":oden:","category":4,"emoji_order":2586},{"name":"sushi","shortname":":sushi:","category":4,"emoji_order":2587},{"name":"fried shrimp","shortname":":fried_shrimp:","category":4,"emoji_order":2588},{"name":"fish cake with swirl","shortname":":fish_cake:","category":4,"emoji_order":2589},{"name":"moon cake","shortname":":moon_cake:","category":4,"emoji_order":2590},{"name":"dango","shortname":":dango:","category":4,"emoji_order":2591},{"name":"dumpling","shortname":":dumpling:","category":4,"emoji_order":2592},{"name":"fortune cookie","shortname":":fortune_cookie:","category":4,"emoji_order":2593},{"name":"takeout box","shortname":":takeout_box:","category":4,"emoji_order":2594},{"name":"crab","shortname":":crab:","category":4,"emoji_order":2595},{"name":"lobster","shortname":":lobster:","category":4,"emoji_order":2596},{"name":"shrimp","shortname":":shrimp:","category":4,"emoji_order":2597},{"name":"squid","shortname":":squid:","category":4,"emoji_order":2598},{"name":"oyster","shortname":":oyster:","category":4,"emoji_order":2599},{"name":"soft ice cream","shortname":":icecream:","category":4,"emoji_order":2600},{"name":"shaved ice","shortname":":shaved_ice:","category":4,"emoji_order":2601},{"name":"ice cream","shortname":":ice_cream:","category":4,"emoji_order":2602},{"name":"doughnut","shortname":":doughnut:","category":4,"emoji_order":2603},{"name":"cookie","shortname":":cookie:","category":4,"emoji_order":2604},{"name":"birthday cake","shortname":":birthday:","category":4,"emoji_order":2605},{"name":"shortcake","shortname":":cake:","category":4,"emoji_order":2606},{"name":"cupcake","shortname":":cupcake:","category":4,"emoji_order":2607},{"name":"pie","shortname":":pie:","category":4,"emoji_order":2608},{"name":"chocolate bar","shortname":":chocolate_bar:","category":4,"emoji_order":2609},{"name":"candy","shortname":":candy:","category":4,"emoji_order":2610},{"name":"lollipop","shortname":":lollipop:","category":4,"emoji_order":2611},{"name":"custard","shortname":":custard:","category":4,"emoji_order":2612},{"name":"honey pot","shortname":":honey_pot:","category":4,"emoji_order":2613},{"name":"baby bottle","shortname":":baby_bottle:","category":4,"emoji_order":2614},{"name":"glass of milk","shortname":":milk:","category":4,"emoji_order":2615},{"name":"hot beverage","shortname":":coffee:","category":4,"emoji_order":2616},{"name":"teacup without handle","shortname":":tea:","category":4,"emoji_order":2617},{"name":"sake","shortname":":sake:","category":4,"emoji_order":2618},{"name":"bottle with popping cork","shortname":":champagne:","category":4,"emoji_order":2619},{"name":"wine glass","shortname":":wine_glass:","category":4,"emoji_order":2620},{"name":"cocktail glass","shortname":":cocktail:","category":4,"emoji_order":2621},{"name":"tropical drink","shortname":":tropical_drink:","category":4,"emoji_order":2622},{"name":"beer mug","shortname":":beer:","category":4,"emoji_order":2623},{"name":"clinking beer mugs","shortname":":beers:","category":4,"emoji_order":2624},{"name":"clinking glasses","shortname":":champagne_glass:","category":4,"emoji_order":2625},{"name":"tumbler glass","shortname":":tumbler_glass:","category":4,"emoji_order":2626},{"name":"cup with straw","shortname":":cup_straw:","category":4,"emoji_order":2627},{"name":"beverage box","shortname":":beverage_box:","category":4,"emoji_order":2628,"aliases":[":juice_box:"]},{"name":"mate","shortname":":mate:","category":4,"emoji_order":2629,"aliases":[":yerba_mate:"]},{"name":"ice","shortname":":ice:","category":4,"emoji_order":2630},{"name":"chopsticks","shortname":":chopsticks:","category":4,"emoji_order":2631},{"name":"fork and knife with plate","shortname":":fork_knife_plate:","category":4,"emoji_order":2633},{"name":"fork and knife","shortname":":utensils:","category":4,"emoji_order":2634},{"name":"spoon","shortname":":spoon:","category":4,"emoji_order":2635},{"name":"kitchen knife","shortname":":knife:","category":4,"emoji_order":2636},{"name":"amphora","shortname":":amphora:","category":4,"emoji_order":2637},{"name":"globe showing Europe-Africa","shortname":":earth_africa:","category":5,"emoji_order":2638},{"name":"globe showing Americas","shortname":":earth_americas:","category":5,"emoji_order":2639},{"name":"globe showing Asia-Australia","shortname":":earth_asia:","category":5,"emoji_order":2640},{"name":"globe with meridians","shortname":":globe:","category":5,"emoji_order":2641},{"name":"world map","shortname":":map:","category":5,"emoji_order":2643},{"name":"map of Japan","shortname":":japan:","category":5,"emoji_order":2644},{"name":"compass","shortname":":compass:","category":5,"emoji_order":2645},{"name":"snow-capped mountain","shortname":":snowy_mountain:","category":5,"emoji_order":2647},{"name":"mountain","shortname":":mountain:","category":5,"emoji_order":2649},{"name":"volcano","shortname":":volcano:","category":5,"emoji_order":2650},{"name":"mount fuji","shortname":":mount_fuji:","category":5,"emoji_order":2651},{"name":"camping","shortname":":camping:","category":5,"emoji_order":2653},{"name":"beach with umbrella","shortname":":beach:","category":5,"emoji_order":2655},{"name":"desert","shortname":":desert:","category":5,"emoji_order":2657},{"name":"desert island","shortname":":island:","category":5,"emoji_order":2659},{"name":"national park","shortname":":park:","category":5,"emoji_order":2661},{"name":"stadium","shortname":":stadium:","category":5,"emoji_order":2663},{"name":"classical building","shortname":":classical_building:","category":5,"emoji_order":2665},{"name":"building construction","shortname":":construction_site:","category":5,"emoji_order":2667},{"name":"brick","shortname":":brick:","category":5,"emoji_order":2668},{"name":"houses","shortname":":homes:","category":5,"emoji_order":2670},{"name":"derelict house","shortname":":house_abandoned:","category":5,"emoji_order":2672},{"name":"house","shortname":":house:","category":5,"emoji_order":2673},{"name":"house with garden","shortname":":house_garden:","category":5,"emoji_order":2674},{"name":"office building","shortname":":office:","category":5,"emoji_order":2675},{"name":"Japanese post office","shortname":":ja_post_office:","category":5,"emoji_order":2676},{"name":"post office","shortname":":post_office:","category":5,"emoji_order":2677},{"name":"hospital","shortname":":hospital:","category":5,"emoji_order":2678},{"name":"bank","shortname":":bank:","category":5,"emoji_order":2679},{"name":"hotel","shortname":":hotel:","category":5,"emoji_order":2680},{"name":"love hotel","shortname":":love_hotel:","category":5,"emoji_order":2681},{"name":"convenience store","shortname":":convenience_store:","category":5,"emoji_order":2682},{"name":"school","shortname":":school:","category":5,"emoji_order":2683},{"name":"department store","shortname":":department_store:","category":5,"emoji_order":2684},{"name":"factory","shortname":":factory:","category":5,"emoji_order":2685},{"name":"Japanese castle","shortname":":japanese_castle:","category":5,"emoji_order":2686},{"name":"castle","shortname":":castle:","category":5,"emoji_order":2687,"aliases":[":european_castle:"]},{"name":"wedding","shortname":":wedding:","category":5,"emoji_order":2688},{"name":"Tokyo tower","shortname":":tokyo_tower:","category":5,"emoji_order":2689},{"name":"Statue of Liberty","shortname":":statue_of_liberty:","category":5,"emoji_order":2690},{"name":"church","shortname":":church:","category":5,"emoji_order":2691},{"name":"mosque","shortname":":mosque:","category":5,"emoji_order":2692},{"name":"hindu temple","shortname":":hindu_temple:","category":5,"emoji_order":2693},{"name":"synagogue","shortname":":synagogue:","category":5,"emoji_order":2694},{"name":"shinto shrine","shortname":":shinto_shrine:","category":5,"emoji_order":2696},{"name":"kaaba","shortname":":kaaba:","category":5,"emoji_order":2697},{"name":"fountain","shortname":":fountain:","category":5,"emoji_order":2698},{"name":"tent","shortname":":tent:","category":5,"emoji_order":2699},{"name":"foggy","shortname":":foggy:","category":5,"emoji_order":2700},{"name":"night with stars","shortname":":night_stars:","category":5,"emoji_order":2701},{"name":"cityscape","shortname":":cityscape:","category":5,"emoji_order":2703},{"name":"sunrise over mountains","shortname":":sunrise_over_mountains:","category":5,"emoji_order":2704},{"name":"sunrise","shortname":":sunrise:","category":5,"emoji_order":2705},{"name":"cityscape at dusk","shortname":":dusk:","category":5,"emoji_order":2706},{"name":"sunset","shortname":":sunset:","category":5,"emoji_order":2707},{"name":"bridge at night","shortname":":bridge_at_night:","category":5,"emoji_order":2708},{"name":"hot springs","shortname":":hotsprings:","category":5,"emoji_order":2710},{"name":"carousel horse","shortname":":carousel_horse:","category":5,"emoji_order":2711},{"name":"ferris wheel","shortname":":ferris_wheel:","category":5,"emoji_order":2712},{"name":"roller coaster","shortname":":roller_coaster:","category":5,"emoji_order":2713},{"name":"barber pole","shortname":":barber:","category":5,"emoji_order":2714},{"name":"circus tent","shortname":":circus_tent:","category":5,"emoji_order":2715},{"name":"locomotive","shortname":":steam_locomotive:","category":5,"emoji_order":2716},{"name":"railway car","shortname":":railway_car:","category":5,"emoji_order":2717},{"name":"high-speed train","shortname":":bullettrain_side:","category":5,"emoji_order":2718},{"name":"bullet train","shortname":":bullettrain:","category":5,"emoji_order":2719},{"name":"train","shortname":":train:","category":5,"emoji_order":2720},{"name":"metro","shortname":":metro:","category":5,"emoji_order":2721},{"name":"light rail","shortname":":light_rail:","category":5,"emoji_order":2722},{"name":"station","shortname":":station:","category":5,"emoji_order":2723},{"name":"tram","shortname":":tram:","category":5,"emoji_order":2724},{"name":"monorail","shortname":":monorail:","category":5,"emoji_order":2725},{"name":"mountain railway","shortname":":mountain_railway:","category":5,"emoji_order":2726},{"name":"tram car","shortname":":tram_car:","category":5,"emoji_order":2727},{"name":"bus","shortname":":bus:","category":5,"emoji_order":2728},{"name":"oncoming bus","shortname":":oncoming_bus:","category":5,"emoji_order":2729},{"name":"trolleybus","shortname":":trolleybus:","category":5,"emoji_order":2730},{"name":"minibus","shortname":":minibus:","category":5,"emoji_order":2731},{"name":"ambulance","shortname":":ambulance:","category":5,"emoji_order":2732},{"name":"fire engine","shortname":":fire_engine:","category":5,"emoji_order":2733},{"name":"police car","shortname":":police_car:","category":5,"emoji_order":2734},{"name":"oncoming police car","shortname":":oncoming_police_car:","category":5,"emoji_order":2735},{"name":"taxi","shortname":":taxi:","category":5,"emoji_order":2736},{"name":"oncoming taxi","shortname":":oncoming_taxi:","category":5,"emoji_order":2737},{"name":"automobile","shortname":":red_car:","category":5,"emoji_order":2738},{"name":"oncoming automobile","shortname":":oncoming_automobile:","category":5,"emoji_order":2739},{"name":"sport utility vehicle","shortname":":blue_car:","category":5,"emoji_order":2740},{"name":"delivery truck","shortname":":truck:","category":5,"emoji_order":2741},{"name":"articulated lorry","shortname":":lorry:","category":5,"emoji_order":2742},{"name":"tractor","shortname":":tractor:","category":5,"emoji_order":2743},{"name":"racing car","shortname":":race_car:","category":5,"emoji_order":2745},{"name":"motorcycle","shortname":":motorcycle:","category":5,"emoji_order":2747},{"name":"motor scooter","shortname":":motor_scooter:","category":5,"emoji_order":2748},{"name":"manual wheelchair","shortname":":wheelchair:","category":5,"emoji_order":2749},{"name":"motorized wheelchair","shortname":":motor_wheelchair:","category":5,"emoji_order":2750},{"name":"auto rickshaw","shortname":":auto_rickshaw:","category":5,"emoji_order":2751},{"name":"bicycle","shortname":":bike:","category":5,"emoji_order":2752},{"name":"kick scooter","shortname":":scooter:","category":5,"emoji_order":2753},{"name":"skateboard","shortname":":skateboard:","category":5,"emoji_order":2754},{"name":"bus stop","shortname":":bus_stop:","category":5,"emoji_order":2755},{"name":"motorway","shortname":":motorway:","category":5,"emoji_order":2757},{"name":"railway track","shortname":":railway_track:","category":5,"emoji_order":2759},{"name":"oil drum","shortname":":oil_drum:","category":5,"emoji_order":2761},{"name":"fuel pump","shortname":":fuel_pump:","category":5,"emoji_order":2762},{"name":"police car light","shortname":":rotating_light:","category":5,"emoji_order":2763,"aliases":[":police_light:"]},{"name":"horizontal traffic light","shortname":":traffic_light:","category":5,"emoji_order":2764},{"name":"vertical traffic light","shortname":":vertical_traffic_light:","category":5,"emoji_order":2765},{"name":"stop sign","shortname":":stop_sign:","category":5,"emoji_order":2766,"aliases":[":octagonal_sign:"]},{"name":"construction","shortname":":construction:","category":5,"emoji_order":2767},{"name":"anchor","shortname":":anchor:","category":5,"emoji_order":2768},{"name":"sailboat","shortname":":sailboat:","category":5,"emoji_order":2769},{"name":"canoe","shortname":":canoe:","category":5,"emoji_order":2770},{"name":"speedboat","shortname":":speedboat:","category":5,"emoji_order":2771},{"name":"passenger ship","shortname":":cruise_ship:","category":5,"emoji_order":2773},{"name":"ferry","shortname":":ferry:","category":5,"emoji_order":2775},{"name":"motor boat","shortname":":motorboat:","category":5,"emoji_order":2777},{"name":"ship","shortname":":ship:","category":5,"emoji_order":2778},{"name":"airplane","shortname":":airplane:","category":5,"emoji_order":2780},{"name":"small airplane","shortname":":small_airplane:","category":5,"emoji_order":2782},{"name":"airplane departure","shortname":":airplane_departure:","category":5,"emoji_order":2783},{"name":"airplane arrival","shortname":":airplane_arriving:","category":5,"emoji_order":2784},{"name":"parachute","shortname":":parachute:","category":5,"emoji_order":2785},{"name":"seat","shortname":":seat:","category":5,"emoji_order":2786},{"name":"helicopter","shortname":":helicopter:","category":5,"emoji_order":2787},{"name":"suspension railway","shortname":":suspension_railway:","category":5,"emoji_order":2788},{"name":"mountain cableway","shortname":":mountain_cableway:","category":5,"emoji_order":2789},{"name":"aerial tramway","shortname":":aerial_tramway:","category":5,"emoji_order":2790},{"name":"satellite","shortname":":satellite:","category":5,"emoji_order":2792},{"name":"rocket","shortname":":rocket:","category":5,"emoji_order":2793},{"name":"flying saucer","shortname":":flying_saucer:","category":5,"emoji_order":2794},{"name":"bellhop bell","shortname":":bellhop:","category":5,"emoji_order":2796},{"name":"luggage","shortname":":luggage:","category":5,"emoji_order":2797},{"name":"hourglass done","shortname":":hourglass:","category":5,"emoji_order":2798},{"name":"hourglass not done","shortname":":hourglass_flowing:","category":5,"emoji_order":2799},{"name":"watch","shortname":":watch:","category":5,"emoji_order":2800},{"name":"alarm clock","shortname":":alarm_clock:","category":5,"emoji_order":2801},{"name":"stopwatch","shortname":":stopwatch:","category":5,"emoji_order":2803},{"name":"timer clock","shortname":":timer:","category":5,"emoji_order":2805},{"name":"mantelpiece clock","shortname":":clock:","category":5,"emoji_order":2807},{"name":"twelve o’clock","shortname":":clock12:","category":5,"emoji_order":2808},{"name":"twelve-thirty","shortname":":clock1230:","category":5,"emoji_order":2809},{"name":"one o’clock","shortname":":clock1:","category":5,"emoji_order":2810},{"name":"one-thirty","shortname":":clock130:","category":5,"emoji_order":2811},{"name":"two o’clock","shortname":":clock2:","category":5,"emoji_order":2812},{"name":"two-thirty","shortname":":clock230:","category":5,"emoji_order":2813},{"name":"three o’clock","shortname":":clock3:","category":5,"emoji_order":2814},{"name":"three-thirty","shortname":":clock330:","category":5,"emoji_order":2815},{"name":"four o’clock","shortname":":clock4:","category":5,"emoji_order":2816},{"name":"four-thirty","shortname":":clock430:","category":5,"emoji_order":2817},{"name":"five o’clock","shortname":":clock5:","category":5,"emoji_order":2818},{"name":"five-thirty","shortname":":clock530:","category":5,"emoji_order":2819},{"name":"six o’clock","shortname":":clock6:","category":5,"emoji_order":2820},{"name":"six-thirty","shortname":":clock630:","category":5,"emoji_order":2821},{"name":"seven o’clock","shortname":":clock7:","category":5,"emoji_order":2822},{"name":"seven-thirty","shortname":":clock730:","category":5,"emoji_order":2823},{"name":"eight o’clock","shortname":":clock8:","category":5,"emoji_order":2824},{"name":"eight-thirty","shortname":":clock830:","category":5,"emoji_order":2825},{"name":"nine o’clock","shortname":":clock9:","category":5,"emoji_order":2826},{"name":"nine-thirty","shortname":":clock930:","category":5,"emoji_order":2827},{"name":"ten o’clock","shortname":":clock10:","category":5,"emoji_order":2828},{"name":"ten-thirty","shortname":":clock1030:","category":5,"emoji_order":2829},{"name":"eleven o’clock","shortname":":clock11:","category":5,"emoji_order":2830},{"name":"eleven-thirty","shortname":":clock1130:","category":5,"emoji_order":2831},{"name":"new moon","shortname":":new_moon:","category":5,"emoji_order":2832},{"name":"waxing crescent moon","shortname":":waxing_crescent_moon:","category":5,"emoji_order":2833},{"name":"first quarter moon","shortname":":first_quarter_moon:","category":5,"emoji_order":2834},{"name":"waxing gibbous moon","shortname":":waxing_gibbous_moon:","category":5,"emoji_order":2835},{"name":"full moon","shortname":":full_moon:","category":5,"emoji_order":2836},{"name":"waning gibbous moon","shortname":":waning_gibbous_moon:","category":5,"emoji_order":2837},{"name":"last quarter moon","shortname":":last_quarter_moon:","category":5,"emoji_order":2838},{"name":"waning crescent moon","shortname":":waning_crescent_moon:","category":5,"emoji_order":2839},{"name":"crescent moon","shortname":":crescent_moon:","category":5,"emoji_order":2840},{"name":"new moon face","shortname":":new_moon_face:","category":5,"emoji_order":2841},{"name":"first quarter moon face","shortname":":first_quarter_moon_face:","category":5,"emoji_order":2842},{"name":"last quarter moon face","shortname":":last_quarter_moon_face:","category":5,"emoji_order":2843},{"name":"thermometer","shortname":":thermometer:","category":5,"emoji_order":2845},{"name":"sun","shortname":":sun:","category":5,"emoji_order":2847},{"name":"full moon face","shortname":":full_moon_face:","category":5,"emoji_order":2848},{"name":"sun with face","shortname":":sun_face:","category":5,"emoji_order":2849},{"name":"ringed planet","shortname":":ringed_planet:","category":5,"emoji_order":2850,"aliases":[":saturn:"]},{"name":"star","shortname":":star:","category":5,"emoji_order":2851},{"name":"glowing star","shortname":":star2:","category":5,"emoji_order":2852,"aliases":[":glowing_star:"]},{"name":"shooting star","shortname":":star3:","category":5,"emoji_order":2853,"aliases":[":shooting_star:"]},{"name":"milky way","shortname":":milky_way:","category":5,"emoji_order":2854},{"name":"cloud","shortname":":cloud:","category":5,"emoji_order":2856},{"name":"sun behind cloud","shortname":":partly_sunny:","category":5,"emoji_order":2857},{"name":"cloud with lightning and rain","shortname":":storm:","category":5,"emoji_order":2859},{"name":"sun behind small cloud","shortname":":overcast:","category":5,"emoji_order":2861},{"name":"sun behind large cloud","shortname":":cloudy:","category":5,"emoji_order":2863},{"name":"sun behind rain cloud","shortname":":sunshower:","category":5,"emoji_order":2865},{"name":"cloud with rain","shortname":":rain:","category":5,"emoji_order":2867},{"name":"cloud with snow","shortname":":snow:","category":5,"emoji_order":2869},{"name":"cloud with lightning","shortname":":lightning:","category":5,"emoji_order":2871},{"name":"tornado","shortname":":tornado:","category":5,"emoji_order":2873},{"name":"fog","shortname":":fog:","category":5,"emoji_order":2875},{"name":"wind face","shortname":":wind_face:","category":5,"emoji_order":2877},{"name":"cyclone","shortname":":cyclone:","category":5,"emoji_order":2878},{"name":"rainbow","shortname":":rainbow:","category":5,"emoji_order":2879},{"name":"closed umbrella","shortname":":closed_umbrella:","category":5,"emoji_order":2880},{"name":"umbrella","shortname":":umbrella:","category":5,"emoji_order":2882},{"name":"umbrella with rain drops","shortname":":umbrella_rain:","category":5,"emoji_order":2883},{"name":"umbrella on ground","shortname":":beach_umbrella:","category":5,"emoji_order":2885},{"name":"high voltage","shortname":":zap:","category":5,"emoji_order":2886,"aliases":[":high_voltage:"]},{"name":"snowflake","shortname":":snowflake:","category":5,"emoji_order":2888},{"name":"snowman","shortname":":snowy_snowman:","category":5,"emoji_order":2890},{"name":"snowman without snow","shortname":":snowman:","category":5,"emoji_order":2891},{"name":"comet","shortname":":comet:","category":5,"emoji_order":2893},{"name":"fire","shortname":":fire:","category":5,"emoji_order":2894},{"name":"droplet","shortname":":droplet:","category":5,"emoji_order":2895},{"name":"water wave","shortname":":ocean:","category":5,"emoji_order":2896},{"name":"jack-o-lantern","shortname":":jack_o_lantern:","category":6,"emoji_order":2897},{"name":"Christmas tree","shortname":":christmas_tree:","category":6,"emoji_order":2898,"aliases":[":xmas_tree:"]},{"name":"fireworks","shortname":":fireworks:","category":6,"emoji_order":2899},{"name":"sparkler","shortname":":sparkler:","category":6,"emoji_order":2900},{"name":"firecracker","shortname":":firecracker:","category":6,"emoji_order":2901},{"name":"sparkles","shortname":":sparkles:","category":6,"emoji_order":2902},{"name":"balloon","shortname":":balloon:","category":6,"emoji_order":2903},{"name":"party popper","shortname":":tada:","category":6,"emoji_order":2904,"aliases":[":party:"]},{"name":"confetti ball","shortname":":confetti_ball:","category":6,"emoji_order":2905},{"name":"tanabata tree","shortname":":tanabata_tree:","category":6,"emoji_order":2906},{"name":"pine decoration","shortname":":bamboo:","category":6,"emoji_order":2907,"aliases":[":pine_decor:"]},{"name":"Japanese dolls","shortname":":dolls:","category":6,"emoji_order":2908},{"name":"carp streamer","shortname":":carp_streamer:","category":6,"emoji_order":2909},{"name":"wind chime","shortname":":wind_chime:","category":6,"emoji_order":2910},{"name":"moon viewing ceremony","shortname":":moon_ceremony:","category":6,"emoji_order":2911,"aliases":[":rice_scene:"]},{"name":"red envelope","shortname":":red_envelope:","category":6,"emoji_order":2912},{"name":"ribbon","shortname":":ribbon:","category":6,"emoji_order":2913},{"name":"wrapped gift","shortname":":gift:","category":6,"emoji_order":2914},{"name":"reminder ribbon","shortname":":reminder_ribbon:","category":6,"emoji_order":2916},{"name":"admission tickets","shortname":":tickets:","category":6,"emoji_order":2918,"aliases":[":admission:"]},{"name":"ticket","shortname":":ticket:","category":6,"emoji_order":2919},{"name":"military medal","shortname":":military_medal:","category":6,"emoji_order":2921},{"name":"trophy","shortname":":trophy:","category":6,"emoji_order":2922},{"name":"sports medal","shortname":":medal:","category":6,"emoji_order":2923},{"name":"1st place medal","shortname":":first_place:","category":6,"emoji_order":2924},{"name":"2nd place medal","shortname":":second_place:","category":6,"emoji_order":2925},{"name":"3rd place medal","shortname":":third_place:","category":6,"emoji_order":2926},{"name":"soccer ball","shortname":":soccer:","category":6,"emoji_order":2927},{"name":"baseball","shortname":":baseball:","category":6,"emoji_order":2928},{"name":"softball","shortname":":softball:","category":6,"emoji_order":2929},{"name":"basketball","shortname":":basketball:","category":6,"emoji_order":2930},{"name":"volleyball","shortname":":volleyball:","category":6,"emoji_order":2931},{"name":"american football","shortname":":football:","category":6,"emoji_order":2932},{"name":"rugby football","shortname":":rugby:","category":6,"emoji_order":2933},{"name":"tennis","shortname":":tennis:","category":6,"emoji_order":2934},{"name":"flying disc","shortname":":flying_disc:","category":6,"emoji_order":2935},{"name":"bowling","shortname":":bowling:","category":6,"emoji_order":2936},{"name":"cricket game","shortname":":cricket_game:","category":6,"emoji_order":2937},{"name":"field hockey","shortname":":field_hockey:","category":6,"emoji_order":2938},{"name":"ice hockey","shortname":":hockey:","category":6,"emoji_order":2939},{"name":"lacrosse","shortname":":lacrosse:","category":6,"emoji_order":2940},{"name":"ping pong","shortname":":ping_pong:","category":6,"emoji_order":2941},{"name":"badminton","shortname":":badminton:","category":6,"emoji_order":2942},{"name":"boxing glove","shortname":":boxing_glove:","category":6,"emoji_order":2943},{"name":"martial arts uniform","shortname":":gi:","category":6,"emoji_order":2944,"aliases":[":martial_arts_uniform:"]},{"name":"goal net","shortname":":goal:","category":6,"emoji_order":2945},{"name":"flag in hole","shortname":":golf:","category":6,"emoji_order":2946},{"name":"ice skate","shortname":":ice_skate:","category":6,"emoji_order":2948},{"name":"fishing pole","shortname":":fishing_pole:","category":6,"emoji_order":2949},{"name":"diving mask","shortname":":diving_mask:","category":6,"emoji_order":2950,"aliases":[":scuba_mask:"]},{"name":"running shirt","shortname":":running_shirt:","category":6,"emoji_order":2951},{"name":"skis","shortname":":ski:","category":6,"emoji_order":2952},{"name":"sled","shortname":":sled:","category":6,"emoji_order":2953},{"name":"curling stone","shortname":":curling_stone:","category":6,"emoji_order":2954},{"name":"direct hit","shortname":":dart:","category":6,"emoji_order":2955},{"name":"yo-yo","shortname":":yoyo:","category":6,"emoji_order":2956},{"name":"kite","shortname":":kite:","category":6,"emoji_order":2957},{"name":"pool 8 ball","shortname":":8ball:","category":6,"emoji_order":2958},{"name":"crystal ball","shortname":":crystal_ball:","category":6,"emoji_order":2959},{"name":"nazar amulet","shortname":":nazar_amulet:","category":6,"emoji_order":2960},{"name":"video game","shortname":":video_game:","category":6,"emoji_order":2961},{"name":"joystick","shortname":":joystick:","category":6,"emoji_order":2963},{"name":"slot machine","shortname":":slot_machine:","category":6,"emoji_order":2964},{"name":"game die","shortname":":game_die:","category":6,"emoji_order":2965},{"name":"puzzle piece","shortname":":jigsaw:","category":6,"emoji_order":2966,"aliases":[":puzzle_piece:"]},{"name":"teddy bear","shortname":":teddy_bear:","category":6,"emoji_order":2967},{"name":"spade suit","shortname":":spades:","category":6,"emoji_order":2969},{"name":"heart suit","shortname":":hearts:","category":6,"emoji_order":2971},{"name":"diamond suit","shortname":":diamonds:","category":6,"emoji_order":2973},{"name":"club suit","shortname":":clubs:","category":6,"emoji_order":2975},{"name":"chess pawn","shortname":":chess_pawn:","category":6,"emoji_order":2977},{"name":"joker","shortname":":black_joker:","category":6,"emoji_order":2978},{"name":"mahjong red dragon","shortname":":mahjong:","category":6,"emoji_order":2979},{"name":"flower playing cards","shortname":":flower_cards:","category":6,"emoji_order":2980},{"name":"performing arts","shortname":":performing_arts:","category":6,"emoji_order":2981},{"name":"framed picture","shortname":":frame_photo:","category":6,"emoji_order":2983},{"name":"artist palette","shortname":":art:","category":6,"emoji_order":2984,"aliases":[":palette:"]},{"name":"thread","shortname":":spool:","category":6,"emoji_order":2985},{"name":"yarn","shortname":":yarn:","category":6,"emoji_order":2986},{"name":"glasses","shortname":":glasses:","category":7,"emoji_order":2987},{"name":"sunglasses","shortname":":sunglasses:","category":7,"emoji_order":2989},{"name":"goggles","shortname":":goggles:","category":7,"emoji_order":2990},{"name":"lab coat","shortname":":lab_coat:","category":7,"emoji_order":2991},{"name":"safety vest","shortname":":safety_vest:","category":7,"emoji_order":2992},{"name":"necktie","shortname":":necktie:","category":7,"emoji_order":2993,"aliases":[":tie:"]},{"name":"t-shirt","shortname":":shirt:","category":7,"emoji_order":2994},{"name":"jeans","shortname":":jeans:","category":7,"emoji_order":2995},{"name":"scarf","shortname":":scarf:","category":7,"emoji_order":2996},{"name":"gloves","shortname":":gloves:","category":7,"emoji_order":2997},{"name":"coat","shortname":":coat:","category":7,"emoji_order":2998},{"name":"socks","shortname":":socks:","category":7,"emoji_order":2999},{"name":"dress","shortname":":dress:","category":7,"emoji_order":3000},{"name":"kimono","shortname":":kimono:","category":7,"emoji_order":3001},{"name":"sari","shortname":":sari:","category":7,"emoji_order":3002},{"name":"one-piece swimsuit","shortname":":one_piece_swimsuit:","category":7,"emoji_order":3003},{"name":"briefs","shortname":":briefs:","category":7,"emoji_order":3004},{"name":"shorts","shortname":":shorts:","category":7,"emoji_order":3005},{"name":"bikini","shortname":":bikini:","category":7,"emoji_order":3006},{"name":"woman’s clothes","shortname":":blouse:","category":7,"emoji_order":3007,"aliases":[":womans_clothes:"]},{"name":"purse","shortname":":purse:","category":7,"emoji_order":3008},{"name":"handbag","shortname":":handbag:","category":7,"emoji_order":3009},{"name":"clutch bag","shortname":":pouch:","category":7,"emoji_order":3010,"aliases":[":clutch_bag:"]},{"name":"shopping bags","shortname":":shopping_bags:","category":7,"emoji_order":3012},{"name":"backpack","shortname":":backpack:","category":7,"emoji_order":3013},{"name":"man’s shoe","shortname":":dress_shoe:","category":7,"emoji_order":3014,"aliases":[":mans_shoe:"]},{"name":"running shoe","shortname":":sneaker:","category":7,"emoji_order":3015,"aliases":[":athletic_shoe:"]},{"name":"hiking boot","shortname":":hiking_boot:","category":7,"emoji_order":3016},{"name":"flat shoe","shortname":":flat_shoe:","category":7,"emoji_order":3017},{"name":"high-heeled shoe","shortname":":high_heel:","category":7,"emoji_order":3018},{"name":"woman’s sandal","shortname":":womans_sandal:","category":7,"emoji_order":3019},{"name":"ballet shoes","shortname":":ballet_shoes:","category":7,"emoji_order":3020},{"name":"woman’s boot","shortname":":womans_boot:","category":7,"emoji_order":3021},{"name":"crown","shortname":":crown:","category":7,"emoji_order":3022},{"name":"woman’s hat","shortname":":womans_hat:","category":7,"emoji_order":3023},{"name":"top hat","shortname":":top_hat:","category":7,"emoji_order":3024},{"name":"graduation cap","shortname":":graduation_cap:","category":7,"emoji_order":3025},{"name":"billed cap","shortname":":billed_cap:","category":7,"emoji_order":3026},{"name":"rescue worker’s helmet","shortname":":helmet_cross:","category":7,"emoji_order":3028},{"name":"prayer beads","shortname":":prayer_beads:","category":7,"emoji_order":3029},{"name":"lipstick","shortname":":lipstick:","category":7,"emoji_order":3030},{"name":"ring","shortname":":ring:","category":7,"emoji_order":3031},{"name":"gem stone","shortname":":gem:","category":7,"emoji_order":3032},{"name":"muted speaker","shortname":":mute:","category":7,"emoji_order":3033,"aliases":[":no_sound:"]},{"name":"speaker low volume","shortname":":speaker:","category":7,"emoji_order":3034,"aliases":[":low_sound:"]},{"name":"speaker medium volume","shortname":":sound:","category":7,"emoji_order":3035},{"name":"speaker high volume","shortname":":loud_sound:","category":7,"emoji_order":3036},{"name":"loudspeaker","shortname":":loudspeaker:","category":7,"emoji_order":3037},{"name":"megaphone","shortname":":megaphone:","category":7,"emoji_order":3038},{"name":"postal horn","shortname":":postal_horn:","category":7,"emoji_order":3039},{"name":"bell","shortname":":bell:","category":7,"emoji_order":3040},{"name":"bell with slash","shortname":":no_bell:","category":7,"emoji_order":3041},{"name":"musical score","shortname":":musical_score:","category":7,"emoji_order":3042},{"name":"musical note","shortname":":musical_note:","category":7,"emoji_order":3043},{"name":"musical notes","shortname":":musical_notes:","category":7,"emoji_order":3044},{"name":"studio microphone","shortname":":studio_microphone:","category":7,"emoji_order":3046},{"name":"level slider","shortname":":level_slider:","category":7,"emoji_order":3048},{"name":"control knobs","shortname":":control_knobs:","category":7,"emoji_order":3050},{"name":"microphone","shortname":":microphone:","category":7,"emoji_order":3051},{"name":"headphone","shortname":":headphones:","category":7,"emoji_order":3052},{"name":"radio","shortname":":radio:","category":7,"emoji_order":3053},{"name":"saxophone","shortname":":saxophone:","category":7,"emoji_order":3054},{"name":"guitar","shortname":":guitar:","category":7,"emoji_order":3055},{"name":"musical keyboard","shortname":":musical_keyboard:","category":7,"emoji_order":3056},{"name":"trumpet","shortname":":trumpet:","category":7,"emoji_order":3057},{"name":"violin","shortname":":violin:","category":7,"emoji_order":3058},{"name":"banjo","shortname":":banjo:","category":7,"emoji_order":3059},{"name":"drum","shortname":":drum:","category":7,"emoji_order":3060},{"name":"mobile phone","shortname":":mobile:","category":7,"emoji_order":3061,"aliases":[":iphone:",":android:"]},{"name":"mobile phone with arrow","shortname":":mobile_calling:","category":7,"emoji_order":3062},{"name":"telephone","shortname":":telephone:","category":7,"emoji_order":3064},{"name":"telephone receiver","shortname":":telephone_receiver:","category":7,"emoji_order":3065},{"name":"pager","shortname":":pager:","category":7,"emoji_order":3066},{"name":"fax machine","shortname":":fax:","category":7,"emoji_order":3067},{"name":"battery","shortname":":battery:","category":7,"emoji_order":3068},{"name":"electric plug","shortname":":electric_plug:","category":7,"emoji_order":3069},{"name":"laptop computer","shortname":":laptop:","category":7,"emoji_order":3070},{"name":"desktop computer","shortname":":desktop:","category":7,"emoji_order":3072,"aliases":[":computer:"]},{"name":"printer","shortname":":printer:","category":7,"emoji_order":3074},{"name":"keyboard","shortname":":keyboard:","category":7,"emoji_order":3076},{"name":"computer mouse","shortname":":computer_mouse:","category":7,"emoji_order":3078},{"name":"trackball","shortname":":trackball:","category":7,"emoji_order":3080},{"name":"computer disk","shortname":":minidisc:","category":7,"emoji_order":3081},{"name":"floppy disk","shortname":":floppy_disk:","category":7,"emoji_order":3082},{"name":"optical disk","shortname":":cd:","category":7,"emoji_order":3083,"aliases":[":disk:"]},{"name":"dvd","shortname":":dvd:","category":7,"emoji_order":3084},{"name":"abacus","shortname":":abacus:","category":7,"emoji_order":3085},{"name":"movie camera","shortname":":movie_camera:","category":7,"emoji_order":3086},{"name":"film frames","shortname":":film_frames:","category":7,"emoji_order":3088},{"name":"film projector","shortname":":projector:","category":7,"emoji_order":3090},{"name":"clapper board","shortname":":clapper:","category":7,"emoji_order":3091},{"name":"television","shortname":":tv:","category":7,"emoji_order":3092},{"name":"camera","shortname":":camera:","category":7,"emoji_order":3093},{"name":"camera with flash","shortname":":camera_flash:","category":7,"emoji_order":3094},{"name":"video camera","shortname":":video_camera:","category":7,"emoji_order":3095},{"name":"videocassette","shortname":":vhs:","category":7,"emoji_order":3096},{"name":"magnifying glass tilted left","shortname":":mag:","category":7,"emoji_order":3097},{"name":"magnifying glass tilted right","shortname":":mag_right:","category":7,"emoji_order":3098},{"name":"candle","shortname":":candle:","category":7,"emoji_order":3100},{"name":"light bulb","shortname":":bulb:","category":7,"emoji_order":3101,"aliases":[":light_bulb:"]},{"name":"flashlight","shortname":":flashlight:","category":7,"emoji_order":3102},{"name":"red paper lantern","shortname":":red_lantern:","category":7,"emoji_order":3103},{"name":"diya lamp","shortname":":diya_lamp:","category":7,"emoji_order":3104},{"name":"notebook with decorative cover","shortname":":decorative_notebook:","category":7,"emoji_order":3105},{"name":"closed book","shortname":":closed_book:","category":7,"emoji_order":3106},{"name":"open book","shortname":":book:","category":7,"emoji_order":3107},{"name":"green book","shortname":":green_book:","category":7,"emoji_order":3108},{"name":"blue book","shortname":":blue_book:","category":7,"emoji_order":3109},{"name":"orange book","shortname":":orange_book:","category":7,"emoji_order":3110},{"name":"books","shortname":":books:","category":7,"emoji_order":3111},{"name":"notebook","shortname":":notebook:","category":7,"emoji_order":3112},{"name":"ledger","shortname":":ledger:","category":7,"emoji_order":3113},{"name":"page with curl","shortname":":page_curl:","category":7,"emoji_order":3114},{"name":"scroll","shortname":":scroll:","category":7,"emoji_order":3115},{"name":"page facing up","shortname":":page_facing_up:","category":7,"emoji_order":3116},{"name":"newspaper","shortname":":newspaper:","category":7,"emoji_order":3117},{"name":"rolled-up newspaper","shortname":":rolled_newspaper:","category":7,"emoji_order":3119},{"name":"bookmark tabs","shortname":":bookmark_tabs:","category":7,"emoji_order":3120},{"name":"bookmark","shortname":":bookmark:","category":7,"emoji_order":3121},{"name":"label","shortname":":label:","category":7,"emoji_order":3123},{"name":"money bag","shortname":":moneybag:","category":7,"emoji_order":3124},{"name":"yen banknote","shortname":":yen:","category":7,"emoji_order":3125},{"name":"dollar banknote","shortname":":dollar:","category":7,"emoji_order":3126},{"name":"euro banknote","shortname":":euro:","category":7,"emoji_order":3127},{"name":"pound banknote","shortname":":pound:","category":7,"emoji_order":3128},{"name":"money with wings","shortname":":money_wings:","category":7,"emoji_order":3129},{"name":"credit card","shortname":":credit_card:","category":7,"emoji_order":3130},{"name":"receipt","shortname":":receipt:","category":7,"emoji_order":3131},{"name":"chart increasing with yen","shortname":":ja_chart:","category":7,"emoji_order":3132},{"name":"currency exchange","shortname":":currency_exchange:","category":7,"emoji_order":3133},{"name":"heavy dollar sign","shortname":":dollar_sign:","category":7,"emoji_order":3134},{"name":"envelope","shortname":":envelope:","category":7,"emoji_order":3136},{"name":"e-mail","shortname":":email:","category":7,"emoji_order":3137},{"name":"incoming envelope","shortname":":incoming_envelope:","category":7,"emoji_order":3138},{"name":"envelope with arrow","shortname":":envelope_arrow:","category":7,"emoji_order":3139},{"name":"outbox tray","shortname":":outbox_tray:","category":7,"emoji_order":3140},{"name":"inbox tray","shortname":":inbox_tray:","category":7,"emoji_order":3141},{"name":"package","shortname":":package:","category":7,"emoji_order":3142},{"name":"closed mailbox with raised flag","shortname":":mailbox:","category":7,"emoji_order":3143},{"name":"closed mailbox with lowered flag","shortname":":mailbox_closed:","category":7,"emoji_order":3144},{"name":"open mailbox with raised flag","shortname":":mailbox_mail:","category":7,"emoji_order":3145},{"name":"open mailbox with lowered flag","shortname":":mailbox_no_mail:","category":7,"emoji_order":3146},{"name":"postbox","shortname":":postbox:","category":7,"emoji_order":3147},{"name":"ballot box with ballot","shortname":":ballot_box:","category":7,"emoji_order":3149},{"name":"pencil","shortname":":pencil:","category":7,"emoji_order":3151},{"name":"black nib","shortname":":black_nib:","category":7,"emoji_order":3153},{"name":"fountain pen","shortname":":fountain_pen:","category":7,"emoji_order":3155},{"name":"pen","shortname":":pen:","category":7,"emoji_order":3157},{"name":"paintbrush","shortname":":paintbrush:","category":7,"emoji_order":3159},{"name":"crayon","shortname":":crayon:","category":7,"emoji_order":3161},{"name":"memo","shortname":":memo:","category":7,"emoji_order":3162},{"name":"briefcase","shortname":":briefcase:","category":7,"emoji_order":3163},{"name":"file folder","shortname":":file_folder:","category":7,"emoji_order":3164},{"name":"open file folder","shortname":":open_file_folder:","category":7,"emoji_order":3165},{"name":"card index dividers","shortname":":dividers:","category":7,"emoji_order":3167},{"name":"calendar","shortname":":date:","category":7,"emoji_order":3168,"aliases":[":calendar:"]},{"name":"tear-off calendar","shortname":":torn_calendar:","category":7,"emoji_order":3169},{"name":"spiral notepad","shortname":":notepad_spiral:","category":7,"emoji_order":3171},{"name":"spiral calendar","shortname":":calendar_spiral:","category":7,"emoji_order":3173},{"name":"card index","shortname":":card_index:","category":7,"emoji_order":3174},{"name":"chart increasing","shortname":":chart_up:","category":7,"emoji_order":3175},{"name":"chart decreasing","shortname":":chart_down:","category":7,"emoji_order":3176},{"name":"bar chart","shortname":":bar_chart:","category":7,"emoji_order":3177},{"name":"clipboard","shortname":":clipboard:","category":7,"emoji_order":3178},{"name":"pushpin","shortname":":pushpin:","category":7,"emoji_order":3179},{"name":"round pushpin","shortname":":round_pushpin:","category":7,"emoji_order":3180},{"name":"paperclip","shortname":":paperclip:","category":7,"emoji_order":3181},{"name":"linked paperclips","shortname":":paperclips:","category":7,"emoji_order":3183},{"name":"straight ruler","shortname":":straight_ruler:","category":7,"emoji_order":3184},{"name":"triangular ruler","shortname":":triangular_ruler:","category":7,"emoji_order":3185},{"name":"scissors","shortname":":scissors:","category":7,"emoji_order":3187},{"name":"card file box","shortname":":card_box:","category":7,"emoji_order":3189},{"name":"file cabinet","shortname":":file_cabinet:","category":7,"emoji_order":3191},{"name":"wastebasket","shortname":":trashcan:","category":7,"emoji_order":3193,"aliases":[":wastebasket:"]},{"name":"locked","shortname":":lock:","category":7,"emoji_order":3194},{"name":"unlocked","shortname":":unlock:","category":7,"emoji_order":3195},{"name":"locked with pen","shortname":":locked_pen:","category":7,"emoji_order":3196},{"name":"locked with key","shortname":":locked_key:","category":7,"emoji_order":3197},{"name":"key","shortname":":key:","category":7,"emoji_order":3198},{"name":"old key","shortname":":old_key:","category":7,"emoji_order":3200},{"name":"hammer","shortname":":hammer:","category":7,"emoji_order":3201},{"name":"axe","shortname":":axe:","category":7,"emoji_order":3202},{"name":"pick","shortname":":pick:","category":7,"emoji_order":3204},{"name":"hammer and pick","shortname":":hammer_pick:","category":7,"emoji_order":3206},{"name":"hammer and wrench","shortname":":tools:","category":7,"emoji_order":3208,"aliases":[":hammer_wrench:"]},{"name":"dagger","shortname":":dagger:","category":7,"emoji_order":3210},{"name":"crossed swords","shortname":":crossed_swords:","category":7,"emoji_order":3212},{"name":"pistol","shortname":":gun:","category":7,"emoji_order":3213,"aliases":[":pistol:"]},{"name":"bow and arrow","shortname":":bow:","category":7,"emoji_order":3214},{"name":"shield","shortname":":shield:","category":7,"emoji_order":3216},{"name":"wrench","shortname":":wrench:","category":7,"emoji_order":3217},{"name":"nut and bolt","shortname":":nut_and_bolt:","category":7,"emoji_order":3218},{"name":"gear","shortname":":gear:","category":7,"emoji_order":3220},{"name":"clamp","shortname":":clamp:","category":7,"emoji_order":3222,"aliases":[":compression:"]},{"name":"balance scale","shortname":":scales:","category":7,"emoji_order":3224},{"name":"probing cane","shortname":":probing_cane:","category":7,"emoji_order":3225},{"name":"link","shortname":":link:","category":7,"emoji_order":3226},{"name":"chains","shortname":":chains:","category":7,"emoji_order":3228},{"name":"toolbox","shortname":":toolbox:","category":7,"emoji_order":3229},{"name":"magnet","shortname":":magnet:","category":7,"emoji_order":3230},{"name":"alembic","shortname":":alembic:","category":7,"emoji_order":3232},{"name":"test tube","shortname":":test_tube:","category":7,"emoji_order":3233},{"name":"petri dish","shortname":":petri_dish:","category":7,"emoji_order":3234},{"name":"dna","shortname":":dna:","category":7,"emoji_order":3235,"aliases":[":double_helix:"]},{"name":"microscope","shortname":":microscope:","category":7,"emoji_order":3236},{"name":"telescope","shortname":":telescope:","category":7,"emoji_order":3237},{"name":"satellite antenna","shortname":":satellite_antenna:","category":7,"emoji_order":3238},{"name":"syringe","shortname":":syringe:","category":7,"emoji_order":3239},{"name":"drop of blood","shortname":":blood_drop:","category":7,"emoji_order":3240},{"name":"pill","shortname":":pill:","category":7,"emoji_order":3241},{"name":"adhesive bandage","shortname":":bandaid:","category":7,"emoji_order":3242,"aliases":[":adhesive_bandage:"]},{"name":"stethoscope","shortname":":stethoscope:","category":7,"emoji_order":3243},{"name":"door","shortname":":door:","category":7,"emoji_order":3244},{"name":"bed","shortname":":bed:","category":7,"emoji_order":3246},{"name":"couch and lamp","shortname":":couch:","category":7,"emoji_order":3248},{"name":"chair","shortname":":chair:","category":7,"emoji_order":3249},{"name":"toilet","shortname":":toilet:","category":7,"emoji_order":3250},{"name":"shower","shortname":":shower:","category":7,"emoji_order":3251},{"name":"bathtub","shortname":":bathtub:","category":7,"emoji_order":3252},{"name":"razor","shortname":":razor:","category":7,"emoji_order":3253},{"name":"lotion bottle","shortname":":lotion:","category":7,"emoji_order":3254},{"name":"safety pin","shortname":":safety_pin:","category":7,"emoji_order":3255},{"name":"broom","shortname":":broom:","category":7,"emoji_order":3256},{"name":"basket","shortname":":basket:","category":7,"emoji_order":3257},{"name":"roll of paper","shortname":":toilet_paper:","category":7,"emoji_order":3258},{"name":"soap","shortname":":soap:","category":7,"emoji_order":3259},{"name":"sponge","shortname":":sponge:","category":7,"emoji_order":3260},{"name":"fire extinguisher","shortname":":fire_extinguisher:","category":7,"emoji_order":3261},{"name":"shopping cart","shortname":":shopping_cart:","category":7,"emoji_order":3262},{"name":"cigarette","shortname":":cigarette:","category":7,"emoji_order":3263,"aliases":[":smoking:"]},{"name":"coffin","shortname":":coffin:","category":7,"emoji_order":3265},{"name":"funeral urn","shortname":":urn:","category":7,"emoji_order":3267},{"name":"moai","shortname":":moai:","category":7,"emoji_order":3268},{"name":"ATM sign","shortname":":atm:","category":8,"emoji_order":3269},{"name":"litter in bin sign","shortname":":litter_bin:","category":8,"emoji_order":3270},{"name":"potable water","shortname":":potable_water:","category":8,"emoji_order":3271},{"name":"wheelchair symbol","shortname":":handicapped:","category":8,"emoji_order":3272},{"name":"men’s room","shortname":":mens:","category":8,"emoji_order":3273},{"name":"women’s room","shortname":":womens:","category":8,"emoji_order":3274},{"name":"restroom","shortname":":restroom:","category":8,"emoji_order":3275,"aliases":[":bathroom:"]},{"name":"baby symbol","shortname":":baby_symbol:","category":8,"emoji_order":3276},{"name":"water closet","shortname":":wc:","category":8,"emoji_order":3277},{"name":"passport control","shortname":":passport_control:","category":8,"emoji_order":3278},{"name":"customs","shortname":":customs:","category":8,"emoji_order":3279},{"name":"baggage claim","shortname":":baggage_claim:","category":8,"emoji_order":3280},{"name":"left luggage","shortname":":left_luggage:","category":8,"emoji_order":3281},{"name":"warning","shortname":":warning:","category":8,"emoji_order":3283},{"name":"children crossing","shortname":":children_crossing:","category":8,"emoji_order":3284},{"name":"no entry","shortname":":no_entry:","category":8,"emoji_order":3285},{"name":"prohibited","shortname":":no_entry_sign:","category":8,"emoji_order":3286},{"name":"no bicycles","shortname":":no_bicycles:","category":8,"emoji_order":3287},{"name":"no smoking","shortname":":no_smoking:","category":8,"emoji_order":3288},{"name":"no littering","shortname":":do_not_litter:","category":8,"emoji_order":3289},{"name":"non-potable water","shortname":":non_potable_water:","category":8,"emoji_order":3290},{"name":"no pedestrians","shortname":":no_pedestrians:","category":8,"emoji_order":3291},{"name":"no mobile phones","shortname":":no_mobile_phones:","category":8,"emoji_order":3292},{"name":"no one under eighteen","shortname":":underage:","category":8,"emoji_order":3293},{"name":"radioactive","shortname":":radioactive:","category":8,"emoji_order":3295},{"name":"biohazard","shortname":":biohazard:","category":8,"emoji_order":3297},{"name":"up arrow","shortname":":arrow_up:","category":8,"emoji_order":3299},{"name":"up-right arrow","shortname":":arrow_upper_right:","category":8,"emoji_order":3301},{"name":"right arrow","shortname":":arrow_right:","category":8,"emoji_order":3303},{"name":"down-right arrow","shortname":":arrow_lower_right:","category":8,"emoji_order":3305},{"name":"down arrow","shortname":":arrow_down:","category":8,"emoji_order":3307},{"name":"down-left arrow","shortname":":arrow_lower_left:","category":8,"emoji_order":3309},{"name":"left arrow","shortname":":arrow_left:","category":8,"emoji_order":3311},{"name":"up-left arrow","shortname":":arrow_upper_left:","category":8,"emoji_order":3313},{"name":"up-down arrow","shortname":":arrow_up_down:","category":8,"emoji_order":3315},{"name":"left-right arrow","shortname":":arrow_left_right:","category":8,"emoji_order":3317},{"name":"right arrow curving left","shortname":":arrow_left_hook:","category":8,"emoji_order":3319},{"name":"left arrow curving right","shortname":":arrow_right_hook:","category":8,"emoji_order":3321},{"name":"right arrow curving up","shortname":":arrow_heading_up:","category":8,"emoji_order":3323},{"name":"right arrow curving down","shortname":":arrow_heading_down:","category":8,"emoji_order":3325},{"name":"clockwise vertical arrows","shortname":":clockwise:","category":8,"emoji_order":3326},{"name":"counterclockwise arrows button","shortname":":counter_clockwise:","category":8,"emoji_order":3327},{"name":"BACK arrow","shortname":":back:","category":8,"emoji_order":3328},{"name":"END arrow","shortname":":end:","category":8,"emoji_order":3329},{"name":"ON! arrow","shortname":":on:","category":8,"emoji_order":3330},{"name":"SOON arrow","shortname":":soon:","category":8,"emoji_order":3331},{"name":"TOP arrow","shortname":":top:","category":8,"emoji_order":3332},{"name":"place of worship","shortname":":place_of_worship:","category":8,"emoji_order":3333},{"name":"atom symbol","shortname":":atom:","category":8,"emoji_order":3335},{"name":"om","shortname":":om_symbol:","category":8,"emoji_order":3337},{"name":"star of David","shortname":":star_of_david:","category":8,"emoji_order":3339},{"name":"wheel of dharma","shortname":":wheel_of_dharma:","category":8,"emoji_order":3341},{"name":"yin yang","shortname":":yin_yang:","category":8,"emoji_order":3343},{"name":"latin cross","shortname":":cross:","category":8,"emoji_order":3345},{"name":"orthodox cross","shortname":":orthodox_cross:","category":8,"emoji_order":3347},{"name":"star and crescent","shortname":":star_and_crescent:","category":8,"emoji_order":3349},{"name":"peace symbol","shortname":":peace:","category":8,"emoji_order":3351},{"name":"menorah","shortname":":menorah:","category":8,"emoji_order":3352},{"name":"dotted six-pointed star","shortname":":six_pointed_star:","category":8,"emoji_order":3353},{"name":"Aries","shortname":":aries:","category":8,"emoji_order":3354},{"name":"Taurus","shortname":":taurus:","category":8,"emoji_order":3355},{"name":"Gemini","shortname":":gemini:","category":8,"emoji_order":3356},{"name":"Cancer","shortname":":cancer:","category":8,"emoji_order":3357},{"name":"Leo","shortname":":leo:","category":8,"emoji_order":3358},{"name":"Virgo","shortname":":virgo:","category":8,"emoji_order":3359},{"name":"Libra","shortname":":libra:","category":8,"emoji_order":3360},{"name":"Scorpio","shortname":":scorpius:","category":8,"emoji_order":3361},{"name":"Sagittarius","shortname":":sagittarius:","category":8,"emoji_order":3362},{"name":"Capricorn","shortname":":capricorn:","category":8,"emoji_order":3363},{"name":"Aquarius","shortname":":aquarius:","category":8,"emoji_order":3364},{"name":"Pisces","shortname":":pisces:","category":8,"emoji_order":3365},{"name":"Ophiuchus","shortname":":ophiuchus:","category":8,"emoji_order":3366},{"name":"shuffle tracks button","shortname":":shuffle:","category":8,"emoji_order":3367},{"name":"repeat button","shortname":":repeat:","category":8,"emoji_order":3368},{"name":"repeat single button","shortname":":repeat_single:","category":8,"emoji_order":3369},{"name":"play button","shortname":":play:","category":8,"emoji_order":3371},{"name":"fast-forward button","shortname":":fast_forward:","category":8,"emoji_order":3372},{"name":"next track button","shortname":":next_track:","category":8,"emoji_order":3374},{"name":"play or pause button","shortname":":play_pause:","category":8,"emoji_order":3376},{"name":"reverse button","shortname":":reverse:","category":8,"emoji_order":3378},{"name":"fast reverse button","shortname":":rewind:","category":8,"emoji_order":3379},{"name":"last track button","shortname":":previous_track:","category":8,"emoji_order":3381},{"name":"upwards button","shortname":":up_button:","category":8,"emoji_order":3382},{"name":"fast up button","shortname":":fast_up_button:","category":8,"emoji_order":3383},{"name":"downwards button","shortname":":down_button:","category":8,"emoji_order":3384},{"name":"fast down button","shortname":":fast_down_button:","category":8,"emoji_order":3385},{"name":"pause button","shortname":":pause:","category":8,"emoji_order":3387},{"name":"stop button","shortname":":stop:","category":8,"emoji_order":3389},{"name":"record button","shortname":":record:","category":8,"emoji_order":3391},{"name":"eject button","shortname":":eject:","category":8,"emoji_order":3393},{"name":"cinema","shortname":":cinema:","category":8,"emoji_order":3394},{"name":"dim button","shortname":":dim:","category":8,"emoji_order":3395,"aliases":[":low_brightness:"]},{"name":"bright button","shortname":":bright:","category":8,"emoji_order":3396,"aliases":[":high_brightness:"]},{"name":"antenna bars","shortname":":signal_strength:","category":8,"emoji_order":3397,"aliases":[":antenna_bars:"]},{"name":"vibration mode","shortname":":vibration_mode:","category":8,"emoji_order":3398},{"name":"mobile phone off","shortname":":mobile_phone_off:","category":8,"emoji_order":3399},{"name":"female sign","shortname":":female:","category":8,"emoji_order":3401,"aliases":[":female_sign:"]},{"name":"male sign","shortname":":male:","category":8,"emoji_order":3403,"aliases":[":male_sign:"]},{"name":"medical symbol","shortname":":medical:","category":8,"emoji_order":3405},{"name":"infinity","shortname":":infinity:","category":8,"emoji_order":3407},{"name":"recycling symbol","shortname":":recycle:","category":8,"emoji_order":3409},{"name":"fleur-de-lis","shortname":":fleur-de-lis:","category":8,"emoji_order":3411},{"name":"trident emblem","shortname":":trident:","category":8,"emoji_order":3412},{"name":"name badge","shortname":":name_badge:","category":8,"emoji_order":3413},{"name":"Japanese symbol for beginner","shortname":":ja_beginner:","category":8,"emoji_order":3414},{"name":"hollow red circle","shortname":":o:","category":8,"emoji_order":3415},{"name":"check mark button","shortname":":white_check_mark:","category":8,"emoji_order":3416},{"name":"check box with check","shortname":":checked_ballot:","category":8,"emoji_order":3418},{"name":"check mark","shortname":":check_mark:","category":8,"emoji_order":3420},{"name":"multiplication sign","shortname":":multiplication:","category":8,"emoji_order":3422},{"name":"cross mark","shortname":":x:","category":8,"emoji_order":3423,"aliases":[":cross_mark:"]},{"name":"cross mark button","shortname":":cross_mark_button:","category":8,"emoji_order":3424},{"name":"plus sign","shortname":":plus:","category":8,"emoji_order":3425},{"name":"minus sign","shortname":":minus:","category":8,"emoji_order":3426},{"name":"division sign","shortname":":division:","category":8,"emoji_order":3427},{"name":"curly loop","shortname":":curly_loop:","category":8,"emoji_order":3428},{"name":"double curly loop","shortname":":double_curly_loop:","category":8,"emoji_order":3429},{"name":"part alternation mark","shortname":":part_alternation_mark:","category":8,"emoji_order":3431},{"name":"eight-spoked asterisk","shortname":":eight_spoked_asterisk:","category":8,"emoji_order":3433},{"name":"eight-pointed star","shortname":":eight_pointed_star:","category":8,"emoji_order":3435},{"name":"sparkle","shortname":":sparkle:","category":8,"emoji_order":3437},{"name":"double exclamation mark","shortname":":bangbang:","category":8,"emoji_order":3439,"aliases":[":double_exclamation:"]},{"name":"exclamation question mark","shortname":":interrobang:","category":8,"emoji_order":3441,"aliases":[":exclamation_question:"]},{"name":"question mark","shortname":":question:","category":8,"emoji_order":3442},{"name":"white question mark","shortname":":white_question:","category":8,"emoji_order":3443},{"name":"white exclamation mark","shortname":":white_exclamation:","category":8,"emoji_order":3444},{"name":"exclamation mark","shortname":":exclamation:","category":8,"emoji_order":3445},{"name":"wavy dash","shortname":":wavy_dash:","category":8,"emoji_order":3447},{"name":"copyright","shortname":":copyright:","category":8,"emoji_order":3449},{"name":"registered","shortname":":registered:","category":8,"emoji_order":3451},{"name":"trade mark","shortname":":tm:","category":8,"emoji_order":3453},{"name":"keycap: #","shortname":":hash:","category":8,"emoji_order":3454},{"name":"keycap: *","shortname":":asterisk:","category":8,"emoji_order":3456},{"name":"keycap: 0","shortname":":zero:","category":8,"emoji_order":3458},{"name":"keycap: 1","shortname":":one:","category":8,"emoji_order":3460},{"name":"keycap: 2","shortname":":two:","category":8,"emoji_order":3462},{"name":"keycap: 3","shortname":":three:","category":8,"emoji_order":3464},{"name":"keycap: 4","shortname":":four:","category":8,"emoji_order":3466},{"name":"keycap: 5","shortname":":five:","category":8,"emoji_order":3468},{"name":"keycap: 6","shortname":":six:","category":8,"emoji_order":3470},{"name":"keycap: 7","shortname":":seven:","category":8,"emoji_order":3472},{"name":"keycap: 8","shortname":":eight:","category":8,"emoji_order":3474},{"name":"keycap: 9","shortname":":nine:","category":8,"emoji_order":3476},{"name":"keycap: 10","shortname":":ten:","category":8,"emoji_order":3478},{"name":"input latin uppercase","shortname":":upper_abcd:","category":8,"emoji_order":3479},{"name":"input latin lowercase","shortname":":abcd:","category":8,"emoji_order":3480},{"name":"input numbers","shortname":":1234:","category":8,"emoji_order":3481},{"name":"input symbols","shortname":":symbols:","category":8,"emoji_order":3482},{"name":"input latin letters","shortname":":abc:","category":8,"emoji_order":3483},{"name":"A button (blood type)","shortname":":a_blood:","category":8,"emoji_order":3485},{"name":"AB button (blood type)","shortname":":ab_blood:","category":8,"emoji_order":3486},{"name":"B button (blood type)","shortname":":b_blood:","category":8,"emoji_order":3488},{"name":"CL button","shortname":":cl:","category":8,"emoji_order":3489},{"name":"COOL button","shortname":":cool:","category":8,"emoji_order":3490},{"name":"FREE button","shortname":":free:","category":8,"emoji_order":3491},{"name":"information","shortname":":info:","category":8,"emoji_order":3493},{"name":"ID button","shortname":":id:","category":8,"emoji_order":3494},{"name":"circled M","shortname":":m:","category":8,"emoji_order":3496},{"name":"NEW button","shortname":":new:","category":8,"emoji_order":3497},{"name":"NG button","shortname":":ng:","category":8,"emoji_order":3498},{"name":"O button (blood type)","shortname":":o_blood:","category":8,"emoji_order":3500},{"name":"OK button","shortname":":ok:","category":8,"emoji_order":3501},{"name":"P button","shortname":":p:","category":8,"emoji_order":3503},{"name":"SOS button","shortname":":sos:","category":8,"emoji_order":3504},{"name":"UP! button","shortname":":up:","category":8,"emoji_order":3505},{"name":"VS button","shortname":":vs:","category":8,"emoji_order":3506},{"name":"Japanese “here” button","shortname":":ja_here:","category":8,"emoji_order":3507,"aliases":[":koko:"]},{"name":"Japanese “service charge” button","shortname":":ja_service_charge:","category":8,"emoji_order":3509},{"name":"Japanese “monthly amount” button","shortname":":ja_monthly_amount:","category":8,"emoji_order":3511},{"name":"Japanese “not free of charge” button","shortname":":ja_not_free_of_carge:","category":8,"emoji_order":3512},{"name":"Japanese “reserved” button","shortname":":ja_reserved:","category":8,"emoji_order":3513},{"name":"Japanese “bargain” button","shortname":":ja_bargain:","category":8,"emoji_order":3514},{"name":"Japanese “discount” button","shortname":":ja_discount:","category":8,"emoji_order":3515},{"name":"Japanese “free of charge” button","shortname":":ja_free_of_charge:","category":8,"emoji_order":3516},{"name":"Japanese “prohibited” button","shortname":":ja_prohibited:","category":8,"emoji_order":3517},{"name":"Japanese “acceptable” button","shortname":":ja_acceptable:","category":8,"emoji_order":3518},{"name":"Japanese “application” button","shortname":":ja_application:","category":8,"emoji_order":3519},{"name":"Japanese “passing grade” button","shortname":":ja_passing_grade:","category":8,"emoji_order":3520},{"name":"Japanese “vacancy” button","shortname":":ja_vacancy:","category":8,"emoji_order":3521},{"name":"Japanese “congratulations” button","shortname":":ja_congratulations:","category":8,"emoji_order":3523},{"name":"Japanese “secret” button","shortname":":ja_secret:","category":8,"emoji_order":3525},{"name":"Japanese “open for business” button","shortname":":ja_open_for_business:","category":8,"emoji_order":3526},{"name":"Japanese “no vacancy” button","shortname":":ja_no_vacancy:","category":8,"emoji_order":3527},{"name":"red circle","shortname":":red_circle:","category":8,"emoji_order":3528},{"name":"orange circle","shortname":":orange_circle:","category":8,"emoji_order":3529},{"name":"yellow circle","shortname":":yellow_circle:","category":8,"emoji_order":3530},{"name":"green circle","shortname":":green_circle:","category":8,"emoji_order":3531},{"name":"blue circle","shortname":":blue_circle:","category":8,"emoji_order":3532},{"name":"purple circle","shortname":":purple_circle:","category":8,"emoji_order":3533},{"name":"brown circle","shortname":":brown_circle:","category":8,"emoji_order":3534},{"name":"black circle","shortname":":black_circle:","category":8,"emoji_order":3535},{"name":"white circle","shortname":":white_circle:","category":8,"emoji_order":3536},{"name":"red square","shortname":":red_square:","category":8,"emoji_order":3537},{"name":"orange square","shortname":":orange_square:","category":8,"emoji_order":3538},{"name":"yellow square","shortname":":yellow_square:","category":8,"emoji_order":3539},{"name":"green square","shortname":":green_square:","category":8,"emoji_order":3540},{"name":"blue square","shortname":":blue_square:","category":8,"emoji_order":3541},{"name":"purple square","shortname":":purple_square:","category":8,"emoji_order":3542},{"name":"brown square","shortname":":brown_square:","category":8,"emoji_order":3543},{"name":"black large square","shortname":":large_black_square:","category":8,"emoji_order":3544},{"name":"white large square","shortname":":large_white_square:","category":8,"emoji_order":3545},{"name":"black medium square","shortname":":medium_black_square:","category":8,"emoji_order":3547},{"name":"white medium square","shortname":":medium_white_square:","category":8,"emoji_order":3549},{"name":"black medium-small square","shortname":":medium_small_black_square:","category":8,"emoji_order":3550},{"name":"white medium-small square","shortname":":medium_small_white_square:","category":8,"emoji_order":3551},{"name":"black small square","shortname":":small_black_square:","category":8,"emoji_order":3553},{"name":"white small square","shortname":":small_white_square:","category":8,"emoji_order":3555},{"name":"large orange diamond","shortname":":large_orange_diamond:","category":8,"emoji_order":3556},{"name":"large blue diamond","shortname":":large_blue_diamond:","category":8,"emoji_order":3557},{"name":"small orange diamond","shortname":":small_orange_diamond:","category":8,"emoji_order":3558},{"name":"small blue diamond","shortname":":small_blue_diamond:","category":8,"emoji_order":3559},{"name":"red triangle pointed up","shortname":":up_red_triangle:","category":8,"emoji_order":3560},{"name":"red triangle pointed down","shortname":":down_red_triangle:","category":8,"emoji_order":3561},{"name":"diamond with a dot","shortname":":diamond_dot:","category":8,"emoji_order":3562},{"name":"radio button","shortname":":radio_button:","category":8,"emoji_order":3563},{"name":"white square button","shortname":":white_square_button:","category":8,"emoji_order":3564},{"name":"black square button","shortname":":black_square_button:","category":8,"emoji_order":3565},{"name":"chequered flag","shortname":":checkered_flag:","category":9,"emoji_order":3566},{"name":"triangular flag","shortname":":triangle_flag:","category":9,"emoji_order":3567},{"name":"crossed flags","shortname":":crossed_flags:","category":9,"emoji_order":3568},{"name":"black flag","shortname":":black_flag:","category":9,"emoji_order":3569},{"name":"white flag","shortname":":white_flag:","category":9,"emoji_order":3571},{"name":"rainbow flag","shortname":":rainbow_flag:","category":9,"emoji_order":3572},{"name":"pirate flag","shortname":":pirate_flag:","category":9,"emoji_order":3574,"aliases":[":jolly_roger:"]},{"name":"flag: Ascension Island","shortname":":flag_ac:","category":9,"emoji_order":3576},{"name":"flag: Andorra","shortname":":flag_ad:","category":9,"emoji_order":3577},{"name":"flag: United Arab Emirates","shortname":":flag_ae:","category":9,"emoji_order":3578},{"name":"flag: Afghanistan","shortname":":flag_af:","category":9,"emoji_order":3579},{"name":"flag: Antigua & Barbuda","shortname":":flag_ag:","category":9,"emoji_order":3580},{"name":"flag: Anguilla","shortname":":flag_ai:","category":9,"emoji_order":3581},{"name":"flag: Albania","shortname":":flag_al:","category":9,"emoji_order":3582},{"name":"flag: Armenia","shortname":":flag_am:","category":9,"emoji_order":3583},{"name":"flag: Angola","shortname":":flag_ao:","category":9,"emoji_order":3584},{"name":"flag: Antarctica","shortname":":flag_aq:","category":9,"emoji_order":3585},{"name":"flag: Argentina","shortname":":flag_ar:","category":9,"emoji_order":3586},{"name":"flag: American Samoa","shortname":":flag_as:","category":9,"emoji_order":3587},{"name":"flag: Austria","shortname":":flag_at:","category":9,"emoji_order":3588},{"name":"flag: Australia","shortname":":flag_au:","category":9,"emoji_order":3589},{"name":"flag: Aruba","shortname":":flag_aw:","category":9,"emoji_order":3590},{"name":"flag: Åland Islands","shortname":":flag_ax:","category":9,"emoji_order":3591},{"name":"flag: Azerbaijan","shortname":":flag_az:","category":9,"emoji_order":3592},{"name":"flag: Bosnia & Herzegovina","shortname":":flag_ba:","category":9,"emoji_order":3593},{"name":"flag: Barbados","shortname":":flag_bb:","category":9,"emoji_order":3594},{"name":"flag: Bangladesh","shortname":":flag_bd:","category":9,"emoji_order":3595},{"name":"flag: Belgium","shortname":":flag_be:","category":9,"emoji_order":3596},{"name":"flag: Burkina Faso","shortname":":flag_bf:","category":9,"emoji_order":3597},{"name":"flag: Bulgaria","shortname":":flag_bg:","category":9,"emoji_order":3598},{"name":"flag: Bahrain","shortname":":flag_bh:","category":9,"emoji_order":3599},{"name":"flag: Burundi","shortname":":flag_bi:","category":9,"emoji_order":3600},{"name":"flag: Benin","shortname":":flag_bj:","category":9,"emoji_order":3601},{"name":"flag: St. Barthélemy","shortname":":flag_bl:","category":9,"emoji_order":3602},{"name":"flag: Bermuda","shortname":":flag_bm:","category":9,"emoji_order":3603},{"name":"flag: Brunei","shortname":":flag_bn:","category":9,"emoji_order":3604},{"name":"flag: Bolivia","shortname":":flag_bo:","category":9,"emoji_order":3605},{"name":"flag: Caribbean Netherlands","shortname":":flag_bq:","category":9,"emoji_order":3606},{"name":"flag: Brazil","shortname":":flag_br:","category":9,"emoji_order":3607},{"name":"flag: Bahamas","shortname":":flag_bs:","category":9,"emoji_order":3608},{"name":"flag: Bhutan","shortname":":flag_bt:","category":9,"emoji_order":3609},{"name":"flag: Bouvet Island","shortname":":flag_bv:","category":9,"emoji_order":3610},{"name":"flag: Botswana","shortname":":flag_bw:","category":9,"emoji_order":3611},{"name":"flag: Belarus","shortname":":flag_by:","category":9,"emoji_order":3612},{"name":"flag: Belize","shortname":":flag_bz:","category":9,"emoji_order":3613},{"name":"flag: Canada","shortname":":flag_ca:","category":9,"emoji_order":3614},{"name":"flag: Cocos (Keeling) Islands","shortname":":flag_cc:","category":9,"emoji_order":3615},{"name":"flag: Congo - Kinshasa","shortname":":flag_cd:","category":9,"emoji_order":3616},{"name":"flag: Central African Republic","shortname":":flag_cf:","category":9,"emoji_order":3617},{"name":"flag: Congo - Brazzaville","shortname":":flag_cg:","category":9,"emoji_order":3618},{"name":"flag: Switzerland","shortname":":flag_ch:","category":9,"emoji_order":3619},{"name":"flag: Côte d’Ivoire","shortname":":flag_ci:","category":9,"emoji_order":3620},{"name":"flag: Cook Islands","shortname":":flag_ck:","category":9,"emoji_order":3621},{"name":"flag: Chile","shortname":":flag_cl:","category":9,"emoji_order":3622},{"name":"flag: Cameroon","shortname":":flag_cm:","category":9,"emoji_order":3623},{"name":"flag: China","shortname":":flag_cn:","category":9,"emoji_order":3624},{"name":"flag: Colombia","shortname":":flag_co:","category":9,"emoji_order":3625},{"name":"flag: Clipperton Island","shortname":":flag_cp:","category":9,"emoji_order":3626},{"name":"flag: Costa Rica","shortname":":flag_cr:","category":9,"emoji_order":3627},{"name":"flag: Cuba","shortname":":flag_cu:","category":9,"emoji_order":3628},{"name":"flag: Cape Verde","shortname":":flag_cv:","category":9,"emoji_order":3629},{"name":"flag: Curaçao","shortname":":flag_cw:","category":9,"emoji_order":3630},{"name":"flag: Christmas Island","shortname":":flag_cx:","category":9,"emoji_order":3631},{"name":"flag: Cyprus","shortname":":flag_cy:","category":9,"emoji_order":3632},{"name":"flag: Czechia","shortname":":flag_cz:","category":9,"emoji_order":3633},{"name":"flag: Germany","shortname":":flag_de:","category":9,"emoji_order":3634},{"name":"flag: Diego Garcia","shortname":":flag_dg:","category":9,"emoji_order":3635},{"name":"flag: Djibouti","shortname":":flag_dj:","category":9,"emoji_order":3636},{"name":"flag: Denmark","shortname":":flag_dk:","category":9,"emoji_order":3637},{"name":"flag: Dominica","shortname":":flag_dm:","category":9,"emoji_order":3638},{"name":"flag: Dominican Republic","shortname":":flag_do:","category":9,"emoji_order":3639},{"name":"flag: Algeria","shortname":":flag_dz:","category":9,"emoji_order":3640},{"name":"flag: Ceuta & Melilla","shortname":":flag_ea:","category":9,"emoji_order":3641},{"name":"flag: Ecuador","shortname":":flag_ec:","category":9,"emoji_order":3642},{"name":"flag: Estonia","shortname":":flag_ee:","category":9,"emoji_order":3643},{"name":"flag: Egypt","shortname":":flag_eg:","category":9,"emoji_order":3644},{"name":"flag: Western Sahara","shortname":":flag_eh:","category":9,"emoji_order":3645},{"name":"flag: Eritrea","shortname":":flag_er:","category":9,"emoji_order":3646},{"name":"flag: Spain","shortname":":flag_es:","category":9,"emoji_order":3647},{"name":"flag: Ethiopia","shortname":":flag_et:","category":9,"emoji_order":3648},{"name":"flag: European Union","shortname":":flag_eu:","category":9,"emoji_order":3649},{"name":"flag: Finland","shortname":":flag_fi:","category":9,"emoji_order":3650},{"name":"flag: Fiji","shortname":":flag_fj:","category":9,"emoji_order":3651},{"name":"flag: Falkland Islands","shortname":":flag_fk:","category":9,"emoji_order":3652},{"name":"flag: Micronesia","shortname":":flag_fm:","category":9,"emoji_order":3653},{"name":"flag: Faroe Islands","shortname":":flag_fo:","category":9,"emoji_order":3654},{"name":"flag: France","shortname":":flag_fr:","category":9,"emoji_order":3655},{"name":"flag: Gabon","shortname":":flag_ga:","category":9,"emoji_order":3656},{"name":"flag: United Kingdom","shortname":":flag_gb:","category":9,"emoji_order":3657},{"name":"flag: Grenada","shortname":":flag_gd:","category":9,"emoji_order":3658},{"name":"flag: Georgia","shortname":":flag_ge:","category":9,"emoji_order":3659},{"name":"flag: French Guiana","shortname":":flag_gf:","category":9,"emoji_order":3660},{"name":"flag: Guernsey","shortname":":flag_gg:","category":9,"emoji_order":3661},{"name":"flag: Ghana","shortname":":flag_gh:","category":9,"emoji_order":3662},{"name":"flag: Gibraltar","shortname":":flag_gi:","category":9,"emoji_order":3663},{"name":"flag: Greenland","shortname":":flag_gl:","category":9,"emoji_order":3664},{"name":"flag: Gambia","shortname":":flag_gm:","category":9,"emoji_order":3665},{"name":"flag: Guinea","shortname":":flag_gn:","category":9,"emoji_order":3666},{"name":"flag: Guadeloupe","shortname":":flag_gp:","category":9,"emoji_order":3667},{"name":"flag: Equatorial Guinea","shortname":":flag_gq:","category":9,"emoji_order":3668},{"name":"flag: Greece","shortname":":flag_gr:","category":9,"emoji_order":3669},{"name":"flag: South Georgia & South Sandwich Islands","shortname":":flag_gs:","category":9,"emoji_order":3670},{"name":"flag: Guatemala","shortname":":flag_gt:","category":9,"emoji_order":3671},{"name":"flag: Guam","shortname":":flag_gu:","category":9,"emoji_order":3672},{"name":"flag: Guinea-Bissau","shortname":":flag_gw:","category":9,"emoji_order":3673},{"name":"flag: Guyana","shortname":":flag_gy:","category":9,"emoji_order":3674},{"name":"flag: Hong Kong SAR China","shortname":":flag_hk:","category":9,"emoji_order":3675},{"name":"flag: Heard & McDonald Islands","shortname":":flag_hm:","category":9,"emoji_order":3676},{"name":"flag: Honduras","shortname":":flag_hn:","category":9,"emoji_order":3677},{"name":"flag: Croatia","shortname":":flag_hr:","category":9,"emoji_order":3678},{"name":"flag: Haiti","shortname":":flag_ht:","category":9,"emoji_order":3679},{"name":"flag: Hungary","shortname":":flag_hu:","category":9,"emoji_order":3680},{"name":"flag: Canary Islands","shortname":":flag_ic:","category":9,"emoji_order":3681},{"name":"flag: Indonesia","shortname":":flag_id:","category":9,"emoji_order":3682},{"name":"flag: Ireland","shortname":":flag_ie:","category":9,"emoji_order":3683},{"name":"flag: Israel","shortname":":flag_il:","category":9,"emoji_order":3684},{"name":"flag: Isle of Man","shortname":":flag_im:","category":9,"emoji_order":3685},{"name":"flag: India","shortname":":flag_in:","category":9,"emoji_order":3686},{"name":"flag: British Indian Ocean Territory","shortname":":flag_io:","category":9,"emoji_order":3687},{"name":"flag: Iraq","shortname":":flag_iq:","category":9,"emoji_order":3688},{"name":"flag: Iran","shortname":":flag_ir:","category":9,"emoji_order":3689},{"name":"flag: Iceland","shortname":":flag_is:","category":9,"emoji_order":3690},{"name":"flag: Italy","shortname":":flag_it:","category":9,"emoji_order":3691},{"name":"flag: Jersey","shortname":":flag_je:","category":9,"emoji_order":3692},{"name":"flag: Jamaica","shortname":":flag_jm:","category":9,"emoji_order":3693},{"name":"flag: Jordan","shortname":":flag_jo:","category":9,"emoji_order":3694},{"name":"flag: Japan","shortname":":flag_jp:","category":9,"emoji_order":3695},{"name":"flag: Kenya","shortname":":flag_ke:","category":9,"emoji_order":3696},{"name":"flag: Kyrgyzstan","shortname":":flag_kg:","category":9,"emoji_order":3697},{"name":"flag: Cambodia","shortname":":flag_kh:","category":9,"emoji_order":3698},{"name":"flag: Kiribati","shortname":":flag_ki:","category":9,"emoji_order":3699},{"name":"flag: Comoros","shortname":":flag_km:","category":9,"emoji_order":3700},{"name":"flag: St. Kitts & Nevis","shortname":":flag_kn:","category":9,"emoji_order":3701},{"name":"flag: North Korea","shortname":":flag_kp:","category":9,"emoji_order":3702},{"name":"flag: South Korea","shortname":":flag_kr:","category":9,"emoji_order":3703},{"name":"flag: Kuwait","shortname":":flag_kw:","category":9,"emoji_order":3704},{"name":"flag: Cayman Islands","shortname":":flag_ky:","category":9,"emoji_order":3705},{"name":"flag: Kazakhstan","shortname":":flag_kz:","category":9,"emoji_order":3706},{"name":"flag: Laos","shortname":":flag_la:","category":9,"emoji_order":3707},{"name":"flag: Lebanon","shortname":":flag_lb:","category":9,"emoji_order":3708},{"name":"flag: St. Lucia","shortname":":flag_lc:","category":9,"emoji_order":3709},{"name":"flag: Liechtenstein","shortname":":flag_li:","category":9,"emoji_order":3710},{"name":"flag: Sri Lanka","shortname":":flag_lk:","category":9,"emoji_order":3711},{"name":"flag: Liberia","shortname":":flag_lr:","category":9,"emoji_order":3712},{"name":"flag: Lesotho","shortname":":flag_ls:","category":9,"emoji_order":3713},{"name":"flag: Lithuania","shortname":":flag_lt:","category":9,"emoji_order":3714},{"name":"flag: Luxembourg","shortname":":flag_lu:","category":9,"emoji_order":3715},{"name":"flag: Latvia","shortname":":flag_lv:","category":9,"emoji_order":3716},{"name":"flag: Libya","shortname":":flag_ly:","category":9,"emoji_order":3717},{"name":"flag: Morocco","shortname":":flag_ma:","category":9,"emoji_order":3718},{"name":"flag: Monaco","shortname":":flag_mc:","category":9,"emoji_order":3719},{"name":"flag: Moldova","shortname":":flag_md:","category":9,"emoji_order":3720},{"name":"flag: Montenegro","shortname":":flag_me:","category":9,"emoji_order":3721},{"name":"flag: St. Martin","shortname":":flag_mf:","category":9,"emoji_order":3722},{"name":"flag: Madagascar","shortname":":flag_mg:","category":9,"emoji_order":3723},{"name":"flag: Marshall Islands","shortname":":flag_mh:","category":9,"emoji_order":3724},{"name":"flag: North Macedonia","shortname":":flag_mk:","category":9,"emoji_order":3725},{"name":"flag: Mali","shortname":":flag_ml:","category":9,"emoji_order":3726},{"name":"flag: Myanmar (Burma)","shortname":":flag_mm:","category":9,"emoji_order":3727},{"name":"flag: Mongolia","shortname":":flag_mn:","category":9,"emoji_order":3728},{"name":"flag: Macao SAR China","shortname":":flag_mo:","category":9,"emoji_order":3729},{"name":"flag: Northern Mariana Islands","shortname":":flag_mp:","category":9,"emoji_order":3730},{"name":"flag: Martinique","shortname":":flag_mq:","category":9,"emoji_order":3731},{"name":"flag: Mauritania","shortname":":flag_mr:","category":9,"emoji_order":3732},{"name":"flag: Montserrat","shortname":":flag_ms:","category":9,"emoji_order":3733},{"name":"flag: Malta","shortname":":flag_mt:","category":9,"emoji_order":3734},{"name":"flag: Mauritius","shortname":":flag_mu:","category":9,"emoji_order":3735},{"name":"flag: Maldives","shortname":":flag_mv:","category":9,"emoji_order":3736},{"name":"flag: Malawi","shortname":":flag_mw:","category":9,"emoji_order":3737},{"name":"flag: Mexico","shortname":":flag_mx:","category":9,"emoji_order":3738},{"name":"flag: Malaysia","shortname":":flag_my:","category":9,"emoji_order":3739},{"name":"flag: Mozambique","shortname":":flag_mz:","category":9,"emoji_order":3740},{"name":"flag: Namibia","shortname":":flag_na:","category":9,"emoji_order":3741},{"name":"flag: New Caledonia","shortname":":flag_nc:","category":9,"emoji_order":3742},{"name":"flag: Niger","shortname":":flag_ne:","category":9,"emoji_order":3743},{"name":"flag: Norfolk Island","shortname":":flag_nf:","category":9,"emoji_order":3744},{"name":"flag: Nigeria","shortname":":flag_ng:","category":9,"emoji_order":3745},{"name":"flag: Nicaragua","shortname":":flag_ni:","category":9,"emoji_order":3746},{"name":"flag: Netherlands","shortname":":flag_nl:","category":9,"emoji_order":3747},{"name":"flag: Norway","shortname":":flag_no:","category":9,"emoji_order":3748},{"name":"flag: Nepal","shortname":":flag_np:","category":9,"emoji_order":3749},{"name":"flag: Nauru","shortname":":flag_nr:","category":9,"emoji_order":3750},{"name":"flag: Niue","shortname":":flag_nu:","category":9,"emoji_order":3751},{"name":"flag: New Zealand","shortname":":flag_nz:","category":9,"emoji_order":3752},{"name":"flag: Oman","shortname":":flag_om:","category":9,"emoji_order":3753},{"name":"flag: Panama","shortname":":flag_pa:","category":9,"emoji_order":3754},{"name":"flag: Peru","shortname":":flag_pe:","category":9,"emoji_order":3755},{"name":"flag: French Polynesia","shortname":":flag_pf:","category":9,"emoji_order":3756},{"name":"flag: Papua New Guinea","shortname":":flag_pg:","category":9,"emoji_order":3757},{"name":"flag: Philippines","shortname":":flag_ph:","category":9,"emoji_order":3758},{"name":"flag: Pakistan","shortname":":flag_pk:","category":9,"emoji_order":3759},{"name":"flag: Poland","shortname":":flag_pl:","category":9,"emoji_order":3760},{"name":"flag: St. Pierre & Miquelon","shortname":":flag_pm:","category":9,"emoji_order":3761},{"name":"flag: Pitcairn Islands","shortname":":flag_pn:","category":9,"emoji_order":3762},{"name":"flag: Puerto Rico","shortname":":flag_pr:","category":9,"emoji_order":3763},{"name":"flag: Palestinian Territories","shortname":":flag_ps:","category":9,"emoji_order":3764},{"name":"flag: Portugal","shortname":":flag_pt:","category":9,"emoji_order":3765},{"name":"flag: Palau","shortname":":flag_pw:","category":9,"emoji_order":3766},{"name":"flag: Paraguay","shortname":":flag_py:","category":9,"emoji_order":3767},{"name":"flag: Qatar","shortname":":flag_qa:","category":9,"emoji_order":3768},{"name":"flag: Réunion","shortname":":flag_re:","category":9,"emoji_order":3769},{"name":"flag: Romania","shortname":":flag_ro:","category":9,"emoji_order":3770},{"name":"flag: Serbia","shortname":":flag_rs:","category":9,"emoji_order":3771},{"name":"flag: Russia","shortname":":flag_ru:","category":9,"emoji_order":3772},{"name":"flag: Rwanda","shortname":":flag_rw:","category":9,"emoji_order":3773},{"name":"flag: Saudi Arabia","shortname":":flag_sa:","category":9,"emoji_order":3774},{"name":"flag: Solomon Islands","shortname":":flag_sb:","category":9,"emoji_order":3775},{"name":"flag: Seychelles","shortname":":flag_sc:","category":9,"emoji_order":3776},{"name":"flag: Sudan","shortname":":flag_sd:","category":9,"emoji_order":3777},{"name":"flag: Sweden","shortname":":flag_se:","category":9,"emoji_order":3778},{"name":"flag: Singapore","shortname":":flag_sg:","category":9,"emoji_order":3779},{"name":"flag: St. Helena","shortname":":flag_sh:","category":9,"emoji_order":3780},{"name":"flag: Slovenia","shortname":":flag_si:","category":9,"emoji_order":3781},{"name":"flag: Svalbard & Jan Mayen","shortname":":flag_sj:","category":9,"emoji_order":3782},{"name":"flag: Slovakia","shortname":":flag_sk:","category":9,"emoji_order":3783},{"name":"flag: Sierra Leone","shortname":":flag_sl:","category":9,"emoji_order":3784},{"name":"flag: San Marino","shortname":":flag_sm:","category":9,"emoji_order":3785},{"name":"flag: Senegal","shortname":":flag_sn:","category":9,"emoji_order":3786},{"name":"flag: Somalia","shortname":":flag_so:","category":9,"emoji_order":3787},{"name":"flag: Suriname","shortname":":flag_sr:","category":9,"emoji_order":3788},{"name":"flag: South Sudan","shortname":":flag_ss:","category":9,"emoji_order":3789},{"name":"flag: São Tomé & Príncipe","shortname":":flag_st:","category":9,"emoji_order":3790},{"name":"flag: El Salvador","shortname":":flag_sv:","category":9,"emoji_order":3791},{"name":"flag: Sint Maarten","shortname":":flag_sx:","category":9,"emoji_order":3792},{"name":"flag: Syria","shortname":":flag_sy:","category":9,"emoji_order":3793},{"name":"flag: Eswatini","shortname":":flag_sz:","category":9,"emoji_order":3794},{"name":"flag: Tristan da Cunha","shortname":":flag_ta:","category":9,"emoji_order":3795},{"name":"flag: Turks & Caicos Islands","shortname":":flag_tc:","category":9,"emoji_order":3796},{"name":"flag: Chad","shortname":":flag_td:","category":9,"emoji_order":3797},{"name":"flag: French Southern Territories","shortname":":flag_tf:","category":9,"emoji_order":3798},{"name":"flag: Togo","shortname":":flag_tg:","category":9,"emoji_order":3799},{"name":"flag: Thailand","shortname":":flag_th:","category":9,"emoji_order":3800},{"name":"flag: Tajikistan","shortname":":flag_tj:","category":9,"emoji_order":3801},{"name":"flag: Tokelau","shortname":":flag_tk:","category":9,"emoji_order":3802},{"name":"flag: Timor-Leste","shortname":":flag_tl:","category":9,"emoji_order":3803},{"name":"flag: Turkmenistan","shortname":":flag_tm:","category":9,"emoji_order":3804},{"name":"flag: Tunisia","shortname":":flag_tn:","category":9,"emoji_order":3805},{"name":"flag: Tonga","shortname":":flag_to:","category":9,"emoji_order":3806},{"name":"flag: Turkey","shortname":":flag_tr:","category":9,"emoji_order":3807},{"name":"flag: Trinidad & Tobago","shortname":":flag_tt:","category":9,"emoji_order":3808},{"name":"flag: Tuvalu","shortname":":flag_tv:","category":9,"emoji_order":3809},{"name":"flag: Taiwan","shortname":":flag_tw:","category":9,"emoji_order":3810},{"name":"flag: Tanzania","shortname":":flag_tz:","category":9,"emoji_order":3811},{"name":"flag: Ukraine","shortname":":flag_ua:","category":9,"emoji_order":3812},{"name":"flag: Uganda","shortname":":flag_ug:","category":9,"emoji_order":3813},{"name":"flag: U.S. Outlying Islands","shortname":":flag_um:","category":9,"emoji_order":3814},{"name":"flag: United Nations","shortname":":flag_un:","category":9,"emoji_order":3815},{"name":"flag: United States","shortname":":flag_us:","category":9,"emoji_order":3816,"aliases":[":usa:"]},{"name":"flag: Uruguay","shortname":":flag_uy:","category":9,"emoji_order":3817},{"name":"flag: Uzbekistan","shortname":":flag_uz:","category":9,"emoji_order":3818},{"name":"flag: Vatican City","shortname":":flag_va:","category":9,"emoji_order":3819},{"name":"flag: St. Vincent & Grenadines","shortname":":flag_vc:","category":9,"emoji_order":3820},{"name":"flag: Venezuela","shortname":":flag_ve:","category":9,"emoji_order":3821},{"name":"flag: British Virgin Islands","shortname":":flag_vg:","category":9,"emoji_order":3822},{"name":"flag: U.S. Virgin Islands","shortname":":flag_vi:","category":9,"emoji_order":3823},{"name":"flag: Vietnam","shortname":":flag_vn:","category":9,"emoji_order":3824},{"name":"flag: Vanuatu","shortname":":flag_vu:","category":9,"emoji_order":3825},{"name":"flag: Wallis & Futuna","shortname":":flag_wf:","category":9,"emoji_order":3826},{"name":"flag: Samoa","shortname":":flag_ws:","category":9,"emoji_order":3827},{"name":"flag: Kosovo","shortname":":flag_xk:","category":9,"emoji_order":3828},{"name":"flag: Yemen","shortname":":flag_ye:","category":9,"emoji_order":3829},{"name":"flag: Mayotte","shortname":":flag_yt:","category":9,"emoji_order":3830},{"name":"flag: South Africa","shortname":":flag_za:","category":9,"emoji_order":3831},{"name":"flag: Zambia","shortname":":flag_zm:","category":9,"emoji_order":3832},{"name":"flag: Zimbabwe","shortname":":flag_zw:","category":9,"emoji_order":3833},{"name":"flag: England","shortname":":flag_gbeng:","category":9,"emoji_order":3834,"aliases":[":england:"]},{"name":"flag: Scotland","shortname":":flag_gbsct:","category":9,"emoji_order":3835,"aliases":[":scotland:"]},{"name":"flag: Wales","shortname":":flag_gbwls:","category":9,"emoji_order":3836,"aliases":[":wales:"]}]
\ No newline at end of file
diff --git a/src/utils/DMRoomMap.js b/src/utils/DMRoomMap.js
index de6d42e4a2..fb66b0a55f 100644
--- a/src/utils/DMRoomMap.js
+++ b/src/utils/DMRoomMap.js
@@ -1,5 +1,6 @@
/*
Copyright 2016 OpenMarket Ltd
+Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,6 +17,7 @@ limitations under the License.
import {MatrixClientPeg} from '../MatrixClientPeg';
import _uniq from 'lodash/uniq';
+import {Room} from "matrix-js-sdk/lib/matrix";
/**
* Class that takes a Matrix Client and flips the m.direct map
@@ -144,6 +146,13 @@ export default class DMRoomMap {
return this.roomToUser[roomId];
}
+ getUniqueRoomsWithIndividuals(): {[userId: string]: Room} {
+ return Object.keys(this.roomToUser)
+ .map(r => ({userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r)}))
+ .filter(r => r.userId && r.room && r.room.getInvitedAndJoinedMemberCount() === 2)
+ .reduce((obj, r) => (obj[r.userId] = r.room) && obj, {});
+ }
+
_getUserToRooms() {
if (!this.userToRooms) {
const userToRooms = this.mDirectEvent;
diff --git a/src/utils/ErrorUtils.js b/src/utils/ErrorUtils.js
index 4a56d64ef1..51b130bdb6 100644
--- a/src/utils/ErrorUtils.js
+++ b/src/utils/ErrorUtils.js
@@ -49,6 +49,12 @@ export function messageForResourceLimitError(limitType, adminContact, strings, e
}
}
+export function messageForSendError(errorData) {
+ if (errorData.errcode === "M_TOO_LARGE") {
+ return _t("The message you are trying to send is too large.");
+ }
+}
+
export function messageForSyncError(err) {
if (err.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') {
const limitError = messageForResourceLimitError(
diff --git a/src/utils/pillify.js b/src/utils/pillify.js
index 8c2eda5f71..24cc8a3c67 100644
--- a/src/utils/pillify.js
+++ b/src/utils/pillify.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import React from "react";
import ReactDOM from 'react-dom';
import {MatrixClientPeg} from '../MatrixClientPeg';
import SettingsStore from "../settings/SettingsStore";
diff --git a/test/components/structures/MessagePanel-test.js b/test/components/structures/MessagePanel-test.js
index 60a392047b..5cc0b5b04c 100644
--- a/test/components/structures/MessagePanel-test.js
+++ b/test/components/structures/MessagePanel-test.js
@@ -36,27 +36,14 @@ const test_utils = require('test-utils');
const mockclock = require('mock-clock');
import Velocity from 'velocity-animate';
+import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
+import RoomContext from "../../../src/contexts/RoomContext";
let client;
const room = new Matrix.Room();
// wrap MessagePanel with a component which provides the MatrixClient in the context.
const WrappedMessagePanel = createReactClass({
- childContextTypes: {
- matrixClient: PropTypes.object,
- room: PropTypes.object,
- },
-
- getChildContext: function() {
- return {
- matrixClient: client,
- room: {
- canReact: true,
- canReply: true,
- },
- };
- },
-
getInitialState: function() {
return {
resizeNotifier: new EventEmitter(),
@@ -64,7 +51,11 @@ const WrappedMessagePanel = createReactClass({
},
render: function() {
- return ;
+ return
+
+
+
+ ;
},
});
diff --git a/test/components/views/elements/MemberEventListSummary-test.js b/test/components/views/elements/MemberEventListSummary-test.js
index a31cbdebb5..906ba45711 100644
--- a/test/components/views/elements/MemberEventListSummary-test.js
+++ b/test/components/views/elements/MemberEventListSummary-test.js
@@ -115,7 +115,8 @@ describe('MemberEventListSummary', function() {
const renderer = new ShallowRenderer();
renderer.render();
- const result = renderer.getRenderOutput();
+ const wrapper = renderer.getRenderOutput(); // matrix cli context wrapper
+ const result = wrapper.props.children;
expect(result.props.children).toEqual([