Merge branch 'develop' into travis/hide-images

This commit is contained in:
Travis Ralston 2019-10-01 09:31:54 -06:00
commit 3c589cffec
69 changed files with 1103 additions and 290 deletions

View file

@ -1,3 +1,29 @@
Changes in [1.6.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.1) (2019-10-01)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.0...v1.6.1)
* Upgrade to JS SDK 2.4.1 to ignore crypto events with empty content
* Update from Weblate
[\#3502](https://github.com/matrix-org/matrix-react-sdk/pull/3502)
* Adjust details of terms dialog
[\#3489](https://github.com/matrix-org/matrix-react-sdk/pull/3489)
* Okay -> OK
[\#3491](https://github.com/matrix-org/matrix-react-sdk/pull/3491)
* Guard against falsy names in getInitialLetter
[\#3498](https://github.com/matrix-org/matrix-react-sdk/pull/3498)
* Update from Weblate
[\#3495](https://github.com/matrix-org/matrix-react-sdk/pull/3495)
* Upgrade deps
[\#3488](https://github.com/matrix-org/matrix-react-sdk/pull/3488)
* Fix: allow mass redaction for members with same or larger power level
[\#3487](https://github.com/matrix-org/matrix-react-sdk/pull/3487)
* Truncate debug logs at the start, not the end
[\#3484](https://github.com/matrix-org/matrix-react-sdk/pull/3484)
* Fix: don't block Shift+Tab in CIDER autocomplete
[\#3481](https://github.com/matrix-org/matrix-react-sdk/pull/3481)
* Fix: make command detection more resilient
[\#3479](https://github.com/matrix-org/matrix-react-sdk/pull/3479)
Changes in [1.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.0) (2019-09-27)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.0-rc.2...v1.6.0)

View file

@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
"version": "1.6.0",
"version": "1.6.1",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {

View file

@ -40,6 +40,7 @@ limitations under the License.
}
.mx_TermsDialog_link {
display: inline-block;
mask-image: url('$(res)/img/external-link.svg');
background-color: $accent-color;
width: 10px;

View file

@ -67,6 +67,11 @@ module.exports = {
* @return {string} the first letter
*/
getInitialLetter(name) {
if (!name) {
// XXX: We should find out what causes the name to sometimes be falsy.
console.trace("`name` argument to `getInitialLetter` not supplied");
return undefined;
}
if (name.length < 1) {
return undefined;
}

View file

@ -2,6 +2,7 @@
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017, 2018 New Vector Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -33,6 +34,7 @@ import url from 'url';
import EMOJIBASE from 'emojibase-data/en/compact.json';
import EMOJIBASE_REGEX from 'emojibase-regex';
import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks";
linkifyMatrix(linkify);
@ -158,30 +160,10 @@ const transformTags = { // custom to matrix
if (attribs.href) {
attribs.target = '_blank'; // by default
let m;
// FIXME: horrible duplication with linkify-matrix
m = attribs.href.match(linkifyMatrix.VECTOR_URL_PATTERN);
if (m) {
attribs.href = m[1];
const transformed = tryTransformPermalinkToLocalHref(attribs.href);
if (transformed !== attribs.href || attribs.href.match(linkifyMatrix.VECTOR_URL_PATTERN)) {
attribs.href = transformed;
delete attribs.target;
} else {
m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN);
if (m) {
const entity = m[1];
switch (entity[0]) {
case '@':
attribs.href = '#/user/' + entity;
break;
case '+':
attribs.href = '#/group/' + entity;
break;
case '#':
case '!':
attribs.href = '#/room/' + entity;
break;
}
delete attribs.target;
}
}
}
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
@ -465,10 +447,12 @@ export function bodyToHtml(content, highlights, opts={}) {
const match = BIGEMOJI_REGEX.exec(contentBodyTrimmed);
emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length &&
// Prevent user pills expanding for users with only emoji in
// their username
// their username. Permalinks (links in pills) can be any URL
// now, so we just check for an HTTP-looking thing.
(
content.formatted_body == undefined ||
!content.formatted_body.includes("https://matrix.to/")
(!content.formatted_body.includes("http:") &&
!content.formatted_body.includes("https:"))
);
}

View file

@ -58,7 +58,17 @@ export const KeyCode = {
KEY_X: 88,
KEY_Y: 89,
KEY_Z: 90,
KEY_BACKTICK: 223,
KEY_BACKTICK: 223, // DO NOT USE THIS: browsers disagree on backtick 192 vs 223
};
export const Key = {
HOME: "Home",
End: "End",
PAGE_UP: "PageUp",
PAGE_DOWN: "PageDown",
BACKTICK: "`",
K: "k",
};
export function isOnlyCtrlOrCmdKeyEvent(ev) {

View file

@ -23,8 +23,6 @@ import dis from './dispatcher';
import sdk from './index';
import {_t, _td} from './languageHandler';
import Modal from './Modal';
import {MATRIXTO_URL_PATTERN} from "./linkify-matrix";
import * as querystring from "querystring";
import MultiInviter from './utils/MultiInviter';
import { linkifyAndSanitizeHtml } from './HtmlUtils';
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
@ -34,6 +32,7 @@ import Promise from "bluebird";
import { getAddressType } from './UserAddress';
import { abbreviateUrl } from './utils/UrlUtils';
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
const singleMxcUpload = async () => {
return new Promise((resolve) => {
@ -441,7 +440,19 @@ export const CommandMap = {
const params = args.split(' ');
if (params.length < 1) return reject(this.getUsage());
const matrixToMatches = params[0].match(MATRIXTO_URL_PATTERN);
let isPermalink = false;
if (params[0].startsWith("http:") || params[0].startsWith("https:")) {
// It's at least a URL - try and pull out a hostname to check against the
// permalink handler
const parsedUrl = new URL(params[0]);
const hostname = parsedUrl.host || parsedUrl.hostname; // takes first non-falsey value
// if we're using a Riot permalink handler, this will catch it before we get much further.
// see below where we make assumptions about parsing the URL.
if (isPermalinkHost(hostname)) {
isPermalink = true;
}
}
if (params[0][0] === '#') {
let roomAlias = params[0];
if (!roomAlias.includes(':')) {
@ -469,29 +480,25 @@ export const CommandMap = {
auto_join: true,
});
return success();
} else if (matrixToMatches) {
let entity = matrixToMatches[1];
let eventId = null;
let viaServers = [];
} else if (isPermalink) {
const permalinkParts = parsePermalink(params[0]);
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];
// This check technically isn't needed because we already did our
// safety checks up above. However, for good measure, let's be sure.
if (!permalinkParts) {
return reject(this.getUsage());
}
// We quietly support event ID permalinks too
if (entity.indexOf('/$') !== -1) {
const parts = entity.split("/$");
entity = parts[0];
eventId = `$${parts[1]}`;
// If for some reason someone wanted to join a group or user, we should
// stop them now.
if (!permalinkParts.roomIdOrAlias) {
return reject(this.getUsage());
}
const entity = permalinkParts.roomIdOrAlias;
const viaServers = permalinkParts.viaServers;
const eventId = permalinkParts.eventId;
const dispatch = {
action: 'view_room',
auto_join: true,

View file

@ -446,7 +446,7 @@ export default createReactClass({
<p>{_t(
"Your keys are being backed up (the first backup could take a few minutes).",
)}</p>
<DialogButtons primaryButton={_t('Okay')}
<DialogButtons primaryButton={_t('OK')}
onPrimaryButtonClick={this._onDone}
hasCancel={false}
/>

View file

@ -23,7 +23,7 @@ import QueryMatcher from './QueryMatcher';
import {PillCompletion} from './Components';
import sdk from '../index';
import _sortBy from 'lodash/sortBy';
import {makeGroupPermalink} from "../matrix-to";
import {makeGroupPermalink} from "../utils/permalinks/Permalinks";
import type {Completion, SelectionRange} from "./Autocompleter";
import FlairStore from "../stores/FlairStore";
@ -105,8 +105,14 @@ export default class CommunityProvider extends AutocompleteProvider {
}
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_pill mx_Autocomplete_Completion_container_truncate">
{ completions }
</div>;
return (
<div
className="mx_Autocomplete_Completion_container_pill mx_Autocomplete_Completion_container_truncate"
role="listbox"
aria-label={_t("Community Autocomplete")}
>
{ completions }
</div>
);
}
}

View file

@ -60,7 +60,7 @@ export class PillCompletion extends React.Component {
...restProps
} = this.props;
return (
<div className={classNames('mx_Autocomplete_Completion_pill', className)} {...restProps}>
<div className={classNames('mx_Autocomplete_Completion_pill', className)} role="option" {...restProps}>
{ initialComponent }
<span className="mx_Autocomplete_Completion_title">{ title }</span>
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>

View file

@ -116,7 +116,9 @@ export default class EmojiProvider extends AutocompleteProvider {
return {
completion: unicode,
component: (
<PillCompletion title={shortname} initialComponent={<span style={{maxWidth: '1em'}}>{ unicode }</span>} />
<PillCompletion title={shortname} aria-label={unicode} initialComponent={
<span style={{maxWidth: '1em'}}>{ unicode }</span>
} />
),
range,
};
@ -130,8 +132,10 @@ export default class EmojiProvider extends AutocompleteProvider {
}
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_pill">
{ completions }
</div>;
return (
<div className="mx_Autocomplete_Completion_container_pill" role="listbox" aria-label={_t("Emoji Autocomplete")}>
{ completions }
</div>
);
}
}

View file

@ -58,8 +58,14 @@ export default class NotifProvider extends AutocompleteProvider {
}
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_pill mx_Autocomplete_Completion_container_truncate">
{ completions }
</div>;
return (
<div
className="mx_Autocomplete_Completion_container_pill mx_Autocomplete_Completion_container_truncate"
role="listbox"
aria-label={_t("Notification Autocomplete")}
>
{ completions }
</div>
);
}
}

View file

@ -26,7 +26,7 @@ import {PillCompletion} from './Components';
import {getDisplayAliasForRoom} from '../Rooms';
import sdk from '../index';
import _sortBy from 'lodash/sortBy';
import {makeRoomPermalink} from "../matrix-to";
import {makeRoomPermalink} from "../utils/permalinks/Permalinks";
import type {Completion, SelectionRange} from "./Autocompleter";
const ROOM_REGEX = /\B#\S*/g;
@ -109,8 +109,14 @@ export default class RoomProvider extends AutocompleteProvider {
}
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_pill mx_Autocomplete_Completion_container_truncate">
{ completions }
</div>;
return (
<div
className="mx_Autocomplete_Completion_container_pill mx_Autocomplete_Completion_container_truncate"
role="listbox"
aria-label={_t("Room Autocomplete")}
>
{ completions }
</div>
);
}
}

View file

@ -28,7 +28,7 @@ import _sortBy from 'lodash/sortBy';
import MatrixClientPeg from '../MatrixClientPeg';
import type {MatrixEvent, Room, RoomMember, RoomState} from 'matrix-js-sdk';
import {makeUserPermalink} from "../matrix-to";
import {makeUserPermalink} from "../utils/permalinks/Permalinks";
import type {Completion, SelectionRange} from "./Autocompleter";
const USER_REGEX = /\B@\S*/g;
@ -164,9 +164,11 @@ export default class UserProvider extends AutocompleteProvider {
}
renderCompletions(completions: [React.Component]): ?React.Component {
return <div className="mx_Autocomplete_Completion_container_pill">
{ completions }
</div>;
return (
<div className="mx_Autocomplete_Completion_container_pill" role="listbox" aria-label={_t("User Autocomplete")}>
{ completions }
</div>
);
}
shouldForceComplete(): boolean {

View file

@ -36,7 +36,7 @@ import classnames from 'classnames';
import GroupStore from '../../stores/GroupStore';
import FlairStore from '../../stores/FlairStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
import {makeGroupPermalink, makeUserPermalink} from "../../matrix-to";
import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/Permalinks";
import {Group} from "matrix-js-sdk";
const LONG_DESC_PLACEHOLDER = _td(

View file

@ -22,7 +22,7 @@ import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { DragDropContext } from 'react-beautiful-dnd';
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
import { Key, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
import PageTypes from '../../PageTypes';
import CallMediaHandler from '../../CallMediaHandler';
import { fixupColorFonts } from '../../utils/FontManager';
@ -353,23 +353,23 @@ const LoggedInView = createReactClass({
const hasModifier = ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey ||
ev.key === "Alt" || ev.key === "Control" || ev.key === "Meta" || ev.key === "Shift";
switch (ev.keyCode) {
case KeyCode.PAGE_UP:
case KeyCode.PAGE_DOWN:
switch (ev.key) {
case Key.PAGE_UP:
case Key.PAGE_DOWN:
if (!hasModifier) {
this._onScrollKeyPressed(ev);
handled = true;
}
break;
case KeyCode.HOME:
case KeyCode.END:
case Key.HOME:
case Key.END:
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
this._onScrollKeyPressed(ev);
handled = true;
}
break;
case KeyCode.KEY_K:
case Key.K:
if (ctrlCmdOnly) {
dis.dispatch({
action: 'focus_room_filter',
@ -377,7 +377,9 @@ const LoggedInView = createReactClass({
handled = true;
}
break;
case KeyCode.KEY_BACKTICK:
case Key.BACKTICK:
if (ev.key !== "`") break;
// Ideally this would be CTRL+P for "Profile", but that's
// taken by the print dialog. CTRL+I for "Information"
// was previously chosen but conflicted with italics in

View file

@ -307,11 +307,11 @@ const RoomSubList = createReactClass({
});
if (isCollapsed) {
return <div ref="subList" className={subListClasses}>
return <div ref="subList" className={subListClasses} role="group" aria-label={this.props.label}>
{this._getHeaderJsx(isCollapsed)}
</div>;
} else if (this._canUseLazyListRendering()) {
return <div ref="subList" className={subListClasses}>
return <div ref="subList" className={subListClasses} role="group" aria-label={this.props.label}>
{this._getHeaderJsx(isCollapsed)}
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={ this._onScroll }>
<LazyRenderList
@ -325,7 +325,7 @@ const RoomSubList = createReactClass({
} else {
const roomTiles = this.props.list.map(r => this.makeRoomTile(r));
const tiles = roomTiles.concat(this.props.extraTiles);
return <div ref="subList" className={subListClasses}>
return <div ref="subList" className={subListClasses} role="group" aria-label={this.props.label}>
{this._getHeaderJsx(isCollapsed)}
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={ this._onScroll }>
{ tiles }
@ -340,7 +340,7 @@ const RoomSubList = createReactClass({
}
return (
<div ref="subList" className="mx_RoomSubList">
<div ref="subList" className="mx_RoomSubList" role="group" aria-label={this.props.label}>
{ this._getHeaderJsx(isCollapsed) }
{ content }
</div>

View file

@ -31,7 +31,7 @@ import Promise from 'bluebird';
import classNames from 'classnames';
import {Room} from "matrix-js-sdk";
import { _t } from '../../languageHandler';
import {RoomPermalinkCreator} from '../../matrix-to';
import {RoomPermalinkCreator} from '../../utils/permalinks/Permalinks';
import MatrixClientPeg from '../../MatrixClientPeg';
import ContentMessages from '../../ContentMessages';

View file

@ -52,7 +52,7 @@ module.exports = createReactClass({
// Extra node inserted after picker input, dropdown and errors
extraNode: PropTypes.node,
value: PropTypes.string,
placeholder: PropTypes.oneOfType(PropTypes.string, PropTypes.func),
placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
roomId: PropTypes.string,
button: PropTypes.string,
focus: PropTypes.bool,

View file

@ -20,7 +20,7 @@ import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import QRCode from 'qrcode-react';
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../matrix-to";
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
import * as ContextualMenu from "../../structures/ContextualMenu";
const socials = [

View file

@ -90,21 +90,17 @@ export default class TermsDialog extends React.PureComponent {
}
}
_summaryForServiceType(serviceType, docName) {
_summaryForServiceType(serviceType) {
switch (serviceType) {
case Matrix.SERVICE_TYPES.IS:
return <div>
{_t("Find others by phone or email")}
<br />
{_t("Be found by phone or email")}
{docName !== null ? <br /> : ''}
{docName !== null ? '('+docName+')' : ''}
</div>;
case Matrix.SERVICE_TYPES.IM:
return <div>
{_t("Use bots, bridges, widgets and sticker packs")}
{docName !== null ? <br /> : ''}
{docName !== null ? '('+docName+')' : ''}
</div>;
}
}
@ -128,19 +124,19 @@ export default class TermsDialog extends React.PureComponent {
const termDoc = policyValues[i];
const termsLang = pickBestLanguage(Object.keys(termDoc).filter((k) => k !== 'version'));
let serviceName;
let summary;
if (i === 0) {
serviceName = this._nameForServiceType(policiesAndService.service.serviceType, parsedBaseUrl.host);
summary = this._summaryForServiceType(
policiesAndService.service.serviceType,
);
}
const summary = this._summaryForServiceType(
policiesAndService.service.serviceType,
policyValues.length > 1 ? termDoc[termsLang].name : null,
);
rows.push(<tr key={termDoc[termsLang].url}>
<td className="mx_TermsDialog_service">{serviceName}</td>
<td className="mx_TermsDialog_summary">{summary}</td>
<td><a rel="noopener" target="_blank" href={termDoc[termsLang].url}>
<div className="mx_TermsDialog_link" />
<td>{termDoc[termsLang].name} <a rel="noopener" target="_blank" href={termDoc[termsLang].url}>
<span className="mx_TermsDialog_link" />
</a></td>
<td><TermsCheckbox
url={termDoc[termsLang].url}
@ -184,13 +180,13 @@ export default class TermsDialog extends React.PureComponent {
hasCancel={false}
>
<div id='mx_Dialog_content'>
<p>{_t("To continue you need to accept the Terms of this service.")}</p>
<p>{_t("To continue you need to accept the terms of this service.")}</p>
<table className="mx_TermsDialog_termsTable"><tbody>
<tr className="mx_TermsDialog_termsTableHeader">
<th>{_t("Service")}</th>
<th>{_t("Summary")}</th>
<th>{_t("Terms")}</th>
<th>{_t("Document")}</th>
<th>{_t("Accept")}</th>
</tr>
{rows}

View file

@ -68,7 +68,7 @@ export default function AccessibleButton(props) {
delete restProps.inputRef;
restProps.tabIndex = restProps.tabIndex || "0";
restProps.role = "button";
restProps.role = restProps.role || "button";
restProps.className = (restProps.className ? restProps.className + " " : "") + "mx_AccessibleButton";
if (kind) {

View file

@ -1,6 +1,7 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -22,23 +23,21 @@ import classNames from 'classnames';
import { Room, RoomMember, MatrixClient } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
import MatrixClientPeg from '../../../MatrixClientPeg';
import { MATRIXTO_URL_PATTERN } from '../../../linkify-matrix';
import { getDisplayAliasForRoom } from '../../../Rooms';
import FlairStore from "../../../stores/FlairStore";
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
// For URLs of matrix.to links in the timeline which have been reformatted by
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
const Pill = createReactClass({
statics: {
isPillUrl: (url) => {
return !!REGEX_MATRIXTO.exec(url);
return !!getPrimaryPermalinkEntity(url);
},
isMessagePillUrl: (url) => {
return !!REGEX_LOCAL_MATRIXTO.exec(url);
return !!REGEX_LOCAL_PERMALINK.exec(url);
},
roomNotifPos: (text) => {
return text.indexOf("@room");
@ -95,22 +94,21 @@ const Pill = createReactClass({
},
async componentWillReceiveProps(nextProps) {
let regex = REGEX_MATRIXTO;
if (nextProps.inMessage) {
regex = REGEX_LOCAL_MATRIXTO;
}
let matrixToMatch;
let resourceId;
let prefix;
if (nextProps.url) {
// Default to the empty array if no match for simplicity
// resource and prefix will be undefined instead of throwing
matrixToMatch = regex.exec(nextProps.url) || [];
if (nextProps.inMessage) {
// Default to the empty array if no match for simplicity
// resource and prefix will be undefined instead of throwing
const matrixToMatch = REGEX_LOCAL_PERMALINK.exec(nextProps.url) || [];
resourceId = matrixToMatch[1]; // The room/user ID
prefix = matrixToMatch[2]; // The first character of prefix
resourceId = matrixToMatch[1]; // The room/user ID
prefix = matrixToMatch[2]; // The first character of prefix
} else {
resourceId = getPrimaryPermalinkEntity(nextProps.url);
prefix = resourceId ? resourceId[0] : undefined;
}
}
const pillType = this.props.type || {

View file

@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
import {wantsDateSeparator} from '../../../DateUtils';
import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
import {makeUserPermalink, RoomPermalinkCreator} from "../../../matrix-to";
import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
import SettingsStore from "../../../settings/SettingsStore";
// This component does no cycle detection, simply because the only way to make such a cycle would be to

View file

@ -59,7 +59,9 @@ export default createReactClass({
);
const av = (
<BaseAvatar name={this.props.member.userId}
<BaseAvatar
name={this.props.member.displayname || this.props.member.userId}
idName={this.props.member.userId}
width={36} height={36}
url={avatarUrl}
/>

View file

@ -19,7 +19,7 @@ import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import dis from '../../../dispatcher';
import { RoomPermalinkCreator } from '../../../matrix-to';
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';

View file

@ -30,9 +30,9 @@ import { _t } from '../../../languageHandler';
import * as ContextualMenu from '../../structures/ContextualMenu';
import SettingsStore from "../../../settings/SettingsStore";
import ReplyThread from "../elements/ReplyThread";
import {host as matrixtoHost} from '../../../matrix-to';
import {pillifyLinks} from '../../../utils/pillify';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
module.exports = createReactClass({
displayName: 'TextualBody',
@ -248,10 +248,10 @@ module.exports = createReactClass({
const url = node.getAttribute("href");
const host = url.match(/^https?:\/\/(.*?)(\/|$)/)[1];
// never preview matrix.to links (if anything we should give a smart
// never preview permalinks (if anything we should give a smart
// preview of the room/user they point to: nobody needs to be reminded
// what the matrix.to site looks like).
if (host === matrixtoHost) return false;
if (isPermalinkHost(host)) return false;
if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) {
// it's a "foo.pl" style link

View file

@ -20,18 +20,17 @@ import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import flatMap from 'lodash/flatMap';
import isEqual from 'lodash/isEqual';
import sdk from '../../../index';
import type {Completion} from '../../../autocomplete/Autocompleter';
import Promise from 'bluebird';
import { Room } from 'matrix-js-sdk';
import {getCompletions} from '../../../autocomplete/Autocompleter';
import SettingsStore from "../../../settings/SettingsStore";
import Autocompleter from '../../../autocomplete/Autocompleter';
const COMPOSER_SELECTED = 0;
export const generateCompletionDomId = (number) => `mx_Autocomplete_Completion_${number}`;
export default class Autocomplete extends React.Component {
constructor(props) {
super(props);
@ -224,7 +223,7 @@ export default class Autocomplete extends React.Component {
setSelection(selectionOffset: number) {
this.setState({selectionOffset, hide: false});
if (this.props.onSelectionChange) {
this.props.onSelectionChange(this.state.completionList[selectionOffset - 1]);
this.props.onSelectionChange(this.state.completionList[selectionOffset - 1], selectionOffset - 1);
}
}
@ -250,9 +249,8 @@ export default class Autocomplete extends React.Component {
let position = 1;
const renderedCompletions = this.state.completions.map((completionResult, i) => {
const completions = completionResult.completions.map((completion, i) => {
const className = classNames('mx_Autocomplete_Completion', {
'selected': position === this.state.selectionOffset,
});
const selected = position === this.state.selectionOffset;
const className = classNames('mx_Autocomplete_Completion', {selected});
const componentPosition = position;
position++;
@ -261,10 +259,12 @@ export default class Autocomplete extends React.Component {
};
return React.cloneElement(completion.component, {
key: i,
ref: `completion${position - 1}`,
"key": i,
"ref": `completion${componentPosition}`,
"id": generateCompletionDomId(componentPosition - 1), // 0 index the completion IDs
className,
onClick,
"aria-selected": selected,
});
});

View file

@ -28,7 +28,7 @@ import {
replaceRangeAndMoveCaret,
} from '../../../editor/operations';
import {getCaretOffsetAndText, getRangeForSelection} from '../../../editor/dom';
import Autocomplete from '../rooms/Autocomplete';
import Autocomplete, {generateCompletionDomId} from '../rooms/Autocomplete';
import {autoCompleteCreator} from '../../../editor/parts';
import {parsePlainTextMessage} from '../../../editor/deserialize';
import {renderModel} from '../../../editor/render';
@ -432,8 +432,9 @@ export default class BasicMessageEditor extends React.Component {
this.props.model.autoComplete.onComponentConfirm(completion);
}
_onAutoCompleteSelectionChange = (completion) => {
_onAutoCompleteSelectionChange = (completion, completionIndex) => {
this.props.model.autoComplete.onComponentSelectionChange(completion);
this.setState({completionIndex});
}
componentWillUnmount() {
@ -535,6 +536,8 @@ export default class BasicMessageEditor extends React.Component {
quote: ctrlShortcutLabel(">"),
};
const {completionIndex} = this.state;
return (<div className={classes}>
{ autoComplete }
<MessageComposerFormatBar ref={ref => this._formatBarRef = ref} onAction={this._onFormatAction} shortcuts={shortcuts} />
@ -548,7 +551,13 @@ export default class BasicMessageEditor extends React.Component {
onKeyDown={this._onKeyDown}
ref={ref => this._editorRef = ref}
aria-label={this.props.label}
></div>
role="textbox"
aria-multiline="true"
aria-autocomplete="both"
aria-haspopup="listbox"
aria-expanded={Boolean(this.state.autoComplete)}
aria-activedescendant={completionIndex >= 0 ? generateCompletionDomId(completionIndex) : undefined}
/>
</div>);
}

View file

@ -23,7 +23,7 @@ import sdk from '../../../index';
import dis from '../../../dispatcher';
import RoomViewStore from '../../../stores/RoomViewStore';
import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../matrix-to';
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
import ContentMessages from '../../../ContentMessages';
import classNames from 'classnames';
import E2EIcon from './E2EIcon';

View file

@ -48,13 +48,11 @@ import Markdown from '../../../Markdown';
import MessageComposerStore from '../../../stores/MessageComposerStore';
import ContentMessages from '../../../ContentMessages';
import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix';
import EMOJIBASE from 'emojibase-data/en/compact.json';
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import {makeUserPermalink} from "../../../matrix-to";
import {getPrimaryPermalinkEntity, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
import ReplyPreview from "./ReplyPreview";
import RoomViewStore from '../../../stores/RoomViewStore';
import ReplyThread from "../elements/ReplyThread";
@ -224,18 +222,15 @@ export default class MessageComposerInput extends React.Component {
// special case links
if (tag === 'a') {
const href = el.getAttribute('href');
let m;
if (href) {
m = href.match(MATRIXTO_URL_PATTERN);
}
if (m) {
const permalinkEntity = getPrimaryPermalinkEntity(href);
if (permalinkEntity) {
return {
object: 'inline',
type: 'pill',
data: {
href,
completion: el.innerText,
completionId: m[1],
completionId: permalinkEntity,
},
};
} else {
@ -541,7 +536,7 @@ export default class MessageComposerInput extends React.Component {
const textWithMdPills = this.plainWithMdPills.serialize(editorState);
const markdown = new Markdown(textWithMdPills);
// HTML deserialize has custom rules to turn matrix.to links into pill objects.
// HTML deserialize has custom rules to turn permalinks into pill objects.
return this.html.deserialize(markdown.toHTML());
}

View file

@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler';
import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore from "../../../settings/SettingsStore";
import PropTypes from "prop-types";
import {RoomPermalinkCreator} from "../../../matrix-to";
import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
function cancelQuoting() {
dis.dispatch({

View file

@ -805,7 +805,7 @@ module.exports = createReactClass({
const subListComponents = this._mapSubListProps(subLists);
return (
<div ref={this._collectResizeContainer} className="mx_RoomList"
<div ref={this._collectResizeContainer} className="mx_RoomList" role="listbox" aria-label={_t("Rooms")}
onMouseMove={this.onMouseMove} onMouseLeave={this.onMouseLeave}>
{ subListComponents }
</div>

View file

@ -33,6 +33,7 @@ import AccessibleButton from '../elements/AccessibleButton';
import ActiveRoomObserver from '../../../ActiveRoomObserver';
import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore from "../../../settings/SettingsStore";
import {_t} from "../../../languageHandler";
module.exports = createReactClass({
displayName: 'RoomTile',
@ -368,6 +369,8 @@ module.exports = createReactClass({
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
let ariaLabel = name;
let dmIndicator;
if (this._isDirectMessageRoom(this.props.room.roomId)) {
dmIndicator = <img
@ -379,12 +382,24 @@ module.exports = createReactClass({
/>;
}
if (notifBadges && mentionBadges && !isInvite) {
ariaLabel += " " + _t("It has %(count)s unread messages including mentions.", {
count: notificationCount,
});
} else if (notifBadges) {
ariaLabel += " " + _t("It has %(count)s unread messages.", { count: notificationCount });
} else if (mentionBadges && !isInvite) {
ariaLabel += " " + _t("It has unread mentions.");
}
return <AccessibleButton tabIndex="0"
className={classes}
onClick={this.onClick}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onContextMenu={this.onContextMenu}
aria-label={ariaLabel}
role="option"
>
<div className={avatarClasses}>
<div className="mx_RoomTile_avatar_container">

View file

@ -182,7 +182,7 @@ export default class SendMessageComposer extends React.Component {
// be extra resilient when somehow the AutocompleteWrapperModel or
// CommandPartCreator fails to insert a command part, so we don't send
// a command as a message
if (firstPart.type === "plain" && firstPart.text.startsWith("/")) {
if (firstPart.text.startsWith("/") && (firstPart.type === "plain" || firstPart.type === "pill-candidate")) {
return true;
}
}

View file

@ -25,7 +25,7 @@ import dis from '../../../dispatcher';
import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../matrix-to';
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
import ContentMessages from '../../../ContentMessages';
import classNames from 'classnames';

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from "react";
import PropTypes from "prop-types";
import {_t, pickBestLanguage} from "../../../languageHandler";
import sdk from "../../../..";
import sdk from "../../..";
export default class InlineTermsAgreement extends React.Component {
static propTypes = {

View file

@ -15,11 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MATRIXTO_URL_PATTERN } from '../linkify-matrix';
import { walkDOMDepthFirst } from "./dom";
import { checkBlockNode } from "../HtmlUtils";
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
import {getPrimaryPermalinkEntity} from "../utils/permalinks/Permalinks";
function parseAtRoomMentions(text, partCreator) {
const ATROOM = "@room";
@ -41,9 +39,8 @@ function parseAtRoomMentions(text, partCreator) {
function parseLink(a, partCreator) {
const {href} = a;
const pillMatch = REGEX_MATRIXTO.exec(href) || [];
const resourceId = pillMatch[1]; // The room/user ID
const prefix = pillMatch[2]; // The first character of prefix
const resourceId = getPrimaryPermalinkEntity(href); // The room/user ID
const prefix = resourceId ? resourceId[0] : undefined; // First character of ID
switch (prefix) {
case "@":
return partCreator.userPill(a.textContent, resourceId);

View file

@ -16,6 +16,7 @@ limitations under the License.
*/
import Markdown from '../Markdown';
import {makeGenericPermalink} from "../utils/permalinks/Permalinks";
export function mdSerialize(model) {
return model.parts.reduce((html, part) => {
@ -29,7 +30,7 @@ export function mdSerialize(model) {
return html + part.text;
case "room-pill":
case "user-pill":
return html + `[${part.text}](https://matrix.to/#/${part.resourceId})`;
return html + `[${part.text}](${makeGenericPermalink(part.resourceId)})`;
}
}, "");
}

View file

@ -1757,7 +1757,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Ключът за възстановяване дава допълнителна сигурност - може да го използвате за да възстановите достъпа до шифрованите съобщения, в случай че забравите паролата.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Пазете ключът за възстановяване на много сигурно място, например в password manager програма или сейф",
"Your keys are being backed up (the first backup could take a few minutes).": "Прави се резервно копие на ключовете Ви (първото копие може да отнеме няколко минути).",
"Okay": "Добре",
"Secure your backup with a passphrase": "Защитете резервното копие с парола",
"Confirm your passphrase": "Потвърдете паролата",
"Recovery key": "Ключ за възстановяване",
@ -2189,5 +2188,6 @@
"Make this room public": "Направи стаята публична",
"Hide advanced": "Скрий разширени настройки",
"Show advanced": "Покажи разширени настройки",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Блокирай присъединяването на потребители от други Matrix сървъри в тази стая (Тази настройка не може да се промени по-късно!)"
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Блокирай присъединяването на потребители от други Matrix сървъри в тази стая (Тази настройка не може да се промени по-късно!)",
"Close dialog": "Затвори прозореца"
}

View file

@ -1227,7 +1227,7 @@
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Chcete-li nadále používat domovský server %(homeserverDomain)s, měli byste si přečíst a odsouhlasit naše smluvní podmínky.",
"Review terms and conditions": "Přečíst smluvní podmínky",
"Did you know: you can use communities to filter your Riot.im experience!": "Věděli jste, že: práci s Riot.im si můžete zpříjemnit s použitím komunit!",
"To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Pro nastavení filtru, přetáhněte obrázek komunity na pantel foltrování na leve straně obrazovky. Potom můžete kdykoliv kliknout na obrazek komunity na tomto panelu a Riot.im Vám bude zobrazovat jen místnosti a lidi z dané komunity.",
"To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Pro nastavení filtru přetáhněte avatar komunity na panel filtrování na levé straně obrazovky. Potom můžete kdykoliv kliknout na avatar komunity na tomto panelu a Riot Vám bude zobrazovat jen místnosti a lidi z dané komunity.",
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.": "<showDevicesText>Zobrazit zařízení</showDevicesText>, <sendAnywayText>i tak odeslat</sendAnywayText> a nebo <cancelText>zrušit</cancelText>.",
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "Dokud si nepřečtete a neodsouhlasíte <consentLink>naše smluvní podmínky</consentLink>, nebudete moci posílat žádné zprávy.",
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.": "Vaše zpráva nebyla odeslána, protože tento domácí server dosáhl svého měsíčního limitu pro aktivní uživatele. Prosím <a>kontaktujte Vašeho administratora</a> pro další využívání služby.",
@ -1276,9 +1276,9 @@
"Set a new password": "Nastavit nové heslo",
"Room Name": "Jméno místnosti",
"Room Topic": "Téma místnosti",
"No room avatar": "Žádná ikona místnosti",
"Room avatar": "Ikona místnosti",
"Upload room avatar": "Nahrát ikonu místnosti",
"No room avatar": "Žádný avatar místnosti",
"Room avatar": "Avatar místnosti",
"Upload room avatar": "Nahrát avatara místnosti",
"Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Změny toho kdo smí číst historické zprávy se aplikují jenom na další zprávy v této místosti. Viditelnost už poslaných zpráv zůstane jaká byla.",
"To link to this room, please add an alias.": "K vytvoření odkazu je potřeba vyrobit místnosti alias.",
"Roles & Permissions": "Funkce & Práva",
@ -1588,7 +1588,6 @@
"<b>Save it</b> on a USB key or backup drive": "<b>Uložte ho</b> na bezpečnou USB flash disk nebo zálohovací disk",
"<b>Copy it</b> to your personal cloud storage": "<b>Zkopírujte si ho</b> na osobní cloudové úložiště",
"Your keys are being backed up (the first backup could take a few minutes).": "Klíče se zálohují (první záloha může trvat pár minut).",
"Okay": "Ok",
"Confirm your passphrase": "Potvrďte heslo",
"Recovery key": "Obnovovací klíč",
"Secure your backup with a passphrase": "Zabezpečte si zálohu silným heslem",
@ -1702,7 +1701,7 @@
"Scissors": "Nůžky",
"Close button should minimize window to tray": "Zavírací tlačítko má jen minimalizovat okno",
"Accept all %(invitedRooms)s invites": "Přijmout všechny pozvánky: %(invitedRooms)s",
"Change room avatar": "Změnit ikonu místnosti",
"Change room avatar": "Změnit avatara místnosti",
"Change room name": "Změnit jméno místnosti",
"Change main address for the room": "Změnit hlavní adresu místnosti",
"Change history visibility": "Změnit viditelnost historie",
@ -1909,5 +1908,99 @@
"Cannot reach identity server": "Nelze se připojit k serveru identity",
"You can register, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete se zaregistrovat, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru.",
"You can reset your password, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete si změnit heslo, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru.",
"You can log in, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete se přihlásit, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru."
"You can log in, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete se přihlásit, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru.",
"Call failed due to misconfigured server": "Volání selhalo, protože je rozbitá konfigurace serveru",
"Please ask the administrator of your homeserver (<code>%(homeserverDomain)s</code>) to configure a TURN server in order for calls to work reliably.": "Zeptejte se administrátora (<code>%(homeserverDomain)s</code>) jestli by nemohl nakonfigurovat server TURN, aby začalo fungoval volání.",
"Alternatively, you can try to use the public server at <code>turn.matrix.org</code>, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Případně můžete zkusit použít veřejný server <code>turn.matrix.org</code>, což nemusí fungovat tak spolehlivě a řekne to tomu cizímu serveru vaší IP adresu. Můžete to udělat v Nastavení.",
"Try using turn.matrix.org": "Zkuste použít turn.matrix.org",
"Failed to start chat": "Nepovedlo se začít chat",
"Messages": "Zprávy",
"Actions": "Akce",
"Sends a message as plain text, without interpreting it as markdown": "Pošle zprávu jako prostý text, neinterpretuje jí jako Markdown",
"You do not have the required permissions to use this command.": "Na provedení tohoto příkazu nemáte dostatečná oprávnění.",
"Changes the avatar of the current room": "Změní vašeho avatara pro tuto místnost",
"Changes your avatar in all rooms": "Změní vašeho avatara pro všechny místnosti",
"Use an identity server": "Používat server identit",
"Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Použít server identit na odeslání emailové pozvánky. Pokračováním použijete výchozí server identit (%(defaultIdentityServerName)s) nebo ho můžete změnit v Nastavení.",
"Use an identity server to invite by email. Manage in Settings.": "Použít server identit na odeslání emailové pozvánky. Můžete spravovat v Nastavení.",
"Displays list of commands with usages and descriptions": "Zobrazuje seznam příkazu s popiskem",
"%(senderName)s made no change.": "%(senderName)s neudělal žádnou změnu.",
"Use the new, faster, but still experimental composer for writing messages (requires refresh)": "Používat nový, rychlejší ale experimentální editor na zprávy (vyžaduje znovunačtení stránky)",
"Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Povolit použití serveru turn.matrix.org na spojení hlasového hovoru pokud váš domovský server tuto služby neposkytuje (sdělí to serveru vaší IP adresu)",
"Send read receipts for messages (requires compatible homeserver to disable)": "Odesílat potvrzení o přijetí (vypnutá volba vyžaduje kompatibilní domovský server)",
"Accept <policyLink /> to continue:": "Pro pokračování odsouhlaste <policyLink />:",
"ID": "ID",
"Public Name": "Veřejné jméno",
"No integrations server configured": "Žádná integrace není nakonfigurovaná",
"This Riot instance does not have an integrations server configured.": "Tento Riot nemá nakonfigurovaný žádný integrační server.",
"Connecting to integrations server...": "Připojování k integračnímu serveru...",
"Cannot connect to integrations server": "Nelze se připojit k integračními serveru",
"The integrations server is offline or it cannot reach your homeserver.": "Nelze se připojit k integračnímu nebo domovskému serveru.",
"Identity Server URL must be HTTPS": "Adresa serveru identit musí být na HTTPS",
"Not a valid Identity Server (status code %(code)s)": "Toto není validní server identit (stavový kód %(code)s)",
"Could not connect to Identity Server": "Nepovedlo se připojení k serveru identit",
"Checking server": "Kontrolování serveru",
"Change identity server": "Změnit server identit",
"Disconnect from the identity server <current /> and connect to <new /> instead?": "Odpojit se ze serveru <current /> a připojit na <new />?",
"Terms of service not accepted or the identity server is invalid.": "Neodsouhlasené podmínky použití a nebo neplatný server identit.",
"Identity server has no terms of service": "Server identit nemá žádné podmínky použití",
"The identity server you have chosen does not have any terms of service.": "Vybraný server identit nemá žádné podmínky použití.",
"Only continue if you trust the owner of the server.": "Pokračujte pouze pokud věříte provozovateli serveru.",
"Disconnect identity server": "Odpojit se ze serveru identit",
"Disconnect from the identity server <idserver />?": "Odpojit se ze serveru identit <idserver />?",
"Disconnect": "Odpojit",
"You are still <b>sharing your personal data</b> on the identity server <idserver />.": "Pořád <b>sdílíte osobní údaje</b> se serverem identit <idserver />.",
"We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Předtím než se odpojíte doporučujeme odstranit emailovou adresu a telefonní číslo ze serveru identit.",
"Disconnect anyway": "Stejně se odpojit",
"Identity Server (%(server)s)": "Server identit (%(server)s)",
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Aktuálně používáte server <server></server> na hledání existujících kontaktů. Níže můžete server identit změnit.",
"If you don't want to use <server /> to discover and be discoverable by existing contacts you know, enter another identity server below.": "Pokud nechcete na hledání existujících kontaktů používat server <server />, zvolte si jiný server.",
"Identity Server": "Server identit",
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Aktuálně nepoužíváte žádný server identit. Na hledání existujících kontaktů a přidání se do registru kontatů přidejte server identit níže.",
"Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Odpojení ze serveru identit znamená, že vás nepůjde najít podle emailové adresy a telefonního čísla and vy taky nebudete moct hledat ostatní.",
"Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Použití serveru identit je volitelné. Nemusíte server identit používat, ale nepůjde vás pak najít podle emailové adresy a telefonního čísla a vy také nebudete moct hledat ostatní.",
"Do not use an identity server": "Nepoužívat server identit",
"Enter a new identity server": "Zadejte nový server identit",
"Failed to update integration manager": "Nepovedlo se aktualizovat správce integrací",
"Integration manager offline or not accessible.": "Správce integrací není dostupný.",
"Terms of service not accepted or the integration manager is invalid.": "Neodsouhlasené podmínky použití nebo nefunkční správce integrací.",
"Integration manager has no terms of service": "Správce integrací nemá podmínky použití",
"The integration manager you have chosen does not have any terms of service.": "Správce integrací který jste si vybrali nemá žádné podmínky použití.",
"You are currently using <b>%(serverName)s</b> to manage your bots, widgets, and sticker packs.": "Aktálně používáte <b>%(serverName)s</b> na správu robotů, widgetů, and balíků samolepek.",
"Add which integration manager you want to manage your bots, widgets, and sticker packs.": "Zadejte kterého správce integrací chcete používat na správu robotů, widgetů a balíků samolepek.",
"Integration Manager": "Správce integrací",
"Enter a new integration manager": "Přidat nový správce integrací",
"Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Musíte odsouhlasit podmínky použití serveru (%(serverName)s) abyste se mohli zapsat do registru emailových adres a telefonních čísel.",
"Deactivate account": "Deaktivovat účet",
"Always show the window menu bar": "Vždy zobrazovat horní lištu okna",
"Use an identity server to invite by email. <default>Use the default (%(defaultIdentityServerName)s)</default> or manage in <settings>Settings</settings>.": "Odeslat pozvánku pomocí serveru identit. <default>Použít výchozí (%(defaultIdentityServerName)s)</default> nebo přenastavit <settings>Nastavení</settings>.",
"Use an identity server to invite by email. Manage in <settings>Settings</settings>.": "Odeslat pozvánku pomocí serveru identit. Přenastavit v <settings>Nastavení</settings>.",
"Close dialog": "Zavřít dialog",
"Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Napište nám prosím co se pokazilo a nebo nám napište issue na GitHub, kde popíšete problém.",
"Removing…": "Odstaňování…",
"Clear all data on this device?": "Smazat všechna data na tomto zařízení?",
"Clearing all data from this device is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Smazání všech dat na tomto zařízení je nevratné. Zašifrované zprávy nepůjde obnovit, pokud nejsou klíče zazálohované.",
"Clear all data": "Smazat všechna data",
"Please enter a name for the room": "Zadejte prosím jméno místnosti",
"Set a room alias to easily share your room with other people.": "Nastavte alias místnosti, abyste mohli místnost snadno sdílet s ostatními.",
"This room is private, and can only be joined by invitation.": "Tato místnost je neveřejná a lze se připojit pouze s pozvánkou.",
"Create a public room": "Vytvořit veřejnou místnost",
"Create a private room": "Vytvořit neveřejnou místnost",
"Topic (optional)": "Téma (volitelné)",
"Make this room public": "Zveřejnit místnost",
"Hide advanced": "Skrýt pokročilé",
"Show advanced": "Zobrazit pokročilé",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Zamezit uživatelům jiných domovských serverů, aby se připojili do místnosti (Toto nelze později změnit!)",
"To verify that this device can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Abyste ověřili, že je toto zařízení důvěryhodné, zkontrolujte, že klíč v Nastavení na tom zařízení se shoduje s tímto klíčem:",
"Your homeserver doesn't seem to support this feature.": "Váš domovský server asi tuto funkci nepodporuje.",
"Message edits": "Editování zpráv",
"Please fill why you're reporting.": "Vyplňte prosím co chcete nahlásit.",
"Report Content to Your Homeserver Administrator": "Nahlásit obsah administrátorovi vašeho domovského serveru",
"Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Nahlášení této zprávy pošle její jedinečné 'event ID' administrátorovi vašeho domovského serveru. Pokud jsou zprávy šifrované, administrátor nebude mít možnost přečíst text zprávy ani se podívat na soubory nebo obrázky.",
"Send report": "Nahlásit",
"Upgrading this room requires closing down the current instance of the room and creating a new room in its place. To give room members the best possible experience, we will:": "Upgrade vyžaduje zrušení této místnosti a vyrobení nové, která jí nahradí. Pro usnadnění procesu pro členy místnosti, provedeme:",
"Command Help": "Nápověda příkazu",
"Integrations Manager": "Správce integrací",
"Find others by phone or email": "Hledat ostatní pomocí emailu nebo telefonu",
"Be found by phone or email": "Umožnit ostatním mě nalézt pomocí emailu nebo telefonu"
}

View file

@ -112,7 +112,7 @@
"No rooms to show": "Ingen rum at vise",
"This email address is already in use": "Denne emailadresse er allerede i brug",
"This phone number is already in use": "Dette telefonnummer er allerede i brug",
"Failed to verify email address: make sure you clicked the link in the email": "Kunne ikke bekræfte emailaddressen: vær sikker på at du klikke på linket i emailen",
"Failed to verify email address: make sure you clicked the link in the email": "Kunne ikke bekræfte emailaddressen: vær sikker på at klikke på linket i emailen",
"Call Timeout": "Opkalds Timeout",
"The remote side failed to pick up": "Den anden side tog den ikke",
"Unable to capture screen": "Kunne ikke optage skærm",
@ -397,5 +397,9 @@
"View Community": "Vis community",
"%(count)s Members|one": "%(count)s medlem",
"Notes:": "Noter:",
"Preparing to send logs": "Forbereder afsendelse af logfiler"
"Preparing to send logs": "Forbereder afsendelse af logfiler",
"The platform you're on": "Den platform du bruger",
"The version of Riot.im": "Versionen af Riot.im",
"Whether or not you're logged in (we don't record your username)": "Om du er logget på eller ej (vi logger ikke dit brugernavn)",
"Your language of choice": "Dit foretrukne sprog"
}

View file

@ -1662,7 +1662,6 @@
"Recovery key": "Wiederherstellungsschlüssel",
"Confirm your passphrase": "Bestätige deine Passphrase",
"Secure your backup with a passphrase": "Sichere dein Backup mit einer Passphrase",
"Okay": "Okay",
"Your keys are being backed up (the first backup could take a few minutes).": "Deine Schlüssel werden gesichert (Das erste Backup könnte ein paar Minuten in Anspruch nehmen).",
"Voice & Video": "Sprache & Video",
"Use Key Backup": "Benutze Schlüssel Backup",

View file

@ -950,6 +950,9 @@
"Securely back up your keys to avoid losing them. <a>Learn more.</a>": "Securely back up your keys to avoid losing them. <a>Learn more.</a>",
"Not now": "Not now",
"Don't ask me again": "Don't ask me again",
"It has %(count)s unread messages including mentions.|other": "It has %(count)s unread messages including mentions.",
"It has %(count)s unread messages.|other": "It has %(count)s unread messages.",
"It has unread mentions.": "It has unread mentions.",
"Add a topic": "Add a topic",
"Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.",
"This room has already been upgraded.": "This room has already been upgraded.",
@ -1386,10 +1389,10 @@
"Be found by phone or email": "Be found by phone or email",
"Use bots, bridges, widgets and sticker packs": "Use bots, bridges, widgets and sticker packs",
"Terms of Service": "Terms of Service",
"To continue you need to accept the Terms of this service.": "To continue you need to accept the Terms of this service.",
"To continue you need to accept the terms of this service.": "To continue you need to accept the terms of this service.",
"Service": "Service",
"Summary": "Summary",
"Terms": "Terms",
"Document": "Document",
"Next": "Next",
"You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.",
"We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.",
@ -1729,11 +1732,16 @@
"Clear personal data": "Clear personal data",
"Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.": "Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.",
"Commands": "Commands",
"Community Autocomplete": "Community Autocomplete",
"Results from DuckDuckGo": "Results from DuckDuckGo",
"Emoji": "Emoji",
"Emoji Autocomplete": "Emoji Autocomplete",
"Notify the whole room": "Notify the whole room",
"Room Notification": "Room Notification",
"Notification Autocomplete": "Notification Autocomplete",
"Room Autocomplete": "Room Autocomplete",
"Users": "Users",
"User Autocomplete": "User Autocomplete",
"unknown device": "unknown device",
"NOT verified": "NOT verified",
"Blacklisted": "Blacklisted",
@ -1786,7 +1794,6 @@
"<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",
"Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).",
"Okay": "Okay",
"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",
"Secure your backup with a passphrase": "Secure your backup with a passphrase",

View file

@ -1389,7 +1389,6 @@
"That doesn't match.": "Tio ne kongruas.",
"Repeat your passphrase...": "Ripetu vian pasfrazon...",
"Download": "Elŝuti",
"Okay": "Bone",
"Success!": "Sukceso!",
"Retry": "Reprovi",
"Set up": "Agordi",
@ -1929,5 +1928,73 @@
"ID": "Identigilo",
"Public Name": "Publika nomo",
"Do not use an identity server": "Ne uzi identigan servilon",
"Enter a new identity server": "Enigi novan identigan servilon"
"Enter a new identity server": "Enigi novan identigan servilon",
"Failed to start chat": "Malsukcesis komenci babilon",
"Messages": "Mesaĝoj",
"Actions": "Agoj",
"Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Uzu identigan servilon por inviti retpoŝte. Klaku al »[…]« por uzi la implicitan identigan servilon (%(defaultIdentityServerName)s) aŭ administru tion en Agordoj.",
"Displays list of commands with usages and descriptions": "Montras liston de komandoj kun priskribo de uzo.",
"Use the new, faster, but still experimental composer for writing messages (requires refresh)": "Uzi la novan, pli rapidan, sed ankoraŭ eksperimentan komponilon de mesaĝoj (bezonas aktualigon)",
"Send read receipts for messages (requires compatible homeserver to disable)": "Sendi legokonfirmojn de mesaĝoj (bezonas akordan hejmservilon por malŝalto)",
"Accept <policyLink /> to continue:": "Akceptu <policyLink /> por daŭrigi:",
"Identity Server URL must be HTTPS": "URL de identiga servilo devas esti HTTPS-a",
"Not a valid Identity Server (status code %(code)s)": "Nevalida identiga servilo (statkodo %(code)s)",
"Could not connect to Identity Server": "Ne povis konektiĝi al identiga servilo",
"Checking server": "Kontrolante servilon",
"Change identity server": "Ŝanĝi identigan servilon",
"Disconnect from the identity server <current /> and connect to <new /> instead?": "Ĉu malkonekti de la nuna identiga servilo <current /> kaj konekti anstataŭe al <new />?",
"Terms of service not accepted or the identity server is invalid.": "Aŭ uzkondiĉoj ne akceptiĝis, aŭ la identiga servilo estas nevalida.",
"Identity server has no terms of service": "Identiga servilo havas neniujn uzkondiĉojn",
"The identity server you have chosen does not have any terms of service.": "La identiga servilo, kiun vi elektis, havas neniujn uzkondiĉojn.",
"Only continue if you trust the owner of the server.": "Nur daŭrigu se vi fidas al la posedanto de la servilo.",
"Disconnect identity server": "Malkonekti la identigan servilon",
"Disconnect from the identity server <idserver />?": "Ĉu malkonektiĝi de la identiga servilo <idserver />?",
"Disconnect": "Malkonekti",
"You are still <b>sharing your personal data</b> on the identity server <idserver />.": "Vi ankoraŭ <b>havigas siajn personajn datumojn</b> je la identiga servilo <idserver />.",
"We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Ni rekomendas, ke vi forigu viajn retpoŝtadresojn kaj telefonnumerojn de la identiga servilo, antaŭ ol vi malkonektiĝos.",
"Disconnect anyway": "Tamen malkonekti",
"Identity Server (%(server)s)": "Identiga servilo (%(server)s)",
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Vi nun uzas servilon <server></server> por trovi kontaktojn, kaj troviĝi de ili. Vi povas ŝanĝi vian identigan servilon sube.",
"If you don't want to use <server /> to discover and be discoverable by existing contacts you know, enter another identity server below.": "Se vi ne volas uzi servilon <server /> por trovi kontaktojn kaj troviĝi mem, enigu alian identigan servilon sube.",
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Vi nun ne uzas identigan servilon. Por trovi kontaktojn kaj troviĝi de ili mem, aldonu iun sube.",
"Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Malkonektiĝo de via identiga servilo signifas, ke vi ne povos troviĝi de aliaj uzantoj, kaj vi ne povos memfare inviti aliajn per retpoŝto aŭ telefono.",
"Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Vi ne devas uzi identigan servilon. Se vi tion elektos, vi ne povos troviĝi de aliaj uzantoj, kaj vi ne povos memfare inviti ilin per retpoŝto aŭ telefono.",
"Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Konsentu al uzkondiĉoj de la identiga servilo (%(serverName)s) por esti trovita per retpoŝtadreso aŭ telefonnumero.",
"Discovery": "Trovado",
"Deactivate account": "Malaktivigi konton",
"Always show the window menu bar": "Ĉiam montri la fenestran menubreton",
"A device's public name is visible to people you communicate with": "Publika nomo de aparato estas videbla de homoj, kun kiuj vi komunikas",
"Upgrade the room": "Gradaltigi la ĉambron",
"Enable room encryption": "Ŝalti ĉifradon de la ĉambro",
"Error changing power level requirement": "Eraris ŝanĝo de postulo de potenconivelo",
"An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de la postuloj de la ĉambro pri potenconivelo. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.",
"Error changing power level": "Eraris ŝanĝo de potenconivelo",
"An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de potenconivelo de la uzanto. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.",
"Remove %(email)s?": "Ĉu forigi %(email)s?",
"Remove %(phone)s?": "Ĉu forigi %(phone)s?",
"No recent messages by %(user)s found": "Neniuj freŝaj mesaĝoj de %(user)s troviĝis",
"Remove recent messages by %(user)s": "Forigi freŝajn mesaĝojn de %(user)s",
"You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|other": "Vi estas forigonta %(count)s mesaĝojn de %(user)s. Ne eblas tion malfari. Ĉu vi volas pluigi?",
"For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.": "Je granda nombro da mesaĝoj, tio povas daŭri iomon da tempo. Bonvolu ne aktualigi vian klienton dume.",
"Remove %(count)s messages|other": "Forigi %(count)s mesaĝojn",
"Deactivate user?": "Ĉu malaktivigi uzanton?",
"Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Malaktivigo de ĉi tiu uzanto adiaŭigos ĝin, kaj malebligos, ke ĝi resalutu. Plie, ĝi foriros de ĉiuj enataj ĉambroj. Tiu ago ne povas malfariĝi. Ĉu vi certe volas malaktivigi ĉi tiun uzanton?",
"Deactivate user": "Malaktivigi uzanton",
"Remove recent messages": "Forigi freŝajn mesaĝojn",
"Bold": "Grase",
"Italics": "Kursive",
"Strikethrough": "Trastrekite",
"Code block": "Kodujo",
"No Identity Server is configured so you cannot add add an email address in order to reset your password in the future.": "Neniu identiga servilo estas agordita, do vi ne povas aldoni retpoŝtadreson por restarigi ose vian pasvorton.",
"Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Agordi retpoŝtadreson por rehavo de konto. Uzu retpoŝton aŭ telefonon por laŭelekte troviĝi de jamaj kontaktoj.",
"Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Agordi retpoŝtadreson por rehavo de konto. Uzu retpoŝton por laŭelekte troviĝi de jamaj kontaktoj.",
"No Identity Server is configured: no email addreses can be added. You will be unable to reset your password.": "Neniu identiga servilo estas agordita: ne eblas aldoni retpoŝtadresojn. Vi ne povos restarigi vian pasvorton.",
"Explore": "Esplori",
"Filter": "Filtri",
"Filter rooms…": "Filtri ĉambrojn…",
"Preview": "Antaŭrigardo",
"View": "Rigardo",
"Find a room…": "Trovi ĉambron…",
"If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.": "Se vi ne povas travi la serĉatan ĉambron, petu inviton aŭ <a>kreu novan ĉambron</a>.",
"Explore rooms": "Esplori ĉambrojn"
}

View file

@ -1588,5 +1588,26 @@
"Scissors": "Tijeras",
"No integrations server configured": "No se ha configurado servidor de integraciones",
"This Riot instance does not have an integrations server configured.": "Esta instancia de Riot no tiene servidor de integraciones configurado.",
"Connecting to integrations server...": "Conectando al servidor de integraciones..."
"Connecting to integrations server...": "Conectando al servidor de integraciones...",
"Call failed due to misconfigured server": "Llamada fallida debido a la mala configuración del servidor",
"Please ask the administrator of your homeserver (<code>%(homeserverDomain)s</code>) to configure a TURN server in order for calls to work reliably.": "Por favor pídele al administrador de tu servidor doméstico (<code>%(homeserverDomain)s</code>) que configure un servidor TURN para que las llamadas funcionen correctamente.",
"Alternatively, you can try to use the public server at <code>turn.matrix.org</code>, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Alternativamente, puedes tratar de usar el servidor público en <code>turn.matrix.org</code>, pero éste no será igual de confiable, y compartirá tu dirección IP con ese servidor. También puedes administrar esto en Ajustes.",
"Try using turn.matrix.org": "Trata de usar turn.matrix.org",
"Failed to start chat": "Error al iniciar el chat",
"Messages": "Mensajes",
"Actions": "Acciones",
"Other": "Otros",
"Sends a message as plain text, without interpreting it as markdown": "Envía un mensaje como texto estándar, sin interpretarlo como Markdown",
"You do not have the required permissions to use this command.": "No tienes los permisos requeridos para usar este comando.",
"Changes the avatar of the current room": "Cambia el ávatar de la sala actual",
"Use an identity server": "Usar un servidor de identidad",
"Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Usar un servidor de identidad para invitar por correo. Presiona continuar par usar el servidor de identidad por defecto (%(defaultIdentityServerName)s) o adminístralo en Ajustes.",
"Use an identity server to invite by email. Manage in Settings.": "Usar un servidor de identidad para invitar por correo. Administrar en Ajustes.",
"Adds a custom widget by URL to the room": "Añade un widget personalizado por URL a la sala",
"Please supply a https:// or http:// widget URL": "Por favor provisiona un URL de widget de http:// o https://",
"You cannot modify widgets in this room.": "No puedes modificar widgets en esta sala.",
"Displays list of commands with usages and descriptions": "Muestra lista de comandos con usos y descripciones",
"Use the new, faster, but still experimental composer for writing messages (requires refresh)": "Usar el compositor nuevo y más rapido para escribir mensajes, pero todavía experimental (requiere que refresques la página)",
"Multiple integration managers": "Administradores de integración múltiples",
"Room upgrade confirmation": "Confirmación de actualización de sala"
}

View file

@ -1739,7 +1739,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Berreskuratze gakoa badaezpadako bat da, zure zifratutako mezuetara sarbidea berreskuratzeko erabili dezakezu pasaesaldia ahaztuz gero.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Gorde berreskuratze gakoa toki oso seguru batean, pasaesaldi kudeatzaile batean esaterako (edo gordailu kutxa batean)",
"Your keys are being backed up (the first backup could take a few minutes).": "Zure gakoen babes-kopia egiten ari da (lehen babes-kopiak minutu batzuk behar ditzake).",
"Okay": "Ados",
"Success!": "Ongi!",
"A new recovery passphrase and key for Secure Messages have been detected.": "Berreskuratze pasaesaldi eta mezu seguruen gako berriak antzeman dira.",
"This device is encrypting history using the new recovery method.": "Gailu honek historiala berreskuratze metodo berriarekin zifratzen du.",

View file

@ -1684,7 +1684,6 @@
"<b>Save it</b> on a USB key or backup drive": "<b>Tallenna se</b> muistitikulle tai varmuuskopiolevylle",
"<b>Copy it</b> to your personal cloud storage": "<b>Kopioi se</b> henkilökohtaiseen pilvitallennustilaasi",
"Your keys are being backed up (the first backup could take a few minutes).": "Avaimiasi varmuuskopioidaan (ensimmäinen varmuuskopio voi viedä muutaman minuutin).",
"Okay": "OK",
"Unable to create key backup": "Avaimen varmuuskopiota ei voi luoda",
"A verification email will be sent to your inbox to confirm setting your new password.": "Ottaaksesi käyttöön uuden salasanasi, seuraa ohjeita sinulle lähetettävässä vahvistussähköpostissa.",
"Room upgrade confirmation": "Huoneen päivitysvarmistus",

View file

@ -1768,7 +1768,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Votre clé de récupération est une mesure de précaution. Vous pouvez lutiliser pour restaurer laccès à vos messages chiffrés si vous oubliez votre phrase de passe.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Conservez votre clé de récupération dans un endroit très sécurisé, comme un gestionnaire de mots de passe (ou un coffre-fort)",
"Your keys are being backed up (the first backup could take a few minutes).": "Vous clés sont en cours de sauvegarde (la première sauvegarde peut prendre quelques minutes).",
"Okay": "OK",
"Secure your backup with a passphrase": "Protégez votre sauvegarde avec une phrase de passe",
"Confirm your passphrase": "Confirmez votre phrase de passe",
"Recovery key": "Clé de récupération",
@ -2202,5 +2201,21 @@
"Report Content": "Signaler le contenu",
"Read Marker lifetime (ms)": "Durée de vie du repère de lecture (ms)",
"Read Marker off-screen lifetime (ms)": "Durée de vie du repère de lecture en dehors de lécran (ms)",
"Changes the avatar of the current room": "Change lavatar du salon actuel"
"Changes the avatar of the current room": "Change lavatar du salon actuel",
"Room alias": "Alias du salon",
"e.g. my-room": "par ex. mon-salon",
"Please provide a room alias": "Veuillez renseigner un alias de salon",
"This alias is available to use": "Cet alias est disponible",
"This alias is already in use": "Cet alias est déjà utilisé",
"Close dialog": "Fermer la boîte de dialogue",
"Please enter a name for the room": "Veuillez renseigner un nom pour le salon",
"Set a room alias to easily share your room with other people.": "Définissez un alias de salon pour partager facilement votre salon avec dautres personnes.",
"This room is private, and can only be joined by invitation.": "Ce salon est privé et ne peut être rejoint que sur invitation.",
"Create a public room": "Créer un salon public",
"Create a private room": "Créer un salon privé",
"Topic (optional)": "Sujet (facultatif)",
"Make this room public": "Rendre ce salon public",
"Hide advanced": "Masquer les informations avancées",
"Show advanced": "Afficher les informations avancées",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Empêcher les utilisateurs dautres serveurs daccueil matrix de rejoindre ce salon (Ce paramètre ne peut pas être modifié plus tard !)"
}

View file

@ -1768,7 +1768,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "A Visszaállítási Kulcs egy olyan biztonsági elem amivel visszaállíthatod a hozzáférésed a titkosított üzenetekhez még akkor is, ha a jelmondatot elfelejtetted.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "A Visszaállítási Kulcsot nagyon biztonságos helyen tárold, mint pl. egy jelszókezelőben (vagy széfben)",
"Your keys are being backed up (the first backup could take a few minutes).": "A kulcsaid mentése folyamatban van (az első mentés több percig is eltarthat).",
"Okay": "Rendben",
"Secure your backup with a passphrase": "Védd a mentést egy jelmondattal",
"Confirm your passphrase": "Erősítsd meg a jelmondatot",
"Recovery key": "Visszaállítási Kulcs",
@ -2207,5 +2206,6 @@
"Make this room public": "A szoba legyen nyilvános",
"Hide advanced": "Haladó elrejtése",
"Show advanced": "Haladó megmutatása",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Felhasználók más matrix szerverekről a szobába való belépésének megakadályozása (Ezt a beállítást később nem lehet megváltoztatni!)"
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Felhasználók más matrix szerverekről a szobába való belépésének megakadályozása (Ezt a beállítást később nem lehet megváltoztatni!)",
"Close dialog": "Ablak bezárása"
}

View file

@ -1777,7 +1777,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "La tua chiave di ripristino è una rete di sicurezza - puoi usarla per recuperare l'accesso ai tuoi messaggi cifrati se dimentichi la tua password.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Conserva la chiave di ripristino in un luogo molto sicuro, come un password manager (o una cassaforte)",
"Your keys are being backed up (the first backup could take a few minutes).": "Il backup delle chiavi è in corso (il primo backup potrebbe richiedere qualche minuto).",
"Okay": "Okay",
"Secure your backup with a passphrase": "Proteggi il tuo backup con una password",
"Confirm your passphrase": "Conferma la tua password",
"Recovery key": "Chiave di ripristino",
@ -2132,5 +2131,39 @@
"Bold": "Grassetto",
"Italics": "Corsivo",
"Strikethrough": "Barrato",
"Code block": "Code block"
"Code block": "Code block",
"Changes the avatar of the current room": "Cambia l'avatar della stanza attuale",
"Send read receipts for messages (requires compatible homeserver to disable)": "Invia notifiche di lettura per i messaggi (richiede homeserver compatibile per disattivare)",
"Verify the link in your inbox": "Verifica il link nella tua posta in arrivo",
"Complete": "Completa",
"Room alias": "Alias stanza",
"e.g. my-room": "es. mia-stanza",
"Please provide a room alias": "Inserisci un alias per la stanza",
"This alias is available to use": "Questo alias è disponibile",
"This alias is already in use": "Questo alias è già in uso",
"Close dialog": "Chiudi finestra",
"Please enter a name for the room": "Inserisci un nome per la stanza",
"Set a room alias to easily share your room with other people.": "Imposta un alias stanza per condividerla facilmente con gli altri.",
"This room is private, and can only be joined by invitation.": "Questa stanza è privata ed è accessibile solo tramite invito.",
"Create a public room": "Crea una stanza pubblica",
"Create a private room": "Crea una stanza privata",
"Topic (optional)": "Argomento (facoltativo)",
"Make this room public": "Rendi questa stanza pubblica",
"Hide advanced": "Nascondi avanzate",
"Show advanced": "Mostra avanzate",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Impedisci agli utenti di altri homeserver Matrix di unirsi alla stanza (non può essere cambiato successivamente!)",
"Please fill why you're reporting.": "Inserisci il motivo della segnalazione.",
"Report Content to Your Homeserver Administrator": "Segnala il contenuto all'amministratore dell'homeserver",
"Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "La segnalazione di questo messaggio invierà il suo 'ID evento' univoco all'amministratore del tuo homeserver. Se i messaggi della stanza sono cifrati, l'amministratore non potrà leggere il messaggio o vedere file e immagini.",
"Send report": "Invia segnalazione",
"Report Content": "Segnala contenuto",
"Explore": "Esplora",
"Filter": "Filtra",
"Filter rooms…": "Filtra stanze…",
"Preview": "Anteprima",
"View": "Vedi",
"Find a room…": "Trova una stanza…",
"Find a room… (e.g. %(exampleRoom)s)": "Trova una stanza… (es. %(exampleRoom)s)",
"If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.": "Se non trovi la stanza che stai cercando, chiedi un invito o <a>Crea una stanza nuova</a>.",
"Explore rooms": "Esplora stanze"
}

View file

@ -1967,7 +1967,6 @@
"<b>Save it</b> on a USB key or backup drive": "USB 키나 백업 드라이브에 <b>저장</b>",
"<b>Copy it</b> to your personal cloud storage": "개인 클라우드 저장소에 <b>복사</b>",
"Your keys are being backed up (the first backup could take a few minutes).": "키를 백업했습니다 (처음 백업에는 시간이 걸릴 수 있습니다).",
"Okay": "네",
"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": "보안 메시지 복구 설정",
"Secure your backup with a passphrase": "암호로 백업 보호",
@ -2061,5 +2060,6 @@
"Make this room public": "이 방 주제를 정하기",
"Hide advanced": "고급 숨기기",
"Show advanced": "고급 보이기",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "다른 매트릭스 홈서버에서 이 방에 참가하려는 사용자를 막기 (이 설정은 이후 변경할 수 없습니다!)"
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "다른 매트릭스 홈서버에서 이 방에 참가하려는 사용자를 막기 (이 설정은 이후 변경할 수 없습니다!)",
"Close dialog": "대화 상자 닫기"
}

View file

@ -1712,7 +1712,6 @@
"<b>Save it</b> on a USB key or backup drive": "<b>Sla hem op</b> op een USB-stick of een back-upschijf",
"<b>Copy it</b> to your personal cloud storage": "<b>Kopieer hem</b> naar uw persoonlijke cloudopslag",
"Your keys are being backed up (the first backup could take a few minutes).": "Er wordt een back-up van uw sleutels gemaakt (de eerste back-up kan enkele minuten duren).",
"Okay": "Oké",
"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.": "Zonder veilig berichtherstel in te stellen, zult u uw versleutelde berichtgeschiedenis niet kunnen herstellen indien u zich afmeldt of een ander apparaat gebruikt.",
"Set up Secure Message Recovery": "Veilig berichtherstel instellen",
"Secure your backup with a passphrase": "Beveilig uw back-up met een wachtwoord",

View file

@ -986,7 +986,7 @@
"%(count)s of your messages have not been sent.|other": "Nokre av meldingane dine vart ikkje sende.",
"%(count)s of your messages have not been sent.|one": "Meldinga di vart ikkje send.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|other": "<resendText>Send alle på nytt</resendText> eller <cancelText>avbryt alle</cancelText>. Du kan ogso velja enkelte meldingar til sending på nytt eller avbryting.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Send melding på nytt</resendText> eller <cancelText>bryt av</cancelText>.",
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Send melding på nytt</resendText> eller <cancelText>avbryt</cancelText>.",
"Connectivity to the server has been lost.": "Tilkoplinga til tenaren vart tapt.",
"Sent messages will be stored until your connection has returned.": "Sende meldingar lagrast ikkje før tilkoplinga di er attende.",
"%(count)s new messages|other": "%(count)s nye meldingar",
@ -994,27 +994,27 @@
"Active call": "Pågåande samtale",
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "Det er ingen andre her! Vil du <inviteText>byda andre inn</inviteText> eller <nowarnText>enda åtvaringa om det tomme rommet?</nowarnText>?",
"more": "meir",
"You seem to be uploading files, are you sure you want to quit?": "Det ser ut til at du lastar filer opp, er du sikker på at du vil slutta?",
"You seem to be in a call, are you sure you want to quit?": "Det ser ut til at du er i ei samtale, er du sikker på at du vil slutta?",
"You seem to be uploading files, are you sure you want to quit?": "Det ser ut til at du lastar opp filer, er du sikker på at du vil avslutte?",
"You seem to be in a call, are you sure you want to quit?": "Det ser ut til at du er i ein samtale, er du sikker på at du vil avslutte?",
"Failed to upload file": "Fekk ikkje til å lasta fila opp",
"Server may be unavailable, overloaded, or the file too big": "Tenaren er kanskje utilgjengeleg, overlasta, elles so er fila for stor",
"Search failed": "Søket gjekk gale",
"Search failed": "Søket feila",
"No more results": "Ingen fleire resultat",
"Unknown room %(roomId)s": "Ukjend rom %(roomId)s",
"Room": "Rom",
"Failed to save settings": "Fekk ikkje til å lagra innstillingar",
"Failed to reject invite": "Fekk ikkje til å seia nei til innbydinga",
"Failed to reject invite": "Fekk ikkje til å avstå invitasjonen",
"Fill screen": "Fyll skjermen",
"Click to unmute video": "Klikk for å avstilna videoen",
"Click to mute video": "Klikk for å stilna videoen",
"Click to unmute audio": "Klikk for å avstilna ljoden",
"Click to mute audio": "Klikk for å stilna ljoden",
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Freista å lasta eit gjeve punkt i rommet si tidslinje, men du har ikkje lov til å sjå den sistnemnde meldinga.",
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Freista å lasta eit gjeve punkt i rommet si tidslinje, men klarte ikkje å finna det.",
"Click to mute audio": "Klikk for å dempe lyden",
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Prøvde å laste eit bestemt punkt i rommet si tidslinje, men du har ikkje lov til å sjå den spesifike meldingen.",
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Prøvde å lasta eit bestemt punkt i rommet si tidslinje, men klarde ikkje å finna det.",
"Failed to load timeline position": "Fekk ikkje til å lasta tidslinjestillinga",
"Uploading %(filename)s and %(count)s others|other": "Lastar %(filename)s og %(count)s til opp",
"Uploading %(filename)s and %(count)s others|zero": "Lastar %(filename)s opp",
"Uploading %(filename)s and %(count)s others|one": "Lastar %(filename)s og %(count)s til opp",
"Uploading %(filename)s and %(count)s others|other": "Lastar opp %(filename)s og %(count)s andre",
"Uploading %(filename)s and %(count)s others|zero": "Lastar opp %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Lastar opp %(filename)s og %(count)s andre",
"Light theme": "Ljost preg",
"Dark theme": "Dimt preg",
"Status.im theme": "Status.im-preg",
@ -1075,30 +1075,30 @@
"The email address linked to your account must be entered.": "Du må skriva epostadressa som er tilknytta brukaren din inn.",
"A new password must be entered.": "Du må skriva eit nytt passord inn.",
"New passwords must match each other.": "Dei nye passorda må vera like.",
"An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Ein epost vart send til %(emailAddress)s. Når du har far fylgd lenkja i den, klikk under.",
"I have verified my email address": "Eg har godkjend epostadressa mi",
"An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Ein e-post vart send til %(emailAddress)s. Når du har har følgd linken i den, klikk under.",
"I have verified my email address": "Eg har godkjend e-postadressa mi",
"Your password has been reset": "Passordet ditt vart attendesett",
"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": "Du vart logga av alle einingar og får ikkje lenger pushvarsel. For å skru varsel på att, logg inn igjen på kvar eining",
"Return to login screen": "Gå attende til innlogging",
"To reset your password, enter the email address linked to your account": "For å attendestilla passordet ditt, skriv epostadressa som er lenkja til brukaren din inn",
"New password": "Nytt passord",
"Confirm your new password": "Stadfest det nye passordet ditt",
"Send Reset Email": "Send attendestillingsepost",
"Send Reset Email": "Send e-post for nullstilling",
"Create an account": "Lag ein brukar",
"This Home Server does not support login using email address.": "Denne Heimtenaren støttar ikkje innlogging med epost.",
"Please contact your service administrator to continue using this service.": "Ver venleg og kontakt din tenesteadministrator for å halda fram med å bruka tenesten.",
"Incorrect username and/or password.": "Urett brukarnamn og/eller passord.",
"Incorrect username and/or password.": "Feil brukarnamn og/eller passord.",
"Please note you are logging into the %(hs)s server, not matrix.org.": "Merk deg at du loggar inn på %(hs)s-tenaren, ikkje matrix.org.",
"Guest access is disabled on this Home Server.": "Gjestetilgang er skrudd av på denne Heimtenaren.",
"The phone number entered looks invalid": "Det innskrivne telefonnummeret ser ugangbart ut",
"Error: Problem communicating with the given homeserver.": "Noko gjekk gale: fekk ikkje samband med den gjevne heimtenaren.",
"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>.": "Kan ikkje kopla til heimtenaren gjennom HTTP når ein HTTPS-URL er i nettlesarsøkjafeltet ditt. Bruk anten HTTPS eller <a>skru utrygge skript på</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.": "Kan ikkje kopla til heimtenaren - ver venleg og sjekk tilkoplinga di, og sjå til at <a>heimtenaren din sitt CCL-sertifikat</a> er stolt på og at ein nettlesarutviding ikkje hindrar førespurnader.",
"The phone number entered looks invalid": "Det innskrivne telefonnummeret virkar å vere ugyldig",
"Error: Problem communicating with the given homeserver.": "Feil: Det gjekk ikkje an å kommunisere med den spesifiserte heimeserveren.",
"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>.": "Kan ikkje koble til heimeserveren via HTTP fordi URL-adressa i nettlesaren er HTTPS. Bruk HTTPS, eller <a>aktiver usikre skript</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.": "Kan ikkje kopla til heimtenaren - ver venleg og sjekk tilkoplinga di, og sjå til at <a>heimtenaren din sitt CCL-sertifikat</a> er stolt på og at ein nettlesartillegg ikkje hindrar førespurnader.",
"Try the app first": "Prøv æppen fyrst",
"Sign in to get started": "Logg inn for å koma i gang",
"Failed to fetch avatar URL": "Klarte ikkje å henta avatar-URLen",
"Set a display name:": "Set eit visingsnamn:",
"Upload an avatar:": "Last ein avatar opp:",
"Upload an avatar:": "Last opp ein avatar:",
"This server does not support authentication with a phone number.": "Denne tenaren støttar ikkje stadfesting gjennom telefonnummer.",
"Missing password.": "Vantande passord.",
"Passwords don't match.": "Passorda er ikkje like.",
@ -1108,10 +1108,10 @@
"You need to enter a user name.": "Du må skriva eit brukarnamn inn.",
"An unknown error occurred.": "Noko ukjend gjekk gale.",
"I already have an account": "Eg har ein brukar allereie",
"Commands": "Påbod",
"Commands": "Kommandoar",
"Results from DuckDuckGo": "Resultat frå DuckDuckGo",
"Emoji": "Emoji",
"Notify the whole room": "Varsl heile rommet",
"Notify the whole room": "Varsle heile rommet",
"Room Notification": "Romvarsel",
"Users": "Brukarar",
"unknown device": "ukjend eining",
@ -1120,7 +1120,7 @@
"Verification": "Godkjenning",
"Ed25519 fingerprint": "Ed25519-fingeravtrykk",
"User ID": "Brukar-ID",
"Curve25519 identity key": "Curve25519-identitetsnykel",
"Curve25519 identity key": "Curve25519-identitetsnøkkel",
"none": "ingen",
"Algorithm": "Algoritme",
"unencrypted": "ikkje-kryptert",
@ -1129,10 +1129,10 @@
"End-to-end encryption information": "Ende-til-ende-krypteringsinfo",
"Event information": "Hendingsinfo",
"Sender device information": "Info om avsendareininga",
"Passphrases must match": "Passetningane må vera like",
"Passphrase must not be empty": "Passetningsfeltet kan ikkje vera tomt",
"Enter passphrase": "Skriv passetning inn",
"Confirm passphrase": "Stadfest passetning",
"Passphrases must match": "Passfrasane må vere identiske",
"Passphrase must not be empty": "Passefrasefeltet kan ikkje vera tomt",
"Enter passphrase": "Skriv inn passfrase",
"Confirm passphrase": "Stadfest passfrase",
"You must specify an event type!": "Du må oppgje ein handlingssort!",
"Call Timeout": "Tidsavbrot i Samtala",
"Enable automatic language detection for syntax highlighting": "Skru automatisk måloppdaging på for syntax-understreking",
@ -1152,7 +1152,7 @@
"You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "Du held på å verta teken til ei tredje-partisside so du kan godkjenna brukaren din til bruk med %(integrationsUrl)s. Vil du gå fram?",
"Token incorrect": "Teiknet er gale",
"Filter community members": "Filtrer samfunnsmedlemer",
"Custom Server Options": "Eigentenar-innstillingar",
"Custom Server Options": "Tilpassa tenar-innstillingar",
"Filter community rooms": "Filtrer samfunnsrom",
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie (please see our <PolicyLink>Cookie Policy</PolicyLink>).": "Ver venleg og hjelp oss å forbetra Riot.im ved å senda <UsageDataLink>anonym brukardata</UsageDataLink>. Dette brukar ei datakake (ver venleg og sjå på <PolicyLink>Datakakeretningslinene våre</PolicyLink>).",
"Please help improve Riot.im by sending <UsageDataLink>anonymous usage data</UsageDataLink>. This will use a cookie.": "Ver venleg og hjelp oss å forbetra Riot.im ved å senda <UsageDataLink>anonym brukardata</UsageDataLink>. Dette brukar ei datakake.",
@ -1187,7 +1187,7 @@
"Hide panel": "Gøym panel",
"Unable to look up room ID from server": "Klarte ikkje å henta rom-ID frå tenaren",
"Your message wasnt sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Meldinga di vart ikkje send fordi heimtenaren har truffe si Månadlege Grense for Aktive Brukarar. Ver venleg og tak kontakt med tenesteadministratoren din for å halda frama med å bruka tenesten.",
"Server may be unavailable, overloaded, or search timed out :(": "Tenaren er kanskje utilgjengeleg, overlasta, elles so vart søket tidsavbroten :(",
"Server may be unavailable, overloaded, or search timed out :(": "Tenaren er kanskje utilgjengeleg, overlasta, elles så vart søket tidsavbrote :(",
"Expand panel": "Utvid panel",
"Collapse panel": "Slå panel saman",
"Filter room names": "Filtrer romnamn",
@ -1199,23 +1199,23 @@
"Access Token:": "Tilgangs-Teikn:",
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Å attendestilla passordet vil førebels attendestilla alle ende-til-ende-krypteringsnyklar på alle einingar, slik at krypterte samtaler vert uleselege, med mindre du fyrst hentar romnyklane ut og hentar dei inn att etterpå. Dette vil forbetrast i framtida.",
"This homeserver has hit its Monthly Active User limit": "Heimtenaren har truffe den Månadlege Grensa si for Aktive Brukarar",
"This homeserver doesn't offer any login flows which are supported by this client.": "Heimtenaren tilbyd ingen nye innloggingsstraumar som støttast av denne klienten.",
"Claimed Ed25519 fingerprint key": "Gjorde krav på Ed25519-fingeravtrykksnykel",
"Export room keys": "Hent romnyklar ut",
"This homeserver doesn't offer any login flows which are supported by this client.": "Heimeserveren tilbyr ingen påloggingsmetodar som er støtta av denne klienten.",
"Claimed Ed25519 fingerprint key": "Gjorde krav på Ed25519-fingeravtrykksnøkkel",
"Export room keys": "Eksporter romnøklar",
"Bulk Options": "Innverknadsrike Innstillingar",
"Export": "Hent ut",
"Import room keys": "Hent romnyklar inn",
"File to import": "Fil til innhenting",
"Import": "Hent inn",
"Export": "Eksporter",
"Import room keys": "Importer romnøklar",
"File to import": "Fil til import",
"Import": "Importer",
"Failed to set direct chat tag": "Fekk ikkje til å setja direktesamtale-merke",
"Failed to remove tag %(tagName)s from room": "Fekk ikkje til å fjerna merket %(tagName)s frå rommet",
"Failed to add tag %(tagName)s to room": "Fekk ikkje til å leggja merket %(tagName)s til i rommet",
"Hide read receipts": "Gøym lesen-lappar",
"For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Av sikkerheitsmessige grunnar vil det å logga ut sletta alle ende-til-ende-krypteringsnyklar frå nettlesaren. Viss du vil kunna dekryptera samtalehistoria di på framtidige Riot-øykter, ver venleg og hent ut romnyklande dine og tak vare på dei.",
"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.": "Dette tillèt deg å henta nyklane for meldingar du har sendt i krypterte rom ut til ei lokal fil. Då kan du henta fila inn til ein annan Matrix-klient i framtida, slik at den klienten òg kan dekryptera meldingane.",
"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.": "Dette tillèt deg å henta nøklane for meldingar du har sendt i krypterte rom ut til ei lokal fil. Då kan du importera fila i ein annan Matrix-klient i framtida, slik at den klienten òg kan dekryptera meldingane.",
"The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Å henta filen ut tillèt kven som helst som kan lesa ho å dekryptera alle krypterte meldingar du kan sjå, so du bør passa på å halda ho trygg. For å hjelpa til med dette bør du skriva ei passetning inn i feltet under, som vil brukast til å kryptere den uthenta dataen. Det vil berre vera mogeleg å henta dataen inn med den same passetninga.",
"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.": "Dette tillèt deg å henta krypteringsnyklar som du tidlegare henta ut frå ein annan Matrix-klient inn. Du vil so kunna dekryptera alle meldingane som den andre klienten kunne dekryptera.",
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Uthentingsfila vil verta verna med ei passetning. Du bør skriva passetninga inn her for å dekryptera fila.",
"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.": "Dette tillèt deg å importere krypteringsnøklar som du tidlegare har eksportert frå ein annan Matrix-klient. Du har deretter moglegheit for å dekryptere alle meldingane som den andre klienten kunne dekryptere.",
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Den eksporterte fila vil bli verna med ein passfrase. Du bør skriva passfrasen her, for å dekryptere fila.",
"Internal room ID: ": "Indre rom-ID: ",
"Room version number: ": "Romutgåvenummer: ",
"This homeserver has hit its Monthly Active User limit. Please <a>contact your service administrator</a> to continue using the service.": "Heimtenaren har truffe si Månadlege Grense for Aktive Brukarar. Ver venleg og <a>tak kontakt med tenesteadministratoren din</a> for å halda fram med å bruka tenesten.",
@ -1229,5 +1229,106 @@
"You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "Du kan òg velja ein eigendefinert identitetstenar, men då kjem du ikkje til å innvitere brukarar gjennom e-post, eller verta invitert med e-post sjølv.",
"Whether or not you're logged in (we don't record your username)": "Om du er innlogga eller ikkje (vi lagrar ikkje brukarnamnet ditt)",
"The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Fila %(fileName)s er større enn heimetenaren si grense for opplastningar",
"Unable to load! Check your network connectivity and try again.": "Klarte ikkje lasta! Sjå på nettilkoplinga di og prøv igjen."
"Unable to load! Check your network connectivity and try again.": "Klarte ikkje lasta! Sjå på nettilkoplinga di og prøv igjen.",
"Your Riot is misconfigured": "Riot-klienten din er feilkonfiguert",
"Sign In": "Logg inn",
"Explore rooms": "Utforsk romma",
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.": "Meldingen din vart ikkje sent for denne heimeserveren har nådd grensa for maksimalt aktive brukarar pr. månad. Kontakt <a>systemadministratoren</a> for å kunne vidare nytte denne tenesten.",
"Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.": "Denne meldingen vart ikkje sendt fordi heimeserveren har nådd grensa for tilgjengelege systemressursar. Kontakt <a>systemadministratoren</a> for å vidare nytte denne tenesten.",
"Add room": "Legg til rom",
"You have %(count)s unread notifications in a prior version of this room.|other": "Du har %(count)s uleste varslingar i ein tidligare versjon av dette rommet.",
"You have %(count)s unread notifications in a prior version of this room.|one": "Du har %(count)s ulest varsel i ein tidligare versjon av dette rommet.",
"Guest": "Gjest",
"Your profile": "Din profil",
"Could not load user profile": "Klarde ikkje å laste brukarprofilen",
"Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.": "Endring av passordet vil nullstille nøklar for ende-til-ende kryptering på alle eningane dine. Alle tidligare meldingar og historikk vil bli ulesbar. Aktiver sikkerheitskopiering for nøklar (Key Backup) eller eksporter romnøklane på ein annan eining før du nullstiller passordet.",
"Your Matrix account on %(serverName)s": "Din Matrix-konto på %(serverName)s",
"Your Matrix account on <underlinedServerName />": "Din Matrix-konto på <underlinedServerName />",
"No identity server is configured: add one in server settings to reset your password.": "Ingen identitetsserver er konfigurert: legg til ein i innstillingane for å nullstille passordet ditt.",
"Sign in instead": "Logg inn istaden",
"A verification email will be sent to your inbox to confirm setting your new password.": "For å stadfeste tilbakestilling av passordet, vil ein e-post vil bli sendt til din innboks.",
"Your password has been reset.": "Passodet ditt vart nullstilt.",
"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.": "Du har blitt logga av frå alle einingane og vil ikkje lengre motta push-varslingar. For a reaktivere slike varsel, logg inn på nytt på kvar separate eining.",
"Set a new password": "Sett nytt passord",
"Invalid homeserver discovery response": "Ugyldig svar frå heimeserveren (discovery response)",
"Failed to get autodiscovery configuration from server": "Kladte ikkje å hente automatisk oppsett frå server",
"Invalid base_url for m.homeserver": "Ugyldig base_url for m.homeserver",
"Homeserver URL does not appear to be a valid Matrix homeserver": "URL-adressa virkar ikkje til å vere ein gyldig Matrix-heimeserver",
"Invalid identity server discovery response": "Ugyldig svar frå identitetsserveren (discovery response)",
"Invalid base_url for m.identity_server": "Ugyldig base_url for m.identity_server",
"Identity server URL does not appear to be a valid identity server": "URL-adressa virkar ikkje til å vere ein gyldig identitetsserver",
"General failure": "Generell feil",
"This homeserver does not support login using email address.": "Denne heimeserveren støttar ikkje innloggingar med e-postadresser.",
"Please <a>contact your service administrator</a> to continue using this service.": "<a>Kontakt din systemadministrator</a> for å vidare å bruke tenesta.",
"This account has been deactivated.": "Denne kontoen har blitt deaktivert.",
"Failed to perform homeserver discovery": "Fekk ikkje til å utforske heimeserveren",
"Sign in with single sign-on": "Logg på med Single-Sign-On",
"Create account": "Lag konto",
"Registration has been disabled on this homeserver.": "Registrering er deaktivert på denne heimeserveren.",
"Unable to query for supported registration methods.": "Klarte ikkje å spørre etter støtta registreringsmetodar.",
"Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "Kontoen din %(newAccountId)s er no registrert, men du er frå tidligare logga på med ein annan konto (%(loggedInUserId)s).",
"Continue with previous account": "Fortsett med tidligare konto",
"<a>Log in</a> to your new account.": "<a>Logg på</a> den nye kontoen din.",
"You can now close this window or <a>log in</a> to your new account.": "Du kan lukke dette vindauget eller<a>logge inn</a> med din nye konto.",
"Registration Successful": "Registrering fullført",
"Create your account": "Lag din konto",
"Failed to re-authenticate due to a homeserver problem": "Fekk ikkje til å re-authentisere grunna ein feil på heimeserveren",
"Failed to re-authenticate": "Fekk ikkje til å re-autentisere",
"Regain access to your account and recover encryption keys stored on this device. Without them, you wont be able to read all of your secure messages on any device.": "Ta tilbake tilgang til din konto og gjenopprett krypteringsnøklar lagra på denne eininga. Du kan ikkje lese krypterte meldingar viss du ikkje har desse.",
"Enter your password to sign in and regain access to your account.": "Skriv inn ditt passord for å logge på og ta tilbake tilgang til kontoen din.",
"Forgotten your password?": "Gløymt passord ?",
"Sign in and regain access to your account.": "Logg på og ta tilbake tilgang til din konto.",
"You cannot sign in to your account. Please contact your homeserver admin for more information.": "Du har ikkje muligheit til å logge på kontoen din. Kontakt systemadministratoren for meir informasjon.",
"You're signed out": "Du er no avlogga",
"Clear personal data": "Fjern personlege data",
"Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.": "Åtvaring: Dine personelge data (inklusive krypteringsnøklar) er fortsatt lagra på denne eininga. Fjern dette viss du ikkje skal bruke kontoen på denne eininga, eller logge på med ein annan konto.",
"Great! This passphrase looks strong enough.": "Flott! Denne passfrasen virkar til å vere sterk nok.",
"We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Vi vil lagre ein kryptert kopi av nøklane dine på vår server. Vern sikkerheitskopien din med ein passfrase.",
"For maximum security, this should be different from your account password.": "For maksimal sikkerheit, bør dette vere noko anna enn kontopassordet ditt.",
"Enter a passphrase...": "Skriv inn ein passfrase...",
"Set up with a Recovery Key": "Sett opp med ein gjenopprettingsnøkkel",
"That matches!": "Dette stemmer!",
"That doesn't match.": "Dette stemmer ikkje.",
"Go back to set it again.": "Gå tilbake for å sette den på nytt.",
"Please enter your passphrase a second time to confirm.": "Skriv inn passfrasen din ein gong til for å stadfeste.",
"Repeat your passphrase...": "Gjenta din passfrase...",
"As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "Som eit sikkerheitsnett, kan du bruke den til å gjenopprette den krypterte meldingshistorikken i tilfelle du gløymer gjennopprettingsnøkkelen.",
"As a safety net, you can use it to restore your encrypted message history.": "Som eit sikkerheitsnett, kan du bruke den til å gjenopprette den krypterte meldingshistorikken.",
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Gjennopprettingsnøkkelen eit sikkerheitsnett, kan du bruke den til å gjenopprette den krypterte meldingshistorikken i tilfelle du gløymer passfrasen.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Ta vare på gjenopprettingsnøkkelen din på ein trygg plass, som for eksempel i eit passordhandteringsprogram (eller ein safe)",
"Your Recovery Key": "Gjenopprettingsnøkkelen din",
"Copy to clipboard": "Kopier til utklippstavle",
"Download": "Last ned",
"Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:": "Gjenopprettingsnøkkelen din har blitt <b>kopiert til utklippstavla</b>, lim den til:",
"Your Recovery Key is in your <b>Downloads</b> folder.": "Gjenopprettingsnøkkelen ligg i <b>Nedlastnings</b>-mappa.",
"<b>Print it</b> and store it somewhere safe": "<b>Skriv ut</b> og ta vare på den på ein trygg stad",
"<b>Save it</b> on a USB key or backup drive": "<b>Lagre</b> til ein USB-pinne eller sikkerheitskopidisk",
"<b>Copy it</b> to your personal cloud storage": "<b>Kopier den</b> til personleg skylagring",
"Your keys are being backed up (the first backup could take a few minutes).": "Nøklane dine blir sikkerheitskopiert (den første kopieringa kan ta nokre minutt).",
"Okay": "Ok",
"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.": "Loggar du av eller brukar ein annan einheit, vil du ikkje ha mulegheit til å hente fram historikk for krypterte meldingar med mindre sikker gjenoppretting for meldingar (Secure Message Recovery) er satt opp.",
"Set up Secure Message Recovery": "Sett opp sikker gjenoppretting for meldingar",
"Secure your backup with a passphrase": "Gjer sikkerheitskopien trygg med ein passfrase",
"Confirm your passphrase": "Stadfest passfrasen din",
"Recovery key": "Gjenopprettingsnøkkel",
"Keep it safe": "Lagre den trygt",
"Starting backup...": "Startar sikkerheitskopiering...",
"Success!": "Suksess!",
"Create Key Backup": "Lag sikkerheitskopi av nøkkel",
"Unable to create key backup": "Klarte ikkje å lage sikkerheitskopi av nøkkelen",
"Retry": "Prøv om att",
"Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Utan å sette opp sikker gjenoppretting for meldingar (Secure Message Recovery) vil meldingshistorikken gå tapt når du loggar av.",
"If you don't want to set this up now, you can later in Settings.": "Ønskjer du ikkje å sette opp dette no, kan du gjere det seinare i innstillingane.",
"Set up": "Sett opp",
"Don't ask again": "Ikkje spør igjen",
"New Recovery Method": "Ny gjenopprettingsmetode",
"A new recovery passphrase and key for Secure Messages have been detected.": "Ein ny gjenopprettingspassfrase og nøkkel for sikre meldingar vart funne.",
"If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Har du ikkje satt opp den nye gjenopprettingsmetoden, kan ein angripar prøve å bryte seg inn på kontoen din. Endre ditt kontopassord og sett opp gjenoppretting umiddelbart under instillingane.",
"This device is encrypting history using the new recovery method.": "Denne eininga krypterer meldingshistorikken med den nye gjenopprettingsmetoden.",
"Go to Settings": "Gå til innstillingar",
"Set up Secure Messages": "Sett opp sikre meldingar (Secure Messages)",
"Recovery Method Removed": "Gjenopprettingsmetode fjerna",
"This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Denne eininga har oppdaga at gjenopprettingspassfrasen og nøkkelen for sikre meldingar er fjerna.",
"If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Gjorde du dette ved eit uhell, kan du sette opp sikre meldingar på denne eininga. Dette vil kryptere all meldingshistorikk med ein ny gjenopprettingsmetode.",
"If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Viss du ikkje fjerna gjenopprettingsmetoden, kan ein angripar prøve å bryte seg inn på kontoen din. Endre kontopassordet ditt og sett ein opp ein ny gjenopprettingsmetode umidellbart under Innstillingar."
}

View file

@ -1882,7 +1882,6 @@
"Your Recovery Key has been <b>copied to your clipboard</b>, paste it to:": "Ваш ключ восстановления <b>скопирован в буфер обмена</b>, вставьте его в:",
"Your Recovery Key is in your <b>Downloads</b> folder.": "Ваш ключ восстановления находится в папке <b>Загрузки</b>.",
"<b>Print it</b> and store it somewhere safe": "<b>Распечатайте его</b> и храните в безопасном месте",
"Okay": "ОК",
"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.": "Без настройки безопасного восстановления сообщений вы не сможете восстановить историю зашифрованных сообщений, если выйдете из системы или используете другое устройство.",
"Secure your backup with a passphrase": "Защитите вашу резервную копию паролем",
"Confirm your passphrase": "Подтвердите свой пароль",

View file

@ -1771,7 +1771,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kľúč obnovenia je bezpečnostný mechanizmus - môžete ho použiť na prístup k šifrovacím kľúčom v prípade, ak zabudnete vaše heslo obnovenia.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Kľúč obnovenia si bezpečne uchovajte, použite napr. správcu hesiel (alebo ho schovajte v trezore)",
"Your keys are being backed up (the first backup could take a few minutes).": "Zálohovanie kľúčov máte aktívne (prvé zálohovanie môže trvať niekoľko minút).",
"Okay": "OK",
"Secure your backup with a passphrase": "Zabezpečte si zálohu zadaním hesla obnovenia",
"Confirm your passphrase": "Potvrdiť heslo obnovenia",
"Recovery key": "Kľúč obnovenia",

View file

@ -1734,7 +1734,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kyçi juaj i rikthimeve është një rrjet sigurie - mund ta përdorni për rikthim hyrjeje te mesazhet tuaj të fshehtëzuar, nëse harroni frazëkalimin tuaj.",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Mbajeni kyçin tuaj të rikthimeve diku në një vend shumë të sigurt, bie fjala, nën një përgjegjës fjalëkalimesh (ose në një kasafortë)",
"Your keys are being backed up (the first backup could take a few minutes).": "Kyçet tuaj po kopjeruhen (kopjeruajtja e parë mund të hajë disa minuta).",
"Okay": "Në rregull",
"Secure your backup with a passphrase": "Sigurojeni kopjeruajtjen tuaj me një frazëkalim",
"Confirm your passphrase": "Ripohoni frazëkalimin tuaj",
"Recovery key": "Kyç rikthimesh",
@ -2148,5 +2147,32 @@
"Find a room…": "Gjeni një dhomë…",
"Find a room… (e.g. %(exampleRoom)s)": "Gjeni një dhomë… (p.sh. %(exampleRoom)s)",
"If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.": "Nëse sgjeni dot dhomën që po kërkoni, kërkoni një ftesë ose <a>Krijoni një dhomë të re</a>.",
"Explore rooms": "Eksploroni dhoma"
"Explore rooms": "Eksploroni dhoma",
"Changes the avatar of the current room": "Ndryshon avatarin e dhomës së atëçastshme",
"Read Marker lifetime (ms)": "Kohëzgjatje e Shenjës së Leximit (ms)",
"Read Marker off-screen lifetime (ms)": "Kohëzgjatje Shenje Leximi jashtë ekrani (ms)",
"Verify the link in your inbox": "Verifikoni lidhjen te mesazhet tuaj",
"Room alias": "Alias dhome",
"e.g. my-room": "p.sh., dhoma-ime",
"Please provide a room alias": "Ju lutemi, jepni një alias dhome",
"This alias is available to use": "Ky alias është i lirë të përdoret",
"This alias is already in use": "Ky alias është i përdorur tashmë",
"Close dialog": "Mbylle dialogun",
"Please enter a name for the room": "Ju lutemi, jepni një emër për dhomën",
"Set a room alias to easily share your room with other people.": "Caktoni një alias dhome për tua dhënë lehtësisht dhomën tuaj personave të tjerë.",
"This room is private, and can only be joined by invitation.": "Kjo dhomë është private dhe në të mund të hyhet vetëm me ftesë.",
"Create a public room": "Krijoni një dhomë publike",
"Create a private room": "Krijoni një dhomë private",
"Topic (optional)": "Temë (në daçi)",
"Make this room public": "Bëje publike këtë dhomë",
"Hide advanced": "Fshihi të mëtejshmet",
"Show advanced": "Shfaqi të mëtejshmet",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Bllokojua hyrjen në këtë dhomë përdoruesve prej shërbyesish Matrix të tjerë Home (Ky rregullim smund të ndryshohet më vonë!)",
"Please fill why you're reporting.": "Ju lutemi, plotësoni arsyen pse po raportoni.",
"Report Content to Your Homeserver Administrator": "Raportoni Lëndë te Përgjegjësi i Shërbyesit Tuaj Home",
"Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Raportimi i këtij mesazhi do të shkaktojë dërgimin e 'ID-së së aktit' unike te përgjegjësi i shërbyesit tuaj Home. Nëse mesazhet në këtë dhomë fshehtëzohen, përgjegjësi i shërbyesit tuaj Home sdo të jetë në gjendje të lexojë tekstin e mesazhit apo të shohë çfarëdo kartelë apo figurë.",
"Send report": "Dërgoje raportin",
"To continue you need to accept the terms of this service.": "Që të vazhdohet, lypset të pranoni kushtet e këtij shërbimi.",
"Document": "Dokument",
"Report Content": "Raportoni Lëndë"
}

View file

@ -1256,5 +1256,10 @@
"Username": "Корисничко име",
"You need to enter a username.": "Морате унети корисничко име.",
"Not sure of your password? <a>Set a new one</a>": "Не сећате се лозинке? <a>Поставите нову</a>",
"Are you sure you want to sign out?": "Да ли сте сигурни да се желите одјавити?"
"Are you sure you want to sign out?": "Да ли сте сигурни да се желите одјавити?",
"Call failed due to misconfigured server": "Позив није успео због погрешно подешеног сервера",
"Please ask the administrator of your homeserver (<code>%(homeserverDomain)s</code>) to configure a TURN server in order for calls to work reliably.": "Замолите администратора вашег сервера (<code>%(homeserverDomain)s</code>) да подеси TURN сервер како би позиви радили поуздано.",
"Alternatively, you can try to use the public server at <code>turn.matrix.org</code>, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Или, можете покушати да користите јавни сервер на <code>turn.matrix.org</code>, али ово неће бити толико поуздано, и поделиће вашу IP адресу са тим сервером. Ово такође можете мењати у Подешавањима.",
"Try using turn.matrix.org": "Покушајте да користите turn.matrix.org",
"A conference call could not be started because the integrations server is not available": "Конференцијски позив није могао бити започет јер интеграциони сервер није расположив"
}

View file

@ -1793,5 +1793,24 @@
"This homeserver does not support communities": "Denna hemserver stöder inte communityn",
"Explore": "Utforska",
"Filter": "Filtrera",
"Filter rooms…": "Filtrera rum…"
"Filter rooms…": "Filtrera rum…",
"Please ask the administrator of your homeserver (<code>%(homeserverDomain)s</code>) to configure a TURN server in order for calls to work reliably.": "Be administratören för din hemserver (<code>%(homeserverDomain)s</code>) att konfigurera en TURN-server för att samtal ska fungera pålitligt.",
"Alternatively, you can try to use the public server at <code>turn.matrix.org</code>, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Alternativt kan du testa att använda den offentliga servern <code>turn.matrix.org</code>, men det är inte lika pålitligt och det kommer att dela din IP-adress med den servern. Du kan också hantera detta under Inställningar.",
"<b>Warning</b>: Upgrading a room will <i>not automatically migrate room members to the new version of the room.</i> We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "<b>Varning</b>: Uppgradering av ett rum <i>flyttar inte automatiskt rumsmedlemmar till den nya versionen av rummet.</i> Vi lägger ut en länk till det nya rummet i den gamla versionen av rummet - rumsmedlemmar måste klicka på den här länken för att gå med i det nya rummet.",
"Please confirm that you'd like to go forward with upgrading this room from <oldVersion /> to <newVersion />.": "Bekräfta att du vill gå vidare med att uppgradera detta rum från <oldVersion /> till <newVersion />.",
"Changes the avatar of the current room": "Ändrar avataren i det aktuella rummet",
"Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Använd en identitetsserver för att bjuda in via epost. Klicka på Fortsätt för att använda standardidentitetsservern (%(defaultIdentityServerName)s) eller hantera det i Inställningar.",
"Use an identity server to invite by email. Manage in Settings.": "Använd en identitetsserver för att bjuda in via epost. Hantera det i inställningar.",
"Unexpected error resolving homeserver configuration": "Oväntat fel vid inläsning av hemserverkonfiguration",
"Unexpected error resolving identity server configuration": "Oväntat fel vid inläsning av identitetsserverkonfiguration",
"Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Tillåt samtalsserver turn.matrix.org som reserv när din hemserver inte erbjuder en (din IP-adress delades under ett samtal)",
"Unable to load key backup status": "Det går inte att ladda status för nyckelsäkerhetskopiering",
"Restore from Backup": "Återställ från säkerhetskopiering",
"This device is backing up your keys. ": "Den här enheten säkerhetskopierar dina nycklar. ",
"This device is <b>not backing up your keys</b>, but you do have an existing backup you can restore from and add to going forward.": "Den här enheten <b>säkerhetskopierar inte dina nycklar</b>, men du har en befintlig säkerhetskopia som du kan återställa från och lägga till i framöver.",
"Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.": "Anslut denna enhet till säkerhetskopiering innan du loggar ut för att undvika att förlora några nycklar som kanske bara finns på den här enheten.",
"Connect this device to Key Backup": "Anslut den här enheten till nyckelsäkerhetskopiering",
"Backing up %(sessionsRemaining)s keys...": "Säkerhetskopierar %(sessionsRemaining)s nycklar...",
"All keys backed up": "Alla nycklar säkerhetskopierade",
"Backup has a signature from <verify>unknown</verify> device with ID %(deviceId)s.": "Säkerhetskopian har en signatur från <verify>okänd</verify> enhet med ID %(deviceId)s."
}

View file

@ -1604,7 +1604,6 @@
"<b>Save it</b> on a USB key or backup drive": "<b>Sloat hem ip</b> ip een USB-stick of e back-upschyf",
"<b>Copy it</b> to your personal cloud storage": "<b>Kopieert hem</b> noa je persoonlike cloudipslag",
"Your keys are being backed up (the first backup could take a few minutes).": "t Wordt e back-up van je sleuters gemakt (den eesten back-up kut e poar minuutn deurn).",
"Okay": "Oké",
"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.": "Zounder veilig berichtherstel in te stelln, goa je je versleuterde berichtgeschiedenisse nie kunn herstelln indien da je jen afmeldt of een ander toestel gebruukt.",
"Set up Secure Message Recovery": "Veilig berichtherstel instelln",
"Secure your backup with a passphrase": "Beveilig je back-up met e paswoord",

View file

@ -1701,7 +1701,6 @@
"<b>Save it</b> on a USB key or backup drive": "<b>保存</b> 在 U 盘或备份磁盘中",
"<b>Copy it</b> to your personal cloud storage": "<b>复制</b> 到您的个人云端存储",
"Your keys are being backed up (the first backup could take a few minutes).": "正在备份您的密钥(第一次备份可能会花费几分钟时间)。",
"Okay": "好",
"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": "设置安全消息恢复",
"Secure your backup with a passphrase": "使用密码保护您的备份",

View file

@ -1768,7 +1768,6 @@
"Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "您的復原金鑰是安全網,如果您忘記您的通關密語的話,您還可以使用它來復原您對加密訊息的存取權。",
"Keep your recovery key somewhere very secure, like a password manager (or a safe)": "把您的復原金鑰放在安全的地方,像是密碼管理員(或保險箱)",
"Your keys are being backed up (the first backup could take a few minutes).": "您的金鑰正在備份(第一次備份會花費數分鐘)。",
"Okay": "好",
"Secure your backup with a passphrase": "使用通關密語保障您備份的安全",
"Confirm your passphrase": "確認您的通關密語",
"Recovery key": "復原金鑰",
@ -2210,5 +2209,6 @@
"Make this room public": "讓聊天室公開",
"Hide advanced": "隱藏進階的",
"Show advanced": "顯示進階的",
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "阻擋其他 matrix 伺服器上的使用加入此聊天室(此設定無法在之後變更!)"
"Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "阻擋其他 matrix 伺服器上的使用加入此聊天室(此設定無法在之後變更!)",
"Close dialog": "關閉對話框"
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {baseUrl} from "./matrix-to";
import {baseUrl} from "./utils/permalinks/SpecPermalinkConstructor";
import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks";
function matrixLinkify(linkify) {
// Text tokens
@ -189,13 +191,6 @@ matrixLinkify.MATRIXTO_MD_LINK_PATTERN =
'\\[([^\\]]*)\\]\\((?:https?://)?(?:www\\.)?matrix\\.to/#/([#@!+][^\\)]*)\\)';
matrixLinkify.MATRIXTO_BASE_URL= baseUrl;
const matrixToEntityMap = {
'@': '#/user/',
'#': '#/room/',
'!': '#/room/',
'+': '#/group/',
};
matrixLinkify.options = {
events: function(href, type) {
switch (type) {
@ -225,20 +220,8 @@ matrixLinkify.options = {
case 'roomalias':
case 'userid':
case 'groupid':
return matrixLinkify.MATRIXTO_BASE_URL + '/#/' + href;
default: {
// FIXME: horrible duplication with HtmlUtils' transform tags
let m = href.match(matrixLinkify.VECTOR_URL_PATTERN);
if (m) {
return m[1];
}
m = href.match(matrixLinkify.MATRIXTO_URL_PATTERN);
if (m) {
const entity = m[1];
if (matrixToEntityMap[entity[0]]) return matrixToEntityMap[entity[0]] + entity;
}
return href;
return tryTransformPermalinkToLocalHref(href);
}
}
},
@ -249,8 +232,8 @@ matrixLinkify.options = {
target: function(href, type) {
if (type === 'url') {
if (href.match(matrixLinkify.VECTOR_URL_PATTERN) ||
href.match(matrixLinkify.MATRIXTO_URL_PATTERN)) {
const transformed = tryTransformPermalinkToLocalHref(href);
if (transformed !== href || href.match(matrixLinkify.VECTOR_URL_PATTERN)) {
return null;
} else {
return '_blank';

View file

@ -0,0 +1,83 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* Interface for classes that actually produce permalinks (strings).
* TODO: Convert this to a real TypeScript interface
*/
export default class PermalinkConstructor {
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
throw new Error("Not implemented");
}
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
throw new Error("Not implemented");
}
forGroup(groupId: string): string {
throw new Error("Not implemented");
}
forUser(userId: string): string {
throw new Error("Not implemented");
}
forEntity(entityId: string): string {
throw new Error("Not implemented");
}
isPermalinkHost(host: string): boolean {
throw new Error("Not implemented");
}
parsePermalink(fullUrl: string): PermalinkParts {
throw new Error("Not implemented");
}
}
// Inspired by/Borrowed with permission from the matrix-bot-sdk:
// https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L1-L6
export class PermalinkParts {
roomIdOrAlias: string;
eventId: string;
userId: string;
groupId: string;
viaServers: string[];
constructor(roomIdOrAlias: string, eventId: string, userId: string, groupId: string, viaServers: string[]) {
this.roomIdOrAlias = roomIdOrAlias;
this.eventId = eventId;
this.groupId = groupId;
this.userId = userId;
this.viaServers = viaServers;
}
static forUser(userId: string): PermalinkParts {
return new PermalinkParts(null, null, userId, null, null);
}
static forGroup(groupId: string): PermalinkParts {
return new PermalinkParts(null, null, null, groupId, null);
}
static forRoom(roomIdOrAlias: string, viaServers: string[]): PermalinkParts {
return new PermalinkParts(roomIdOrAlias, null, null, null, viaServers || []);
}
static forEvent(roomId: string, eventId: string, viaServers: string[]): PermalinkParts {
return new PermalinkParts(roomId, eventId, null, null, viaServers || []);
}
}

View file

@ -14,12 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import MatrixClientPeg from "./MatrixClientPeg";
import MatrixClientPeg from "../../MatrixClientPeg";
import isIp from "is-ip";
import utils from 'matrix-js-sdk/lib/utils';
import SpecPermalinkConstructor, {baseUrl as matrixtoBaseUrl} from "./SpecPermalinkConstructor";
import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor";
import RiotPermalinkConstructor from "./RiotPermalinkConstructor";
import * as matrixLinkify from "../../linkify-matrix";
export const host = "matrix.to";
export const baseUrl = `https://${host}`;
const SdkConfig = require("../../SdkConfig");
// The maximum number of servers to pick when working out which servers
// to add to permalinks. The servers are appended as ?via=example.org
@ -73,7 +76,7 @@ export class RoomPermalinkCreator {
// We support being given a roomId as a fallback in the event the `room` object
// doesn't exist or is not healthy for us to rely on. For example, loading a
// permalink to a room which the MatrixClient doesn't know about.
constructor(room, roomId=null) {
constructor(room, roomId = null) {
this._room = room;
this._roomId = room ? room.roomId : roomId;
this._highestPlUserId = null;
@ -124,15 +127,11 @@ export class RoomPermalinkCreator {
}
forEvent(eventId) {
const roomId = this._roomId;
const permalinkBase = `${baseUrl}/#/${roomId}/${eventId}`;
return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
return getPermalinkConstructor().forEvent(this._roomId, eventId, this._serverCandidates);
}
forRoom() {
const roomId = this._roomId;
const permalinkBase = `${baseUrl}/#/${roomId}`;
return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
return getPermalinkConstructor().forRoom(this._roomId, this._serverCandidates);
}
onRoomState(event) {
@ -182,8 +181,8 @@ export class RoomPermalinkCreator {
}
const serverName = getServerName(userId);
return !isHostnameIpAddress(serverName) &&
!isHostInRegex(serverName, this._bannedHostsRegexps) &&
isHostInRegex(serverName, this._allowedHostsRegexps);
!isHostInRegex(serverName, this._bannedHostsRegexps) &&
isHostInRegex(serverName, this._allowedHostsRegexps);
});
const maxEntry = allowedEntries.reduce((max, entry) => {
return (entry[1] > max[1]) ? entry : max;
@ -221,7 +220,7 @@ export class RoomPermalinkCreator {
}
_updatePopulationMap() {
const populationMap: {[server:string]:number} = {};
const populationMap: { [server: string]: number } = {};
for (const member of this._room.getJoinedMembers()) {
const serverName = getServerName(member.userId);
if (!populationMap[serverName]) {
@ -242,9 +241,9 @@ export class RoomPermalinkCreator {
.sort((a, b) => this._populationMap[b] - this._populationMap[a])
.filter(a => {
return !candidates.includes(a) &&
!isHostnameIpAddress(a) &&
!isHostInRegex(a, this._bannedHostsRegexps) &&
isHostInRegex(a, this._allowedHostsRegexps);
!isHostnameIpAddress(a) &&
!isHostInRegex(a, this._bannedHostsRegexps) &&
isHostInRegex(a, this._allowedHostsRegexps);
});
const remainingServers = serversByPopulation.slice(0, MAX_SERVER_CANDIDATES - candidates.length);
@ -254,25 +253,27 @@ export class RoomPermalinkCreator {
}
}
export function makeGenericPermalink(entityId: string): string {
return getPermalinkConstructor().forEntity(entityId);
}
export function makeUserPermalink(userId) {
return `${baseUrl}/#/${userId}`;
return getPermalinkConstructor().forUser(userId);
}
export function makeRoomPermalink(roomId) {
const permalinkBase = `${baseUrl}/#/${roomId}`;
if (!roomId) {
throw new Error("can't permalink a falsey roomId");
}
// If the roomId isn't actually a room ID, don't try to list the servers.
// Aliases are already routable, and don't need extra information.
if (roomId[0] !== '!') return permalinkBase;
if (roomId[0] !== '!') return getPermalinkConstructor().forRoom(roomId, []);
const client = MatrixClientPeg.get();
const room = client.getRoom(roomId);
if (!room) {
return permalinkBase;
return getPermalinkConstructor().forRoom(roomId, []);
}
const permalinkCreator = new RoomPermalinkCreator(room);
permalinkCreator.load();
@ -280,12 +281,96 @@ export function makeRoomPermalink(roomId) {
}
export function makeGroupPermalink(groupId) {
return `${baseUrl}/#/${groupId}`;
return getPermalinkConstructor().forGroup(groupId);
}
export function encodeServerCandidates(candidates) {
if (!candidates || candidates.length === 0) return '';
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
export function isPermalinkHost(host: string): boolean {
// Always check if the permalink is a spec permalink (callers are likely to call
// parsePermalink after this function).
if (new SpecPermalinkConstructor().isPermalinkHost(host)) return true;
return getPermalinkConstructor().isPermalinkHost(host);
}
/**
* Transforms a permalink (or possible permalink) into a local URL if possible. If
* the given permalink is found to not be a permalink, it'll be returned unaltered.
* @param {string} permalink The permalink to try and transform.
* @returns {string} The transformed permalink or original URL if unable.
*/
export function tryTransformPermalinkToLocalHref(permalink: string): string {
if (!permalink.startsWith("http:") && !permalink.startsWith("https:")) {
return permalink;
}
const m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN);
if (m) {
return m[1];
}
// A bit of a hack to convert permalinks of unknown origin to Riot links
try {
const permalinkParts = parsePermalink(permalink);
if (permalinkParts) {
if (permalinkParts.roomIdOrAlias) {
const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : '';
permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`;
} else if (permalinkParts.groupId) {
permalink = `#/group/${permalinkParts.groupId}`;
} else if (permalinkParts.userId) {
permalink = `#/user/${permalinkParts.userId}`;
} // else not a valid permalink for our purposes - do not handle
}
} catch (e) {
// Not an href we need to care about
}
return permalink;
}
export function getPrimaryPermalinkEntity(permalink: string): string {
try {
let permalinkParts = parsePermalink(permalink);
// If not a permalink, try the vector patterns.
if (!permalinkParts) {
const m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN);
if (m) {
// A bit of a hack, but it gets the job done
const handler = new RiotPermalinkConstructor("http://localhost");
const entityInfo = m[1].split('#').slice(1).join('#');
permalinkParts = handler.parsePermalink(`http://localhost/#${entityInfo}`);
}
}
if (!permalinkParts) return null; // not processable
if (permalinkParts.userId) return permalinkParts.userId;
if (permalinkParts.groupId) return permalinkParts.groupId;
if (permalinkParts.roomIdOrAlias) return permalinkParts.roomIdOrAlias;
} catch (e) {
// no entity - not a permalink
}
return null;
}
function getPermalinkConstructor(): PermalinkConstructor {
const riotPrefix = SdkConfig.get()['permalinkPrefix'];
if (riotPrefix && riotPrefix !== matrixtoBaseUrl) {
return new RiotPermalinkConstructor(riotPrefix);
}
return new SpecPermalinkConstructor();
}
export function parsePermalink(fullUrl: string): PermalinkParts {
const riotPrefix = SdkConfig.get()['permalinkPrefix'];
if (fullUrl.startsWith(matrixtoBaseUrl)) {
return new SpecPermalinkConstructor().parsePermalink(fullUrl);
} else if (riotPrefix && fullUrl.startsWith(riotPrefix)) {
return new RiotPermalinkConstructor(riotPrefix).parsePermalink(fullUrl);
}
return null; // not a permalink we can handle
}
function getServerName(userId) {

View file

@ -0,0 +1,111 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor";
/**
* Generates permalinks that self-reference the running webapp
*/
export default class RiotPermalinkConstructor extends PermalinkConstructor {
_riotUrl: string;
constructor(riotUrl: string) {
super();
this._riotUrl = riotUrl;
if (!this._riotUrl.startsWith("http:") && !this._riotUrl.startsWith("https:")) {
throw new Error("Riot prefix URL does not appear to be an HTTP(S) URL");
}
}
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
return `${this._riotUrl}/#/room/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`;
}
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
return `${this._riotUrl}/#/room/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`;
}
forUser(userId: string): string {
return `${this._riotUrl}/#/user/${userId}`;
}
forGroup(groupId: string): string {
return `${this._riotUrl}/#/group/${groupId}`;
}
forEntity(entityId: string): string {
if (entityId[0] === '!' || entityId[0] === '#') {
return this.forRoom(entityId);
} else if (entityId[0] === '@') {
return this.forUser(entityId);
} else if (entityId[0] === '+') {
return this.forGroup(entityId);
} else throw new Error("Unrecognized entity");
}
isPermalinkHost(testHost: string): boolean {
const parsedUrl = new URL(this._riotUrl);
return testHost === (parsedUrl.host || parsedUrl.hostname); // one of the hosts should match
}
encodeServerCandidates(candidates: string[]) {
if (!candidates || candidates.length === 0) return '';
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
}
// Heavily inspired by/borrowed from the matrix-bot-sdk (with permission):
// https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L33-L61
// Adapted for Riot's URL format
parsePermalink(fullUrl: string): PermalinkParts {
if (!fullUrl || !fullUrl.startsWith(this._riotUrl)) {
throw new Error("Does not appear to be a permalink");
}
const parts = fullUrl.substring(`${this._riotUrl}/#/`.length).split("/");
if (parts.length < 2) { // we're expecting an entity and an ID of some kind at least
throw new Error("URL is missing parts");
}
const entityType = parts[0];
const entity = parts[1];
if (entityType === 'user') {
// Probably a user, no further parsing needed.
return PermalinkParts.forUser(entity);
} else if (entityType === 'group') {
// Probably a group, no further parsing needed.
return PermalinkParts.forGroup(entity);
} else if (entityType === 'room') {
if (parts.length === 2) {
return PermalinkParts.forRoom(entity, []);
}
// rejoin the rest because v3 events can have slashes (annoyingly)
const eventIdAndQuery = parts.length > 2 ? parts.slice(2).join('/') : "";
const secondaryParts = eventIdAndQuery.split("?");
const eventId = secondaryParts[0];
const query = secondaryParts.length > 1 ? secondaryParts[1] : "";
// TODO: Verify Riot works with via args
const via = query.split("via=").filter(p => !!p);
return PermalinkParts.forEvent(entity, eventId, via);
} else {
throw new Error("Unknown entity type in permalink");
}
}
}

View file

@ -0,0 +1,94 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor";
export const host = "matrix.to";
export const baseUrl = `https://${host}`;
/**
* Generates matrix.to permalinks
*/
export default class SpecPermalinkConstructor extends PermalinkConstructor {
constructor() {
super();
}
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
return `${baseUrl}/#/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`;
}
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
return `${baseUrl}/#/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`;
}
forUser(userId: string): string {
return `${baseUrl}/#/${userId}`;
}
forGroup(groupId: string): string {
return `${baseUrl}/#/${groupId}`;
}
forEntity(entityId: string): string {
return `${baseUrl}/#/${entityId}`;
}
isPermalinkHost(testHost: string): boolean {
return testHost === host;
}
encodeServerCandidates(candidates: string[]) {
if (!candidates || candidates.length === 0) return '';
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
}
// Heavily inspired by/borrowed from the matrix-bot-sdk (with permission):
// https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L33-L61
parsePermalink(fullUrl: string): PermalinkParts {
if (!fullUrl || !fullUrl.startsWith(baseUrl)) {
throw new Error("Does not appear to be a permalink");
}
const parts = fullUrl.substring(`${baseUrl}/#/`.length).split("/");
const entity = parts[0];
if (entity[0] === '@') {
// Probably a user, no further parsing needed.
return PermalinkParts.forUser(entity);
} else if (entity[0] === '+') {
// Probably a group, no further parsing needed.
return PermalinkParts.forGroup(entity);
} else if (entity[0] === '#' || entity[0] === '!') {
if (parts.length === 1) {
return PermalinkParts.forRoom(entity, []);
}
// rejoin the rest because v3 events can have slashes (annoyingly)
const eventIdAndQuery = parts.length > 1 ? parts.slice(1).join('/') : "";
const secondaryParts = eventIdAndQuery.split("?");
const eventId = secondaryParts[0];
const query = secondaryParts.length > 1 ? secondaryParts[1] : "";
const via = query.split("via=").filter(p => !!p);
return PermalinkParts.forEvent(entity, eventId, via);
} else {
throw new Error("Unknown entity type in permalink");
}
}
}

View file

@ -1,5 +1,7 @@
/*
Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -12,14 +14,14 @@ limitations under the License.
*/
import expect from 'expect';
import peg from '../src/MatrixClientPeg';
import peg from '../../../src/MatrixClientPeg';
import {
makeGroupPermalink,
makeRoomPermalink,
makeUserPermalink,
RoomPermalinkCreator,
} from "../src/matrix-to";
import * as testUtils from "./test-utils";
} from "../../../src/utils/permalinks/Permalinks";
import * as testUtils from "../../test-utils";
function mockRoom(roomId, members, serverACL) {
members.forEach(m => m.membership = "join");
@ -62,7 +64,7 @@ function mockRoom(roomId, members, serverACL) {
};
}
describe('matrix-to', function() {
describe('Permalinks', function() {
let sandbox;
beforeEach(function() {

View file

@ -5188,8 +5188,8 @@ mathml-tag-names@^2.0.1:
integrity sha512-pWB896KPGSGkp1XtyzRBftpTzwSOL0Gfk0wLvxt4f2mgzjY19o0LxJ3U25vNWTzsh7da+KTbuXQoQ3lOJZ8WHw==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
version "2.4.0"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/562bf9331bd5ee8701625cd9632c8e73374b18f4"
version "2.4.1"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e024d047e358cf26caa47542c8f6d9a469a11cb2"
dependencies:
another-json "^0.2.0"
babel-runtime "^6.26.0"