Merge branch 'develop' into experimental

This commit is contained in:
Bruno Windels 2018-11-26 13:10:29 +01:00
commit 67e0030ccd
60 changed files with 3738 additions and 222 deletions

View file

@ -1,4 +1,4 @@
{
"presets": ["react", "es2015", "es2016"],
"plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-bluebird", "transform-runtime", "add-module-exports"]
"plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-bluebird", "transform-runtime", "add-module-exports", "syntax-dynamic-import"]
}

View file

@ -1,3 +1,84 @@
Changes in [0.14.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.6) (2018-11-22)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.5...v0.14.6)
* Warning when crypto DB is too new to use.
Changes in [0.14.5](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.5) (2018-11-19)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.5-rc.2...v0.14.5)
* No changes since rc.1
Changes in [0.14.5-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.5-rc.2) (2018-11-15)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.5-rc.1...v0.14.5-rc.2)
* Update to js-sdk v0.14.0-rc.1 which uses the new Olm API
(v0.14.0-rc.1 was broken because of this).
Changes in [0.14.5-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.5-rc.1) (2018-11-15)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.4...v0.14.5-rc.1)
* Update from Weblate.
[\#2278](https://github.com/matrix-org/matrix-react-sdk/pull/2278)
* Support room IDs and event permalinks in the join command
[\#2272](https://github.com/matrix-org/matrix-react-sdk/pull/2272)
* Align encrypted event buttons in Safari
[\#2274](https://github.com/matrix-org/matrix-react-sdk/pull/2274)
* Align buttons in encrypted event dialog
[\#2273](https://github.com/matrix-org/matrix-react-sdk/pull/2273)
* Add visible guest warning to encourage login
[\#2268](https://github.com/matrix-org/matrix-react-sdk/pull/2268)
* Regenerate the room list when m.fully_read is issued
[\#2266](https://github.com/matrix-org/matrix-react-sdk/pull/2266)
* Remove the request-only stuff we don't need anymore
[\#2263](https://github.com/matrix-org/matrix-react-sdk/pull/2263)
* Improve performance of room list and fix timestamp ordering when pinning
rooms
[\#2265](https://github.com/matrix-org/matrix-react-sdk/pull/2265)
* Add options to pin unread/mentioned rooms to the top of the room list
[\#1936](https://github.com/matrix-org/matrix-react-sdk/pull/1936)
* only run e2e tests on PRs targeted on develop
[\#2261](https://github.com/matrix-org/matrix-react-sdk/pull/2261)
* Fix and test matrix.to alias permalinks
[\#2254](https://github.com/matrix-org/matrix-react-sdk/pull/2254)
* click-through svg on tag tile context menu to make it less weird
[\#2257](https://github.com/matrix-org/matrix-react-sdk/pull/2257)
* Hide Matthew's Time Machine
[\#2256](https://github.com/matrix-org/matrix-react-sdk/pull/2256)
* Update babel-eslint to 8.1.1
[\#2255](https://github.com/matrix-org/matrix-react-sdk/pull/2255)
* Support routing matrix.to links to joinable rooms
[\#2250](https://github.com/matrix-org/matrix-react-sdk/pull/2250)
* Fix autoreplacement of ascii emoji
[\#2253](https://github.com/matrix-org/matrix-react-sdk/pull/2253)
* Repair DevTools button padding by centralizing styles
[\#2252](https://github.com/matrix-org/matrix-react-sdk/pull/2252)
* Redirect widgets to another location before deleting them
[\#2232](https://github.com/matrix-org/matrix-react-sdk/pull/2232)
* disable e2e tests for PRs targeted at experimental (redesign)
[\#2251](https://github.com/matrix-org/matrix-react-sdk/pull/2251)
* Fix emoji replacement in composer
[\#2247](https://github.com/matrix-org/matrix-react-sdk/pull/2247)
* Add a devtools button to roomsettings
[\#2249](https://github.com/matrix-org/matrix-react-sdk/pull/2249)
* Add warning when administrator leaves community (#5724)
[\#2242](https://github.com/matrix-org/matrix-react-sdk/pull/2242)
Changes in [0.14.4](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.4) (2018-11-13)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.3...v0.14.4)
* Include change that was supposed to be included in orevious version
Changes in [0.14.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.3) (2018-11-13)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.2...v0.14.3)
* Add banner with login/register links for users who aren't logged in
Changes in [0.14.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.2) (2018-10-29)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.2-rc.1...v0.14.2)

View file

@ -4,7 +4,7 @@ set -e
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm use 6
nvm use 10
set -x

View file

@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
"version": "0.14.2",
"version": "0.14.6",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
@ -53,6 +53,7 @@
"test-multi": "karma start"
},
"dependencies": {
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-runtime": "^6.26.0",
"bluebird": "^3.5.0",
"blueimp-canvas-to-blob": "^3.5.0",
@ -75,7 +76,7 @@
"linkifyjs": "^2.1.6",
"lodash": "^4.13.1",
"lolex": "2.3.2",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
"matrix-js-sdk": "0.14.1",
"optimist": "^0.6.1",
"pako": "^1.0.5",
"prop-types": "^15.5.8",

View file

@ -254,6 +254,11 @@ textarea {
opacity: 0.7;
}
.mx_linkButton {
cursor: pointer;
color: $accent-color;
}
.mx_Dialog_title {
min-height: 16px;
padding-top: 40px;

View file

@ -35,17 +35,21 @@
@import "./views/dialogs/_ChatInviteDialog.scss";
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
@import "./views/dialogs/_CreateGroupDialog.scss";
@import "./views/dialogs/_CreateKeyBackupDialog.scss";
@import "./views/dialogs/_CreateRoomDialog.scss";
@import "./views/dialogs/_DeactivateAccountDialog.scss";
@import "./views/dialogs/_DevtoolsDialog.scss";
@import "./views/dialogs/_EncryptedEventDialog.scss";
@import "./views/dialogs/_GroupAddressPicker.scss";
@import "./views/dialogs/_RestoreKeyBackupDialog.scss";
@import "./views/dialogs/_RoomUpgradeDialog.scss";
@import "./views/dialogs/_SetEmailDialog.scss";
@import "./views/dialogs/_SetMxIdDialog.scss";
@import "./views/dialogs/_SetPasswordDialog.scss";
@import "./views/dialogs/_ShareDialog.scss";
@import "./views/dialogs/_UnknownDeviceDialog.scss";
@import "./views/dialogs/keybackup/_CreateKeyBackupDialog.scss";
@import "./views/dialogs/keybackup/_RestoreKeyBackupDialog.scss";
@import "./views/directory/_NetworkDropdown.scss";
@import "./views/elements/_AccessibleButton.scss";
@import "./views/elements/_AddressSelector.scss";
@ -111,6 +115,7 @@
@import "./views/rooms/_WhoIsTypingTile.scss";
@import "./views/settings/_DevicesPanel.scss";
@import "./views/settings/_IntegrationsManager.scss";
@import "./views/settings/_KeyBackupPanel.scss";
@import "./views/settings/_Notifications.scss";
@import "./views/voip/_CallView.scss";
@import "./views/voip/_IncomingCallbox.scss";

View file

@ -33,3 +33,16 @@ limitations under the License.
.mx_HomePage_body {
// margin-left: 63px;
}
.mx_HomePage_guest_warning {
display: flex;
background-color: $secondary-accent-color;
border: 1px solid $accent-color;
margin: 20px;
padding: 20px 40px;
border-radius: 5px;
}
.mx_HomePage_guest_warning img {
padding-right: 10px;
}

View file

@ -142,6 +142,17 @@ limitations under the License.
color: $primary-fg-color;
}
.mx_Login_sso_link {
display: block;
text-align: center;
font-size: 15px;
margin-bottom: 20px;
}
.mx_Login_sso_link:link {
color: $primary-fg-color;
}
.mx_Login_loader {
display: inline;
position: relative;

View file

@ -0,0 +1,25 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_CreateKeyBackupDialog {
padding-right: 40px;
}
.mx_CreateKeyBackupDialog_recoveryKey {
padding: 20px;
color: $info-plinth-fg-color;
background-color: $info-plinth-bg-color;
}

View file

@ -24,4 +24,8 @@ limitations under the License.
@mixin mx_DialogButton;
background-color: $primary-bg-color;
color: $accent-color;
}
}
.mx_EncryptedEventDialog button {
margin-top: 0px;
}

View file

@ -0,0 +1,19 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_RestoreKeyBackupDialog_keyStatus {
height: 30px;
}

View file

@ -0,0 +1,39 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_CreateKeyBackupDialog_primaryContainer {
/*FIXME: plinth colour in new theme(s). background-color: $accent-color;*/
padding: 20px
}
.mx_CreateKeyBackupDialog_passPhraseInput {
width: 300px;
border: 1px solid $accent-color;
border-radius: 5px;
padding: 10px;
}
.mx_CreateKeyBackupDialog_passPhraseMatch {
float: right;
}
.mx_CreateKeyBackupDialog_recoveryKeyButtons {
float: right;
}
.mx_CreateKeyBackupDialog_recoveryKey {
width: 300px;
}

View file

@ -0,0 +1,29 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_RestoreKeyBackupDialog_primaryContainer {
/*FIXME: plinth colour in new theme(s). background-color: $accent-color;*/
padding: 20px
}
.mx_RestoreKeyBackupDialog_passPhraseInput,
.mx_RestoreKeyBackupDialog_recoveryKeyInput {
width: 300px;
border: 1px solid $accent-color;
border-radius: 5px;
padding: 10px;
}

View file

@ -35,8 +35,24 @@ limitations under the License.
margin-bottom: 5px;
}
.mx_InteractiveAuthEntryComponents_termsSubmit {
margin-top: 20px;
margin-bottom: 5px;
display: block;
width: 100%;
}
// XXX: This should be a common button class
.mx_InteractiveAuthEntryComponents_msisdnSubmit:disabled {
background-color: $light-fg-color;
cursor: default;
}
.mx_InteractiveAuthEntryComponents_termsSubmit:disabled {
background-color: $accent-color-50pct;
cursor: default;
}
.mx_InteractiveAuthEntryComponents_termsPolicy {
display: block;
}

View file

@ -46,3 +46,14 @@ limitations under the License.
.mx_MImageBody_thumbnail_spinner > * {
transform: translate(-50%, -50%);
}
.mx_MImageBody_gifLabel {
position: absolute;
display: block;
top: 0px;
left: 14px;
padding: 5px;
border-radius: 5px;
background: $imagebody-giflabel;
border: 2px solid $imagebody-giflabel-border;
}

View file

@ -19,7 +19,6 @@ limitations under the License.
}
.mx_MemberDeviceInfo.mx_DeviceVerifyButtons {
padding: 6px 0;
display: flex;
flex-wrap: wrap;
justify-content: space-between;

View file

@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,25 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
.mx_KeyBackupPanel_sigValid, .mx_KeyBackupPanel_sigInvalid,
.mx_KeyBackupPanel_deviceVerified, .mx_KeyBackupPanel_deviceNotVerified {
font-weight: bold;
}
import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
.mx_KeyBackupPanel_sigValid, .mx_KeyBackupPanel_deviceVerified {
color: $e2e-verified-color;
}
module.exports = React.createClass({
displayName: 'CasLogin',
.mx_KeyBackupPanel_sigInvalid, .mx_KeyBackupPanel_deviceNotVerified {
color: $e2e-warning-color;
}
propTypes: {
onSubmit: PropTypes.func, // fn()
},
render: function() {
return (
<div>
<button onClick={this.props.onSubmit}>{ _t("Sign in with CAS") }</button>
</div>
);
},
});
.mx_KeyBackupPanel_deviceName {
font-style: italic;
}

View file

@ -12,6 +12,7 @@ $light-fg-color: #747474;
// button UI (white-on-green in light skin)
$accent-fg-color: $primary-bg-color;
$accent-color: #76CFA6;
$accent-color-50pct: #76CFA67F;
$selection-fg-color: $primary-fg-color;
@ -158,6 +159,9 @@ $lightbox-bg-color: #454545;
$lightbox-fg-color: #ffffff;
$lightbox-border-color: #ffffff;
$imagebody-giflabel: rgba(1, 1, 1, 0.7);
$imagebody-giflabel-border: rgba(1, 1, 1, 0.2);
// unused?
$progressbar-color: #000;

View file

@ -20,6 +20,7 @@ $focus-bg-color: #dddddd;
// button UI (white-on-green in light skin)
$accent-fg-color: #ffffff;
$accent-color: #76CFA6;
$accent-color-50pct: #76CFA67F;
$selection-fg-color: $primary-bg-color;
@ -173,6 +174,9 @@ $lightbox-bg-color: #454545;
$lightbox-fg-color: #ffffff;
$lightbox-border-color: #ffffff;
$imagebody-giflabel: rgba(0, 0, 0, 0.7);
$imagebody-giflabel-border: rgba(0, 0, 0, 0.2);
// unused?
$progressbar-color: #000;

View file

@ -16,7 +16,6 @@ limitations under the License.
*/
import Matrix from "matrix-js-sdk";
import { _t } from "./languageHandler";
import Promise from 'bluebird';
import url from 'url';
@ -225,19 +224,18 @@ export default class Login {
});
}
redirectToCas() {
getSsoLoginUrl(loginType) {
const client = this._createTemporaryClient();
const parsedUrl = url.parse(window.location.href, true);
// XXX: at this point, the fragment will always be #/login, which is no
// use to anyone. Ideally, we would get the intended fragment from
// MatrixChat.screenAfterLogin so that you could follow #/room links etc
// through a CAS login.
// through an SSO login.
parsedUrl.hash = "";
parsedUrl.query["homeserver"] = client.getHomeserverUrl();
parsedUrl.query["identityServer"] = client.getIdentityServerUrl();
const casUrl = client.getCasLoginUrl(url.format(parsedUrl));
window.location.href = casUrl;
return client.getSsoLoginUrl(url.format(parsedUrl), loginType);
}
}

View file

@ -23,10 +23,12 @@ import Matrix from 'matrix-js-sdk';
import utils from 'matrix-js-sdk/lib/utils';
import EventTimeline from 'matrix-js-sdk/lib/models/event-timeline';
import EventTimelineSet from 'matrix-js-sdk/lib/models/event-timeline-set';
import sdk from './index';
import createMatrixClient from './utils/createMatrixClient';
import SettingsStore from './settings/SettingsStore';
import MatrixActionCreators from './actions/MatrixActionCreators';
import {phasedRollOutExpiredForUser} from "./PhasedRollOut";
import Modal from './Modal';
interface MatrixClientCreds {
homeserverUrl: string,
@ -116,6 +118,14 @@ class MatrixClientPeg {
await this.matrixClient.initCrypto();
}
} catch (e) {
if (e.name === 'InvalidCryptoStoreError') {
// The js-sdk found a crypto DB too new for it to use
const CryptoStoreTooNewDialog =
sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog");
Modal.createDialog(CryptoStoreTooNewDialog, {
host: window.location.host,
});
}
// this can happen for a number of reasons, the most likely being
// that the olm library was missing. It's not fatal.
console.warn("Unable to initialise e2e: " + e);

View file

@ -23,6 +23,7 @@ import PropTypes from 'prop-types';
import Analytics from './Analytics';
import sdk from './index';
import dis from './dispatcher';
import { _t } from './languageHandler';
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
@ -32,15 +33,15 @@ const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
*/
const AsyncWrapper = React.createClass({
propTypes: {
/** A function which takes a 'callback' argument which it will call
* with the real component once it loads.
/** A promise which resolves with the real component
*/
loader: PropTypes.func.isRequired,
prom: PropTypes.object.isRequired,
},
getInitialState: function() {
return {
component: null,
error: null,
};
},
@ -49,14 +50,18 @@ const AsyncWrapper = React.createClass({
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
console.log('Starting load of AsyncWrapper for modal');
this.props.loader((e) => {
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
console.log('AsyncWrapper load completed with '+e.displayName);
this.props.prom.then((result) => {
if (this._unmounted) {
return;
}
this.setState({component: e});
// Take the 'default' member if it's there, then we support
// passing in just an import()ed module, since ES6 async import
// always returns a module *namespace*.
const component = result.default ? result.default : result;
this.setState({component});
}).catch((e) => {
console.warn('AsyncWrapper promise failed', e);
this.setState({error: e});
});
},
@ -64,11 +69,27 @@ const AsyncWrapper = React.createClass({
this._unmounted = true;
},
_onWrapperCancelClick: function() {
this.props.onFinished(false);
},
render: function() {
const {loader, ...otherProps} = this.props;
if (this.state.component) {
const Component = this.state.component;
return <Component {...otherProps} />;
} else if (this.state.error) {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <BaseDialog onFinished={this.props.onFinished}
title={_t("Error")}
>
{_t("Unable to load! Check your network connectivity and try again.")}
<DialogButtons primaryButton={_t("Dismiss")}
onPrimaryButtonClick={this._onWrapperCancelClick}
hasCancel={false}
/>
</BaseDialog>;
} else {
// show a spinner until the component is loaded.
const Spinner = sdk.getComponent("elements.Spinner");
@ -115,7 +136,7 @@ class ModalManager {
}
createDialog(Element, ...rest) {
return this.createDialogAsync((cb) => {cb(Element);}, ...rest);
return this.createDialogAsync(new Promise(resolve => resolve(Element)), ...rest);
}
createTrackedDialogAsync(analyticsAction, analyticsInfo, ...rest) {
@ -133,9 +154,8 @@ class ModalManager {
* require(['<module>'], cb);
* }
*
* @param {Function} loader a function which takes a 'callback' argument,
* which it should call with a React component which will be displayed as
* the modal view.
* @param {Promise} prom a promise which resolves with a React component
* which will be displayed as the modal view.
*
* @param {Object} props properties to pass to the displayed
* component. (We will also pass an 'onFinished' property.)
@ -147,7 +167,7 @@ class ModalManager {
* Also, when closed, all modals will be removed
* from the stack.
*/
createDialogAsync(loader, props, className, isPriorityModal) {
createDialogAsync(prom, props, className, isPriorityModal) {
const self = this;
const modal = {};
@ -178,7 +198,7 @@ class ModalManager {
// FIXME: If a dialog uses getDefaultProps it clobbers the onFinished
// property set here so you can't close the dialog from a button click!
modal.elem = (
<AsyncWrapper key={modalCount} loader={loader} {...props}
<AsyncWrapper key={modalCount} prom={prom} {...props}
onFinished={closeDialog} />
);
modal.onFinished = props ? props.onFinished : null;

View file

@ -45,7 +45,7 @@ export async function startAnyRegistrationFlow(options) {
// caution though.
const hasIlagFlow = flows.some((flow) => {
return flow.stages.every((stage) => {
return ['m.login.dummy', 'm.login.recaptcha'].includes(stage);
return ['m.login.dummy', 'm.login.recaptcha', 'm.login.terms'].includes(stage);
});
});

View file

@ -24,6 +24,8 @@ import sdk from './index';
import {_t, _td} from './languageHandler';
import Modal from './Modal';
import SettingsStore, {SettingLevel} from './settings/SettingsStore';
import {MATRIXTO_URL_PATTERN} from "./linkify-matrix";
import * as querystring from "querystring";
class Command {
@ -153,11 +155,24 @@ export const CommandMap = {
description: _td('Joins room with given alias'),
runFn: function(roomId, args) {
if (args) {
const matches = args.match(/^(\S+)$/);
if (matches) {
let roomAlias = matches[1];
if (roomAlias[0] !== '#') return reject(this.getUsage());
// Note: we support 2 versions of this command. The first is
// the public-facing one for most users and the other is a
// power-user edition where someone may join via permalink or
// room ID with optional servers. Practically, this results
// in the following variations:
// /join #example:example.org
// /join !example:example.org
// /join !example:example.org altserver.com elsewhere.ca
// /join https://matrix.to/#/!example:example.org?via=altserver.com
// The command also supports event permalinks transparently:
// /join https://matrix.to/#/!example:example.org/$something:example.org
// /join https://matrix.to/#/!example:example.org/$something:example.org?via=altserver.com
const params = args.split(' ');
if (params.length < 1) return reject(this.getUsage());
const matrixToMatches = params[0].match(MATRIXTO_URL_PATTERN);
if (params[0][0] === '#') {
let roomAlias = params[0];
if (!roomAlias.includes(':')) {
roomAlias += ':' + MatrixClientPeg.get().getDomain();
}
@ -167,7 +182,65 @@ export const CommandMap = {
room_alias: roomAlias,
auto_join: true,
});
return success();
} else if (params[0][0] === '!') {
const roomId = params[0];
const viaServers = params.splice(0);
dis.dispatch({
action: 'view_room',
room_id: roomId,
opts: {
// These are passed down to the js-sdk's /join call
server_name: viaServers,
},
auto_join: true,
});
return success();
} else if (matrixToMatches) {
let entity = matrixToMatches[1];
let eventId = null;
let viaServers = [];
if (entity[0] !== '!' && entity[0] !== '#') return reject(this.getUsage());
if (entity.indexOf('?') !== -1) {
const parts = entity.split('?');
entity = parts[0];
const parsed = querystring.parse(parts[1]);
viaServers = parsed["via"];
if (typeof viaServers === 'string') viaServers = [viaServers];
}
// We quietly support event ID permalinks too
if (entity.indexOf('/$') !== -1) {
const parts = entity.split("/$");
entity = parts[0];
eventId = `$${parts[1]}`;
}
const dispatch = {
action: 'view_room',
auto_join: true,
};
if (entity[0] === '!') dispatch["room_id"] = entity;
else dispatch["room_alias"] = entity;
if (eventId) {
dispatch["event_id"] = eventId;
dispatch["highlighted"] = true;
}
if (viaServers) {
dispatch["opts"] = {
// These are passed down to the js-sdk's /join call
server_name: viaServers,
};
}
dis.dispatch(dispatch);
return success();
}
}
@ -492,6 +565,7 @@ export const CommandMap = {
const aliases = {
j: "join",
newballsplease: "discardsession",
goto: "join", // because it handles event permalinks magically
};

View file

@ -0,0 +1,460 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import sdk from '../../../../index';
import MatrixClientPeg from '../../../../MatrixClientPeg';
import FileSaver from 'file-saver';
import { _t, _td } from '../../../../languageHandler';
const PHASE_PASSPHRASE = 0;
const PHASE_PASSPHRASE_CONFIRM = 1;
const PHASE_SHOWKEY = 2;
const PHASE_KEEPITSAFE = 3;
const PHASE_BACKINGUP = 4;
const PHASE_DONE = 5;
const PHASE_OPTOUT_CONFIRM = 6;
// XXX: copied from ShareDialog: factor out into utils
function selectText(target) {
const range = document.createRange();
range.selectNodeContents(target);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
/**
* Walks the user through the process of creating an e2e key backup
* on the server.
*/
export default React.createClass({
getInitialState: function() {
return {
phase: PHASE_PASSPHRASE,
passPhrase: '',
passPhraseConfirm: '',
copied: false,
downloaded: false,
};
},
componentWillMount: function() {
this._recoveryKeyNode = null;
this._keyBackupInfo = null;
},
_collectRecoveryKeyNode: function(n) {
this._recoveryKeyNode = n;
},
_onCopyClick: function() {
selectText(this._recoveryKeyNode);
const successful = document.execCommand('copy');
if (successful) {
this.setState({
copied: true,
phase: PHASE_KEEPITSAFE,
});
}
},
_onDownloadClick: function() {
const blob = new Blob([this._keyBackupInfo.recovery_key], {
type: 'text/plain;charset=us-ascii',
});
FileSaver.saveAs(blob, 'recovery-key.txt');
this.setState({
downloaded: true,
phase: PHASE_KEEPITSAFE,
});
},
_createBackup: function() {
this.setState({
phase: PHASE_BACKINGUP,
error: null,
});
this._createBackupPromise = MatrixClientPeg.get().createKeyBackupVersion(
this._keyBackupInfo,
).then((info) => {
return MatrixClientPeg.get().backupAllGroupSessions(info.version);
}).then(() => {
this.setState({
phase: PHASE_DONE,
});
}).catch(e => {
console.log("Error creating key backup", e);
this.setState({
error: e,
});
});
},
_onCancel: function() {
this.props.onFinished(false);
},
_onDone: function() {
this.props.onFinished(true);
},
_onOptOutClick: function() {
this.setState({phase: PHASE_OPTOUT_CONFIRM});
},
_onSetUpClick: function() {
this.setState({phase: PHASE_PASSPHRASE});
},
_onSkipPassPhraseClick: async function() {
this._keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion();
this.setState({
copied: false,
phase: PHASE_SHOWKEY,
});
},
_onPassPhraseNextClick: function() {
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
},
_onPassPhraseKeyPress: function(e) {
if (e.key === 'Enter' && this._passPhraseIsValid()) {
this._onPassPhraseNextClick();
}
},
_onPassPhraseConfirmNextClick: async function() {
this._keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion(this.state.passPhrase);
this.setState({
copied: false,
phase: PHASE_SHOWKEY,
});
},
_onPassPhraseConfirmKeyPress: function(e) {
if (e.key === 'Enter' && this.state.passPhrase === this.state.passPhraseConfirm) {
this._onPassPhraseConfirmNextClick();
}
},
_onSetAgainClick: function() {
this.setState({
passPhrase: '',
passPhraseConfirm: '',
phase: PHASE_PASSPHRASE,
});
},
_onKeepItSafeGotItClick: function() {
this.setState({
phase: PHASE_SHOWKEY,
});
},
_onPassPhraseChange: function(e) {
this.setState({
passPhrase: e.target.value,
});
},
_onPassPhraseConfirmChange: function(e) {
this.setState({
passPhraseConfirm: e.target.value,
});
},
_passPhraseIsValid: function() {
return this.state.passPhrase !== '';
},
_renderPhasePassPhrase: function() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return <div>
<p>{_t("Secure your encrypted message history with a Recovery Passphrase.")}</p>
<p>{_t("You'll need it if you log out or lose access to this device.")}</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<input type="password"
onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase}
className="mx_CreateKeyBackupDialog_passPhraseInput"
placeholder={_t("Enter a passphrase...")}
/>
</div>
<DialogButtons primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseNextClick}
hasCancel={false}
disabled={!this._passPhraseIsValid()}
/>
<p>{_t(
"If you don't want encrypted message history to be availble on other devices, "+
"<button>opt out</button>.",
{},
{
button: sub => <AccessibleButton
element="span"
className="mx_linkButton"
onClick={this._onOptOutClick}
>
{sub}
</AccessibleButton>,
},
)}</p>
<p>{_t(
"Or, if you don't want to create a Recovery Passphrase, skip this step and "+
"<button>download a recovery key</button>.",
{},
{
button: sub => <AccessibleButton
element="span"
className="mx_linkButton"
onClick={this._onSkipPassPhraseClick}
>
{sub}
</AccessibleButton>,
},
)}</p>
</div>;
},
_renderPhasePassPhraseConfirm: function() {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let passPhraseMatch = null;
if (this.state.passPhraseConfirm.length > 0) {
let matchText;
if (this.state.passPhraseConfirm === this.state.passPhrase) {
matchText = _t("That matches!");
} else {
matchText = _t("That doesn't match.");
}
passPhraseMatch = <div className="mx_CreateKeyBackupDialog_passPhraseMatch">
<div>{matchText}</div>
<div>
<AccessibleButton element="span" className="mx_linkButton" onClick={this._onSetAgainClick}>
{_t("Go back to set it again.")}
</AccessibleButton>
</div>
</div>;
}
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<p>{_t(
"Type in your Recovery Passphrase to confirm you remember it. " +
"If it helps, add it to your password manager or store it " +
"somewhere safe.",
)}</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
{passPhraseMatch}
<div>
<input type="password"
onChange={this._onPassPhraseConfirmChange}
onKeyPress={this._onPassPhraseConfirmKeyPress}
value={this.state.passPhraseConfirm}
className="mx_CreateKeyBackupDialog_passPhraseInput"
placeholder={_t("Repeat your passphrase...")}
autoFocus={true}
/>
</div>
</div>
<DialogButtons primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseConfirmNextClick}
hasCancel={false}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
/>
</div>;
},
_renderPhaseShowKey: function() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<p>{_t("Make a copy of this Recovery Key and keep it safe.")}</p>
<p>{_t("As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.")}</p>
<p className="mx_CreateKeyBackupDialog_primaryContainer">
<div>{_t("Your Recovery Key")}</div>
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
<button onClick={this._onCopyClick}>
{_t("Copy to clipboard")}
</button>
{
// FIXME REDESIGN: buttons should be adjacent but insufficient room in current design
}
<br /><br />
<button onClick={this._onDownloadClick}>
{_t("Download")}
</button>
</div>
<div className="mx_CreateKeyBackupDialog_recoveryKey">
<code ref={this._collectRecoveryKeyNode}>{this._keyBackupInfo.recovery_key}</code>
</div>
</p>
<br />
<DialogButtons primaryButton={_t("I've made a copy")}
onPrimaryButtonClick={this._createBackup}
hasCancel={false}
disabled={!this.state.copied && !this.state.downloaded}
/>
</div>;
},
_renderPhaseKeepItSafe: function() {
let introText;
if (this.state.copied) {
introText = _t(
"Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:",
{}, {b: s => <b>{s}</b>},
);
} else if (this.state.downloaded) {
introText = _t(
"Your Recovery Key is in your <b>Downloads</b> folder.",
{}, {b: s => <b>{s}</b>},
);
}
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
{introText}
<ul>
<li>{_t("<b>Print it</b> and store it somewhere safe", {}, {b: s => <b>{s}</b>})}</li>
<li>{_t("<b>Save it</b> on a USB key or backup drive", {}, {b: s => <b>{s}</b>})}</li>
<li>{_t("<b>Copy it</b> to your personal cloud storage", {}, {b: s => <b>{s}</b>})}</li>
</ul>
<DialogButtons primaryButton={_t("Got it")}
onPrimaryButtonClick={this._onKeepItSafeGotItClick}
hasCancel={false}
/>
</div>;
},
_renderBusyPhase: function(text) {
const Spinner = sdk.getComponent('views.elements.Spinner');
return <div>
<p>{_t(text)}</p>
<Spinner />
</div>;
},
_renderPhaseDone: function() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<p>{_t("Backup created")}</p>
<p>{_t("Your encryption keys are now being backed up to your Homeserver.")}</p>
<DialogButtons primaryButton={_t('Close')}
onPrimaryButtonClick={this._onDone}
hasCancel={false}
/>
</div>;
},
_renderPhaseOptOutConfirm: function() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
{_t(
"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.",
)}
<DialogButtons primaryButton={_t('Set up Secure Message Recovery')}
onPrimaryButtonClick={this._onSetUpClick}
hasCancel={false}
>
<button onClick={this._onCancel}>I understand, continue without</button>
</DialogButtons>
</div>;
},
_titleForPhase: function(phase) {
switch (phase) {
case PHASE_PASSPHRASE:
return _t('Create a Recovery Passphrase');
case PHASE_PASSPHRASE_CONFIRM:
return _t('Confirm Recovery Passphrase');
case PHASE_OPTOUT_CONFIRM:
return _t('Warning!');
case PHASE_SHOWKEY:
return _t('Recovery Key');
case PHASE_KEEPITSAFE:
return _t('Keep it safe');
case PHASE_BACKINGUP:
return _t('Backing up...');
default:
return _t("Create Key Backup");
}
},
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
let content;
if (this.state.error) {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
content = <div>
<p>{_t("Unable to create key backup")}</p>
<div className="mx_Dialog_buttons">
<DialogButtons primaryButton={_t('Retry')}
onPrimaryButtonClick={this._createBackup}
hasCancel={true}
onCancel={this._onCancel}
/>
</div>
</div>;
} else {
switch (this.state.phase) {
case PHASE_PASSPHRASE:
content = this._renderPhasePassPhrase();
break;
case PHASE_PASSPHRASE_CONFIRM:
content = this._renderPhasePassPhraseConfirm();
break;
case PHASE_SHOWKEY:
content = this._renderPhaseShowKey();
break;
case PHASE_KEEPITSAFE:
content = this._renderPhaseKeepItSafe();
break;
case PHASE_BACKINGUP:
content = this._renderBusyPhase(_td("Backing up..."));
break;
case PHASE_DONE:
content = this._renderPhaseDone();
break;
case PHASE_OPTOUT_CONFIRM:
content = this._renderPhaseOptOutConfirm();
break;
}
}
return (
<BaseDialog className='mx_CreateKeyBackupDialog'
onFinished={this.props.onFinished}
title={this._titleForPhase(this.state.phase)}
hasCancel={[PHASE_DONE].includes(this.state.phase)}
>
<div>
{content}
</div>
</BaseDialog>
);
},
});

View file

@ -23,6 +23,8 @@ import request from 'browser-request';
import { _t } from '../../languageHandler';
import sanitizeHtml from 'sanitize-html';
import sdk from '../../index';
import { MatrixClient } from 'matrix-js-sdk';
import dis from '../../dispatcher';
class HomePage extends React.Component {
static displayName = 'HomePage';
@ -37,6 +39,10 @@ class HomePage extends React.Component {
homePageUrl: PropTypes.string,
};
static contextTypes = {
matrixClient: PropTypes.instanceOf(MatrixClient),
};
state = {
iframeSrc: '',
page: '',
@ -85,10 +91,47 @@ class HomePage extends React.Component {
this._unmounted = true;
}
onLoginClick() {
dis.dispatch({ action: 'start_login' });
}
onRegisterClick() {
dis.dispatch({ action: 'start_registration' });
}
render() {
let guestWarning = "";
if (this.context.matrixClient.isGuest()) {
guestWarning = (
<div className="mx_HomePage_guest_warning">
<img src="img/warning.svg" width="24" height="23" />
<div>
<div>
{ _t("You are currently using Riot anonymously as a guest.") }
</div>
<div>
{ _t(
'If you would like to create a Matrix account you can <a>register</a> now.',
{},
{ 'a': (sub) => <a href="#" onClick={this.onRegisterClick}>{ sub }</a> },
) }
</div>
<div>
{ _t(
'If you already have a Matrix account you can <a>log in</a> instead.',
{},
{ 'a': (sub) => <a href="#" onClick={this.onLoginClick}>{ sub }</a> },
) }
</div>
</div>
</div>
);
}
if (this.state.iframeSrc) {
return (
<div className="mx_HomePage">
{ guestWarning }
<iframe src={ this.state.iframeSrc } />
</div>
);
@ -96,6 +139,7 @@ class HomePage extends React.Component {
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
return (
<GeminiScrollbarWrapper autoshow={true} className="mx_HomePage">
{ guestWarning }
<div className="mx_HomePage_body" dangerouslySetInnerHTML={{ __html: this.state.page }}>
</div>
</GeminiScrollbarWrapper>

View file

@ -68,6 +68,11 @@ export default React.createClass({
// If true, poll to see if the auth flow has been completed
// out-of-band
poll: PropTypes.bool,
// If true, components will be told that the 'Continue' button
// is managed by some other party and should not be managed by
// the component itself.
continueIsManaged: PropTypes.bool,
},
getInitialState: function() {
@ -128,6 +133,12 @@ export default React.createClass({
}
},
tryContinue: function() {
if (this.refs.stageComponent && this.refs.stageComponent.tryContinue) {
this.refs.stageComponent.tryContinue();
}
},
_authStateUpdated: function(stageType, stageState) {
const oldStage = this.state.authStage;
this.setState({
@ -192,6 +203,7 @@ export default React.createClass({
fail={this._onAuthStageFailed}
setEmailSid={this._setEmailSid}
makeRegistrationUrl={this.props.makeRegistrationUrl}
showContinue={!this.props.continueIsManaged}
/>
);
},

View file

@ -1375,6 +1375,7 @@ export default React.createClass({
cli.on("crypto.roomKeyRequestCancellation", (req) => {
krh.handleKeyRequestCancellation(req);
});
cli.on("Room", (room) => {
if (MatrixClientPeg.get().isCryptoEnabled()) {
const blacklistEnabled = SettingsStore.getValueAt(

View file

@ -590,23 +590,21 @@ module.exports = React.createClass({
},
_onExportE2eKeysClicked: function() {
Modal.createTrackedDialogAsync('Export E2E Keys', '', (cb) => {
require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
});
Modal.createTrackedDialogAsync('Export E2E Keys', '',
import('../../async-components/views/dialogs/ExportE2eKeysDialog'),
{
matrixClient: MatrixClientPeg.get(),
},
);
},
_onImportE2eKeysClicked: function() {
Modal.createTrackedDialogAsync('Import E2E Keys', '', (cb) => {
require.ensure(['../../async-components/views/dialogs/ImportE2eKeysDialog'], () => {
cb(require('../../async-components/views/dialogs/ImportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
});
Modal.createTrackedDialogAsync('Import E2E Keys', '',
import('../../async-components/views/dialogs/ImportE2eKeysDialog'),
{
matrixClient: MatrixClientPeg.get(),
},
);
},
_renderGroupSettings: function() {
@ -740,6 +738,16 @@ module.exports = React.createClass({
</div>
);
}
let keyBackupSection;
if (SettingsStore.isFeatureEnabled("feature_keybackup")) {
const KeyBackupPanel = sdk.getComponent('views.settings.KeyBackupPanel');
keyBackupSection = <div className="mx_UserSettings_section">
<h3>{ _t("Key Backup") }</h3>
<KeyBackupPanel />
</div>;
}
return (
<div>
<h3>{ _t("Cryptography") }</h3>
@ -755,6 +763,7 @@ module.exports = React.createClass({
<div className="mx_UserSettings_section">
{ CRYPTO_SETTINGS.map( this._renderDeviceSetting ) }
</div>
{keyBackupSection}
</div>
);
},

View file

@ -121,13 +121,12 @@ module.exports = React.createClass({
},
_onExportE2eKeysClicked: function() {
Modal.createTrackedDialogAsync('Export E2E Keys', 'Forgot Password', (cb) => {
require.ensure(['../../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
cb(require('../../../async-components/views/dialogs/ExportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
});
Modal.createTrackedDialogAsync('Export E2E Keys', 'Forgot Password',
import('../../../async-components/views/dialogs/ExportE2eKeysDialog'),
{
matrixClient: MatrixClientPeg.get(),
},
);
},
onInputChanged: function(stateKey, ev) {

View file

@ -84,7 +84,10 @@ module.exports = React.createClass({
// letting you do that login type
this._stepRendererMap = {
'm.login.password': this._renderPasswordStep,
'm.login.cas': this._renderCasStep,
// CAS and SSO are the same thing, modulo the url we link to
'm.login.cas': () => this._renderSsoStep(this._loginLogic.getSsoLoginUrl("cas")),
'm.login.sso': () => this._renderSsoStep(this._loginLogic.getSsoLoginUrl("sso")),
};
this._initLoginLogic();
@ -186,10 +189,6 @@ module.exports = React.createClass({
}).done();
},
onCasLogin: function() {
this._loginLogic.redirectToCas();
},
_onLoginAsGuestClick: function() {
const self = this;
self.setState({
@ -403,10 +402,9 @@ module.exports = React.createClass({
);
},
_renderCasStep: function() {
const CasLogin = sdk.getComponent('login.CasLogin');
_renderSsoStep: function(url) {
return (
<CasLogin onSubmit={this.onCasLogin} />
<a href={url} className="mx_Login_sso_link">{ _t('Sign in with single sign-on') }</a>
);
},

View file

@ -0,0 +1,71 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';
export default (props) => {
const _onLogoutClicked = () => {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Logout e2e db too new', '', QuestionDialog, {
title: _t("Sign out"),
description: _t(
"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",
),
button: _t("Sign out"),
focus: false,
onFinished: (doLogout) => {
if (doLogout) {
dis.dispatch({action: 'logout'});
props.onFinished();
}
},
});
};
const description =
_t("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. ",
{host: props.host},
);
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return (<BaseDialog className="mx_CryptoStoreTooNewDialog"
contentId='mx_Dialog_content'
title={_t("Incompatible Database")}
hasCancel={false}
onFinished={props.onFinished}
>
<div className="mx_Dialog_content" id='mx_Dialog_content'>
{ description }
</div>
<DialogButtons primaryButton={_t('Continue With Encryption Disabled')}
hasCancel={false}
onPrimaryButtonClick={props.onFinished}
>
<button onClick={_onLogoutClicked} >
{ _t('Sign out') }
</button>
</DialogButtons>
</BaseDialog>);
};

View file

@ -101,6 +101,9 @@ export default React.createClass({
},
onSubmit: function(ev) {
if (this.refs.uiAuth) {
this.refs.uiAuth.tryContinue();
}
this.setState({
doingUIAuth: true,
});
@ -217,6 +220,8 @@ export default React.createClass({
onAuthFinished={this._onUIAuthFinished}
inputs={{}}
poll={true}
ref="uiAuth"
continueIsManaged={true}
/>;
}
const inputClasses = classnames({

View file

@ -0,0 +1,309 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import sdk from '../../../../index';
import MatrixClientPeg from '../../../../MatrixClientPeg';
import Modal from '../../../../Modal';
import { _t } from '../../../../languageHandler';
/**
* Dialog for restoring e2e keys from a backup and the user's recovery key
*/
export default React.createClass({
getInitialState: function() {
return {
backupInfo: null,
loading: false,
loadError: null,
restoreError: null,
recoveryKey: "",
recoverInfo: null,
recoveryKeyValid: false,
forceRecoveryKey: false,
passPhrase: '',
};
},
componentWillMount: function() {
this._loadBackupStatus();
},
_onCancel: function() {
this.props.onFinished(false);
},
_onDone: function() {
this.props.onFinished(true);
},
_onUseRecoveryKeyClick: function() {
this.setState({
forceRecoveryKey: true,
});
},
_onResetRecoveryClick: function() {
this.props.onFinished(false);
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
import('../../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
{
onFinished: () => {
this._loadBackupStatus();
},
},
);
},
_onRecoveryKeyChange: function(e) {
this.setState({
recoveryKey: e.target.value,
recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value),
});
},
_onPassPhraseNext: async function() {
this.setState({
loading: true,
restoreError: null,
});
try {
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithPassword(
this.state.passPhrase, undefined, undefined, this.state.backupInfo.version,
);
this.setState({
loading: false,
recoverInfo,
});
} catch (e) {
console.log("Error restoring backup", e);
this.setState({
loading: false,
restoreError: e,
});
}
},
_onRecoveryKeyNext: async function() {
this.setState({
loading: true,
restoreError: null,
});
try {
const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithRecoveryKey(
this.state.recoveryKey, undefined, undefined, this.state.backupInfo.version,
);
this.setState({
loading: false,
recoverInfo,
});
} catch (e) {
console.log("Error restoring backup", e);
this.setState({
loading: false,
restoreError: e,
});
}
},
_onPassPhraseChange: function(e) {
this.setState({
passPhrase: e.target.value,
});
},
_onPassPhraseKeyPress: function(e) {
if (e.key === "Enter") {
this._onPassPhraseNext();
}
},
_onRecoveryKeyKeyPress: function(e) {
if (e.key === "Enter" && this.state.recoveryKeyValid) {
this._onRecoveryKeyNext();
}
},
_loadBackupStatus: async function() {
this.setState({
loading: true,
loadError: null,
});
try {
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
this.setState({
loadError: null,
loading: false,
backupInfo,
});
} catch (e) {
console.log("Error loading backup status", e);
this.setState({
loadError: e,
loading: false,
});
}
},
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const Spinner = sdk.getComponent("elements.Spinner");
const backupHasPassphrase = (
this.state.backupInfo &&
this.state.backupInfo.auth_data &&
this.state.backupInfo.auth_data.private_key_salt &&
this.state.backupInfo.auth_data.private_key_iterations
);
let content;
let title;
if (this.state.loading) {
title = _t("Loading...");
content = <Spinner />;
} else if (this.state.loadError) {
title = _t("Error");
content = _t("Unable to load backup status");
} else if (this.state.restoreError) {
title = _t("Error");
content = _t("Unable to restore backup");
} else if (this.state.backupInfo === null) {
title = _t("Error");
content = _t("No backup found!");
} else if (this.state.recoverInfo) {
title = _t("Backup Restored");
let failedToDecrypt;
if (this.state.recoverInfo.total > this.state.recoverInfo.imported) {
failedToDecrypt = <p>{_t(
"Failed to decrypt %(failedCount)s sessions!",
{failedCount: this.state.recoverInfo.total - this.state.recoverInfo.imported},
)}</p>;
}
content = <div>
<p>{_t("Restored %(sessionCount)s session keys", {sessionCount: this.state.recoverInfo.imported})}</p>
{failedToDecrypt}
</div>;
} else if (backupHasPassphrase && !this.state.forceRecoveryKey) {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
title = _t("Enter Recovery Passphrase");
content = <div>
{_t(
"Access your secure message history and set up secure " +
"messaging by entering your recovery passphrase.",
)}<br />
<div className="mx_RestoreKeyBackupDialog_primaryContainer">
<input type="password"
className="mx_RestoreKeyBackupDialog_passPhraseInput"
onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase}
autoFocus={true}
/>
<DialogButtons primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseNext}
hasCancel={true}
onCancel={this._onCancel}
focus={false}
/>
</div>
{_t(
"If you've forgotten your recovery passphrase you can "+
"<button1>use your recovery key</button1> or " +
"<button2>set up new recovery options</button2>"
, {}, {
button1: s => <AccessibleButton className="mx_linkButton"
element="span"
onClick={this._onUseRecoveryKeyClick}
>
{s}
</AccessibleButton>,
button2: s => <AccessibleButton className="mx_linkButton"
element="span"
onClick={this._onResetRecoveryClick}
>
{s}
</AccessibleButton>,
})}
</div>;
} else {
title = _t("Enter Recovery Key");
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let keyStatus;
if (this.state.recoveryKey.length === 0) {
keyStatus = <div className="mx_RestoreKeyBackupDialog_keyStatus"></div>;
} else if (this.state.recoveryKeyValid) {
keyStatus = <div className="mx_RestoreKeyBackupDialog_keyStatus">
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")}
</div>;
} else {
keyStatus = <div className="mx_RestoreKeyBackupDialog_keyStatus">
{"\uD83D\uDC4E "}{_t("Not a valid recovery key")}
</div>;
}
content = <div>
{_t(
"Access your secure message history and set up secure " +
"messaging by entering your recovery key.",
)}<br />
<div className="mx_RestoreKeyBackupDialog_primaryContainer">
<input className="mx_RestoreKeyBackupDialog_recoveryKeyInput"
onChange={this._onRecoveryKeyChange}
onKeyPress={this._onRecoveryKeyKeyPress}
value={this.state.recoveryKey}
autoFocus={true}
/>
{keyStatus}
<DialogButtons primaryButton={_t('Next')}
onPrimaryButtonClick={this._onRecoveryKeyNext}
hasCancel={true}
onCancel={this._onCancel}
focus={false}
primaryDisabled={!this.state.recoveryKeyValid}
/>
</div>
{_t(
"If you've forgotten your recovery passphrase you can "+
"<button>set up new recovery options</button>"
, {}, {
button: s => <AccessibleButton className="mx_linkButton"
element="span"
onClick={this._onResetRecoveryClick}
>
{s}
</AccessibleButton>,
})}
</div>;
}
return (
<BaseDialog className='mx_RestoreKeyBackupDialog'
onFinished={this.props.onFinished}
title={title}
>
<div>
{content}
</div>
</BaseDialog>
);
},
});

View file

@ -43,7 +43,11 @@ module.exports = React.createClass({
focus: PropTypes.bool,
// disables the primary and cancel buttons
disabled: PropTypes.bool,
// disables only the primary button
primaryDisabled: PropTypes.bool,
},
getDefaultProps: function() {
@ -73,9 +77,9 @@ module.exports = React.createClass({
{ cancelButton }
{ this.props.children }
<button className={primaryButtonClassName}
onClick={this.props.onPrimaryButtonClick}
autoFocus={this.props.focus}
disabled={this.props.disabled}
onClick={this.props.onPrimaryButtonClick}
autoFocus={this.props.focus}
disabled={this.props.disabled || this.props.primaryDisabled}
>
{ this.props.primaryButton }
</button>

View file

@ -222,6 +222,7 @@ export const TermsAuthEntry = React.createClass({
stageParams: PropTypes.object.isRequired,
errorText: PropTypes.string,
busy: PropTypes.bool,
showContinue: PropTypes.bool,
},
componentWillMount: function() {
@ -275,19 +276,30 @@ export const TermsAuthEntry = React.createClass({
});
},
_trySubmit: function(policyId) {
tryContinue: function() {
this._trySubmit();
},
_togglePolicy: function(policyId) {
const newToggles = {};
let allChecked = true;
for (const policy of this.state.policies) {
let checked = this.state.toggledPolicies[policy.id];
if (policy.id === policyId) checked = !checked;
newToggles[policy.id] = checked;
}
this.setState({"toggledPolicies": newToggles});
},
_trySubmit: function() {
let allChecked = true;
for (const policy of this.state.policies) {
let checked = this.state.toggledPolicies[policy.id];
allChecked = allChecked && checked;
}
this.setState({"toggledPolicies": newToggles});
if (allChecked) this.props.submitAuthDict({type: TermsAuthEntry.LOGIN_TYPE});
else this.setState({errorText: _t("Please review and accept all of the homeserver's policies")});
},
render: function() {
@ -303,27 +315,35 @@ export const TermsAuthEntry = React.createClass({
allChecked = allChecked && checked;
checkboxes.push(
<label key={"policy_checkbox_" + policy.id}>
<input type="checkbox" onClick={() => this._trySubmit(policy.id)} checked={checked} />
<label key={"policy_checkbox_" + policy.id} className="mx_InteractiveAuthEntryComponents_termsPolicy">
<input type="checkbox" onClick={() => this._togglePolicy(policy.id)} checked={checked} />
<a href={policy.url} target="_blank" rel="noopener">{ policy.name }</a>
</label>,
);
}
let errorSection;
if (this.props.errorText) {
if (this.props.errorText || this.state.errorText) {
errorSection = (
<div className="error" role="alert">
{ this.props.errorText }
{ this.props.errorText || this.state.errorText }
</div>
);
}
let submitButton;
if (this.props.showContinue !== false) {
// XXX: button classes
submitButton = <button className="mx_InteractiveAuthEntryComponents_termsSubmit mx_UserSettings_button"
onClick={this._trySubmit} disabled={!allChecked}>{_t("Accept")}</button>;
}
return (
<div>
<p>{_t("Please review and accept the policies of this homeserver:")}</p>
{ checkboxes }
{ errorSection }
{ submitButton }
</div>
);
},

View file

@ -278,6 +278,7 @@ export default class MImageBody extends React.Component {
let img = null;
let placeholder = null;
let gifLabel = null;
// e2e image hasn't been decrypted yet
if (content.file !== undefined && this.state.decryptedUrl === null) {
@ -302,11 +303,14 @@ export default class MImageBody extends React.Component {
onMouseLeave={this.onImageLeave} />;
}
if (this._isGif() && !SettingsStore.getValue("autoplayGifsAndVideos") && !this.state.hover) {
gifLabel = <p className="mx_MImageBody_gifLabel">GIF</p>;
}
const thumbnail = (
<div className="mx_MImageBody_thumbnail_container" style={{ maxHeight: maxHeight + "px" }} >
{ /* Calculate aspect ratio, using %padding will size _container correctly */ }
<div style={{ paddingBottom: (100 * infoHeight / infoWidth) + '%' }} />
{ showPlaceholder &&
<div className="mx_MImageBody_thumbnail" style={{
// Constrain width here so that spinner appears central to the loaded thumbnail
@ -320,6 +324,7 @@ export default class MImageBody extends React.Component {
<div style={{display: !showPlaceholder ? undefined : 'none'}}>
{ img }
{ gifLabel }
</div>
{ this.state.hover && this.getTooltip() }

View file

@ -416,11 +416,10 @@ module.exports = withMatrixClient(React.createClass({
onCryptoClicked: function(e) {
const event = this.props.mxEvent;
Modal.createTrackedDialogAsync('Encrypted Event Dialog', '', (cb) => {
require(['../../../async-components/views/dialogs/EncryptedEventDialog'], cb);
}, {
event: event,
});
Modal.createTrackedDialogAsync('Encrypted Event Dialog', '',
import('../../../async-components/views/dialogs/EncryptedEventDialog'),
{event},
);
},
onRequestKeysClick: function() {

View file

@ -179,13 +179,12 @@ module.exports = React.createClass({
},
_onExportE2eKeysClicked: function() {
Modal.createTrackedDialogAsync('Export E2E Keys', 'Change Password', (cb) => {
require.ensure(['../../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
cb(require('../../../async-components/views/dialogs/ExportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
});
Modal.createTrackedDialogAsync('Export E2E Keys', 'Change Password',
import('../../../async-components/views/dialogs/ExportE2eKeysDialog'),
{
matrixClient: MatrixClientPeg.get(),
},
);
},
onClickChange: function(ev) {

View file

@ -0,0 +1,242 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';
export default class KeyBackupPanel extends React.Component {
constructor(props) {
super(props);
this._startNewBackup = this._startNewBackup.bind(this);
this._deleteBackup = this._deleteBackup.bind(this);
this._verifyDevice = this._verifyDevice.bind(this);
this._onKeyBackupStatus = this._onKeyBackupStatus.bind(this);
this._restoreBackup = this._restoreBackup.bind(this);
this._unmounted = false;
this.state = {
loading: true,
error: null,
backupInfo: null,
};
}
componentWillMount() {
this._loadBackupStatus();
MatrixClientPeg.get().on('crypto.keyBackupStatus', this._onKeyBackupStatus);
}
componentWillUnmount() {
this._unmounted = true;
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatus);
}
}
_onKeyBackupStatus() {
this._loadBackupStatus();
}
async _loadBackupStatus() {
this.setState({loading: true});
try {
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo);
if (this._unmounted) return;
this.setState({
backupInfo,
backupSigStatus,
loading: false,
});
} catch (e) {
console.log("Unable to fetch key backup status", e);
if (this._unmounted) return;
this.setState({
error: e,
loading: false,
});
return;
}
}
_startNewBackup() {
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
import('../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
{
onFinished: () => {
this._loadBackupStatus();
},
},
);
}
_deleteBackup() {
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
Modal.createTrackedDialog('Delete Backup', '', QuestionDialog, {
title: _t('Delete Backup'),
description: _t(
"Delete your backed up encryption keys from the server? " +
"You will no longer be able to use your recovery key to read encrypted message history",
),
button: _t('Delete backup'),
danger: true,
onFinished: (proceed) => {
if (!proceed) return;
this.setState({loading: true});
MatrixClientPeg.get().deleteKeyBackupVersion(this.state.backupInfo.version).then(() => {
this._loadBackupStatus();
});
},
});
}
_restoreBackup() {
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, {
});
}
_verifyDevice(e) {
const device = this.state.backupSigStatus.sigs[e.target.getAttribute('data-sigindex')].device;
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, {
userId: MatrixClientPeg.get().credentials.userId,
device: device,
onFinished: () => {
this._loadBackupStatus();
},
});
}
render() {
const Spinner = sdk.getComponent("elements.Spinner");
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
if (this.state.error) {
return (
<div className="error">
{_t("Unable to load key backup status")}
</div>
);
} else if (this.state.loading) {
return <Spinner />;
} else if (this.state.backupInfo) {
let clientBackupStatus;
if (MatrixClientPeg.get().getKeyBackupEnabled()) {
clientBackupStatus = _t("This device is uploading keys to this backup");
} else {
// XXX: display why and how to fix it
clientBackupStatus = _t(
"This device is <b>not</b> uploading keys to this backup", {},
{b: x => <b>{x}</b>},
);
}
let backupSigStatuses = this.state.backupSigStatus.sigs.map((sig, i) => {
const sigStatusSubstitutions = {
validity: sub =>
<span className={sig.valid ? 'mx_KeyBackupPanel_sigValid' : 'mx_KeyBackupPanel_sigInvalid'}>
{sub}
</span>,
verify: sub =>
<span className={sig.device.isVerified() ? 'mx_KeyBackupPanel_deviceVerified' : 'mx_KeyBackupPanel_deviceNotVerified'}>
{sub}
</span>,
device: sub => <span className="mx_KeyBackupPanel_deviceName">{sig.device.getDisplayName()}</span>,
};
let sigStatus;
if (sig.device.getFingerprint() === MatrixClientPeg.get().getDeviceEd25519Key()) {
sigStatus = _t(
"Backup has a <validity>valid</validity> signature from this device",
{}, sigStatusSubstitutions,
);
} else if (sig.valid && sig.device.isVerified()) {
sigStatus = _t(
"Backup has a <validity>valid</validity> signature from " +
"<verify>verified</verify> device <device>x</device>",
{}, sigStatusSubstitutions,
);
} else if (sig.valid && !sig.device.isVerified()) {
sigStatus = _t(
"Backup has a <validity>valid</validity> signature from " +
"<verify>unverified</verify> device <device></device>",
{}, sigStatusSubstitutions,
);
} else if (!sig.valid && sig.device.isVerified()) {
sigStatus = _t(
"Backup has an <validity>invalid</validity> signature from " +
"<verify>verified</verify> device <device></device>",
{}, sigStatusSubstitutions,
);
} else if (!sig.valid && !sig.device.isVerified()) {
sigStatus = _t(
"Backup has an <validity>invalid</validity> signature from " +
"<verify>unverified</verify> device <device></device>",
{}, sigStatusSubstitutions,
);
}
let verifyButton;
if (!sig.device.isVerified()) {
verifyButton = <div><br /><AccessibleButton className="mx_UserSettings_button"
onClick={this._verifyDevice} data-sigindex={i}>
{ _t("Verify...") }
</AccessibleButton></div>;
}
return <div key={i}>
{sigStatus}
{verifyButton}
</div>;
});
if (this.state.backupSigStatus.sigs.length === 0) {
backupSigStatuses = _t("Backup is not signed by any of your devices");
}
return <div>
{_t("Backup version: ")}{this.state.backupInfo.version}<br />
{_t("Algorithm: ")}{this.state.backupInfo.algorithm}<br />
{clientBackupStatus}<br />
<div>{backupSigStatuses}</div><br />
<br />
<AccessibleButton className="mx_UserSettings_button"
onClick={this._restoreBackup}>
{ _t("Restore backup") }
</AccessibleButton>&nbsp;&nbsp;&nbsp;
<AccessibleButton className="mx_UserSettings_button danger"
onClick={this._deleteBackup}>
{ _t("Delete backup") }
</AccessibleButton>
</div>;
} else {
return <div>
{_t("No backup is present")}<br /><br />
<AccessibleButton className="mx_UserSettings_button"
onClick={this._startNewBackup}>
{ _t("Start a new backup") }
</AccessibleButton>
</div>;
}
}
}

View file

@ -1291,5 +1291,13 @@
"Clear cache and resync": "Изчисти кеша и ресинхронизирай",
"Please accept all of the policies": "Моля, приемете всички политики",
"Please review and accept the policies of this homeserver:": "Моля, прегледайте и приемете политиките на този сървър:",
"Add some now": "Добави сега"
"Add some now": "Добави сега",
"Pin unread rooms to the top of the room list": "Закачане на непрочетени стаи най-отгоре в списъка",
"Pin rooms I'm mentioned in to the top of the room list": "Закачане на споменаващи ме стаи най-отгоре в списъка",
"Joining room...": "Влизане в стая...",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Вие сте администратор на тази общност. Няма да можете да се присъедините пак без покана от друг администратор.",
"Open Devtools": "Отвори инструментите за разработчици",
"Show developer tools": "Покажи инструментите за разработчици",
"If you would like to create a Matrix account you can <a>register</a> now.": "Ако искате да създадете Matrix акаунт, може да се <a>регистрирате</a> тук.",
"You are currently using Riot anonymously as a guest.": "В момента използвате Riot анонимно, като гост."
}

View file

@ -1291,5 +1291,14 @@
"Incompatible local cache": "Inkompatibler lokaler Zwischenspeicher",
"Clear cache and resync": "Zwischenspeicher löschen und erneut synchronisieren",
"Please accept all of the policies": "Bitte akzeptiere alle Bedingungen",
"Please review and accept the policies of this homeserver:": "Bitte sieh dir alle Bedingungen dieses Heimservers an und akzeptiere sie:"
"Please review and accept the policies of this homeserver:": "Bitte sieh dir alle Bedingungen dieses Heimservers an und akzeptiere sie:",
"Pin unread rooms to the top of the room list": "Ungelesene Räume oben an der Raumliste anheften",
"Pin rooms I'm mentioned in to the top of the room list": "Räume mit Erwähnungen oben an der Raumliste anheften",
"Joining room...": "Trete Raum bei...",
"Add some now": "Füge jetzt hinzu",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Du bist ein Administrator dieser Community. Du wirst nicht erneut hinzutreten können, wenn du nicht von einem anderen Administrator eingeladen wirst.",
"Open Devtools": "Öffne Entwickler-Werkzeuge",
"Show developer tools": "Zeige Entwickler-Werkzeuge",
"If you would like to create a Matrix account you can <a>register</a> now.": "Wenn du ein Matrix-Konto erstellen möchtest, kannst du dich jetzt <a>registrieren</a>.",
"You are currently using Riot anonymously as a guest.": "Du benutzt aktuell Riot anonym als Gast."
}

View file

@ -82,6 +82,8 @@
"Failed to invite users to community": "Failed to invite users to community",
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
"Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.",
"Dismiss": "Dismiss",
"Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings",
"Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again",
"Unable to enable Notifications": "Unable to enable Notifications",
@ -223,6 +225,7 @@
"Failed to join room": "Failed to join room",
"Message Pinning": "Message Pinning",
"Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view",
"Backup of encryption keys to server": "Backup of encryption keys to server",
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
"Use compact timeline layout": "Use compact timeline layout",
"Hide removed messages": "Hide removed messages",
@ -249,10 +252,11 @@
"Enable URL previews for this room (only affects you)": "Enable URL previews for this room (only affects you)",
"Enable URL previews by default for participants in this room": "Enable URL previews by default for participants in this room",
"Room Colour": "Room Colour",
"Pin unread rooms to the top of the room list": "Pin unread rooms to the top of the room list",
"Pin rooms I'm mentioned in to the top of the room list": "Pin rooms I'm mentioned in to the top of the room list",
"Pin unread rooms to the top of the room list": "Pin unread rooms to the top of the room list",
"Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets",
"Show empty room list headings": "Show empty room list headings",
"Show developer tools": "Show developer tools",
"Collecting app version information": "Collecting app version information",
"Collecting logs": "Collecting logs",
"Uploading report": "Uploading report",
@ -306,6 +310,24 @@
"Failed to set display name": "Failed to set display name",
"Disable Notifications": "Disable Notifications",
"Enable Notifications": "Enable Notifications",
"Delete Backup": "Delete Backup",
"Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history": "Delete your backed up encryption keys from the server? You will no longer be able to use your recovery key to read encrypted message history",
"Delete backup": "Delete backup",
"Unable to load key backup status": "Unable to load key backup status",
"This device is uploading keys to this backup": "This device is uploading keys to this backup",
"This device is <b>not</b> uploading keys to this backup": "This device is <b>not</b> uploading keys to this backup",
"Backup has a <validity>valid</validity> signature from this device": "Backup has a <validity>valid</validity> signature from this device",
"Backup has a <validity>valid</validity> signature from <verify>verified</verify> device <device>x</device>": "Backup has a <validity>valid</validity> signature from <verify>verified</verify> device <device>x</device>",
"Backup has a <validity>valid</validity> signature from <verify>unverified</verify> device <device></device>": "Backup has a <validity>valid</validity> signature from <verify>unverified</verify> device <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>verified</verify> device <device></device>": "Backup has an <validity>invalid</validity> signature from <verify>verified</verify> device <device></device>",
"Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> device <device></device>": "Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> device <device></device>",
"Verify...": "Verify...",
"Backup is not signed by any of your devices": "Backup is not signed by any of your devices",
"Backup version: ": "Backup version: ",
"Algorithm: ": "Algorithm: ",
"Restore backup": "Restore backup",
"No backup is present": "No backup is present",
"Start a new backup": "Start a new backup",
"Error saving email notification preferences": "Error saving email notification preferences",
"An error occurred whilst saving your email notification preferences.": "An error occurred whilst saving your email notification preferences.",
"Keywords": "Keywords",
@ -553,6 +575,7 @@
"Click here to fix": "Click here to fix",
"To send events of type <eventType/>, you must be a": "To send events of type <eventType/>, you must be a",
"Upgrade room to version %(ver)s": "Upgrade room to version %(ver)s",
"Open Devtools": "Open Devtools",
"Who can access this room?": "Who can access this room?",
"Only people who have been invited": "Only people who have been invited",
"Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
@ -638,14 +661,13 @@
"Message removed": "Message removed",
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
"This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot",
"Sign in with CAS": "Sign in with CAS",
"Custom Server Options": "Custom Server Options",
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.",
"This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.",
"You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.",
"Dismiss": "Dismiss",
"To continue, please enter your password.": "To continue, please enter your password.",
"Password:": "Password:",
"Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies",
"Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:",
"An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s",
"Please check your email to continue registration.": "Please check your email to continue registration.",
@ -737,7 +759,6 @@
"Unblacklist": "Unblacklist",
"Blacklist": "Blacklist",
"Unverify": "Unverify",
"Verify...": "Verify...",
"No results": "No results",
"Delete": "Delete",
"Communities": "Communities",
@ -849,6 +870,11 @@
"Advanced options": "Advanced options",
"Block users on other matrix homeservers from joining this room": "Block users on other matrix homeservers from joining this room",
"This setting cannot be changed later!": "This setting cannot be changed later!",
"Sign out": "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": "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",
"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. ": "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. ",
"Incompatible Database": "Incompatible Database",
"Continue With Encryption Disabled": "Continue With Encryption Disabled",
"Failed to indicate account erasure": "Failed to indicate account erasure",
"Unknown error": "Unknown error",
"Incorrect password": "Incorrect password",
@ -903,7 +929,6 @@
"Update any local room aliases to point to the new room": "Update any local room aliases to point to the new room",
"Stop users from speaking in the old version of the room, and post a message advising users to move to the new room": "Stop users from speaking in the old version of the room, and post a message advising users to move to the new room",
"Put a link back to the old room at the start of the new room so people can see old messages": "Put a link back to the old room at the start of the new room so people can see old messages",
"Sign out": "Sign out",
"Log out and remove encryption keys?": "Log out and remove encryption keys?",
"Clear Storage and Sign Out": "Clear Storage and Sign Out",
"Send Logs": "Send Logs",
@ -948,6 +973,55 @@
"Room contains unknown devices": "Room contains unknown devices",
"\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.",
"Unknown devices": "Unknown devices",
"Secure your encrypted message history with a Recovery Passphrase.": "Secure your encrypted message history with a Recovery Passphrase.",
"You'll need it if you log out or lose access to this device.": "You'll need it if you log out or lose access to this device.",
"Enter a passphrase...": "Enter a passphrase...",
"Next": "Next",
"If you don't want encrypted message history to be availble on other devices, <button>opt out</button>.": "If you don't want encrypted message history to be availble on other devices, <button>opt out</button>.",
"Or, if you don't want to create a Recovery Passphrase, skip this step and <button>download a recovery key</button>.": "Or, if you don't want to create a Recovery Passphrase, skip this step and <button>download a recovery key</button>.",
"That matches!": "That matches!",
"That doesn't match.": "That doesn't match.",
"Go back to set it again.": "Go back to set it again.",
"Type in your Recovery Passphrase to confirm you remember it. If it helps, add it to your password manager or store it somewhere safe.": "Type in your Recovery Passphrase to confirm you remember it. If it helps, add it to your password manager or store it somewhere safe.",
"Repeat your passphrase...": "Repeat your passphrase...",
"Make a copy of this Recovery Key and keep it safe.": "Make a copy of this Recovery Key and keep it safe.",
"As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.",
"Your Recovery Key": "Your Recovery Key",
"Copy to clipboard": "Copy to clipboard",
"Download": "Download",
"I've made a copy": "I've made a copy",
"Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:": "Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:",
"Your Recovery Key is in your <b>Downloads</b> folder.": "Your Recovery Key is in your <b>Downloads</b> folder.",
"<b>Print it</b> and store it somewhere safe": "<b>Print it</b> and store it somewhere safe",
"<b>Save it</b> on a USB key or backup drive": "<b>Save it</b> on a USB key or backup drive",
"<b>Copy it</b> to your personal cloud storage": "<b>Copy it</b> to your personal cloud storage",
"Got it": "Got it",
"Backup created": "Backup created",
"Your encryption keys are now being backed up to your Homeserver.": "Your encryption keys are now being backed up to your Homeserver.",
"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.": "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.",
"Set up Secure Message Recovery": "Set up Secure Message Recovery",
"Create a Recovery Passphrase": "Create a Recovery Passphrase",
"Confirm Recovery Passphrase": "Confirm Recovery Passphrase",
"Recovery Key": "Recovery Key",
"Keep it safe": "Keep it safe",
"Backing up...": "Backing up...",
"Create Key Backup": "Create Key Backup",
"Unable to create key backup": "Unable to create key backup",
"Retry": "Retry",
"Unable to load backup status": "Unable to load backup status",
"Unable to restore backup": "Unable to restore backup",
"No backup found!": "No backup found!",
"Backup Restored": "Backup Restored",
"Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!",
"Restored %(sessionCount)s session keys": "Restored %(sessionCount)s session keys",
"Enter Recovery Passphrase": "Enter Recovery Passphrase",
"Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Access your secure message history and set up secure messaging by entering your recovery passphrase.",
"If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>": "If you've forgotten your recovery passphrase you can <button1>use your recovery key</button1> or <button2>set up new recovery options</button2>",
"Enter Recovery Key": "Enter Recovery Key",
"This looks like a valid recovery key!": "This looks like a valid recovery key!",
"Not a valid recovery key": "Not a valid recovery key",
"Access your secure message history and set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.",
"If you've forgotten your recovery passphrase you can <button>set up new recovery options</button>": "If you've forgotten your recovery passphrase you can <button>set up new recovery options</button>",
"Private Chat": "Private Chat",
"Public Chat": "Public Chat",
"Custom": "Custom",
@ -1034,6 +1108,8 @@
"This Home server does not support communities": "This Home server does not support communities",
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
"Couldn't load home page": "Couldn't load home page",
"You are currently using Riot anonymously as a guest.": "You are currently using Riot anonymously as a guest.",
"If you would like to create a Matrix account you can <a>register</a> now.": "If you would like to create a Matrix account you can <a>register</a> now.",
"Login": "Login",
"Failed to reject invitation": "Failed to reject invitation",
"This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.",
@ -1143,6 +1219,7 @@
"Autocomplete Delay (ms):": "Autocomplete Delay (ms):",
"<not supported>": "<not supported>",
"Import E2E room keys": "Import E2E room keys",
"Key Backup": "Key Backup",
"Cryptography": "Cryptography",
"Device ID:": "Device ID:",
"Device key:": "Device key:",
@ -1217,6 +1294,7 @@
"Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.",
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.",
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.",
"Sign in with single sign-on": "Sign in with single sign-on",
"Try the app first": "Try the app first",
"Sign in to get started": "Sign in to get started",
"Failed to fetch avatar URL": "Failed to fetch avatar URL",
@ -1271,7 +1349,5 @@
"Import": "Import",
"Failed to set direct chat tag": "Failed to set direct chat tag",
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
"Open Devtools": "Open Devtools",
"Show developer tools": "Show developer tools"
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room"
}

View file

@ -644,7 +644,9 @@
"Username not available": "Username not available",
"Something went wrong!": "Something went wrong!",
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.",
"If you would like to create a Matrix account you can <a>register</a> now.": "If you would like to create a Matrix account you can <a>register</a> now.",
"If you already have a Matrix account you can <a>log in</a> instead.": "If you already have a Matrix account you can <a>log in</a> instead.",
"You are currently using Riot anonymously as a guest.": "You are currently using Riot anonymously as a guest.",
"Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions",
"Not a valid Riot keyfile": "Not a valid Riot keyfile",
"Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?",
@ -838,5 +840,45 @@
"Your homeserver's URL": "Your homeserver's URL",
"Your identity server's URL": "Your identity server's URL",
"e.g. %(exampleValue)s": "e.g. %(exampleValue)s",
"Every page you use in the app": "Every page you use in the app"
"Every page you use in the app": "Every page you use in the app",
"e.g. <CurrentPageURL>": "e.g. <CurrentPageURL>",
"Your User Agent": "Your User Agent",
"Your device resolution": "Your device resolution",
"The information being sent to us to help make Riot.im better includes:": "The information being sent to us to help make Riot.im better includes:",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.",
"Call Failed": "Call Failed",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.",
"Review Devices": "Review Devices",
"Call Anyway": "Call Anyway",
"Answer Anyway": "Answer Anyway",
"Call": "Call",
"Answer": "Answer",
"A conference call could not be started because the intgrations server is not available": "A conference call could not be started because the integrations server is not available",
"Call in Progress": "Call in Progress",
"A call is currently being placed!": "A call is currently being placed!",
"A call is already in progress!": "A call is already in progress!",
"Permission Required": "Permission Required",
"You do not have permission to start a conference call in this room": "You do not have permission to start a conference call in this room",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
"Who would you like to add to this community?": "Who would you like to add to this community?",
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID",
"Invite new community members": "Invite new community members",
"Name or matrix ID": "Name or matrix ID",
"Invite to Community": "Invite to Community",
"Which rooms would you like to add to this community?": "Which rooms would you like to add to this community?",
"Show these rooms to non-members on the community page and room list?": "Show these rooms to non-members on the community page and room list?",
"Add rooms to the community": "Add rooms to the community",
"Room name or alias": "Room name or alias",
"Add to community": "Add to community",
"Failed to invite the following users to %(groupId)s:": "Failed to invite the following users to %(groupId)s:",
"Failed to invite users to community": "Failed to invite users to community",
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
"Registration Required": "Registration Required",
"You need to register to do this. Would you like to register now?": "You need to register to do this. Would you like to register now?",
"Restricted": "Restricted",
"Missing roomId.": "Missing roomId.",
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
"Forces the current outbound group session in an encrypted room to be discarded": "Forces the current outbound group session in an encrypted room to be discarded",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s changed their display name to %(displayName)s."
}

View file

@ -1110,5 +1110,19 @@
"<requestLink>Re-request encryption keys</requestLink> from your other devices.": "<requestLink>Redemandi ĉifroŝlosilojn</requestLink> el viaj aliaj aparatoj.",
"Encrypting": "Ĉifranta",
"Encrypted, not sent": "Ĉifrita, ne sendita",
"If your other devices do not have the key for this message you will not be able to decrypt them.": "Se viaj aliaj aparatoj ne havas la ŝlosilon por ĉi tiu mesaĝo, vi ne povos malĉifri ĝin."
"If your other devices do not have the key for this message you will not be able to decrypt them.": "Se viaj aliaj aparatoj ne havas la ŝlosilon por ĉi tiu mesaĝo, vi ne povos malĉifri ĝin.",
"Permission Required": "Necesas permeso",
"Registration Required": "Necesas registriĝo",
"You need to register to do this. Would you like to register now?": "Por fari ĉi tion, vi bezonas registriĝi. Ĉu vi volas registriĝi nun?",
"Missing roomId.": "Mankas identigilo de la ĉambro.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s aldonis %(addedAddresses)s kiel adresojn por la ĉambro.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s aldonis %(addedAddresses)s kiel adreson por la ĉambro.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s forigis %(removedAddresses)s kiel adresojn por la ĉambro.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s forigis %(removedAddresses)s kiel adreson por la ĉambro.",
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s aldonis %(addedAddresses)s kaj forigis %(removedAddresses)s kiel adresojn por la ĉambro.",
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s agordis la ĉefan adreson por la ĉambro al %(address)s.",
"%(senderName)s removed the main address for this room.": "%(senderName)s forigis la ĉefan adreson de la ĉambro.",
"Please <a>contact your service administrator</a> to continue using the service.": "Bonvolu <a>kontakti administranton de la servo</a> por daŭre uzadi la servon.",
"Pin unread rooms to the top of the room list": "Fiksi nelegitajn ĉambrojn supre de la listo",
"Pin rooms I'm mentioned in to the top of the room list": "Fiksi ĉambrojn kun mencioj de mia nomo supre de la listo"
}

View file

@ -1292,5 +1292,13 @@
"If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "Rioten beste bertsioa oraindik beste fitxat batean irekita badago, itxi ezazu zerbitzari bera aldi berean karga alferra gaituta eta desgaituta erabiltzeak arazoak sor ditzakeelako.",
"Incompatible local cache": "Katxe lokal bateraezina",
"Clear cache and resync": "Garbitu katxea eta sinkronizatu berriro",
"Add some now": "Gehitu batzuk orain"
"Add some now": "Gehitu batzuk orain",
"Joining room...": "Gelara elkartzen...",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Komunitate honen administratzailea zara. Ezin izango duzu berriro elkartu ez bazaitu beste administratzaile batek gonbidatzen.",
"Open Devtools": "Ireki garapen tresnak",
"Show developer tools": "Erakutsi garapen tresnak",
"Pin unread rooms to the top of the room list": "Finkatu irakurri gabeko gelak gelen zerrendaren goialdean",
"Pin rooms I'm mentioned in to the top of the room list": "Finkatu aipatu nauten gelak gelen zerrendaren goialdean",
"If you would like to create a Matrix account you can <a>register</a> now.": "Matrix kontu bat sortu nahi baduzu, <a>izena eman</a> dezakezu.",
"You are currently using Riot anonymously as a guest.": "Riot anonimoki gonbidatu gisa erabiltzen ari zara."
}

View file

@ -102,7 +102,7 @@
"Devices": "Appareils",
"Devices will not yet be able to decrypt history from before they joined the room": "Les appareils ne pourront pas encore déchiffrer l'historique de messages d'avant leur arrivée sur le salon",
"Failed to join room": "Échec de linscription au salon",
"Failed to kick": "Échec de l'exclusion",
"Failed to kick": "Échec de l'expulsion",
"Failed to leave room": "Échec du départ du salon",
"Failed to load timeline position": "Échec du chargement de la position dans l'historique",
"Failed to mute user": "Échec de la mise en sourdine de l'utilisateur",
@ -157,9 +157,9 @@
"Join Room": "Rejoindre le salon",
"%(targetName)s joined the room.": "%(targetName)s a rejoint le salon.",
"Joins room with given alias": "Rejoint le salon avec l'alias renseigné",
"%(senderName)s kicked %(targetName)s.": "%(senderName)s a exclu %(targetName)s.",
"Kick": "Exclure",
"Kicks user with given id": "Exclut l'utilisateur à partir de son identifiant",
"%(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",
"Leave room": "Quitter le salon",
"%(targetName)s left the room.": "%(targetName)s a quitté le salon.",
@ -592,7 +592,7 @@
"Verified": "Vérifié",
"Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?": "Souhaitez-vous <acceptText>accepter</acceptText> ou <declineText>refuser</declineText> cette invitation ?",
"You have been banned from %(roomName)s by %(userName)s.": "Vous avez été banni(e) de %(roomName)s par %(userName)s.",
"You have been kicked from %(roomName)s by %(userName)s.": "Vous avez été exclu de %(roomName)s par %(userName)s.",
"You have been kicked from %(roomName)s by %(userName)s.": "%(userName)s vous a expulsé de %(roomName)s.",
"You may wish to login with a different account, or add this email to this account.": "Vous souhaiteriez peut-être vous identifier avec un autre compte, ou ajouter cette e-mail à votre compte.",
"Your home server does not support device management.": "Votre serveur d'accueil ne prend pas en charge la gestion d'appareils.",
"(~%(count)s results)|one": "(~%(count)s résultat)",
@ -637,7 +637,7 @@
"Define the power level of a user": "Définir le rang d'un utilisateur",
"Edit": "Modifier",
"Enable automatic language detection for syntax highlighting": "Activer la détection automatique de la langue pour la correction orthographique",
"Hide join/leave messages (invites/kicks/bans unaffected)": "Masquer les messages d'arrivée/départ (n'affecte pas les invitations/exclusions/bannissements)",
"Hide join/leave messages (invites/kicks/bans unaffected)": "Masquer les messages d'arrivée/départ (n'affecte pas les invitations/expulsions/bannissements)",
"Revoke widget access": "Révoquer les accès du widget",
"Sets the room topic": "Défini le sujet du salon",
"To get started, please pick a username!": "Pour commencer, choisissez un nom d'utilisateur !",
@ -719,7 +719,7 @@
"Guests can join": "Les invités peuvent rejoindre le salon",
"To invite users into the room, you must be a": "Pour inviter des utilisateurs dans le salon, vous devez être un(e)",
"To configure the room, you must be a": "Pour configurer le salon, vous devez être un(e)",
"To kick users, you must be a": "Pour exclure des utilisateurs, vous devez être un(e)",
"To kick users, you must be a": "Pour expulser des utilisateurs, vous devez être",
"To ban users, you must be a": "Pour bannir des utilisateurs, vous devez être un(e)",
"To remove other users' messages, you must be a": "Pour supprimer les messages d'autres utilisateurs, vous devez être un(e)",
"To send events of type <eventType/>, you must be a": "Pour envoyer des évènements du type <eventType/>, vous devez être un",
@ -730,14 +730,14 @@
"%(senderName)s sent a video": "%(senderName)s a envoyé une vidéo",
"%(senderName)s uploaded a file": "%(senderName)s a transféré un fichier",
"Disinvite this user?": "Désinviter l'utilisateur ?",
"Kick this user?": "Exclure cet utilisateur ?",
"Kick this user?": "Expulser cet utilisateur ?",
"Unban this user?": "Révoquer le bannissement de cet utilisateur ?",
"Ban this user?": "Bannir cet utilisateur ?",
"Drop here to favourite": "Déposer ici pour mettre en favori",
"Drop here to tag direct chat": "Déposer ici pour marquer comme conversation directe",
"Drop here to restore": "Déposer ici pour restaurer",
"Drop here to demote": "Déposer ici pour rétrograder",
"You have been kicked from this room by %(userName)s.": "Vous avez été exclu de ce salon par %(userName)s.",
"You have been kicked from this room by %(userName)s.": "%(userName)s vous a expulsé de ce salon.",
"You have been banned from this room by %(userName)s.": "Vous avez été banni de ce salon par %(userName)s.",
"You are trying to access a room.": "Vous essayez d'accéder à un salon.",
"Members only (since the point in time of selecting this option)": "Seulement les membres (depuis la sélection de cette option)",
@ -798,10 +798,10 @@
"were unbanned %(count)s times|one": "ont vu leur bannissement révoqué",
"was unbanned %(count)s times|other": "a vu son bannissement révoqué %(count)s fois",
"was unbanned %(count)s times|one": "a vu son bannissement révoqué",
"were kicked %(count)s times|other": "ont été exclus %(count)s fois",
"were kicked %(count)s times|one": "ont été exclus",
"was kicked %(count)s times|other": "a été exclu %(count)s fois",
"was kicked %(count)s times|one": "a été exclu",
"were kicked %(count)s times|other": "ont été expulsés %(count)s fois",
"were kicked %(count)s times|one": "ont été expulsés",
"was kicked %(count)s times|other": "a été expulsé %(count)s fois",
"was kicked %(count)s times|one": "a été expulsé",
"%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s ont changé de nom %(count)s fois",
"%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s ont changé de nom",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s a changé de nom %(count)s fois",
@ -1294,5 +1294,13 @@
"Clear cache and resync": "Vider le cache et resynchroniser",
"Please accept all of the policies": "Veuillez accepter toutes les politiques",
"Please review and accept the policies of this homeserver:": "Veuillez lire et accepter les politiques de ce serveur d'accueil :",
"Add some now": "En ajouter maintenant"
"Add some now": "En ajouter maintenant",
"Joining room...": "Adhésion au salon…",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Vous êtes administrateur de cette communauté. Vous ne pourrez pas revenir sans une invitation d'un autre administrateur.",
"Open Devtools": "Ouvrir les outils développeur",
"Show developer tools": "Afficher les outils de développeur",
"Pin unread rooms to the top of the room list": "Épingler les salons non lus en haut de la liste des salons",
"Pin rooms I'm mentioned in to the top of the room list": "Épingler les salons où l'on me mentionne en haut de la liste des salons",
"If you would like to create a Matrix account you can <a>register</a> now.": "Si vous souhaitez créer un compte Matrix, vous pouvez <a>vous inscrire</a> maintenant.",
"You are currently using Riot anonymously as a guest.": "Vous utilisez Riot de façon anonyme en tant qu'invité."
}

View file

@ -1295,5 +1295,12 @@
"Please accept all of the policies": "Kérlek fogadd el a felhasználói feltételeket",
"Please review and accept the policies of this homeserver:": "Kérlek nézd át és fogadd el a Matrix szerver felhasználói feltételeit:",
"Add some now": "Adj hozzá párat",
"Joining room...": "Belépés a szobába.."
"Joining room...": "Belépés a szobába..",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Te vagy ennek a közösségnek az adminisztrátora. Egy másik adminisztrátortól kapott meghívó nélkül nem tudsz majd újra csatlakozni.",
"Open Devtools": "Fejlesztői eszközök megnyitása",
"Show developer tools": "Fejlesztői eszközök megjelenítése",
"Pin unread rooms to the top of the room list": "Nem olvasott üzeneteket tartalmazó szobák a szobalista elejére",
"Pin rooms I'm mentioned in to the top of the room list": "Megemlítéseket tartalmazó szobák a szobalista elejére",
"If you would like to create a Matrix account you can <a>register</a> now.": "Ha létre szeretnél hozni egy Matrix fiókot most <a>regisztrálhatsz</a>.",
"You are currently using Riot anonymously as a guest.": "A Riotot ismeretlen vendégként használod."
}

View file

@ -1290,5 +1290,12 @@
"Incompatible local cache": "Cache locale non compatibile",
"Clear cache and resync": "Svuota cache e risincronizza",
"Please accept all of the policies": "Si prega di accettare tutte le condizioni",
"Please review and accept the policies of this homeserver:": "Consulta ed accetta le condizioni di questo homeserver:"
"Please review and accept the policies of this homeserver:": "Consulta ed accetta le condizioni di questo homeserver:",
"Pin unread rooms to the top of the room list": "Fissa le stanze non lette in cima all'elenco stanze",
"Pin rooms I'm mentioned in to the top of the room list": "Fissa le stanze dove sono menzionato in cima all'elenco stanze",
"Joining room...": "Ingresso nella stanza...",
"Add some now": "Aggiungine ora",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Sei un amministratore di questa comunità. Non potrai rientrare senza un invito da parte di un altro amministratore.",
"Open Devtools": "Apri Devtools",
"Show developer tools": "Mostra strumenti sviluppatore"
}

File diff suppressed because it is too large Load diff

View file

@ -872,5 +872,16 @@
"You have no historical rooms": "Jūs neturite istorinių kambarių",
"Historical": "Istoriniai",
"Every page you use in the app": "Kiekvienas puslapis, kurį naudoji programoje",
"Call Timeout": "Skambučio laikas baigėsi"
"Call Timeout": "Skambučio laikas baigėsi",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s kaip šio kambario adresus pridėjo %(addedAddresses)s.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s kaip šio kambario adresą pridėjo %(addedAddresses)s.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s kaip šio kambario adresus pašalino %(removedAddresses)s.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s kaip šio kambario adresą pašalino %(removedAddresses)s.",
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s kaip šio kambario adresus pridėjo %(addedAddresses)s ir pašalino %(removedAddresses)s.",
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s nustatė pagrindinį šio kambario adresą į %(address)s.",
"%(senderName)s removed the main address for this room.": "%(senderName)s pašalino pagrindinį šio kambario adresą.",
"Disinvite": "Atšaukti pakvietimą",
"Disinvite this user?": "Atšaukti pakvietimą šiam naudotojui?",
"Unknown for %(duration)s": "Nežinoma jau %(duration)s",
"(warning: cannot be disabled again!)": "(įspėjimas: nebeįmanoma bus išjungti!)"
}

View file

@ -548,8 +548,8 @@
"code": "kod",
"quote": "cytat",
"Create": "Utwórz",
"Online": "Dostępny",
"Offline": "Niedostępny",
"Online": "Dostępny(-a)",
"Offline": "Niedostępny(-a)",
"Add an Integration": "Dodaj integrację",
"Token incorrect": "Niepoprawny token",
"To link to a room it must have <a>an address</a>.": "Aby móc stworzyć link do pokoju musi on mieć swój <a>adres</a>.",
@ -775,7 +775,7 @@
"Failed to get protocol list from Home Server": "Nie można pobrać listy protokołów z serwera domowego",
"You are not receiving desktop notifications": "Nie otrzymujesz powiadomień na pulpit",
"Friday": "Piątek",
"Update": "Uaktualnienie",
"Update": "Zaktualizuj",
"What's New": "Co nowego",
"Add an email address above to configure email notifications": "Dodaj adres e-mail powyżej, aby skonfigurować powiadomienia e-mailowe",
"Expand panel": "Rozwiń panel",
@ -1180,5 +1180,32 @@
"Show empty room list headings": "Pokaż nagłówki z pustym pokojem",
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "W zaszyfrowanych pokojach, takich jak ten, podgląd adresów URL jest domyślnie wyłączony, aby upewnić się, że serwer (w którym generowane są podglądy) nie może zbierać informacji o linkach widocznych w tym pokoju.",
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "Gdy ktoś umieści URL w wiadomości, można wyświetlić podgląd adresu URL, aby podać więcej informacji o tym łączu, takich jak tytuł, opis i obraz ze strony internetowej.",
"This event could not be displayed": "Ten event nie może zostać wyświetlony"
"This event could not be displayed": "Ten event nie może zostać wyświetlony",
"This room has been replaced and is no longer active.": "Ten pokój został zamieniony i nie jest już aktywny.",
"The conversation continues here.": "Konwersacja jest kontynuowana tutaj.",
"System Alerts": "Alerty systemowe",
"You don't currently have any stickerpacks enabled": "Nie masz obecnie włączonych żadnych pakietów naklejek",
"Stickerpack": "Pakiet naklejek",
"This room is a continuation of another conversation.": "Ten pokój jest kontynuacją innej rozmowy.",
"Click here to see older messages.": "Kliknij tutaj, aby zobaczyć starsze wiadomości.",
"This homeserver has hit its Monthly Active User limit so <b>some users will not be able to log in</b>.": "Ten serwer osiągnął miesięczny limit aktywnych użytkowników, więc <b>niektórzy użytkownicy nie będą mogli się zalogować</b>.",
"This homeserver has exceeded one of its resource limits so <b>some users will not be able to log in</b>.": "Ten serwer przekroczył jeden z limitów, więc <b>niektórzy użytkownicy nie będą mogli się zalogować</b>.",
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s dołączyli(-ły) %(count)s razy",
"%(oneUser)sjoined %(count)s times|other": "%(oneUser)s dołączył(a) %(count)s razy",
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)s dołączył(a)",
"%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s wyszli(-ły)",
"Internal room ID: ": "Wewnętrzny identyfikator pokoju ",
"were invited %(count)s times|one": "zostali(-ły) zaproszeni(-one)",
"Show developer tools": "Pokaż narzędzia deweloperskie",
"An email address is required to register on this homeserver.": "Adres e-mail jest wymagany do rejestracji na tym serwerze domowym.",
"A phone number is required to register on this homeserver.": "Numer telefonu jest wymagany do rejestracji na tym serwerze domowym.",
"Updating Riot": "Aktualizowanie Riot",
"Submit Debug Logs": "Wyślij dzienniki błędów",
"Please <a>contact your service administrator</a> to continue using this service.": "Proszę, <a>skontaktuj się z administratorem</a> aby korzystać dalej z funkcji.",
"Only room administrators will see this warning": "Tylko administratorzy pokojów widzą to ostrzeżenie",
"Open Devtools": "Otwórz narzędzia deweloperskie",
"Clear cache and resync": "Wyczyść pamięć podręczną i zsynchronizuj ponownie",
"Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot używa teraz 3-5x mniej pamięci, ładując informacje o innych użytkownikach tylko wtedy, gdy jest to konieczne. Poczekaj, aż ponownie zsynchronizujemy się z serwerem!",
"If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "Jeśli inna wersja Riot jest nadal otwarta w innej zakładce, proszę zamknij ją, ponieważ używanie Riot na tym samym komputerze z włączonym i wyłączonym jednocześnie leniwym ładowaniem będzie powodować problemy.",
"And %(count)s more...|other": "I %(count)s więcej…"
}

1
src/i18n/strings/ro.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -1,45 +1,45 @@
{
"This email address is already in use": "Kjo adresë e-mail-i tashmë është në përdorim",
"This phone number is already in use": "Ky numër telefoni tashmë është në përdorim",
"This email address is already in use": "Kjo adresë email është tashmë në përdorim",
"This phone number is already in use": "Ky numër telefoni është tashmë në përdorim",
"Failed to verify email address: make sure you clicked the link in the email": "Vërtetimi i adresës e-mail i pasukseshëm: Sigurohu që ke klikuar lidhjen në e-mail",
"The platform you're on": "Platforma që jë duke përdorur",
"The platform you're on": "Platforma ku gjendeni",
"The version of Riot.im": "Versioni i Riot.im-it",
"Whether or not you're logged in (we don't record your user name)": "A je i lajmëruar apo jo (ne nuk do të inçizojmë emrin përdorues tëndë)",
"Your language of choice": "Gjuha jote e zgjedhur",
"Which officially provided instance you are using, if any": "Cilën instancë zyrtarisht të ofruar je duke përdorur, në rast që je",
"Your language of choice": "Gjuha juaj e zgjedhur",
"Which officially provided instance you are using, if any": "Cilën instancë të furnizuar zyrtarish po përdorni, në pastë",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "A je duke e përdorur mënyrën e tekstit të pasuruar të redaktionuesit të tekstit të pasuruar apo jo",
"Your homeserver's URL": "URL-ja e server-it shtëpiak tëndë",
"Your identity server's URL": "URL-ja e server-it identiteti tëndë",
"Your homeserver's URL": "URL e Shërbyesit tuaj Home",
"Your identity server's URL": "URL e shërbyesit tuaj të identiteteve",
"Analytics": "Analiza",
"The information being sent to us to help make Riot.im better includes:": "Informacionet që dërgohen për t'i ndihmuar Riot.im-it të përmirësohet përmbajnë:",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Kur kjo faqe pëmban informacione që mund të të identifikojnë, sikur një dhomë, përdorues apo identifikatues grupi, këto të dhëna do të mënjanohen para se ti dërgohën një server-it.",
"Call Failed": "Thirrja nuk mundej të realizohet",
"Call Failed": "Thirrja Dështoi",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Pajisje të panjohura ndodhen në këtë dhomë: nësë vazhdon pa i vërtetuar, është e mundshme që dikush të jua përgjon thirrjen.",
"Review Devices": "Rishiko pajisjet",
"Call Anyway": "Thirr prapëseprapë",
"Answer Anyway": "Prano prapëseprapë",
"Call": "Thirr",
"Answer": "Prano",
"Call Anyway": "Thirre Sido Qoftë",
"Answer Anyway": "Përgjigju Sido Qoftë",
"Call": "Thirrje",
"Answer": "Përgjigje",
"Call Timeout": "Skadim kohe thirrjeje",
"The remote side failed to pick up": "Ana e kundërt nuk arriti të pranojë",
"The remote side failed to pick up": "Ana e largët dështoi të përgjigjet",
"Unable to capture screen": "Ekrani nuk mundi të inçizohej",
"Existing Call": "Thirrje aktuale",
"You are already in a call.": "Tashmë je në thirrje.",
"You are already in a call.": "Jeni tashmë në një thirrje.",
"VoIP is unsupported": "VoIP nuk mbulohet",
"You cannot place VoIP calls in this browser.": "Thirrjet me VoIP nuk mbulohen nga ky kërkues uebi.",
"You cannot place a call with yourself.": "Nuk mund të realizosh thirrje me vetveten.",
"You cannot place a call with yourself.": "Smund të bëni thirrje me vetveten.",
"Conference calls are not supported in this client": "Thirrjet konference nuk mbulohen nga ky klienti",
"Conference calls are not supported in encrypted rooms": "Thirrjet konference nuk mbulohen në dhoma të shifruara",
"Warning!": "Paralajmërim!",
"Warning!": "Sinjalizim!",
"Conference calling is in development and may not be reliable.": "Thirrja konference ende është në zhvillim dhe mund të jetë e paqëndrueshme.",
"Failed to set up conference call": "Thirrja konference nuk mundi të realizohej",
"Conference call failed.": "Thirrja konference dështoi.",
"The file '%(fileName)s' failed to upload": "Fajli '%(fileName)s' nuk mundi të mbartet",
"The file '%(fileName)s' failed to upload": "Dështoi ngarkimi i kartelës '%(fileName)s'",
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "Fajli '%(fileName)s' tejkalon kufirin madhësie për mbartje e këtij server-i shtëpiak",
"Upload Failed": "Mbartja dështoi",
"Upload Failed": "Ngarkimi Dështoi",
"Failure to create room": "Dhoma nuk mundi të krijohet",
"Server may be unavailable, overloaded, or you hit a bug.": "Server-i është i padisponueshëm, i ngarkuar tej mase, apo ka një gabim.",
"Send anyway": "Dërgo prapëseprapë",
"Send anyway": "Dërgoje sido qoftë",
"Send": "Dërgoje",
"Sun": "Die",
"Mon": "Hën",
@ -51,15 +51,15 @@
"%(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",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s %(time)s",
"Who would you like to add to this community?": "Kë kishe dashur të shtosh në këtë komunitet?",
"Who would you like to add to this community?": "Kë do të donit të shtonit te kjo bashkësi?",
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Paralajmërim: se cili që e shton në një komunitet do ti doket se cilit që e di identifikatuesin e komunitetit",
"Invite new community members": "Fto anëtar të ri komuniteti",
"Name or matrix ID": "Emri apo identifikatuesi matrix-i",
"Invite new community members": "Ftoni anëtarë të rinj bashkësie",
"Name or matrix ID": "Emër ose ID matrix-i",
"Invite to Community": "Fto në komunitet",
"Which rooms would you like to add to this community?": "Cilët dhoma kishe dashur ti shtosh në këtë komunitet?",
"Show these rooms to non-members on the community page and room list?": "A ti duken dhomat joanëtarëvë ne faqën komuniteti si dhe listën dhome?",
"Add rooms to the community": "Shto dhoma komunitetit",
"Add to community": "Shto në komunitet",
"Add rooms to the community": "Shtoni dhoma te bashkësia",
"Add to community": "Shtoje te kjo bashkësi",
"Jan": "Jan",
"Feb": "Shk",
"Mar": "Mar",
@ -77,42 +77,42 @@
"Failed to invite users to community": "Përdoruesit nuk mundën të ftohën",
"Failed to invite users to %(groupId)s": "Nuk mundën të ftohën përdoruesit në %(groupId)s",
"Failed to add the following rooms to %(groupId)s:": "Nuk mundën të shtohen dhomat vijuese në %(groupId)s:",
"Unnamed Room": "Dhomë paemër",
"Unnamed Room": "Dhomë e Paemërtuar",
"Riot does not have permission to send you notifications - please check your browser settings": "Riot nuk ka lejim të të dergojë lajmërime - të lutem kontrollo rregullimet e kërkuesit ueb tëndë",
"Riot was not given permission to send notifications - please try again": "Riot-it nuk i është dhënë leje të dërgojë lajmërime - të lutëm përpjeku serish",
"Unable to enable Notifications": "Lajmërimet nuk mundën të lëshohen",
"This email address was not found": "Kjo adresë e-mail-i nuk është gjetur",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Adresa e-mail-i yt nuk duket së është lidhur më një indentifikatues matrix në këtë server shtëpiak.",
"Default": "Standardi",
"Restricted": "Kufizuar",
"This email address was not found": "Kjo adresë email su gjet",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Adresa juaj email sduket të jetë e përshoqëruar me një ID Matrix në këtë shërbyes Home.",
"Default": "Parazgjedhje",
"Restricted": "E kufizuar",
"Moderator": "Moderator",
"Admin": "Administrator",
"Start a chat": "Fillo bisedë",
"Who would you like to communicate with?": "Me kë kishe dashur të bisedosh?",
"Email, name or matrix ID": "E-mail-i, emri apo identifikatuesi matrix",
"Start Chat": "Fillo bisedë",
"Invite new room members": "Fto anëtar dhome të rinjë",
"Who would you like to add to this room?": "Kë kishe dashur të shtosh në këtë dhomë?",
"Send Invites": "Dërgo ftesa",
"Admin": "Përgjegjës",
"Start a chat": "Nisni një fjalosje",
"Who would you like to communicate with?": "Me kë do të donit të komunikonit?",
"Email, name or matrix ID": "Email, emër ose ID matrix",
"Start Chat": "Filloni Fjalosje",
"Invite new room members": "Ftoni anëtarë të rinj dhome",
"Who would you like to add to this room?": "Kë do të donit të shtonit te kjo dhomë?",
"Send Invites": "Dërgoni Ftesa",
"Failed to invite user": "Përdoruesi nuk mundi të ftohej",
"Operation failed": "Veprimi dështoi",
"Failed to invite": "Nuk mundi të ftohet",
"Failed to invite the following users to the %(roomName)s room:": "Përdoruesit vijuesë nuk mundën të ftohen në dhomën %(roomName)s:",
"You need to be logged in.": "Duhesh të jesh i lajmëruar.",
"You need to be able to invite users to do that.": "Duhesh të kesh aftësinë të ftosh përdorues për të bërë këtë.",
"You need to be logged in.": "Lypset të jeni i futur në llogarinë tuaj.",
"You need to be able to invite users to do that.": "Që ta bëni këtë, lypset të jeni në gjendje të ftoni përdorues.",
"Unable to create widget.": "Widget-i nuk mundi të krijohet.",
"Failed to send request.": "Lutja nuk mundi të dërgohej.",
"This room is not recognised.": "Kjo dhomë nuk është e njohur.",
"This room is not recognised.": "Kjo dhomë sështë e pranuar.",
"Power level must be positive integer.": "Niveli fuqie duhet të jetë numër i plotë pozitiv.",
"You are not in this room.": "Ti nuk je në këtë dhomë.",
"You do not have permission to do that in this room.": "Nuk ke leje të bësh këtë në këtë dhomë.",
"Room %(roomId)s not visible": "Dhoma %(roomId)s e padukshme",
"Usage": "Përdorimi",
"You are not in this room.": "Sgjendeni në këtë dhomë.",
"You do not have permission to do that in this room.": "Skeni leje për ta bërë këtë në këtë dhomë.",
"Room %(roomId)s not visible": "Dhoma %(roomId)s sështë e dukshme",
"Usage": "Përdorim",
"/ddg is not a command": "/ddg s'është komandë",
"To use it, just wait for autocomplete results to load and tab through them.": "Për të përdorur, thjesht prit derisa të mbushën rezultatat vetëplotësuese dhe pastaj shfletoji.",
"Unrecognised room alias:": "Emri i dhomës të panjohur:",
"Ignored user": "Përdoruesë të shpërfillur",
"You are now ignoring %(userId)s": "Tash %(userId)s shpërfillet prej teje",
"Unrecognised room alias:": "Alias dhome jo i pranuar:",
"Ignored user": "Përdorues i shpërfillur",
"You are now ignoring %(userId)s": "Tani po e shpërfillni %(userId)s",
"Unignored user": "Përdorues jo më i shpërfillur",
"Fetching third party location failed": "Dështoi prurja e vendndodhjes së palës së tretë",
"A new version of Riot is available.": "Ka gati një version të ri Riot-it.",
@ -269,7 +269,7 @@
"Enable audible notifications in web client": "Aktivizoni njoftime audio te klienti web",
"Register": "Regjistrohuni",
"Off": "Off",
"Edit": "Përpunoni",
"Edit": "Përpuno",
"Riot does not know how to join a room on this network": "Riot-i nuk di si të hyjë në një dhomë në këtë rrjet",
"Mentions only": "Vetëm @përmendje",
"remove %(name)s from the directory.": "hiqe %(name)s prej drejtorie.",
@ -287,7 +287,7 @@
"%(count)s Members|one": "%(count)s Anëtar",
"Developer Tools": "Mjete Zhvilluesi",
"View Source": "Shihini Burimin",
"Custom Server Options": "Mundësi Vetjake Shërbyesi",
"Custom Server Options": "Mundësi Shërbyesi Vetjak",
"Event Content": "Lëndë Akti",
"Rooms": "Dhoma",
"#example": "#shembull",
@ -295,5 +295,429 @@
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Me shfletuesin tuaj të tanishëm, pamja dhe ndjesitë nga aplikacioni mund të jenë plotësisht të pasakta, dhe disa nga ose krejt veçoritë të mos funksionojnë. Nëse doni ta provoni sido qoftë, mund të vazhdoni, por mos u ankoni për çfarëdo problemesh që mund të hasni!",
"Checking for an update...": "Po kontrollohet për një përditësim…",
"There are advanced notifications which are not shown here": "Ka njoftime të thelluara që nuk shfaqen këtu",
"Show empty room list headings": "Shfaqi emrat e listave të zbrazëta dhomash"
"Show empty room list headings": "Shfaqi emrat e listave të zbrazëta dhomash",
"PM": "PM",
"AM": "AM",
"Room name or alias": "Emër dhome ose alias",
"Unknown (user, device) pair:": "Çift (përdorues, pajisje) i panjohur:",
"Device already verified!": "Pajisjeje tashmë e verifikuar!",
"Verified key": "Kyç i verifikuar",
"Unrecognised command:": "Urdhër jo i pranuar: ",
"Reason": "Arsye",
"%(senderName)s requested a VoIP conference.": "%(senderName)s kërkoi një konferencë VoIP.",
"VoIP conference started.": "Konferenca VoIP filloi.",
"VoIP conference finished.": "Konferenca VoIP përfundoi.",
"Someone": "Dikush",
"(no answer)": "(ska përgjigje)",
"%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për krejt anëtarët e dhomës, prej çastit kur janë ftuar.",
"%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për krejt anëtarët e dhomës, prej çastit kur morën pjesë.",
"%(senderName)s made future room history visible to all room members.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për krejt anëtarët e dhomës.",
"%(senderName)s made future room history visible to anyone.": "%(senderName)s e bëri historikun e ardhshëm të dhomës të dukshëm për këdo.",
"%(displayName)s is typing": "%(displayName)s po shtyp",
"Use compact timeline layout": "Përdorni skemë grafike kompakte për rrjedhën kohore",
"Always show message timestamps": "Shfaq përherë vula kohore për mesazhet",
"Room Colour": "Ngjyrë Dhome",
"unknown caller": "thirrës i panjohur",
"Decline": "Hidhe poshtë",
"Accept": "Pranoje",
"Incorrect verification code": "Kod verifikimi i pasaktë",
"Enter Code": "Jepni Kod",
"Submit": "Parashtroje",
"Phone": "Telefon",
"Add phone number": "Shtoni numër telefoni",
"Add": "Shtojeni",
"Upload new:": "Ngarko të re:",
"No display name": "Ska emër shfaqjeje",
"New passwords don't match": "Fjalëkalimet e reja spërputhen",
"Passwords can't be empty": "Fjalëkalimet smund të jenë të zbrazët",
"Export E2E room keys": "Eksporto kyçe dhome E2E",
"Do you want to set an email address?": "Doni të caktoni një adresë email?",
"Current password": "Fjalëkalimi i tanishëm",
"Password": "Fjalëkalim",
"New Password": "Fjalëkalim i Ri",
"Confirm password": "Ripohoni frazëkalimin",
"Change Password": "Ndryshoni Fjalëkalimin",
"Authentication": "Mirëfilltësim",
"Delete %(count)s devices|one": "Fshije pajisjen",
"Device ID": "ID Pajisjeje",
"Device Name": "Emër Pajisjeje",
"Last seen": "Parë së fundi më",
"Select devices": "Përzgjidhni pajisje",
"Disable Notifications": "Çaktivizo Njoftimet",
"Enable Notifications": "Aktivizo Njoftimet",
"Invalid alias format": "Format i pavlefshëm aliasesh",
"Invalid address format": "Format i pavlefshëm adresash",
"not specified": "e papërcaktuar",
"not set": "sështë caktuar",
"Remote addresses for this room:": "Adresa të largëta për këtë dhomë:",
"Addresses": "Adresa",
"The main address for this room is": "Adresa kryesore për këtë dhomë është",
"Local addresses for this room:": "Adresa vendore për këtë dhomë:",
"This room has no local addresses": "Kjo dhomë ska adresë vendore",
"Invalid community ID": "ID bashkësie e pavlefshme",
"URL Previews": "Paraparje URL-sh",
"Add a widget": "Shtoni një widget",
"Drop File Here": "Hidheni Kartelën Këtu",
"Drop file here to upload": "Hidheni kartelën këtu që të ngarkohet",
" (unsupported)": " (e pambuluar)",
"%(senderName)s sent an image": "%(senderName)s dërgoi një figurë",
"%(senderName)s sent a video": "%(senderName)s dërgoi një video",
"Options": "Mundësi",
"Key request sent.": "Kërkesa për kyç u dërgua.",
"Encrypted by a verified device": "Fshehtëzuar nga një pajisje e verifikuar",
"Encrypted by an unverified device": "Fshehtëzuar nga një pajisje e paverifikuar",
"Unencrypted message": "Mesazh i pafshehtëzuar",
"Please select the destination room for this message": "Ju lutemi, përzgjidhni dhomën vendmbërritje për këtë mesazh",
"Blacklisted": "Në Listë të Zezë",
"Verified": "E verifikuar",
"Unverified": "I paverifikuar",
"device id: ": "ID pajisjeje: ",
"Kick": "Përzëre",
"Kick this user?": "Të përzihet ky përdorues?",
"Unban": "Hiqja dëbimin",
"Ban": "Dëboje",
"Ban this user?": "Të dëbohet ky përdorues?",
"Are you sure?": "Jeni i sigurt?",
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Sdo të jeni në gjendje ta zhbëni këtë ndryshim, ngaqë po e promovoni përdoruesin të ketë të njëjtën shkallë pushteti si ju vetë.",
"No devices with registered encryption keys": "Ska pajisje me kyçe fshehtëzimi të regjistruar",
"Devices": "Pajisje",
"Unignore": "Shpërfille",
"Ignore": "Shpërfille",
"Mention": "Përmendje",
"Invite": "Ftoje",
"User Options": "Mundësi Përdoruesi",
"Direct chats": "Fjalosje të drejtpërdrejta",
"Make Moderator": "Kaloje Moderator",
"Admin Tools": "Mjete Përgjegjësi",
"Level:": "Nivel:",
"and %(count)s others...|other": "dhe %{count} të tjerë…",
"and %(count)s others...|one": "dhe një tjetër…",
"Filter room members": "Filtroni anëtarë dhome",
"Attachment": "Bashkëngjitje",
"Upload Files": "Ngarkoni Kartela",
"Are you sure you want to upload the following files?": "Jeni i sigurt se doni të ngarkohen kartelat vijuese?",
"Encrypted room": "Dhomë e fshehtëzuar",
"Unencrypted room": "Dhomë e pafshehtëzuar",
"Voice call": "Thirrje audio",
"Video call": "Thirrje video",
"Upload file": "Ngarkoni kartelë",
"Show Text Formatting Toolbar": "Shfaq Panel Formatimi Tekstesh",
"Send an encrypted reply…": "Dërgoni një përgjigje të fshehtëzuar…",
"Send a reply (unencrypted)…": "Dërgoni një përgjigje (të pafshehtëzuar)…",
"Send an encrypted message…": "Dërgoni një mesazh të fshehtëzuar…",
"Send a message (unencrypted)…": "Dërgoni një mesazh (të pafshehtëzuar)…",
"You do not have permission to post to this room": "Skeni leje të postoni në këtë dhomë",
"Server error": "Gabim shërbyesi",
"Command error": "Gabim urdhri",
"bold": "të trasha",
"italic": "të pjerrta",
"Markdown is disabled": "Markdown është i çaktivizuar",
"Markdown is enabled": "Markdown është i aktivizuar",
"Loading...": "Po ngarkohet…",
"Pinned Messages": "Mesazhe të Fiksuar",
"Jump to message": "Kalo te mesazhi",
"%(duration)ss": "%(duration)ss",
"%(duration)sm": "%(duration)sm",
"%(duration)sh": "%(duration)sh",
"%(duration)sd": "%(duration)sd",
"Online for %(duration)s": "Në linjë për %(duration)s",
"Idle for %(duration)s": "I plogësht për %(duration)s",
"Offline for %(duration)s": "Jo në linjë për %(duration)s",
"Idle": "I plogësht",
"Offline": "Jo në linjë",
"Unknown": "I panjohur",
"Seen by %(userName)s at %(dateTime)s": "Parë nga %(userName)s më %(dateTime)s",
"Save": "Ruaje",
"Join Room": "Hyni në dhomë",
"Upload avatar": "Ngarkoni avatar",
"Remove avatar": "Hiqe avatarin",
"Settings": "Rregullime",
"Forget room": "Harroje dhomën",
"Show panel": "Shfaq panel",
"Community Invites": "Ftesa Bashkësie",
"Invites": "Ftesa",
"Favourites": "Të parapëlqyer",
"People": "Persona",
"Low priority": "Me përparësi të ulët",
"You may wish to login with a different account, or add this email to this account.": "Mund të doni të hyni me një llogari tjetër, ose ta shtoni këtë email te kjo llogari.",
"You have been invited to join this room by %(inviterName)s": "Jeni ftuar të merrni pjesë në këtë dhomë nga %(inviterName)s",
"Reason: %(reasonText)s": "Arsye: %(reasonText)s",
"Rejoin": "Rifutuni",
"You have been kicked from %(roomName)s by %(userName)s.": "Jeni përzënë prej %(roomName)s nga %(userName)s.",
"You have been kicked from this room by %(userName)s.": "Jeni përzënë nga kjo dhomë prej %(userName)s.",
"You have been banned from %(roomName)s by %(userName)s.": "Jeni dëbuar prej %(roomName)s ngas %(userName)s.",
"You have been banned from this room by %(userName)s.": "Jeni dëbuar prej kësaj dhome nga %(userName)s.",
"This room": "Këtë dhomë",
"%(roomName)s does not exist.": "%(roomName)s sekziston.",
"You are trying to access a room.": "Po provoni të hyni në një dhomë.",
"This is a preview of this room. Room interactions have been disabled": "Kjo është një paraparje e kësaj dhome. Ndërveprimet në dhomë janë çaktivizuar",
"Banned by %(displayName)s": "Dëbuar nga %(displayName)s",
"Privacy warning": "Sinjalizim privatësie",
"The visibility of existing history will be unchanged": "Dukshmëria e historikut ekzistues nuk do të ndryshohet.",
"You should not yet trust it to secure data": "Sduhet ti zini ende besë për sigurim të dhënash.",
"Enable encryption": "Aktivizoni fshehtëzim",
"Encryption is enabled in this room": "Në këtë dhomë është i aktivizuar fshehtëzimi",
"Encryption is not enabled in this room": "Në këtë dhomë sështë i aktivizuar fshehtëzimi",
"The default role for new room members is": "Roli parazgjedhje për përdorues të ri në dhomë është",
"Privileged Users": "Përdorues të Privilegjuar",
"Banned users": "Përdorues të dëbuar",
"Leave room": "Dilni nga dhomë",
"Tagged as: ": "Etiketuar me:",
"Click here to fix": "Klikoni këtu për ta ndrequr",
"Who can access this room?": "Kush mund të hyjë në këtë dhomë?",
"Only people who have been invited": "Vetëm persona që janë ftuar",
"Anyone who knows the room's link, apart from guests": "Cilido që di lidhjen e dhomës, hiq vizitorët",
"Anyone who knows the room's link, including guests": "Cilido që di lidhjen e dhomës, përfshi vizitorë",
"Who can read history?": "Kush mund të lexojë historikun?",
"Anyone": "Cilido",
"Permissions": "Leje",
"Advanced": "Të mëtejshme",
"Add a topic": "Shtoni një temë",
"Decrypt %(text)s": "Shfshehtëzoje %(text)s",
"Copied!": "U kopjua!",
"Add an Integration": "Shtoni një Integrim",
"This Home Server would like to make sure you are not a robot": "Ky Shërbyes Home do të donte të sigurohej se sjeni robot",
"Sign in with CAS": "Hyni me CAS",
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Mund ti përdorni mundësitë e shërbyesit vetjak për të hyrë në shërbyes të tjerë Matrix, duke dhënë URL-në e një tjetër shërbyesi Home.",
"Password:": "Fjalëkalim:",
"Please check your email to continue registration.": "Ju lutemi, që të vazhdojë regjistrimi, kontrolloni email-in tuaj.",
"Please enter the code it contains:": "Ju lutemi, jepni kodin që përmbahet:",
"Code": "Kod",
"Start authentication": "Fillo mirëfilltësim",
"Username on %(hs)s": "Emër përdoruesi në %(hs)s",
"User name": "Emër përdoruesi",
"Mobile phone number": "Numër telefoni celular",
"Forgot your password?": "Harruat fjalëkalimin tuaj?",
"Sign in with": "Hyni me",
"Email address": "Adresë email",
"Sign in": "Hyni",
"Email address (optional)": "Adresë email (opsionale)",
"You are registering with %(SelectedTeamName)s": "Po regjistroheni te %(SelectedTeamName)s",
"Mobile phone number (optional)": "Numër telefoni celular (opsionale)",
"Default server": "Shërbyes parazgjedhje",
"Custom server": "Shërbyes vetjak",
"Home server URL": "URL shërbyesi Home",
"Identity server URL": "URL shërbyesi identitetesh",
"What does this mean?": do të thotë kjo?",
"Filter community members": "Filtroni anëtarë bashkësie",
"Something went wrong!": "Diçka shkoi ters!",
"Visibility in Room List": "Dukshmëri në Listë Dhomash",
"Visible to everyone": "I dukshëm për këdo",
"Only visible to community members": "E dukshme vetëm për anëtarë të bashkësisë",
"Filter community rooms": "Filtroni dhoma bashkësie",
"You're not currently a member of any communities.": "Hëpërhë, sjeni anëtar i ndonjë bashkësie.",
"Unknown Address": "Adresë e Panjohur",
"Allow": "Lejoje",
"Revoke widget access": "Shfuqizo hyrje widget",
"Create new room": "Krijoni dhomë të re",
"Unblacklist": "Hiqe nga listë e zezë",
"Blacklist": "Listë e zezë",
"Unverify": "Hiqi verifikimin",
"Verify...": "Verifikoni…",
"No results": "Ska përfundime",
"Delete": "Fshije",
"Communities": "Bashkësi",
"Home": "Kreu",
"Integrations Error": "Gabim Integrimesh",
"Could not connect to the integration server": "Su lidh dot te shërbyesi i integrimt",
"Manage Integrations": "Administroni Integrime",
"were invited %(count)s times|one": "janë ftuar",
"was invited %(count)s times|other": "është ftuar %(count)s herë",
"was invited %(count)s times|one": "është ftuar",
"were banned %(count)s times|one": "janë dëbuar",
"was banned %(count)s times|other": "është dëbuar %(count)s herë",
"was banned %(count)s times|one": "është dëbuar",
"were unbanned %(count)s times|one": "u është hequr dëbimi",
"was unbanned %(count)s times|other": "i është hequr dëbimi %(count)s herë",
"was unbanned %(count)s times|one": "i është hequr dëbimi",
"were kicked %(count)s times|one": "janë përzënë",
"was kicked %(count)s times|other": "është përzënë %(count)s herë",
"was kicked %(count)s times|one": "është përzënë",
"%(items)s and %(count)s others|other": "%(items)s dhe %(count)s të tjerë",
"%(items)s and %(count)s others|one": "%(items)s dhe një tjetër",
"%(items)s and %(lastItem)s": "%(items)s dhe %(lastItem)s",
"collapse": "tkurre",
"expand": "zgjeroje",
"Custom level": "Nivel vetjak",
"<a>In reply to</a> <pill>": "<a>Në Përgjigje të</a> <pill>",
"Room directory": "Drejtori Dhome",
"Start chat": "Filloni fjalosje",
"Add User": "Shtoni Përdorues",
"Matrix ID": "ID Matrix",
"Matrix Room ID": "ID Matrix dhome",
"email address": "adresë email",
"You have entered an invalid address.": "Keni dhënë adresë email të pavlefshme.",
"Start new chat": "Filloni fjalosje të re",
"Confirm Removal": "Ripohoni Heqjen",
"Create Community": "Krijoje Bashkësinë",
"Community Name": "Emër Bashkësie",
"Example": "Shembull",
"Community ID": "ID Bashkësie",
"example": "shembull",
"Create": "Krijoje",
"Create Room": "Krijoje Dhomën",
"Room name (optional)": "Emër dhome (në daçi)",
"Advanced options": "Mundësi të mëtejshme",
"Unknown error": "Gabim i panjohur",
"Incorrect password": "Fjalëkalim i pasaktë",
"Deactivate Account": "Çaktivizoje Llogarinë",
"Device name": "Emër pajisjeje",
"Device key": "Kyç pajisjeje",
"Verify device": "Verifiko pajisjen",
"An error has occurred.": "Ndodhi një gabim.",
"You added a new device '%(displayName)s', which is requesting encryption keys.": "Shtuat një pajisje të re '%(displayName)s', e cila po kërkon kyçe fshehtëzimi.",
"Your unverified device '%(displayName)s' is requesting encryption keys.": "Pajisja juaj e paverifikuar '%(displayName)s' po kërkon kyçe fshehtëzimi.",
"Start verification": "Fillo verifikimin",
"Share without verifying": "Ndajeni me të tjerë pa e verifikuar",
"Ignore request": "Shpërfille kërkesën",
"Loading device info...": "Po ngarkohen të dhëna pajisjeje…",
"Encryption key request": "Kërkesë kyçi fshehtëzimesh",
"Invalid Email Address": "Adresë Email e Pavlefshme",
"This doesn't appear to be a valid email address": "Kjo sduket se është adresë email e vlefshme",
"Verification Pending": "Verifikim Në Pritje të Miratimit",
"Skip": "Anashkaloje",
"User names may only contain letters, numbers, dots, hyphens and underscores.": "Emrat e përdoruesve mund të përmbajnë vetëm shkronja, numra, pika, vija ndarëse dhe nënvija",
"Username not available": "Emri i përdoruesit sështë i lirë",
"Username invalid: %(errMessage)s": "Emër përdoruesi i pavlefshëm: %(errMessage)s",
"Username available": "Emri i përdoruesit është i lirë",
"Room contains unknown devices": "Dhoma përmban pajisje të panjohura",
"Unknown devices": "Pajisje të panjohura",
"Private Chat": "Fjalosje Private",
"Public Chat": "Fjalosje Publike",
"Custom": "Vetjak",
"Alias (optional)": "Alias (opsional)",
"Name": "Emër",
"Topic": "Temë",
"Make this room private": "Kaloje këtë dhomë si private",
"Encrypt room": "Fshehtëzoje dhomën",
"You must <a>register</a> to use this functionality": "Që të përdorni këtë funksion, duhet të <a>regjistroheni</a>",
"Add rooms to the community summary": "Shtoni dhoma te përmbledhja mbi bashkësinë",
"Add to summary": "Shtoje te përmbledhja",
"Add a Room": "Shtoni një Dhomë",
"Add users to the community summary": "Shtoni përdorues te përmbledhja mbi bashkësinë",
"Who would you like to add to this summary?": "Kë do të donit të shtonit te kjo përmbledhje?",
"Add a User": "Shtoni një Përdorues",
"Leave Community": "Braktiseni Bashkësinë",
"Leave %(groupName)s?": "Të braktiset {groupName}?",
"Community Settings": "Rregullime Bashkësie",
"%(inviter)s has invited you to join this community": "%s ju ftoi të bëheni pjesë e kësaj bashkësie",
"You are an administrator of this community": "Jeni një përgjegjës i kësaj bashkësie",
"You are a member of this community": "Jeni anëtar i këtij ekipi",
"Long Description (HTML)": "Përshkrim i Gjatë (HTML)",
"Description": "Përshkrim",
"Reject invitation": "Hidheni tej ftesën",
"Are you sure you want to reject the invitation?": "Jeni i sigurt se doni të hidhet tej kjo ftesë?",
"Are you sure you want to leave the room '%(roomName)s'?": "Jeni i sigurt se doni të dilni nga dhoma '%(roomName)s'?",
"Signed Out": "I dalë",
"Old cryptography data detected": "U pikasën të dhëna kriptografie të vjetër",
"Logout": "Dalje",
"Your Communities": "Bashkësitë Tuaja",
"Create a new community": "Krijoni një bashkësi të re",
"You have no visible notifications": "Skeni njoftime të dukshme.",
"%(count)s of your messages have not been sent.|other": "Disa nga mesazhet tuaj sjanë dërguar.",
"%(count)s of your messages have not been sent.|one": "Mesazhi juaj su dërgua.",
"%(count)s new messages|other": "%(count)s mesazhe të rinj",
"%(count)s new messages|one": "%(count)s mesazh i ri",
"Active call": "Thirrje aktive",
"You seem to be in a call, are you sure you want to quit?": "Duket se jeni në një thirrje, jeni i sigurt se doni të dilet?",
"Search failed": "Kërkimi shtoi",
"No more results": "Jo më tepër përfundime",
"Room": "Dhomë",
"Clear filter": "Pastroje filtrin",
"Uploading %(filename)s and %(count)s others|other": "Po ngarkohet %(filename)s dhe %(count)s të tjera",
"Uploading %(filename)s and %(count)s others|zero": "Po ngarkohet %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Po ngarkohet %(filename)s dhe %(count)s tjetër",
"Light theme": "Temë e çelët",
"Dark theme": "Temë e errët",
"Status.im theme": "Temë Status.im",
"Can't load user settings": "Sngarkohen dot rregullime përdoruesi",
"Server may be unavailable or overloaded": "Shërbyesi mund të jetë i pakapshëm ose i mbingarkuar",
"Sign out": "Dilni",
"Success": "Sukses",
"Remove %(threePid)s?": "Të hiqet %(threePid)s?",
"Refer a friend to Riot:": "Tregojini një shoku për Riot-in:",
"Interface Language": "Gjuhë Ndërfaqeje",
"User Interface": "Ndërfaqe Përdoruesi",
"Cryptography": "Kriptografi",
"Device ID:": "ID Pajisjeje:",
"Device key:": "Kyç pajisjeje:",
"Ignored Users": "Përdorues të Shpërfillur",
"Riot collects anonymous analytics to allow us to improve the application.": "Riot-i grumbullon të dhëna analitike anonime që të na lejojë ta përmirësojmë aplikacionin.",
"These are experimental features that may break in unexpected ways": "Këto janë veçori eksperimentale që mund të ngecin në rrugë të papritura",
"Use with caution": "Përdoreni me masë",
"Deactivate my account": "Çaktivizoje llogarinë time",
"Clear Cache": "Pastroje Fshehtinën",
"Updates": "Përditësime",
"Check for update": "Kontrollo për përditësime",
"Bulk Options": "Veprime Masive",
"No media permissions": "Ska leje mediash",
"No Microphones detected": "Su pikasën Mikrofona",
"No Webcams detected": "Su pikasën kamera",
"Default Device": "Pajisje Parazgjedhje",
"Microphone": "Mikrofon",
"Camera": "Kamerë",
"VoIP": "VOIP",
"Email": "Email",
"Add email address": "Shtoni adresë email",
"Profile": "Profil",
"Display name": "Emër në ekran",
"Account": "Llogari",
"Logged in as:": "I futur si:",
"Access Token:": "Token Hyrjesh:",
"Identity Server is": "Shërbyes Identitetesh është",
"riot-web version:": "Version riot-web:",
"olm version:": "version olm:",
"The email address linked to your account must be entered.": "Duhet dhënë adresa email e lidhur me llogarinë tuaj.",
"Your password has been reset": "Fjalëkalimi juaj u ricaktua",
"Return to login screen": "Kthehuni te skena e hyrjeve",
"New password": "Fjalëkalim i ri",
"Confirm your new password": "Ripohoni fjalëkalimin tuaj të ri",
"Send Reset Email": "Dërgo Email Ricaktimi",
"Create an account": "Krijoni një llogari",
"Incorrect username and/or password.": "Emër përdoruesi dhe/ose fjalëkalim i pasaktë.",
"The phone number entered looks invalid": "Numri i telefonit që u dha duket i pavlefshëm",
"Sign in to get started": "Që tia filloni, bëni hyrjen",
"Set a display name:": "Caktoni emër ekrani",
"Upload an avatar:": "Ngarkoni një avatar:",
"This server does not support authentication with a phone number.": "Ky shërbyes nuk mbulon mirëfilltësim me një numër telefoni.",
"Missing password.": "Mungon fjalëkalimi.",
"Passwords don't match.": "Fjalëkalimet spërputhen.",
"This doesn't look like a valid email address.": "Kjo sduket si adresë email e vlefshme.",
"This doesn't look like a valid phone number.": "Ky sduket si numër telefoni i vlefshëm.",
"You need to enter a user name.": "Lypset të jepni një emër përdoruesi.",
"I already have an account": "Kam tashmë një llogari",
"Bans user with given id": "Dëbon përdoruesin me ID-në e dhënë",
"Unbans user with given id": "I heq dëbimin përdoruesit me ID-në e dhënë",
"Sets the room topic": "Caktoni temë dhome",
"Kicks user with given id": "Përzë përdoruesin me ID-në e dhënë",
"Opens the Developer Tools dialog": "Hap dialogun Mjete Zhvilluesi",
"Commands": "Urdhra",
"Results from DuckDuckGo": "Përfundimet vijnë nga DuckDuckGo",
"Notify the whole room": "Njofto krejt dhomën",
"Room Notification": "Njoftim Dhome",
"Users": "Përdorues",
"unknown device": "pajisje e panjohur",
"NOT verified": "JO e verifikua",
"verified": "e verifikuar",
"Verification": "Verifikim",
"User ID": "ID përdoruesi",
"Curve25519 identity key": "Kyç identiteti Curve25519",
"none": "asnjë",
"Algorithm": "Algoritëm",
"unencrypted": "të pafshehtëzuara",
"Decryption error": "Gabim shfshehtëzimi",
"Session ID": "ID sesioni",
"End-to-end encryption information": "Të dhëna fshehtëzimi skaj-më-skaj",
"Event information": "Të dhëna akti",
"Sender device information": "Të dhëna pajisjeje dërguesi",
"Passphrases must match": "Frazëkalimet duhet të përputhen",
"Passphrase must not be empty": "Frazëkalimi smund të jetë i zbrazët",
"Export room keys": "Eksporto kyçe dhome",
"This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Ky proces ju lejon të eksportoni te një kartelë vendore kyçet për mesazhe që keni marrë në dhoma të fshehtëzuara. Mandej do të jeni në gjendje ta importoni kartelën te një tjetër klient Matrix në të ardhmen, që kështu ai klient të jetë në gjendje ti fshehtëzojë këto mesazhe.",
"Enter passphrase": "Jepni frazëkalimin",
"Confirm passphrase": "Ripohoni frazëkalimin",
"Export": "Eksporto",
"Import room keys": "Importo kyçe dhome",
"This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Ky proces ju lejon të importoni kyçe fshehtëzimi që keni eksportuar më parë nga një tjetër klient Matrix. Mandej do të jeni në gjendje të shfshehtëzoni çfarëdo mesazhesh që mund të shfshehtëzojë ai klient tjetër.",
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Kartela e eksportit është e mbrojtur me një frazëkalim. Që të shfshehtëzoni kartelën, duhet ta jepni frazëkalimin këtu."
}

View file

@ -1267,5 +1267,28 @@
"You need to register to do this. Would you like to register now?": "Du måste registrera dig för att göra detta. Vill du registrera dig nu?",
"Forces the current outbound group session in an encrypted room to be discarded": "Tvingar den aktuella utgående gruppsessionen i ett krypterat rum att överges",
"Unable to connect to Homeserver. Retrying...": "Det gick inte att ansluta till hemserver. Försöker igen ...",
"Unable to query for supported registration methods": "Det gick inte att hämta stödda registreringsmetoder"
"Unable to query for supported registration methods": "Det gick inte att hämta stödda registreringsmetoder",
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s satte huvudadressen för detta rum till %(address)s.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s lade till %(addedAddresses)s som adresser för detta rum.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s lade till %(addedAddresses)s som adress för detta rum.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s tog bort %(removedAddresses)s som adresser för detta rum.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s tog bort %(removedAddresses)s som adress för detta rum.",
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s lade till %(addedAddresses)s och tog bort %(removedAddresses)s som adresser för detta rum.",
"%(senderName)s removed the main address for this room.": "%(senderName)s tog bort huvudadressen för detta rum.",
"Pin unread rooms to the top of the room list": "Nåla fast olästa rum längst upp i rumslistan",
"Pin rooms I'm mentioned in to the top of the room list": "Nåla fast rum jag nämns i längst upp i rumslistan",
"Joining room...": "Går med i rum...",
"Add some now": "Lägg till några nu",
"Please review and accept the policies of this homeserver:": "Granska och acceptera policyn för denna hemserver:",
"Before submitting logs, you must <a>create a GitHub issue</a> to describe your problem.": "Innan du skickar in loggar måste du <a>skapa en GitHub-bugg</a> för att beskriva problemet.",
"What GitHub issue are these logs for?": "Vilken GitHub-bugg är dessa loggar för?",
"Updating Riot": "Uppdaterar Riot",
"Submit Debug Logs": "Skicka felsökningsloggar",
"An email address is required to register on this homeserver.": "En epostadress krävs för att registrera sig på denna hemserver.",
"A phone number is required to register on this homeserver.": "Ett telefonnummer krävs för att registrera sig på denna hemserver.",
"Open Devtools": "Öppna Devtools",
"Show developer tools": "Visa utvecklingsverktyg",
"You are currently using Riot anonymously as a guest.": "Du använder för närvarande Riot anonymt som gäst.",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Du är administratör för denna community. Du kommer inte kunna gå med igen utan en inbjudan från en annan administratör.",
"If you would like to create a Matrix account you can <a>register</a> now.": "Om du vill skapa ett Matrix-konto kan du <a>registrera dig</a> nu."
}

View file

@ -53,10 +53,10 @@
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s గది పేరు తొలగించబడింది.",
"Changes to who can read history will only apply to future messages in this room": "చరిత్ర చదివేవారికి మార్పులు ఈ గదిలో భవిష్య సందేశాలకు మాత్రమే వర్తిస్తాయి",
"Changes your display nickname": "మీ ప్రదర్శన మారుపేరుని మారుస్తుంది",
"You cannot place a call with yourself.": "మీరు మీతో కాల్ చేయలేరు.",
"You cannot place a call with yourself.": "మీకు మీరే కాల్ చేయలేరు.",
"You are already in a call.": "మీరు ఇప్పటికే కాల్లో ఉన్నారు.",
"You are trying to access %(roomName)s.": "మీరు %(roomName)s లను యాక్సెస్ చేయడానికి ప్రయత్నిస్తున్నారు.",
"You cannot place VoIP calls in this browser.": "మీరు ఈ బ్రౌజర్లో VoIP కాల్లను ఉంచలేరు.",
"You cannot place VoIP calls in this browser.": "మీరు ఈ బ్రౌజర్లో కాల్లను చేయలేరు.",
"You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "మీరు అన్ని పరికరాల నుండి లాగ్ అవుట్ అయ్యారు మరియు ఇకపై పుష్ ఉండదు.\nప్రకటనలను నోటిఫికేషన్లను పునఃప్రారంభించడానికి, ప్రతి పరికరంలో మళ్లీ సైన్ ఇన్ చేయండి",
"You have no visible notifications": "మీకు కనిపించే నోటిఫికేషన్లు లేవు",
"You need to be able to invite users to do that.": "మీరు దీన్ని చేయడానికి వినియోగదారులను ఆహ్వానించగలరు.",
@ -275,5 +275,26 @@
"#example": "#ఉదాహరణ",
"Collapse panel": "ప్యానెల్ కుదించు",
"Checking for an update...": "నవీకరణ కోసం చూస్తోంది...",
"Saturday": "శనివారం"
"Saturday": "శనివారం",
"This email address is already in use": "ఈ ఇమెయిల్ అడ్రస్ ఇప్పటికే వాడుకం లో ఉంది",
"This phone number is already in use": "ఈ ఫోన్ నంబర్ ఇప్పటికే వాడుకం లో ఉంది",
"Failed to verify email address: make sure you clicked the link in the email": "ఇమెయిల్ అడ్రస్ ని నిరూపించలేక పోయాము. ఈమెయిల్ లో వచ్చిన లింక్ ని నొక్కారా",
"The platform you're on": "మీరు ఉన్న ప్లాట్ఫార్మ్",
"The version of Riot.im": "రయట్.ఐఎమ్ యొక్క వెర్సన్",
"Your homeserver's URL": "మీ హోమ్ సర్వర్ యొక్క URL",
"Your identity server's URL": "మీ ఐడెంటిటి సర్వర్ యొక్క URL",
"e.g. %(exampleValue)s": "ఉ.దా. %(exampleValue)s 1",
"Every page you use in the app": "ఆప్ లో మీరు వాడే ప్రతి పేజి",
"e.g. <CurrentPageURL>": "ఉ.దా. <CurrentPageURL>",
"Your User Agent": "మీ యీసర్ ఏజెంట్",
"Call Failed": "కాల్ విఫలమయింది",
"Review Devices": "పరికరాలని ఒక మారు చూసుకో",
"Call": "కాల్",
"Answer": "ఎత్తు",
"The remote side failed to pick up": "అటు వైపు ఎత్తలేకపోయారు",
"Unable to capture screen": "తెరని చూపలేకపోతున్నారు",
"Existing Call": "నజుస్తున్న కాల్",
"VoIP is unsupported": "కాల్ చేయుట ఈ పరికరం పోషించలేదు",
"A conference call could not be started because the intgrations server is not available": "ఇంటిగ్రేషన్ సర్వర్ లేనప్పుడు కాన్ఫరెన్స్ కాల్ మొదలుపెట్టలేరు",
"Call in Progress": "నడుస్తున్న కాల్"
}

View file

@ -23,8 +23,8 @@
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Текстове повідомлення було надіслано +%(msisdn)s. Введіть, будь ласка, код підтвердження з цього повідомлення",
"Accept": "Прийняти",
"Account": "Обліковка",
"%(targetName)s accepted an invitation.": "%(targetName)s прийняв запрошення.",
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s прийняв запрошення від %(displayName)s.",
"%(targetName)s accepted an invitation.": "%(targetName)s прийняв/ла запрошення.",
"%(targetName)s accepted the invitation for %(displayName)s.": "Запрошення від %(displayName)s прийнято %(targetName)s.",
"Access Token:": "Токен доступу:",
"Active call (%(roomName)s)": "Активний виклик (%(roomName)s)",
"Add": "Додати",
@ -57,7 +57,7 @@
"A new password must be entered.": "Має бути введений новий пароль.",
"Add a widget": "Добавити віджет",
"Allow": "Принюти",
"%(senderName)s answered the call.": "%(senderName)s відповіла на дзвінок.",
"%(senderName)s answered the call.": "%(senderName)s відповів/ла на дзвінок.",
"An error has occurred.": "Трапилась помилка.",
"Anyone": "Кожний",
"Anyone who knows the room's link, apart from guests": "Кожний, хто знає посилання на кімнату, окрім гостей",
@ -68,10 +68,10 @@
"Are you sure you want to upload the following files?": "Ви впевнені, що ви хочете відправити наступний файл?",
"Attachment": "Прикріплення",
"Autoplay GIFs and videos": "Автовідтворення GIF і відео",
"%(senderName)s banned %(targetName)s.": "%(senderName)s заблокував(ла) %(targetName)s.",
"%(senderName)s banned %(targetName)s.": "%(senderName)s заблокував/ла %(targetName)s.",
"Ban": "Заблокувати",
"Banned users": "Заблоковані користувачі",
"Bans user with given id": "Блокує користувача з заданим ID",
"Bans user with given id": "Блокує користувача з вказаним ID",
"Blacklisted": "В чорному списку",
"Bulk Options": "Групові параметри",
"Call Timeout": "Час очікування виклика",
@ -79,22 +79,22 @@
"Can't load user settings": "Неможливо завантажити настройки користувача",
"Cannot add any more widgets": "Неможливо додати більше віджетів",
"Change Password": "Поміняти пароль",
"%(senderName)s changed their profile picture.": "%(senderName)s змінив зображення профіля.",
"%(senderName)s changed their profile picture.": "%(senderName)s змінив/ла зображення профілю.",
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s змінив(ла) рівень доступу для %(powerLevelDiffText)s.",
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s змінив(ла) назву кімнати на %(roomName)s.",
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s змінив/ла назву кімнати на %(roomName)s.",
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s видалив ім'я кімнати.",
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s змінив тему на %(topic)s.",
"Email": "е-пошта",
"Email address": "Адреса е-почти",
"Email address (optional)": "Адреса е-почти (не обов'язково)",
"Email, name or matrix ID": "Е-почта, ім'я або matrix ID",
"Email, name or matrix ID": "Електронна пошта, ім'я або matrix ID",
"Failed to send email": "Помилка відправки е-почти",
"Edit": "Редактувати",
"Unpin Message": "Відкріпити повідомлення",
"Register": "Зарегіструватись",
"Register": "Зареєструватися",
"Rooms": "Кімнати",
"Add rooms to this community": "Добавити кімнати в це суспільство",
"This email address is already in use": "Ця е-пошта вже використовується",
"This email address is already in use": "Ця електронна пошта вже використовується",
"This phone number is already in use": "Цей телефонний номер вже використовується",
"Fetching third party location failed": "Не вдалось отримати стороннє місцеперебування",
"Messages in one-to-one chats": "Повідомлення у чатах \"сам на сам\"",
@ -267,10 +267,10 @@
"The version of Riot.im": "Версія Riot.im",
"Whether or not you're logged in (we don't record your user name)": "Чи увійшли ви, чи ні (ми не зберігаємо ваше ім'я користувача)",
"Your language of choice": "Обрана мова",
"Which officially provided instance you are using, if any": "Яким офіційно наданим примірником ви користуєтесь (якщо користуєтесь)",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Чи використовуєте ви режим Richtext у редакторі Rich Text Editor",
"Your homeserver's URL": "URL адреса вашого домашнього серверу",
"Failed to verify email address: make sure you clicked the link in the email": "Не вдалось перевірити адресу е-пошти: переконайтесь, що ви перейшли за посиланням у листі",
"Which officially provided instance you are using, if any": "Яким офіційно наданим клієнтом ви користуєтесь (якщо користуєтесь)",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Чи використовуєте ви режим форматованого тексту у редакторі Rich Text Editor",
"Your homeserver's URL": "URL адреса вашого домашнього сервера",
"Failed to verify email address: make sure you clicked the link in the email": "Не вдалось перевірити адресу електронної пошти: переконайтесь, що ви перейшли за посиланням у листі",
"The platform you're on": "Використовувана платформа",
"Your identity server's URL": "URL адреса серверу ідентифікації",
"e.g. %(exampleValue)s": "напр. %(exampleValue)s",
@ -329,5 +329,248 @@
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s, %(day)s, %(time)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s, %(day)s, %(fullYear)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
"Who would you like to add to this community?": "Кого ви хочете додати до цієї спільноти?"
"Who would you like to add to this community?": "Кого ви хочете додати до цієї спільноти?",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Якщо сторінка містить ідентифікаційну інформацію, як то ідентифікатор кімнати, користувача чи групи, ці дані будуть вилучені перед надсиланням на сервер.",
"Could not connect to the integration server": "Неможливо приєднатися до інтеграційного сервера",
"A conference call could not be started because the intgrations server is not available": "Неможливо здійснити дзвінок-конференцію, оскільки інтеграційний сервер недоступний",
"Call in Progress": "Іде виклик",
"A call is currently being placed!": "Зараз іде виклик!",
"A call is already in progress!": "Вже здійснюється дзвінок!",
"Permission Required": "Потрібен дозвіл",
"You do not have permission to start a conference call in this room": "У вас немає дозволу, щоб розпочати дзвінок-конференцію в цій кімнаті",
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Зверніть увагу: будь-яка людина, яку ви додаєте до спільноти, буде публічно видимою тим, хто знає ідентифікатор спільноти",
"Invite new community members": "Запросити до спільноти",
"Name or matrix ID": "Ім'я або ідентифікатор matrix",
"Invite to Community": "Запросити до спільноти",
"Which rooms would you like to add to this community?": "Які кімнати ви хочете додати до цієї спільноти?",
"Show these rooms to non-members on the community page and room list?": "Показувати ці кімнати тим, хто не належить до спільноти, на сторінці спільноти та списку кімнат?",
"Add rooms to the community": "Додати кімнати до спільноти",
"Room name or alias": "Назва або псевдонім кімнати",
"Add to community": "Додати до спільноти",
"Failed to invite the following users to %(groupId)s:": "Не вдалося запросити таких користувачів до %(groupId)s:",
"Failed to invite users to community": "Не вдалося запросити користувачів до кімнати",
"Failed to invite users to %(groupId)s": "Не вдалося запросити користувачів до %(groupId)s",
"Failed to add the following rooms to %(groupId)s:": "Не вдалося додати такі кімнати до %(groupId)s:",
"Riot does not have permission to send you notifications - please check your browser settings": "Riot не має дозволу надсилати вам сповіщення — будь ласка, перевірте налаштування переглядача",
"Riot was not given permission to send notifications - please try again": "Riot не має дозволу надсилати сповіщення — будь ласка, спробуйте ще раз",
"Unable to enable Notifications": "Не вдалося увімкнути сповіщення",
"This email address was not found": "Не знайдено адресу електронної пошти",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Схоже, ваша адреса електронної пошти не пов'язана з жодним ідентифікатор Matrix на цьому домашньому сервері.",
"Registration Required": "Потрібна реєстрація",
"You need to register to do this. Would you like to register now?": "Вам потрібно зареєструватися, щоб це зробити. Бажаєте зареєструватися зараз?",
"Restricted": "Обмежено",
"Moderator": "Модератор",
"Start a chat": "Розпочати балачку",
"Who would you like to communicate with?": "З ким бажаєте спілкуватися?",
"Start Chat": "Розпочати балачку",
"Invite new room members": "Запросити до кімнати",
"Who would you like to add to this room?": "Кого бажаєте додати до цієї кімнати?",
"Send Invites": "Надіслати запрошення",
"Failed to invite user": "Не вдалося запросити користувача",
"Failed to invite": "Не вдалося запросити",
"Failed to invite the following users to the %(roomName)s room:": "Не вдалося запросити таких користувачів до кімнати %(roomName)s:",
"You need to be logged in.": "Вам потрібно увійти.",
"You need to be able to invite users to do that.": "Щоб це зробити, вам необхідно мати можливість запрошувати людей.",
"Unable to create widget.": "Неможливо створити віджет.",
"Missing roomId.": "Бракує ідентифікатора кімнати.",
"Failed to send request.": "Не вдалося надіслати запит.",
"This room is not recognised.": "Кімнату не знайдено.",
"Power level must be positive integer.": "Рівень прав мусить бути додатнім цілим числом.",
"You are not in this room.": "Вас немає в цій кімнаті.",
"You do not have permission to do that in this room.": "У вас немає прав виконувати для цього в цій кімнаті.",
"Missing room_id in request": "У запиті бракує room_id",
"Room %(roomId)s not visible": "Кімната %(roomId)s не видима",
"Missing user_id in request": "У запиті пропущено user_id",
"Usage": "Використання",
"Searches DuckDuckGo for results": "Здійснює пошук через DuckDuckGo",
"/ddg is not a command": "/ddg — це не команда",
"To use it, just wait for autocomplete results to load and tab through them.": "Щоб цим скористатися, просто почекайте на підказки доповнення й перемикайтеся між ними клавішею TAB.",
"Changes your display nickname": "Змінює ваш нік",
"Changes colour scheme of current room": "Змінює кольорову схему кімнати",
"Sets the room topic": "Встановлює тему кімнати",
"Invites user with given id to current room": "Запрошує користувача з вказаним ідентифікатором до кімнати",
"Joins room with given alias": "Приєднується до кімнати з поданим ідентифікатором",
"Leave room": "Покинути кімнату",
"Unrecognised room alias:": "Кімнату не знайдено:",
"Kicks user with given id": "Викинути з кімнати користувача з вказаним ідентифікатором",
"Unbans user with given id": "Розблоковує користувача з вказаним ідентифікатором",
"Ignores a user, hiding their messages from you": "Ігнорувати користувача (приховує повідомлення від них)",
"Ignored user": "Користувача ігноровано",
"You are now ignoring %(userId)s": "Ви ігноруєте %(userId)s",
"Stops ignoring a user, showing their messages going forward": "Припинити ігнорувати користувача (показує їхні повідомлення від цього моменту)",
"Unignored user": "Припинено ігнорування користувача",
"You are no longer ignoring %(userId)s": "Ви більше не ігноруєте %(userId)s",
"Define the power level of a user": "Вказати рівень прав користувача",
"Deops user with given id": "Знімає права оператора з користувача з вказаним ідентифікатором",
"Opens the Developer Tools dialog": "Відкриває вікно інструментів розробника",
"Verifies a user, device, and pubkey tuple": "Перевіряє комбінацію користувача, пристрою і публічного ключа",
"Unknown (user, device) pair:": "Невідома комбінація користувача і пристрою:",
"Device already verified!": "Пристрій вже перевірено!",
"WARNING: Device already verified, but keys do NOT MATCH!": "УВАГА: Пристрій уже перевірено, але ключі НЕ ЗБІГАЮТЬСЯ!",
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "УВАГА: КЛЮЧ НЕ ПРОЙШОВ ПЕРЕВІРКУ! Підписний ключ %(userId)s на пристрої %(deviceId)s — це «%(fprint)s», і він не збігається з наданим ключем «%(fingerprint)s». Це може означати, що ваші повідомлення перехоплюють!",
"Verified key": "Ключ перевірено",
"The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Підписний ключ, який ви вказали, збігається з підписним ключем, отриманим від пристрою %(deviceId)s користувача %(userId)s. Пристрій позначено як перевірений.",
"Displays action": "Показує дію",
"Unrecognised command:": "Невідома команда:",
"Reason": "Причина",
"%(senderName)s requested a VoIP conference.": "%(senderName)s бажає розпочати дзвінок-конференцію.",
"%(senderName)s invited %(targetName)s.": "%(senderName)s запросив/ла %(targetName)s.",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s змінив/ла своє видиме ім'я на %(displayName)s.",
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s вказав/ла своє видиме ім'я: %(displayName)s.",
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s ліквідував/ла своє видиме ім'я (%(oldDisplayName)s).",
"%(senderName)s removed their profile picture.": "%(senderName)s вилучав/ла свою світлину профілю.",
"%(senderName)s set a profile picture.": "%(senderName)s встановив/ла світлину профілю.",
"VoIP conference started.": "Розпочато дзвінок-конференцію.",
"%(targetName)s joined the room.": "%(targetName)s приєднав/лася до кімнати.",
"VoIP conference finished.": "Дзвінок-конференцію завершено.",
"%(targetName)s rejected the invitation.": "%(targetName)s відкинув/ла запрошення.",
"%(targetName)s left the room.": "%(targetName)s залишив/ла кімнату.",
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s розблокував/ла %(targetName)s.",
"%(senderName)s kicked %(targetName)s.": "%(senderName)s викинув/ла %(targetName)s.",
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s відкликав/ла запрошення %(targetName)s.",
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s надіслав/ла зображення.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|other": "%(senderName)s додав/ла %(addedAddresses)s як адреси цієї кімнати.",
"%(senderName)s added %(count)s %(addedAddresses)s as addresses for this room.|one": "%(senderName)s додав/ла %(addedAddresses)s як адресу цієї кімнати.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|other": "%(senderName)s вилучив/ла %(removedAddresses)s з адрес цієї кімнати.",
"%(senderName)s removed %(count)s %(removedAddresses)s as addresses for this room.|one": "%(senderName)s вилучив/ла %(removedAddresses)s з адрес цієї кімнати.",
"%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.": "%(senderName)s додав/ла %(addedAddresses)s і вилучив/ла %(removedAddresses)s з адрес цієї кімнати.",
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s призначив/ла основну адресу цієї кімнати: %(address)s.",
"%(senderName)s removed the main address for this room.": "%(senderName)s вилучив/ла основу адресу цієї кімнати.",
"Someone": "Хтось",
"(not supported by this browser)": "(не підтримується цією веб-переглядачкою)",
"(could not connect media)": "(не можливо під'єднати медіа)",
"(no answer)": "(немає відповіді)",
"(unknown failure: %(reason)s)": "(невідома помилка: %(reason)s)",
"%(senderName)s ended the call.": "%(senderName)s завершив/ла дзвінок.",
"%(senderName)s placed a %(callType)s call.": "%(senderName)s ініціював/ла дзвінок %(callType)s.",
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s надіслав/ла запрошення %(targetDisplayName)s приєднатися до кімнати.",
"Show developer tools": "Показати інструменти розробки",
"Default": "Типово",
"%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s зробив/ла майбутню історію кімнати видимою для всіх учасників, з моменту, коли вони приєдналися.",
"%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s зробив/ла майбутню історію кімнати видимою для всіх учасників, з моменту, коли вони приєдналися.",
"%(senderName)s made future room history visible to all room members.": "%(senderName)s зробив/ла майбутню історію видимою для всіх учасників кімнати.",
"%(senderName)s made future room history visible to anyone.": "%(senderName)s зробив/ла майбутню історію кімнати видимою для всіх.",
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s зробив/ла майбутню історію видимою невідомим (%(visibility)s).",
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s увімкнув/ла наскрізне шифрування (алгоритм %(algorithm)s).",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s з %(fromPowerLevel)s до %(toPowerLevel)s",
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s змінив/ла прикріплені повідомлення для кімнати.",
"%(widgetName)s widget modified by %(senderName)s": "%(senderName)s змінив/ла %(widgetName)s",
"%(widgetName)s widget added by %(senderName)s": "%(senderName)s додав/ла %(widgetName)s",
"%(widgetName)s widget removed by %(senderName)s": "%(senderName)s вилучив/ла %(widgetName)s",
"%(displayName)s is typing": "%(displayName)s пише",
"%(names)s and %(count)s others are typing|other": "%(names)s і %(count)s пишуть",
"%(names)s and %(count)s others are typing|one": "%(names)s та ще одна особа пишуть",
"Failure to create room": "Не вдалося створити кімнату",
"Server may be unavailable, overloaded, or you hit a bug.": "Сервер може бути недоступний, перевантажений, або ж ви натрапили на ваду.",
"Send anyway": "Все-таки надіслати",
"Unnamed Room": "Кімната без назви",
"This homeserver has hit its Monthly Active User limit.": "Цей домашній сервер досягнув свого ліміту щомісячних активних користувачів.",
"This homeserver has exceeded one of its resource limits.": "Цей домашній сервер досягнув одного зі своїх лімітів ресурсів.",
"Please <a>contact your service administrator</a> to continue using the service.": "Будь ласка, <a>зв'яжіться з адміністратором вашого сервісу</a>, щоб продовжити користуватися цим сервісом.",
"Unable to connect to Homeserver. Retrying...": "Не вдається приєднатися до домашнього сервера. Повторення спроби...",
"Your browser does not support the required cryptography extensions": "Ваша веб-переглядачка не підтримує необхідних криптографічних функцій",
"Not a valid Riot keyfile": "Файл ключа Riot некоректний",
"Authentication check failed: incorrect password?": "Помилка автентифікації: неправильний пароль?",
"Sorry, your homeserver is too old to participate in this room.": "Вибачте, ваш домашній сервер занадто старий, щоб приєднатися до цієї кімнати.",
"Please contact your homeserver administrator.": "Будь ласка, зв'яжіться з адміністратором вашого домашнього сервера.",
"Failed to join room": "Не вдалося приєднатися до кімнати",
"Message Pinning": "Прикріплені повідомлення",
"Increase performance by only loading room members on first view": "Покращити швидкодію, завантажуючи список учасників кімнати лише при першому перегляді",
"Disable Emoji suggestions while typing": "Вимкнути підказки емоційок під час писання",
"Use compact timeline layout": "Використовувати компактну розкладку стрічки",
"Hide join/leave messages (invites/kicks/bans unaffected)": "Приховувати повідомлення про приєднання/вихід (не стосується запрошень, викидань і блокувань)",
"Hide avatar changes": "Приховувати зміни аватарів",
"Hide display name changes": "Приховувати зміни видимих імен",
"Hide read receipts": "Приховувати сповіщення про прочитання",
"Show timestamps in 12 hour format (e.g. 2:30pm)": "Показувати час у 12-годинному форматі (напр. 2:30 pm)",
"Always show encryption icons": "Завжди показувати значки шифрування",
"Enable automatic language detection for syntax highlighting": "Показувати автоматичне визначення мови для підсвічування синтаксису",
"Hide avatars in user and room mentions": "Приховувати аватари у згадках користувачів і кімнат",
"Disable big emoji in chat": "Вимкнути великі емоційки в балачці",
"Don't send typing notifications": "Не надсилати сповіщення «пишу»",
"Automatically replace plain text Emoji": "Автоматично замінювати емоційки в простому тексті",
"Mirror local video feed": "Показувати локальне відео віддзеркалено",
"Disable Community Filter Panel": "Вимкнути панель фільтру спільнот",
"Disable Peer-to-Peer for 1:1 calls": "Вимкнути peer-to-peer для дзвінків 1:1",
"Send analytics data": "Надсилати дані аналітики",
"Never send encrypted messages to unverified devices from this device": "Ніколи не надсилати шифрованих повідомлень до неперевірених пристроїв з цього пристрою",
"Never send encrypted messages to unverified devices in this room from this device": "Ніколи не надсилати шифрованих повідомлень до неперевірених пристроїв у цій кімнаті з цього пристрою",
"Enable inline URL previews by default": "Увімкнути вбудований перегляд гіперпосилань за умовчанням",
"Enable URL previews for this room (only affects you)": "Увімкнути попередній перегляд гіперпосилань в цій кімнаті (стосується тільки вас)",
"Enable URL previews by default for participants in this room": "Увімкнути попередній перегляд гіперпосилань за умовчанням для учасників цієї кімнати",
"Room Colour": "Колір кімнати",
"Pin unread rooms to the top of the room list": "Прикріпити кімнати з непрочитаними повідомленнями на початку списку",
"Pin rooms I'm mentioned in to the top of the room list": "Прикріпити кімнати, у яких мене згадували, на початку списку",
"Incoming voice call from %(name)s": "Вхідний дзвінок від %(name)s",
"Incoming video call from %(name)s": "Вхідний відеодзвінок від %(name)s",
"Incoming call from %(name)s": "Вхідний дзвінок від %(name)s",
"Decline": "Відхилити",
"Incorrect verification code": "Неправильний код перевірки",
"Enter Code": "Вкажіть код",
"Submit": "Надіслати",
"Phone": "Телефон",
"Failed to upload profile picture!": "Не вдалося завантажити світлину профілю!",
"Upload new:": "Завантажити нову:",
"No display name": "Немає імені для показу",
"New passwords don't match": "Нові паролі не збігаються",
"Passwords can't be empty": "Пароль не може бути пустим",
"Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Зміна пароля поки що також скидає ключі наскрізного шифрування на всіх пристроях, внаслідок чого шифрована історія балачки стає недоступною, якщо ви не експортуєте ключі шифрування кімнат і не імпортуєте їх після зміни пароля. У майбутньому це буде поліпшено.",
"Export E2E room keys": "Експортувати ключі наскрізного шифрування кімнат",
"Do you want to set an email address?": "Бажаєте вказати адресу електронної пошти?",
"Current password": "Поточний пароль",
"Password": "Пароль",
"New Password": "Новий пароль",
"Confirm password": "Підтвердження пароля",
"Your home server does not support device management.": "Ваш домашній сервер не підтримує керування пристроями.",
"Unable to load device list": "Не вдалося завантажити перелік пристроїв",
"Delete %(count)s devices|other": "Вилучити %(count)s пристроїв",
"Delete %(count)s devices|one": "Вилучити пристрій",
"Device ID": "ID пристрою",
"Device Name": "Назва пристрою",
"Last seen": "Востаннє з'являвся",
"Select devices": "Вибрати пристрої",
"Failed to set display name": "Не вдалося встановити ім'я для показу",
"Disable Notifications": "Вимкнути сповіщення",
"Enable Notifications": "Увімкнути сповіщення",
"The maximum permitted number of widgets have already been added to this room.": "Максимально дозволену кількість віджетів уже додано до цієї кімнати.",
"Drop File Here": "Киньте файл сюди",
"Drop file here to upload": "Киньте файл сюди, щоб вивантажити",
" (unsupported)": " (не підтримується)",
"Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.": "Приєднатися <voiceText>голосом</voiceText> або <videoText>відео</videoText>.",
"Ongoing conference call%(supportedText)s.": "Триває дзвінок-конференція%(supportedText)s.",
"This event could not be displayed": "Неможливо показати цю подію",
"%(senderName)s sent an image": "%(senderName)s надіслав/ла зображення",
"%(senderName)s sent a video": "%(senderName)s надіслав/ла відео",
"%(senderName)s uploaded a file": "%(senderName)s надіслав/ла файл",
"Options": "Налаштування",
"Your key share request has been sent - please check your other devices for key share requests.": "Ваш запит поширення ключа надіслано — гляньте запити поширення ключа на своїх інших пристроях.",
"Key share requests are sent to your other devices automatically. If you rejected or dismissed the key share request on your other devices, click here to request the keys for this session again.": "Запити поширення ключа надсилаються на ваші пристрої автоматично. Якщо ви відкинули запит поширення ключа на своїх інших пристроях, натисніть тут, щоб наново надіслати запит поширення ключа для цього сеансу.",
"If your other devices do not have the key for this message you will not be able to decrypt them.": "Якщо на ваших інших пристроях немає ключа для цього повідомлення, ви не зможете його прочитати.",
"Key request sent.": "Запит ключа надіслано.",
"<requestLink>Re-request encryption keys</requestLink> from your other devices.": "<requestLink>Наново надіслати запит ключів шифрування</requestLink> на ваші інші пристрої.",
"Undecryptable": "Неможливо розшифрувати",
"Encrypting": "Шифрування",
"Encrypted, not sent": "Зашифровано, не надіслано",
"Encrypted by a verified device": "Зашифровано перевіреним пристроєм",
"Encrypted by an unverified device": "Зашифровано неперевіреним пристроєм",
"Unencrypted message": "Незашифроване повідомлення",
"Please select the destination room for this message": "Будь ласка, виберіть кімнату, куди потрібно надіслати це повідомлення",
"Verified": "Перевірено",
"Unverified": "Неперевірено",
"device id: ": "id пристрою: ",
"Disinvite": "Скасувати запрошення",
"Kick": "Викинути",
"Disinvite this user?": "Скасувати запрошення для цього користувача?",
"Kick this user?": "Викинути цього користувача?",
"Failed to kick": "Не вдалося викинути",
"Unban": "Розблокувати",
"Unban this user?": "Розблокувати цього користувача?",
"Ban this user?": "Заблокувати цього користувача?",
"Failed to ban user": "Не вдалося заблокувати користувача",
"Demote yourself?": "Знизити свій рівень прав?",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Ви не зможете скасувати цю дію, оскільки ви знижуєте свій рівень прав. Якщо ви останній користувач з правами в цій кімнаті, ви не зможете отримати повноваження знову.",
"Demote": "Знизити рівень прав",
"Failed to mute user": "Не вдалося заглушити користувача",
"Failed to toggle moderator status": "Не вдалося перемкнути статус модератора",
"Failed to change power level": "Не вдалося змінити рівень повноважень"
}

View file

@ -155,7 +155,7 @@
"New password": "新密码",
"Add a topic": "添加主题",
"Admin": "管理员",
"Admin Tools": "管理工具",
"Admin Tools": "管理工具",
"VoIP": "IP 电话",
"Missing Media Permissions, click here to request.": "没有媒体存储权限,点此获取。",
"No Microphones detected": "未检测到麦克风",
@ -424,7 +424,7 @@
"Unable to capture screen": "无法录制屏幕",
"Unable to enable Notifications": "无法启用通知",
"Unable to load device list": "无法加载设备列表",
"Undecryptable": "无法解密",
"Undecryptable": "无法解密",
"Unencrypted room": "未加密的聊天室",
"unencrypted": "未加密的",
"Unencrypted message": "未加密消息",
@ -498,7 +498,7 @@
"Use with caution": "谨慎使用",
"User Interface": "用户界面",
"User name": "用户名",
"(no answer)": "(无响应",
"(no answer)": "(无回复",
"(warning: cannot be disabled again!)": "(警告:无法再被禁用!)",
"WARNING: Device already verified, but keys do NOT MATCH!": "警告:设备已验证,但密钥不匹配!",
"Who can access this room?": "谁有权访问此聊天室?",
@ -807,7 +807,7 @@
"<requestLink>Re-request encryption keys</requestLink> from your other devices.": "从其他设备上 <requestLink>重新请求密钥</requestLink>。",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "如果您是房间中最后一位有权限的用户,在您降低自己的权限等级后将无法撤回此修改,因为你将无法重新获得权限。",
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "您将无法撤回此修改,因为您正在将此用户的滥权等级提升至与你相同。",
"No devices with registered encryption keys": "没有设备有已注册加密密钥",
"No devices with registered encryption keys": "没有已注册加密密钥的设备",
"Unmute": "取消静音",
"%(userName)s (power %(powerLevelNumber)s)": "%(userName)s滥权等级 %(powerLevelNumber)s",
"Hide Stickers": "隐藏贴图",
@ -817,15 +817,15 @@
"%(duration)sh": "%(duration)s 小时",
"%(duration)sd": "%(duration)s 天",
"Online for %(duration)s": "已上线 %(duration)s",
"Idle for %(duration)s": "已 idle %(duration)s",
"Idle for %(duration)s": "已闲置%(duration)s",
"Offline for %(duration)s": "已离线 %(duration)s",
"Unknown for %(duration)s": "在线状态未知 %(duration)s",
"Unknown for %(duration)s": "未知状态 %(duration)s",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "%(displayName)s (%(userName)s) 在 %(dateTime)s 看到这里",
"Remove avatar": "移除头像",
"Drop here to favourite": "拖动到这里以加入收藏",
"Drop here to tag direct chat": "拖动到这里以加入私聊",
"Drop here to restore": "拖动到这里以还原",
"Drop here to demote": "拖动到这里以加入低优先级",
"Drop here to demote": "拖动到这里以低优先级",
"Unable to ascertain that the address this invite was sent to matches one associated with your account.": "无法确定此邀请发送给的邮件地址是否与您帐户所关联的邮件地址相匹配。",
"You have been kicked from this room by %(userName)s.": "您已被 %(userName)s 从此聊天室中移除。",
"'%(groupId)s' is not a valid community ID": "“%(groupId)s” 不是有效的社区 ID",
@ -885,7 +885,7 @@
"To change the room's avatar, you must be a": "无法修改此聊天室的头像,因为您不是此聊天室的",
"To change the room's name, you must be a": "无法修改此聊天室的名称,因为您不是此聊天室的",
"To change the room's main address, you must be a": "无法修改此聊天室的主地址,因为您不是此聊天室的",
"To change the room's history visibility, you must be a": "无法修改此聊天室的历史聊天记录可见性,因为您不是此聊天室的",
"To change the room's history visibility, you must be a": "无法更改本聊天室历史记录的可见性,您必须为",
"To change the permissions in the room, you must be a": "无法修改此聊天室中的权限情况,因为您不是此聊天室的",
"Showing flair for these communities:": "为这些社区显示 flair",
"This room is not showing flair for any communities": "此聊天室没有对任何社区显示 flair",
@ -1150,7 +1150,7 @@
"A call is currently being placed!": "已发起一次通话!",
"Permission Required": "需要权限",
"You do not have permission to start a conference call in this room": "您没有在此聊天室发起通话会议的权限",
"Show empty room list headings": "为空的聊天室列表显示 heading",
"Show empty room list headings": "为空的聊天室列表显示标题",
"This event could not be displayed": "无法显示此事件",
"Share Link to User": "分享链接给其他用户",
"deleted": "删除线",

View file

@ -1293,5 +1293,12 @@
"Please accept all of the policies": "請接受所有政策",
"Please review and accept the policies of this homeserver:": "請審閱並接受此家伺服器的政策:",
"Add some now": "現在就新增一些",
"Joining room...": "正在加入聊天室……"
"Joining room...": "正在加入聊天室……",
"Pin unread rooms to the top of the room list": "釘選未讀的聊天示到聊天室清單頂端",
"Pin rooms I'm mentioned in to the top of the room list": "釘選我被提及的聊天室到聊天室清單頂端",
"If you would like to create a Matrix account you can <a>register</a> now.": "若您想要建立一個 Matrix 帳號,您現在可以<a>註冊</a>了。",
"You are currently using Riot anonymously as a guest.": "您目前是以訪客的身份匿名使用 Riot。",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "您是此社群的管理員。您將無法在沒有其他管理員的邀請下重新加入。",
"Open Devtools": "開啟開發者工具",
"Show developer tools": "顯示開發者工具"
}

View file

@ -90,6 +90,12 @@ export const SETTINGS = {
controller: new LazyLoadingController(),
default: true,
},
"feature_keybackup": {
isFeature: true,
displayName: _td("Backup of encryption keys to server"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"MessageComposerInput.dontSuggestEmoji": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
displayName: _td('Disable Emoji suggestions while typing'),