Merge remote-tracking branch 'upstream/develop' into compact-reply-rendering

This commit is contained in:
Tulir Asokan 2021-04-19 13:37:27 +03:00
commit b8ef67e447
170 changed files with 5269 additions and 1639 deletions

View file

@ -39,7 +39,7 @@ import {ModalWidgetStore} from "../stores/ModalWidgetStore";
import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
import VoipUserMapper from "../VoipUserMapper";
import {SpaceStoreClass} from "../stores/SpaceStore";
import {VoiceRecorder} from "../voice/VoiceRecorder";
import {VoiceRecording} from "../voice/VoiceRecording";
declare global {
interface Window {
@ -71,7 +71,7 @@ declare global {
mxModalWidgetStore: ModalWidgetStore;
mxVoipUserMapper: VoipUserMapper;
mxSpaceStore: SpaceStoreClass;
mxVoiceRecorder: typeof VoiceRecorder;
mxVoiceRecorder: typeof VoiceRecording;
}
interface Document {

View file

@ -212,6 +212,18 @@ export default abstract class BasePlatform {
throw new Error("Unimplemented");
}
supportsWarnBeforeExit(): boolean {
return false;
}
async shouldWarnBeforeExit(): Promise<boolean> {
return false;
}
async setWarnBeforeExit(enabled: boolean): Promise<void> {
throw new Error("Unimplemented");
}
supportsAutoHideMenuBar(): boolean {
return false;
}

View file

@ -154,6 +154,9 @@ function getRemoteAudioElement(): HTMLAudioElement {
export default class CallHandler {
private calls = new Map<string, MatrixCall>(); // roomId -> call
// Calls started as an attended transfer, ie. with the intention of transferring another
// call with a different party to this one.
private transferees = new Map<string, MatrixCall>(); // callId (target) -> call (transferee)
private audioPromises = new Map<AudioID, Promise<void>>();
private dispatcherRef: string = null;
private supportsPstnProtocol = null;
@ -325,6 +328,10 @@ export default class CallHandler {
return callsNotInThatRoom;
}
getTransfereeForCallId(callId: string): MatrixCall {
return this.transferees[callId];
}
play(audioId: AudioID) {
// TODO: Attach an invisible element for this instead
// which listens?
@ -622,6 +629,7 @@ export default class CallHandler {
private async placeCall(
roomId: string, type: PlaceCallType,
localElement: HTMLVideoElement, remoteElement: HTMLVideoElement,
transferee: MatrixCall,
) {
Analytics.trackEvent('voip', 'placeCall', 'type', type);
CountlyAnalytics.instance.trackStartCall(roomId, type === PlaceCallType.Video, false);
@ -634,6 +642,9 @@ export default class CallHandler {
const call = createNewMatrixCall(MatrixClientPeg.get(), mappedRoomId);
this.calls.set(roomId, call);
if (transferee) {
this.transferees[call.callId] = transferee;
}
this.setCallListeners(call);
this.setCallAudioElement(call);
@ -723,7 +734,10 @@ export default class CallHandler {
} else if (members.length === 2) {
console.info(`Place ${payload.type} call in ${payload.room_id}`);
this.placeCall(payload.room_id, payload.type, payload.local_element, payload.remote_element);
this.placeCall(
payload.room_id, payload.type, payload.local_element, payload.remote_element,
payload.transferee,
);
} else { // > 2
dis.dispatch({
action: "place_conference_call",

View file

@ -97,7 +97,7 @@ export function formatFullDateNoTime(date: Date): string {
});
}
export function formatFullDate(date: Date, showTwelveHour = false): string {
export function formatFullDate(date: Date, showTwelveHour = false, showSeconds = true): string {
const days = getDaysArray();
const months = getMonthsArray();
return _t('%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s', {
@ -105,7 +105,7 @@ export function formatFullDate(date: Date, showTwelveHour = false): string {
monthName: months[date.getMonth()],
day: date.getDate(),
fullYear: date.getFullYear(),
time: formatFullTime(date, showTwelveHour),
time: showSeconds ? formatFullTime(date, showTwelveHour) : formatTime(date, showTwelveHour),
});
}

View file

@ -161,27 +161,27 @@ const messageComposerBindings = (): KeyBinding<MessageComposerAction>[] => {
const autocompleteBindings = (): KeyBinding<AutocompleteAction>[] => {
return [
{
action: AutocompleteAction.ApplySelection,
action: AutocompleteAction.CompleteOrNextSelection,
keyCombo: {
key: Key.TAB,
},
},
{
action: AutocompleteAction.ApplySelection,
action: AutocompleteAction.CompleteOrNextSelection,
keyCombo: {
key: Key.TAB,
ctrlKey: true,
},
},
{
action: AutocompleteAction.ApplySelection,
action: AutocompleteAction.CompleteOrPrevSelection,
keyCombo: {
key: Key.TAB,
shiftKey: true,
},
},
{
action: AutocompleteAction.ApplySelection,
action: AutocompleteAction.CompleteOrPrevSelection,
keyCombo: {
key: Key.TAB,
ctrlKey: true,
@ -360,11 +360,11 @@ const navigationBindings = (): KeyBinding<NavigationAction>[] => {
action: NavigationAction.GoToHome,
keyCombo: {
key: Key.H,
ctrlOrCmd: true,
altKey: true,
ctrlKey: true,
altKey: !isMac,
shiftKey: isMac,
},
},
{
action: NavigationAction.SelectPrevRoom,
keyCombo: {

View file

@ -52,14 +52,19 @@ export enum MessageComposerAction {
/** Actions for text editing autocompletion */
export enum AutocompleteAction {
/** Apply the current autocomplete selection */
ApplySelection = 'ApplySelection',
/** Cancel autocompletion */
Cancel = 'Cancel',
/**
* Select previous selection or, if the autocompletion window is not shown, open the window and select the first
* selection.
*/
CompleteOrPrevSelection = 'ApplySelection',
/** Select next selection or, if the autocompletion window is not shown, open it and select the first selection */
CompleteOrNextSelection = 'CompleteOrNextSelection',
/** Move to the previous autocomplete selection */
PrevSelection = 'PrevSelection',
/** Move to the next autocomplete selection */
NextSelection = 'NextSelection',
/** Close the autocompletion window */
Cancel = 'Cancel',
}
/** Actions for the room list sidebar */

View file

@ -36,6 +36,7 @@ export interface IModal<T extends any[]> {
onBeforeClose?(reason?: string): Promise<boolean>;
onFinished(...args: T): void;
close(...args: T): void;
hidden?: boolean;
}
export interface IHandle<T extends any[]> {
@ -93,6 +94,12 @@ export class ModalManager {
return container;
}
public toggleCurrentDialogVisibility() {
const modal = this.getCurrentModal();
if (!modal) return;
modal.hidden = !modal.hidden;
}
public hasDialogs() {
return this.priorityModal || this.staticModal || this.modals.length > 0;
}
@ -364,7 +371,7 @@ export class ModalManager {
}
const modal = this.getCurrentModal();
if (modal !== this.staticModal) {
if (modal !== this.staticModal && !modal.hidden) {
const classes = classNames("mx_Dialog_wrapper", modal.className, {
mx_Dialog_wrapperWithStaticUnder: this.staticModal,
});

View file

@ -1,16 +1,15 @@
import React from "react";
import ReactDom from "react-dom";
import Velocity from "velocity-animate";
import PropTypes from 'prop-types';
/**
* The Velociraptor contains components and animates transitions with velocity.
* The NodeAnimator contains components and animates transitions.
* It will only pick up direct changes to properties ('left', currently), and so
* will not work for animating positional changes where the position is implicit
* from DOM order. This makes it a lot simpler and lighter: if you need fully
* automatic positional animation, look at react-shuffle or similar libraries.
*/
export default class Velociraptor extends React.Component {
export default class NodeAnimator extends React.Component {
static propTypes = {
// either a list of child nodes, or a single child.
children: PropTypes.any,
@ -20,14 +19,10 @@ export default class Velociraptor extends React.Component {
// a list of state objects to apply to each child node in turn
startStyles: PropTypes.array,
// a list of transition options from the corresponding startStyle
enterTransitionOpts: PropTypes.array,
};
static defaultProps = {
startStyles: [],
enterTransitionOpts: [],
};
constructor(props) {
@ -41,6 +36,18 @@ export default class Velociraptor extends React.Component {
this._updateChildren(this.props.children);
}
/**
*
* @param {HTMLElement} node element to apply styles to
* @param {object} styles a key/value pair of CSS properties
* @returns {void}
*/
_applyStyles(node, styles) {
Object.entries(styles).forEach(([property, value]) => {
node.style[property] = value;
});
}
_updateChildren(newChildren) {
const oldChildren = this.children || {};
this.children = {};
@ -50,17 +57,8 @@ export default class Velociraptor extends React.Component {
const oldNode = ReactDom.findDOMNode(this.nodes[old.key]);
if (oldNode && oldNode.style.left !== c.props.style.left) {
Velocity(oldNode, { left: c.props.style.left }, this.props.transition).then(() => {
// special case visibility because it's nonsensical to animate an invisible element
// so we always hidden->visible pre-transition and visible->hidden after
if (oldNode.style.visibility === 'visible' && c.props.style.visibility === 'hidden') {
oldNode.style.visibility = c.props.style.visibility;
}
});
//console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
}
if (oldNode && oldNode.style.visibility === 'hidden' && c.props.style.visibility === 'visible') {
oldNode.style.visibility = c.props.style.visibility;
this._applyStyles(oldNode, { left: c.props.style.left });
// console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
}
// clone the old element with the props (and children) of the new element
// so prop updates are still received by the children.
@ -94,33 +92,22 @@ export default class Velociraptor extends React.Component {
this.props.startStyles.length > 0
) {
const startStyles = this.props.startStyles;
const transitionOpts = this.props.enterTransitionOpts;
const domNode = ReactDom.findDOMNode(node);
// start from startStyle 1: 0 is the one we gave it
// to start with, so now we animate 1 etc.
for (var i = 1; i < startStyles.length; ++i) {
Velocity(domNode, startStyles[i], transitionOpts[i-1]);
/*
console.log("start:",
JSON.stringify(transitionOpts[i-1]),
"->",
JSON.stringify(startStyles[i]),
);
*/
for (let i = 1; i < startStyles.length; ++i) {
this._applyStyles(domNode, startStyles[i]);
// console.log("start:"
// JSON.stringify(startStyles[i]),
// );
}
// and then we animate to the resting state
Velocity(domNode, restingStyle,
transitionOpts[i-1])
.then(() => {
// once we've reached the resting state, hide the element if
// appropriate
domNode.style.visibility = restingStyle.visibility;
});
setTimeout(() => {
this._applyStyles(domNode, restingStyle);
}, 0);
// console.log("enter:",
// JSON.stringify(transitionOpts[i-1]),
// "->",
// JSON.stringify(restingStyle));
}
this.nodes[k] = node;
@ -128,9 +115,7 @@ export default class Velociraptor extends React.Component {
render() {
return (
<span>
{ Object.values(this.children) }
</span>
<>{ Object.values(this.children) }</>
);
}
}

View file

@ -383,6 +383,10 @@ export const Notifier = {
// don't bother notifying as user was recently active in this room
return;
}
if (SettingsStore.getValue("doNotDisturb")) {
// Don't bother the user if they didn't ask to be bothered
return;
}
if (this.isEnabled()) {
this._displayPopupNotification(ev, room);

View file

@ -395,6 +395,8 @@ export async function accessSecretStorage(func = async () => { }, forceReset = f
} catch (e) {
SecurityCustomisations.catchAccessSecretStorageError?.(e);
console.error(e);
// Re-throw so that higher level logic can abort as needed
throw e;
} finally {
// Clear secret storage key cache now that work is complete
secretStorageBeingAccessed = false;

View file

@ -20,7 +20,7 @@ limitations under the License.
import * as React from 'react';
import { ContentHelpers } from 'matrix-js-sdk';
import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers';
import {MatrixClientPeg} from './MatrixClientPeg';
import dis from './dispatcher/dispatcher';
import * as sdk from './index';
@ -155,6 +155,18 @@ function success(promise?: Promise<any>) {
*/
export const Commands = [
new Command({
command: 'spoiler',
args: '<message>',
description: _td('Sends the given message as a spoiler'),
runFn: function(roomId, message) {
return success(ContentHelpers.makeHtmlMessage(
message,
`<span data-mx-spoiler>${message}</span>`,
));
},
category: CommandCategories.messages,
}),
new Command({
command: 'shrug',
args: '<message>',

View file

@ -95,9 +95,10 @@ function textForMemberEvent(ev) {
senderName,
targetName,
}) + ' ' + reason;
} else {
// sender is not target and made the target leave, if not from invite/ban then this is a kick
} else if (prevContent.membership === "join") {
return _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + reason;
} else {
return "";
}
}
}

View file

@ -1,17 +0,0 @@
import Velocity from "velocity-animate";
// courtesy of https://github.com/julianshapiro/velocity/issues/283
// We only use easeOutBounce (easeInBounce is just sort of nonsensical)
function bounce( p ) {
let pow2;
let bounce = 4;
while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {
// just sets pow2
}
return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
}
Velocity.Easings.easeOutBounce = function(p) {
return 1 - bounce(1 - p);
};

View file

@ -265,7 +265,7 @@ const shortcuts: Record<Categories, IShortcut[]> = {
description: _td("Toggle this dialog"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL, Modifiers.ALT],
modifiers: [Modifiers.CONTROL, isMac ? Modifiers.SHIFT : Modifiers.ALT],
key: Key.H,
}],
description: _td("Go to Home View"),

View file

@ -23,7 +23,6 @@ interface IOptions<T extends {}> {
keys: Array<string | keyof T>;
funcs?: Array<(T) => string>;
shouldMatchWordsOnly?: boolean;
shouldMatchPrefix?: boolean;
// whether to apply unhomoglyph and strip diacritics to fuzz up the search. Defaults to true
fuzzy?: boolean;
}
@ -56,12 +55,6 @@ export default class QueryMatcher<T extends Object> {
if (this._options.shouldMatchWordsOnly === undefined) {
this._options.shouldMatchWordsOnly = true;
}
// By default, match anywhere in the string being searched. If enabled, only return
// matches that are prefixed with the query.
if (this._options.shouldMatchPrefix === undefined) {
this._options.shouldMatchPrefix = false;
}
}
setObjects(objects: T[]) {
@ -112,7 +105,7 @@ export default class QueryMatcher<T extends Object> {
resultKey = resultKey.replace(/[^\w]/g, '');
}
const index = resultKey.indexOf(query);
if (index !== -1 && (!this._options.shouldMatchPrefix || index === 0)) {
if (index !== -1) {
matches.push(
...candidates.map((candidate) => ({index, ...candidate})),
);

View file

@ -56,7 +56,6 @@ export default class UserProvider extends AutocompleteProvider {
this.matcher = new QueryMatcher([], {
keys: ['name'],
funcs: [obj => obj.userId.slice(1)], // index by user id minus the leading '@'
shouldMatchPrefix: true,
shouldMatchWordsOnly: false,
});

View file

@ -981,7 +981,7 @@ export default class GroupView extends React.Component {
<Spinner />
</div>;
}
const httpInviterAvatar = this.state.inviterProfile
const httpInviterAvatar = this.state.inviterProfile && this.state.inviterProfile.avatarUrl
? mediaFromMxc(this.state.inviterProfile.avatarUrl).getSquareThumbnailHttp(36)
: null;

View file

@ -34,7 +34,6 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore";
import ResizeNotifier from "../../utils/ResizeNotifier";
import SettingsStore from "../../settings/SettingsStore";
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore";
import {Key} from "../../Keyboard";
import IndicatorScrollbar from "../structures/IndicatorScrollbar";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { OwnProfileStore } from "../../stores/OwnProfileStore";
@ -43,6 +42,7 @@ import LeftPanelWidget from "./LeftPanelWidget";
import {replaceableComponent} from "../../utils/replaceableComponent";
import {mediaFromMxc} from "../../customisations/Media";
import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
interface IProps {
isMinimized: boolean;
@ -297,17 +297,18 @@ export default class LeftPanel extends React.Component<IProps, IState> {
private onKeyDown = (ev: React.KeyboardEvent) => {
if (!this.focusedElement) return;
switch (ev.key) {
case Key.ARROW_UP:
case Key.ARROW_DOWN:
const action = getKeyBindingsManager().getRoomListAction(ev);
switch (action) {
case RoomListAction.NextRoom:
case RoomListAction.PrevRoom:
ev.stopPropagation();
ev.preventDefault();
this.onMoveFocus(ev.key === Key.ARROW_UP);
this.onMoveFocus(action === RoomListAction.PrevRoom);
break;
}
};
private onEnter = () => {
private selectRoom = () => {
const firstRoom = this.listContainerRef.current.querySelector<HTMLDivElement>(".mx_RoomTile");
if (firstRoom) {
firstRoom.click();
@ -388,8 +389,8 @@ export default class LeftPanel extends React.Component<IProps, IState> {
>
<RoomSearch
isMinimized={this.props.isMinimized}
onVerticalArrow={this.onKeyDown}
onEnter={this.onEnter}
onKeyDown={this.onKeyDown}
onSelectRoom={this.selectRoom}
/>
<AccessibleTooltipButton
className={classNames("mx_LeftPanel_exploreButton", {

View file

@ -444,6 +444,7 @@ class LoggedInView extends React.Component<IProps, IState> {
case RoomAction.RoomScrollDown:
case RoomAction.JumpToFirstMessage:
case RoomAction.JumpToLatestMessage:
// pass the event down to the scroll panel
this._onScrollKeyPressed(ev);
handled = true;
break;

View file

@ -84,6 +84,7 @@ import {replaceableComponent} from "../../utils/replaceableComponent";
import RoomListStore from "../../stores/room-list/RoomListStore";
import {RoomUpdateCause} from "../../stores/room-list/models";
import defaultDispatcher from "../../dispatcher/dispatcher";
import SecurityCustomisations from "../../customisations/Security";
/** constants for MatrixChat.state.view */
export enum Views {
@ -395,7 +396,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
if (crossSigningIsSetUp) {
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) {
this.onLoggedIn();
} else {
this.setStateForNewView({view: Views.COMPLETE_SECURITY});
}
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
this.setStateForNewView({ view: Views.E2E_SETUP });
} else {

View file

@ -659,6 +659,7 @@ export default class MessagePanel extends React.Component {
showReactions={this.props.showReactions}
layout={this.props.layout}
enableFlair={this.props.enableFlair}
showReadReceipts={this.props.showReadReceipts}
/>
</TileErrorBoundary>
</li>,

View file

@ -30,8 +30,11 @@ import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
interface IProps {
isMinimized: boolean;
onVerticalArrow(ev: React.KeyboardEvent): void;
onEnter(ev: React.KeyboardEvent): boolean;
onKeyDown(ev: React.KeyboardEvent): void;
/**
* @returns true if a room has been selected and the search field should be cleared
*/
onSelectRoom(): boolean;
}
interface IState {
@ -120,10 +123,11 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
break;
case RoomListAction.NextRoom:
case RoomListAction.PrevRoom:
this.props.onVerticalArrow(ev);
// we don't handle these actions here put pass the event on to the interested party (LeftPanel)
this.props.onKeyDown(ev);
break;
case RoomListAction.SelectRoom: {
const shouldClear = this.props.onEnter(ev);
const shouldClear = this.props.onSelectRoom();
if (shouldClear) {
// wrap in set immediate to delay it so that we don't clear the filter & then change room
setImmediate(() => {

View file

@ -1137,10 +1137,16 @@ export default class RoomView extends React.Component<IProps, IState> {
ev.stopPropagation();
ev.preventDefault();
this.setState({
dragCounter: this.state.dragCounter + 1,
draggingFile: true,
});
// We always increment the counter no matter the types, because dragging is
// still happening. If we didn't, the drag counter would get out of sync.
this.setState({dragCounter: this.state.dragCounter + 1});
// See:
// https://docs.w3cub.com/dom/datatransfer/types
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) {
this.setState({draggingFile: true});
}
};
private onDragLeave = ev => {
@ -1164,6 +1170,9 @@ export default class RoomView extends React.Component<IProps, IState> {
ev.dataTransfer.dropEffect = 'none';
// See:
// https://docs.w3cub.com/dom/datatransfer/types
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
if (ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")) {
ev.dataTransfer.dropEffect = 'copy';
}

View file

@ -16,10 +16,10 @@ limitations under the License.
import React, {createRef} from "react";
import PropTypes from 'prop-types';
import { Key } from '../../Keyboard';
import Timer from '../../utils/Timer';
import AutoHideScrollbar from "./AutoHideScrollbar";
import {replaceableComponent} from "../../utils/replaceableComponent";
import {getKeyBindingsManager, RoomAction} from "../../KeyBindingsManager";
const DEBUG_SCROLL = false;
@ -535,29 +535,19 @@ export default class ScrollPanel extends React.Component {
* @param {object} ev the keyboard event
*/
handleScrollKey = ev => {
switch (ev.key) {
case Key.PAGE_UP:
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
this.scrollRelative(-1);
}
const roomAction = getKeyBindingsManager().getRoomAction(ev);
switch (roomAction) {
case RoomAction.ScrollUp:
this.scrollRelative(-1);
break;
case Key.PAGE_DOWN:
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
this.scrollRelative(1);
}
case RoomAction.RoomScrollDown:
this.scrollRelative(1);
break;
case Key.HOME:
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
this.scrollToTop();
}
case RoomAction.JumpToFirstMessage:
this.scrollToTop();
break;
case Key.END:
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
this.scrollToBottom();
}
case RoomAction.JumpToLatestMessage:
this.scrollToBottom();
break;
}
};

View file

@ -74,6 +74,7 @@ interface IState {
export default class UserMenu extends React.Component<IProps, IState> {
private dispatcherRef: string;
private themeWatcherRef: string;
private dndWatcherRef: string;
private buttonRef: React.RefObject<HTMLButtonElement> = createRef();
private tagStoreRef: fbEmitter.EventSubscription;
@ -89,6 +90,9 @@ export default class UserMenu extends React.Component<IProps, IState> {
if (SettingsStore.getValue("feature_spaces")) {
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
}
// Force update is the easiest way to trigger the UI update (we don't store state for this)
this.dndWatcherRef = SettingsStore.watchSetting("doNotDisturb", null, () => this.forceUpdate());
}
private get hasHomePage(): boolean {
@ -103,6 +107,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
public componentWillUnmount() {
if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef);
if (this.dndWatcherRef) SettingsStore.unwatchSetting(this.dndWatcherRef);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate);
this.tagStoreRef.remove();
@ -288,6 +293,12 @@ export default class UserMenu extends React.Component<IProps, IState> {
this.setState({contextMenuPosition: null}); // also close the menu
};
private onDndToggle = (ev) => {
ev.stopPropagation();
const current = SettingsStore.getValue("doNotDisturb");
SettingsStore.setValue("doNotDisturb", null, SettingLevel.DEVICE, !current);
};
private renderContextMenu = (): React.ReactNode => {
if (!this.state.contextMenuPosition) return null;
@ -534,6 +545,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
{/* masked image in CSS */}
</span>
);
let dnd;
if (this.state.selectedSpace) {
name = (
<div className="mx_UserMenu_doubleName">
@ -560,6 +572,16 @@ export default class UserMenu extends React.Component<IProps, IState> {
</div>
);
isPrototype = true;
} else if (SettingsStore.getValue("feature_dnd")) {
const isDnd = SettingsStore.getValue("doNotDisturb");
dnd = <AccessibleButton
onClick={this.onDndToggle}
className={classNames({
"mx_UserMenu_dnd": true,
"mx_UserMenu_dnd_noisy": !isDnd,
"mx_UserMenu_dnd_muted": isDnd,
})}
/>;
}
if (this.props.isMinimized) {
name = null;
@ -595,6 +617,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
/>
</span>
{name}
{dnd}
{buttons}
</div>
</ContextMenuButton>

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import * as sdk from '../../../index';
import Modal from "../../../Modal";
import PasswordReset from "../../../PasswordReset";
@ -27,7 +27,9 @@ import classNames from 'classnames';
import AuthPage from "../../views/auth/AuthPage";
import CountlyAnalytics from "../../../CountlyAnalytics";
import ServerPicker from "../../views/elements/ServerPicker";
import PassphraseField from '../../views/auth/PassphraseField';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
// Phases
// Show the forgot password inputs
@ -137,10 +139,14 @@ export default class ForgotPassword extends React.Component {
// refresh the server errors, just in case the server came back online
await this._checkServerLiveliness(this.props.serverConfig);
await this['password_field'].validate({ allowEmpty: false });
if (!this.state.email) {
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
} else if (!this.state.password || !this.state.password2) {
this.showErrorDialog(_t('A new password must be entered.'));
} else if (!this.state.passwordFieldValid) {
this.showErrorDialog(_t('Please choose a strong password'));
} else if (this.state.password !== this.state.password2) {
this.showErrorDialog(_t('New passwords must match each other.'));
} else {
@ -186,6 +192,12 @@ export default class ForgotPassword extends React.Component {
});
}
onPasswordValidate(result) {
this.setState({
passwordFieldValid: result.valid,
});
}
renderForgot() {
const Field = sdk.getComponent('elements.Field');
@ -230,12 +242,15 @@ export default class ForgotPassword extends React.Component {
/>
</div>
<div className="mx_AuthBody_fieldRow">
<Field
<PassphraseField
name="reset_password"
type="password"
label={_t('New Password')}
label={_td('New Password')}
value={this.state.password}
minScore={PASSWORD_MIN_SCORE}
onChange={this.onInputChanged.bind(this, "password")}
fieldRef={field => this['password_field'] = field}
onValidate={(result) => this.onPasswordValidate(result)}
onFocus={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword_focus")}
onBlur={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword_blur")}
autoComplete="new-password"

View file

@ -436,6 +436,8 @@ export default class Registration extends React.Component<IProps, IState> {
// ok fine, there's still no session: really go to the login page
this.props.onLoginClick();
}
return sessionLoaded;
};
private renderRegisterComponent() {
@ -557,7 +559,12 @@ export default class Registration extends React.Component<IProps, IState> {
loggedInUserId: this.state.differentLoggedInUserId,
},
)}</p>
<p><AccessibleButton element="span" className="mx_linkButton" onClick={this.onLoginClickWithCheck}>
<p><AccessibleButton element="span" className="mx_linkButton" onClick={async event => {
const sessionLoaded = await this.onLoginClickWithCheck(event);
if (sessionLoaded) {
dis.dispatch({action: "view_welcome_page"});
}
}}>
{_t("Continue with previous account")}
</AccessibleButton></p>
</div>;

View file

@ -40,7 +40,7 @@ enum RegistrationField {
PasswordConfirm = "field_password_confirm",
}
const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from offline slow-hash scenario.
export const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from offline slow-hash scenario.
interface IProps {
// Values pre-filled in the input boxes when the component loads

View file

@ -26,6 +26,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {useEventEmitter} from "../../../hooks/useEventEmitter";
import {toPx} from "../../../utils/units";
import {ResizeMethod} from "../../../Avatar";
import { _t } from '../../../languageHandler';
interface IProps {
name: string; // The name (first initial used as default)
@ -140,6 +141,7 @@ const BaseAvatar = (props: IProps) => {
if (onClick) {
return (
<AccessibleButton
aria-label={_t("Avatar")}
{...otherProps}
element="span"
className={classNames("mx_BaseAvatar", className)}

View file

@ -129,7 +129,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
name: this.props.room.name,
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
};
public render() {

View file

@ -52,6 +52,9 @@ export default class MessageContextMenu extends React.Component {
/* callback called when the menu is dismissed */
onFinished: PropTypes.func,
/* if the menu is inside a dialog, we sometimes need to close that dialog after click (forwarding) */
onCloseDialog: PropTypes.func,
};
state = {
@ -141,6 +144,7 @@ export default class MessageContextMenu extends React.Component {
const cli = MatrixClientPeg.get();
try {
if (this.props.onCloseDialog) this.props.onCloseDialog();
await cli.redactEvent(
this.props.mxEvent.getRoomId(),
this.props.mxEvent.getId(),
@ -190,6 +194,7 @@ export default class MessageContextMenu extends React.Component {
};
onForwardClick = () => {
if (this.props.onCloseDialog) this.props.onCloseDialog();
dis.dispatch({
action: 'forward_event',
event: this.props.mxEvent,

View file

@ -29,7 +29,10 @@ import dis from "../../../dispatcher/dispatcher";
import IdentityAuthClient from "../../../IdentityAuthClient";
import Modal from "../../../Modal";
import {humanizeTime} from "../../../utils/humanize";
import createRoom, {canEncryptToAllUsers, findDMForUser, privateShouldBeEncrypted} from "../../../createRoom";
import createRoom, {
canEncryptToAllUsers, ensureDMExists, findDMForUser, privateShouldBeEncrypted,
IInvite3PID,
} from "../../../createRoom";
import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite";
import {Key} from "../../../Keyboard";
import {Action} from "../../../dispatcher/actions";
@ -332,6 +335,7 @@ interface IInviteDialogState {
threepidResultsMixin: { user: Member, userId: string}[];
canUseIdentityServer: boolean;
tryingIdentityServer: boolean;
consultFirst: boolean;
// These two flags are used for the 'Go' button to communicate what is going on.
busy: boolean,
@ -380,6 +384,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
threepidResultsMixin: [],
canUseIdentityServer: !!MatrixClientPeg.get().getIdentityServerUrl(),
tryingIdentityServer: false,
consultFirst: false,
// These two flags are used for the 'Go' button to communicate what is going on.
busy: false,
@ -395,6 +400,10 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
}
}
private onConsultFirstChange = (ev) => {
this.setState({consultFirst: ev.target.checked});
}
static buildRecents(excludedTargetIds: Set<string>): {userId: string, user: RoomMember, lastActive: number}[] {
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room
@ -610,13 +619,14 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
_startDm = async () => {
this.setState({busy: true});
const client = MatrixClientPeg.get();
const targets = this._convertFilter();
const targetIds = targets.map(t => t.userId);
// Check if there is already a DM with these people and reuse it if possible.
let existingRoom: Room;
if (targetIds.length === 1) {
existingRoom = findDMForUser(MatrixClientPeg.get(), targetIds[0]);
existingRoom = findDMForUser(client, targetIds[0]);
} else {
existingRoom = DMRoomMap.shared().getDMRoomForIdentifiers(targetIds);
}
@ -638,7 +648,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
// If so, enable encryption in the new room.
const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
if (!has3PidMembers) {
const client = MatrixClientPeg.get();
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
if (allHaveDeviceKeys) {
createRoomOptions.encryption = true;
@ -648,35 +657,41 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
// Check if it's a traditional DM and create the room if required.
// TODO: [Canonical DMs] Remove this check and instead just create the multi-person DM
let createRoomPromise = Promise.resolve(null) as Promise<string | null | boolean>;
const isSelf = targetIds.length === 1 && targetIds[0] === MatrixClientPeg.get().getUserId();
if (targetIds.length === 1 && !isSelf) {
createRoomOptions.dmUserId = targetIds[0];
createRoomPromise = createRoom(createRoomOptions);
} else if (isSelf) {
createRoomPromise = createRoom(createRoomOptions);
} else {
// Create a boring room and try to invite the targets manually.
createRoomPromise = createRoom(createRoomOptions).then(roomId => {
return inviteMultipleToRoom(roomId, targetIds);
}).then(result => {
if (this._shouldAbortAfterInviteError(result)) {
return true; // abort
}
});
}
try {
const isSelf = targetIds.length === 1 && targetIds[0] === client.getUserId();
if (targetIds.length === 1 && !isSelf) {
createRoomOptions.dmUserId = targetIds[0];
}
// the createRoom call will show the room for us, so we don't need to worry about that.
createRoomPromise.then(abort => {
if (abort === true) return; // only abort on true booleans, not roomIds or something
if (targetIds.length > 1) {
createRoomOptions.createOpts = targetIds.reduce(
(roomOptions, address) => {
const type = getAddressType(address);
if (type === 'email') {
const invite: IInvite3PID = {
id_server: client.getIdentityServerUrl(true),
medium: 'email',
address,
};
roomOptions.invite_3pid.push(invite);
} else if (type === 'mx-user-id') {
roomOptions.invite.push(address);
}
return roomOptions;
},
{ invite: [], invite_3pid: [] },
)
}
await createRoom(createRoomOptions);
this.props.onFinished();
}).catch(err => {
} catch (err) {
console.error(err);
this.setState({
busy: false,
errorText: _t("We couldn't create your DM."),
});
});
}
};
_inviteUsers = async () => {
@ -704,8 +719,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
this.props.onFinished();
}
if (cli.isRoomEncrypted(this.props.roomId) &&
SettingsStore.getValue("feature_room_history_key_sharing")) {
if (cli.isRoomEncrypted(this.props.roomId)) {
const visibilityEvent = room.currentState.getStateEvents(
"m.room.history_visibility", "",
);
@ -745,16 +759,34 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
});
}
this.setState({busy: true});
try {
await this.props.call.transfer(targetIds[0]);
this.setState({busy: false});
this.props.onFinished();
} catch (e) {
this.setState({
busy: false,
errorText: _t("Failed to transfer call"),
if (this.state.consultFirst) {
const dmRoomId = await ensureDMExists(MatrixClientPeg.get(), targetIds[0]);
dis.dispatch({
action: 'place_call',
type: this.props.call.type,
room_id: dmRoomId,
transferee: this.props.call,
});
dis.dispatch({
action: 'view_room',
room_id: dmRoomId,
should_peek: false,
joining: false,
});
this.props.onFinished();
} else {
this.setState({busy: true});
try {
await this.props.call.transfer(targetIds[0]);
this.setState({busy: false});
this.props.onFinished();
} catch (e) {
this.setState({
busy: false,
errorText: _t("Failed to transfer call"),
});
}
}
};
@ -1215,6 +1247,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
let helpText;
let buttonText;
let goButtonFn;
let consultSection;
let keySharingWarning = <span />;
const identityServersEnabled = SettingsStore.getValue(UIFeature.IdentityServer);
@ -1317,8 +1350,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
buttonText = _t("Invite");
goButtonFn = this._inviteUsers;
if (SettingsStore.getValue("feature_room_history_key_sharing") &&
cli.isRoomEncrypted(this.props.roomId)) {
if (cli.isRoomEncrypted(this.props.roomId)) {
const room = cli.getRoom(this.props.roomId);
const visibilityEvent = room.currentState.getStateEvents(
"m.room.history_visibility", "",
@ -1339,6 +1371,12 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
title = _t("Transfer");
buttonText = _t("Transfer");
goButtonFn = this._transferCall;
consultSection = <div>
<label>
<input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} />
{_t("Consult first")}
</label>
</div>;
} else {
console.error("Unknown kind of InviteDialog: " + this.props.kind);
}
@ -1375,6 +1413,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
{this._renderSection('recents')}
{this._renderSection('suggestions')}
</div>
{consultSection}
</div>
</BaseDialog>
);

View file

@ -0,0 +1,54 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import {_t} from "../../../languageHandler";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
import {IDialogProps} from "./IDialogProps";
@replaceableComponent("views.dialogs.SeshatResetDialog")
export default class SeshatResetDialog extends React.PureComponent<IDialogProps> {
render() {
return (
<BaseDialog
hasCancel={true}
onFinished={this.props.onFinished.bind(null, false)}
title={_t("Reset event store?")}>
<div>
<p>
{_t("You most likely do not want to reset your event index store")}
<br />
{_t("If you do, please note that none of your messages will be deleted, " +
"but the search experience might be degraded for a few moments" +
"whilst the index is recreated",
)}
</p>
</div>
<DialogButtons
primaryButton={_t("Reset event store")}
onPrimaryButtonClick={this.props.onFinished.bind(null, true)}
primaryButtonClass="danger"
cancelButton={_t("Cancel")}
onCancel={this.props.onFinished.bind(null, false)}
/>
</BaseDialog>
);
}
}

View file

@ -25,6 +25,8 @@ import Field from '../../elements/Field';
import AccessibleButton from '../../elements/AccessibleButton';
import {_t} from '../../../../languageHandler';
import {IDialogProps} from "../IDialogProps";
import {accessSecretStorage} from "../../../../SecurityManager";
import Modal from "../../../../Modal";
// Maximum acceptable size of a key file. It's 59 characters including the spaces we encode,
// so this should be plenty and allow for people putting extra whitespace in the file because
@ -47,6 +49,7 @@ interface IState {
forceRecoveryKey: boolean;
passPhrase: string;
keyMatches: boolean | null;
resetting: boolean;
}
/*
@ -66,10 +69,14 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
forceRecoveryKey: false,
passPhrase: '',
keyMatches: null,
resetting: false,
};
}
private onCancel = () => {
if (this.state.resetting) {
this.setState({resetting: false});
}
this.props.onFinished(false);
};
@ -201,6 +208,55 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
});
};
private onResetAllClick = (ev: React.MouseEvent<HTMLAnchorElement>) => {
ev.preventDefault();
this.setState({resetting: true});
};
private onConfirmResetAllClick = async () => {
// Hide ourselves so the user can interact with the reset dialogs.
// We don't conclude the promise chain (onFinished) yet to avoid confusing
// any upstream code flows.
//
// Note: this will unmount us, so don't call `setState` or anything in the
// rest of this function.
Modal.toggleCurrentDialogVisibility();
try {
// Force reset secret storage (which resets the key backup)
await accessSecretStorage(async () => {
// Now reset cross-signing so everything Just Works™ again.
const cli = MatrixClientPeg.get();
await cli.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async (makeRequest) => {
// XXX: Making this an import breaks the app.
const InteractiveAuthDialog = sdk.getComponent("views.dialogs.InteractiveAuthDialog");
const {finished} = Modal.createTrackedDialog(
'Cross-signing keys dialog', '', InteractiveAuthDialog,
{
title: _t("Setting up keys"),
matrixClient: cli,
makeRequest,
},
);
const [confirmed] = await finished;
if (!confirmed) {
throw new Error("Cross-signing key upload auth canceled");
}
},
setupNewCrossSigning: true,
});
// Now we can indicate that the user is done pressing buttons, finally.
// Upstream flows will detect the new secret storage, key backup, etc and use it.
this.props.onFinished(true);
}, true);
} catch (e) {
console.error(e);
this.props.onFinished(false);
}
};
private getKeyValidationText(): string {
if (this.state.recoveryKeyFileError) {
return _t("Wrong file type");
@ -216,8 +272,9 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
}
render() {
// Caution: Making this an import will break tests.
// Caution: Making these an import will break tests.
const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
const DialogButtons = sdk.getComponent("views.elements.DialogButtons");
const hasPassphrase = (
this.props.keyInfo &&
@ -226,11 +283,36 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
this.props.keyInfo.passphrase.iterations
);
const resetButton = (
<div className="mx_AccessSecretStorageDialog_reset">
{_t("Forgotten or lost all recovery methods? <a>Reset all</a>", null, {
a: (sub) => <a
href="" onClick={this.onResetAllClick}
className="mx_AccessSecretStorageDialog_reset_link">{sub}</a>,
})}
</div>
);
let content;
let title;
let titleClass;
if (hasPassphrase && !this.state.forceRecoveryKey) {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
if (this.state.resetting) {
title = _t("Reset everything");
titleClass = ['mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_resetBadge'];
content = <div>
<p>{_t("Only do this if you have no other device to complete verification with.")}</p>
<p>{_t("If you reset everything, you will restart with no trusted sessions, no trusted users, and "
+ "might not be able to see past messages.")}</p>
<DialogButtons
primaryButton={_t('Reset')}
onPrimaryButtonClick={this.onConfirmResetAllClick}
hasCancel={true}
onCancel={this.onCancel}
focus={false}
primaryButtonClass="danger"
/>
</div>;
} else if (hasPassphrase && !this.state.forceRecoveryKey) {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
title = _t("Security Phrase");
titleClass = ['mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_securePhraseTitle'];
@ -278,13 +360,13 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
onCancel={this.onCancel}
focus={false}
primaryDisabled={this.state.passPhrase.length === 0}
additive={resetButton}
/>
</form>
</div>;
} else {
title = _t("Security Key");
titleClass = ['mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_secureBackupTitle'];
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const feedbackClasses = classNames({
'mx_AccessSecretStorageDialog_recoveryKeyFeedback': true,
@ -339,6 +421,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
onCancel={this.onCancel}
focus={false}
primaryDisabled={!this.state.recoveryKeyValid}
additive={resetButton}
/>
</form>
</div>;

View file

@ -19,7 +19,6 @@ import classnames from 'classnames';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import * as Avatar from '../../../Avatar';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import EventTile from '../rooms/EventTile';
import SettingsStore from "../../../settings/SettingsStore";
import {Layout} from "../../../settings/Layout";
@ -41,15 +40,26 @@ interface IProps {
* classnames to apply to the wrapper of the preview
*/
className: string;
/**
* The ID of the displayed user
*/
userId: string;
/**
* The display name of the displayed user
*/
displayName?: string;
/**
* The mxc:// avatar URL of the displayed user
*/
avatarUrl?: string;
}
/* eslint-disable camelcase */
interface IState {
userId: string;
displayname: string;
avatar_url: string;
message: string;
}
/* eslint-enable camelcase */
const AVATAR_SIZE = 32;
@ -57,45 +67,28 @@ const AVATAR_SIZE = 32;
export default class EventTilePreview extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
userId: "@erim:fink.fink",
displayname: "Erimayas Fink",
avatar_url: null,
message: props.message,
};
}
async componentDidMount() {
// Fetch current user data
const client = MatrixClientPeg.get();
const userId = client.getUserId();
const profileInfo = await client.getProfileInfo(userId);
const avatarUrl = profileInfo.avatar_url;
this.setState({
userId,
displayname: profileInfo.displayname,
avatar_url: avatarUrl,
});
}
private fakeEvent({userId, displayname, avatar_url: avatarUrl}: IState) {
private fakeEvent({message}: IState) {
// Fake it till we make it
/* eslint-disable quote-props */
const rawEvent = {
type: "m.room.message",
sender: userId,
sender: this.props.userId,
content: {
"m.new_content": {
msgtype: "m.text",
body: this.props.message,
displayname: displayname,
avatar_url: avatarUrl,
body: message,
displayname: this.props.displayName,
avatar_url: this.props.avatarUrl,
},
msgtype: "m.text",
body: this.props.message,
displayname: displayname,
avatar_url: avatarUrl,
body: message,
displayname: this.props.displayName,
avatar_url: this.props.avatarUrl,
},
unsigned: {
age: 97,
@ -108,12 +101,15 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
// Fake it more
event.sender = {
name: displayname,
userId: userId,
name: this.props.displayName,
userId: this.props.userId,
getAvatarUrl: (..._) => {
return Avatar.avatarUrlForUser({avatarUrl}, AVATAR_SIZE, AVATAR_SIZE, "crop");
return Avatar.avatarUrlForUser(
{ avatarUrl: this.props.avatarUrl },
AVATAR_SIZE, AVATAR_SIZE, "crop",
);
},
getMxcAvatarUrl: () => avatarUrl,
getMxcAvatarUrl: () => this.props.avatarUrl,
};
return event;

View file

@ -1,235 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {formatDate} from '../../../DateUtils';
import { _t } from '../../../languageHandler';
import filesize from "filesize";
import AccessibleButton from "./AccessibleButton";
import Modal from "../../../Modal";
import * as sdk from "../../../index";
import {Key} from "../../../Keyboard";
import FocusLock from "react-focus-lock";
import {replaceableComponent} from "../../../utils/replaceableComponent";
@replaceableComponent("views.elements.ImageView")
export default class ImageView extends React.Component {
static propTypes = {
src: PropTypes.string.isRequired, // the source of the image being displayed
name: PropTypes.string, // the main title ('name') for the image
link: PropTypes.string, // the link (if any) applied to the name of the image
width: PropTypes.number, // width of the image src in pixels
height: PropTypes.number, // height of the image src in pixels
fileSize: PropTypes.number, // size of the image src in bytes
onFinished: PropTypes.func.isRequired, // callback when the lightbox is dismissed
// the event (if any) that the Image is displaying. Used for event-specific stuff like
// redactions, senders, timestamps etc. Other descriptors are taken from the explicit
// properties above, which let us use lightboxes to display images which aren't associated
// with events.
mxEvent: PropTypes.object,
};
constructor(props) {
super(props);
this.state = { rotationDegrees: 0 };
}
onKeyDown = (ev) => {
if (ev.key === Key.ESCAPE) {
ev.stopPropagation();
ev.preventDefault();
this.props.onFinished();
}
};
onRedactClick = () => {
const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog");
Modal.createTrackedDialog('Confirm Redact Dialog', 'Image View', ConfirmRedactDialog, {
onFinished: (proceed) => {
if (!proceed) return;
this.props.onFinished();
MatrixClientPeg.get().redactEvent(
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(),
).catch(function(e) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
// display error message stating you couldn't delete this.
const code = e.errcode || e.statusCode;
Modal.createTrackedDialog('You cannot delete this image.', '', ErrorDialog, {
title: _t('Error'),
description: _t('You cannot delete this image. (%(code)s)', {code: code}),
});
});
},
});
};
getName() {
let name = this.props.name;
if (name && this.props.link) {
name = <a href={ this.props.link } target="_blank" rel="noreferrer noopener">{ name }</a>;
}
return name;
}
rotateCounterClockwise = () => {
const cur = this.state.rotationDegrees;
const rotationDegrees = (cur - 90) % 360;
this.setState({ rotationDegrees });
};
rotateClockwise = () => {
const cur = this.state.rotationDegrees;
const rotationDegrees = (cur + 90) % 360;
this.setState({ rotationDegrees });
};
render() {
/*
// In theory max-width: 80%, max-height: 80% on the CSS should work
// but in practice, it doesn't, so do it manually:
var width = this.props.width || 500;
var height = this.props.height || 500;
var maxWidth = document.documentElement.clientWidth * 0.8;
var maxHeight = document.documentElement.clientHeight * 0.8;
var widthFrac = width / maxWidth;
var heightFrac = height / maxHeight;
var displayWidth;
var displayHeight;
if (widthFrac > heightFrac) {
displayWidth = Math.min(width, maxWidth);
displayHeight = (displayWidth / width) * height;
} else {
displayHeight = Math.min(height, maxHeight);
displayWidth = (displayHeight / height) * width;
}
var style = {
width: displayWidth,
height: displayHeight
};
*/
let style = {};
let res;
if (this.props.width && this.props.height) {
style = {
width: this.props.width,
height: this.props.height,
};
res = style.width + "x" + style.height + "px";
}
let size;
if (this.props.fileSize) {
size = filesize(this.props.fileSize);
}
let sizeRes;
if (size && res) {
sizeRes = size + ", " + res;
} else {
sizeRes = size || res;
}
let mayRedact = false;
const showEventMeta = !!this.props.mxEvent;
let eventMeta;
if (showEventMeta) {
// Figure out the sender, defaulting to mxid
let sender = this.props.mxEvent.getSender();
const cli = MatrixClientPeg.get();
const room = cli.getRoom(this.props.mxEvent.getRoomId());
if (room) {
mayRedact = room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId);
const member = room.getMember(sender);
if (member) sender = member.name;
}
eventMeta = (<div className="mx_ImageView_metadata">
{ _t('Uploaded on %(date)s by %(user)s', {
date: formatDate(new Date(this.props.mxEvent.getTs())),
user: sender,
}) }
</div>);
}
let eventRedact;
if (mayRedact) {
eventRedact = (<div className="mx_ImageView_button" onClick={this.onRedactClick}>
{ _t('Remove') }
</div>);
}
const rotationDegrees = this.state.rotationDegrees;
const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style};
return (
<FocusLock
returnFocus={true}
lockProps={{
onKeyDown: this.onKeyDown,
role: "dialog",
}}
className="mx_ImageView"
>
<div className="mx_ImageView_lhs">
</div>
<div className="mx_ImageView_content">
<img src={this.props.src} title={this.props.name} style={effectiveStyle} className="mainImage" />
<div className="mx_ImageView_labelWrapper">
<div className="mx_ImageView_label">
<AccessibleButton className="mx_ImageView_rotateCounterClockwise" title={_t("Rotate Left")} onClick={ this.rotateCounterClockwise }>
<img src={require("../../../../res/img/rotate-ccw.svg")} alt={ _t('Rotate counter-clockwise') } width="18" height="18" />
</AccessibleButton>
<AccessibleButton className="mx_ImageView_rotateClockwise" title={_t("Rotate Right")} onClick={ this.rotateClockwise }>
<img src={require("../../../../res/img/rotate-cw.svg")} alt={ _t('Rotate clockwise') } width="18" height="18" />
</AccessibleButton>
<AccessibleButton className="mx_ImageView_cancel" title={_t("Close")} onClick={ this.props.onFinished }>
<img src={require("../../../../res/img/cancel-white.svg")} width="18" height="18" alt={ _t('Close') } />
</AccessibleButton>
<div className="mx_ImageView_shim">
</div>
<div className="mx_ImageView_name">
{ this.getName() }
</div>
{ eventMeta }
<a className="mx_ImageView_link" href={ this.props.src } download={ this.props.name } target="_blank" rel="noopener">
<div className="mx_ImageView_download">
{ _t('Download this file') }<br />
<span className="mx_ImageView_size">{ sizeRes }</span>
</div>
</a>
{ eventRedact }
<div className="mx_ImageView_shim">
</div>
</div>
</div>
</div>
<div className="mx_ImageView_rhs">
</div>
</FocusLock>
);
}
}

View file

@ -0,0 +1,439 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2020, 2021 Šimon Brandner <simon.bra.ag@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef } from 'react';
import { _t } from '../../../languageHandler';
import AccessibleTooltipButton from "./AccessibleTooltipButton";
import {Key} from "../../../Keyboard";
import FocusLock from "react-focus-lock";
import MemberAvatar from "../avatars/MemberAvatar";
import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/ContextMenuTooltipButton";
import MessageContextMenu from "../context_menus/MessageContextMenu";
import {aboveLeftOf, ContextMenu} from '../../structures/ContextMenu';
import MessageTimestamp from "../messages/MessageTimestamp";
import SettingsStore from "../../../settings/SettingsStore";
import {formatFullDate} from "../../../DateUtils";
import dis from '../../../dispatcher/dispatcher';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
const MIN_ZOOM = 100;
const MAX_ZOOM = 300;
// This is used for the buttons
const ZOOM_STEP = 10;
// This is used for mouse wheel events
const ZOOM_COEFFICIENT = 10;
// If we have moved only this much we can zoom
const ZOOM_DISTANCE = 10;
interface IProps {
src: string, // the source of the image being displayed
name?: string, // the main title ('name') for the image
link?: string, // the link (if any) applied to the name of the image
width?: number, // width of the image src in pixels
height?: number, // height of the image src in pixels
fileSize?: number, // size of the image src in bytes
onFinished(): void, // callback when the lightbox is dismissed
// the event (if any) that the Image is displaying. Used for event-specific stuff like
// redactions, senders, timestamps etc. Other descriptors are taken from the explicit
// properties above, which let us use lightboxes to display images which aren't associated
// with events.
mxEvent: MatrixEvent,
permalinkCreator: RoomPermalinkCreator,
}
interface IState {
rotation: number,
zoom: number,
translationX: number,
translationY: number,
moving: boolean,
contextMenuDisplayed: boolean,
}
@replaceableComponent("views.elements.ImageView")
export default class ImageView extends React.Component<IProps, IState> {
constructor(props) {
super(props);
this.state = {
rotation: 0,
zoom: MIN_ZOOM,
translationX: 0,
translationY: 0,
moving: false,
contextMenuDisplayed: false,
};
}
// XXX: Refs to functional components
private contextMenuButton = createRef<any>();
private focusLock = createRef<any>();
private initX = 0;
private initY = 0;
private lastX = 0;
private lastY = 0;
private previousX = 0;
private previousY = 0;
componentDidMount() {
// We have to use addEventListener() because the listener
// needs to be passive in order to work with Chromium
this.focusLock.current.addEventListener('wheel', this.onWheel, { passive: false });
}
componentWillUnmount() {
this.focusLock.current.removeEventListener('wheel', this.onWheel);
}
private onKeyDown = (ev: KeyboardEvent) => {
if (ev.key === Key.ESCAPE) {
ev.stopPropagation();
ev.preventDefault();
this.props.onFinished();
}
};
private onWheel = (ev: WheelEvent) => {
ev.stopPropagation();
ev.preventDefault();
const newZoom = this.state.zoom - (ev.deltaY * ZOOM_COEFFICIENT);
if (newZoom <= MIN_ZOOM) {
this.setState({
zoom: MIN_ZOOM,
translationX: 0,
translationY: 0,
});
return;
}
if (newZoom >= MAX_ZOOM) {
this.setState({zoom: MAX_ZOOM});
return;
}
this.setState({
zoom: newZoom,
});
};
private onRotateCounterClockwiseClick = () => {
const cur = this.state.rotation;
const rotationDegrees = cur - 90;
this.setState({ rotation: rotationDegrees });
};
private onRotateClockwiseClick = () => {
const cur = this.state.rotation;
const rotationDegrees = cur + 90;
this.setState({ rotation: rotationDegrees });
};
private onZoomInClick = () => {
if (this.state.zoom >= MAX_ZOOM) {
this.setState({zoom: MAX_ZOOM});
return;
}
this.setState({
zoom: this.state.zoom + ZOOM_STEP,
});
};
private onZoomOutClick = () => {
if (this.state.zoom <= MIN_ZOOM) {
this.setState({
zoom: MIN_ZOOM,
translationX: 0,
translationY: 0,
});
return;
}
this.setState({
zoom: this.state.zoom - ZOOM_STEP,
});
};
private onDownloadClick = () => {
const a = document.createElement("a");
a.href = this.props.src;
a.download = this.props.name;
a.target = "_blank";
a.click();
};
private onOpenContextMenu = () => {
this.setState({
contextMenuDisplayed: true,
});
};
private onCloseContextMenu = () => {
this.setState({
contextMenuDisplayed: false,
});
};
private onPermalinkClicked = (ev: React.MouseEvent) => {
// This allows the permalink to be opened in a new tab/window or copied as
// matrix.to, but also for it to enable routing within Element when clicked.
ev.preventDefault();
dis.dispatch({
action: 'view_room',
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
});
this.props.onFinished();
};
private onStartMoving = (ev: React.MouseEvent) => {
ev.stopPropagation();
ev.preventDefault();
// Zoom in if we are completely zoomed out
if (this.state.zoom === MIN_ZOOM) {
this.setState({zoom: MAX_ZOOM});
return;
}
this.setState({moving: true});
this.previousX = this.state.translationX;
this.previousY = this.state.translationY;
this.initX = ev.pageX - this.lastX;
this.initY = ev.pageY - this.lastY;
};
private onMoving = (ev: React.MouseEvent) => {
ev.stopPropagation();
ev.preventDefault();
if (!this.state.moving) return;
this.lastX = ev.pageX - this.initX;
this.lastY = ev.pageY - this.initY;
this.setState({
translationX: this.lastX,
translationY: this.lastY,
});
};
private onEndMoving = () => {
// Zoom out if we haven't moved much
if (
this.state.moving === true &&
Math.abs(this.state.translationX - this.previousX) < ZOOM_DISTANCE &&
Math.abs(this.state.translationY - this.previousY) < ZOOM_DISTANCE
) {
this.setState({
zoom: MIN_ZOOM,
translationX: 0,
translationY: 0,
});
}
this.setState({moving: false});
};
private renderContextMenu() {
let contextMenu = null;
if (this.state.contextMenuDisplayed) {
contextMenu = (
<ContextMenu
{...aboveLeftOf(this.contextMenuButton.current.getBoundingClientRect())}
onFinished={this.onCloseContextMenu}
>
<MessageContextMenu
mxEvent={this.props.mxEvent}
permalinkCreator={this.props.permalinkCreator}
onFinished={this.onCloseContextMenu}
onCloseDialog={this.props.onFinished}
/>
</ContextMenu>
);
}
return (
<React.Fragment>
{ contextMenu }
</React.Fragment>
);
}
render() {
const showEventMeta = !!this.props.mxEvent;
let cursor;
if (this.state.moving) {
cursor= "grabbing";
} else if (this.state.zoom === MIN_ZOOM) {
cursor = "zoom-in";
} else {
cursor = "zoom-out";
}
const rotationDegrees = this.state.rotation + "deg";
const zoomPercentage = this.state.zoom/100;
const translatePixelsX = this.state.translationX + "px";
const translatePixelsY = this.state.translationY + "px";
// The order of the values is important!
// First, we translate and only then we rotate, otherwise
// we would apply the translation to an already rotated
// image causing it translate in the wrong direction.
const style = {
cursor: cursor,
transition: this.state.moving ? null : "transform 200ms ease 0s",
transform: `translateX(${translatePixelsX})
translateY(${translatePixelsY})
scale(${zoomPercentage})
rotate(${rotationDegrees})`,
};
let info;
if (showEventMeta) {
const mxEvent = this.props.mxEvent;
const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
let permalink = "#";
if (this.props.permalinkCreator) {
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
}
const senderName = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
const sender = (
<div className="mx_ImageView_info_sender">
{senderName}
</div>
);
const messageTimestamp = (
<a
href={permalink}
onClick={this.onPermalinkClicked}
aria-label={formatFullDate(new Date(this.props.mxEvent.getTs()), showTwelveHour, false)}
>
<MessageTimestamp
showFullDate={true}
showTwelveHour={showTwelveHour}
ts={mxEvent.getTs()}
showSeconds={false}
/>
</a>
);
const avatar = (
<MemberAvatar
member={mxEvent.sender}
width={32} height={32}
viewUserOnClick={true}
/>
);
info = (
<div className="mx_ImageView_info_wrapper">
{avatar}
<div className="mx_ImageView_info">
{sender}
{messageTimestamp}
</div>
</div>
);
} else {
// If there is no event - we're viewing an avatar, we set
// an empty div here, since the panel uses space-between
// and we want the same placement of elements
info = (
<div></div>
);
}
let contextMenuButton;
if (this.props.mxEvent) {
contextMenuButton = (
<ContextMenuTooltipButton
className="mx_ImageView_button mx_ImageView_button_more"
title={_t("Options")}
onClick={this.onOpenContextMenu}
inputRef={this.contextMenuButton}
isExpanded={this.state.contextMenuDisplayed}
/>
);
}
return (
<FocusLock
returnFocus={true}
lockProps={{
onKeyDown: this.onKeyDown,
role: "dialog",
}}
className="mx_ImageView"
ref={this.focusLock}
>
<div className="mx_ImageView_panel">
{info}
<div className="mx_ImageView_toolbar">
<AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_rotateCW"
title={_t("Rotate Right")}
onClick={this.onRotateClockwiseClick}>
</AccessibleTooltipButton>
<AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_rotateCCW"
title={_t("Rotate Left")}
onClick={ this.onRotateCounterClockwiseClick }>
</AccessibleTooltipButton>
<AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_zoomOut"
title={_t("Zoom out")}
onClick={ this.onZoomOutClick }>
</AccessibleTooltipButton>
<AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_zoomIn"
title={_t("Zoom in")}
onClick={ this.onZoomInClick }>
</AccessibleTooltipButton>
<AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_download"
title={_t("Download")}
onClick={ this.onDownloadClick }>
</AccessibleTooltipButton>
{contextMenuButton}
<AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_close"
title={_t("Close")}
onClick={ this.props.onFinished }>
</AccessibleTooltipButton>
{this.renderContextMenu()}
</div>
</div>
<div className="mx_ImageView_image_wrapper">
<img
src={this.props.src}
title={this.props.name}
style={style}
className="mx_ImageView_image"
draggable={true}
onMouseDown={this.onStartMoving}
onMouseMove={this.onMoving}
onMouseUp={this.onEndMoving}
onMouseLeave={this.onEndMoving}
/>
</div>
</FocusLock>
);
}
}

View file

@ -0,0 +1,62 @@
/*
Copyright 2021 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 classNames from "classnames";
import React from "react";
import { _t } from "../../../languageHandler";
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
reason: string;
}
interface IState {
hidden: boolean;
}
@replaceableComponent("views.elements.InviteReason")
export default class InviteReason extends React.PureComponent<IProps, IState> {
constructor(props) {
super(props);
this.state = {
// We hide the reason for invitation by default, since it can be a
// vector for spam/harassment.
hidden: true,
};
}
onViewClick = () => {
this.setState({
hidden: false,
});
}
render() {
const classes = classNames({
"mx_InviteReason": true,
"mx_InviteReason_hidden": this.state.hidden,
});
return <div className={classes}>
<div className="mx_InviteReason_reason">{this.props.reason}</div>
<div className="mx_InviteReason_view"
onClick={this.onViewClick}
>
{_t("View message")}
</div>
</div>;
}
}

View file

@ -41,6 +41,9 @@ export default class MImageBody extends React.Component {
/* the maximum image height to use */
maxImageHeight: PropTypes.number,
/* the permalinkCreator */
permalinkCreator: PropTypes.object,
};
static contextType = MatrixClientContext;
@ -106,6 +109,7 @@ export default class MImageBody extends React.Component {
src: httpUrl,
name: content.body && content.body.length > 0 ? content.body : _t('Attachment'),
mxEvent: this.props.mxEvent,
permalinkCreator: this.props.permalinkCreator,
};
if (content.info) {
@ -114,7 +118,7 @@ export default class MImageBody extends React.Component {
params.fileSize = content.info.size;
}
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
}
}

View file

@ -132,7 +132,7 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
// enable the play button. Firefox does not seem to care either
// way, so it's fine to do for all browsers.
decryptedUrl: `data:${content?.info?.mimetype},`,
decryptedThumbnailUrl: thumbnailUrl,
decryptedThumbnailUrl: thumbnailUrl || `data:${content?.info?.mimetype},`,
decryptedBlob: null,
});
}

View file

@ -47,8 +47,12 @@ export default class MessageEvent extends React.Component {
/* the maximum image height to use, if the event is an image */
maxImageHeight: PropTypes.number,
/* overrides for the msgtype-specific components, used by ReplyTile to override file rendering */
overrideBodyTypes: PropTypes.object,
overrideEventTypes: PropTypes.object,
/* the permalinkCreator */
permalinkCreator: PropTypes.object,
};
constructor(props) {
@ -132,6 +136,7 @@ export default class MessageEvent extends React.Component {
editState={this.props.editState}
onHeightChanged={this.props.onHeightChanged}
onMessageAllowed={this.onTileUpdate}
permalinkCreator={this.props.permalinkCreator}
/> : null;
}
}

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import {formatFullDate, formatTime} from '../../../DateUtils';
import {formatFullDate, formatTime, formatFullTime} from '../../../DateUtils';
import {replaceableComponent} from "../../../utils/replaceableComponent";
@replaceableComponent("views.messages.MessageTimestamp")
@ -25,13 +25,24 @@ export default class MessageTimestamp extends React.Component {
static propTypes = {
ts: PropTypes.number.isRequired,
showTwelveHour: PropTypes.bool,
showFullDate: PropTypes.bool,
showSeconds: PropTypes.bool,
};
render() {
const date = new Date(this.props.ts);
let timestamp;
if (this.props.showFullDate) {
timestamp = formatFullDate(date, this.props.showTwelveHour, this.props.showSeconds);
} else if (this.props.showSeconds) {
timestamp = formatFullTime(date, this.props.showTwelveHour);
} else {
timestamp = formatTime(date, this.props.showTwelveHour);
}
return (
<span className="mx_MessageTimestamp" title={formatFullDate(date, this.props.showTwelveHour)} aria-hidden={true}>
{ formatTime(date, this.props.showTwelveHour) }
{timestamp}
</span>
);
}

View file

@ -49,7 +49,7 @@ export default class RoomAvatarEvent extends React.Component {
src: httpUrl,
name: text,
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
};
render() {

View file

@ -24,6 +24,7 @@ import {RoomMember} from 'matrix-js-sdk/src/models/room-member';
import {User} from 'matrix-js-sdk/src/models/user';
import {Room} from 'matrix-js-sdk/src/models/room';
import {EventTimeline} from 'matrix-js-sdk/src/models/event-timeline';
import {MatrixEvent} from 'matrix-js-sdk/src/models/event';
import dis from '../../../dispatcher/dispatcher';
import Modal from '../../../Modal';
@ -496,11 +497,11 @@ const isMuted = (member: RoomMember, powerLevelContent: IPowerLevelsContent) =>
export const useRoomPowerLevels = (cli: MatrixClient, room: Room) => {
const [powerLevels, setPowerLevels] = useState<IPowerLevelsContent>({});
const update = useCallback(() => {
if (!room) {
return;
}
const event = room.currentState.getStateEvents("m.room.power_levels", "");
const update = useCallback((ev?: MatrixEvent) => {
if (!room) return;
if (ev && ev.getType() !== EventType.RoomPowerLevels) return;
const event = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
if (event) {
setPowerLevels(event.getContent());
} else {
@ -511,7 +512,7 @@ export const useRoomPowerLevels = (cli: MatrixClient, room: Room) => {
};
}, [room]);
useEventEmitter(cli, "RoomState.members", update);
useEventEmitter(cli, "RoomState.events", update);
useEffect(() => {
update();
return () => {
@ -1431,7 +1432,7 @@ const UserInfoHeader: React.FC<{
name: member.name,
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
}, [member]);
const avatarElement = (
@ -1494,7 +1495,7 @@ const UserInfoHeader: React.FC<{
e2eIcon = <E2EIcon size={18} status={e2eStatus} isUser={true} />;
}
const displayName = member.name || member.displayname;
const displayName = member.rawDisplayName || member.displayname;
return <React.Fragment>
{ avatarElement }

View file

@ -149,8 +149,8 @@ export default class AuxPanel extends React.Component<IProps, IState> {
const callView = (
<CallViewForRoom
roomId={this.props.room.roomId}
onResize={this.props.onResize}
maxVideoHeight={this.props.maxHeight}
resizeNotifier={this.props.resizeNotifier}
/>
);

View file

@ -140,7 +140,12 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
}
public componentDidUpdate(prevProps: IProps) {
if (this.props.placeholder !== prevProps.placeholder && this.props.placeholder) {
// We need to re-check the placeholder when the enabled state changes because it causes the
// placeholder element to remount, which gets rid of the `::before` class. Re-evaluating the
// placeholder means we get a proper `::before` with the placeholder.
const enabledChange = this.props.disabled !== prevProps.disabled;
const placeholderChanged = this.props.placeholder !== prevProps.placeholder;
if (this.props.placeholder && (placeholderChanged || enabledChange)) {
const {isEmpty} = this.props.model;
if (isEmpty) {
this.showPlaceholder();
@ -485,16 +490,14 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
if (model.autoComplete && model.autoComplete.hasCompletions()) {
const autoComplete = model.autoComplete;
switch (autocompleteAction) {
case AutocompleteAction.CompleteOrPrevSelection:
case AutocompleteAction.PrevSelection:
autoComplete.onUpArrow(event);
autoComplete.selectPreviousSelection();
handled = true;
break;
case AutocompleteAction.CompleteOrNextSelection:
case AutocompleteAction.NextSelection:
autoComplete.onDownArrow(event);
handled = true;
break;
case AutocompleteAction.ApplySelection:
autoComplete.onTab(event);
autoComplete.selectNextSelection();
handled = true;
break;
case AutocompleteAction.Cancel:
@ -504,8 +507,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
default:
return; // don't preventDefault on anything else
}
} else if (autocompleteAction === AutocompleteAction.ApplySelection) {
this.tabCompleteName(event);
} else if (autocompleteAction === AutocompleteAction.CompleteOrPrevSelection
|| autocompleteAction === AutocompleteAction.CompleteOrNextSelection) {
// there is no current autocomplete window, try to open it
this.tabCompleteName();
handled = true;
} else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) {
this.formatBarRef.current.hide();
@ -517,7 +522,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
}
};
private async tabCompleteName(event: React.KeyboardEvent) {
private async tabCompleteName() {
try {
await new Promise<void>(resolve => this.setState({showVisualBell: false}, resolve));
const {model} = this.props;
@ -540,7 +545,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
// Don't try to do things with the autocomplete if there is none shown
if (model.autoComplete) {
await model.autoComplete.onTab(event);
await model.autoComplete.startSelection();
if (!model.autoComplete.hasSelection()) {
this.setState({showVisualBell: true});
model.autoComplete.close();
@ -670,8 +675,6 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
});
const classes = classNames("mx_BasicMessageComposer_input", {
"mx_BasicMessageComposer_input_shouldShowPillAvatar": this.state.showPillAvatar,
// TODO: @@ TravisR: This doesn't work properly. The composer resets in a strange way.
"mx_BasicMessageComposer_input_disabled": this.props.disabled,
});

View file

@ -1,8 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,18 +15,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import ReplyThread from "../elements/ReplyThread";
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import classNames from "classnames";
import {EventType} from "matrix-js-sdk/src/@types/event";
import {EventStatus} from 'matrix-js-sdk/src/models/event';
import ReplyThread from "../elements/ReplyThread";
import { _t } from '../../../languageHandler';
import * as TextForEvent from "../../../TextForEvent";
import * as sdk from "../../../index";
import dis from '../../../dispatcher/dispatcher';
import SettingsStore from "../../../settings/SettingsStore";
import {Layout, LayoutPropType} from "../../../settings/Layout";
import {EventStatus} from 'matrix-js-sdk/src/models/event';
import {formatTime} from "../../../DateUtils";
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import {ALL_RULE_TYPES} from "../../../mjolnir/BanList";
@ -43,39 +42,56 @@ import {replaceableComponent} from "../../../utils/replaceableComponent";
import Tooltip from "../elements/Tooltip";
const eventTileTypes = {
'm.room.message': 'messages.MessageEvent',
'm.sticker': 'messages.MessageEvent',
'm.key.verification.cancel': 'messages.MKeyVerificationConclusion',
'm.key.verification.done': 'messages.MKeyVerificationConclusion',
'm.room.encryption': 'messages.EncryptionEvent',
'm.call.invite': 'messages.TextualEvent',
'm.call.answer': 'messages.TextualEvent',
'm.call.hangup': 'messages.TextualEvent',
'm.call.reject': 'messages.TextualEvent',
[EventType.RoomMessage]: 'messages.MessageEvent',
[EventType.Sticker]: 'messages.MessageEvent',
[EventType.KeyVerificationCancel]: 'messages.MKeyVerificationConclusion',
[EventType.KeyVerificationDone]: 'messages.MKeyVerificationConclusion',
[EventType.CallInvite]: 'messages.TextualEvent',
[EventType.CallAnswer]: 'messages.TextualEvent',
[EventType.CallHangup]: 'messages.TextualEvent',
[EventType.CallReject]: 'messages.TextualEvent',
};
const stateEventTileTypes = {
'm.room.encryption': 'messages.EncryptionEvent',
'm.room.canonical_alias': 'messages.TextualEvent',
'm.room.create': 'messages.RoomCreate',
'm.room.member': 'messages.TextualEvent',
'm.room.name': 'messages.TextualEvent',
'm.room.avatar': 'messages.RoomAvatarEvent',
'm.room.third_party_invite': 'messages.TextualEvent',
'm.room.history_visibility': 'messages.TextualEvent',
'm.room.topic': 'messages.TextualEvent',
'm.room.power_levels': 'messages.TextualEvent',
'm.room.pinned_events': 'messages.TextualEvent',
'm.room.server_acl': 'messages.TextualEvent',
[EventType.RoomEncryption]: 'messages.EncryptionEvent',
[EventType.RoomCanonicalAlias]: 'messages.TextualEvent',
[EventType.RoomCreate]: 'messages.RoomCreate',
[EventType.RoomMember]: 'messages.TextualEvent',
[EventType.RoomName]: 'messages.TextualEvent',
[EventType.RoomAvatar]: 'messages.RoomAvatarEvent',
[EventType.RoomThirdPartyInvite]: 'messages.TextualEvent',
[EventType.RoomHistoryVisibility]: 'messages.TextualEvent',
[EventType.RoomTopic]: 'messages.TextualEvent',
[EventType.RoomPowerLevels]: 'messages.TextualEvent',
[EventType.RoomPinnedEvents]: 'messages.TextualEvent',
[EventType.RoomServerAcl]: 'messages.TextualEvent',
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
'im.vector.modular.widgets': 'messages.TextualEvent',
[WIDGET_LAYOUT_EVENT_TYPE]: 'messages.TextualEvent',
'm.room.tombstone': 'messages.TextualEvent',
'm.room.join_rules': 'messages.TextualEvent',
'm.room.guest_access': 'messages.TextualEvent',
'm.room.related_groups': 'messages.TextualEvent',
[EventType.RoomTombstone]: 'messages.TextualEvent',
[EventType.RoomJoinRules]: 'messages.TextualEvent',
[EventType.RoomGuestAccess]: 'messages.TextualEvent',
'm.room.related_groups': 'messages.TextualEvent', // legacy communities flair
};
const stateEventSingular = new Set([
EventType.RoomEncryption,
EventType.RoomCanonicalAlias,
EventType.RoomCreate,
EventType.RoomName,
EventType.RoomAvatar,
EventType.RoomHistoryVisibility,
EventType.RoomTopic,
EventType.RoomPowerLevels,
EventType.RoomPinnedEvents,
EventType.RoomServerAcl,
WIDGET_LAYOUT_EVENT_TYPE,
EventType.RoomTombstone,
EventType.RoomJoinRules,
EventType.RoomGuestAccess,
'm.room.related_groups',
]);
// Add all the Mjolnir stuff to the renderer
for (const evType of ALL_RULE_TYPES) {
stateEventTileTypes[evType] = 'messages.TextualEvent';
@ -132,7 +148,12 @@ export function getHandlerTile(ev) {
}
}
return ev.isState() ? stateEventTileTypes[type] : eventTileTypes[type];
if (ev.isState()) {
if (stateEventSingular.has(type) && ev.getStateKey() !== "") return undefined;
return stateEventTileTypes[type];
}
return eventTileTypes[type];
}
const MAX_READ_AVATARS = 5;
@ -239,6 +260,9 @@ export default class EventTile extends React.Component {
// whether or not to show flair at all
enableFlair: PropTypes.bool,
// whether or not to show read receipts
showReadReceipts: PropTypes.bool,
};
static defaultProps = {
@ -837,8 +861,6 @@ export default class EventTile extends React.Component {
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
}
const readAvatars = this.getReadAvatars();
let avatar;
let sender;
let avatarSize;
@ -936,7 +958,7 @@ export default class EventTile extends React.Component {
);
const TooltipButton = sdk.getComponent('elements.TooltipButton');
const keyRequestInfo = isEncryptionFailure ?
const keyRequestInfo = isEncryptionFailure && !isRedacted ?
<div className="mx_EventTile_keyRequestInfo">
<span className="mx_EventTile_keyRequestInfo_text">
{ keyRequestInfoContent }
@ -967,6 +989,16 @@ export default class EventTile extends React.Component {
const groupPadlock = !useIRCLayout && !isBubbleMessage && this._renderE2EPadlock();
const ircPadlock = useIRCLayout && !isBubbleMessage && this._renderE2EPadlock();
let msgOption;
if (this.props.showReadReceipts) {
const readAvatars = this.getReadAvatars();
msgOption = (
<div className="mx_EventTile_msgOption">
{ readAvatars }
</div>
);
}
switch (this.props.tileShape) {
case 'notif': {
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
@ -1080,14 +1112,13 @@ export default class EventTile extends React.Component {
highlights={this.props.highlights}
highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
permalinkCreator={this.props.permalinkCreator}
onHeightChanged={this.props.onHeightChanged} />
{ keyRequestInfo }
{ reactionsRow }
{ actionBar }
</div>
<div className="mx_EventTile_msgOption">
{ readAvatars }
</div>
{msgOption}
{
// The avatar goes after the event tile as it's absolutely positioned to be over the
// event tile line, so needs to be later in the DOM so it appears on top (this avoids

View file

@ -96,7 +96,7 @@ export default class LinkPreviewWidget extends React.Component {
link: this.props.link,
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
};
render() {

View file

@ -29,11 +29,10 @@ import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import ReplyPreview from "./ReplyPreview";
import {UIFeature} from "../../../settings/UIFeature";
import WidgetStore from "../../../stores/WidgetStore";
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
import ActiveWidgetStore from "../../../stores/ActiveWidgetStore";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import VoiceRecordComposerTile from "./VoiceRecordComposerTile";
import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
function ComposerAvatar(props) {
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
@ -178,15 +177,12 @@ export default class MessageComposer extends React.Component {
this._onRoomStateEvents = this._onRoomStateEvents.bind(this);
this._onTombstoneClick = this._onTombstoneClick.bind(this);
this.renderPlaceholderText = this.renderPlaceholderText.bind(this);
WidgetStore.instance.on(UPDATE_EVENT, this._onWidgetUpdate);
ActiveWidgetStore.on('update', this._onActiveWidgetUpdate);
VoiceRecordingStore.instance.on(UPDATE_EVENT, this._onVoiceStoreUpdate);
this._dispatcherRef = null;
this.state = {
tombstone: this._getRoomTombstone(),
canSendMessages: this.props.room.maySendMessage(),
hasConference: WidgetStore.instance.doesRoomHaveConference(this.props.room),
joinedConference: WidgetStore.instance.isJoinedToConferenceIn(this.props.room),
isComposerEmpty: true,
haveRecording: false,
};
@ -204,14 +200,6 @@ export default class MessageComposer extends React.Component {
}
};
_onWidgetUpdate = () => {
this.setState({hasConference: WidgetStore.instance.doesRoomHaveConference(this.props.room)});
};
_onActiveWidgetUpdate = () => {
this.setState({joinedConference: WidgetStore.instance.isJoinedToConferenceIn(this.props.room)});
};
componentDidMount() {
this.dispatcherRef = dis.register(this.onAction);
MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents);
@ -238,8 +226,7 @@ export default class MessageComposer extends React.Component {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents);
}
WidgetStore.instance.removeListener(UPDATE_EVENT, this._onWidgetUpdate);
ActiveWidgetStore.removeListener('update', this._onActiveWidgetUpdate);
VoiceRecordingStore.instance.off(UPDATE_EVENT, this._onVoiceStoreUpdate);
dis.unregister(this.dispatcherRef);
}
@ -327,8 +314,8 @@ export default class MessageComposer extends React.Component {
});
}
onVoiceUpdate = (haveRecording: boolean) => {
this.setState({haveRecording});
_onVoiceStoreUpdate = () => {
this.setState({haveRecording: !!VoiceRecordingStore.instance.activeRecording});
};
render() {
@ -352,7 +339,6 @@ export default class MessageComposer extends React.Component {
permalinkCreator={this.props.permalinkCreator}
replyToEvent={this.props.replyToEvent}
onChange={this.onChange}
// TODO: @@ TravisR - Disabling the composer doesn't work
disabled={this.state.haveRecording}
/>,
);
@ -373,8 +359,7 @@ export default class MessageComposer extends React.Component {
if (SettingsStore.getValue("feature_voice_messages")) {
controls.push(<VoiceRecordComposerTile
key="controls_voice_record"
room={this.props.room}
onRecording={this.onVoiceUpdate} />);
room={this.props.room} />);
}
if (!this.state.isComposerEmpty || this.state.haveRecording) {

View file

@ -17,22 +17,13 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import '../../../VelocityBounce';
import { _t } from '../../../languageHandler';
import {formatDate} from '../../../DateUtils';
import Velociraptor from "../../../Velociraptor";
import NodeAnimator from "../../../NodeAnimator";
import * as sdk from "../../../index";
import {toPx} from "../../../utils/units";
import {replaceableComponent} from "../../../utils/replaceableComponent";
let bounce = false;
try {
if (global.localStorage) {
bounce = global.localStorage.getItem('avatar_bounce') == 'true';
}
} catch (e) {
}
@replaceableComponent("views.rooms.ReadReceiptMarker")
export default class ReadReceiptMarker extends React.PureComponent {
static propTypes = {
@ -115,7 +106,18 @@ export default class ReadReceiptMarker extends React.PureComponent {
// we've already done our display - nothing more to do.
return;
}
this._animateMarker();
}
componentDidUpdate(prevProps) {
const differentLeftOffset = prevProps.leftOffset !== this.props.leftOffset;
const visibilityChanged = prevProps.hidden !== this.props.hidden;
if (differentLeftOffset || visibilityChanged) {
this._animateMarker();
}
}
_animateMarker() {
// treat new RRs as though they were off the top of the screen
let oldTop = -15;
@ -139,42 +141,18 @@ export default class ReadReceiptMarker extends React.PureComponent {
}
const startStyles = [];
const enterTransitionOpts = [];
if (oldInfo && oldInfo.left) {
// start at the old height and in the old h pos
startStyles.push({ top: startTopOffset+"px",
left: toPx(oldInfo.left) });
const reorderTransitionOpts = {
duration: 100,
easing: 'easeOut',
};
enterTransitionOpts.push(reorderTransitionOpts);
}
// then shift to the rightmost column,
// and then it will drop down to its resting position
//
// XXX: We use a small left value to trick velocity-animate into actually animating.
// This is a very annoying bug where if it thinks there's no change to `left` then it'll
// skip applying it, thus making our read receipt at +14px instead of +0px like it
// should be. This does cause a tiny amount of drift for read receipts, however with a
// value so small it's not perceived by a user.
// Note: Any smaller values (or trying to interchange units) might cause read receipts to
// fail to fall down or cause gaps.
startStyles.push({ top: startTopOffset+'px', left: '1px' });
enterTransitionOpts.push({
duration: bounce ? Math.min(Math.log(Math.abs(startTopOffset)) * 200, 3000) : 300,
easing: bounce ? 'easeOutBounce' : 'easeOutCubic',
});
startStyles.push({ top: startTopOffset+'px', left: '0' });
this.setState({
suppressDisplay: false,
startStyles: startStyles,
enterTransitionOpts: enterTransitionOpts,
});
}
@ -187,7 +165,6 @@ export default class ReadReceiptMarker extends React.PureComponent {
const style = {
left: toPx(this.props.leftOffset),
top: '0px',
visibility: this.props.hidden ? 'hidden' : 'visible',
};
let title;
@ -210,9 +187,8 @@ export default class ReadReceiptMarker extends React.PureComponent {
}
return (
<Velociraptor
startStyles={this.state.startStyles}
enterTransitionOpts={this.state.enterTransitionOpts} >
<NodeAnimator
startStyles={this.state.startStyles} >
<MemberAvatar
member={this.props.member}
fallbackUserId={this.props.fallbackUserId}
@ -223,7 +199,7 @@ export default class ReadReceiptMarker extends React.PureComponent {
onClick={this.props.onClick}
inputRef={this._avatar}
/>
</Velociraptor>
</NodeAnimator>
);
}
}

View file

@ -1,7 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Copyright 2015-2021 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.
@ -27,7 +25,8 @@ import SdkConfig from "../../../SdkConfig";
import IdentityAuthClient from '../../../IdentityAuthClient';
import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import InviteReason from "../elements/InviteReason";
const MessageCase = Object.freeze({
NotLoggedIn: "NotLoggedIn",
@ -306,6 +305,7 @@ export default class RoomPreviewBar extends React.Component {
let showSpinner = false;
let title;
let subTitle;
let reasonElement;
let primaryActionHandler;
let primaryActionLabel;
let secondaryActionHandler;
@ -491,6 +491,12 @@ export default class RoomPreviewBar extends React.Component {
primaryActionLabel = _t("Accept");
}
const myUserId = MatrixClientPeg.get().getUserId();
const reason = this.props.room.currentState.getMember(myUserId).events.member.event.content.reason;
if (reason) {
reasonElement = <InviteReason reason={reason} />;
}
primaryActionHandler = this.props.onJoinClick;
secondaryActionLabel = _t("Reject");
secondaryActionHandler = this.props.onRejectClick;
@ -582,6 +588,7 @@ export default class RoomPreviewBar extends React.Component {
{ titleElement }
{ subTitleElements }
</div>
{ reasonElement }
<div className="mx_RoomPreviewBar_actions">
{ secondaryButton }
{ extraComponents }

View file

@ -563,7 +563,11 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
let messagePreview = null;
if (this.showMessagePreview && this.state.messagePreview) {
messagePreview = (
<div className="mx_RoomTile_messagePreview" id={messagePreviewId(this.props.room.roomId)}>
<div
className="mx_RoomTile_messagePreview"
id={messagePreviewId(this.props.room.roomId)}
title={this.state.messagePreview}
>
{this.state.messagePreview}
</div>
);

View file

@ -477,6 +477,10 @@ export default class SendMessageComposer extends React.Component {
}
onAction = (payload) => {
// don't let the user into the composer if it is disabled - all of these branches lead
// to the cursor being in the composer
if (this.props.disabled) return;
switch (payload.action) {
case 'reply_to_event':
case Action.FocusComposer:

View file

@ -17,20 +17,27 @@ limitations under the License.
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import {_t} from "../../../languageHandler";
import React from "react";
import {VoiceRecorder} from "../../../voice/VoiceRecorder";
import {VoiceRecording} from "../../../voice/VoiceRecording";
import {Room} from "matrix-js-sdk/src/models/room";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import classNames from "classnames";
import LiveRecordingWaveform from "../voice_messages/LiveRecordingWaveform";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import LiveRecordingClock from "../voice_messages/LiveRecordingClock";
import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
interface IProps {
room: Room;
onRecording: (haveRecording: boolean) => void;
}
interface IState {
recorder?: VoiceRecorder;
recorder?: VoiceRecording;
}
/**
* Container tile for rendering the voice message recorder in the composer.
*/
@replaceableComponent("views.rooms.VoiceRecordComposerTile")
export default class VoiceRecordComposerTile extends React.PureComponent<IProps, IState> {
public constructor(props) {
super(props);
@ -50,20 +57,24 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
msgtype: "org.matrix.msc2516.voice",
url: mxc,
});
await VoiceRecordingStore.instance.disposeRecording();
this.setState({recorder: null});
this.props.onRecording(false);
return;
}
const recorder = new VoiceRecorder(MatrixClientPeg.get());
const recorder = VoiceRecordingStore.instance.startRecording();
await recorder.start();
this.props.onRecording(true);
// TODO: @@ TravisR: Run through EQ component
// recorder.frequencyData.onUpdate((freq) => {
// console.log('@@ UPDATE', freq);
// });
this.setState({recorder});
};
private renderWaveformArea() {
if (!this.state.recorder) return null;
return <div className='mx_VoiceRecordComposerTile_waveformContainer'>
<LiveRecordingClock recorder={this.state.recorder} />
<LiveRecordingWaveform recorder={this.state.recorder} />
</div>;
}
public render() {
const classes = classNames({
'mx_MessageComposer_button': !this.state.recorder,
@ -77,12 +88,13 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
tooltip = _t("Stop & send recording");
}
return (
return (<>
{this.renderWaveformArea()}
<AccessibleTooltipButton
className={classes}
onClick={this.onStartStopVoiceMessage}
title={tooltip}
/>
);
</>);
}
}

View file

@ -28,13 +28,12 @@ import Modal from "../../../Modal";
import PassphraseField from "../auth/PassphraseField";
import CountlyAnalytics from "../../../CountlyAnalytics";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { PASSWORD_MIN_SCORE } from '../auth/RegistrationForm';
const FIELD_OLD_PASSWORD = 'field_old_password';
const FIELD_NEW_PASSWORD = 'field_new_password';
const FIELD_NEW_PASSWORD_CONFIRM = 'field_new_password_confirm';
const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from offline slow-hash scenario.
@replaceableComponent("views.settings.ChangePassword")
export default class ChangePassword extends React.Component {
static propTypes = {

View file

@ -26,6 +26,7 @@ import {formatBytes, formatCountLong} from "../../../utils/FormattingUtils";
import EventIndexPeg from "../../../indexing/EventIndexPeg";
import {SettingLevel} from "../../../settings/SettingLevel";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import SeshatResetDialog from '../dialogs/SeshatResetDialog';
@replaceableComponent("views.settings.EventIndexPanel")
export default class EventIndexPanel extends React.Component {
@ -122,6 +123,20 @@ export default class EventIndexPanel extends React.Component {
await this.updateState();
}
_confirmEventStoreReset = () => {
const self = this;
const { close } = Modal.createDialog(SeshatResetDialog, {
onFinished: async (success) => {
if (success) {
await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false);
await EventIndexPeg.deleteEventIndex();
await self._onEnable();
close();
}
},
});
}
render() {
let eventIndexingSettings = null;
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
@ -167,7 +182,7 @@ export default class EventIndexPanel extends React.Component {
);
} else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) {
const nativeLink = (
"https://github.com/vector-im/element-web/blob/develop/" +
"https://github.com/vector-im/element-desktop/blob/develop/" +
"docs/native-node-modules.md#" +
"adding-seshat-for-search-in-e2e-encrypted-rooms"
);
@ -212,7 +227,10 @@ export default class EventIndexPanel extends React.Component {
eventIndexingSettings = (
<div className='mx_SettingsTab_subsectionText'>
<p>
{_t("Message search initilisation failed")}
{this.state.enabling
? <InlineSpinner />
: _t("Message search initilisation failed")
}
</p>
{EventIndexPeg.error && (
<details>
@ -220,6 +238,11 @@ export default class EventIndexPanel extends React.Component {
<code>
{EventIndexPeg.error.message}
</code>
<p>
<AccessibleButton key="delete" kind="danger" onClick={this._confirmEventStoreReset}>
{_t("Reset")}
</AccessibleButton>
</p>
</details>
)}

View file

@ -1,5 +1,5 @@
/*
Copyright 2019 New Vector Ltd
Copyright 2019, 2021 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,17 +22,19 @@ import * as sdk from "../../../../..";
import AccessibleButton from "../../../elements/AccessibleButton";
import Modal from "../../../../../Modal";
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
import {EventType} from "matrix-js-sdk/src/@types/event";
const plEventsToLabels = {
// These will be translated for us later.
"m.room.avatar": _td("Change room avatar"),
"m.room.name": _td("Change room name"),
"m.room.canonical_alias": _td("Change main address for the room"),
"m.room.history_visibility": _td("Change history visibility"),
"m.room.power_levels": _td("Change permissions"),
"m.room.topic": _td("Change topic"),
"m.room.tombstone": _td("Upgrade the room"),
"m.room.encryption": _td("Enable room encryption"),
[EventType.RoomAvatar]: _td("Change room avatar"),
[EventType.RoomName]: _td("Change room name"),
[EventType.RoomCanonicalAlias]: _td("Change main address for the room"),
[EventType.RoomHistoryVisibility]: _td("Change history visibility"),
[EventType.RoomPowerLevels]: _td("Change permissions"),
[EventType.RoomTopic]: _td("Change topic"),
[EventType.RoomTombstone]: _td("Upgrade the room"),
[EventType.RoomEncryption]: _td("Enable room encryption"),
[EventType.RoomServerAcl]: _td("Change server ACLs"),
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
"im.vector.modular.widgets": _td("Modify widgets"),
@ -40,14 +42,15 @@ const plEventsToLabels = {
const plEventsToShow = {
// If an event is listed here, it will be shown in the PL settings. Defaults will be calculated.
"m.room.avatar": {isState: true},
"m.room.name": {isState: true},
"m.room.canonical_alias": {isState: true},
"m.room.history_visibility": {isState: true},
"m.room.power_levels": {isState: true},
"m.room.topic": {isState: true},
"m.room.tombstone": {isState: true},
"m.room.encryption": {isState: true},
[EventType.RoomAvatar]: {isState: true},
[EventType.RoomName]: {isState: true},
[EventType.RoomCanonicalAlias]: {isState: true},
[EventType.RoomHistoryVisibility]: {isState: true},
[EventType.RoomPowerLevels]: {isState: true},
[EventType.RoomTopic]: {isState: true},
[EventType.RoomTombstone]: {isState: true},
[EventType.RoomEncryption]: {isState: true},
[EventType.RoomServerAcl]: {isState: true},
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
"im.vector.modular.widgets": {isState: true},

View file

@ -18,6 +18,7 @@ limitations under the License.
import React from 'react';
import {_t} from "../../../../../languageHandler";
import SdkConfig from "../../../../../SdkConfig";
import { MatrixClientPeg } from '../../../../../MatrixClientPeg';
import SettingsStore from "../../../../../settings/SettingsStore";
import { enumerateThemes } from "../../../../../theme";
import ThemeWatcher from "../../../../../settings/watchers/ThemeWatcher";
@ -63,6 +64,10 @@ interface IState extends IThemeState {
systemFont: string;
showAdvanced: boolean;
layout: Layout;
// User profile data for the message preview
userId: string;
displayName: string;
avatarUrl: string;
}
@replaceableComponent("views.settings.tabs.user.AppearanceUserSettingsTab")
@ -84,9 +89,25 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
systemFont: SettingsStore.getValue("systemFont"),
showAdvanced: false,
layout: SettingsStore.getValue("layout"),
userId: "@erim:fink.fink",
displayName: "Erimayas Fink",
avatarUrl: null,
};
}
async componentDidMount() {
// Fetch the current user profile for the message preview
const client = MatrixClientPeg.get();
const userId = client.getUserId();
const profileInfo = await client.getProfileInfo(userId);
this.setState({
userId,
displayName: profileInfo.displayname,
avatarUrl: profileInfo.avatar_url,
});
}
private calculateThemeState(): IThemeState {
// We have to mirror the logic from ThemeWatcher.getEffectiveTheme so we
// show the right values for things.
@ -307,6 +328,9 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
className="mx_AppearanceUserSettingsTab_fontSlider_preview"
message={this.MESSAGE_PREVIEW_TEXT}
layout={this.state.layout}
userId={this.state.userId}
displayName={this.state.displayName}
avatarUrl={this.state.avatarUrl}
/>
<div className="mx_AppearanceUserSettingsTab_fontSlider">
<div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div>

View file

@ -74,6 +74,8 @@ export default class PreferencesUserSettingsTab extends React.Component {
this.state = {
autoLaunch: false,
autoLaunchSupported: false,
warnBeforeExit: true,
warnBeforeExitSupported: false,
alwaysShowMenuBar: true,
alwaysShowMenuBarSupported: false,
minimizeToTray: true,
@ -96,6 +98,12 @@ export default class PreferencesUserSettingsTab extends React.Component {
autoLaunch = await platform.getAutoLaunchEnabled();
}
const warnBeforeExitSupported = await platform.supportsWarnBeforeExit();
let warnBeforeExit = false;
if (warnBeforeExitSupported) {
warnBeforeExit = await platform.shouldWarnBeforeExit();
}
const alwaysShowMenuBarSupported = await platform.supportsAutoHideMenuBar();
let alwaysShowMenuBar = true;
if (alwaysShowMenuBarSupported) {
@ -111,6 +119,8 @@ export default class PreferencesUserSettingsTab extends React.Component {
this.setState({
autoLaunch,
autoLaunchSupported,
warnBeforeExit,
warnBeforeExitSupported,
alwaysShowMenuBarSupported,
alwaysShowMenuBar,
minimizeToTraySupported,
@ -122,6 +132,10 @@ export default class PreferencesUserSettingsTab extends React.Component {
PlatformPeg.get().setAutoLaunchEnabled(checked).then(() => this.setState({autoLaunch: checked}));
};
_onWarnBeforeExitChange = (checked) => {
PlatformPeg.get().setWarnBeforeExit(checked).then(() => this.setState({warnBeforeExit: checked}));
}
_onAlwaysShowMenuBarChange = (checked) => {
PlatformPeg.get().setAutoHideMenuBarEnabled(!checked).then(() => this.setState({alwaysShowMenuBar: checked}));
};
@ -161,6 +175,14 @@ export default class PreferencesUserSettingsTab extends React.Component {
label={_t('Start automatically after system login')} />;
}
let warnBeforeExitOption = null;
if (this.state.warnBeforeExitSupported) {
warnBeforeExitOption = <LabelledToggleSwitch
value={this.state.warnBeforeExit}
onChange={this._onWarnBeforeExitChange}
label={_t('Warn before quitting')} />;
}
let autoHideMenuOption = null;
if (this.state.alwaysShowMenuBarSupported) {
autoHideMenuOption = <LabelledToggleSwitch
@ -202,6 +224,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
{minimizeToTrayOption}
{autoHideMenuOption}
{autoLaunchOption}
{warnBeforeExitOption}
<Field
label={_t('Autocomplete delay (ms)')}
type='number'

View file

@ -0,0 +1,42 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps {
seconds: number;
}
interface IState {
}
/**
* Simply converts seconds into minutes and seconds. Note that hours will not be
* displayed, making it possible to see "82:29".
*/
@replaceableComponent("views.voice_messages.Clock")
export default class Clock extends React.PureComponent<IProps, IState> {
public constructor(props) {
super(props);
}
public render() {
const minutes = Math.floor(this.props.seconds / 60).toFixed(0).padStart(2, '0');
const seconds = Math.round(this.props.seconds % 60).toFixed(0).padStart(2, '0'); // hide millis
return <span className='mx_Clock'>{minutes}:{seconds}</span>;
}
}

View file

@ -0,0 +1,55 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import {IRecordingUpdate, VoiceRecording} from "../../../voice/VoiceRecording";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import Clock from "./Clock";
interface IProps {
recorder: VoiceRecording;
}
interface IState {
seconds: number;
}
/**
* A clock for a live recording.
*/
@replaceableComponent("views.voice_messages.LiveRecordingClock")
export default class LiveRecordingClock extends React.Component<IProps, IState> {
public constructor(props) {
super(props);
this.state = {seconds: 0};
this.props.recorder.liveData.onUpdate(this.onRecordingUpdate);
}
shouldComponentUpdate(nextProps: Readonly<IProps>, nextState: Readonly<IState>, nextContext: any): boolean {
const currentFloor = Math.floor(this.state.seconds);
const nextFloor = Math.floor(nextState.seconds);
return currentFloor !== nextFloor;
}
private onRecordingUpdate = (update: IRecordingUpdate) => {
this.setState({seconds: update.timeSeconds});
};
public render() {
return <Clock seconds={this.state.seconds} />;
}
}

View file

@ -0,0 +1,62 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import {IRecordingUpdate, VoiceRecording} from "../../../voice/VoiceRecording";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {arrayFastResample, arraySeed} from "../../../utils/arrays";
import {percentageOf} from "../../../utils/numbers";
import Waveform from "./Waveform";
interface IProps {
recorder: VoiceRecording;
}
interface IState {
heights: number[];
}
const DOWNSAMPLE_TARGET = 35; // number of bars we want
/**
* A waveform which shows the waveform of a live recording
*/
@replaceableComponent("views.voice_messages.LiveRecordingWaveform")
export default class LiveRecordingWaveform extends React.PureComponent<IProps, IState> {
public constructor(props) {
super(props);
this.state = {heights: arraySeed(0, DOWNSAMPLE_TARGET)};
this.props.recorder.liveData.onUpdate(this.onRecordingUpdate);
}
private onRecordingUpdate = (update: IRecordingUpdate) => {
// The waveform and the downsample target are pretty close, so we should be fine to
// do this, despite the docs on arrayFastResample.
const bars = arrayFastResample(Array.from(update.waveform), DOWNSAMPLE_TARGET);
this.setState({
// The incoming data is between zero and one, but typically even screaming into a
// microphone won't send you over 0.6, so we artificially adjust the gain for the
// waveform. This results in a slightly more cinematic/animated waveform for the
// user.
heights: bars.map(b => percentageOf(b, 0, 0.50)),
});
};
public render() {
return <Waveform relHeights={this.state.heights} />;
}
}

View file

@ -0,0 +1,45 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps {
relHeights: number[]; // relative heights (0-1)
}
interface IState {
}
/**
* A simple waveform component. This renders bars (centered vertically) for each
* height provided in the component properties. Updating the properties will update
* the rendered waveform.
*/
@replaceableComponent("views.voice_messages.Waveform")
export default class Waveform extends React.PureComponent<IProps, IState> {
public constructor(props) {
super(props);
}
public render() {
return <div className='mx_Waveform'>
{this.props.relHeights.map((h, i) => {
return <span key={i} style={{height: (h * 100) + '%'}} className='mx_Waveform_bar' />;
})}
</div>;
}
}

View file

@ -40,9 +40,6 @@ interface IProps {
// Another ongoing call to display information about
secondaryCall?: MatrixCall,
// maxHeight style attribute for the video panel
maxVideoHeight?: number;
// a callback which is called when the content in the callview changes
// in a way that is likely to cause a resize.
onResize?: any;
@ -96,9 +93,6 @@ function exitFullscreen() {
const CONTROLS_HIDE_DELAY = 1000;
// Height of the header duplicated from CSS because we need to subtract it from our max
// height to get the max height of the video
const HEADER_HEIGHT = 44;
const BOTTOM_PADDING = 10;
const BOTTOM_MARGIN_TOP_BOTTOM = 10; // top margin plus bottom margin
const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px)
@replaceableComponent("views.voip.CallView")
@ -364,6 +358,11 @@ export default class CallView extends React.Component<IProps, IState> {
CallHandler.sharedInstance().setActiveCallRoomId(userFacingRoomId);
}
private onTransferClick = () => {
const transfereeCall = CallHandler.sharedInstance().getTransfereeForCallId(this.props.call.callId);
this.props.call.transferToCall(transfereeCall);
}
public render() {
const client = MatrixClientPeg.get();
const callRoomId = CallHandler.roomIdForCall(this.props.call);
@ -479,25 +478,52 @@ export default class CallView extends React.Component<IProps, IState> {
// for voice calls (fills the bg)
let contentView: React.ReactNode;
const transfereeCall = CallHandler.sharedInstance().getTransfereeForCallId(this.props.call.callId);
const isOnHold = this.state.isLocalOnHold || this.state.isRemoteOnHold;
let onHoldText = null;
if (this.state.isRemoteOnHold) {
const holdString = CallHandler.sharedInstance().hasAnyUnheldCall() ?
_td("You held the call <a>Switch</a>") : _td("You held the call <a>Resume</a>");
onHoldText = _t(holdString, {}, {
a: sub => <AccessibleButton kind="link" onClick={this.onCallResumeClick}>
{sub}
</AccessibleButton>,
});
} else if (this.state.isLocalOnHold) {
onHoldText = _t("%(peerName)s held the call", {
peerName: this.props.call.getOpponentMember().name,
});
let holdTransferContent;
if (transfereeCall) {
const transferTargetRoom = MatrixClientPeg.get().getRoom(CallHandler.roomIdForCall(this.props.call));
const transferTargetName = transferTargetRoom ? transferTargetRoom.name : _t("unknown person");
const transfereeRoom = MatrixClientPeg.get().getRoom(
CallHandler.roomIdForCall(transfereeCall),
);
const transfereeName = transfereeRoom ? transfereeRoom.name : _t("unknown person");
holdTransferContent = <div className="mx_CallView_holdTransferContent">
{_t(
"Consulting with %(transferTarget)s. <a>Transfer to %(transferee)s</a>",
{
transferTarget: transferTargetName,
transferee: transfereeName,
},
{
a: sub => <AccessibleButton kind="link" onClick={this.onTransferClick}>{sub}</AccessibleButton>,
},
)}
</div>;
} else if (isOnHold) {
let onHoldText = null;
if (this.state.isRemoteOnHold) {
const holdString = CallHandler.sharedInstance().hasAnyUnheldCall() ?
_td("You held the call <a>Switch</a>") : _td("You held the call <a>Resume</a>");
onHoldText = _t(holdString, {}, {
a: sub => <AccessibleButton kind="link" onClick={this.onCallResumeClick}>
{sub}
</AccessibleButton>,
});
} else if (this.state.isLocalOnHold) {
onHoldText = _t("%(peerName)s held the call", {
peerName: this.props.call.getOpponentMember().name,
});
}
holdTransferContent = <div className="mx_CallView_holdTransferContent">
{onHoldText}
</div>;
}
if (this.props.call.type === CallType.Video) {
let localVideoFeed = null;
let onHoldContent = null;
let onHoldBackground = null;
const backgroundStyle: CSSProperties = {};
const containerClasses = classNames({
@ -505,9 +531,6 @@ export default class CallView extends React.Component<IProps, IState> {
mx_CallView_video_hold: isOnHold,
});
if (isOnHold) {
onHoldContent = <div className="mx_CallView_video_holdContent">
{onHoldText}
</div>;
const backgroundAvatarUrl = avatarUrlForMember(
// is it worth getting the size of the div to pass here?
this.props.call.getOpponentMember(), 1024, 1024, 'crop',
@ -519,22 +542,11 @@ export default class CallView extends React.Component<IProps, IState> {
localVideoFeed = <VideoFeed type={VideoFeedType.Local} call={this.props.call} />;
}
// if we're fullscreen, we don't want to set a maxHeight on the video element.
const maxVideoHeight = getFullScreenElement() || !this.props.maxVideoHeight ? null : (
this.props.maxVideoHeight - (HEADER_HEIGHT + BOTTOM_PADDING + BOTTOM_MARGIN_TOP_BOTTOM)
);
contentView = <div className={containerClasses}
ref={this.contentRef} onMouseMove={this.onMouseMove}
// Put the max height on here too because this div is ended up 4px larger than the content
// and is causing it to scroll, and I am genuinely baffled as to why.
style={{maxHeight: maxVideoHeight}}
>
contentView = <div className={containerClasses} ref={this.contentRef} onMouseMove={this.onMouseMove}>
{onHoldBackground}
<VideoFeed type={VideoFeedType.Remote} call={this.props.call} onResize={this.props.onResize}
maxHeight={maxVideoHeight}
/>
<VideoFeed type={VideoFeedType.Remote} call={this.props.call} onResize={this.props.onResize} />
{localVideoFeed}
{onHoldContent}
{holdTransferContent}
{callControls}
</div>;
} else {
@ -554,7 +566,7 @@ export default class CallView extends React.Component<IProps, IState> {
/>
</div>
</div>
<div className="mx_CallView_voice_holdText">{onHoldText}</div>
{holdTransferContent}
{callControls}
</div>;
}

View file

@ -19,6 +19,8 @@ import React from 'react';
import CallHandler from '../../../CallHandler';
import CallView from './CallView';
import dis from '../../../dispatcher/dispatcher';
import {Resizable} from "re-resizable";
import ResizeNotifier from "../../../utils/ResizeNotifier";
import {replaceableComponent} from "../../../utils/replaceableComponent";
interface IProps {
@ -28,9 +30,7 @@ interface IProps {
// maxHeight style attribute for the video panel
maxVideoHeight?: number;
// a callback which is called when the content in the callview changes
// in a way that is likely to cause a resize.
onResize?: any;
resizeNotifier: ResizeNotifier,
}
interface IState {
@ -79,11 +79,50 @@ export default class CallViewForRoom extends React.Component<IProps, IState> {
return call;
}
private onResizeStart = () => {
this.props.resizeNotifier.startResizing();
};
private onResize = () => {
this.props.resizeNotifier.notifyTimelineHeightChanged();
};
private onResizeStop = () => {
this.props.resizeNotifier.stopResizing();
};
public render() {
if (!this.state.call) return null;
// We subtract 8 as it the margin-bottom of the mx_CallViewForRoom_ResizeWrapper
const maxHeight = this.props.maxVideoHeight - 8;
return <CallView call={this.state.call} pipMode={false}
onResize={this.props.onResize} maxVideoHeight={this.props.maxVideoHeight}
/>;
return (
<div className="mx_CallViewForRoom">
<Resizable
minHeight={380}
maxHeight={maxHeight}
enable={{
top: false,
right: false,
bottom: true,
left: false,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false,
}}
onResizeStart={this.onResizeStart}
onResize={this.onResize}
onResizeStop={this.onResizeStop}
className="mx_CallViewForRoom_ResizeWrapper"
handleClasses={{bottom: "mx_CallViewForRoom_ResizeHandle"}}
>
<CallView
call={this.state.call}
pipMode={false}
/>
</Resizable>
</div>
);
}
}

View file

@ -30,9 +30,6 @@ interface IProps {
type: VideoFeedType,
// maxHeight style attribute for the video element
maxHeight?: number,
// a callback which is called when the video element is resized
// due to a change in video metadata
onResize?: (e: Event) => void,
@ -82,9 +79,6 @@ export default class VideoFeed extends React.Component<IProps> {
),
};
let videoStyle = {};
if (this.props.maxHeight) videoStyle = { maxHeight: this.props.maxHeight };
return <video className={classnames(videoClasses)} ref={this.vid} style={videoStyle} />;
return <video className={classnames(videoClasses)} ref={this.vid} />;
}
}

View file

@ -90,6 +90,12 @@ export interface IOpts {
parentSpace?: Room;
}
export interface IInvite3PID {
id_server: string,
medium: 'email',
address: string,
}
/**
* Create a new room, and switch to it.
*

View file

@ -74,8 +74,20 @@ export interface ISecurityCustomisations {
catchAccessSecretStorageError?: typeof catchAccessSecretStorageError,
setupEncryptionNeeded?: typeof setupEncryptionNeeded,
getDehydrationKey?: typeof getDehydrationKey,
/**
* When false, disables the post-login UI from showing. If there's
* an error during setup, that will be shown to the user.
*
* Note: when this is set to false then the app will assume the user's
* encryption is set up some other way which would circumvent the default
* UI, such as by presenting alternative UI.
*/
SHOW_ENCRYPTION_SETUP_UI?: boolean, // default true
}
// A real customisation module will define and export one or more of the
// customisation points that make up `ISecurityCustomisations`.
export default {} as ISecurityCustomisations;
export default {
SHOW_ENCRYPTION_SETUP_UI: true,
} as ISecurityCustomisations;

View file

@ -68,24 +68,24 @@ export default class AutocompleteWrapperModel {
this.updateCallback({close: true});
}
public async onTab(e: KeyboardEvent) {
/**
* If there is no current autocompletion, start one and move to the first selection.
*/
public async startSelection() {
const acComponent = this.getAutocompleterComponent();
if (acComponent.countCompletions() === 0) {
// Force completions to show for the text currently entered
await acComponent.forceComplete();
// Select the first item by moving "down"
await acComponent.moveSelection(+1);
} else {
await acComponent.moveSelection(e.shiftKey ? -1 : +1);
}
}
public onUpArrow(e: KeyboardEvent) {
public selectPreviousSelection() {
this.getAutocompleterComponent().moveSelection(-1);
}
public onDownArrow(e: KeyboardEvent) {
public selectNextSelection() {
this.getAutocompleterComponent().moveSelection(+1);
}

View file

@ -143,11 +143,11 @@ function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLEl
// math nodes are translated back into delimited latex strings
if (n.hasAttribute("data-mx-maths")) {
const delimLeft = (n.nodeName == "SPAN") ?
(SdkConfig.get()['latex_maths_delims'] || {})['inline_left'] || "$" :
(SdkConfig.get()['latex_maths_delims'] || {})['display_left'] || "$$";
((SdkConfig.get()['latex_maths_delims'] || {})['inline'] || {})['left'] || "\\(" :
((SdkConfig.get()['latex_maths_delims'] || {})['display'] || {})['left'] || "\\[";
const delimRight = (n.nodeName == "SPAN") ?
(SdkConfig.get()['latex_maths_delims'] || {})['inline_right'] || "$" :
(SdkConfig.get()['latex_maths_delims'] || {})['display_right'] || "$$";
((SdkConfig.get()['latex_maths_delims'] || {})['inline'] || {})['right'] || "\\)" :
((SdkConfig.get()['latex_maths_delims'] || {})['display'] || {})['right'] || "\\]";
const tex = n.getAttribute("data-mx-maths");
return partCreator.plain(delimLeft + tex + delimRight);
} else if (!checkDescendInto(n)) {

View file

@ -47,21 +47,65 @@ export function mdSerialize(model: EditorModel) {
export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) {
let md = mdSerialize(model);
// copy of raw input to remove unwanted math later
const orig = md;
if (SettingsStore.getValue("feature_latex_maths")) {
const displayPattern = (SdkConfig.get()['latex_maths_delims'] || {})['display_pattern'] ||
"\\$\\$(([^$]|\\\\\\$)*)\\$\\$";
const inlinePattern = (SdkConfig.get()['latex_maths_delims'] || {})['inline_pattern'] ||
"\\$(([^$]|\\\\\\$)*)\\$";
const patternNames = ['tex', 'latex'];
const patternTypes = ['display', 'inline'];
const patternDefaults = {
"tex": {
// detect math with tex delimiters, inline: $...$, display $$...$$
// preferably use negative lookbehinds, not supported in all major browsers:
// const displayPattern = "^(?<!\\\\)\\$\\$(?![ \\t])(([^$]|\\\\\\$)+?)\\$\\$$";
// const inlinePattern = "(?:^|\\s)(?<!\\\\)\\$(?!\\s)(([^$]|\\\\\\$)+?)(?<!\\\\|\\s)\\$";
md = md.replace(RegExp(displayPattern, "gm"), function(m, p1) {
const p1e = AllHtmlEntities.encode(p1);
return `<div data-mx-maths="${p1e}">\n\n</div>\n\n`;
});
// conditions for display math detection $$...$$:
// - pattern starts at beginning of line or is not prefixed with backslash or dollar
// - left delimiter ($$) is not escaped by backslash
"display": "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$",
md = md.replace(RegExp(inlinePattern, "gm"), function(m, p1) {
const p1e = AllHtmlEntities.encode(p1);
return `<span data-mx-maths="${p1e}"></span>`;
// conditions for inline math detection $...$:
// - pattern starts at beginning of line, follows whitespace character or punctuation
// - pattern is on a single line
// - left and right delimiters ($) are not escaped by backslashes
// - left delimiter is not followed by whitespace character
// - right delimiter is not prefixed with whitespace character
"inline":
"(^|\\s|[.,!?:;])(?!\\\\)\\$(?!\\s)(([^$\\n]|\\\\\\$)*([^\\\\\\s\\$]|\\\\\\$)(?:\\\\\\$)?)\\$",
},
"latex": {
// detect math with latex delimiters, inline: \(...\), display \[...\]
// conditions for display math detection \[...\]:
// - pattern starts at beginning of line or is not prefixed with backslash
// - pattern is not empty
"display": "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]",
// conditions for inline math detection \(...\):
// - pattern starts at beginning of line or is not prefixed with backslash
// - pattern is not empty
"inline": "(^|[^\\\\])\\\\\\((?!\\\\\\))(.*?)\\\\\\)",
},
};
patternNames.forEach(function(patternName) {
patternTypes.forEach(function(patternType) {
// get the regex replace pattern from config or use the default
const pattern = (((SdkConfig.get()["latex_maths_delims"] ||
{})[patternType] || {})["pattern"] || {})[patternName] ||
patternDefaults[patternName][patternType];
md = md.replace(RegExp(pattern, "gms"), function(m, p1, p2) {
const p2e = AllHtmlEntities.encode(p2);
switch (patternType) {
case "display":
return `${p1}<div data-mx-maths="${p2e}">\n\n</div>\n\n`;
case "inline":
return `${p1}<span data-mx-maths="${p2e}"></span>`;
}
});
});
});
// make sure div tags always start on a new line, otherwise it will confuse
@ -73,15 +117,29 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} =
if (!parser.isPlainText() || forceHTML) {
// feed Markdown output to HTML parser
const phtml = cheerio.load(parser.toHTML(),
{ _useHtmlParser2: true, decodeEntities: false })
{ _useHtmlParser2: true, decodeEntities: false });
// add fallback output for latex math, which should not be interpreted as markdown
phtml('div, span').each(function(i, e) {
const tex = phtml(e).attr('data-mx-maths')
if (tex) {
phtml(e).html(`<code>${tex}</code>`)
}
});
if (SettingsStore.getValue("feature_latex_maths")) {
// original Markdown without LaTeX replacements
const parserOrig = new Markdown(orig);
const phtmlOrig = cheerio.load(parserOrig.toHTML(),
{ _useHtmlParser2: true, decodeEntities: false });
// since maths delimiters are handled before Markdown,
// code blocks could contain mangled content.
// replace code blocks with original content
phtmlOrig('code').each(function(i) {
phtml('code').eq(i).text(phtmlOrig('code').eq(i).text());
});
// add fallback output for latex math, which should not be interpreted as markdown
phtml('div, span').each(function(i, e) {
const tex = phtml(e).attr('data-mx-maths')
if (tex) {
phtml(e).html(`<code>${tex}</code>`)
}
});
}
return phtml.html();
}
// ensure removal of escape backslashes in non-Markdown messages

View file

@ -14,7 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
const EMAIL_ADDRESS_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
// Regexp based on Simpler Version from https://gist.github.com/gregseth/5582254 - matches RFC2822
const EMAIL_ADDRESS_REGEX = new RegExp(
"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*" + // localpart
"@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", "i");
export function looksValid(email: string): boolean {
return EMAIL_ADDRESS_REGEX.test(email);

View file

@ -748,7 +748,7 @@
"Chat with %(brand)s Bot": "تخاطب مع الروبوت الخاص ب%(brand)s",
"For help with using %(brand)s, click <a>here</a> or start a chat with our bot using the button below.": "للمساعدة في استخدام %(brand)s ، انقر <a> هنا </a> أو ابدأ محادثة مع برنامج الروبوت الخاص بنا باستخدام الزر أدناه.",
"For help with using %(brand)s, click <a>here</a>.": "للمساعدة في استخدام %(brand)s انقر <a>هنا</a>.",
"Credits": "أمانة",
"Credits": "رصيد",
"Legal": "قانوني",
"General": "عام",
"Discovery": "الاكتشاف",
@ -1488,5 +1488,68 @@
"Call failed because no webcam or microphone could not be accessed. Check that:": "فشلت المكالمة نظرًا لتعذر الوصول إلى كاميرا الويب أو الميكروفون. تحقق مما يلي:",
"Unable to access webcam / microphone": "تعذر الوصول إلى كاميرا الويب / الميكروفون",
"Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "فشلت المكالمة لأنه لا يمكن الوصول إلى ميكروفون. تحقق من توصيل الميكروفون وإعداده بشكل صحيح.",
"Unable to access microphone": "تعذر الوصول إلى الميكروفون"
"Unable to access microphone": "تعذر الوصول إلى الميكروفون",
"Cuba": "كوبا",
"Croatia": "كرواتيا",
"Costa Rica": "كوستا ريكا",
"Cook Islands": "جزر كوك",
"Congo - Kinshasa": "الكونغو - كينشاسا",
"Congo - Brazzaville": "الكونغو - برازافيل",
"Comoros": "جزر القمر",
"Colombia": "كولومبيا",
"Cocos (Keeling) Islands": "جزر كوكوس (كيلينغ)",
"Christmas Island": "جزيرة الكريسماس",
"China": "الصين",
"Chile": "تشيلي",
"Chad": "تشاد",
"Central African Republic": "جمهورية افريقيا الوسطى",
"Cayman Islands": "جزر كايمان",
"Caribbean Netherlands": "هولندا الكاريبية",
"Cape Verde": "الرأس الأخضر",
"Canada": "كندا",
"Cameroon": "الكاميرون",
"Cambodia": "كمبوديا",
"Burundi": "بوروندي",
"Burkina Faso": "بوركينا فاسو",
"Bulgaria": "بلغاريا",
"Brunei": "بروناي",
"British Virgin Islands": "جزر فيرجن البريطانية",
"British Indian Ocean Territory": "إقليم المحيط البريطاني الهندي",
"Brazil": "البرازيل",
"Bouvet Island": "جزيرة بوفيت",
"Botswana": "بوتسوانا",
"Bosnia": "البوسنة",
"Bolivia": "بوليفيا",
"Bhutan": "بوتان",
"Bermuda": "برمودا",
"Benin": "بنين",
"Belize": "بليز",
"Belgium": "بلجيكا",
"Belarus": "بيلاروسيا",
"Barbados": "بربادوس",
"Bangladesh": "بنغلاديش",
"Bahrain": "البحرين",
"Bahamas": "جزر البهاما",
"Azerbaijan": "أذربيجان",
"Austria": "النمسا",
"Australia": "أستراليا",
"Aruba": "أروبا",
"Armenia": "أرمينيا",
"Argentina": "الأرجنتين",
"Antigua & Barbuda": "أنتيغوا وبربودا",
"Antarctica": "أنتاركتيكا",
"Anguilla": "أنغيلا",
"Angola": "انجولا",
"Andorra": "أندورا",
"American Samoa": "ساموا الأمريكية",
"Algeria": "الجزائر",
"Åland Islands": "جزر آلاند",
"Try again": "حاول مجددا",
"We couldn't log you in": "لا يمكننا تسجيل دخولك",
"You're already in a call with this person.": "انت بالفعل في مكالمة مع هذا الشخص.",
"Already in call": "في مكالمة بالفعل",
"You've reached the maximum number of simultaneous calls.": "لقد وصلت للحد الاقصى من المكالمات المتزامنة.",
"Too Many Calls": "مكالمات كثيرة جدا",
"Call failed because webcam or microphone could not be accessed. Check that:": "فشلت المكالمة لعدم امكانية الوصل للميكروفون او الكاميرا , من فضلك قم بالتأكد.",
"Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "فشلت المكالمة لعدم امكانية الوصل للميكروفون , تأكد من ان المكروفون متصل وتم اعداده بشكل صحيح."
}

View file

@ -469,8 +469,8 @@
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)svstoupil(a)",
"%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s %(count)s krát opustili",
"%(severalUsers)sleft %(count)s times|one": "%(severalUsers)sopustili",
"%(oneUser)sleft %(count)s times|other": "%(oneUser)s %(count)s krát opustil",
"%(oneUser)sleft %(count)s times|one": "%(oneUser)sopustil",
"%(oneUser)sleft %(count)s times|other": "%(oneUser)s %(count)s krát opustil(a)",
"%(oneUser)sleft %(count)s times|one": "%(oneUser)sopustil(a)",
"%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s %(count)s krát vstoupili a opustili",
"%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)svstoupili a opustili",
"%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s %(count)s krát vstoupil(a) a opustil(a)",
@ -478,7 +478,7 @@
"%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s %(count)s krát opustili a znovu vstoupili",
"%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)sopustili a znovu vstoupili",
"%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s %(count)s krát opustil(a) a znovu vstoupil(a)",
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)sopustil a znovu vstoupil",
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)sopustil(a) a znovu vstoupil(a)",
"%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s %(count)s krát odmítli pozvání",
"%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)sodmítli pozvání",
"%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)s %(count)s krát odmítl pozvání",
@ -491,14 +491,14 @@
"were invited %(count)s times|one": "byli pozváni",
"was invited %(count)s times|other": "byl %(count)s krát pozván",
"was invited %(count)s times|one": "byl(a) pozván(a)",
"were banned %(count)s times|other": "mělid %(count)s krát zakázaný vstup",
"were banned %(count)s times|one": "měli zakázaný vstup",
"was banned %(count)s times|other": "měl %(count)s krát zakázaný vstup",
"was banned %(count)s times|one": "měl zakázaný vstup",
"were unbanned %(count)s times|other": "měli %(count)s krát povolený vstup",
"were unbanned %(count)s times|one": "měli povolený vstup",
"was unbanned %(count)s times|other": "měl %(count)s krát povolený vstup",
"was unbanned %(count)s times|one": "měl povolený vstup",
"were banned %(count)s times|other": "byli %(count)s krát vykázáni",
"were banned %(count)s times|one": "byl(a) vykázán(a)",
"was banned %(count)s times|other": "byli %(count)s krát vykázáni",
"was banned %(count)s times|one": "byl(a) vykázán(a)",
"were unbanned %(count)s times|other": "byli %(count)s přijati zpět",
"were unbanned %(count)s times|one": "byl(a) přijat(a) zpět",
"was unbanned %(count)s times|other": "byli %(count)s krát přijati zpět",
"was unbanned %(count)s times|one": "byl(a) přijat(a) zpět",
"were kicked %(count)s times|other": "byli %(count)s krát vyhozeni",
"were kicked %(count)s times|one": "byli vyhozeni",
"was kicked %(count)s times|other": "byl %(count)s krát vyhozen",
@ -1280,7 +1280,7 @@
"Invited by %(sender)s": "Pozván od uživatele %(sender)s",
"Error updating flair": "Nepovedlo se změnit příslušnost ke skupině",
"There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "Pro tuto místnost se nepovedlo změnit příslušnost ke skupině. Možná to server neumožňuje, nebo došlo k dočasné chybě.",
"<reactors/><reactedWith>reacted with %(shortName)s</reactedWith>": "<reactors/><reactedWith> reagoval(a) s %(shortName)s</reactedWith>",
"<reactors/><reactedWith>reacted with %(shortName)s</reactedWith>": "<reactors/><reactedWith> reagoval(a) %(shortName)s</reactedWith>",
"edited": "upraveno",
"Maximize apps": "Maximalizovat aplikace",
"Rotate Left": "Otočit doleva",
@ -1663,7 +1663,7 @@
"Verify": "Ověřit",
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>": "Tohoto uživatele ignorujete, takže jsou jeho zprávy skryté. <a>Přesto zobrazit.</a>",
"Reactions": "Reakce",
"<reactors/><reactedWith> reacted with %(content)s</reactedWith>": "<reactors/><reactedWith> reagoval %(content)s</reactedWith>",
"<reactors/><reactedWith> reacted with %(content)s</reactedWith>": "<reactors/><reactedWith> reagoval(a) %(content)s</reactedWith>",
"Any of the following data may be shared:": "Následující data můžou být sdílena:",
"Your display name": "Vaše zobrazované jméno",
"Your avatar URL": "URL vašeho avataru",
@ -3118,5 +3118,52 @@
"Welcome to <name/>": "Vítejte v <name/>",
"Support": "Podpora",
"Room name": "Název místnosti",
"Finish": "Dokončit"
"Finish": "Dokončit",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Toto obvykle ovlivňuje pouze to, jak je místnost zpracována na serveru. Pokud máte problémy s %(brand)s, nahlaste prosím chybu.",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Pro každého z nich vytvoříme místnost. Později můžete přidat další, včetně již existujících.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Vytvořme místnost pro každého z nich. Později můžete přidat i další, včetně již existujících.",
"Make sure the right people have access. You can invite more later.": "Zajistěte přístup pro správné lidi. Další můžete pozvat později.",
"A private space to organise your rooms": "Soukromý space pro uspořádání vašich místností",
"Make sure the right people have access to %(name)s": "Zajistěte, aby do %(name)s měli přístup správní lidé",
"Go to my first room": "Jít do mé první místnosti",
"It's just you at the moment, it will be even better with others.": "V tuto chvíli to jste jen vy, s ostatními to bude ještě lepší.",
"Share %(name)s": "Sdílet %(name)s",
"Private space": "Soukromý space",
"Public space": "Veřejný space",
"<inviter/> invites you": "<inviter/> vás zve",
"Search names and description": "Prohledat jména a popisy",
"Create room": "Vytvořit místnost",
"You may want to try a different search or check for typos.": "Možná budete chtít zkusit vyhledat něco jiného nebo zkontrolovat překlepy.",
"No results found": "Nebyly nalezeny žádné výsledky",
"Mark as suggested": "Označit jako doporučené",
"Mark as not suggested": "Označit jako nedoporučené",
"Removing...": "Odebírání...",
"Failed to remove some rooms. Try again later": "Odebrání některých místností se nezdařilo. Zkuste to později znovu",
"%(count)s rooms and 1 space|one": "%(count)s místnost a 1 space",
"%(count)s rooms and 1 space|other": "%(count)s místností a 1 space",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s místnost a %(numSpaces)s space",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s místností a %(numSpaces)s spaces",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Pokud nemůžete najít místnost, kterou hledáte, požádejte o pozvánku nebo <a>vytvořte novou místnost</a>.",
"This room is suggested as a good one to join": "Tato místnost je doporučena jako dobrá pro připojení",
"Suggested": "Doporučeno",
"%(count)s rooms|one": "%(count)s místnost",
"%(count)s rooms|other": "%(count)s místností",
"You don't have permission": "Nemáte povolení",
"%(count)s messages deleted.|one": "%(count)s zpráva smazána.",
"%(count)s messages deleted.|other": "%(count)s zpráv smazáno.",
"Invite to %(roomName)s": "Pozvat do %(roomName)s",
"Invite People": "Pozvat lidi",
"Invite with email or username": "Pozvěte e-mailem nebo uživatelským jménem",
"You can change these anytime.": "Tyto údaje můžete kdykoli změnit.",
"Add some details to help people recognise it.": "Přidejte několik podrobností, aby to lidé lépe rozpoznali.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Spaces jsou nový způsob, jak seskupovat místnosti a lidi. Chcete-li se připojit ke stávajícímu space, budete potřebovat pozvánku.",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "K vašemu účtu přistupuje nové přihlášení: %(name)s (%(deviceID)s) pomocí %(ip)s",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Z %(deviceName)s (%(deviceId)s) pomocí %(ip)s",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Ověřte toto přihlášení, abyste získali přístup k šifrovaným zprávám a dokázali ostatním, že jste to opravdu vy.",
"Verify with another session": "Ověřit pomocí jiné relace",
"Just me": "Jen já",
"Edit devices": "Upravit zařízení",
"Check your devices": "Zkontrolujte svá zařízení",
"You have unverified logins": "Máte neověřená přihlášení",
"Open": "Otevřít"
}

File diff suppressed because it is too large Load diff

View file

@ -417,6 +417,7 @@
"Other": "Other",
"Command error": "Command error",
"Usage": "Usage",
"Sends the given message as a spoiler": "Sends the given message as a spoiler",
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message",
"Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message": "Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message",
"Prepends ┬──┬ ( ゜-゜ノ) to a plain-text message": "Prepends ┬──┬ ( ゜-゜ノ) to a plain-text message",
@ -785,6 +786,7 @@
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
"Change notification settings": "Change notification settings",
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.",
"Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode",
"Send and receive voice messages (in development)": "Send and receive voice messages (in development)",
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
"Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.",
@ -799,7 +801,6 @@
"Show message previews for reactions in DMs": "Show message previews for reactions in DMs",
"Show message previews for reactions in all rooms": "Show message previews for reactions in all rooms",
"Offline encrypted messaging using dehydrated devices": "Offline encrypted messaging using dehydrated devices",
"Share decryption keys for room history when inviting users": "Share decryption keys for room history when inviting users",
"Enable advanced debugging for the room list": "Enable advanced debugging for the room list",
"Show info about bridges in room settings": "Show info about bridges in room settings",
"Font size": "Font size",
@ -880,6 +881,8 @@
"sends fireworks": "sends fireworks",
"Sends the given message with snowfall": "Sends the given message with snowfall",
"sends snowfall": "sends snowfall",
"unknown person": "unknown person",
"Consulting with %(transferTarget)s. <a>Transfer to %(transferee)s</a>": "Consulting with %(transferTarget)s. <a>Transfer to %(transferee)s</a>",
"You held the call <a>Switch</a>": "You held the call <a>Switch</a>",
"You held the call <a>Resume</a>": "You held the call <a>Resume</a>",
"%(peerName)s held the call": "%(peerName)s held the call",
@ -1287,6 +1290,7 @@
"Room ID or address of ban list": "Room ID or address of ban list",
"Subscribe": "Subscribe",
"Start automatically after system login": "Start automatically after system login",
"Warn before quitting": "Warn before quitting",
"Always show the window menu bar": "Always show the window menu bar",
"Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close",
"Preferences": "Preferences",
@ -1357,6 +1361,7 @@
"Change topic": "Change topic",
"Upgrade the room": "Upgrade the room",
"Enable room encryption": "Enable room encryption",
"Change server ACLs": "Change server ACLs",
"Modify widgets": "Modify widgets",
"Failed to unban": "Failed to unban",
"Unban": "Unban",
@ -1914,15 +1919,13 @@
"collapse": "collapse",
"expand": "expand",
"%(count)s people you know have already joined|other": "%(count)s people you know have already joined",
"%(count)s people you know have already joined|one": "%(count)s person you know has already joined",
"You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)",
"Uploaded on %(date)s by %(user)s": "Uploaded on %(date)s by %(user)s",
"Rotate Left": "Rotate Left",
"Rotate counter-clockwise": "Rotate counter-clockwise",
"Rotate Right": "Rotate Right",
"Rotate clockwise": "Rotate clockwise",
"Download this file": "Download this file",
"Rotate Left": "Rotate Left",
"Zoom out": "Zoom out",
"Zoom in": "Zoom in",
"Download": "Download",
"Information": "Information",
"View message": "View message",
"Language Dropdown": "Language Dropdown",
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)sjoined %(count)s times",
@ -2216,6 +2219,7 @@
"Invite someone using their name, username (like <userId/>) or <a>share this room</a>.": "Invite someone using their name, username (like <userId/>) or <a>share this room</a>.",
"Invited people will be able to read old messages.": "Invited people will be able to read old messages.",
"Transfer": "Transfer",
"Consult first": "Consult first",
"a new master key signature": "a new master key signature",
"a new cross-signing key signature": "a new cross-signing key signature",
"a device cross-signing signature": "a device cross-signing signature",
@ -2306,6 +2310,10 @@
"Use your preferred Matrix homeserver if you have one, or host your own.": "Use your preferred Matrix homeserver if you have one, or host your own.",
"Learn more": "Learn more",
"About homeservers": "About homeservers",
"Reset event store?": "Reset event store?",
"You most likely do not want to reset your event index store": "You most likely do not want to reset your event index store",
"If you do, please note that none of your messages will be deleted, but the search experience might be degraded for a few momentswhilst the index is recreated": "If you do, please note that none of your messages will be deleted, but the search experience might be degraded for a few momentswhilst the index is recreated",
"Reset event store": "Reset event store",
"Sign out and remove encryption keys?": "Sign out and remove encryption keys?",
"Clear Storage and Sign Out": "Clear Storage and Sign Out",
"Send Logs": "Send Logs",
@ -2370,6 +2378,10 @@
"Looks good!": "Looks good!",
"Wrong Security Key": "Wrong Security Key",
"Invalid Security Key": "Invalid Security Key",
"Forgotten or lost all recovery methods? <a>Reset all</a>": "Forgotten or lost all recovery methods? <a>Reset all</a>",
"Reset everything": "Reset everything",
"Only do this if you have no other device to complete verification with.": "Only do this if you have no other device to complete verification with.",
"If you reset everything, you will restart with no trusted sessions, no trusted users, and might not be able to see past messages.": "If you reset everything, you will restart with no trusted sessions, no trusted users, and might not be able to see past messages.",
"Security Phrase": "Security Phrase",
"Unable to access secret storage. Please verify that you entered the correct Security Phrase.": "Unable to access secret storage. Please verify that you entered the correct Security Phrase.",
"Enter your Security Phrase or <button>Use your Security Key</button> to continue.": "Enter your Security Phrase or <button>Use your Security Key</button> to continue.",
@ -2438,6 +2450,7 @@
"Revoke permissions": "Revoke permissions",
"Move left": "Move left",
"Move right": "Move right",
"Avatar": "Avatar",
"This room is public": "This room is public",
"Away": "Away",
"User Status": "User Status",
@ -2693,6 +2706,7 @@
"Failed to send email": "Failed to send email",
"The email address linked to your account must be entered.": "The email address linked to your account must be entered.",
"A new password must be entered.": "A new password must be entered.",
"Please choose a strong password": "Please choose a strong password",
"New passwords must match each other.": "New passwords must match each other.",
"Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.",
"New Password": "New Password",
@ -2787,7 +2801,6 @@
"Your Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your Security Phrase.": "Your Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your Security Phrase.",
"Keep a copy of it somewhere secure, like a password manager or even a safe.": "Keep a copy of it somewhere secure, like a password manager or even a safe.",
"Your Security Key": "Your Security Key",
"Download": "Download",
"Your Security Key has been <b>copied to your clipboard</b>, paste it to:": "Your Security Key has been <b>copied to your clipboard</b>, paste it to:",
"Your Security Key is in your <b>Downloads</b> folder.": "Your Security Key is in your <b>Downloads</b> folder.",
"<b>Print it</b> and store it somewhere safe": "<b>Print it</b> and store it somewhere safe",

View file

@ -1671,7 +1671,7 @@
"More options": "Pliaj elektebloj",
"Integrations are disabled": "Kunigoj estas malŝaltitaj",
"Integrations not allowed": "Kunigoj ne estas permesitaj",
"Suggestions": "Proponoj",
"Suggestions": "Rekomendoj",
"Automatically invite users": "Memage inviti uzantojn",
"Upgrade private room": "Gradaltigi privatan ĉambron",
"Upgrade public room": "Gradaltigi publikan ĉambron",
@ -2113,7 +2113,7 @@
"Delete sessions|other": "Forigi salutaĵojn",
"Delete sessions|one": "Forigi salutaĵon",
"Where youre logged in": "Kie vi salutis",
"Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.": "Sube administru la nomojn de viaj salutaĵoj kaj ilin adiaŭu, aŭ <a>kontrolu ilin en via profilo de uzanto</a>.",
"Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.": "Sube administru la nomojn de viaj salutaĵoj kaj ilin adiaŭu, aŭ <a>ilin kontrolu en via profilo de uzanto</a>.",
"Waiting for you to accept on your other session…": "Atendante vian akcepton en via alia salutaĵo…",
"Almost there! Is your other session showing the same shield?": "Preskaŭ finite! Ĉu via alia salutaĵo montras la saman ŝildon?",
"Almost there! Is %(displayName)s showing the same shield?": "Preskaŭ finite! Ĉu %(displayName)s montras la saman ŝildon?",
@ -2376,7 +2376,7 @@
"Set a Security Phrase": "Agordi Sekurecan frazon",
"Confirm Security Phrase": "Konfirmi Sekurecan frazon",
"Save your Security Key": "Konservi vian Sekurecan ŝlosilon",
"New spinner design": "Nova fasono de la turniĝilo",
"New spinner design": "Nova aspekto de la atendosimbolo",
"Show rooms with unread messages first": "Montri ĉambrojn kun nelegitaj mesaĝoj kiel unuajn",
"Show previews of messages": "Montri antaŭrigardojn al mesaĝoj",
"This room is public": "Ĉi tiu ĉambro estas publika",
@ -3013,11 +3013,11 @@
"Just a heads up, if you don't add an email and forget your password, you could <b>permanently lose access to your account</b>.": "Averte, se vi ne aldonos retpoŝtadreson kaj poste forgesos vian pasvorton, vi eble <b>por ĉiam perdos aliron al via konto</b>.",
"Continuing without email": "Daŭrigante sen retpoŝtadreso",
"We recommend you change your password and Security Key in Settings immediately": "Ni rekomendas, ke vi tuj ŝanĝu viajn pasvorton kaj Sekurecan ŝlosilon per la Agordoj",
"Transfer": "Transigi",
"Transfer": "Transdoni",
"Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.": "Invitu iun per ĝia nomo, retpoŝtadreso, uzantonomo (ekz. <userId/>), aŭ <a>konigu ĉi tiun ĉambron</a>.",
"Start a conversation with someone using their name, email address or username (like <userId/>).": "Komencu interparolon kun iu per ĝia nomo, retpoŝtadreso, aŭ uzantonomo (ekz. <userId/>).",
"Failed to transfer call": "Malsukcesis transigi vokon",
"A call can only be transferred to a single user.": "Voko povas transiĝi nur al unu uzanto.",
"Failed to transfer call": "Malsukcesis transdoni vokon",
"A call can only be transferred to a single user.": "Voko povas transdoniĝi nur al unu uzanto.",
"Learn more in our <privacyPolicyLink />, <termsOfServiceLink /> and <cookiePolicyLink />.": "Eksciu plion per niaj <privacyPolicyLink />, <termsOfServiceLink /> kaj <cookiePolicyLink />.",
"Failed to connect to your homeserver. Please close this dialog and try again.": "Malsukcesis konektiĝi al via hejmservilo. Bonvolu fermi ĉi tiun interagujon kaj reprovi.",
"Edit Values": "Redakti valorojn",
@ -3045,5 +3045,148 @@
"Use Ctrl + F to search": "Serĉu per stirklavo (Ctrl) + F",
"Use Command + F to search": "Serĉu per komanda klavo + F",
"%(senderDisplayName)s changed the server ACLs for this room.": "%(senderDisplayName)s ŝanĝis la servilblokajn listojn por ĉi tiu ĉambro.",
"%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s agordis la servilblokajn listojn por ĉi tiu ĉambro."
"%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s agordis la servilblokajn listojn por ĉi tiu ĉambro.",
"Public": "Publika",
"Delete": "Forigi",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "De %(deviceName)s (%(deviceId)s) de %(ip)s",
"Jump to the bottom of the timeline when you send a message": "Salti al subo de historio sendinte mesaĝon",
"Check your devices": "Kontrolu viajn aparatojn",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Nova saluto aliras vian konton: %(name)s (%(deviceID)s) de %(ip)s",
"You have unverified logins": "Vi havas nekontrolitajn salutojn",
"You're already in a call with this person.": "Vi jam vokas ĉi tiun personon.",
"Already in call": "Jam vokanta",
"<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even add images with Matrix URLs <img src=\"mxc://url\" />\n</p>\n": "<h1>HTML por la paĝo de via komunumo</h1>\n<p>\n Uzu la longan priskribon por enkonduki novajn anojn en la komunumon, aŭ disdoni\n kelkajn gravajn <a href=\"foo\">ligilojn</a>.\n</p>\n<p>\n Vi povas eĉ aldoni bildojn per Matriks-URL <img src=\"mxc://url\" />\n</p>\n",
"View dev tools": "Montri programistilojn",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Ĉi tiu kutime influas nur traktadon de la ĉambro servil-flanke. Se vi spertas problemojn pri via %(brand)s, bonvolu raporti eraron.",
"Mark as suggested": "Marki rekomendata",
"Mark as not suggested": "Marki nerekomendata",
"Suggested": "Rekomendata",
"This room is suggested as a good one to join": "Ĉi tiu ĉambro estas rekomendata kiel aliĝinda",
"Suggested Rooms": "Rekomendataj ĉambroj",
"Failed to create initial space rooms": "Malsukcesis krei komencajn ĉambrojn de aro",
"Room name": "Nomo de ĉambro",
"Support": "Subteno",
"Random": "Hazarda",
"Welcome to <name/>": "Bonvenu al <name/>",
"Your private space <name/>": "Via privata aro <name/>",
"Your public space <name/>": "Via publika aro <name/>",
"Your server does not support showing space hierarchies.": "Via servilo ne subtenas montradon de hierarĥioj de aroj.",
"Add existing rooms & spaces": "Aldoni jamajn ĉambrojn kaj arojn",
"Private space": "Privata aro",
"Public space": "Publika aro",
"<inviter/> invites you": "<inviter/> invitas vin",
"Search names and description": "Serĉi nomojn kaj priskribojn",
"No results found": "Neniuj rezultoj troviĝis",
"Removing...": "Forigante…",
"Failed to remove some rooms. Try again later": "Malsukcesis forigi iujn arojn. Reprovu poste",
"%(count)s rooms and 1 space|one": "%(count)s ĉambro kaj 1 aro",
"%(count)s rooms and 1 space|other": "%(count)s ĉambroj kaj 1 aro",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s ĉambro kaj %(numSpaces)s aroj",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s ĉambroj kaj %(numSpaces)s aroj",
"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 trovi la ĉambron, kiun vi serĉas, petu inviton aŭ <a>kreu novan ĉambron</a>.",
"%(count)s rooms|one": "%(count)s ĉambro",
"%(count)s rooms|other": "%(count)s ĉambroj",
"%(count)s members|one": "%(count)s ano",
"%(count)s members|other": "%(count)s anoj",
"Open": "Malfermi",
"%(count)s messages deleted.|one": "%(count)s mesaĝo foriĝis.",
"%(count)s messages deleted.|other": "%(count)s mesaĝoj foriĝis.",
"Are you sure you want to leave the space '%(spaceName)s'?": "Ĉu vi certe volas forlasi la aron «%(spaceName)s»?",
"This space is not public. You will not be able to rejoin without an invite.": "Ĉi tiu aro ne estas publika. Vi ne povos re-aliĝi sen invito.",
"Upgrade to %(hostSignupBrand)s": "Gradaltigi al %(hostSignupBrand)s",
"Start audio stream": "Komenci sonelsendon",
"Failed to start livestream": "Malsukcesis komenci tujelsendon",
"Unable to start audio streaming.": "Ne povas komenci sonelsendon.",
"Save Changes": "Konservi ŝanĝojn",
"Saving...": "Konservante…",
"Leave Space": "Forlasi aron",
"Make this space private": "Privatigi ĉi tiun aron",
"Edit settings relating to your space.": "Redaktu agordojn pri via aro.",
"Space settings": "Agordoj de aro",
"Failed to save space settings.": "Malsukcesis konservi agordojn de aro.",
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Invitu iun per ĝia nomo, uzantonomo (kiel <userId/>), aŭ <a>diskonigu ĉi tiun aron</a>.",
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Invitu iun per ĝia nomo, retpoŝtadreso, uzantonomo (kiel <userId/>), aŭ <a>diskonigu ĉi tiun aron</a>.",
"Invite to %(roomName)s": "Inviti al %(roomName)s",
"Unnamed Space": "Sennoma aro",
"Invite to %(spaceName)s": "Inviti al %(spaceName)s",
"Abort": "Nuligi",
"Don't want to add an existing room?": "Ĉu vi ne volas aldoni jaman ĉambron?",
"Failed to add rooms to space": "Malsukcesis aldoni ĉambrojn al aro",
"Apply": "Apliki",
"Applying...": "Aplikante…",
"Create a new room": "Krei novan ĉambron",
"Spaces": "Aroj",
"Filter your rooms and spaces": "Filtru viajn ĉambrojn kaj arojn",
"Add existing spaces/rooms": "Aldoni jamajn arojn/ĉambrojn",
"Space selection": "Elekto de aro",
"Edit devices": "Redakti aparatojn",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the space it will be impossible to regain privileges.": "Vi ne povos malfari ĉi tiun ŝanĝon, ĉar vi malrangaltigas vin mem; se vi estas la lasta altranga uzanto de la aro, vi ne plu povos rehavi viajn rajtojn.",
"Invite People": "Inviti personojn",
"Empty room": "Malplena ĉamrbo",
"You do not have permissions to add rooms to this space": "Vi ne havas permeson aldoni ĉambrojn al ĉi tiu aro",
"Explore space rooms": "Esplori ĉambrojn de aro",
"Add existing room": "Aldoni jaman ĉambron",
"You do not have permissions to create new rooms in this space": "Vi ne havas permeson krei novajn ĉambrojn en ĉi tiu aro",
"Send message": "Sendi mesaĝon",
"Invite to this space": "Inviti al ĉi tiu aro",
"Your message was sent": "Via mesaĝo sendiĝis",
"Encrypting your message...": "Ĉifrante mesaĝon…",
"Sending your message...": "Sendante mesaĝon…",
"New room": "Nova ĉambro",
"Leave space": "Forlasi aron",
"Share your public space": "Diskonigu vian publikan aron",
"Invite members": "Inviti anojn",
"Invite with email or username": "Inviti per retpoŝtadreso aŭ uzantonomo",
"Invite people": "Inviti personojn",
"Share invite link": "Diskonigi invitan ligilon",
"Click to copy": "Klaku por kopii",
"Collapse space panel": "Maletendi arbreton",
"Expand space panel": "Etendi arbreton",
"Creating...": "Kreante…",
"You can change these anytime.": "Vi povas ŝanĝi ĉi tiujn kiam ajn vi volas.",
"Add some details to help people recognise it.": "Aldonu kelkajn detalojn, por ke ĝi estu rekonebla.",
"Your private space": "Via privata aro",
"Your public space": "Via publika aro",
"You can change this later": "Vi povas ŝanĝi ĉi tion poste",
"Invite only, best for yourself or teams": "Nur invita, ideala por vi mem aŭ por skipoj",
"Private": "Privata",
"Open space for anyone, best for communities": "Malferma aro por ĉiu ajn, ideala por komunumoj",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Aroj estas novaj manieroj grupigi ĉambrojn kaj personojn. Por aliĝi al aro, vi bezonas inviton.",
"Create a space": "Krei aron",
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Pratipo de Aroj. Malkonforma kun Komunumoj, Komunumoj v2, kaj Propraj etikedoj. Bezonas konforman hejmservilon por iuj funkcioj.",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Kontrolu ĉi tiun saluton por aliri viajn ĉifritajn mesaĝojn, kaj pruvi al aliuloj, ke la salutanto vere estas vi.",
"Verify with another session": "Knotroli per alia salutaĵo",
"Original event source": "Originala fonto de evento",
"Decrypted event source": "Malĉifrita fonto de evento",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Por ĉiu el ili ni kreos ĉambron. Vi povos aldoni pliajn pli poste, inkluzive jam ekzistantajn.",
"What projects are you working on?": "Kiujn projektojn vi prilaboras?",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Ni kreu ĉambron por ĉiu el ili. Vi povas aldoni pliajn poste, inkluzive jam ekzistantajn.",
"What are some things you want to discuss?": "Pri kio volas vi paroli?",
"Invite by username": "Inviti per uzantonomo",
"Make sure the right people have access. You can invite more later.": "Certigu, ke la ĝustaj personoj povas aliri. Vi povas inviti pliajn pli poste.",
"Invite your teammates": "Invitu viajn kunulojn",
"Inviting...": "Invitante…",
"Failed to invite the following users to your space: %(csvUsers)s": "Malsukcesis inviti la jenajn uzantojn al via aro: %(csvUsers)s",
"A private space for you and your teammates": "Privata aro por vi kaj viaj kunuloj",
"Me and my teammates": "Mi kaj miaj kunuloj",
"A private space to organise your rooms": "Privata aro por organizado de viaj ĉambroj",
"Just me": "Nur mi",
"Make sure the right people have access to %(name)s": "Certigu, ke la ĝustaj personoj povas aliri al %(name)s",
"Who are you working with?": "Kun kiu vi laboras?",
"Go to my first room": "Eniri mian unuan ĉambron",
"It's just you at the moment, it will be even better with others.": "Nun estas sole vi; estos eĉ pli bone kun aliuloj.",
"Share %(name)s": "Diskonigi %(name)s",
"Creating rooms...": "Kreante ĉambrojn…",
"You may want to try a different search or check for typos.": "Eble vi provu serĉi alion, aŭ kontroli je mistajpoj.",
"You don't have permission": "Vi ne rajtas",
"Values at explicit levels in this room:": "Valoroj por malimplicitaj niveloj en ĉi tiu ĉambro:",
"Values at explicit levels:": "Valoroj por malimplicitaj niveloj:",
"Save setting values": "Konservi valorojn de la agordoj",
"Values at explicit levels in this room": "Valoroj por malimplicitaj niveloj en ĉi tiu ĉambro",
"Values at explicit levels": "Valoroj por malimplicitaj niveloj",
"Spell check dictionaries": "Literumadaj vortaroj",
"Space options": "Agordoj de aro",
"Space Home": "Hejmo de aro",
"with state key %(stateKey)s": "kun statŝlosilo %(stateKey)s",
"with an empty state key": "kun malplena statŝlosilo"
}

View file

@ -1941,7 +1941,7 @@
"User Status": "Estado de usuario",
"This homeserver would like to make sure you are not a robot.": "A este servidor le gustaría asegurarse de que no eres un robot.",
"Country Dropdown": "Seleccione país",
"Confirm your identity by entering your account password below.": "Confirme su identidad introduciendo la contraseña de su cuenta.",
"Confirm your identity by entering your account password below.": "Confirma tu identidad introduciendo la contraseña de tu cuenta.",
"Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.": "Falta la clave pública del captcha en la configuración del servidor base. Por favor, informa de esto al administrador de tu servidor base.",
"Please review and accept all of the homeserver's policies": "Por favor, revisa y acepta todas las políticas del servidor base",
"Please review and accept the policies of this homeserver:": "Por favor, revisa y acepta las políticas de este servidor base:",
@ -3070,7 +3070,7 @@
"%(count)s members|other": "%(count)s miembros",
"Your server does not support showing space hierarchies.": "Este servidor no soporta mostrar jerarquías de espacios.",
"Default Rooms": "Salas por defecto",
"Add existing rooms & spaces": "Añadir salas y espacios ya existentes",
"Add existing rooms & spaces": "Añadir salas y espacios existentes",
"Accept Invite": "Aceptar invitación",
"Manage rooms": "Gestionar salas",
"Save changes": "Guardar cambios",
@ -3098,11 +3098,11 @@
"Invite to %(spaceName)s": "Invitar a %(spaceName)s",
"Failed to add rooms to space": "No se han podido añadir las salas al espacio",
"Apply": "Aplicar",
"Create a new room": "Crea una nueva",
"Create a new room": "Crear una nueva sala",
"Don't want to add an existing room?": "¿No quieres añadir una sala que ya exista?",
"Spaces": "Espacios",
"Filter your rooms and spaces": "Filtra tus salas y espacios",
"Add existing spaces/rooms": "Añadir espacios o salas ya existentes",
"Add existing spaces/rooms": "Añadir espacios o salas existentes",
"Space selection": "Selección de espacio",
"Empty room": "Sala vacía",
"Suggested Rooms": "Salas sugeridas",
@ -3141,5 +3141,52 @@
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Prototipo de espacios. No compatible con comunidades, comunidades v2 o etiquetas personalizadas. Necesita un servidor base compatible para algunas funcionalidades.",
"This homeserver has been blocked by its administrator.": "Este servidor base ha sido bloqueado por su administración.",
"This homeserver has been blocked by it's administrator.": "Este servidor base ha sido bloqueado por su administración.",
"You're already in a call with this person.": "Ya estás en una llamada con esta persona."
"You're already in a call with this person.": "Ya estás en una llamada con esta persona.",
"This room is suggested as a good one to join": "Unirse a esta sala está sugerido",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Esto solo afecta normalmente a cómo el servidor procesa la sala. Si estás teniendo problemas con %(brand)s, por favor, infórmanos del problema.",
"It's just you at the moment, it will be even better with others.": "Ahora mismo no hay nadie más.",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Verifica este inicio de sesión para acceder a tus mensajes cifrados y probar a otras personas que realmente eres tú quien está iniciando sesión.",
"Verify with another session": "Verificar con otra sesión",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Crearemos salas para cada uno. Puedes añadir más después, incluso salas que ya existan.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Vamos a crear una sala para cada uno. Puedes añadir más después, incluso salas que ya existan.",
"Make sure the right people have access. You can invite more later.": "Vamos a asegurarnos de que solo la gente adecuada tiene acceso. Puedes invitar a más después.",
"A private space to organise your rooms": "Un espacio privado para organizar tus salas",
"Make sure the right people have access to %(name)s": "Vamos a asegurarnos de que solo la gente adecuada tiene acceso a %(name)s",
"Just me": "Solo yo",
"Go to my first room": "Ir a mi primera sala",
"Share %(name)s": "Compartir %(name)s",
"Private space": "Espacio privado",
"Public space": "Espacio público",
"<inviter/> invites you": "<inviter/> te ha invitado",
"Search names and description": "Buscar nombres y descripciones",
"You may want to try a different search or check for typos.": "Prueba con otro término de búsqueda o comprueba que no haya erratas.",
"Create room": "Crear sala",
"No results found": "Ningún resultado",
"Mark as suggested": "Sugerir",
"Mark as not suggested": "No sugerir",
"Removing...": "Quitando...",
"Failed to remove some rooms. Try again later": "No se han podido quitar algunas salas. Prueba de nuevo más tarde",
"%(count)s rooms and 1 space|one": "%(count)s sala y 1 espacio",
"%(count)s rooms and 1 space|other": "%(count)s salas y 1 espacio",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s sala y %(numSpaces)s espacios",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s salas y %(numSpaces)s espacios",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Si no encuentras la sala que estás buscando, pide que te inviten o <a>crea una nueva</a>.",
"Suggested": "Sugerencias",
"%(count)s rooms|one": "%(count)s sala",
"%(count)s rooms|other": "%(count)s salas",
"You don't have permission": "No tienes permisos",
"Open": "Abrir",
"%(count)s messages deleted.|one": "%(count)s mensaje eliminado.",
"%(count)s messages deleted.|other": "%(count)s mensajes eliminados.",
"Invite to %(roomName)s": "Invitar a %(roomName)s",
"Edit devices": "Editar dispositivos",
"Invite People": "Invitar a gente",
"Invite with email or username": "Invitar correos electrónicos o nombres de usuario",
"You can change these anytime.": "Puedes cambiar todo esto en cualquier momento.",
"Add some details to help people recognise it.": "Añade algún detalle para ayudar a que la gente lo reconozca.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Los espacios son la nueva manera de agrupar personas y salas. Para unirte a un espacio, necesitarás que te inviten.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "De %(deviceName)s (%(deviceId)s) en",
"Check your devices": "Comprueba tus dispositivos",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Alguien está iniciando sesión a tu cuenta: %(name)s (%(deviceID)s) en %(ip)s",
"You have unverified logins": "Tienes inicios de sesión sin verificar"
}

View file

@ -1019,7 +1019,7 @@
"Welcome to %(appName)s": "Tere tulemast suhtlusrakenduse %(appName)s kasutajaks",
"Liberate your communication": "Vabasta oma suhtlus",
"Send a Direct Message": "Saada otsesõnum",
"Are you sure you want to leave the room '%(roomName)s'?": "Kas oled kindel, et soovid lahkuda jututoast '%(roomName)s'?",
"Are you sure you want to leave the room '%(roomName)s'?": "Kas oled kindel, et soovid lahkuda jututoast „%(roomName)s“?",
"Unknown error": "Teadmata viga",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Selleks et jätkata koduserveri %(homeserverDomain)s kasutamist sa pead üle vaatama ja nõustuma meie kasutustingimustega.",
"Permissions": "Õigused",
@ -3092,5 +3092,139 @@
"This homeserver has been blocked by it's administrator.": "Ligipääs sellele koduserverile on sinu serveri haldaja poolt blokeeritud.",
"This homeserver has been blocked by its administrator.": "Ligipääs sellele koduserverile on sinu serveri haldaja poolt blokeeritud.",
"You're already in a call with this person.": "Sinul juba kõne käsil selle osapoolega.",
"Already in call": "Kõne on juba pooleli"
"Already in call": "Kõne on juba pooleli",
"Your message wasn't sent because this homeserver has been blocked by it's administrator. Please <a>contact your service administrator</a> to continue using the service.": "Sinu sõnumit ei saadetud, kuna see koduserver on haldaja poolt blokeeritud. Teenuse kasutamiseks palun <a>võta ühendust serveri haldajaga</a>.",
"Are you sure you want to leave the space '%(spaceName)s'?": "Kas oled kindel, et soovid lahkuda kogukonnakeskusest „%(spaceName)s“?",
"This space is not public. You will not be able to rejoin without an invite.": "See ei ole avalik kogukonnakeskus. Ilma kutseta sa ei saa uuesti liituda.",
"Start audio stream": "Käivita audiovoog",
"Failed to start livestream": "Videovoo käivitamine ei õnnestu",
"Unable to start audio streaming.": "Audiovoo käivitamine ei õnnestu.",
"Save Changes": "Salvesta muutused",
"Saving...": "Salvestan...",
"View dev tools": "Näita arendaja töövahendeid",
"Leave Space": "Lahku kogukonnakeskusest",
"Make this space private": "Muuda see kogukonnakeskus privaatseks",
"Edit settings relating to your space.": "Muuda oma kogukonnakeskuse seadistusi.",
"Space settings": "Kogukonnakeskuse seadistused",
"Failed to save space settings.": "Kogukonnakeskuse seadistuste salvestamine ei õnnestunud.",
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Kutsu kedagi tema nime, kasutajanime (nagu <userId/>) alusel või <a>jaga seda kogukonnakeskust</a>.",
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Kutsu teist osapoolt tema nime, e-posti aadressi, kasutajanime (nagu <userId/>) alusel või <a>jaga seda kogukonnakeskust</a>.",
"Unnamed Space": "Nimetu kogukonnakeskus",
"Invite to %(spaceName)s": "Kutsu kogukonnakeskusesse %(spaceName)s",
"Failed to add rooms to space": "Jututubade lisamine kogukonnakeskusesse ei õnnestunud",
"Apply": "Rakenda",
"Applying...": "Rakendan...",
"Create a new room": "Loo uus jututuba",
"Don't want to add an existing room?": "Kas sa ei soovi lisada olemasolevat jututuba?",
"Spaces": "Kogukonnakeskused",
"Filter your rooms and spaces": "Otsi olemasolevate kogukonnakeskuste ja jututubade seast",
"Add existing spaces/rooms": "Lisa olemasolevaid kogukonnakeskuseid ja jututube",
"Space selection": "Kogukonnakeskuse valik",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the space it will be impossible to regain privileges.": "Kuna sa vähendad enda õigusi, siis sul ei pruugi hiljem olla võimalik seda muutust tagasi pöörata. Kui sa juhtumisi oled viimane haldusõigustega kasutaja kogukonnakeskuses, siis hiljem on võimatu samu õigusi tagasi saada.",
"Empty room": "Tühi jututuba",
"Suggested Rooms": "Soovitatud jututoad",
"Explore space rooms": "Tutvu kogukonnakeskuses leiduvate jututubadega",
"You do not have permissions to add rooms to this space": "Sul pole õigusi siia kogukonnakeskusesse lisada jututubasid",
"Add existing room": "Lisa olemasolev jututuba",
"You do not have permissions to create new rooms in this space": "Sul pole õigusi luua siin kogukonnakeskuses uusi jututubasid",
"Send message": "Saada sõnum",
"Invite to this space": "Kutsu siia kogukonnakeskusesse",
"Your message was sent": "Sinu sõnum sai saadetud",
"Encrypting your message...": "Krüptin sinu sõnumit...",
"Sending your message...": "Saadan sinu sõnumit...",
"Spell check dictionaries": "Õigekirja sõnastikud",
"Space options": "Kogukonnakeskus eelistused",
"Space Home": "Kogukonnakeskuse avaleht",
"New room": "Uus jututuba",
"Leave space": "Lahku kogukonnakeskusest",
"Share your public space": "Jaga oma avalikku kogukonnakeskust",
"Invite members": "Kutsu uusi osalejaid",
"Invite people": "Kutsu teisi kasutajaid",
"Share invite link": "Jaga kutse linki",
"Click to copy": "Kopeerimiseks klõpsa",
"Collapse space panel": "Ahenda kogukonnakeskuste paneeli",
"Expand space panel": "Laienda kogukonnakeskuste paneeli",
"Creating...": "Loon...",
"Your private space": "Sinu privaatne kogukonnakeskus",
"Your public space": "Sinu avalik kogukonnakeskus",
"You can change this later": "Sa võid seda hiljem muuta",
"Invite only, best for yourself or teams": "Liitumine vaid kutse alusel, sobib sulle ja sinu lähematele kaaslastele",
"Private": "Privaatne",
"Open space for anyone, best for communities": "Avaliku ligipääsuga kogukonnakeskus",
"Public": "Avalik",
"Create a space": "Loo kogukonnakeskus",
"Delete": "Kustuta",
"Jump to the bottom of the timeline when you send a message": "Sõnumi saatmiseks hüppa ajajoone lõppu",
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Kogukonnakeskuse prototüüp. Ei ühildu varasemate kogukonnalehtedega ega kohandatud siltidega. Mõned funktsionaalsused eeldavad ühilduva koduserveri kasutamist.",
"%(count)s members|other": "%(count)s liiget",
"%(count)s members|one": "%(count)s liige",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Seadmest %(deviceName)s (%(deviceId)s) aadressiga %(ip)s",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Kogukonnakeskused on uus viis inimeste ja jututubade ühendamiseks. Kogukonnakeskusega liitumiseks vajad sa kutset.",
"Add some details to help people recognise it.": "Tegemaks teiste jaoks äratundmise lihtsamaks, palun lisa natuke teavet.",
"You can change these anytime.": "Sa võid neid alati muuta.",
"Invite with email or username": "Kutsu e-posti aadressi või kasutajanime alusel",
"Invite People": "Kutsu teisi kasutajaid",
"Edit devices": "Muuda seadmeid",
"Invite to %(roomName)s": "Kutsu jututuppa %(roomName)s",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "See tavaliselt mõjutab vaid viisi, kuidas server jututuba teenindab. Kui sul tekib %(brand)s kasutamisel vigu, siis palun anna sellest meile teada.",
"%(count)s messages deleted.|other": "%(count)s sõnumit on kustutatud.",
"%(count)s messages deleted.|one": "%(count)s sõnum on kustutatud.",
"You don't have permission": "Sul puuduvad selleks õigused",
"%(count)s rooms|other": "%(count)s jututuba",
"%(count)s rooms|one": "%(count)s jututuba",
"This room is suggested as a good one to join": "Teised kasutajad soovitavad liitumist selle jututoaga",
"Suggested": "Soovitatud",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Kui sa ei leia otsitavat jututuba, siis palu sinna kutset või <a>loo uus jututuba</a>.",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s jututuba ja %(numSpaces)s kogukonnakeskust",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s jututuba ja %(numSpaces)s kogukonnakeskust",
"%(count)s rooms and 1 space|other": "%(count)s jututuba ja 1 kogukonnakeskus",
"%(count)s rooms and 1 space|one": "%(count)s jututuba ja 1 kogukonnakeskus",
"Add existing rooms & spaces": "Lisa olemasolevaid jututubasid ja kogukonnakeskuseid",
"Default Rooms": "Vaikimisi jututoad",
"Your server does not support showing space hierarchies.": "Sinu koduserver ei võimalda kuvada kogukonnakeskuste hierarhiat.",
"Your public space <name/>": "Sinu avalik kogukonnakeskus <name/>",
"Your private space <name/>": "Sinu privaatne kogukonnakeskus <name/>",
"Welcome to <name/>": "Tete tulemast <name/> liikmeks",
"Random": "Juhuslik",
"Support": "Toeta",
"Room name": "Jututoa nimi",
"Failed to create initial space rooms": "Algsete jututubade loomine ei õnnestunud",
"Skip for now": "Hetkel jäta vahele",
"Creating rooms...": "Loon jututubasid…",
"Who are you working with?": "Kellega sa koos töötad?",
"Me and my teammates": "Mina ja minu kaasteelised",
"A private space for you and your teammates": "Privaatne kogukonnakeskus sinu ja sinu kaasteeliste jaoks",
"Failed to invite the following users to your space: %(csvUsers)s": "Järgnevate kasutajate kutsumine kogukonnakeskusesse ei õnnestunud: %(csvUsers)s",
"Inviting...": "Kutsun...",
"Invite your teammates": "Kutsu oma kaasteelisi",
"Invite by username": "Kutsu kasutajanime alusel",
"What are some things you want to discuss?": "Mis on need teemad, mida tahaksid arutada?",
"What projects are you working on?": "Mis ettevõtmistega sa tegeled?",
"Decrypted event source": "Sündmuse dekrüptitud lähtekood",
"Original event source": "Algse sündmuse lähtekood",
"Failed to remove some rooms. Try again later": "Mõnede jututubade eemaldamine ei õnnestunud. Proovi hiljem uuesti",
"Removing...": "Eemaldan...",
"Mark as not suggested": "Eemalda soovitus",
"Mark as suggested": "Märgi soovituseks",
"No results found": "Tulemusi ei ole",
"You may want to try a different search or check for typos.": "Aga proovi muuta otsingusõna või kontrolli ega neis trükivigu polnud.",
"Search names and description": "Otsi nimede ja kirjelduste seast",
"<inviter/> invites you": "<inviter/> saatis sulle kutse",
"Public space": "Avalik kogukonnakeskus",
"Private space": "Privaatne kogukonnakeskus",
"Share %(name)s": "Jaga %(name)s",
"It's just you at the moment, it will be even better with others.": "Hetkel oled siin vaid sina, aga aina paremaks läheb, kui teised liituvad.",
"Go to my first room": "Mine minu esimese jututoa juurde",
"Make sure the right people have access to %(name)s": "Palun kontrolli, et vajalikel inimestel oleks ligipääs siia - %(name)s",
"Just me": "Vaid mina",
"A private space to organise your rooms": "Privaatne kogukonnakeskus jututubade koondamiseks",
"Make sure the right people have access. You can invite more later.": "Kontrolli, et vajalikel inimestel oleks siia ligipääs. Teistele võid kutse saata ka hiljem.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Loome nüüd igaühe jaoks jututoa. Sa võid neid ka hiljem lisada, sealhulgas olemasolevaid.",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Ma loome igaühe jaoks jututoa. Sa võid neid ka hiljem lisada, sealhulgas olemasolevaid.",
"Verify with another session": "Verifitseeri teise sessiooniga",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Oma krüptitud sõnumite lugemiseks verifitseeri see sisselogimissessioon. Samaga kinnitad ka teistele, et tegemist on tõesti sinuga.",
"Open": "Ava",
"Check your devices": "Kontrolli oma seadmeid",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Uus sisselogimissessioon kasutab sinu Matrixi kontot: %(name)s %(deviceID)s aadressil %(ip)s",
"You have unverified logins": "Sul on verifitseerimata sisselogimissessioone"
}

View file

@ -222,5 +222,94 @@
"I have verified my email address": "ایمیل خود را تأید کردم",
"Home": "خانه",
"Hangup": "قطع",
"For security, this session has been signed out. Please sign in again.": "برای امنیت، این نشست نامعتبر شده است. لطفاً دوباره وارد سیستم شوید."
"For security, this session has been signed out. Please sign in again.": "برای امنیت، این نشست نامعتبر شده است. لطفاً دوباره وارد سیستم شوید.",
"We couldn't log you in": "ما نتوانستیم شما را وارد حسابتان کنیم",
"Trust": "اعتماد کن",
"Only continue if you trust the owner of the server.": "تنها در صورتی که به صاحب سرور اطمینان دارید، ادامه دهید.",
"Identity server has no terms of service": "سرور هویت‌سنجی، شرایط استفاده از خدمت (terms of service) را مشخص نکرده‌است",
"Unnamed Room": "اتاق بدون نام",
"Failed to add the following rooms to %(groupId)s:": "افزودن این اتاق‌ها به فضای کاری %(groupId)s موفقیت‌آمیز نبود:",
"Failed to invite users to %(groupId)s": "دعوت کاربران به فضای کاری %(groupId)s موفقیت‌آمیز نبود",
"Failed to invite users to community": "دعوت کاربران به این فضای کاری موفقیت‌آمیز نبود",
"Failed to invite the following users to %(groupId)s:": "دعوت این کاربران به فضای کاری %(groupId)s موفقیت‌آمیز نبود:",
"Add to community": "افزودن به فضای کاری",
"Room name or address": "نام یا آدرس اتاق",
"Invite new community members": "اعضای جدیدی را به فضای کاری دعوت کنید",
"Add rooms to the community": "افزودن اتاق به فضای کاری",
"Show these rooms to non-members on the community page and room list?": "آیا تمایل دارید نام این اتاق‌ها در صفحه‌ی این فضای کاری و همچنین لیست اتاق‌ها، به کاربرانی که عضو آن‌ها نیستند نمایش داده شود؟",
"Which rooms would you like to add to this community?": "تمایل دارید کدام اتاق‌ها را به این فضای کاری اضافه کنید؟",
"Invite to Community": "دعوت به فضای کاری",
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "هشدار: هر کاربری را که به این فضای کاری اضافه می‌کنید، برای تمام افرادی که شناسه این فضای کاری را در اختیار داشته باشند، قابل مشاهده هستند",
"Who would you like to add to this community?": "تمایل دارید چه افراد دیگری را به این فضای کاری اضافه کنید؟",
"Name or Matrix ID": "نام یا شناسه ماتریکس",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s.%(day)s.%(fullYear)s.%(time)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s.%(day)s.%(fullYear)s",
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s.%(day)s.%(time)s",
"%(weekDayName)s %(time)s": "%(weekDayName)s.%(time)s",
"AM": "قبل از ظهر",
"PM": "بعد از ظهر",
"Dec": "دسامبر",
"Nov": "نوامبر",
"Oct": "اکتبر",
"Sep": "سپتامبر",
"Aug": "اوت",
"Jul": "ژوئیه",
"Jun": "ژوئن",
"May": "می",
"Apr": "آوریل",
"Mar": "مارس",
"Feb": "فوریه",
"Jan": "ژانویه",
"Sat": "شنبه",
"Fri": "جمعه",
"Thu": "پنجشنبه",
"Wed": "چهارشنبه",
"Tue": "سه‌شنبه",
"Mon": "دوشنبه",
"Sun": "یکشنبه",
"The server does not support the room version specified.": "سرور از نسخه‌ی اتاقی که مشخص شده‌است، پشتیبانی نمی‌کند.",
"Server may be unavailable, overloaded, or you hit a bug.": "سرور ممکن است از دسترس خارج شده، یا فشار بار زیادی را تحمل کرده، و یا به یک باگ نرم‌افزاری برخورد کرده باشد.",
"Upload Failed": "بارگذاری موفقیت‌آمیز نبود",
"The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "حجم پرونده‌ی '%(fileName)s' از آستانه‌ی تنظیم‌شده بر روی سرور بیشتر است",
"The file '%(fileName)s' failed to upload.": "بارگذاری پرونده '%(fileName)s' موفقیت‌آمیز نبود.",
"At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "در حال حاضر امکان پاسخ از طریق یک پرونده وجود ندارد. آیا تمایل دارید این پرونده را در حالتی که پاسخ نیست، بارگذاری کنید؟",
"Replying With Files": "در حال پاسخ با پرونده",
"This will end the conference for everyone. Continue?": "با این کار، جلسه‌ی تصویری برای همه به اتمام می‌رسد. ادامه می‌دهید؟",
"End conference": "جلسه را پایان بده",
"You do not have permission to start a conference call in this room": "شما اجازه‌ی شروع جلسه‌ی تصویری در این اتاق را ندارید",
"Permission Required": "اجازه نیاز است",
"A call is currently being placed!": "یک تماس هم‌اکنون برقرار است!",
"Call in Progress": "تماس در جریان است",
"You cannot place a call with yourself.": "امکان برقراری تماس با خودتان وجود ندارد.",
"You're already in a call with this person.": "شما هم‌اکنون با این فرد در تماس هستید.",
"Already in call": "هم‌اکنون در تماس هستید",
"You've reached the maximum number of simultaneous calls.": "شما به بیشینه‌ی تعداد تماس‌های هم‌زمان رسیده‌اید.",
"Too Many Calls": "تعداد زیاد تماس",
"You cannot place VoIP calls in this browser.": "امکان برقراری تماس بر بستر VoIP در این مرورگر وجود ندارد.",
"VoIP is unsupported": "قابلیت VoIP پشتیبانی نمی‌شود",
"Unable to capture screen": "امکان ضبط صفحه‌ی نمایش وجود ندارد",
"No other application is using the webcam": "برنامه‌ی دیگری از دوربین استفاده نکند",
"Permission is granted to use the webcam": "دسترسی مورد نیاز به دوربین داده شده باشد",
"A microphone and webcam are plugged in and set up correctly": "میکروفون و دوربین به درستی تنظیم شده باشند",
"Call failed because webcam or microphone could not be accessed. Check that:": "تماس به دلیل مشکل در دسترسی به دوربین یا میکروفون موفقیت‌آمیز نبود. لطفا بررسی کنید:",
"Unable to access webcam / microphone": "امکان دسترسی به دوربین/میکروفون وجود ندارد",
"Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "تماس به دلیل عدم دسترسی به میکروفون موفقیت‌آمیز نبود. لطفا اتصال و تنظیمات صحیح میکروفون را بررسی نمائید.",
"Unable to access microphone": "دسترسی به میکروفون امکان‌پذیر نیست",
"Try using turn.matrix.org": "turn.hivaa.im را امتحان کنید",
"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.": "همچنین شما می‌توانید از سرور TURN عمومی <code>turn.hivaa.im</code> استفاده نمائید؛ توجه کنید در این حالت، میزان کیفیت تماس‌ها چندان قابل اتکاء نبوده و همچنین آدرس IP کاربران برای سرور مشخص می‌شود. در صورت نیاز می‌توانید پیکربندی این بخش را در تنظیمات برنامه تغییر دهید.",
"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 را پیکربندی نماید.",
"Call failed due to misconfigured server": "تماس به دلیل پیکربندی نادرست سرور موفقیت‌آمیز نبود",
"The call was answered on another device.": "تماس بر روی دستگاه دیگری پاسخ داده شد.",
"Answered Elsewhere": "در جای دیگری پاسخ داده شد",
"The call could not be established": "امکان برقراری تماس وجود ندارد",
"The other party declined the call.": "طرف مقابل تماس را رد کرد.",
"Call Declined": "تماس رد شد",
"Call Failed": "تماس موفقیت‌آمیز نبود",
"Unable to load! Check your network connectivity and try again.": "امکان بارگیری محتوا وجود ندارد! لطفا وضعیت اتصال خود به اینترنت را بررسی کرده و مجددا اقدام نمائید.",
"The information being sent to us to help make %(brand)s better includes:": "اطلاعاتی که به ما برای افزایش کیفیت %(brand)s ارسال می‌شوند عبارتند از:",
"Analytics": "تجزیه و تحلیل",
"Your device resolution": "وضوح دستگاه شما",
"e.g. <CurrentPageURL>": "برای مثال <CurrentPageURL>",
"Every page you use in the app": "هر صفحه‌ی برنامه از که آن استفاده می‌کنید",
"e.g. %(exampleValue)s": "برای مثال %(exampleValue)s"
}

View file

@ -2756,7 +2756,7 @@
"Invite by email": "Kutsu sähköpostilla",
"Report a bug": "Raportoi virheestä",
"Invalid Recovery Key": "Virheellinen palautusavain",
"Confirm Security Phrase": "Vahvista turvalauseke",
"Confirm Security Phrase": "Vahvista turvalause",
"Upload a file": "Lähetä tiedosto",
"Confirm encryption setup": "Vahvista salauksen asetukset",
"Verify other session": "Vahvista toinen istunto",
@ -2775,7 +2775,7 @@
"Rate %(brand)s": "Arvioi %(brand)s",
"%(brand)s Desktop": "%(brand)s Desktop",
"%(brand)s Web": "%(brand)s Web",
"Security Phrase": "Turvalauseke",
"Security Phrase": "Turvalause",
"Security Key": "Turva-avain",
"Verify session": "Vahvista istunto",
"Hold": "Pidä",
@ -2820,8 +2820,8 @@
"Prepends ┬──┬ ( ゜-゜ノ) to a plain-text message": "Lisää ┬──┬ ( ゜-゜ノ) viestin alkuun",
"Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message": "Lisää (╯°□°)╯︵ ┻━┻ viestin alkuun",
"Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Salaisen tallenustilan avaaminen epäonnistui. Varmista, että syötit oikean palautuksen salasanan.",
"Enter a Security Phrase": "Kirjoita turvalauseke",
"Set a Security Phrase": "Aseta turvalauseke",
"Enter a Security Phrase": "Kirjoita turvalause",
"Set a Security Phrase": "Aseta turvalause",
"Unable to query secret storage status": "Salaisen tallennustilan tilaa ei voi kysellä",
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "Jos peruutat nyt, voit menettää salattuja viestejä ja tietoja, jos menetät pääsyn kirjautumistietoihisi.",
"You can also set up Secure Backup & manage your keys in Settings.": "Voit myös ottaa käyttöön suojatun varmuuskopioinnin ja hallita avaimia asetuksista.",
@ -2870,5 +2870,85 @@
"Privacy Policy": "Tietosuojakäytäntö",
"Cookie Policy": "Evästekäytäntö",
"Recent changes that have not yet been received": "Tuoreet muutokset, joita ei ole vielä otettu vastaan",
"We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Pyysimme selainta muistamaan kirjautumista varten mitä kotipalvelinta käytät, mutta selain on unohtanut sen. Mene kirjautumissivulle ja yritä uudelleen."
"We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Pyysimme selainta muistamaan kirjautumista varten mitä kotipalvelinta käytät, mutta selain on unohtanut sen. Mene kirjautumissivulle ja yritä uudelleen.",
"Search (must be enabled)": "Haku (pitää olla käytössä)",
"Apply": "Käytä",
"Applying...": "Käytetään...",
"Channel: <channelLink/>": "Kanava: <channelLink/>",
"This homeserver has been blocked by it's administrator.": "Tämän kotipalvelimen ylläpitäjä on estänyt sen.",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Tehdään huone jokaiselle. Voit myös lisätä niitä myöhemmin, mukaan lukien olemassa olevia.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Tehdään huone jokaiselle. Voit myös lisätä niitä myöhemmin, mukaan lukien olemassa olevia.",
"What are some things you want to discuss?": "Mistä asioista haluat keskustella?",
"Inviting...": "Kutsutaan...",
"Share %(name)s": "Jaa %(name)s",
"Creating rooms...": "Luodaan huoneita...",
"Skip for now": "Ohita tältä erää",
"Room name": "Huoneen nimi",
"Default Rooms": "Oletushuoneet",
"<inviter/> invites you": "<inviter/> kutsuu sinut",
"You may want to try a different search or check for typos.": "Kokeile eri hakua tai tarkista haku kirjoitusvirheiden varalta.",
"No results found": "Tuloksia ei löytynyt",
"Mark as not suggested": "Merkitse ei-ehdotetuksi",
"Mark as suggested": "Merkitse ehdotetuksi",
"Removing...": "Poistetaan...",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Jos et löydä etsimääsi huonetta, pyydä kutsua tai <a>luo uusi huone</a>.",
"%(count)s rooms|one": "%(count)s huone",
"%(count)s rooms|other": "%(count)s huonetta",
"%(count)s members|one": "%(count)s jäsen",
"%(count)s members|other": "%(count)s jäsentä",
"You don't have permission": "Sinulla ei ole lupaa",
"%(count)s messages deleted.|one": "%(count)s viesti poistettu.",
"%(count)s messages deleted.|other": "%(count)s viestiä poistettu.",
"Remember this": "Muista tämä",
"Save Changes": "Tallenna muutokset",
"Saving...": "Tallennetaan...",
"View dev tools": "Näytä kehitystyökalut",
"Invite to %(roomName)s": "Kutsu huoneeseen %(roomName)s",
"Minimize dialog": "Pienennä ikkuna",
"Maximize dialog": "Suurenna ikkuna",
"Edit Values": "Muokkaa arvoja",
"Value in this room:": "Arvo tässä huoneessa:",
"Value:": "Arvo:",
"Save setting values": "Tallenna asetusarvot",
"Settable at room": "Asetettavissa huoneessa",
"Settable at global": "Asetettavissa globaalisti",
"Level": "Taso",
"Setting definition:": "Asetuksen määritelmä:",
"This UI does NOT check the types of the values. Use at your own risk.": "Tämä käyttöliittymä EI tarkista arvojen tyyppejä. Käytä omalla vastuullasi.",
"Caution:": "Varoitus:",
"Setting:": "Asetus:",
"Value in this room": "Arvo tässä huoneessa",
"Value": "Arvo",
"Failed to save settings": "Asetusten tallentaminen epäonnistui",
"Create a new room": "Luo uusi huone",
"Don't want to add an existing room?": "Etkö halua lisätä olemassa olevaa huonetta?",
"Edit devices": "Muokkaa laitteita",
"Invite People": "Kutsu ihmisiä",
"Empty room": "Tyhjä huone",
"Suggested Rooms": "Ehdotetut huoneet",
"Add existing room": "Lisää olemassa oleva huone",
"Send message": "Lähetä viesti",
"Your message was sent": "Viestisi lähetettiin",
"Encrypting your message...": "Viestiäsi salataan...",
"Sending your message...": "Viestiäsi lähetetään...",
"Spell check dictionaries": "Oikolukusanastot",
"New room": "Uusi huone",
"Invite members": "Kutsu jäseniä",
"Invite people": "Kutsu ihmisiä",
"Share invite link": "Jaa kutsulinkki",
"Click to copy": "Kopioi napsauttamalla",
"Creating...": "Luodaan...",
"You can change these anytime.": "Voit muuttaa näitä koska tahansa.",
"You can change this later": "Voit muuttaa tätä myöhemmin",
"Private": "Yksityinen",
"Public": "Julkinen",
"Delete": "Poista",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Laitteelta %(deviceName)s (%(deviceId)s) osoitteesta %(ip)s",
"Show chat effects (animations when receiving e.g. confetti)": "Näytä keskustelutehosteet (animaatiot, kun saat esim. konfettia)",
"Jump to the bottom of the timeline when you send a message": "Siirry aikajanan pohjalle, kun lähetät viestin",
"Check your devices": "Tarkista laitteesi",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Uusi kirjautuminen tilillesi: %(name)s (%(deviceID)s) osoitteesta %(ip)s",
"This homeserver has been blocked by its administrator.": "Tämä kotipalvelin on ylläpitäjänsä estämä.",
"You're already in a call with this person.": "Olet jo puhelussa tämän henkilön kanssa.",
"Already in call": "Olet jo puhelussa"
}

View file

@ -102,7 +102,7 @@
"%(senderName)s kicked %(targetName)s.": "%(senderName)s a expulsé %(targetName)s.",
"Kick": "Expulser",
"Kicks user with given id": "Expulse lutilisateur à partir de son identifiant",
"Labs": "Labo",
"Labs": "Expérimental",
"Leave room": "Quitter le salon",
"%(targetName)s left the room.": "%(targetName)s a quitté le salon.",
"Logout": "Se déconnecter",
@ -637,7 +637,7 @@
"Community IDs cannot be empty.": "Les identifiants de communauté ne peuvent pas être vides.",
"<a>In reply to</a> <pill>": "<a>En réponse à</a> <pill>",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s a modifié son nom daffichage en %(displayName)s.",
"Failed to set direct chat tag": "Échec de lajout de létiquette discussion directe",
"Failed to set direct chat tag": "Échec de lajout de létiquette de conversation privée",
"Failed to remove tag %(tagName)s from room": "Échec de la suppression de létiquette %(tagName)s du salon",
"Failed to add tag %(tagName)s to room": "Échec de lajout de létiquette %(tagName)s au salon",
"Clear filter": "Supprimer les filtres",
@ -684,7 +684,7 @@
"Noisy": "Sonore",
"Room not found": "Salon non trouvé",
"Messages containing my display name": "Messages contenant mon nom daffichage",
"Messages in one-to-one chats": "Messages dans les discussions directes",
"Messages in one-to-one chats": "Messages dans les conversations privées",
"Unavailable": "Indisponible",
"View Decrypted Source": "Voir la source déchiffrée",
"Failed to update keywords": "Échec de la mise à jour des mots-clés",
@ -945,7 +945,7 @@
"Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Si vous ne configurez pas la récupération de messages sécurisée, vous perdrez l'historique de vos messages sécurisés quand vous vous déconnectez.",
"If you don't want to set this up now, you can later in Settings.": "Si vous ne voulez pas le configurer maintenant, vous pouvez le faire plus tard dans les paramètres.",
"Messages containing @room": "Messages contenant @room",
"Encrypted messages in one-to-one chats": "Messages chiffrés dans les discussions directes",
"Encrypted messages in one-to-one chats": "Messages chiffrés dans les conversations privées",
"Encrypted messages in group chats": "Messages chiffrés dans les discussions de groupe",
"That doesn't look like a valid email address": "Cela ne ressemble pas à une adresse e-mail valide",
"Checking...": "Vérification…",
@ -1029,7 +1029,7 @@
"Composer": "Compositeur",
"Room list": "Liste de salons",
"Timeline": "Fil de discussion",
"Autocomplete delay (ms)": "Retard pour lautocomplétion (ms)",
"Autocomplete delay (ms)": "Délai pour lautocomplétion (ms)",
"Chat with %(brand)s Bot": "Discuter avec le bot %(brand)s",
"Roles & Permissions": "Rôles et permissions",
"Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Les modifications concernant l'accès à lhistorique ne s'appliqueront quaux futurs messages de ce salon. La visibilité de lhistorique existant ne sera pas modifiée.",
@ -1626,7 +1626,7 @@
"Custom (%(level)s)": "Personnalisé (%(level)s)",
"Trusted": "Fiable",
"Not trusted": "Non fiable",
"Direct message": "Message direct",
"Direct message": "Conversation privée",
"<strong>%(role)s</strong> in %(roomName)s": "<strong>%(role)s</strong> dans %(roomName)s",
"Messages in this room are end-to-end encrypted.": "Les messages dans ce salon sont chiffrés de bout en bout.",
"Security": "Sécurité",
@ -1669,7 +1669,7 @@
"%(senderName)s placed a video call.": "%(senderName)s a passé un appel vidéo.",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s a passé un appel vidéo. (non pris en charge par ce navigateur)",
"Clear notifications": "Vider les notifications",
"Customise your experience with experimental labs features. <a>Learn more</a>.": "Personnalisez votre expérience avec des fonctionnalités expérimentales du labo. <a>En savoir plus</a>.",
"Customise your experience with experimental labs features. <a>Learn more</a>.": "Personnalisez votre expérience avec des fonctionnalités expérimentales. <a>En savoir plus</a>.",
"Error upgrading room": "Erreur lors de la mise à niveau du salon",
"Double check that your server supports the room version chosen and try again.": "Vérifiez que votre serveur prend en charge la version de salon choisie et réessayez.",
"This message cannot be decrypted": "Ce message ne peut pas être déchiffré",
@ -1735,7 +1735,7 @@
"Help": "Aide",
"Show more": "En voir plus",
"Recent Conversations": "Conversations récentes",
"Direct Messages": "Messages directs",
"Direct Messages": "Conversations privées",
"Go": "Cest parti",
"Show info about bridges in room settings": "Afficher des informations à propos des passerelles dans les paramètres du salon",
"This bridge is managed by <user />.": "Cette passerelle est gérée par <user />.",
@ -1762,7 +1762,7 @@
"We couldn't create your DM. Please check the users you want to invite and try again.": "Impossible de créer votre conversation privée. Vérifiez quels utilisateurs que vous souhaitez inviter et réessayez.",
"Something went wrong trying to invite the users.": "Une erreur est survenue en essayant dinviter les utilisateurs.",
"We couldn't invite those users. Please check the users you want to invite and try again.": "Impossible dinviter ces utilisateurs. Vérifiez quels utilisateurs que vous souhaitez inviter et réessayez.",
"Recently Direct Messaged": "Messages directs récents",
"Recently Direct Messaged": "Conversations privées récentes",
"Start": "Commencer",
"Session verified": "Session vérifiée",
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Votre nouvelle session est maintenant vérifiée. Elle a accès à vos messages chiffrés et les autres utilisateurs la verront comme fiable.",
@ -2113,7 +2113,7 @@
"Click the button below to confirm deleting these sessions.|one": "Cliquez sur le bouton ci-dessous pour confirmer la suppression de cette session.",
"Welcome to %(appName)s": "Bienvenue sur %(appName)s",
"Liberate your communication": "Libérez votre communication",
"Send a Direct Message": "Envoyez un message direct",
"Send a Direct Message": "Envoyez un message privé",
"Explore Public Rooms": "Explorez les salons publics",
"Create a Group Chat": "Créez une discussion de groupe",
"%(name)s is requesting verification": "%(name)s demande une vérification",
@ -2234,7 +2234,7 @@
"A new version of %(brand)s is available!": "Une nouvelle version de %(brand)s est disponible !",
"New version available. <a>Update now.</a>": "Nouvelle version disponible. <a>Faire la mise à niveau maintenant.</a>",
"Emoji picker": "Sélecteur démojis",
"Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Ladministrateur de votre serveur a désactivé le chiffrement de bout en bout par défaut dans les salons privés et les messages directs.",
"Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Ladministrateur de votre serveur a désactivé le chiffrement de bout en bout par défaut dans les salons privés et les conversations privées.",
"People": "Personnes",
"Switch to light mode": "Passer au mode clair",
"Switch to dark mode": "Passer au mode sombre",
@ -2264,7 +2264,7 @@
"Use Recovery Key": "Utiliser la clé de récupération",
"Use the improved room list (will refresh to apply changes)": "Utiliser la liste de salons améliorée (actualisera pour appliquer les changements)",
"Use custom size": "Utiliser une taille personnalisée",
"Hey you. You're the best!": "Eh vous. Vous êtes les meilleurs !",
"Hey you. You're the best!": "Hé vous. Vous êtes le meilleur !",
"Message layout": "Mise en page des messages",
"Compact": "Compacte",
"Modern": "Moderne",
@ -2905,7 +2905,7 @@
"<a>Add a topic</a> to help people know what it is about.": "<a>Ajoutez un sujet</a> pour aider les gens à savoir de quoi il est question.",
"Topic: %(topic)s ": "Sujet : %(topic)s ",
"Topic: %(topic)s (<a>edit</a>)": "Sujet : %(topic)s (<a>modifier</a>)",
"This is the beginning of your direct message history with <displayName/>.": "Cest le début de votre historique de messages privés avec <displayName/>.",
"This is the beginning of your direct message history with <displayName/>.": "Cest le début de lhistorique de votre conversation privée avec <displayName/>.",
"Only the two of you are in this conversation, unless either of you invites anyone to join.": "Vous nêtes que tous les deux dans cette conversation, à moins que lun de vous invite quelquun à vous rejoindre.",
"%(name)s on hold": "%(name)s est en attente",
"Return to call": "Revenir à lappel",
@ -3137,7 +3137,7 @@
"Add existing spaces/rooms": "Ajouter des espaces/salons existants",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the space it will be impossible to regain privileges.": "Vous ne pourrez pas annuler ce changement puisque vous vous rétrogradez. Si vous êtes le dernier utilisateur a privilèges de cet espace, il deviendra impossible den reprendre contrôle.",
"Empty room": "Salon vide",
"Suggested Rooms": "Salons suggérés",
"Suggested Rooms": "Salons recommandés",
"Explore space rooms": "Parcourir les salons de cet espace",
"You do not have permissions to add rooms to this space": "Vous navez pas la permission dajouter des salons à cet espace",
"Add existing room": "Ajouter un salon existant",
@ -3178,5 +3178,52 @@
"This homeserver has been blocked by it's administrator.": "Ce serveur daccueil a été banni par ses administrateurs.",
"This homeserver has been blocked by its administrator.": "Ce serveur daccueil a été banni par ses administrateurs.",
"You're already in a call with this person.": "Vous êtes déjà en cours dappel avec cette personne.",
"Already in call": "Déjà en cours dappel"
"Already in call": "Déjà en cours dappel",
"Space selection": "Sélection dun espace",
"Search names and description": "Rechercher par nom ou description",
"Go to my first room": "Rejoindre mon premier salon",
"Mark as suggested": "Marquer comme recommandé",
"Mark as not suggested": "Marquer comme non recommandé",
"Suggested": "Recommandé",
"This room is suggested as a good one to join": "Ce salon recommandé peut être intéressant à rejoindre",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Vérifiez cette connexion pour accéder à vos messages chiffrés et prouver aux autres quil sagit bien de vous.",
"Verify with another session": "Vérifier avec une autre session",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Nous allons créer un salon pour chaque. Vous pourrez en ajouter plus tard, y compris certains déjà existant.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Créons un salon pour chacun dentre eux. Vous pourrez en ajouter plus tard, y compris certains déjà existant.",
"Make sure the right people have access. You can invite more later.": "Assurez-vous que les accès sont accordés aux bonnes personnes. Vous pourrez en inviter dautres plus tard.",
"A private space to organise your rooms": "Un espace privé pour organiser vos salons",
"Just me": "Seulement moi",
"Make sure the right people have access to %(name)s": "Assurez-vous que les bonnes personnes ont accès à %(name)s",
"It's just you at the moment, it will be even better with others.": "Vous êtes seul pour linstant, ce sera plus agréable avec de la compagnie.",
"Share %(name)s": "Partager %(name)s",
"Private space": "Espace privé",
"Public space": "Espace public",
"<inviter/> invites you": "<inviter/> vous a invité",
"You may want to try a different search or check for typos.": "Essayez une requête différente, ou vérifiez que vous navez pas fait de faute de frappe.",
"No results found": "Aucun résultat",
"Removing...": "Suppression…",
"Failed to remove some rooms. Try again later": "Échec de la suppression de certains salons. Veuillez réessayez plus tard",
"%(count)s rooms and 1 space|one": "%(count)s salon et 1 espace",
"%(count)s rooms and 1 space|other": "%(count)s salons et 1 espace",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s salon et %(numSpaces)s espaces",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s salons et %(numSpaces)s espaces",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Si vous ne trouvez pas le salon que vous cherchez, demandez une invitation ou <a>créez un nouveau salon</a>.",
"%(count)s rooms|one": "%(count)s salon",
"%(count)s rooms|other": "%(count)s salons",
"You don't have permission": "Vous navez pas lautorisation",
"Open": "Ouvrir",
"%(count)s messages deleted.|one": "%(count)s message supprimé.",
"%(count)s messages deleted.|other": "%(count)s messages supprimés.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Cela naffecte généralement que la façon dont le salon est traité sur le serveur. Si vous avez des problèmes avec votre %(brand)s, signalez une anomalie.",
"Invite to %(roomName)s": "Inviter dans %(roomName)s",
"Edit devices": "Modifier les appareils",
"Invite People": "Inviter des personnes",
"Invite with email or username": "Inviter par e-mail ou nom dutilisateur",
"You can change these anytime.": "Vous pouvez les changer à nimporte quel moment.",
"Add some details to help people recognise it.": "Ajoutez des informations pour aider les personnes à lidentifier.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Les espaces permettent de grouper les salons et les personnes. Pour rejoindre un espace existant, il vous faut une invitation.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Sur %(deviceName)s %(deviceId)s depuis %(ip)s",
"Check your devices": "Vérifiez vos appareils",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Une nouvelle session a accès à votre compte : %(name)s %(deviceID)s depuis %(ip)s",
"You have unverified logins": "Vous avez des sessions non-vérifiées"
}

View file

@ -2956,7 +2956,7 @@
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Element with an existing Matrix account on a different homeserver.": "Podes usar as opcións do servidor para poder conectarte a outros servidores Matrix indicando o URL dese servidor. Esto permíteche usar Element cunha conta Matrix existente noutro servidor.",
"Server Options": "Opcións do servidor",
"Reason (optional)": "Razón (optativa)",
"We call the places where you can host your account homeservers.": "Ós lugares onde podes ter unha conta chamámoslle 'servidores de inicio'.",
"We call the places where you can host your account homeservers.": "Chamámoslle 'servidores de inicio' aos lugares onde podes ter unha conta.",
"Invalid URL": "URL non válido",
"Unable to validate homeserver": "Non se puido validar o servidor de inicio",
"sends confetti": "envía confetti",
@ -3202,5 +3202,51 @@
"This homeserver has been blocked by it's administrator.": "Este servidor de inicio foi bloqueado pola súa administración.",
"This homeserver has been blocked by its administrator.": "O servidor de inicio foi bloqueado pola súa administración.",
"You're already in a call with this person.": "Xa estás nunha conversa con esta persoa.",
"Already in call": "Xa estás nunha chamada"
"Already in call": "Xa estás nunha chamada",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Verifica esta conexión para acceder ás túas mensaxes cifradas e demostrarlle a outras persoas que es ti realmente.",
"Verify with another session": "Verificar con outra sesión",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Crearemos salas para cada un. Podes engadir outras máis tarde, incluíndo as xa existentes.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Crea unha sala para cada un. Podes engadir outras máis tarde, incluíndo as xa existentes.",
"Make sure the right people have access. You can invite more later.": "Asegúrate de que as persoas axeitadas teñen acceso. Podes convidar a outras máis tarde.",
"A private space to organise your rooms": "Un espazo privado para organizar as túas salas",
"Just me": "Só eu",
"Make sure the right people have access to %(name)s": "Asegúrate de que as persoas axeitadas teñen acceso a %(name)s",
"Go to my first room": "Ir á miña primeira sala",
"It's just you at the moment, it will be even better with others.": "Por agora só estás ti, será incluso mellor con outras persoas.",
"Share %(name)s": "Compartir %(name)s",
"Private space": "Espazo privado",
"Public space": "Espazo público",
"<inviter/> invites you": "<inviter/> convídate",
"Search names and description": "Busca por nomes e descrición",
"You may want to try a different search or check for typos.": "Podes intentar unha busca diferente ou comprobar o escrito.",
"No results found": "Sen resultados",
"Mark as suggested": "Marcar como suxerida",
"Mark as not suggested": "Marcar como non suxerida",
"Removing...": "Eliminando...",
"Failed to remove some rooms. Try again later": "Fallou a eliminación de algunhas salas. Inténtao máis tarde",
"%(count)s rooms and 1 space|one": "%(count)s sala e 1 espazo",
"%(count)s rooms and 1 space|other": "%(count)s salas e 1 espazo",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s sala e %(numSpaces)s espazos",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s salas e %(numSpaces)s espazos",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Se non atopas a sala que buscas, pide un convite ou <a>crea unha nova sala</a>.",
"Suggested": "Recomendada",
"This room is suggested as a good one to join": "Esta sala é recomendada como apropiada para unirse",
"%(count)s rooms|one": "%(count)s sala",
"%(count)s rooms|other": "%(count)s salas",
"You don't have permission": "Non tes permiso",
"Open": "Abrir",
"%(count)s messages deleted.|one": "%(count)s mensaxe eliminada.",
"%(count)s messages deleted.|other": "%(count)s mensaxes eliminadas.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Normalmente esto só afecta a como se xestiona a sala no servidor. Se tes problemas co teu %(brand)s, informa do fallo.",
"Invite to %(roomName)s": "Convidar a %(roomName)s",
"Edit devices": "Editar dispositivos",
"Invite People": "Convida a persoas",
"Invite with email or username": "Convida con email ou nome de usuaria",
"You can change these anytime.": "Poderás cambialo en calquera momento.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Espazos é un novo xeito de agrupar salas e persoas. Para unirte a un espazo existente precisarás un convite.",
"Add some details to help people recognise it.": "Engade algún detalle para que sexa recoñecible.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Desde %(deviceName)s%(deviceId)s en %(ip)s",
"Check your devices": "Comproba os teus dispositivos",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Hai unha nova conexión á túa conta: %(name)s %(deviceID)s desde %(ip)s",
"You have unverified logins": "Tes conexións sen verificar"
}

View file

@ -3197,5 +3197,51 @@
"This homeserver has been blocked by it's administrator.": "Ezt a matrix szervert az adminisztrátor lezárta.",
"This homeserver has been blocked by its administrator.": "Ezt a matrix szervert az adminisztrátor lezárta.",
"You're already in a call with this person.": "Már hívásban van ezzel a személlyel.",
"Already in call": "A hívás már folyamatban van"
"Already in call": "A hívás már folyamatban van",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Ellenőrizze ezt a bejelentkezést, hogy hozzáférjen a titkosított üzeneteihez, valamint be tudja bizonyítani másoknak, hogy ez bejelentkezés önhöz tartozik.",
"Verify with another session": "Ellenőrizze egy másik munkamenettel",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Készítünk mindegyik szobához egyet. Később is hozzáadhat újakat vagy akár meglévőket.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Készítsünk mindegyik szobához egyet. Később is hozzáadhat újakat vagy akár meglévőket.",
"Make sure the right people have access. You can invite more later.": "Ellenőrizze, hogy a megfelelő személyeknek van hozzáférése. Később meghívhat másokat is.",
"A private space to organise your rooms": "Privát tér a szobái csoportosításához",
"Just me": "Csak én",
"Make sure the right people have access to %(name)s": "Ellenőrizze, hogy a megfelelő személyeknek hozzáférése van ehhez: %(name)s",
"Go to my first room": "Ugrás az első szobámra",
"It's just you at the moment, it will be even better with others.": "Egyenlőre csak ön, még jobb lehet másokkal együtt.",
"Share %(name)s": "Megosztás: %(name)s",
"Private space": "Privát tér",
"Public space": "Nyilvános tér",
"<inviter/> invites you": "<inviter/> meghívta",
"Search names and description": "Nevek és leírás keresése",
"You may want to try a different search or check for typos.": "Esetleg próbáljon ki egy másik keresést vagy nézze át elgépelések után.",
"No results found": "Nincs találat",
"Mark as suggested": "Javasoltnak jelölés",
"Mark as not suggested": "Nem javasoltnak jelölés",
"Removing...": "Törlés...",
"Failed to remove some rooms. Try again later": "Néhány szoba törlése sikertelen. Próbálja később",
"%(count)s rooms and 1 space|one": "%(count)s szoba és 1 tér",
"%(count)s rooms and 1 space|other": "%(count)s szoba és 1 tér",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s szoba és %(numSpaces)s tér",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s szoba és %(numSpaces)s tér",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Ha nem található a szoba amit keresett kérjen egy meghívót vagy <a>Készítsen egy új szobát</a>.",
"Suggested": "Javaslat",
"This room is suggested as a good one to join": "Ez egy javasolt szoba csatlakozáshoz",
"%(count)s rooms|one": "%(count)s szoba",
"%(count)s rooms|other": "%(count)s szoba",
"You don't have permission": "Nincs jogosultsága",
"Open": "Megnyitás",
"%(count)s messages deleted.|one": "%(count)s üzenet törölve.",
"%(count)s messages deleted.|other": "%(count)s üzenet törölve.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Ez általában a szoba szerver oldali kezelésében jelent változást. Ha probléma van itt: %(brand)s, kérjük küldjön hibajelentést.",
"Invite to %(roomName)s": "Meghívás ide: %(roomName)s",
"Edit devices": "Eszközök szerkesztése",
"Invite People": "Személyek meghívása",
"Invite with email or username": "Meghívás e-mail címmel vagy felhasználói névvel",
"You can change these anytime.": "Bármikor megváltoztatható.",
"Add some details to help people recognise it.": "Információ hozzáadása, hogy könnyebben felismerhető legyen.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "A terek egy új lehetőség a szobák és emberek csoportosításához. Létező térhez meghívóval lehet csatlakozni.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Innen: %(deviceName)s (%(deviceId)s), %(ip)s",
"Check your devices": "Ellenőrizze az eszközeit",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Új bejelentkezéssel hozzáférés történik a fiókjához: %(name)s (%(deviceID)s), %(ip)s",
"You have unverified logins": "Ellenőrizetlen bejelentkezései vannak"
}

View file

@ -3202,5 +3202,51 @@
"This homeserver has been blocked by it's administrator.": "Questo homeserver è stato bloccato dal suo amministratore.",
"This homeserver has been blocked by its administrator.": "Questo homeserver è stato bloccato dal suo amministratore.",
"You're already in a call with this person.": "Sei già in una chiamata con questa persona.",
"Already in call": "Già in una chiamata"
"Already in call": "Già in una chiamata",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Verifica questa sessione per accedere ai tuoi messaggi cifrati e provare agli altri che questo sei veramente tu.",
"Verify with another session": "Verifica con un'altra sessione",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Creeremo stanze per ognuno di essi. Puoi aggiungerne altri dopo, inclusi quelli già esistenti.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Inizia a creare una stanza per ognuno di essi. Puoi aggiungerne altri dopo, inclusi quelli già esistenti.",
"Make sure the right people have access. You can invite more later.": "Assicurati che le persone giuste abbiano accesso. Puoi invitarne altre dopo.",
"A private space to organise your rooms": "Uno spazio privato per organizzare le tue stanze",
"Just me": "Solo io",
"Make sure the right people have access to %(name)s": "Assicurati che le persone giuste abbiano accesso a %(name)s",
"Go to my first room": "Vai alla mia prima stanza",
"It's just you at the moment, it will be even better with others.": "Ci sei solo tu al momento, sarà ancora meglio con gli altri.",
"Share %(name)s": "Condividi %(name)s",
"Private space": "Spazio privato",
"Public space": "Spazio pubblico",
"<inviter/> invites you": "<inviter/> ti ha invitato",
"Search names and description": "Cerca nomi e descrizioni",
"You may want to try a different search or check for typos.": "Prova a fare una ricerca diversa o controllare errori di battitura.",
"No results found": "Nessun risultato trovato",
"Mark as suggested": "Segna come consigliato",
"Mark as not suggested": "Segna come non consigliato",
"Removing...": "Rimozione...",
"Failed to remove some rooms. Try again later": "Rimozione di alcune stanze fallita. Riprova più tardi",
"%(count)s rooms and 1 space|one": "%(count)s stanza e 1 spazio",
"%(count)s rooms and 1 space|other": "%(count)s stanze e 1 spazio",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s stanza e %(numSpaces)s spazi",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s stanze e %(numSpaces)s spazi",
"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>.",
"Suggested": "Consigliato",
"This room is suggested as a good one to join": "Questa è una buona stanza in cui entrare",
"%(count)s rooms|one": "%(count)s stanza",
"%(count)s rooms|other": "%(count)s stanze",
"You don't have permission": "Non hai il permesso",
"%(count)s messages deleted.|one": "%(count)s messaggio eliminato.",
"%(count)s messages deleted.|other": "%(count)s messaggi eliminati.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Solitamente ciò influisce solo su come la stanza viene elaborata sul server. Se stai riscontrando problemi con il tuo %(brand)s, segnala un errore.",
"Invite to %(roomName)s": "Invita in %(roomName)s",
"Edit devices": "Modifica dispositivi",
"Invite People": "Invita persone",
"Invite with email or username": "Invita con email o nome utente",
"You can change these anytime.": "Puoi cambiarli in qualsiasi momento.",
"Add some details to help people recognise it.": "Aggiungi qualche dettaglio per aiutare le persone a riconoscerlo.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Gli spazi sono nuovi modi di raggruppare stanze e persone. Per entrare in uno spazio esistente ti serve un invito.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Da %(deviceName)s (%(deviceId)s) al %(ip)s",
"Check your devices": "Controlla i tuoi dispositivi",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Una nuova sessione sta accedendo al tuo account: %(name)s (%(deviceID)s) al %(ip)s",
"You have unverified logins": "Hai accessi non verificati",
"Open": "Apri"
}

View file

@ -2444,5 +2444,9 @@
"This homeserver has been blocked by it's administrator.": "このホームサーバーは管理者によりブロックされています。",
"This homeserver has been blocked by its administrator.": "このホームサーバーは管理者によりブロックされています。",
"You're already in a call with this person.": "あなたは既にこの人と通話中です。",
"Already in call": "既に電話中です"
"Already in call": "既に電話中です",
"Invite People": "ユーザーを招待",
"Edit devices": "デバイスを編集",
"%(count)s messages deleted.|one": "%(count)s 件のメッセージが削除されました。",
"%(count)s messages deleted.|other": "%(count)s 件のメッセージが削除されました。"
}

View file

@ -1,5 +1,5 @@
{
"Accept": "Pieņemt",
"Accept": "Akceptēt",
"%(targetName)s accepted an invitation.": "%(targetName)s pieņēma uzaicinājumu.",
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s pieņēma uzaicinājumu no %(displayName)s.",
"Account": "Konts",
@ -27,8 +27,8 @@
"Anyone who knows the room's link, apart from guests": "Ikviens, kurš zina adreses saiti uz istabu, izņemot viesus",
"Anyone who knows the room's link, including guests": "Ikviens, kurš zina adreses saiti uz istabu, tai skaitā arī viesi",
"Are you sure?": "Vai tiešām to vēlaties?",
"Are you sure you want to leave the room '%(roomName)s'?": "Vai tiešām vēlies pamest istabu: '%(roomName)s'?",
"Are you sure you want to reject the invitation?": "Vai tiešām vēlies noraidīt šo uzaicinājumu?",
"Are you sure you want to leave the room '%(roomName)s'?": "Vai tiešām vēlaties pamest istabu: '%(roomName)s'?",
"Are you sure you want to reject the invitation?": "Vai tiešām vēlaties noraidīt šo uzaicinājumu?",
"Attachment": "Pielikums",
"Autoplay GIFs and videos": "Automātiski rādīt GIF animācijas un video",
"%(senderName)s banned %(targetName)s.": "%(senderName)s liedza pieeju %(targetName)s.",
@ -60,7 +60,7 @@
"Cryptography": "Kriptogrāfija",
"Current password": "Pašreizējā parole",
"Custom": "Pielāgots",
"Custom level": "Īpašais līmenis",
"Custom level": "Pielāgots līmenis",
"/ddg is not a command": "/ddg nav komanda",
"Deactivate Account": "Deaktivizēt kontu",
"Decline": "Noraidīt",
@ -74,7 +74,7 @@
"Email": "Epasts",
"Email address": "Epasta adrese",
"Emoji": "Emocijzīmes",
"%(senderName)s ended the call.": "%(senderName)s pārtrauca zvanu.",
"%(senderName)s ended the call.": "%(senderName)s pabeidza zvanu.",
"Enter passphrase": "Ievadiet frāzveida paroli",
"Error": "Kļūda",
"Error decrypting attachment": "Kļūda atšifrējot pielikumu",
@ -182,7 +182,7 @@
"Register": "Reģistrēties",
"%(targetName)s rejected the invitation.": "%(targetName)s noraidīja uzaicinājumu.",
"Reject invitation": "Noraidīt uzaicinājumu",
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s dzēsa attēlojamo/redzamo vārdu (%(oldDisplayName)s).",
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s dzēsa parādāmo vārdu (%(oldDisplayName)s).",
"%(senderName)s removed their profile picture.": "%(senderName)s dzēsa profila attēlu.",
"Remove": "Dzēst",
"%(senderName)s requested a VoIP conference.": "%(senderName)s vēlas VoIP konferenci.",
@ -191,7 +191,7 @@
"%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s nav atļauts nosūtīt jums paziņojumus. Lūdzu pārbaudi sava pārlūka iestatījumus",
"%(brand)s was not given permission to send notifications - please try again": "%(brand)s nav piešķirta atļauja nosūtīt paziņojumus. Lūdzu mēģini vēlreiz",
"%(brand)s version:": "%(brand)s versija:",
"Unable to enable Notifications": "Nav iespējams iespējot paziņojumus",
"Unable to enable Notifications": "Neizdevās iespējot paziņojumus",
"You have no visible notifications": "Tev nav redzamo paziņojumu",
"This will allow you to reset your password and receive notifications.": "Tas atļaus Tev atiestatīt paroli un saņemt paziņojumus.",
"Room %(roomId)s not visible": "Istaba %(roomId)s nav redzama",
@ -201,7 +201,7 @@
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s nosūtīja attēlu.",
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s nosūtīja uzaicinājumu %(targetDisplayName)s pievienoties istabai.",
"%(senderName)s set a profile picture.": "%(senderName)s uzstādīja profila attēlu.",
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s nomainīja attēlojamo/redzamo vārdu uz: %(displayName)s.",
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s nomainīja parādāmo vārdu uz: %(displayName)s.",
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s atcēla pieejas liegumu %(targetName)s.",
"Uploading %(filename)s and %(count)s others|zero": "Tiek augšupielādēts %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Tiek augšupielādēts %(filename)s un %(count)s citi",
@ -258,9 +258,9 @@
"To use it, just wait for autocomplete results to load and tab through them.": "Lai to izmantotu, vienkārši gaidi, kamēr ielādējas automātiski ieteiktie rezultāti, un pārvietojies caur tiem.",
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Notika mēģinājums ielādēt šīs istabas specifisku laikpaziņojumu sadaļu, bet Tev nav atļaujas skatīt šo ziņu.",
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Mēģinājums ielādēt šīs istabas čata vēstures izvēlēto posmu neizdevās, jo tas netika atrasts.",
"Unable to add email address": "Nav iespējams pievienot epasta adresi",
"Unable to remove contact information": "Nav iespējams dzēst kontaktinformāciju",
"Unable to verify email address.": "Nav iespējams apstiprināt epasta adresi.",
"Unable to add email address": "Neizdevās pievienot epasta adresi",
"Unable to remove contact information": "Neizdevās dzēst kontaktinformāciju",
"Unable to verify email address.": "Neizdevās apstiprināt epasta adresi.",
"Unban": "Atcelt pieejas liegumu",
"Unable to capture screen": "Neizdevās uzņemt ekrānattēlu",
"unknown caller": "nezināms zvanītājs",
@ -350,10 +350,10 @@
"You must join the room to see its files": "Tev ir jāpievienojas istabai, lai redzētu tās failus",
"Failed to invite": "Neizdevās uzaicināt",
"Confirm Removal": "Apstipriniet dzēšanu",
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Vai tiešām vēlies dzēst šo notikumu? Ņem vērā, ka istabas nosaukuma vai tēmas nosaukuma maiņa var ietekmēt (atsaukt) izmaiņas.",
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Vai tiešām vēlaties dzēst šo notikumu? Ņemiet vērā, ka istabas nosaukuma dzēšana vai temata maiņa var atcelt izmaiņas.",
"Unknown error": "Nezināma kļūda",
"Incorrect password": "Nepareiza parole",
"Unable to restore session": "Nav iespējams atjaunot sesiju",
"Unable to restore session": "Neizdevās atjaunot sesiju",
"If you have previously used a more recent version of %(brand)s, your session may be incompatible with this version. Close this window and return to the more recent version.": "Ja iepriekš izmantojāt jaunāku %(brand)s versiju, jūsu sesija var nebūt saderīga ar šo versiju. Aizveriet šo logu un atgriezieties jaunākajā versijā.",
"Unknown Address": "Nezināma adrese",
"ex. @bob:example.com": "piemēram, @valters:smaidu.lv",
@ -398,7 +398,7 @@
"PM": "PM",
"The maximum permitted number of widgets have already been added to this room.": "Maksimāli atļautais vidžetu skaits šai istabai jau sasniegts.",
"To get started, please pick a username!": "Lai sāktu, lūdzu izvēlies lietotājvārdu!",
"Unable to create widget.": "Nav iespējams izveidot widžetu.",
"Unable to create widget.": "Neizdevās izveidot widžetu.",
"You are not in this room.": "Tu neatrodies šajā istabā.",
"You do not have permission to do that in this room.": "Tev nav atļaujas šai darbībai šajā istabā.",
"Example": "Piemērs",
@ -433,7 +433,7 @@
"Invite to Community": "Uzaicināt kopienā",
"Which rooms would you like to add to this community?": "Kuras istabas vēlies pievienot šai kopienai?",
"Show these rooms to non-members on the community page and room list?": "Vai ne-biedriem rādīt kopienas lapā un istabu sarakstā šīs istabas?",
"Add rooms to the community": "Istabu pievienošana kopienai",
"Add rooms to the community": "Pievienot istabas kopienai",
"Add to community": "Pievienot kopienai",
"Failed to invite the following users to %(groupId)s:": "Neizdevās uzaicināt sekojošus lietotājus grupā %(groupId)s:",
"Failed to invite users to community": "Neizdevās uzaicināt lietotājus komūnā",
@ -444,7 +444,7 @@
"You are now ignoring %(userId)s": "Tagad Tu ignorē %(userId)s",
"Unignored user": "Atignorēts lietotājs",
"You are no longer ignoring %(userId)s": "Tu vairāk neignorē %(userId)s",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s nomainīja savu attēlojamo/redzamo vārdu uz %(displayName)s.",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s nomainīja savu parādāmo vārdu uz %(displayName)s.",
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s nomainīja šajā istabā piespraustās ziņas.",
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s vidžets, kuru mainīja %(senderName)s",
"Message Pinning": "Ziņu piespraušana",
@ -457,7 +457,7 @@
"%(senderName)s uploaded a file": "%(senderName)s augšupielādēja failu",
"Disinvite this user?": "Atsaukt uzaicinājumu šim lietotājam?",
"Kick this user?": "Padzīt šo lietotāju?",
"Unban this user?": "Atbanot/atbloķēt šo lietotāju (atcelt liegumu šim lietotājam)?",
"Unban this user?": "Atcelt liegumu šim lietotājam?",
"Ban this user?": "Nobanot/bloķēt šo lietotāju (uzlikt liegumu šim lietotājam)?",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "Jūs nevarēsiet atcelt šīs izmaiņas pēc sava statusa pazemināšanas. Gadījumā, ja esat pēdējais priviliģētais lietotājs istabā, būs neiespējami atgūt šīs privilēģijas.",
"Unignore": "Atcelt ignorēšanu",
@ -473,7 +473,7 @@
"Pinned Messages": "Piespraustās ziņas",
"%(duration)ss": "%(duration)s sek",
"%(duration)sm": "%(duration)smin",
"%(duration)sh": "%(duration)sstundas",
"%(duration)sh": "%(duration)s stundas",
"%(duration)sd": "%(duration)s dienas",
"Online for %(duration)s": "Tiešsaistē %(duration)s",
"Idle for %(duration)s": "Dīkstāvē (neaktīvs) %(duration)s",
@ -502,13 +502,13 @@
"Failed to copy": "Nokopēt neizdevās",
"An email has been sent to %(emailAddress)s": "Vēstule tika nosūtīta uz %(emailAddress)s",
"A text message has been sent to %(msisdn)s": "Teksta ziņa tika nosūtīta uz %(msisdn)s",
"Remove from community": "Izdzēst no kopienas",
"Remove from community": "Dzēst no kopienas",
"Disinvite this user from community?": "Atcelt šim lietotājam nosūtīto uzaicinājumu pievienoties kopienai?",
"Remove this user from community?": "Izdzēst šo lietotāju no kopienas?",
"Failed to withdraw invitation": "Neizdevās atcelt uzaicinājumu",
"Failed to remove user from community": "Neizdevās izdzēst lietotāju no kopienas",
"Filter community members": "Kopienas biedru filtrs",
"Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Vai tiešām vēlies izdzēst '%(roomName)s' no %(groupId)s?",
"Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Vai tiešām vēlaties dzēst '%(roomName)s' no %(groupId)s?",
"Removing a room from the community will also remove it from the community page.": "Dzēšot istabu no kopienas tā tiks dzēsta arī no kopienas lapas.",
"Failed to remove room from community": "Neizdevās dzēst istabu no kopienas",
"Failed to remove '%(roomName)s' from %(groupId)s": "Neizdevās dzēst '%(roomName)s' no %(groupId)s",
@ -528,16 +528,16 @@
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)spievienojās %(count)s reizes",
"%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)spievienojās",
"%(oneUser)sjoined %(count)s times|other": "%(oneUser)spievienojās %(count)s reizes",
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)s pievienojās",
"%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s izgāja %(count)s reizes",
"%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s izgāja",
"%(oneUser)sleft %(count)s times|other": "%(oneUser)s izgāja %(count)s reizes",
"%(oneUser)sleft %(count)s times|one": "%(oneUser)s izgāja",
"%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s pievienojās un izgāja %(count)s reizes",
"%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s pievienojās un izgāja",
"%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s pievienojās un izgāja %(count)s reizes",
"%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s pievienojās un izgāja",
"%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s izgāja un atkal pievienojās %(count)s reizes",
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)spievienojās",
"%(severalUsers)sleft %(count)s times|other": "%(severalUsers)spameta %(count)s reizes",
"%(severalUsers)sleft %(count)s times|one": "%(severalUsers)spameta",
"%(oneUser)sleft %(count)s times|other": "%(oneUser)spameta %(count)s reizes",
"%(oneUser)sleft %(count)s times|one": "%(oneUser)spameta",
"%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)spievienojās un pameta %(count)s reizes",
"%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)spievienojās un pameta",
"%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)spievienojās un pameta %(count)s reizes",
"%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)spievienojās un pameta",
"%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)spameta un atkal pievienojās %(count)s reizes",
"%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s noraidīja uzaicinājumus %(count)s reizes",
"%(severalUsers)shad their invitations withdrawn %(count)s times|other": "%(severalUsers)s atsauca izsniegtos uzaicinājumus %(count)s reizes",
"were banned %(count)s times|other": "tika bloķēti (liegta piekļuve) %(count)s reizes",
@ -560,9 +560,9 @@
"Community Name": "Kopienas nosaukums",
"Community ID": "Kopienas ID",
"example": "piemērs",
"%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s aizgājuši un atgriezušies",
"%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s aizgājis un atgriezies %(count)s reizes",
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s aizgājis un atgriezies",
"%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)spameta un atkal pievienojās",
"%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)spameta un atkal pievienojās %(count)s reizes",
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)spameta un atkal pievienojās",
"%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)s noraidīja uzaicinājumus",
"were invited %(count)s times|one": "tika uzaicināti",
"was invited %(count)s times|other": "tika uzaicināta %(count)s reizes",
@ -575,8 +575,8 @@
"were kicked %(count)s times|one": "tika padzīti",
"was kicked %(count)s times|other": "tika padzīts %(count)s reizes",
"was kicked %(count)s times|one": "tika padzīts",
"%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s izmainīja savu lietotājvārdu %(count)s reizes",
"%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s izmainīja savu lietotājvārdu",
"%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)sizmainīja savu lietotājvārdu %(count)s reizes",
"%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)sizmainīja savu lietotājvārdu",
"<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even use 'img' tags\n</p>\n": "<h1>Tavas kopienas lapas HTML</h1>\n<p>\n Izmanto garāku aprakstu, lai iepazīstinātu jaunos lietoājus ar kopienu, \n vai padalies ar kādām attiecināmām <a href=\"foo\">web-saitēm</a>\n</p>\n<p>\n Vari izmantot arī 'img' birkas\n</p>\n",
"Add rooms to the community summary": "Pievienot istabas kopienas informatīvajā kopsavilkumā",
"Which rooms would you like to add to this summary?": "Kuras istabas vēlaties pievienot šim kopsavilkumam?",
@ -590,7 +590,7 @@
"These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Šīs istabas tiek rādītas kopienas dalībniekiem šīs kopienas lapā. Kopienas dalībnieki var pievienoties istabām, uzklikšķinot uz tām.",
"Your community hasn't got a Long Description, a HTML page to show to community members.<br />Click here to open settings and give it one!": "Jūsu kopienai nav plašāka HTML-lapas apraksta ko parādīt dalībniekiem.<br />Klikšķini šeit, lai atvērtu iestatījumus un to pievienotu!",
"Description": "Apraksts",
"Failed to load %(groupId)s": "Neizdevās ielādēt %(groupId)s",
"Failed to load %(groupId)s": "%(groupId)s ielādes kļūda",
"This room is not public. You will not be able to rejoin without an invite.": "Šī istaba nav publiska un jūs nevarēsiet atkārtoti pievienoties bez uzaicinājuma.",
"Old cryptography data detected": "Tika uzieti novecojuši šifrēšanas dati",
"Data from an older version of %(brand)s has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Uzieti dati no vecākas %(brand)s versijas. Tas novedīs pie \"end-to-end\" šifrēšanas problēmām vecākajā versijā. Šajā versijā nevar tikt atšifrēti ziņojumi, kuri radīti izmantojot vecākajā versijā \"end-to-end\" šifrētas ziņas. Tas var arī novest pie ziņapmaiņas, kas veikta ar šo versiju, neizdošanās. Ja rodas ķibeles, izraksties un par jaunu pieraksties sistēmā. Lai saglabātu ziņu vēsturi, eksportē un tad importē savas šifrēšanas atslēgas.",
@ -615,9 +615,9 @@
"%(oneUser)shad their invitation withdrawn %(count)s times|other": "%(oneUser)satsauca savus uzaicinājumus %(count)s reizes",
"%(oneUser)shad their invitation withdrawn %(count)s times|one": "%(oneUser)satsauca savu uzaicinājumu",
"were invited %(count)s times|other": "bija uzaicināti %(count)s reizes",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s izmainīja savu vārdu %(count)s reizes",
"%(oneUser)schanged their name %(count)s times|one": "%(oneUser)s izmainīja savu vārdu",
"%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)s nomainīja savu avataru %(count)s reizes",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)sizmainīja savu vārdu %(count)s reizes",
"%(oneUser)schanged their name %(count)s times|one": "%(oneUser)sizmainīja savu vārdu",
"%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)snomainīja savu avataru %(count)s reizes",
"%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)snomainīja savu avataru",
"%(items)s and %(count)s others|one": "%(items)s un viens cits",
"%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)snomainīja savu avataru %(count)s reizes",
@ -630,10 +630,10 @@
"Failed to remove a user from the summary of %(groupId)s": "Neizdevās dzēst lietotāju no %(groupId)s kopsavilkuma",
"The user '%(displayName)s' could not be removed from the summary.": "Lietotājs '%(displayName)s' nevarēja tikt dzēsts no kopsavilkuma.",
"Failed to update community": "Neizdevās atjaunināt kopienu",
"Unable to accept invite": "Nav iespējams pieņemt uzaicinājumu",
"Unable to reject invite": "Nav iespējams noraidīt uzaicinājumu",
"Unable to accept invite": "Neizdevās pieņemt uzaicinājumu",
"Unable to reject invite": "Neizdevās noraidīt uzaicinājumu",
"Leave %(groupName)s?": "Pamest %(groupName)s?",
"%(inviter)s has invited you to join this community": "%(inviter)s uzaicina Tevi pievienoties šai kopienai",
"%(inviter)s has invited you to join this community": "%(inviter)s uzaicināja jūs pievienoties šai kopienai",
"You are an administrator of this community": "Tu esi šīs kopienas administrators",
"You are a member of this community": "Tu esi šīs kopienas biedrs",
"Long Description (HTML)": "Garais apraksts (HTML)",
@ -666,7 +666,7 @@
"Advanced notification settings": "Paziņojumu papildu iestatījumi",
"Failed to send logs: ": "Neizdevās nosūtīt logfailus: ",
"Forget": "Aizmirst",
"You cannot delete this image. (%(code)s)": "Šo attēlu nevar izdzēst (%(code)s)",
"You cannot delete this image. (%(code)s)": "Jūs nevarat dzēst šo attēlu. (%(code)s)",
"Cancel Sending": "Atcelt sūtīšanu",
"This Room": "Šajā istabā",
"Noisy": "Ar skaņu",
@ -734,7 +734,7 @@
"When I'm invited to a room": "Kad esmu uzaicināts/a istabā",
"Can't update user notification settings": "Neizdodas atjaunināt lietotāja paziņojumu iestatījumus",
"Notify for all other messages/rooms": "Paziņot par visām citām ziņām/istabām",
"Unable to look up room ID from server": "Nav iespējams no servera iegūt istabas Id",
"Unable to look up room ID from server": "Neizdevās no servera iegūt istabas ID",
"Couldn't find a matching Matrix room": "Atbilstoša Matrix istaba netika atrasta",
"Invite to this room": "Uzaicināt uz šo istabu",
"Thursday": "Ceturtdiena",
@ -745,7 +745,7 @@
"Show message in desktop notification": "Parādīt ziņu darbvirsmas paziņojumos",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Atutošanas logfaili satur programmas datus, ieskaitot Tavu lietotājvārdu, istabu/grupu ID vai aliases, kuras esi apmeklējis un citu lietotāju lietotājvārdus. Tie nesatur pašas ziņas.",
"Unhide Preview": "Rādīt priekšskatījumu",
"Unable to join network": "Nav iespējams pievienoties tīklam",
"Unable to join network": "Neizdodas pievienoties tīklam",
"Sorry, your browser is <b>not</b> able to run %(brand)s.": "Atvaino, diemžēl tavs tīmekļa pārlūks <b>nespēj</b> darbināt %(brand)s.",
"Uploaded on %(date)s by %(user)s": "Augšuplādēja %(user)s %(date)s",
"Messages in group chats": "Ziņas grupas čatos",
@ -785,10 +785,10 @@
"Replying With Files": "Atbildot ar failiem",
"At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Šobrīd nav iespējams atbildēt ar failu. Vai vēlaties augšupielādēt šo failu, neatbildot?",
"Your %(brand)s is misconfigured": "Jūsu %(brand)s ir nepareizi konfigurēts",
"Add Email Address": "Pievienot e-pasta adresi",
"Add Email Address": "Pievienot epasta adresi",
"Add Phone Number": "Pievienot tālruņa numuru",
"Call failed due to misconfigured server": "Zvans neizdevās nekorekti nokonfigurēta servera dēļ",
"Verify this login": "Verificējiet šo sesiju",
"Verify this login": "Verificēt šo pierakstīšanos",
"You sent a verification request": "Jūs nosūtījāt verifikācijas pieprasījumu",
"Start Verification": "Uzsākt verifikāciju",
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Jaunā sesija ir verificēta un ir dota piekļuve jūsu šifrētajām ziņām, kā arī citi lietotāji redzēs, ka šī sesija ir uzticama.",
@ -797,7 +797,7 @@
"%(count)s verified sessions|one": "1 verificēta sesija",
"%(count)s verified sessions|other": "%(count)s verificētas sesijas",
"Encrypted by an unverified session": "Šifrēts ar neverificētu sesiju",
"Waiting for your other session to verify…": "Teik gaidīts uz verificēšanu no citas jūsu sesijas…",
"Waiting for your other session to verify…": "Tiek gaidīts uz verificēšanu no citas jūsu sesijas…",
"Waiting for your other session, %(deviceName)s (%(deviceId)s), to verify…": "Tiek gaidīts uz citu jūsu sesiju, %(deviceName)s (%(deviceId)s), lai verificētu…",
"Verify your other session using one of the options below.": "Verificējiet citas jūsu sesijas, izmantojot kādu no iespējām zemāk.",
"%(names)s and %(count)s others are typing …|other": "%(names)s un %(count)s citi raksta…",
@ -972,14 +972,14 @@
"Set a new password": "Iestati jaunu paroli",
"Set a new account password...": "Iestatiet jaunu konta paroli...",
"Sign in instead": "Pierakstīties",
"A verification email will be sent to your inbox to confirm setting your new password.": "Apstiprinājuma vēstule tiks nosūtīta uz tavu epasta adresi, lai apstiprinātu paroles nomaiņu.",
"A verification email will be sent to your inbox to confirm setting your new password.": "Apstiprinājuma vēstule tiks nosūtīta uz jūsu epasta adresi, lai apstiprinātu paroles nomaiņu.",
"Forgot password?": "Aizmirsi paroli?",
"No homeserver URL provided": "Nav iestatīts bāzes servera URL",
"Cannot reach homeserver": "Neizdodas savienoties ar bāzes serveri",
"The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Fails '%(fileName)s pārsniedz augšupielādējama faila izmēra limitu šajā bāzes serverī",
"Please ask the administrator of your homeserver (<code>%(homeserverDomain)s</code>) to configure a TURN server in order for calls to work reliably.": "Lūdzu, jautājiet sava bāzes servera administratoram (<code>%(homeserverDomain)s</code>) sakonfigurēt TURN serveri, lai zvani strādātu stabili.",
"Join millions for free on the largest public server": "Pievienojieties bez maksas miljoniem lietotāju lielākajā publiskajā serverī",
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Element with an existing Matrix account on a different homeserver.": "Jūs varat pielāgot servera parametrus, lai pierakstītos citos Matrix bāzes serveros, norādot atbilstošu bāzes servera URL. Tas ļauj jums izmantot Element ar eksistējošu Matrix kontu uz cita bāzes servera.",
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Element with an existing Matrix account on a different homeserver.": "Jūs varat pielāgot servera parametrus, lai pierakstītos citos Matrix bāzes serveros, norādot atbilstošu bāzes servera URL. Tas ļauj jums izmantot Element ar uz cita bāzes servera izveidotu Matrix kontu.",
"Server Options": "Servera parametri",
"%(ssoButtons)s Or %(usernamePassword)s": "%(ssoButtons)s vai %(usernamePassword)s",
"That username already exists, please try another.": "Šis lietotājvārds jau eksistē, mēģiniet citu.",
@ -995,7 +995,7 @@
"Invite people to join %(communityName)s": "Aiciniet cilvēkus pievienoties %(communityName)s",
"People you know on %(brand)s": "Cilvēki %(brand)s, kurus jūs pazīstat",
"Smileys & People": "Smaidiņi & cilvēki",
"%(count)s people|one": "%(count)s cilvēki",
"%(count)s people|one": "%(count)s cilvēks",
"%(count)s people|other": "%(count)s cilvēki",
"People": "Cilvēki",
"Add a photo, so people can easily spot your room.": "Pievienojiet foto, lai padarītu istabu vieglāk pamanāmu citiem cilvēkiem.",
@ -1073,7 +1073,7 @@
"Show display name changes": "Rādīt parādāmā vārda izmaiņas",
"%(displayName)s cancelled verification.": "%(displayName)s atcēla verificēšanu.",
"Your display name": "Jūsu parādāmais vārds",
"Add an email address to configure email notifications": "Pievienojiet e-pasta adresi, lai konfigurētu e-pasta paziņojumus",
"Add an email address to configure email notifications": "Pievienojiet epasta adresi, lai konfigurētu epasta paziņojumus",
"Enable audible notifications for this session": "Iespējot dzirdamus paziņojumus šai sesijai",
"Enable desktop notifications for this session": "Iespējot darbvirsmas paziņojumus šai sesijai",
"Enable 'Manage Integrations' in Settings to do this.": "Iespējojiet 'Pārvaldīt integrācijas' iestatījumos, lai to izdarītu.",
@ -1172,8 +1172,8 @@
"Your server requires encryption to be enabled in private rooms.": "Jūsu serveris pieprasa iespējotu šifrēšānu privātās istabās.",
"Enable end-to-end encryption": "Iespējot pilnīgu šifrēšanu",
"Make this room public": "Padarīt istabu publiski pieejamu",
"Create a private room": "Privātas istabas izveidošana",
"Create a public room": "Publiskas istabas izveidošana",
"Create a private room": "Izveidot privātu istabu",
"Create a public room": "Izveidot publisku istabu",
"Add a new server...": "Pievienot jaunu serveri...",
"Add a new server": "Pievienot jaunu serveri",
"Your homeserver": "Jūsu bāzes serveris",
@ -1188,7 +1188,7 @@
"Show files": "Rādīt failus",
"Help & About": "Palīdzība un par lietotni",
"About homeservers": "Par bāzes serveriem",
"About": "Detaļas",
"About": "Par",
"%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s uzsāka balss zvanu. (Netiek atbalstīts šajā pārlūkā)",
"%(senderName)s placed a voice call.": "%(senderName)s uzsāka balss zvanu.",
"Incoming voice call": "Ienākošais balss zvans",
@ -1277,7 +1277,7 @@
"Already have an account? <a>Sign in here</a>": "Jau ir konts? <a>Pierakstieties šeit</a>",
"Continue with %(ssoButtons)s": "Turpināt ar %(ssoButtons)s",
"Registration has been disabled on this homeserver.": "Šajā bāzes serverī reģistrācija ir atspējota.",
"Unable to query for supported registration methods.": "Nevar pieprasīt atbalstītās reģistrācijas metodes.",
"Unable to query for supported registration methods.": "Neizdevās pieprasīt atbalstītās reģistrācijas metodes.",
"New? <a>Create account</a>": "Pirmā reize? <a>Izveidojiet kontu</a>",
"If you've joined lots of rooms, this might take a while": "Ja esat pievienojies daudzām istabām, tas var aizņemt kādu laiku",
"Signing In...": "Pierakstīšanās…",
@ -1307,9 +1307,9 @@
"Who can join this community?": "Kas var pievienoties šai kopienai?",
"Leave this community": "Pamest šo kopienu",
"Join this community": "Pievienoties šai kopienai",
"Unable to leave community": "Neizdodas pamest kopienu",
"Unable to leave community": "Neizdevās pamest kopienu",
"You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Jūs esat šīs kopienas administrators. Jūs nevarēsit atkārtoti pievienoties bez cita administratora ielūguma.",
"Unable to join community": "Neizdodas pievienoties kopienai",
"Unable to join community": "Neizdevās pievienoties kopienai",
"Create community": "Izveidot kopienu",
"Couldn't load page": "Neizdevās ielādēt lapu",
"Sign in with SSO": "Pierakstieties, izmantojot SSO",
@ -1330,7 +1330,7 @@
"Enter password": "Ievadiet paroli",
"Something went wrong in confirming your identity. Cancel and try again.": "Kaut kas nogāja greizi, mēģinot apstiprināt jūsu identitāti. Atceliet un mēģiniet vēlreiz.",
"Session key": "Sesijas atslēga",
"Secure Backup": "Droša reze",
"Secure Backup": "Droša rezerves kopija",
"Accept all %(invitedRooms)s invites": "Pieņemt visus %(invitedRooms)s uzaicinājumus",
"Bulk options": "Lielapjoma opcijas",
"Clear cache and reload": "Notīrīt kešatmiņu un pārlādēt",
@ -1394,5 +1394,170 @@
"Macau": "Makao",
"Luxembourg": "Luksemburga",
"Lithuania": "Lietuva",
"Latvia": "Latvija"
"Latvia": "Latvija",
"Link to selected message": "Saite uz izvēlēto ziņu",
"Share Room Message": "Dalīties ar istabas ziņu",
"Share Message": "Dalīties ar ziņu",
"Unable to load! Check your network connectivity and try again.": "Ielāde neizdevās! Pārbaudiet interneta savienojumu un mēģiniet vēlreiz.",
"Open": "Atvērt",
"Are you sure you want to sign out?": "Vai tiešām vēlaties izrakstīties?",
"Almost there! Is %(displayName)s showing the same shield?": "Gandrīz galā! Vai %(displayName)s tiek parādīts tas pats vairogs?",
"Almost there! Is your other session showing the same shield?": "Gandrīz galā! Vai jūsu otrā sesijā tiek parādīts tas pats vairogs?",
"Verify by emoji": "Verificēt ar emocijzīmēm",
"Verify by comparing unique emoji.": "Verificēt, salīdzinot unikālās emocijzīmes.",
"If you can't scan the code above, verify by comparing unique emoji.": "Ja nevarat noskenēt kodu, veiciet verifkāciju, salīdzinot unikālās emocijzīmes.",
"Verify this user by confirming the following emoji appear on their screen.": "Verificēt šo lietotāju, apstiprinot, ka sekojošās emocijzīmes pārādās lietotāja ekrānā.",
"Ask %(displayName)s to scan your code:": "Aiciniet %(displayName)s noskenēt jūsu kodu:",
"Verify by scanning": "Verificēt noskenējot",
"%(name)s wants to verify": "%(name)s vēlas veikt verifikāciju",
"Decline All": "Noraidīt visu",
"%(name)s declined": "%(name)s noraidīja",
"You declined": "Jūs noraidījāt",
"Decline (%(counter)s)": "Noraidīt (%(counter)s)",
"Incoming Verification Request": "Ienākošais veifikācijas pieprasījums",
"%(name)s is requesting verification": "%(name)s pieprasa verifikāciju",
"Self-verification request": "Pašverifikācijas pieprasījums",
"Verification Requests": "Verifikācijas pieprasījumi",
"Verification Request": "Verifikācijas pieprasījums",
"Your Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your Security Phrase.": "Jūsu drošības atslēga ir drošības tīkls - jūs to var izmantot, lai atjaunotu piekļuvi šifrētām ziņām, ja esat aizmirsis savu slepeno frāzi.",
"Store your Security Key somewhere safe, like a password manager or a safe, as its used to safeguard your encrypted data.": "Glabājiet drošības atslēgu kaut kur drošā vietā, piemēram, paroļu pārvaldniekā vai seifā, jo tā tiek izmantota jūsu šifrēto datu aizsardzībai.",
"Download": "Lejupielādēt",
"Copy": "Kopēt",
"Activate selected button": "Aktivizēt izvēlēto pogu",
"Currently indexing: %(currentRoom)s": "Pašlaik indeksē: %(currentRoom)s",
"A private space for you and your teammates": "Privāta vieta jums un jūsu komandas dalībniekiem",
"A private space to organise your rooms": "Privāta vieta, kur organizēt jūsu istabas",
"Default Rooms": "Noklusējuma istabas",
"Add existing rooms & spaces": "Pievienot eksistējošas istabas un vietas",
"<inviter/> invites you": "<inviter/> uzaicina jūs",
"%(count)s rooms and 1 space|one": "%(count)s istaba un viena vieta",
"%(count)s rooms and 1 space|other": "%(count)s istabas un 1 vieta",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s istaba un %(numSpaces)s vietas",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s istabas un %(numSpaces)s vietas",
"%(count)s rooms|one": "%(count)s istaba",
"%(count)s rooms|other": "%(count)s istabas",
"Are you sure you want to leave the space '%(spaceName)s'?": "Vai tiešām vēlaties pamest vietu '%(spaceName)s'?",
"Create a Group Chat": "Izveidot grupas čatu",
"Missing session data": "Trūkst sesijas datu",
"Create a new room with the same name, description and avatar": "Izveidot istabu ar to pašu nosaukumu, aprakstu un avataru",
"Email (optional)": "Epasts (izvēles)",
"Invite to %(roomName)s": "Uzaicināt uz %(roomName)s",
"Invite to %(spaceName)s": "Uzaicināt uz %(spaceName)s",
"Abort": "Pārtraukt",
"Add comment": "Pievienot komentāru",
"Continue With Encryption Disabled": "Turpināt ar atspējotu šifrēšanu",
"Create a room in %(communityName)s": "Izveidot istabu kopienā %(communityName)s",
"Add image (optional)": "Pievienot attēlu (izvēles)",
"Add another email": "Pievienot citu epasta adresi",
"Create a new room": "Izveidot jaunu istabu",
"Add existing spaces/rooms": "Pievienot eksistējošas vietas/istabas",
"Are you sure you want to remove <b>%(serverName)s</b>": "Vai tiešām vēlaties dzēst <b>%(serverName)s</b>",
"All rooms": "Visas istabas",
"Continue with %(provider)s": "Turpināt ar %(provider)s",
"%(oneUser)smade no changes %(count)s times|one": "%(oneUser)sneveica nekādas izmaiņas",
"%(oneUser)smade no changes %(count)s times|other": "%(oneUser)sneveica nekādas izmaiņas %(count)s reizes",
"%(severalUsers)smade no changes %(count)s times|one": "%(severalUsers)sneveica nekādas izmaiņas",
"%(severalUsers)smade no changes %(count)s times|other": "%(severalUsers)sneveica nekādas izmaiņas %(count)s reizes",
"<reactors/><reactedWith> reacted with %(content)s</reactedWith>": "<reactors/><reactedWith> reaģēja ar %(content)s</reactedWith>",
"Declining …": "Noraida …",
"Accepting …": "Akceptē …",
"%(name)s cancelled": "%(name)s atcēla",
"%(name)s cancelled verifying": "%(name)s atcēla verifikāciju",
"Deactivate user": "Deaktivizēt lietotāju",
"Deactivate user?": "Deaktivizēt lietotāju?",
"Demote": "Pazemināt",
"Demote yourself?": "Pazemināt sevi?",
"Accepting…": "Akceptē…",
"%(count)s unread messages.|one": "1 nelasīta ziņa.",
"%(count)s unread messages.|other": "%(count)s nelasītas ziņas.",
"%(count)s unread messages including mentions.|one": "1 neslasīts pieminējums.",
"%(count)s unread messages including mentions.|other": "%(count)s nelasītas ziņas ieskaitot pieminējumus.",
"A-Z": "A-Ž",
"%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s priekšskatījums nav pieejams. Vai vēlaties tai pievienoties?",
"%(count)s results|one": "%(count)s rezultāts",
"%(count)s results|other": "%(count)s rezultāti",
"Empty room": "Tukša istaba",
"Add existing room": "Pievienot eksistējošu istabu",
"Add room": "Pievienot istabu",
"Invite to this space": "Uzaicināt uz šo vietu",
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "Teksta ziņa tika nosūtīta uz +%(msisdn)s. Lūdzu, ievadiet tajā esošo verifikācijas kodu.",
"Always show the window menu bar": "Vienmēr parādīt loga izvēlnes joslu",
"Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Piekrītiet identitāšu servera (%(serverName)s) pakalpojumu sniegšanas noteikumiem, lai padarītu sevi atrodamu citiem, izmantojot epasta adresi vai tālruņa numuru.",
"Add theme": "Pievienot tēmu",
"Algorithm:": "Algoritms:",
"Display Name": "Parādāmais vārds",
"Add some details to help people recognise it.": "Pievienojiet aprakstu, lai palīdzētu cilvēkiem to atpazīt.",
"Create a space": "Izveidot vietu",
"Delete": "Dzēst",
"Accept <policyLink /> to continue:": "Akceptēt <policyLink />, lai turpinātu:",
"Anchor": "Enkurs",
"Aeroplane": "Aeroplāns",
"%(senderName)s ended the call": "%(senderName)s pabeidza zvanu",
"A word by itself is easy to guess": "Vārds pats par sevi ir viegli uzminams",
"Add another word or two. Uncommon words are better.": "Papildiniet ar vēl kādiem vārdiem. Netipiski vārdi ir labāk.",
"All-uppercase is almost as easy to guess as all-lowercase": "Visus lielos burtus ir gandrīz tikpat viegli uzminēt kā visus mazos",
"%(name)s (%(userId)s)": "%(name)s (%(userId)s)",
"%(num)s days from now": "%(num)s dienas kopš šī brīža",
"about a day from now": "aptuveni dienu kopš šī brīža",
"%(num)s hours from now": "%(num)s stundas kopš šī brīža",
"about an hour from now": "aptuveni stundu kopš šī brīža",
"%(num)s minutes from now": "%(num)s minūtes kopš šī brīža",
"about a minute from now": "aptuveni minūti kopš šī brīža",
"a few seconds from now": "dažas sekundes kopš šī brīža",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) pierakstījās jaunā sesijā, neveicot tās verifikāciju:",
"(an error occurred)": "(notika kļūda)",
"Actions": "Darbības",
"Denmark": "Dānija",
"American Samoa": "Amerikāņu Samoa",
"Algeria": "Alžīrija",
"Verify with another session": "Verificēt ar citu sesiju",
"Original event source": "Oriģinālais notikuma pirmkods",
"Decrypted event source": "Atšifrēt notikuma pirmkods",
"Removing...": "Dzēš…",
"You don't have permission": "Jums nav atļaujas",
"You do not have permission to create rooms in this community.": "Jums nav atļaujas veidot istabas šajā kopienā.",
"Attach files from chat or just drag and drop them anywhere in a room.": "Pievienojiet failus no čata vai vienkārši velciet un nometiet tos jebkur istabā.",
"No files visible in this room": "Šajā istabā nav redzamu failu",
"Remove for everyone": "Dzēst visiem",
"Verify other session": "Verificēt citu sesiju",
"Share User": "Dalīties ar lietotāja kontaktdatiem",
"Verify session": "Verificēt sesiju",
"Verifying this device will mark it as trusted, and users who have verified with you will trust this device.": "Verificējot šo ierīci, tā tiks atzīmēta kā uzticama, un ierīci verificējušie lietotāji tai uzticēsies.",
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.": "Verificējot šo lietotāju, tā sesija tiks atzīmēta kā uzticama, kā arī jūsu sesija viņiem tiks atzīmēta kā uzticama.",
"Removing…": "Dzēš…",
"Remove server": "Dzēst serveri",
"Homeserver": "Bāzes serveris",
"Use the <a>Desktop app</a> to see all encrypted files": "Lietojiet <a>Desktop lietotni</a>, lai apskatītu visus šifrētos failus",
"Room ID": "Istabas ID",
"edited": "rediģēts",
"Edited at %(date)s. Click to view edits.": "Rediģēts %(date)s. Noklikšķiniet, lai skatītu redakcijas.",
"Edited at %(date)s": "Rediģēts %(date)s",
"You cancelled": "Jūs atcēlāt",
"You cancelled verifying %(name)s": "Jūs atvēlāt %(name)s verifikāciju",
"You cancelled verification.": "Jūs atcēlāt verifikāciju.",
"You cancelled verification on your other session.": "Jūs atcēlāt verifikāciju citā savā sesijā.",
"Edit devices": "Rediģēt ierīces",
"Remove %(count)s messages|one": "Dzēst 1 ziņu",
"Remove %(count)s messages|other": "Dzēst %(count)s ziņas",
"For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.": "Lielam ziņu apjomam tas var aizņemt kādu laiku. Lūdzu, tikmēr neatsvaidziniet klientu.",
"You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|one": "Tiks neatgriezeniski dzēsta 1 ziņa no %(user)s. Vai vēlaties turpināt?",
"You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|other": "Tiks neatgriezeniski dzēsta %(count)s ziņas no %(user)s. Vai vēlaties turpināt?",
"You don't have permission to delete the address.": "Jums nav atļaujas dzēst adresi.",
"Add some now": "Pievienot kādu tagad",
"You don't currently have any stickerpacks enabled": "Neviena uzlīmju paka nav iespējota",
"This invite to %(roomName)s was sent to %(email)s": "Šis uzaicinājums uz %(roomName)s tika nosūtīts %(email)s",
"This invite to %(roomName)s was sent to %(email)s which is not associated with your account": "Šis uzaicinājums uz %(roomName)s tika nosūtīts %(email)s, kas nav saistīts ar jūsu kontu",
"Your message was sent": "Jūsu ziņa ir nosūtīta",
"Remove %(phone)s?": "Dzēst %(phone)s?",
"Remove %(email)s?": "Dēst %(email)s?",
"Waiting for %(displayName)s to verify…": "Gaida uz %(displayName)s, lai verificētu…",
"Verify this user by confirming the following number appears on their screen.": "Verificēt šo lietotāju, apstiprinot, ka šāds numurs pārādās lietotāja ekrānā.",
"Verify this session by confirming the following number appears on its screen.": "Verificējiet šo sesiju, apstiprinot, ka tās ekrānā parādās šāds numurs.",
"You ended the call": "Jūs pabeidzāt zvanu",
"Other users may not trust it": "Citi lietotāji var neuzskatīt to par uzticamu",
"Verify": "Verificēt",
"Verify this session": "Verificēt šo sesiju",
"You signed in to a new session without verifying it:": "Jūs pierakstījāties jaunā sesijā, neveicot tās verifikāciju:",
"You're already in a call with this person.": "Jums jau notiek zvans ar šo personu.",
"Already in call": "Notiek zvans"
}

View file

@ -82,7 +82,7 @@
"Yesterday": "I går",
"Low Priority": "Lav Prioritet",
"%(brand)s does not know how to join a room on this network": "%(brand)s vet ikke hvordan man kan komme inn på et rom på dette nettverket",
"An error occurred whilst saving your email notification preferences.": "En feil oppsto i forbindelse med lagring av epost varsel innstillinger.",
"An error occurred whilst saving your email notification preferences.": "En feil oppsto i forbindelse med lagring av innstillinger for e-postvarsel.",
"remove %(name)s from the directory.": "fjern %(name)s fra katalogen.",
"Off": "Av",
"Failed to remove tag %(tagName)s from room": "Kunne ikke fjerne tagg %(tagName)s fra rommet",
@ -116,7 +116,7 @@
"You cannot place VoIP calls in this browser.": "Du kan ikke ringe via VoIP i denne nettleseren.",
"You cannot place a call with yourself.": "Du kan ikke ringe deg selv.",
"Call in Progress": "Samtale pågår",
"A call is currently being placed!": "En samtale holder allerede på å starte",
"A call is currently being placed!": "En samtale holder på å starte!",
"A call is already in progress!": "En samtale er allerede i gang!",
"Permission Required": "Tillatelse kreves",
"You do not have permission to start a conference call in this room": "Du har ikke tillatelse til å starte en konferansesamtale i dette rommet",
@ -167,8 +167,8 @@
"%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s har ikke tillatelse til å sende deg varsler - vennligst sjekk nettleserinnstillingene",
"%(brand)s was not given permission to send notifications - please try again": "%(brand)s fikk ikke tillatelse til å sende deg varsler - vennligst prøv igjen",
"Unable to enable Notifications": "Klarte ikke slå på Varslinger",
"This email address was not found": "Denne e-post adressen ble ikke funnet",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "E-post adressen din ser ikke ut til å være koplet til en Matrix-ID på denne hjemmetjeneren.",
"This email address was not found": "Denne e-postadressen ble ikke funnet",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "E-postadressen din ser ikke ut til å være koplet til en Matrix-ID på denne hjemmetjeneren.",
"Register": "Registrer",
"Default": "Standard",
"Restricted": "Begrenset",
@ -494,7 +494,7 @@
"Logout": "Logg ut",
"Preview": "Forhåndsvisning",
"View": "Vis",
"Explore rooms": "Utforsk rom",
"Explore rooms": "Se alle rom",
"Room": "Rom",
"Clear filter": "Tøm filtret",
"Guest": "Gjest",
@ -590,7 +590,7 @@
"Change identity server": "Bytt ut identitetstjener",
"You should:": "Du burde:",
"Identity Server": "Identitetstjener",
"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.": "Å bruke en identitetstjener er valgfritt. Dersom du velger å ikke en identitetstjener, vil du ikke kunne oppdages av andre brukere, og du vil ikke kunne invitere andre ut i fra E-postadresse eller telefonnummer.",
"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.": "Å bruke en identitetstjener er valgfritt. Dersom du velger å ikke bruke en identitetstjener, vil du ikke kunne oppdages av andre brukere, og du vil ikke kunne invitere andre ut i fra E-postadresse eller telefonnummer.",
"Do not use an identity server": "Ikke bruk en identitetstjener",
"Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, and sticker packs.": "Bruk en integreringsbehandler <b>(%(serverName)s)</b> til å behandle botter, moduler, og klistremerkepakker.",
"Use an Integration Manager to manage bots, widgets, and sticker packs.": "Bruk en integreringsbehandler til å behandle botter, moduler, og klistremerkepakker.",
@ -1038,7 +1038,7 @@
"Kick this user?": "Vil du sparke ut denne brukeren?",
"No pinned messages.": "Ingen klistrede meldinger.",
"Pinned Messages": "Klistrede meldinger",
"Unpin Message": "Avklistre meldingen",
"Unpin Message": "Løsne meldingen",
"Try to join anyway": "Forsøk å bli med likevel",
"%(count)s unread messages including mentions.|one": "1 ulest nevnelse.",
"%(count)s unread messages.|other": "%(count)s uleste meldinger.",
@ -1441,5 +1441,71 @@
"User menu": "Brukermeny",
"Use Recovery Key": "Bruk gjenopprettingsnøkkel",
"%(brand)s iOS": "%(brand)s iOS",
"%(brand)s Android": "%(brand)s Android"
"%(brand)s Android": "%(brand)s Android",
"Add image (optional)": "Legg til bilde (valgfritt)",
"Enter name": "Skriv navn",
"Please select the destination room for this message": "Vennligst velg mottagerrom for denne meldingen",
"Your message was sent": "Meldingen ble sendt",
"Encrypting your message...": "Krypterer meldingen...",
"Sending your message...": "Sender meldingen...",
"The authenticity of this encrypted message can't be guaranteed on this device.": "Autentisiteten av denne krypterte meldingen kan ikke garanteres på denne enheten.",
"Encrypted by a deleted session": "Kryptert av en slettet sesjon",
"Jordan": "Jordan",
"Jersey": "Jersey",
"Japan": "Japan",
"Italy": "Italia",
"Israel": "Israel",
"Ireland": "Irland",
"Iraq": "Irak",
"Indonesia": "Indonesia",
"Iran": "Iran",
"India": "India",
"Backup could not be decrypted with this Security Key: please verify that you entered the correct Security Key.": "Sikkerhetskopien kunne ikke dekrypteres med denne sikkerhetsnøkkelen: Vennligst verifiser at du tastet korrekt sikkerhetsnøkkel.",
"Security Key mismatch": "Sikkerhetsnøkkel uoverensstemmelse",
"Unable to load backup status": "Klarte ikke å laste sikkerhetskopi-status",
"%(completed)s of %(total)s keys restored": "%(completed)s av %(total)s nøkler gjenopprettet",
"Revoke permissions": "Trekk tilbake rettigheter",
"Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Klarte ikke å trekke tilbake invitasjonen. Tjener kan ha et forbigående problem, eller det kan hende at du ikke har tilstrekkelige rettigheter for å trekke tilbake invitasjonen.",
"Failed to revoke invite": "Klarte ikke å trekke tilbake invitasjon",
"Unable to revoke sharing for phone number": "Klarte ikke trekke tilbake deling for telefonnummer",
"Unable to revoke sharing for email address": "Klarte ikke å trekke tilbake deling for denne e-postadressen",
"%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s trakk tilbake invitasjonen dette rommet for %(targetDisplayName)s.",
"Unpin a widget to view it in this panel": "Løsne en widget for å se den i dette panelet",
"Unpin": "Løsne",
"Mentions & Keywords": "Der du nevnes & nøkkelord",
"Great, that'll help people know it's you": "Flott, det vil hjelp folk å ha tillit til at det er deg",
"Put a link back to the old room at the start of the new room so people can see old messages": "Legg inn en lenke tilbake til det gamle rommet i starten av det nye rommet slik at folk kan finne eldre meldinger",
"People you know on %(brand)s": "Folk du kjenner i %(brand)s",
"Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their avatar.": "Meldinger i dette rommer er ende-til-ende-kryptert. Når folk kommer med kan du verifisere dem ved klikke på avataren i profilen deres.",
"%(count)s people|one": "%(count)s person",
"Invite People": "Inviter Personer",
"Add a photo, so people can easily spot your room.": "Legg til et bilde så folk lettere kan finne rommet ditt.",
"<a>Add a topic</a> to help people know what it is about.": "<a>Legg til et tema</a> for hjelpe folk å forstå hva dette handler om.",
"Invite people": "Inviter personer",
"Add some details to help people recognise it.": "Legg til mer detaljer for å gjøre det letter å gjenkjenne.",
"You do not have permission to invite people to this room.": "Du har ikke tilgang til å invitere personer til dette rommet.",
"Click the button below to confirm adding this email address.": "Klikk på knappen under for å bekrefte at du vil legge til denne e-postadressen.",
"Hey you. You're the best!": "Hei der. Du er fantastisk!",
"Use custom size": "Bruk tilpasset størrelse",
"Use Single Sign On to continue": "Bruk Single Sign On for å fortsette",
"Appearance Settings only affect this %(brand)s session.": "Stilendringer gjelder kun i denne %(brand)s sesjonen.",
"Use Ctrl + Enter to send a message": "Bruk Ctrl + Enter for å sende en melding",
"Use Ctrl + F to search": "Bruk Ctrl + F for å søke",
"%(count)s people|other": "%(count)s personer",
"%(count)s unread messages including mentions.|other": "%(count)s uleste meldinger inkludert der du nevnes.",
"Creating...": "Oppretter...",
"User settings": "Brukerinnstillinger",
"Open": "Åpne",
"Try using one of the following valid address types: %(validTypesList)s.": "Prøv å bruke en av følgende gyldige adresser: %(validTypesList)s.",
"The user '%(displayName)s' could not be removed from the summary.": "Brukeren '%(displayName)s' kunne ikke fjernes fra oversikten.",
"Belgium": "Belgia",
"American Samoa": "Amerikansk Samoa",
"United States": "USA",
"%(name)s is requesting verification": "%(name)s ber om verifisering",
"Try again": "Prøv igjen",
"We couldn't log you in": "Vi kunne ikke logge deg inn",
"This will end the conference for everyone. Continue?": "Dette vil avslutte konferansen for alle. Fortsett?",
"End conference": "Avslutt konferanse",
"You're already in a call with this person.": "Du er allerede i en samtale med denne personen.",
"Already in call": "Allerede i en samtale"
}

View file

@ -57,7 +57,7 @@
"Anyone": "Iedereen",
"Are you sure you want to leave the room '%(roomName)s'?": "Weet u zeker dat u het gesprek %(roomName)s wilt verlaten?",
"Close": "Sluiten",
"Create new room": "Een nieuw gesprek aanmaken",
"Create new room": "Nieuw gesprek aanmaken",
"Custom Server Options": "Aangepaste serverinstellingen",
"Dismiss": "Afwijzen",
"Error": "Fout",
@ -230,7 +230,7 @@
"Room Colour": "Gesprekskleur",
"%(roomName)s does not exist.": "%(roomName)s bestaat niet.",
"%(roomName)s is not accessible at this time.": "%(roomName)s is op dit moment niet toegankelijk.",
"Rooms": "Groepen",
"Rooms": "Gesprekken",
"Save": "Opslaan",
"Search failed": "Zoeken mislukt",
"Searches DuckDuckGo for results": "Zoekt op DuckDuckGo voor resultaten",
@ -393,7 +393,7 @@
"Delete widget": "Widget verwijderen",
"Edit": "Bewerken",
"Enable automatic language detection for syntax highlighting": "Automatische taaldetectie voor zinsbouwmarkeringen inschakelen",
"Publish this room to the public in %(domain)s's room directory?": "Dit gesprek vermelden in de gesprekkencatalogus van %(domain)s?",
"Publish this room to the public in %(domain)s's room directory?": "Dit gesprek vermelden in de openbare gesprekkencatalogus van %(domain)s?",
"AM": "AM",
"PM": "PM",
"The maximum permitted number of widgets have already been added to this room.": "Het maximum aan toegestane widgets voor dit gesprek is al bereikt.",
@ -672,7 +672,7 @@
"Files": "Bestanden",
"You are not receiving desktop notifications": "U ontvangt momenteel geen bureaubladmeldingen",
"Friday": "Vrijdag",
"Update": "Bijwerken",
"Update": "Updaten",
"What's New": "Wat is er nieuw",
"On": "Aan",
"Changelog": "Wijzigingslogboek",
@ -779,7 +779,7 @@
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Met uw huidige browser kan de toepassing er volledig onjuist uitzien. Tevens is het mogelijk dat niet alle functies naar behoren werken. U kunt doorgaan als u het toch wilt proberen, maar bij problemen bent u volledig op uzelf aangewezen!",
"Checking for an update...": "Bezig met controleren op updates…",
"Logs sent": "Logboeken verstuurd",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Foutopsporingslogboeken bevatten gebruiksgegevens over de toepassing, inclusief uw gebruikersnaam, de IDs of bijnamen van de gesprekken en groepen die u heeft bezocht, evenals de gebruikersnamen van andere gebruikers. Ze bevatten geen berichten.",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Foutopsporingslogboeken bevatten gebruiksgegevens over de toepassing, inclusief uw gebruikersnaam, de IDs of bijnamen van de gesprekken die u heeft bezocht, evenals de gebruikersnamen van andere gebruikers. Ze bevatten geen berichten.",
"Failed to send logs: ": "Versturen van logboeken mislukt: ",
"Preparing to send logs": "Logboeken worden voorbereid voor versturen",
"e.g. %(exampleValue)s": "bv. %(exampleValue)s",
@ -1137,7 +1137,7 @@
"Upgrade Room Version": "Gespreksversie upgraden",
"Create a new room with the same name, description and avatar": "Een nieuw gesprek aanmaken met dezelfde naam, beschrijving en avatar",
"Update any local room aliases to point to the new room": "Alle lokale gespreksbijnamen naar het nieuwe gesprek laten verwijzen",
"Stop users from speaking in the old version of the room, and post a message advising users to move to the new room": "Gebruikers verhinderen aan de oude versie van het gesprek bij te dragen, en daar een bericht plaatsen dat de gebruikers verwijst naar het nieuwe gesprek",
"Stop users from speaking in the old version of the room, and post a message advising users to move to the new room": "Mensen verhinderen aan de oude versie van het gesprek bij te dragen en daar een bericht te plaatsen dat de gebruikers verwijst naar het nieuwe gesprek",
"Put a link back to the old room at the start of the new room so people can see old messages": "Bovenaan het nieuwe gesprek naar het oude verwijzen, om oude berichten te lezen",
"A username can only contain lower case letters, numbers and '=_-./'": "Een gebruikersnaam mag enkel kleine letters, cijfers en =_-./ bevatten",
"Checking...": "Bezig met controleren…",
@ -1177,7 +1177,7 @@
"Homeserver URL": "Thuisserver-URL",
"Identity Server URL": "Identiteitsserver-URL",
"Free": "Gratis",
"Join millions for free on the largest public server": "Doe mee met miljoenen anderen op de grootste publieke server",
"Join millions for free on the largest public server": "Doe mee met miljoenen anderen op de grootste openbare server",
"Premium": "Premium",
"Premium hosting for organisations <a>Learn more</a>": "Premium hosting voor organisaties <a>Lees meer</a>",
"Other": "Overige",
@ -2319,12 +2319,12 @@
"We couldn't log you in": "We konden u niet inloggen",
"Room Info": "Gespreksinfo",
"Matrix.org is the biggest public homeserver in the world, so its a good place for many.": "Matrix.org is de grootste openbare homeserver van de wereld, dus het is een goede plek voor vele.",
"Explore Public Rooms": "Verken openbare groepen",
"Explore Public Rooms": "Verken openbare gesprekken",
"Private rooms can be found and joined by invitation only. Public rooms can be found and joined by anyone in this community.": "Privégesprekken zijn alleen zichtbaar en toegankelijk met een uitnodiging. Openbare gesprekken zijn zichtbaar en toegankelijk voor iedereen in deze gemeenschap.",
"This room is public": "Dit gesprek is openbaar",
"Show previews of messages": "Voorvertoning van berichten inschakelen",
"Show message previews for reactions in all rooms": "Toon berichtvoorbeelden voor reacties in alle gesprekken",
"Explore public rooms": "Verken openbare groepen",
"Explore public rooms": "Verken openbare gesprekken",
"Leave Room": "Gesprek verlaten",
"Room options": "Gesprekopties",
"Start a conversation with someone using their name, email address or username (like <userId/>).": "Start een gesprek met iemand door hun naam, emailadres of gebruikersnaam (zoals <userId/>) te typen.",
@ -2349,8 +2349,8 @@
"Show rooms with unread messages first": "Gesprekken met ongelezen berichten als eerste tonen",
"%(count)s results|one": "%(count)s resultaten",
"%(count)s results|other": "%(count)s resultaten",
"Explore all public rooms": "Verken alle openbare groepen",
"Start a new chat": "Een nieuw gesprek beginnen",
"Explore all public rooms": "Verken alle openbare gespreken",
"Start a new chat": "Nieuw gesprek beginnen",
"Can't see what youre looking for?": "Niet kunnen vinden waar u naar zocht?",
"Custom Tag": "Aangepast label",
"Explore community rooms": "Gemeenschapsgesprekken verkennen",
@ -2900,7 +2900,7 @@
"Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first.": "Gepubliceerde adressen kunnen door iedereen op elke server gebruikt worden om aan je groep deel te nemen. Om een adres te publiceren moet het eerste ingesteld worden als lokaaladres.",
"Published Addresses": "Gepubliceerde adressen",
"Mentions & Keywords": "Vermeldingen & Trefwoorden",
"Use the + to make a new room or explore existing ones below": "Gebruik de + om een nieuw gesprek te starten of ontdek de bestaande groepen hieronder",
"Use the + to make a new room or explore existing ones below": "Gebruik de + om een nieuw gesprek te beginnen of ontdek de bestaande gesprekken hieronder",
"Open dial pad": "Kiestoetsen openen",
"Recently visited rooms": "Onlangs geopende gesprekken",
"Add a photo, so people can easily spot your room.": "Voeg een foto toe, zodat personen u gemakkelijk kunnen herkennen in het gesprek.",
@ -3039,7 +3039,7 @@
"Failed to add rooms to space": "Het toevoegen van gesprekken aan de space is mislukt",
"Apply": "Toepassen",
"Applying...": "Toepassen...",
"Create a new room": "Een nieuw gesprek aanmaken",
"Create a new room": "Nieuw gesprek aanmaken",
"Don't want to add an existing room?": "Wilt u geen bestaand gesprek toevoegen?",
"Spaces": "Spaces",
"Filter your rooms and spaces": "Gesprekken en spaces filteren",
@ -3063,7 +3063,7 @@
"New room": "Nieuw gesprek",
"Leave space": "Space verlaten",
"Invite people": "Personen uitnodigen",
"Share your public space": "Deel uw publieke space",
"Share your public space": "Deel uw openbare space",
"Invite members": "Leden uitnodigen",
"Invite by email or username": "Uitnodigen per e-mail of gebruikersnaam",
"Share invite link": "Deel uitnodigingskoppeling",
@ -3087,5 +3087,52 @@
"This homeserver has been blocked by it's administrator.": "Deze homeserver is geblokkeerd door zijn beheerder.",
"This homeserver has been blocked by its administrator.": "Deze homeserver is geblokkeerd door uw beheerder.",
"Already in call": "Al in gesprek",
"You're already in a call with this person.": "U bent al in gesprek met deze persoon."
"You're already in a call with this person.": "U bent al in gesprek met deze persoon.",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Verifieer deze login om toegang te krijgen tot uw versleutelde berichten en om anderen te bewijzen dat deze login echt van u is.",
"Verify with another session": "Verifieer met een andere sessie",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "We zullen voor elk een gesprek maken. U kunt er later meer toevoegen, inclusief al bestaande gesprekken.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Laten we voor elk een gesprek maken. U kunt er later meer toevoegen, inclusief al bestaande gesprekken.",
"Make sure the right people have access. You can invite more later.": "Controleer of de juiste mensen toegang hebben. U kunt later meer mensen uitnodigen.",
"A private space to organise your rooms": "Een privé space om uw gesprekken te organiseren",
"Just me": "Alleen ik",
"Make sure the right people have access to %(name)s": "Controleer of de juiste mensen toegang hebben tot %(name)s",
"Go to my first room": "Ga naar mijn eerste gesprek",
"It's just you at the moment, it will be even better with others.": "Het is alleen u op dit moment, het zal nog beter zijn met anderen.",
"Share %(name)s": "Deel %(name)s",
"Private space": "Privé space",
"Public space": "Openbare space",
"<inviter/> invites you": "<inviter/> nodigt u uit",
"Search names and description": "Zoek in namen en beschrijvingen",
"Create room": "Gesprek aanmaken",
"You may want to try a different search or check for typos.": "U kunt een andere zoekterm proberen of controleren op een typefout.",
"No results found": "Geen resultaten gevonden",
"Mark as suggested": "Markeer als aanbeveling",
"Mark as not suggested": "Markeer als geen aanbeveling",
"Removing...": "Verwijderen...",
"Failed to remove some rooms. Try again later": "Het verwijderen van sommige gesprekken is mislukt. Probeer het opnieuw",
"%(count)s rooms and 1 space|one": "%(count)s gesprek en 1 space",
"%(count)s rooms and 1 space|other": "%(count)s gesprekken en 1 space",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s gesprek en %(numSpaces)s spaces",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s gesprekken en %(numSpaces)s spaces",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Als u uw gesprek niet kan vinden, vraag dan om een uitnodiging of <a>maak een nieuw gesprek</a>.",
"Suggested": "Aanbevolen",
"This room is suggested as a good one to join": "Dit is een aanbevolen gesprek om aan deel te nemen",
"%(count)s rooms|one": "%(count)s gesprek",
"%(count)s rooms|other": "%(count)s gesprekken",
"You don't have permission": "U heeft geen toestemming",
"Open": "Openen",
"%(count)s messages deleted.|one": "%(count)s bericht verwijderd.",
"%(count)s messages deleted.|other": "%(count)s berichten verwijderd.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Normaal gesproken heeft dit alleen invloed op het verwerken van het gesprek op de server. Als u problemen ervaart met %(brand)s, stuur dan een bugmelding.",
"Invite to %(roomName)s": "Uitnodiging voor %(roomName)s",
"Edit devices": "Apparaten bewerken",
"Invite People": "Mensen uitnodigen",
"Invite with email or username": "Uitnodigen per e-mail of gebruikersnaam",
"You can change these anytime.": "U kan dit elk moment nog aanpassen.",
"Add some details to help people recognise it.": "Voeg details toe zodat mensen het herkennen.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Spaces zijn een nieuwe manier voor het groeperen van gesprekken. Voor deelname aan een bestaande space heeft u een uitnodiging nodig.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Van %(deviceName)s (%(deviceId)s) op %(ip)s",
"Check your devices": "Controleer uw apparaten",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Een nieuwe login heeft toegang tot uw account: %(name)s (%(deviceID)s) op %(ip)s",
"You have unverified logins": "U heeft ongeverifieerde logins"
}

View file

@ -1410,7 +1410,7 @@
"Integrations are disabled": "Integracje są wyłączone",
"Enable 'Manage Integrations' in Settings to do this.": "Włącz „Zarządzaj integracjami” w ustawieniach, aby to zrobić.",
"Encryption upgrade available": "Dostępna aktualizacja szyfrowania",
"Upgrade": "Uaktualnij",
"Upgrade": "Ulepsz",
"Delete sessions|other": "Usuń sesje",
"Delete %(count)s sessions|one": "Usuń %(count)s sesję",
"Manage": "Zarządzaj",

View file

@ -730,7 +730,7 @@
"Enable them now": "Включить их сейчас",
"Toolbox": "Панель инструментов",
"Collecting logs": "Сбор журналов",
"You must specify an event type!": "Необходимо указать тип мероприятия!",
"You must specify an event type!": "Необходимо указать тип события!",
"(HTTP status %(httpStatus)s)": "(статус HTTP %(httpStatus)s)",
"Invite to this room": "Пригласить в комнату",
"Send logs": "Отправить журналы",
@ -770,13 +770,13 @@
"Wednesday": "Среда",
"You can now return to your account after signing out, and sign in on other devices.": "Теперь вы сможете вернуться к своей учётной записи после выхода и войти на других устройствах.",
"Enable email notifications": "Включить уведомления на email",
"Event Type": "Тип мероприятия",
"Event Type": "Тип события",
"Download this file": "Скачать файл",
"Pin Message": "Закрепить сообщение",
"Failed to change settings": "Не удалось изменить настройки",
"View Community": "Просмотр сообщества",
"Event sent!": "Событие отправлено!",
"Event Content": "Содержание мероприятия",
"Event Content": "Содержимое события",
"Thank you!": "Спасибо!",
"Quote": "Цитата",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "В текущем браузере внешний вид приложения может быть полностью неверным, а некоторые или все функции могут не работать. Если вы хотите попробовать в любом случае, то можете продолжить, но с теми проблемами, с которыми вы можете столкнуться вам придется разбираться самостоятельно!",
@ -831,7 +831,7 @@
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "Вы не можете отправлять сообщения до тех пор, пока вы не примете <consentLink>наши правила и положения</consentLink>.",
"Demote": "Понижение",
"Demote yourself?": "Понизить самого себя?",
"This event could not be displayed": "Это событие отобразить невозможно",
"This event could not be displayed": "Не удалось отобразить это событие",
"Permission Required": "Требуется разрешение",
"You do not have permission to start a conference call in this room": "У вас нет разрешения на запуск конференции в этой комнате",
"A call is currently being placed!": "Есть активный вызов!",
@ -3074,5 +3074,100 @@
"Failed to save settings": "Не удалось сохранить настройки",
"Show chat effects (animations when receiving e.g. confetti)": "Показать эффекты чата (анимация при получении, например, конфетти)",
"Caution:": "Предупреждение:",
"Settings Explorer": "Обзор настроек"
"Settings Explorer": "Обзор настроек",
"Suggested": "Рекомендуется",
"This room is suggested as a good one to join": "Эта комната рекомендуется, чтобы присоединиться",
"%(count)s rooms|one": "%(count)s комната",
"%(count)s rooms|other": "%(count)s комнат",
"%(count)s members|one": "%(count)s участник",
"%(count)s members|other": "%(count)s участников",
"You don't have permission": "У вас нет разрешения",
"Open": "Открыть",
"Your message wasn't sent because this homeserver has been blocked by it's administrator. Please <a>contact your service administrator</a> to continue using the service.": "Ваше сообщение не было отправлено, потому что этот домашний сервер был заблокирован администратором. Пожалуйста, <a>обратитесь к вашему администратору</a>, чтобы продолжить использование сервиса.",
"%(count)s messages deleted.|one": "%(count)s сообщение удалено.",
"%(count)s messages deleted.|other": "%(count)s сообщений удалено.",
"Are you sure you want to leave the space '%(spaceName)s'?": "Вы уверены, что хотите покинуть пространство \"%(spaceName)s\"?",
"This space is not public. You will not be able to rejoin without an invite.": "Это пространство не публично. Вы не сможете вновь войти без приглашения.",
"Unable to start audio streaming.": "Невозможно запустить аудио трансляцию.",
"Start audio stream": "Запустить аудио трансляцию",
"Failed to start livestream": "Не удалось запустить прямую трансляцию",
"Save Changes": "Сохранить изменения",
"Saving...": "Сохранение…",
"View dev tools": "Просмотр инструментов для разработчиков",
"Leave Space": "Покинуть пространство",
"Make this space private": "Сделать это пространство приватным",
"Edit settings relating to your space.": "Редактировать настройки, относящиеся к вашему пространству.",
"Space settings": "Настройки пространства",
"Failed to save space settings.": "Не удалось сохранить настройки пространства.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Обычно это только влияет на то, как комната обрабатывается на сервере. Если у вас проблемы с вашим %(brand)s, сообщите об ошибке.",
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.": "Пригласите кого-нибудь, используя их имя, адрес электронной почты, имя пользователя (например, <userId/>) или <a>поделитесь этим пространством</a>.",
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Пригласите кого-нибудь используя их имя, имя пользователя (как <userId/>) или <a>поделитесь этим пространством</a>.",
"Invite to %(roomName)s": "Пригласить в %(roomName)s",
"Unnamed Space": "Безымянное пространство",
"Invite to %(spaceName)s": "Пригласить в %(spaceName)s",
"Setting definition:": "Установка определения:",
"Failed to add rooms to space": "Не удалось добавить комнаты в пространство",
"Apply": "Применить",
"Applying...": "Применение…",
"Create a new room": "Создать новую комнату",
"Don't want to add an existing room?": "Не хотите добавить существующую комнату?",
"Spaces": "Пространства",
"Filter your rooms and spaces": "Отфильтруйте свои комнаты и пространства",
"Add existing spaces/rooms": "Добавить существующие пространства/комнаты",
"Space selection": "Выбор пространства",
"Edit devices": "Редактировать устройства",
"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the space it will be impossible to regain privileges.": "Вы не сможете отменить это изменение, поскольку вы понижаете свои права, если вы являетесь последним привилегированным пользователем в пространстве, будет невозможно восстановить привилегии вбудущем.",
"Invite People": "Пригласить людей",
"Empty room": "Пустая комната",
"Suggested Rooms": "Предлагаемые комнаты",
"Explore space rooms": "Исследовать комнаты пространства",
"You do not have permissions to add rooms to this space": "У вас нет разрешений, чтобы добавить комнаты в это пространство",
"Add existing room": "Добавить существующую комнату",
"You do not have permissions to create new rooms in this space": "У вас нет разрешений для создания новых комнат в этом пространстве",
"Send message": "Отправить сообщение",
"Invite to this space": "Пригласить в это пространство",
"Your message was sent": "Ваше сообщение было отправлено",
"Encrypting your message...": "Шифрование вашего сообщения…",
"Sending your message...": "Отправка вашего сообщения…",
"Spell check dictionaries": "Словари для проверки орфографии",
"Space options": "Настройки пространства",
"Space Home": "Домашняя страница пространства",
"New room": "Новая комната",
"Leave space": "Покинуть пространство",
"Share your public space": "Поделитесь своим публичным пространством",
"Invite members": "Пригласить участников",
"Invite with email or username": "Пригласить по электронной почте или имени пользователя",
"Invite people": "Пригласить людей",
"Share invite link": "Поделиться ссылкой на приглашение",
"Click to copy": "Нажмите, чтобы скопировать",
"Collapse space panel": "Свернуть панель пространств",
"Expand space panel": "Развернуть панель пространств",
"Creating...": "Создание…",
"You can change this later": "Вы можете изменить это позже",
"You can change these anytime.": "Вы можете изменить их в любое время.",
"Add some details to help people recognise it.": "Добавьте некоторые подробности, чтобы помочь людям узнать его.",
"Your private space": "Ваше приватное пространство",
"Your public space": "Ваше публичное пространство",
"Invite only, best for yourself or teams": "Только по приглашениям, лучший вариант для себя или команды",
"Private": "Приватное",
"Open space for anyone, best for communities": "Открытое пространство для всех, лучший вариант для сообществ",
"Public": "Публичное",
"Create a space": "Создать пространство",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Пространства являются новыми способами группировки комнат и людей. Чтобы присоединиться к существующему пространству, вам понадобится приглашение.",
"Delete": "Удалить",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "От %(deviceName)s (%(deviceId)s) %(ip)s",
"Jump to the bottom of the timeline when you send a message": "Перейти к нижней части временной шкалы, когда вы отправляете сообщение",
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Прототип пространства. Несовместимо с сообществами, сообществами версии 2 и пользовательскими тегами. Требуется совместимый домашний сервер для некоторых функций.",
"Check your devices": "Проверьте ваши устройства",
"You have unverified logins": "У вас есть непроверенные входы в систему",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Новый вход в систему через вашу учётную запись: %(name)s (%(deviceID)s) %(ip)s",
"This homeserver has been blocked by it's administrator.": "Доступ к этому домашнему серверу заблокирован вашим администратором.",
"This homeserver has been blocked by its administrator.": "Доступ к этому домашнему серверу заблокирован вашим администратором.",
"You're already in a call with this person.": "Вы уже разговариваете с этим человеком.",
"Already in call": "Уже в вызове",
"Original event source": "Оригинальный исходный код",
"Decrypted event source": "Расшифрованный исходный код",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s комната и %(numSpaces)s пространств",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s комнат и %(numSpaces)s пространств",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Если вы не можете найти комнату, попросите приглашение или <a>создайте новую комнату</a>."
}

View file

@ -2987,7 +2987,7 @@
"We'll store an encrypted copy of your keys on our server. Secure your backup with a Security Phrase.": "Do të ruajmë një kopje të fshehtëzuar të kyçeve tuaj në shërbyesin tonë. Siguroni kopjeruajtjen tuaj me një Frazë Sigurie.",
"Use Security Key": "Përdorni Kyç Sigurie",
"A new Security Phrase and key for Secure Messages have been detected.": "Janë pikasur një Frazë e re Sigurie dhe kyç i ri për Mesazhe të Sigurt.",
"If you've forgotten your Security Key you can <button>set up new recovery options</button>": "Nëse keni harruar Kyçin tuaj të Sigurisë, mund të <button>ujdisni mundësi të reja rimarrjeje</button>.",
"If you've forgotten your Security Key you can <button>set up new recovery options</button>": "Nëse keni harruar Kyçin tuaj të Sigurisë, mund të <button>ujdisni mundësi të reja rimarrjeje</button>",
"Your Security Key has been <b>copied to your clipboard</b>, paste it to:": "Kyçi juaj i Sigurisë është <b>kopjuar te e papastra juaj</b>, ngjiteni te:",
"Confirm your Security Phrase": "Ripohoni Frazën tuaj të Sigurisë",
"Secure your backup with a Security Phrase": "Sigurojeni kopjeruajtjen tuaj me një Frazë Sigurie",
@ -3130,7 +3130,7 @@
"View dev tools": "Shihni mjete zhvilluesi",
"Leave Space": "Braktiseni Hapësirën",
"Make this space private": "Bëje këtë hapësirë private",
"Edit settings relating to your space.": "Përpunoni rregullime që lidhen me hapësirën tuaj",
"Edit settings relating to your space.": "Përpunoni rregullime që lidhen me hapësirën tuaj.",
"Space settings": "Rregullime hapësire",
"Failed to save space settings.": "Su arrit të ruhen rregullime hapësire.",
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.": "Ftoni dikë duke përdorur emrin e tij, emrin e tij të përdoruesit (bie fjala, <userId/>) ose <a>ndani me të këtë hapësirë</a>.",
@ -3191,5 +3191,54 @@
"This homeserver has been blocked by it's administrator.": "Ky shërbyes Home është bllokuar nga përgjegjësi i tij.",
"This homeserver has been blocked by its administrator.": "Ky shërbyes Home është bllokuar nga përgjegjësit e tij.",
"You're already in a call with this person.": "Gjendeni tashmë në thirrje me këtë person.",
"Already in call": "Tashmë në thirrje"
"Already in call": "Tashmë në thirrje",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Verifikoni këto kredenciale për hyrje te mesazhet tuaja të fshehtëzuara dhe dëshmojuni të tjerëve se këto kredenciale hyrjeje janë vërtet tuajat.",
"Verify with another session": "Verifikojeni me tjetër sesion",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Do të krijojmë dhoma për çdo një prej tyre. Mund të shtoni edhe të tjera më vonë, përfshi ato ekzistueset tashmë.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Le të krijojmë një dhomë për secilën prej tyre. Mund të shtoni të tjera më vonë, përfshi ato ekzistuese tashmë.",
"Make sure the right people have access. You can invite more later.": "Siguroni se kanë hyrje personat e duhur. Mund të shtoni të tjerë më vonë.",
"A private space to organise your rooms": "Një hapësirë private për të sistemuar dhomat tuaja",
"Just me": "Vetëm unë",
"Make sure the right people have access to %(name)s": "Siguroni se te %(name)s kanë hyrje personat e duhur",
"Go to my first room": "Kalo te dhoma ime e parë",
"It's just you at the moment, it will be even better with others.": "Vetëm ju, hëpërhë, do të jetë edhe më mirë me të tjerë.",
"Share %(name)s": "Ndajeni %(name)s me të tjerët",
"Private space": "Hapësirë private",
"Public space": "Hapësirë publike",
"<inviter/> invites you": "<inviter/> ju fton",
"Search names and description": "Kërkoni emra dhe përshkrim",
"You may want to try a different search or check for typos.": "Mund të doni të provoni një tjetër kërkim ose të kontrolloni për gabime shkrimi.",
"No results found": "Su gjetën përfundime",
"Mark as suggested": "Vëri shenjë si e sugjeruar",
"Mark as not suggested": "Hiqi shenjë si e sugjeruar",
"Removing...": "Po hiqet…",
"Failed to remove some rooms. Try again later": "Sua arrit të hiqen disa dhoma. Riprovoni më vonë",
"%(count)s rooms and 1 space|one": "%(count)s dhomë dhe 1 hapësirë",
"%(count)s rooms and 1 space|other": "%(count)s dhoma dhe 1 hapësirë",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s dhomë dhe %(numSpaces)s hapësira",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s dhoma dhe %(numSpaces)s hapësira",
"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>.",
"Suggested": "E sugjeruar",
"This room is suggested as a good one to join": "Kjo dhomë sugjerohet si një e mirë për të marrë pjesë",
"%(count)s rooms|one": "%(count)s dhomë",
"%(count)s rooms|other": "%(count)s dhoma",
"You don't have permission": "Skeni leje",
"Youre all caught up": "Jeni në rregull",
"%(count)s messages deleted.|one": "%(count)s mesazh i fshirë.",
"%(count)s messages deleted.|other": "%(count)s mesazhe të fshirë.",
"Failed to start livestream": "Su arrit të nisej transmetim i drejtpërdrejtë",
"You're all caught up.": "Jeni në rregull.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Kjo zakonisht prek vetëm mënyrën se si përpunohet dhoma te shërbyesi. Nëse keni probleme me %(brand)s-in, ju lutemi, njoftoni një të metë.",
"Invite to %(roomName)s": "Ftojeni te %(roomName)s",
"Windows": "Windows",
"Edit devices": "Përpunoni pajisje",
"Invite People": "Ftoni Njerëz",
"Invite with email or username": "Ftoni përmes email-i ose emri përdoruesi",
"You can change these anytime.": "Këto mund ti ndryshoni në çfarëdo kohe.",
"Add some details to help people recognise it.": "Shtoni ca hollësi që të ndihmoni njerëzit ta dallojnë.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Hapësirat janë rrugë e re për të grupuar dhoma dhe njerëz. Për tu bërë pjesë e një hapësire ekzistuese, do tju duhet një ftesë.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Nga %(deviceName)s (%(deviceId)s) te %(ip)s",
"Check your devices": "Kontrolloni pajisjet tuaja",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "Në llogarinë tuaj po hyhet nga një palë kredenciale të reja: %(name)s (%(deviceID)s) te %(ip)s",
"You have unverified logins": "Keni kredenciale të erifikuar"
}

View file

@ -87,42 +87,42 @@
"%(senderName)s requested a VoIP conference.": "%(senderName)s је затражио VoIP конференцију.",
"%(senderName)s invited %(targetName)s.": "%(senderName)s је позвао %(targetName)s.",
"%(senderName)s banned %(targetName)s.": "%(senderName)s је бановао %(targetName)s.",
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s постави приказно име на %(displayName)s.",
"%(senderName)s removed their display name (%(oldDisplayName)s).": "Корисник %(senderName)s је себи уклонио приказно име %(oldDisplayName)s.",
"%(senderName)s removed their profile picture.": "Корисник %(senderName)s је себи уклонио профилну слику.",
"%(senderName)s changed their profile picture.": "Корисник %(senderName)s је себи променио профилну слику.",
"%(senderName)s set a profile picture.": "Корисник %(senderName)s је себи поставио профилну слику.",
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s је поставио приказно име на %(displayName)s.",
"%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s је себи уклонио приказно име %(oldDisplayName)s.",
"%(senderName)s removed their profile picture.": "%(senderName)s је себи уклонио профилну слику.",
"%(senderName)s changed their profile picture.": "%(senderName)s је себи променио профилну слику.",
"%(senderName)s set a profile picture.": "%(senderName)s је себи поставио профилну слику.",
"VoIP conference started.": "VoIP конференција је започета.",
"%(targetName)s joined the room.": "Корисник %(targetName)s је ушао у собу.",
"%(targetName)s joined the room.": "%(targetName)s је ушао у собу.",
"VoIP conference finished.": "VoIP конференција је завршена.",
"%(targetName)s rejected the invitation.": "Корисник %(targetName)s је одбацио позивницу.",
"%(targetName)s left the room.": "Корисник %(targetName)s је напустио собу.",
"%(senderName)s unbanned %(targetName)s.": "Корисник %(senderName)s је скинуо забрану приступа са %(targetName)s.",
"%(senderName)s kicked %(targetName)s.": "Корисник %(senderName)s је избацио %(targetName)s.",
"%(senderName)s withdrew %(targetName)s's invitation.": "Пошиљалац %(senderName)s је повукао позивницу за %(targetName)s.",
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "Корисник %(senderDisplayName)s је променио тему у „%(topic)s“.",
"%(senderDisplayName)s removed the room name.": "Корисник %(senderDisplayName)s је уклонио назив собе.",
"%(senderDisplayName)s changed the room name to %(roomName)s.": "Корисник %(senderDisplayName)s је променио назив собе у %(roomName)s.",
"%(senderDisplayName)s sent an image.": "Корисник %(senderDisplayName)s је послао слику.",
"%(targetName)s rejected the invitation.": "%(targetName)s је одбацио позивницу.",
"%(targetName)s left the room.": "%(targetName)s је напустио собу.",
"%(senderName)s unbanned %(targetName)s.": "%(senderName)s је скинуо забрану приступа са %(targetName)s.",
"%(senderName)s kicked %(targetName)s.": "%(senderName)s је избацио %(targetName)s.",
"%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s је повукао позивницу за %(targetName)s.",
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s је променио тему у „%(topic)s“.",
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s је уклонио назив собе.",
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s је променио назив собе у %(roomName)s.",
"%(senderDisplayName)s sent an image.": "%(senderDisplayName)s је послао слику.",
"Someone": "Неко",
"(not supported by this browser)": "(није подржано од стране овог прегледача)",
"%(senderName)s answered the call.": "Корисник %(senderName)s се јавио.",
"%(senderName)s answered the call.": "%(senderName)s се јавио.",
"(could not connect media)": "(не могу да повежем медије)",
"(no answer)": "(нема одговора)",
"(unknown failure: %(reason)s)": "(непозната грешка: %(reason)s)",
"%(senderName)s ended the call.": "Корисник %(senderName)s је окончао позив.",
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "Корисник %(senderName)s је послао позивницу за приступ соби ка %(targetDisplayName)s.",
"%(senderName)s made future room history visible to all room members, from the point they are invited.": "Корисник %(senderName)s је учинио будући историјат собе видљивим свим члановима собе, од тренутка позивања у собу.",
"%(senderName)s made future room history visible to all room members, from the point they joined.": "Корисник %(senderName)s је учинио будући историјат собе видљивим свим члановима собе, од тренутка приступања соби.",
"%(senderName)s made future room history visible to all room members.": "Корисник %(senderName)s је учинио будући историјат собе видљивим свим члановима собе.",
"%(senderName)s made future room history visible to anyone.": "Корисник %(senderName)s је учинио будући историјат собе видљивим свима.",
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "Корисник %(senderName)s је учинио будући историјат собе непознатим (%(visibility)s).",
"%(senderName)s ended the call.": "%(senderName)s је окончао позив.",
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s је послао позивницу за приступ соби ка %(targetDisplayName)s.",
"%(senderName)s made future room history visible to all room members, from the point they are invited.": "%(senderName)s је учинио будући историјат собе видљивим свим члановима собе, од тренутка позивања у собу.",
"%(senderName)s made future room history visible to all room members, from the point they joined.": "%(senderName)s је учинио будући историјат собе видљивим свим члановима собе, од тренутка приступања соби.",
"%(senderName)s made future room history visible to all room members.": "%(senderName)s је учинио будући историјат собе видљивим свим члановима собе.",
"%(senderName)s made future room history visible to anyone.": "%(senderName)s је учинио будући историјат собе видљивим свима.",
"%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s је учинио будући историјат собе непознатим (%(visibility)s).",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s са %(fromPowerLevel)s на %(toPowerLevel)s",
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s промени ниво снаге за %(powerLevelDiffText)s.",
"%(senderName)s changed the pinned messages for the room.": "Корисник %(senderName)s је променио закачене поруке у соби.",
"%(widgetName)s widget modified by %(senderName)s": "Корисник %(senderName)s је променио виџет %(widgetName)s",
"%(widgetName)s widget added by %(senderName)s": "Корисник %(senderName)s је додао виџет %(widgetName)s",
"%(widgetName)s widget removed by %(senderName)s": "Корисник %(senderName)s је уклонио виџет %(widgetName)s",
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s је променио закачене поруке у соби.",
"%(widgetName)s widget modified by %(senderName)s": "%(senderName)s је променио виџет %(widgetName)s",
"%(widgetName)s widget added by %(senderName)s": "%(senderName)s је додао виџет %(widgetName)s",
"%(widgetName)s widget removed by %(senderName)s": "%(senderName)s је уклонио виџет %(widgetName)s",
"Failure to create room": "Неуспех при прављењу собе",
"Server may be unavailable, overloaded, or you hit a bug.": "Сервер је можда недоступан, преоптерећен или сте нашли грешку.",
"Send": "Пошаљи",
@ -443,7 +443,7 @@
"Create Room": "Направи собу",
"Unknown error": "Непозната грешка",
"Incorrect password": "Нетачна лозинка",
"Deactivate Account": "Угаси налог",
"Deactivate Account": "Деактивирај налог",
"An error has occurred.": "Догодила се грешка.",
"OK": "У реду",
"Unable to restore session": "Не могу да повратим сесију",
@ -521,7 +521,7 @@
"Analytics": "Аналитика",
"The information being sent to us to help make %(brand)s better includes:": "У податке које нам шаљете зарад побољшавања %(brand)s-а спадају:",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Ако страница садржи поверљиве податке (као што је назив собе, ИД корисника или групе), ти подаци се уклањају пре слања на сервер.",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "Корисник %(oldDisplayName)s је променио приказно име у %(displayName)s.",
"%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s је променио приказно име у %(displayName)s.",
"Failed to set direct chat tag": "Нисам успео да поставим ознаку директног ћаскања",
"Failed to remove tag %(tagName)s from room": "Нисам успео да скинем ознаку %(tagName)s са собе",
"Failed to add tag %(tagName)s to room": "Нисам успео да додам ознаку %(tagName)s на собу",
@ -838,7 +838,7 @@
"This room has no topic.": "Ова соба нема тему.",
"Sets the room name": "Поставља назив собе",
"Forces the current outbound group session in an encrypted room to be discarded": "Присиљава одбацивање тренутне одлазне сесије групе у шифрованој соби",
"%(senderDisplayName)s upgraded this room.": "Корисник %(senderDisplayName)s је надоградио ову собу.",
"%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s је надоградио ову собу.",
"Free": "Бесплатан",
"Join millions for free on the largest public server": "Придружите се милионима других бесплатно на највећем јавном серверу",
"Premium": "Премијум",
@ -877,10 +877,10 @@
"Room name or address": "Назив собе или адреса",
"Identity server has no terms of service": "Идентитетски сервер нема услове коришћења",
"Changes your avatar in all rooms": "Промените ваш аватар у свим собама",
"%(senderName)s placed a voice call.": "Корисник %(senderName)s је започео гласовни позив.",
"%(senderName)s placed a voice call. (not supported by this browser)": "Корисник %(senderName)s је започео гласовни позив. (није подржано од стране овог прегледача)",
"%(senderName)s placed a video call.": "Корисник %(senderName)s је започео видео позив.",
"%(senderName)s placed a video call. (not supported by this browser)": "Корисник %(senderName)s је започео видео позив. (није подржано од стране овог прегледача)",
"%(senderName)s placed a voice call.": "%(senderName)s је започео гласовни позив.",
"%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s је започео гласовни позив. (није подржано од стране овог прегледача)",
"%(senderName)s placed a video call.": "%(senderName)s је започео видео позив.",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s је започео видео позив. (није подржано од стране овог прегледача)",
"You do not have permission to invite people to this room.": "Немате дозволу за позивање људи у ову собу.",
"Set up encryption": "Подеси шифровање",
"Encryption upgrade available": "Надоградња шифровања је доступна",
@ -903,7 +903,7 @@
"Set a new account password...": "Подеси нову лозинку налога…",
"Language and region": "Језик и област",
"General": "Опште",
"Discovery": "Откривање",
"Discovery": "Откриће",
"None": "Ништа",
"Security & Privacy": "Безбедност и приватност",
"Change room name": "Промени назив собе",
@ -1359,18 +1359,18 @@
"Unexpected error resolving homeserver configuration": "Неочекивана грешка при откривању подешавања сервера",
"No homeserver URL provided": "Није наведен УРЛ сервера",
"Cannot reach homeserver": "Сервер недоступан",
"%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s додаде алтернативну адресу %(addresses)s за ову собу.",
"%(senderName)s removed the main address for this room.": "%(senderName)s уклони главну адресу за ову собу.",
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s постави главну адресу собе на %(address)s.",
"%(senderName)s added the alternative addresses %(addresses)s for this room.|other": "%(senderName)s је додао алтернативну адресу %(addresses)s за ову собу.",
"%(senderName)s removed the main address for this room.": "%(senderName)s је уклони главну адресу за ову собу.",
"%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s је постави главну адресу собе на %(address)s.",
"🎉 All servers are banned from participating! This room can no longer be used.": "🎉 Свим серверима је забрањено да учествују! Ова соба се више не може користити.",
"%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s измени гостински приступ на %(rule)s",
"%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s спречи госте да се придруже у соби.",
"%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s дозволи гостима да се придруже у собу.",
"%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s измени правило придруживања на %(rule)s",
"%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s учини собу доступном само позивницом.",
"%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s учини собу јавном за све који знају везу.",
"%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s измени назив собе из %(oldRoomName)s у %(newRoomName)s.",
"%(senderName)s made no change.": "%(senderName)s не направи измене.",
"%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s је изменио гостински приступ на %(rule)s",
"%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s је спречио госте да се придруже у соби.",
"%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s је дозволи гостима да се придруже у собу.",
"%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s је измени правило придруживања на %(rule)s",
"%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s је учини собу доступном само позивницом.",
"%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s је учини собу јавном за све који знају везу.",
"%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s је изменио назив собе из %(oldRoomName)s у %(newRoomName)s.",
"%(senderName)s made no change.": "%(senderName)s није направио никакву измену.",
"Takes the call in the current room off hold": "Узима позив са чекања у тренутној соби",
"Places the call in the current room on hold": "Ставља позив на чекање у тренутној соби",
"Sends a message to the given user": "Шаље поруку наведеном кориснику",
@ -1403,7 +1403,7 @@
"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.": "Ако нисте ви уклонили начин опоравка, нападач можда покушава да приступи вашем налогу. Промените своју лозинку и поставите нови начин опоравка у поставкама, одмах.",
"If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.": "Ако сте то случајно учинили, безбедне поруке можете подесити у овој сесији, која ће поново шифровати историју порука сесије помоћу новог начина опоравка.",
"This session has detected that your Security Phrase and key for Secure Messages have been removed.": "Сесија је открила да су ваша безбедносна фраза и кључ за безбедне поруке уклоњени.",
"Cancel autocomplete": "Откажи ауто-довршавање",
"Cancel autocomplete": "Откажи само-довршавање",
"Direct message": "Директна порука",
"Hide sessions": "Сакриј сесије",
"Trusted": "поуздан",
@ -1419,18 +1419,18 @@
"Error changing power level": "Грешка при промени нивоа снаге",
"Power level": "Ниво снаге",
"Explore rooms": "Истражи собе",
"%(senderName)s has updated the widget layout": "%(senderName)s освежи распоред виџета",
"%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s повуче позивницу за приступ соби кориснику %(targetDisplayName)s.",
"%(senderName)s has updated the widget layout": "%(senderName)s је освежио распоред виџета",
"%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s је повукао позивницу за приступ соби кориснику %(targetDisplayName)s.",
"%(senderName)s declined the call.": "%(senderName)s одби позив.",
"(an error occurred)": "(дошло је до грешке)",
"(their device couldn't start the camera / microphone)": "(туђи уређај не може да покрене камеру / микрофон)",
"(connection failed)": "(неуспела веза)",
"%(senderName)s changed the addresses for this room.": "%(senderName)s измени адресе за ову собу.",
"%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s измени главну и алтернативне адресе за ову собу.",
"%(senderName)s changed the alternative addresses for this room.": "%(senderName)s измени алтернативне адресе за ову собу.",
"%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s уклони алтернативне адресе %(addresses)s за ову собу.",
"%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s уклони алтернативну адресу %(addresses)s за ову собу.",
"%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s додаде алтернативну адресу %(addresses)s за ову собу.",
"%(senderName)s changed the addresses for this room.": "%(senderName)s је изменио адресе за ову собу.",
"%(senderName)s changed the main and alternative addresses for this room.": "%(senderName)s је изменио главну и алтернативне адресе за ову собу.",
"%(senderName)s changed the alternative addresses for this room.": "%(senderName)s је изменио алтернативне адресе за ову собу.",
"%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s је уклонио алтернативне адресе %(addresses)s за ову собу.",
"%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s је уклонио алтернативну адресу %(addresses)s за ову собу.",
"%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s је додао алтернативну адресу %(addresses)s за ову собу.",
"Converts the room to a DM": "Претвара собу у директно дописивање",
"Converts the DM to a room": "Претвара директно дописивање у собу",
"Changes your avatar in this current room only": "Мења ваш аватар само у тренутној соби",
@ -1503,10 +1503,262 @@
"Horse": "коњ",
"Lion": "лав",
"Cat": "мачка",
"Dog": "пас",
"Dog": "Пас",
"To be secure, do this in person or use a trusted way to communicate.": "Да будете сигурни, ово обавите лично или путем поузданог начина комуникације.",
"They don't match": "Не поклапају се",
"They match": "Поклапају се",
"Cancelling…": "Отказујем…",
"Show stickers button": "Прикажи дугме за налепнице"
"Show stickers button": "Прикажи дугме за налепнице",
"%(brand)s is securely caching encrypted messages locally for them to appear in search results:": "%(brand)s је локално сигурно кешира шифроване поруке да би се појавиле у резултатима претраге:",
"Search names and description": "Претражите имена и опис",
"You may want to try a different search or check for typos.": "Можда ћете желети да испробате другачију претрагу или да проверите да ли имате правописне грешке.",
"This version of %(brand)s does not support searching encrypted messages": "Ова верзија %(brand)s с не подржава претраживање шифрованих порука",
"Cancel search": "Откажи претрагу",
"Message search": "Претрага порука",
"Securely cache encrypted messages locally for them to appear in search results.": "Сигурно локално кеширајте шифроване поруке да би се појавиле у резултатима претраге.",
"Enable message search in encrypted rooms": "Омогућите претрагу порука у шифрованим собама",
"Space settings": "Подешавања простора",
"Failed to save space settings.": "Чување подешавања простора није успело.",
"We recommend you change your password and Security Key in Settings immediately": "Препоручујемо вам да одмах промените лозинку и безбедносни кључ у подешавањима",
"Confirm this user's session by comparing the following with their User Settings:": "Потврдите сесију овог корисника упоређивањем следећег са њиховим корисничким подешавањима:",
"Confirm by comparing the following with the User Settings in your other session:": "Потврдите упоређивањем следећег са корисничким подешавањима у вашој другој сесији:",
"You can also set up Secure Backup & manage your keys in Settings.": "Такође можете да подесите Сигурносну копију и управљате својим тастерима у подешавањима.",
"User settings": "Подешавања корисника",
"Community settings": "Подешавања заједнице",
"Edit settings relating to your space.": "Уредите поставке које се односе на ваш простор.",
"Go to Settings": "Идите на подешавања",
"Enable 'Manage Integrations' in Settings to do this.": "Омогућите „Управљање интеграцијама“ у подешавањима да бисте то урадили.",
"Failed to save settings": "Неуспешно чување подешавања",
"Settings Explorer": "Подешавања истраживаача",
"Share this email in Settings to receive invites directly in %(brand)s.": "Поделите ову е-пошту у подешавањима да бисте директно добијали позиве у %(brand)s.",
"Use an identity server in Settings to receive invites directly in %(brand)s.": "Користите сервер за идентитет у Подешавањима за директно примање позивница %(brand)s.",
"Link this email with your account in Settings to receive invites directly in %(brand)s.": "Повежите ову е-пошту са својим налогом у Подешавањима да бисте директно добијали позиве у %(brand)s.",
"Change settings": "Промени подешавања",
"⚠ These settings are meant for advanced users.": "⚠ Ова подешавања су намењена напредним корисницима.",
"Change notification settings": "Промените подешавања обавештења",
"Verification code": "Верификациони код",
"Please enter verification code sent via text.": "Унесите верификациони код послат путем текста.",
"Unable to verify phone number.": "Није могуће верификовати број телефона.",
"Unable to share phone number": "Није могуће делити телефонски број",
"Share": "Објави",
"Complete": "Заврши",
"You'll need to authenticate with the server to confirm the upgrade.": "Да бисте потврдили надоградњу, мораћете да се пријавите на серверу.",
"Restore": "Врати",
"Restore your key backup to upgrade your encryption": "Вратите сигурносну копију кључа да бисте надоградили шифровање",
"Enter your account password to confirm the upgrade:": "Унесите лозинку за налог да бисте потврдили надоградњу:",
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "Заштитите од губитка приступа шифрованим порукама и подацима је подржан сигурносном копијом кључева за шифровање на серверу.",
"Enter name": "Унесите име",
"What's the name of your community or team?": "Како се зове ваша заједница или тим?",
"Show": "Прикажи",
"Hide": "Сакриј",
"Clear all data": "Очисти све податке",
"Filter": "Филтер",
"Failed to load group members": "Учитавање чланова групе није успело",
"The person who invited you already left the room, or their server is offline.": "Особа која вас је позвала већ је напустила собу или је њен сервер ван мреже.",
"The person who invited you already left the room.": "Особа која вас је позвала већ је напустила собу.",
"Guest": "Гост",
"New version of %(brand)s is available": "Доступна је нова верзија %(brand)s",
"Update %(brand)s": "Ажурирај %(brand)s",
"Check your devices": "Проверите своје уређаје",
"New login. Was this you?": "Нова пријава. Да ли сте то били Ви?",
"Other users may not trust it": "Други корисници можда немају поверења у то",
"Safeguard against losing access to encrypted messages & data": "Заштитите се од губитка приступа шифрованим порукама и подацима",
"Profile picture": "Слика профила",
"Display Name": "Прикажи име",
"You cancelled verification on your other session.": "Отказали сте верификацију током друге сесије.",
"Cannot reach identity server": "Није могуће приступити серверу идентитета",
"Your %(brand)s is misconfigured": "Ваш %(brand)s је погрешно конфигурисан",
"Ensure you have a stable internet connection, or get in touch with the server admin": "Уверите се да имате стабилну интернет везу или контактирајте администратора сервера",
"See <b>%(msgtype)s</b> messages posted to your active room": "Видите <b>%(msgtype)s</b> поруке објављене у Вашој активној соби",
"See <b>%(msgtype)s</b> messages posted to this room": "Видите <b>%(msgtype)s</b> поруке објављене у овој соби",
"Send <b>%(msgtype)s</b> messages as you in your active room": "Пошаљи <b>%(msgtype)s</b> поруке као Ви у активној соби",
"Send <b>%(msgtype)s</b> messages as you in this room": "Пошаљи <b>%(msgtype)s</b> поруке као Ви у овој соби",
"See general files posted to your active room": "Погледајте опште датотеке објављене у Вашој активној соби",
"See general files posted to this room": "Погледајте опште датотеке објављене у овој соби",
"Send general files as you in your active room": "Шаљите опште датотеке као у активној соби",
"Send general files as you in this room": "Шаљите опште датотеке као у овој соби",
"See videos posted to your active room": "Погледајте видео снимке објављене у вашој активној соби",
"See videos posted to this room": "Погледајте видео снимке објављене у овој соби",
"Send videos as you in your active room": "Шаљите видео снимке као Ви у активној соби",
"Send videos as you in this room": "Шаљите видео записе као Ви у овој соби",
"See images posted to your active room": "Погледајте слике објављене у вашој активној соби",
"See images posted to this room": "Погледајте слике објављене у овој соби",
"Send images as you in your active room": "Пошаљите слике као Ви у активној соби",
"Send images as you in this room": "Пошаљите слике као Ви у овој соби",
"See emotes posted to your active room": "Погледајте емоције објављене у Вашој активној соби",
"See emotes posted to this room": "Погледајте емоције објављене у овој соби",
"Send emotes as you in your active room": "Шаљите емоције као у активној соби",
"Send emotes as you in this room": "Пошаљите емоције као Ви у ову собу",
"See text messages posted to your active room": "Погледајте текстуалне поруке објављене у Вашој активној соби",
"See text messages posted to this room": "Погледајте текстуалне поруке објављене у овој соби",
"Send text messages as you in your active room": "Шаљите текстуалне поруке као Ви у активној соби",
"Send text messages as you in this room": "Шаљите текстуалне поруке као Ви у овој соби",
"See messages posted to your active room": "Погледајте поруке објављене у Вашој активној соби",
"See messages posted to this room": "Погледајте поруке објављене у овој соби",
"Send messages as you in your active room": "Шаљите поруке као Ви у активној соби",
"Send messages as you in this room": "Шаљите поруке као Ви у овој соби",
"The <b>%(capability)s</b> capability": "<b>%(capability)s</b> способност",
"See <b>%(eventType)s</b> events posted to your active room": "Видите <b>%(eventType)s</b> догађаје објављене у вашој активној соби",
"Send <b>%(eventType)s</b> events as you in your active room": "Пошаљите <b>%(eventType)s</b> догађаја у активној соби",
"See <b>%(eventType)s</b> events posted to this room": "Видите <b>%(eventType)s</b> догађаји објављени у овој соби",
"Send <b>%(eventType)s</b> events as you in this room": "Шаљите <b>%(eventType)s</b> догађаје као у овој соби",
"with an empty state key": "са празним статусним кључем",
"with state key %(stateKey)s": "са статусним кључем %(stateKey)s",
"See when anyone posts a sticker to your active room": "Погледајте када неко постави налепницу у вашу активну собу",
"Send stickers to your active room as you": "Пошаљите налепнице у своју активну собу као и Ви",
"See when a sticker is posted in this room": "Погледајте када је налепница постављена у овој соби",
"Send stickers to this room as you": "Пошаљите налепнице у ову собу као и Ви",
"See when the avatar changes in your active room": "Погледајте када се аватар промени у вашој активној соби",
"Change the avatar of your active room": "Промените аватар своје активне собе",
"See when the avatar changes in this room": "Погледајте када се аватар промени у овој соби",
"Change the avatar of this room": "Промените аватар ове собе",
"See when the name changes in your active room": "Погледајте када се име промени у вашој активној соби",
"Change the name of your active room": "Промените име своје активне собе",
"See when the name changes in this room": "Погледајте када се име промени у овој соби",
"Change the name of this room": "Промените име ове собе",
"See when the topic changes in your active room": "Погледајте када се тема промени у вашој активној соби",
"Change the topic of your active room": "Промените тему своје активне собе",
"See when the topic changes in this room": "Погледајте када се тема промени у овој соби",
"Change the topic of this room": "Промените тему ове собе",
"Change which room, message, or user you're viewing": "Промените коју собу, поруку или корисника гледате",
"Change which room you're viewing": "Промените коју собу гледате",
"Send stickers into your active room": "Пошаљите налепнице у своју активну собу",
"Send stickers into this room": "Пошаљите налепнице у ову собу",
"Remain on your screen when viewing another room, when running": "Останите на екрану док гледате другу собу, током рада",
"Remain on your screen while running": "Останите на екрану током рада",
"%(names)s and %(lastPerson)s are typing …": "%(names)s и %(lastPerson)s куцају…",
"%(names)s and %(count)s others are typing …|one": "%(names)s и још један корисник куца…",
"%(names)s and %(count)s others are typing …|other": "%(names)s и %(count)s корисници куцају…",
"%(displayName)s is typing …": "%(displayName)s куца …",
"Couldn't load page": "Учитавање странице није успело",
"Sign in with SSO": "Пријавите се помоћу SSO",
"Use email to optionally be discoverable by existing contacts.": "Користите е-пошту да бисте је по жељи могли открити постојећи контакти.",
"Use email or phone to optionally be discoverable by existing contacts.": "Користите е-пошту или телефон да би вас постојећи контакти опционално могли открити.",
"Add an email to be able to reset your password.": "Додајте е-пошту да бисте могли да ресетујете лозинку.",
"Phone (optional)": "Телефон (необавезно)",
"Use lowercase letters, numbers, dashes and underscores only": "Користите само мала слова, бројеве, цртице и доње црте",
"Enter phone number (required on this homeserver)": "Унесите број телефона (захтева на овом кућном серверу)",
"Other users can invite you to rooms using your contact details": "Други корисници могу да вас позову у собе користећи ваше контакт податке",
"Enter email address (required on this homeserver)": "Унесите адресу е-поште (захтева на овом кућном серверу)",
"Use an email address to recover your account": "Користите адресу е-поште за опоравак налога",
"Forgot password?": "Заборавили сте лозинку?",
"That phone number doesn't look quite right, please check and try again": "Тај телефонски број не изгледа сасвим у реду, проверите и покушајте поново",
"Enter phone number": "Унесите број телефона",
"Enter email address": "Унесите адресу е-поште",
"Enter username": "Унесите корисничко име",
"Keep going...": "Настави...",
"Password is allowed, but unsafe": "Лозинка је дозвољена, али небезбедна",
"Nice, strong password!": "Лепа, јака лозинка!",
"Enter password": "Унесите лозинку",
"Something went wrong in confirming your identity. Cancel and try again.": "Нешто је пошло по наопако у потврђивању вашег идентитета. Откажите и покушајте поново.",
"Kosovo": "/",
"Open the link in the email to continue registration.": "Отворите везу у е-поруци да бисте наставили регистрацију.",
"A confirmation email has been sent to %(emailAddress)s": "Е-пошта са потврдом је послат на %(emailAddress)s",
"Please review and accept the policies of this homeserver:": "Молимо вас да прегледате и прихватите смернице овог кућног сервера:",
"Please review and accept all of the homeserver's policies": "Молимо вас да прегледате и прихватите све смернице кућног сервера",
"Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.": "Недостаје јавни кључ captcha-е у конфигурацији матичног сервера. Молимо пријавите ово администратору кућног сервера.",
"Confirm your identity by entering your account password below.": "Потврдите свој идентитет уносом лозинке за налог испод.",
"Country Dropdown": "Падајући списак земаља",
"This homeserver would like to make sure you are not a robot.": "Овај кућни сервер жели да се увери да нисте робот.",
"User Status": "Статус корисника",
"Away": "Неприсутан",
"Toggle this dialog": "Укључи / искључи овај дијалог",
"Go to Home View": "Идите на почетни приказ",
"Move autocomplete selection up/down": "Померите избор само-довршавање горе / доле",
"End": "",
"Credits": "Заслуге",
"Legal": "Легално",
"Deactivating your account is a permanent action - be careful!": "Драктивирање вашег налога је трајна акција - будите опрезни!",
"Deactivate account": "Деактивирај налог",
"Account management": "Управљање профилом",
"Server name": "Име сервера",
"Enter the name of a new server you want to explore.": "Унесите име новог сервера који желите да истражите.",
"Add a new server": "Додајте нови сервер",
"Matrix": "Матрикс",
"Remove server": "Уклоните сервер",
"Are you sure you want to remove <b>%(serverName)s</b>": "Да ли сте сигурни да желите да уклоните <b>%(serverName)s</b>",
"Your server": "Ваш сервер",
"All rooms": "Све собе",
"Low bandwidth mode": "Режим ниског протока",
"Who are you working with?": "Са ким радите?",
"Screens": "Екрани",
"Share your screen": "Поделите свој екран",
"Alt Gr": "Алт Гр",
"Alt": "Алт",
"Autocomplete": "Аутоматско довршавање",
"This room is public": "Ова соба је јавна",
"Caution:": "Опрез:",
"Change room avatar": "Промените аватар собе",
"Browse": "Прегледајте",
"Versions": "Верзије",
"Set a new status...": "Поставите нови статус ...",
"Set status": "Постави статус",
"Update status": "Ажурирај статус",
"Clear status": "Очисти статус",
"User rules": "Корисничка правила",
"Use the <a>Desktop app</a> to see all encrypted files": "Користи <a> десктоп апликација </a> да видиш све шифроване датотеке",
"This widget may use cookies.": "Овај виџет може користити колачиће.",
"Widget added by": "Додао је виџет",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s.": "Коришћење овог виџета може да дели податке <helpIcon /> са %(widgetDomain)s.",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.": "Коришћење овог виџета може да дели податке <helpIcon /> са %(widgetDomain)s и вашим интеграционим менаџером.",
"Widget ID": "ИД виџета",
"Room ID": "ИД собе",
"%(brand)s URL": "%(brand)s УРЛ",
"Your user ID": "Ваша корисничка ИД",
"Your avatar URL": "УРЛ вашег аватара",
"Your display name": "Ваше име за приказ",
"exists": "постоји",
"Collapse room list section": "Скупи одељак листе соба",
"Select room from the room list": "Изаберите собу са листе соба",
"Navigate up/down in the room list": "Крећите се горе / доле у листи соба",
"Jump to room search": "Пређите на претрагу собе",
"Search (must be enabled)": "Претрага (мора бити омогућена)",
"Upload a file": "Отпремите датотеку",
"Jump to oldest unread message": "Скочите на најстарију непрочитану поруку",
"Dismiss read marker and jump to bottom": "Одбаците ознаку за читање и скочите до дна",
"Done": "Готово",
"Interactively verify by Emoji": "Интерактивно верификујте смајлићима",
"Manually Verify by Text": "Ручно потврди текстом",
"Not Trusted": "Није поуздано",
"Ask this user to verify their session, or manually verify it below.": "Питајте овог корисника да потврди његову сесију или ручно да потврди у наставку.",
"%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) се улоговао у нову сесију без потврђивања:",
"Verify your other session using one of the options below.": "Потврдите другу сесију помоћу једних од опција у испод.",
"%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s је створиоправило о забрани које се подударају са %(glob)s због %(reason)s",
"%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s је створио правило које забрањије сервере који се подударају са %(glob)s због %(reason)s",
"%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s је створио правило које забрањује собе које се подударају са %(glob)s због %(reason)s",
"%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s је створио правило које забрањује кориснике који се подударају са %(glob)s због %(reason)s",
"%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s је аужурирао правило о забрани које се поударају са %(glob)s због %(reason)s",
"%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s је уклонио правило које забрањује кориснике који се подударају са %(glob)s",
"%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s је уклонио правило које забрањује собе које подударају са %(glob)s",
"%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s је уклонио правил које забрањује сервере који подударају са %(glob)s",
"%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s је аужурирао правило које забрањује сервере које се подударају са %(glob)s због %(reason)s",
"%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s је аужурирао правило које забрањује соба које се подударају са %(glob)s због %(reason)s",
"%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s је аужурирао правило о забрани корисника који се подударају са %(glob)s због %(reason)s",
"You signed in to a new session without verifying it:": "Пријавили сте се у нову сесију без потврђивања:",
"%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s је аужурирао правило о забрани које се подударало са %(oldGlob)s да би се подударало са %(newGlob)s због %(reason)s",
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s је променио правило које је забрањинвало сервере који су се подударале са %(oldGlob)s да би се подударале са %(newGlob)s због %(reason)s",
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s је променио правило које је забрањивало собе који се подударају са %(oldGlob)s да би се подударале са %(newGlob)s због %(reason)s",
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s је променио правило које забрањије кориснике који се подударају са %(oldGlob)s да се подудара са %(newGlob)s због %(reason)s",
"Open": "Отвори",
"Accept all %(invitedRooms)s invites": "Прихвати све %(invitedRooms)s позивнице",
"%(senderName)s updated an invalid ban rule": "%(senderName)s је аужурирао неважеће правило о забрани",
"%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s је уклонио правило о забрани које подудара са %(glob)s",
"Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Користите сервер за идентитет да бисте послали позивнице е-поштом. Кликните на даље да бисте користили уобичајни сервер идентитета %(defaultIdentityServerName)s или управљајте у подешавањима.",
"%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s је омогућио њух за %(groups)s у овој суби.",
"%(senderDisplayName)s changed the server ACLs for this room.": "%(senderDisplayName)s је променио ACL сервере за ову собу.",
"%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s је онемогућио њух за %(groups)s у овој соби.",
"%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s је омогућио њух за %(newGroups)s и онемогућио њух за %(oldGroups)s у овој соби.",
"%(senderDisplayName)s set the server ACLs for this room.": "%(senderDisplayName)s је подесио ACL сервере за ову собу.",
"Sends the given emote coloured as a rainbow": "Шаље дату емоцију обојену као дуга",
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Кључ за потписивање који сте навели поклапа се са кључем за потписивање који сте добили од %(userId)s сесије %(deviceId)s. Сесија је означена као проверена.",
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "УПОЗОРЕЊЕ: ПРОВЕРА КЉУЧА НИЈЕ УСПЕЛА! Кључ за потписивање за %(userId)s и сесију %(deviceId)s је \"%(fprint)s\", који се не подудара са наведеним кључем \"%(fingerprint)s\". То може значити да су ваше комуникације пресретнуте!",
"Verifies a user, session, and pubkey tuple": "Верификује корисника, сесију и pubkey tuple",
"Réunion": "Реунион",
"Your homeserver rejected your log in attempt. This could be due to things just taking too long. Please try again. If this continues, please contact your homeserver administrator.": "Ваш кућни сервер је одбио ваш покушај пријављивања. То би могло бити због ствари које предуго трају. Молим вас, покушајте поново. Ако се ово настави, контактирајте администратора кућног сервера.",
"Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.": "Ваш кућни сервер није био доступан и није могао да вас пријави. Покушајте поново. Ако се ово настави, контактирајте администратора кућног сервера.",
"We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Тражили смо од прегледача да запамти који кућни сервер користите за пријаву, али нажалост ваш претраживач га је заборавио. Идите на страницу за пријављивање и покушајте поново.",
"You're already in a call with this person.": "Већ разговарате са овом особом.",
"Already in call": "Већ у позиву",
"Whether you're using %(brand)s as an installed Progressive Web App": "Без обзира да ли користите %(brand)s као инсталирану Прогресивну веб апликацију",
"Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "Без обзира да ли користите функцију „breadcrumbs“ (аватари изнад листе соба)"
}

View file

@ -3133,5 +3133,52 @@
"This homeserver has been blocked by it's administrator.": "Den här hemservern har blockerats av sin administratör.",
"This homeserver has been blocked by its administrator.": "Hemservern har blockerats av sin administratör.",
"You're already in a call with this person.": "Du är redan i ett samtal med den här personen.",
"Already in call": "Redan i samtal"
"Already in call": "Redan i samtal",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "Verifiera den här inloggningen för att komma åt dina krypterade meddelanden och visa för andra att den här inloggningen verkligen är du.",
"Verify with another session": "Verifiera med en annan session",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "Vi kommer att skapa rum för varje. Du kan lägga till fler senare, inklusive såna som redan finns.",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "Låt oss skapa ett rum för varje. Du kan lägga till fler sen, inklusive såna som redan finns.",
"Make sure the right people have access. You can invite more later.": "Se till att rätt personer har tillgång. Du kan bjuda in fler senare.",
"A private space to organise your rooms": "Ett privat utrymme för att organisera dina rum",
"Just me": "Bara jag",
"Make sure the right people have access to %(name)s": "Försäkra dig om att rätt personer har tillgång till %(name)s",
"Go to my first room": "Gå till mitt första rum",
"It's just you at the moment, it will be even better with others.": "Bara du är här för tillfället, det kommer att vara ännu bättre med andra.",
"Share %(name)s": "Dela %(name)s",
"Private space": "Privat utrymme",
"Public space": "Offentligt utrymme",
"<inviter/> invites you": "<inviter/> bjuder in dig",
"Search names and description": "Sök bland namn och beskrivningar",
"Create room": "Skapa rum",
"You may want to try a different search or check for typos.": "Du kanske vill pröva en annan söksträng eller kolla efter felstavningar.",
"No results found": "Inga resultat funna",
"Mark as suggested": "Markera som föreslaget",
"Mark as not suggested": "Markera som inte föreslaget",
"Removing...": "Tar bort…",
"Failed to remove some rooms. Try again later": "Misslyckades att ta bort vissa rum. Försök igen senare",
"%(count)s rooms and 1 space|one": "%(count)s rum och 1 utrymme",
"%(count)s rooms and 1 space|other": "%(count)s rum och 1 utrymme",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s rum och %(numSpaces)s utrymmen",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s rum och %(numSpaces)s utrymmen",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "Om du inte hittar rummet du letar efter, be om en inbjudan eller <a>skapa ett nytt rum</a>.",
"Suggested": "Föreslaget",
"This room is suggested as a good one to join": "Det här rummet föreslås som ett bra att gå med i",
"%(count)s rooms|one": "%(count)s rum",
"%(count)s rooms|other": "%(count)s rum",
"You don't have permission": "Du har inte behörighet",
"Open": "Öppna",
"%(count)s messages deleted.|one": "%(count)s meddelande raderat.",
"%(count)s messages deleted.|other": "%(count)s meddelanden raderade.",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Detta påverkar normalt bara hur rummet hanteras på serven. Om du upplever problem med din %(brand)s, vänligen rapportera en bugg.",
"Invite to %(roomName)s": "Bjud in till %(roomName)s",
"Edit devices": "Redigera enheter",
"Invite People": "Bjud in personer",
"Invite with email or username": "Bjud in med e-postadress eller användarnamn",
"You can change these anytime.": "Du kan ändra dessa när som helst.",
"Add some details to help people recognise it.": "Lägg till några detaljer för att hjälpa folk att känn igen det.",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Utrymmen är nya sätt att gruppera rum och personer. För att gå med i ett existerande utrymme så behöver du en inbjudan.",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "Från %(deviceName)s %(deviceId)s på %(ip)s",
"Check your devices": "Kolla dina enheter",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "En ny inloggning kommer åt ditt konto: %(name)s %(deviceID)s på %(ip)s",
"You have unverified logins": "Du har overifierade inloggningar"
}

View file

@ -34,5 +34,6 @@
"e.g. %(exampleValue)s": "a.m. %(exampleValue)s",
"The version of %(brand)s": "Taleqqemt n %(brand)s",
"Add Phone Number": "Rnu uṭṭun n utilifun",
"Add Email Address": "Rnu tasna imayl"
"Add Email Address": "Rnu tasna imayl",
"Open": "Ṛẓem"
}

View file

@ -54,7 +54,7 @@
"Anyone who knows the room's link, apart from guests": "Кожний, хто знає посилання на кімнату, окрім гостей",
"Anyone who knows the room's link, including guests": "Кожний, хто знає посилання на кімнату, включно гостей",
"Are you sure?": "Ви впевнені?",
"Are you sure you want to leave the room '%(roomName)s'?": "Ви впевнені, що хочете покинути '%(roomName)s'?",
"Are you sure you want to leave the room '%(roomName)s'?": "Ви впевнені, що хочете залишити '%(roomName)s'?",
"Are you sure you want to reject the invitation?": "Ви впевнені, що ви хочете відхилити запрошення?",
"Attachment": "Прикріплення",
"Autoplay GIFs and videos": "Автовідтворення GIF і відео",
@ -310,7 +310,7 @@
"To use it, just wait for autocomplete results to load and tab through them.": "Щоб цим скористатися, просто почекайте на підказки доповнення й перемикайтеся між ними клавішею TAB.",
"Changes your display nickname": "Змінює ваш нік",
"Invites user with given id to current room": "Запрошує користувача з вказаним ідентифікатором до кімнати",
"Leave room": "Покинути кімнату",
"Leave room": "Залишити кімнату",
"Kicks user with given id": "Викидає з кімнати користувача з вказаним ідентифікатором",
"Ignores a user, hiding their messages from you": "Ігнорує користувача, приховуючи його повідомлення від вас",
"Ignored user": "Зігнорований користувач",
@ -609,7 +609,7 @@
"You have %(count)s unread notifications in a prior version of this room.|other": "Ви маєте %(count)s непрочитаних сповіщень у попередній версії цієї кімнати.",
"You have %(count)s unread notifications in a prior version of this room.|one": "У вас %(count)s непрочитане сповіщення у попередній версії цієї кімнати.",
"Deactivate user?": "Знедіяти користувача?",
"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?": "Знедіювання цього користувача виведе їх з системи і унеможливить вхід у майбутньому. До того ж, вони залишать усі кімнати, в яких перебувають. Ця дія є безповоротною. Ви впевнені, що хочете знедіяти цього користувача?",
"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?": "Знедіювання цього користувача виведе їх з системи й унеможливить вхід у майбутньому. До того ж, вони залишать усі кімнати, в яких перебувають. Ця дія є безповоротною. Ви впевнені, що хочете знедіяти цього користувача?",
"Deactivate user": "Знедіяти користувача",
"Failed to deactivate user": "Не вдалось знедіяти користувача",
"Deactivating your account <b>does not by default cause us to forget messages you have sent.</b> If you would like us to forget your messages, please tick the box below.": "Знедіювання вашого облікового запису <b>типово не призводить до забуття надісланих вами повідомлень.</b> Якщо ви бажаєте, щоб ми забули ваші повідомлення, поставте прапорець внизу.",
@ -1032,7 +1032,7 @@
"Use default": "Типово",
"Mentions & Keywords": "Згадки та ключові слова",
"Notification options": "Параметри сповіщень",
"Leave Room": "Вийти з кімнати",
"Leave Room": "Залишити кімнату",
"Forget Room": "Забути кімнату",
"Favourited": "Улюблено",
"%(count)s unread messages including mentions.|other": "%(count)s непрочитаних повідомлень включно зі згадками.",
@ -1600,5 +1600,7 @@
"Try again": "Спробувати ще раз",
"%(creator)s created this DM.": "%(creator)s створює цю приватну розмову.",
"Share Link to User": "Поділитися посиланням на користувача",
"Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their avatar.": "Повідомлення тут захищено наскрізним шифруванням. Підтвердьте %(displayName)s у їхньому профілі — натиснувши на їх аватар."
"Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their avatar.": "Повідомлення тут захищено наскрізним шифруванням. Підтвердьте %(displayName)s у їхньому профілі — натиснувши на їх аватар.",
"Open": "Відкрити",
"<a>In reply to</a> <pill>": "<a>У відповідь на</a> <pill>"
}

File diff suppressed because it is too large Load diff

View file

@ -3205,5 +3205,51 @@
"This homeserver has been blocked by it's administrator.": "此家伺服器已被其管理員封鎖。",
"This homeserver has been blocked by its administrator.": "此家伺服器已被其管理員封鎖。",
"You're already in a call with this person.": "您已與此人通話。",
"Already in call": "已在通話中"
"Already in call": "已在通話中",
"Verify this login to access your encrypted messages and prove to others that this login is really you.": "驗證此登入已存取您的已加密訊息,並向其他人證明此登入真的視您。",
"Verify with another session": "使用另一個工作階段進行驗證",
"We'll create rooms for each of them. You can add more later too, including already existing ones.": "我們將會為每個主題建立一個聊天室。稍後您還可以新增更多,包含既有的。",
"Let's create a room for each of them. You can add more later too, including already existing ones.": "讓我們為每個主題建立一個聊天室。稍後您還可以新增更多,包含既有的。",
"Make sure the right people have access. You can invite more later.": "確保合適的人有權存取。稍後您可以邀請更多人。",
"A private space to organise your rooms": "供整理您聊天室的私人空間",
"Just me": "只有我",
"Make sure the right people have access to %(name)s": "確保合適的人有權存取 %(name)s",
"Go to my first room": "到我的第一個聊天室",
"It's just you at the moment, it will be even better with others.": "目前只有您一個人,有其他人會更好。",
"Share %(name)s": "分享 %(name)s",
"Private space": "私人空間",
"Public space": "公開空間",
"<inviter/> invites you": "<inviter/> 邀請您",
"Search names and description": "搜尋名稱與描述",
"You may want to try a different search or check for typos.": "您可能要嘗試其他搜尋或檢查是否有拼字錯誤。",
"No results found": "找不到結果",
"Mark as suggested": "標記為建議",
"Mark as not suggested": "標記為不建議",
"Removing...": "正在移除……",
"Failed to remove some rooms. Try again later": "移除部份聊天是失敗。稍後再試",
"%(count)s rooms and 1 space|one": "%(count)s 個聊天室與 1 個空間",
"%(count)s rooms and 1 space|other": "%(count)s 個聊天室與 1 個空間",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s 個聊天室與 %(numSpaces)s 個空間",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s 個聊天室與 %(numSpaces)s 個空間",
"If you can't find the room you're looking for, ask for an invite or <a>create a new room</a>.": "如果您找不到您在找的聊天室,請尋求邀請或<a>建立新聊天室</a>。",
"Suggested": "建議",
"This room is suggested as a good one to join": "建議加入這個相當不錯的聊天室",
"%(count)s rooms|one": "%(count)s 個聊天室",
"%(count)s rooms|other": "%(count)s 個聊天室",
"You don't have permission": "您沒有權限",
"Open": "開啟",
"%(count)s messages deleted.|one": "已刪除 %(count)s 則訊息。",
"%(count)s messages deleted.|other": "已刪除 %(count)s 則訊息。",
"This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "這通常只影響伺服器如何處理聊天是。如果您的 %(brand)s 遇到問題,請回報臭蟲。",
"Invite to %(roomName)s": "邀請至 %(roomName)s",
"Edit devices": "編輯裝置",
"Invite People": "邀請夥伴",
"Invite with email or username": "使用電子郵件或使用者名稱邀請",
"You can change these anytime.": "您隨時可以變更這些。",
"Add some details to help people recognise it.": "新增一些詳細資訊來協助人們識別它。",
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "空間是將聊天室與人們分類的新方法。要加入既有的空間,您需要邀請。",
"From %(deviceName)s (%(deviceId)s) at %(ip)s": "從 %(deviceName)s (%(deviceId)s) 於 %(ip)s",
"Check your devices": "檢查您的裝置",
"A new login is accessing your account: %(name)s (%(deviceID)s) at %(ip)s": "新登入正在存取您的帳號:%(name)s (%(deviceID)s) 於 %(ip)s",
"You have unverified logins": "您有未驗證的登入"
}

View file

@ -128,6 +128,12 @@ export const SETTINGS: {[setting: string]: ISetting} = {
default: false,
controller: new ReloadOnChangeController(),
},
"feature_dnd": {
isFeature: true,
displayName: _td("Show options to enable 'Do not disturb' mode"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_voice_messages": {
isFeature: true,
displayName: _td("Send and receive voice messages (in development)"),
@ -220,18 +226,16 @@ export const SETTINGS: {[setting: string]: ISetting} = {
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_room_history_key_sharing": {
isFeature: true,
displayName: _td("Share decryption keys for room history when inviting users"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"advancedRoomListLogging": {
// TODO: Remove flag before launch: https://github.com/vector-im/element-web/issues/14231
displayName: _td("Enable advanced debugging for the room list"),
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
},
"doNotDisturb": {
supportedLevels: [SettingLevel.DEVICE],
default: false,
},
"mjolnirRooms": {
supportedLevels: [SettingLevel.ACCOUNT],
default: [],

View file

@ -121,21 +121,16 @@ export class SetupEncryptionStore extends EventEmitter {
// on the first trust check, and the key backup restore will happen
// in the background.
await new Promise((resolve, reject) => {
try {
accessSecretStorage(async () => {
await cli.checkOwnCrossSigningTrust();
resolve();
if (backupInfo) {
// A complete restore can take many minutes for large
// accounts / slow servers, so we allow the dialog
// to advance before this.
await cli.restoreKeyBackupWithSecretStorage(backupInfo);
}
}).catch(reject);
} catch (e) {
console.error(e);
reject(e);
}
accessSecretStorage(async () => {
await cli.checkOwnCrossSigningTrust();
resolve();
if (backupInfo) {
// A complete restore can take many minutes for large
// accounts / slow servers, so we allow the dialog
// to advance before this.
await cli.restoreKeyBackupWithSecretStorage(backupInfo);
}
}).catch(reject);
});
if (cli.getCrossSigningId()) {

Some files were not shown because too many files have changed in this diff Show more