Merge pull request #2860 from ClearlyClaire/glitch-soc/merge-upstream

Merge upstream changes up to 9d664f87a0
This commit is contained in:
Claire 2024-09-29 20:36:32 +02:00 committed by GitHub
commit 9bf624b44d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
240 changed files with 3054 additions and 1462 deletions

View file

@ -134,7 +134,7 @@ GEM
bindata (2.5.0) bindata (2.5.0)
binding_of_caller (1.0.1) binding_of_caller (1.0.1)
debug_inspector (>= 1.2.0) debug_inspector (>= 1.2.0)
blurhash (0.1.7) blurhash (0.1.8)
bootsnap (1.18.4) bootsnap (1.18.4)
msgpack (~> 1.2) msgpack (~> 1.2)
brakeman (6.2.1) brakeman (6.2.1)

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
class Api::V1::DomainBlocks::PreviewsController < Api::BaseController
before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }
before_action :require_user!
before_action :set_domain
before_action :set_domain_block_preview
def show
render json: @domain_block_preview, serializer: REST::DomainBlockPreviewSerializer
end
private
def set_domain
@domain = TagManager.instance.normalize_domain(params[:domain])
end
def set_domain_block_preview
@domain_block_preview = with_read_replica do
DomainBlockPreviewPresenter.new(
following_count: current_account.following.where(domain: @domain).count,
followers_count: current_account.followers.where(domain: @domain).count
)
end
end
end

View file

@ -10,16 +10,17 @@ module SettingsHelper
end end
def featured_tags_hint(recently_used_tags) def featured_tags_hint(recently_used_tags)
safe_join( recently_used_tags.present? &&
[ safe_join(
t('simple_form.hints.featured_tag.name'), [
safe_join( t('simple_form.hints.featured_tag.name'),
links_for_featured_tags(recently_used_tags), safe_join(
', ' links_for_featured_tags(recently_used_tags),
), ', '
], ),
' ' ],
) ' '
)
end end
def session_device_icon(session) def session_device_icon(session)

View file

@ -70,6 +70,7 @@ export async function apiRequest<ApiResponse = unknown>(
args: { args: {
params?: RequestParamsOrData; params?: RequestParamsOrData;
data?: RequestParamsOrData; data?: RequestParamsOrData;
timeout?: number;
} = {}, } = {},
) { ) {
const { data } = await api().request<ApiResponse>({ const { data } = await api().request<ApiResponse>({

View file

@ -0,0 +1,67 @@
import { useState, useCallback, useRef } from 'react';
import { FormattedMessage } from 'react-intl';
import Overlay from 'react-overlays/Overlay';
import type {
OffsetValue,
UsePopperOptions,
} from 'react-overlays/esm/usePopper';
const offset = [0, 4] as OffsetValue;
const popperConfig = { strategy: 'fixed' } as UsePopperOptions;
export const AltTextBadge: React.FC<{
description: string;
}> = ({ description }) => {
const anchorRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false);
const handleClick = useCallback(() => {
setOpen((v) => !v);
}, [setOpen]);
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
return (
<>
<button
ref={anchorRef}
className='media-gallery__alt__label'
onClick={handleClick}
>
ALT
</button>
<Overlay
rootClose
onHide={handleClose}
show={open}
target={anchorRef.current}
placement='top-end'
flip
offset={offset}
popperConfig={popperConfig}
>
{({ props }) => (
<div {...props} className='hover-card-controller'>
<div
className='media-gallery__alt__popover dropdown-animation'
role='tooltip'
>
<h4>
<FormattedMessage
id='alt_text_badge.title'
defaultMessage='Alt text'
/>
</h4>
<p>{description}</p>
</div>
</div>
)}
</Overlay>
</>
);
};

View file

@ -7,6 +7,7 @@ interface BaseProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> { extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
block?: boolean; block?: boolean;
secondary?: boolean; secondary?: boolean;
dangerous?: boolean;
} }
interface PropsChildren extends PropsWithChildren<BaseProps> { interface PropsChildren extends PropsWithChildren<BaseProps> {
@ -26,6 +27,7 @@ export const Button: React.FC<Props> = ({
disabled, disabled,
block, block,
secondary, secondary,
dangerous,
className, className,
title, title,
text, text,
@ -46,6 +48,7 @@ export const Button: React.FC<Props> = ({
className={classNames('button', className, { className={classNames('button', className, {
'button-secondary': secondary, 'button-secondary': secondary,
'button--block': block, 'button--block': block,
'button--dangerous': dangerous,
})} })}
disabled={disabled} disabled={disabled}
onClick={handleClick} onClick={handleClick}

View file

@ -10,7 +10,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { AltTextBadge } from 'flavours/glitch/components/alt_text_badge';
import { Blurhash } from 'flavours/glitch/components/blurhash'; import { Blurhash } from 'flavours/glitch/components/blurhash';
import { formatTime } from 'flavours/glitch/features/video';
import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state'; import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
@ -58,7 +60,7 @@ class Item extends PureComponent {
hoverToPlay () { hoverToPlay () {
const { attachment } = this.props; const { attachment } = this.props;
return !this.getAutoPlay() && attachment.get('type') === 'gifv'; return !this.getAutoPlay() && ['gifv', 'video'].includes(attachment.get('type'));
} }
handleClick = (e) => { handleClick = (e) => {
@ -97,7 +99,7 @@ class Item extends PureComponent {
} }
if (attachment.get('description')?.length > 0) { if (attachment.get('description')?.length > 0) {
badges.push(<span key='alt' className='media-gallery__alt__label'>ALT</span>); badges.push(<AltTextBadge key='alt' description={attachment.get('description')} />);
} }
const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
@ -152,10 +154,15 @@ class Item extends PureComponent {
/> />
</a> </a>
); );
} else if (attachment.get('type') === 'gifv') { } else if (['gifv', 'video'].includes(attachment.get('type'))) {
const autoPlay = this.getAutoPlay(); const autoPlay = this.getAutoPlay();
const duration = attachment.getIn(['meta', 'original', 'duration']);
badges.push(<span key='gif' className='media-gallery__gifv__label'>GIF</span>); if (attachment.get('type') === 'gifv') {
badges.push(<span key='gif' className='media-gallery__alt__label media-gallery__alt__label--non-interactive'>GIF</span>);
} else {
badges.push(<span key='video' className='media-gallery__alt__label media-gallery__alt__label--non-interactive'>{formatTime(Math.floor(duration))}</span>);
}
thumbnail = ( thumbnail = (
<div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}> <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
@ -169,6 +176,7 @@ class Item extends PureComponent {
onClick={this.handleClick} onClick={this.handleClick}
onMouseEnter={this.handleMouseEnter} onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave} onMouseLeave={this.handleMouseLeave}
onLoadedData={this.handleImageLoad}
autoPlay={autoPlay} autoPlay={autoPlay}
playsInline playsInline
loop loop

View file

@ -4,22 +4,22 @@ import AccountNavigation from 'flavours/glitch/features/account/navigation';
import Trends from 'flavours/glitch/features/getting_started/containers/trends_container'; import Trends from 'flavours/glitch/features/getting_started/containers/trends_container';
import { showTrends } from 'flavours/glitch/initial_state'; import { showTrends } from 'flavours/glitch/initial_state';
const DefaultNavigation: React.FC = () => const DefaultNavigation: React.FC = () => (showTrends ? <Trends /> : null);
showTrends ? (
<>
<div className='flex-spacer' />
<Trends />
</>
) : null;
export const NavigationPortal: React.FC = () => ( export const NavigationPortal: React.FC = () => (
<Switch> <div className='navigation-panel__portal'>
<Route path='/@:acct' exact component={AccountNavigation} /> <Switch>
<Route path='/@:acct/tagged/:tagged?' exact component={AccountNavigation} /> <Route path='/@:acct' exact component={AccountNavigation} />
<Route path='/@:acct/with_replies' exact component={AccountNavigation} /> <Route
<Route path='/@:acct/followers' exact component={AccountNavigation} /> path='/@:acct/tagged/:tagged?'
<Route path='/@:acct/following' exact component={AccountNavigation} /> exact
<Route path='/@:acct/media' exact component={AccountNavigation} /> component={AccountNavigation}
<Route component={DefaultNavigation} /> />
</Switch> <Route path='/@:acct/with_replies' exact component={AccountNavigation} />
<Route path='/@:acct/followers' exact component={AccountNavigation} />
<Route path='/@:acct/following' exact component={AccountNavigation} />
<Route path='/@:acct/media' exact component={AccountNavigation} />
<Route component={DefaultNavigation} />
</Switch>
</div>
); );

View file

@ -648,6 +648,27 @@ class Status extends ImmutablePureComponent {
media={status.get('media_attachments')} media={status.get('media_attachments')}
/>, />,
); );
} else if (['image', 'gifv'].includes(status.getIn(['media_attachments', 0, 'type'])) || status.get('media_attachments').size > 1) {
media.push(
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
{Component => (
<Component
media={attachments}
lang={language}
sensitive={status.get('sensitive')}
letterbox={settings.getIn(['media', 'letterbox'])}
fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
hidden={isCollapsed || !isExpanded}
onOpenMedia={this.handleOpenMedia}
cacheWidth={this.props.cacheMediaWidth}
defaultWidth={this.props.cachedMediaWidth}
visible={this.state.showMedia}
onToggleVisibility={this.handleToggleMediaVisibility}
/>
)}
</Bundle>,
);
mediaIcons.push('picture-o');
} else if (attachments.getIn([0, 'type']) === 'audio') { } else if (attachments.getIn([0, 'type']) === 'audio') {
const attachment = status.getIn(['media_attachments', 0]); const attachment = status.getIn(['media_attachments', 0]);
const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
@ -703,27 +724,6 @@ class Status extends ImmutablePureComponent {
</Bundle>, </Bundle>,
); );
mediaIcons.push('video-camera'); mediaIcons.push('video-camera');
} else { // Media type is 'image' or 'gifv'
media.push(
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
{Component => (
<Component
media={attachments}
lang={language}
sensitive={status.get('sensitive')}
letterbox={settings.getIn(['media', 'letterbox'])}
fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
hidden={isCollapsed || !isExpanded}
onOpenMedia={this.handleOpenMedia}
cacheWidth={this.props.cacheMediaWidth}
defaultWidth={this.props.cachedMediaWidth}
visible={this.state.showMedia}
onToggleVisibility={this.handleToggleMediaVisibility}
/>
)}
</Bundle>,
);
mediaIcons.push('picture-o');
} }
if (!status.get('sensitive') && !(status.get('spoiler_text').length > 0) && settings.getIn(['collapsed', 'backgrounds', 'preview_images'])) { if (!status.get('sensitive') && !(status.get('spoiler_text').length > 0) && settings.getIn(['collapsed', 'backgrounds', 'preview_images'])) {

View file

@ -43,10 +43,7 @@ class AccountNavigation extends PureComponent {
} }
return ( return (
<> <FeaturedTags accountId={accountId} tagged={tagged} />
<div className='flex-spacer' />
<FeaturedTags accountId={accountId} tagged={tagged} />
</>
); );
} }

View file

@ -1,158 +0,0 @@
import PropTypes from 'prop-types';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import AudiotrackIcon from '@/material-icons/400-24px/music_note.svg?react';
import PlayArrowIcon from '@/material-icons/400-24px/play_arrow.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { Blurhash } from 'flavours/glitch/components/blurhash';
import { Icon } from 'flavours/glitch/components/icon';
import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/initial_state';
export default class MediaItem extends ImmutablePureComponent {
static propTypes = {
attachment: ImmutablePropTypes.map.isRequired,
displayWidth: PropTypes.number.isRequired,
onOpenMedia: PropTypes.func.isRequired,
};
state = {
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
loaded: false,
};
handleImageLoad = () => {
this.setState({ loaded: true });
};
handleMouseEnter = e => {
if (this.hoverToPlay()) {
e.target.play();
}
};
handleMouseLeave = e => {
if (this.hoverToPlay()) {
e.target.pause();
e.target.currentTime = 0;
}
};
hoverToPlay () {
return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1;
}
handleClick = e => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault();
if (this.state.visible) {
this.props.onOpenMedia(this.props.attachment);
} else {
this.setState({ visible: true });
}
}
};
render () {
const { attachment, displayWidth } = this.props;
const { visible, loaded } = this.state;
const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
const height = width;
const status = attachment.get('status');
const title = status.get('spoiler_text') || attachment.get('description');
let thumbnail, label, icon, content;
if (!visible) {
icon = (
<span className='account-gallery__item__icons'>
<Icon id='eye-slash' icon={VisibilityOffIcon} />
</span>
);
} else {
if (['audio', 'video'].includes(attachment.get('type'))) {
content = (
<img
src={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
alt={attachment.get('description')}
lang={status.get('language')}
onLoad={this.handleImageLoad}
/>
);
if (attachment.get('type') === 'audio') {
label = <Icon id='music' icon={AudiotrackIcon} />;
} else {
label = <Icon id='play' icon={PlayArrowIcon} />;
}
} else if (attachment.get('type') === 'image') {
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
const x = ((focusX / 2) + .5) * 100;
const y = ((focusY / -2) + .5) * 100;
content = (
<img
src={attachment.get('preview_url')}
alt={attachment.get('description')}
lang={status.get('language')}
style={{ objectPosition: `${x}% ${y}%` }}
onLoad={this.handleImageLoad}
/>
);
} else if (attachment.get('type') === 'gifv') {
content = (
<video
className='media-gallery__item-gifv-thumbnail'
aria-label={attachment.get('description')}
title={attachment.get('description')}
lang={status.get('language')}
role='application'
src={attachment.get('url')}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
autoPlay={autoPlayGif}
playsInline
loop
muted
/>
);
label = 'GIF';
}
thumbnail = (
<div className='media-gallery__gifv'>
{content}
{label && (
<div className='media-gallery__item__badges'>
<span className='media-gallery__gifv__label'>{label}</span>
</div>
)}
</div>
);
}
return (
<div className='account-gallery__item' style={{ width, height }}>
<a className='media-gallery__item-thumbnail' href={status.get('url')} onClick={this.handleClick} title={title} target='_blank' rel='noopener noreferrer'>
<Blurhash
hash={attachment.get('blurhash')}
className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })}
dummy={!useBlurhash}
/>
{visible ? thumbnail : icon}
</a>
</div>
);
}
}

View file

@ -0,0 +1,203 @@
import { useState, useCallback } from 'react';
import classNames from 'classnames';
import HeadphonesIcon from '@/material-icons/400-24px/headphones-fill.svg?react';
import MovieIcon from '@/material-icons/400-24px/movie-fill.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { AltTextBadge } from 'flavours/glitch/components/alt_text_badge';
import { Blurhash } from 'flavours/glitch/components/blurhash';
import { Icon } from 'flavours/glitch/components/icon';
import { formatTime } from 'flavours/glitch/features/video';
import {
autoPlayGif,
displayMedia,
useBlurhash,
} from 'flavours/glitch/initial_state';
import type { Status, MediaAttachment } from 'flavours/glitch/models/status';
export const MediaItem: React.FC<{
attachment: MediaAttachment;
onOpenMedia: (arg0: MediaAttachment) => void;
}> = ({ attachment, onOpenMedia }) => {
const [visible, setVisible] = useState(
(displayMedia !== 'hide_all' &&
!attachment.getIn(['status', 'sensitive'])) ||
displayMedia === 'show_all',
);
const [loaded, setLoaded] = useState(false);
const handleImageLoad = useCallback(() => {
setLoaded(true);
}, [setLoaded]);
const handleMouseEnter = useCallback(
(e: React.MouseEvent<HTMLVideoElement>) => {
if (e.target instanceof HTMLVideoElement) {
void e.target.play();
}
},
[],
);
const handleMouseLeave = useCallback(
(e: React.MouseEvent<HTMLVideoElement>) => {
if (e.target instanceof HTMLVideoElement) {
e.target.pause();
e.target.currentTime = 0;
}
},
[],
);
const handleClick = useCallback(
(e: React.MouseEvent<HTMLAnchorElement>) => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault();
if (visible) {
onOpenMedia(attachment);
} else {
setVisible(true);
}
}
},
[attachment, visible, onOpenMedia, setVisible],
);
const status = attachment.get('status') as Status;
const description = (attachment.getIn(['translation', 'description']) ||
attachment.get('description')) as string | undefined;
const previewUrl = attachment.get('preview_url') as string;
const fullUrl = attachment.get('url') as string;
const avatarUrl = status.getIn(['account', 'avatar_static']) as string;
const lang = status.get('language') as string;
const blurhash = attachment.get('blurhash') as string;
const statusUrl = status.get('url') as string;
const type = attachment.get('type') as string;
let thumbnail;
const badges = [];
if (description && description.length > 0) {
badges.push(<AltTextBadge key='alt' description={description} />);
}
if (!visible) {
thumbnail = (
<div className='media-gallery__item__overlay'>
<Icon id='eye-slash' icon={VisibilityOffIcon} />
</div>
);
} else if (type === 'audio') {
thumbnail = (
<>
<img
src={previewUrl || avatarUrl}
alt={description}
title={description}
lang={lang}
onLoad={handleImageLoad}
/>
<div className='media-gallery__item__overlay media-gallery__item__overlay--corner'>
<Icon id='music' icon={HeadphonesIcon} />
</div>
</>
);
} else if (type === 'image') {
const focusX = (attachment.getIn(['meta', 'focus', 'x']) || 0) as number;
const focusY = (attachment.getIn(['meta', 'focus', 'y']) || 0) as number;
const x = (focusX / 2 + 0.5) * 100;
const y = (focusY / -2 + 0.5) * 100;
thumbnail = (
<img
src={previewUrl}
alt={description}
title={description}
lang={lang}
style={{ objectPosition: `${x}% ${y}%` }}
onLoad={handleImageLoad}
/>
);
} else if (['video', 'gifv'].includes(type)) {
const duration = attachment.getIn([
'meta',
'original',
'duration',
]) as number;
thumbnail = (
<div className='media-gallery__gifv'>
<video
className='media-gallery__item-gifv-thumbnail'
aria-label={description}
title={description}
lang={lang}
src={fullUrl}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onLoadedData={handleImageLoad}
autoPlay={autoPlayGif}
playsInline
loop
muted
/>
{type === 'video' && (
<div className='media-gallery__item__overlay media-gallery__item__overlay--corner'>
<Icon id='play' icon={MovieIcon} />
</div>
)}
</div>
);
if (type === 'gifv') {
badges.push(
<span
key='gif'
className='media-gallery__alt__label media-gallery__alt__label--non-interactive'
>
GIF
</span>,
);
} else {
badges.push(
<span
key='video'
className='media-gallery__alt__label media-gallery__alt__label--non-interactive'
>
{formatTime(Math.floor(duration))}
</span>,
);
}
}
return (
<div className='media-gallery__item media-gallery__item--square'>
<Blurhash
hash={blurhash}
className={classNames('media-gallery__preview', {
'media-gallery__preview--hidden': visible && loaded,
})}
dummy={!useBlurhash}
/>
<a
className='media-gallery__item-thumbnail'
href={statusUrl}
onClick={handleClick}
target='_blank'
rel='noopener noreferrer'
>
{thumbnail}
</a>
{badges.length > 0 && (
<div className='media-gallery__item__badges'>{badges}</div>
)}
</div>
);
};

View file

@ -20,7 +20,7 @@ import { expandAccountMediaTimeline } from '../../actions/timelines';
import HeaderContainer from '../account_timeline/containers/header_container'; import HeaderContainer from '../account_timeline/containers/header_container';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
import MediaItem from './components/media_item'; import { MediaItem } from './components/media_item';
const mapStateToProps = (state, { params: { acct, id } }) => { const mapStateToProps = (state, { params: { acct, id } }) => {
const accountId = id || state.getIn(['accounts_map', normalizeForLookup(acct)]); const accountId = id || state.getIn(['accounts_map', normalizeForLookup(acct)]);

View file

@ -21,11 +21,11 @@ const messages = defineMessages({
export const SensitiveButton = () => { export const SensitiveButton = () => {
const intl = useIntl(); const intl = useIntl();
const spoilersAlwaysOn = useAppSelector((state) => state.getIn(['local_settings', 'always_show_spoilers_field'])); const spoilersAlwaysOn = useAppSelector((state) => state.local_settings.getIn(['always_show_spoilers_field']));
const spoilerText = useAppSelector((state) => state.getIn(['compose', 'spoiler_text'])); const spoilerText = useAppSelector((state) => state.compose.get('spoiler_text'));
const sensitive = useAppSelector((state) => state.getIn(['compose', 'sensitive'])); const sensitive = useAppSelector((state) => state.compose.get('sensitive'));
const spoiler = useAppSelector((state) => state.getIn(['compose', 'spoiler'])); const spoiler = useAppSelector((state) => state.compose.get('spoiler'));
const mediaCount = useAppSelector((state) => state.getIn(['compose', 'media_attachments']).size); const mediaCount = useAppSelector((state) => state.compose.get('media_attachments').size);
const disabled = spoilersAlwaysOn ? (spoilerText && spoilerText.length > 0) : spoiler; const disabled = spoilersAlwaysOn ? (spoilerText && spoilerText.length > 0) : spoiler;
const active = sensitive || (spoilersAlwaysOn && spoilerText && spoilerText.length > 0); const active = sensitive || (spoilersAlwaysOn && spoilerText && spoilerText.length > 0);

View file

@ -1,81 +0,0 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import spring from 'react-motion/lib/spring';
import CloseIcon from '@/material-icons/400-20px/close.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import { undoUploadCompose, initMediaEditModal } from 'flavours/glitch/actions/compose';
import { Blurhash } from 'flavours/glitch/components/blurhash';
import { Icon } from 'flavours/glitch/components/icon';
import Motion from 'flavours/glitch/features/ui/util/optional_motion';
export const Upload = ({ id, onDragStart, onDragEnter, onDragEnd }) => {
const dispatch = useDispatch();
const media = useSelector(state => state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id));
const sensitive = useSelector(state => state.getIn(['compose', 'sensitive']));
const handleUndoClick = useCallback(() => {
dispatch(undoUploadCompose(id));
}, [dispatch, id]);
const handleFocalPointClick = useCallback(() => {
dispatch(initMediaEditModal(id));
}, [dispatch, id]);
const handleDragStart = useCallback(() => {
onDragStart(id);
}, [onDragStart, id]);
const handleDragEnter = useCallback(() => {
onDragEnter(id);
}, [onDragEnter, id]);
if (!media) {
return null;
}
const focusX = media.getIn(['meta', 'focus', 'x']);
const focusY = media.getIn(['meta', 'focus', 'y']);
const x = ((focusX / 2) + .5) * 100;
const y = ((focusY / -2) + .5) * 100;
const missingDescription = (media.get('description') || '').length === 0;
return (
<div className='compose-form__upload' draggable onDragStart={handleDragStart} onDragEnter={handleDragEnter} onDragEnd={onDragEnd}>
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
{({ scale }) => (
<div className='compose-form__upload__thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: !sensitive ? `url(${media.get('preview_url')})` : null, backgroundPosition: `${x}% ${y}%` }}>
{sensitive && <Blurhash
hash={media.get('blurhash')}
className='compose-form__upload__preview'
/>}
<div className='compose-form__upload__actions'>
<button type='button' className='icon-button compose-form__upload__delete' onClick={handleUndoClick}><Icon icon={CloseIcon} /></button>
<button type='button' className='icon-button' onClick={handleFocalPointClick}><Icon icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
</div>
<div className='compose-form__upload__warning'>
<button type='button' className={classNames('icon-button', { active: missingDescription })} onClick={handleFocalPointClick}>{missingDescription && <Icon icon={WarningIcon} />} ALT</button>
</div>
</div>
)}
</Motion>
</div>
);
};
Upload.propTypes = {
id: PropTypes.string,
onDragEnter: PropTypes.func,
onDragStart: PropTypes.func,
onDragEnd: PropTypes.func,
};

View file

@ -0,0 +1,130 @@
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import CloseIcon from '@/material-icons/400-20px/close.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import {
undoUploadCompose,
initMediaEditModal,
} from 'flavours/glitch/actions/compose';
import { Blurhash } from 'flavours/glitch/components/blurhash';
import { Icon } from 'flavours/glitch/components/icon';
import type { MediaAttachment } from 'flavours/glitch/models/media_attachment';
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
export const Upload: React.FC<{
id: string;
dragging?: boolean;
overlay?: boolean;
tall?: boolean;
wide?: boolean;
}> = ({ id, dragging, overlay, tall, wide }) => {
const dispatch = useAppDispatch();
const media = useAppSelector(
(state) =>
state.compose // eslint-disable-line @typescript-eslint/no-unsafe-call
.get('media_attachments') // eslint-disable-line @typescript-eslint/no-unsafe-member-access
.find((item: MediaAttachment) => item.get('id') === id) as // eslint-disable-line @typescript-eslint/no-unsafe-member-access
| MediaAttachment
| undefined,
);
const sensitive = useAppSelector(
(state) => state.compose.get('sensitive') as boolean, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const handleUndoClick = useCallback(() => {
dispatch(undoUploadCompose(id));
}, [dispatch, id]);
const handleFocalPointClick = useCallback(() => {
dispatch(initMediaEditModal(id));
}, [dispatch, id]);
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id });
if (!media) {
return null;
}
const focusX = media.getIn(['meta', 'focus', 'x']) as number;
const focusY = media.getIn(['meta', 'focus', 'y']) as number;
const x = (focusX / 2 + 0.5) * 100;
const y = (focusY / -2 + 0.5) * 100;
const missingDescription =
((media.get('description') as string | undefined) ?? '').length === 0;
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div
className={classNames('compose-form__upload media-gallery__item', {
dragging,
overlay,
'media-gallery__item--tall': tall,
'media-gallery__item--wide': wide,
})}
ref={setNodeRef}
style={style}
{...attributes}
{...listeners}
>
<div
className='compose-form__upload__thumbnail'
style={{
backgroundImage: !sensitive
? `url(${media.get('preview_url') as string})`
: undefined,
backgroundPosition: `${x}% ${y}%`,
}}
>
{sensitive && (
<Blurhash
hash={media.get('blurhash') as string}
className='compose-form__upload__preview'
/>
)}
<div className='compose-form__upload__actions'>
<button
type='button'
className='icon-button compose-form__upload__delete'
onClick={handleUndoClick}
>
<Icon id='close' icon={CloseIcon} />
</button>
<button
type='button'
className='icon-button'
onClick={handleFocalPointClick}
>
<Icon id='edit' icon={EditIcon} />{' '}
<FormattedMessage id='upload_form.edit' defaultMessage='Edit' />
</button>
</div>
<div className='compose-form__upload__warning'>
<button
type='button'
className={classNames('icon-button', {
active: missingDescription,
})}
onClick={handleFocalPointClick}
>
{missingDescription && <Icon id='warning' icon={WarningIcon} />} ALT
</button>
</div>
</div>
</div>
);
};

View file

@ -1,56 +0,0 @@
import { useRef, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { changeMediaOrder } from 'flavours/glitch/actions/compose';
import { SensitiveButton } from './sensitive_button';
import { Upload } from './upload';
import { UploadProgress } from './upload_progress';
export const UploadForm = () => {
const dispatch = useDispatch();
const mediaIds = useSelector(state => state.getIn(['compose', 'media_attachments']).map(item => item.get('id')));
const active = useSelector(state => state.getIn(['compose', 'is_uploading']));
const progress = useSelector(state => state.getIn(['compose', 'progress']));
const isProcessing = useSelector(state => state.getIn(['compose', 'is_processing']));
const dragItem = useRef();
const dragOverItem = useRef();
const handleDragStart = useCallback(id => {
dragItem.current = id;
}, [dragItem]);
const handleDragEnter = useCallback(id => {
dragOverItem.current = id;
}, [dragOverItem]);
const handleDragEnd = useCallback(() => {
dispatch(changeMediaOrder(dragItem.current, dragOverItem.current));
dragItem.current = null;
dragOverItem.current = null;
}, [dispatch, dragItem, dragOverItem]);
return (
<>
<UploadProgress active={active} progress={progress} isProcessing={isProcessing} />
{mediaIds.size > 0 && (
<div className='compose-form__uploads'>
{mediaIds.map(id => (
<Upload
key={id}
id={id}
onDragStart={handleDragStart}
onDragEnter={handleDragEnter}
onDragEnd={handleDragEnd}
/>
))}
</div>
)}
{!mediaIds.isEmpty() && <SensitiveButton />}
</>
);
};

View file

@ -0,0 +1,188 @@
import { useState, useCallback, useMemo } from 'react';
import { useIntl, defineMessages } from 'react-intl';
import type { List } from 'immutable';
import type {
DragStartEvent,
DragEndEvent,
UniqueIdentifier,
Announcements,
ScreenReaderInstructions,
} from '@dnd-kit/core';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
DragOverlay,
} from '@dnd-kit/core';
import {
SortableContext,
sortableKeyboardCoordinates,
rectSortingStrategy,
} from '@dnd-kit/sortable';
import { changeMediaOrder } from 'flavours/glitch/actions/compose';
import type { MediaAttachment } from 'flavours/glitch/models/media_attachment';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
import { SensitiveButton } from './sensitive_button';
import { Upload } from './upload';
import { UploadProgress } from './upload_progress';
const messages = defineMessages({
screenReaderInstructions: {
id: 'upload_form.drag_and_drop.instructions',
defaultMessage:
'To pick up a media attachment, press space or enter. While dragging, use the arrow keys to move the media attachment in any given direction. Press space or enter again to drop the media attachment in its new position, or press escape to cancel.',
},
onDragStart: {
id: 'upload_form.drag_and_drop.on_drag_start',
defaultMessage: 'Picked up media attachment {item}.',
},
onDragOver: {
id: 'upload_form.drag_and_drop.on_drag_over',
defaultMessage: 'Media attachment {item} was moved.',
},
onDragEnd: {
id: 'upload_form.drag_and_drop.on_drag_end',
defaultMessage: 'Media attachment {item} was dropped.',
},
onDragCancel: {
id: 'upload_form.drag_and_drop.on_drag_cancel',
defaultMessage:
'Dragging was cancelled. Media attachment {item} was dropped.',
},
});
export const UploadForm: React.FC = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const mediaIds = useAppSelector(
(state) =>
state.compose // eslint-disable-line @typescript-eslint/no-unsafe-call
.get('media_attachments') // eslint-disable-line @typescript-eslint/no-unsafe-member-access
.map((item: MediaAttachment) => item.get('id')) as List<string>, // eslint-disable-line @typescript-eslint/no-unsafe-member-access
);
const active = useAppSelector(
(state) => state.compose.get('is_uploading') as boolean, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const progress = useAppSelector(
(state) => state.compose.get('progress') as number, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const isProcessing = useAppSelector(
(state) => state.compose.get('is_processing') as boolean, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const [activeId, setActiveId] = useState<UniqueIdentifier | null>(null);
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 5,
},
}),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
);
const handleDragStart = useCallback(
(e: DragStartEvent) => {
const { active } = e;
setActiveId(active.id);
},
[setActiveId],
);
const handleDragEnd = useCallback(
(e: DragEndEvent) => {
const { active, over } = e;
if (over && active.id !== over.id) {
dispatch(changeMediaOrder(active.id, over.id));
}
setActiveId(null);
},
[dispatch, setActiveId],
);
const accessibility: {
screenReaderInstructions: ScreenReaderInstructions;
announcements: Announcements;
} = useMemo(
() => ({
screenReaderInstructions: {
draggable: intl.formatMessage(messages.screenReaderInstructions),
},
announcements: {
onDragStart({ active }) {
return intl.formatMessage(messages.onDragStart, { item: active.id });
},
onDragOver({ active }) {
return intl.formatMessage(messages.onDragOver, { item: active.id });
},
onDragEnd({ active }) {
return intl.formatMessage(messages.onDragEnd, { item: active.id });
},
onDragCancel({ active }) {
return intl.formatMessage(messages.onDragCancel, { item: active.id });
},
},
}),
[intl],
);
return (
<>
<UploadProgress
active={active}
progress={progress}
isProcessing={isProcessing}
/>
{mediaIds.size > 0 && (
<div
className={`compose-form__uploads media-gallery media-gallery--layout-${mediaIds.size}`}
>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
accessibility={accessibility}
>
<SortableContext
items={mediaIds.toArray()}
strategy={rectSortingStrategy}
>
{mediaIds.map((id, idx) => (
<Upload
key={id}
id={id}
dragging={id === activeId}
tall={mediaIds.size < 3 || (mediaIds.size === 3 && idx === 0)}
wide={mediaIds.size === 1}
/>
))}
</SortableContext>
<DragOverlay>
{activeId ? <Upload id={activeId as string} overlay /> : null}
</DragOverlay>
</DndContext>
</div>
)}
{!mediaIds.isEmpty() && <SensitiveButton />}
</>
);
};

View file

@ -195,6 +195,28 @@ export const DetailedStatus: React.FC<{
) )
) { ) {
media.push(<AttachmentList media={status.get('media_attachments')} />); media.push(<AttachmentList media={status.get('media_attachments')} />);
} else if (
['image', 'gifv'].includes(
status.getIn(['media_attachments', 0, 'type']) as string,
) ||
status.get('media_attachments').size > 1
) {
media.push(
<MediaGallery
standalone
sensitive={status.get('sensitive')}
media={status.get('media_attachments')}
lang={language}
height={300}
letterbox={letterboxMedia}
fullwidth={fullwidthMedia}
hidden={!expanded}
onOpenMedia={onOpenMedia}
visible={showMedia}
onToggleVisibility={onToggleMediaVisibility}
/>,
);
mediaIcons.push('picture-o');
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
const attachment = status.getIn(['media_attachments', 0]); const attachment = status.getIn(['media_attachments', 0]);
const description = const description =
@ -249,23 +271,6 @@ export const DetailedStatus: React.FC<{
/>, />,
); );
mediaIcons.push('video-camera'); mediaIcons.push('video-camera');
} else {
media.push(
<MediaGallery
standalone
sensitive={status.get('sensitive')}
media={status.get('media_attachments')}
lang={language}
height={300}
letterbox={letterboxMedia}
fullwidth={fullwidthMedia}
hidden={!expanded}
onOpenMedia={onOpenMedia}
visible={showMedia}
onToggleVisibility={onToggleMediaVisibility}
/>,
);
mediaIcons.push('picture-o');
} }
} else if (status.get('spoiler_text').length === 0) { } else if (status.get('spoiler_text').length === 0) {
media.push( media.push(

View file

@ -99,7 +99,7 @@ export const BlockModal = ({ accountId, acct }) => {
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' /> <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
</button> </button>
<Button onClick={handleClick} autoFocus> <Button onClick={handleClick} dangerous autoFocus>
<FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' /> <FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' />
</Button> </Button>
</div> </div>

View file

@ -3,11 +3,11 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { useRouteMatch, NavLink } from 'react-router-dom'; import { useRouteMatch, NavLink } from 'react-router-dom';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, onClick, href, method, badge, transparent, ...other }) => { const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, onClick, href, method, badge, transparent, optional, ...other }) => {
const match = useRouteMatch(to); const match = useRouteMatch(to);
const className = classNames('column-link', { 'column-link--transparent': transparent }); const className = classNames('column-link', { 'column-link--transparent': transparent, 'column-link--optional': optional });
const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null; const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon; const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon;
const activeIconElement = activeIcon ?? (activeIconComponent ? <Icon id={icon} icon={activeIconComponent} className='column-link__icon' /> : iconElement); const activeIconElement = activeIcon ?? (activeIconComponent ? <Icon id={icon} icon={activeIconComponent} className='column-link__icon' /> : iconElement);
@ -58,6 +58,7 @@ ColumnLink.propTypes = {
method: PropTypes.string, method: PropTypes.string,
badge: PropTypes.node, badge: PropTypes.node,
transparent: PropTypes.bool, transparent: PropTypes.bool,
optional: PropTypes.bool,
}; };
export default ColumnLink; export default ColumnLink;

View file

@ -1,106 +0,0 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react';
import HistoryIcon from '@/material-icons/400-24px/history.svg?react';
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { blockAccount } from 'flavours/glitch/actions/accounts';
import { blockDomain } from 'flavours/glitch/actions/domain_blocks';
import { closeModal } from 'flavours/glitch/actions/modal';
import { Button } from 'flavours/glitch/components/button';
import { Icon } from 'flavours/glitch/components/icon';
export const DomainBlockModal = ({ domain, accountId, acct }) => {
const dispatch = useDispatch();
const handleClick = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockDomain(domain));
}, [dispatch, domain]);
const handleSecondaryClick = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockAccount(accountId));
}, [dispatch, accountId]);
const handleCancel = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
}, [dispatch]);
return (
<div className='modal-root__modal safety-action-modal'>
<div className='safety-action-modal__top'>
<div className='safety-action-modal__header'>
<div className='safety-action-modal__header__icon'>
<Icon icon={DomainDisabledIcon} />
</div>
<div>
<h1><FormattedMessage id='domain_block_modal.title' defaultMessage='Block domain?' /></h1>
<div>{domain}</div>
</div>
</div>
<div className='safety-action-modal__bullet-points'>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={CampaignIcon} /></div>
<div><FormattedMessage id='domain_block_modal.they_wont_know' defaultMessage="They won't know they've been blocked." /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={VisibilityOffIcon} /></div>
<div><FormattedMessage id='domain_block_modal.you_wont_see_posts' defaultMessage="You won't see posts or notifications from users on this server." /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={PersonRemoveIcon} /></div>
<div><FormattedMessage id='domain_block_modal.you_will_lose_followers' defaultMessage='All your followers from this server will be removed.' /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={ReplyIcon} /></div>
<div><FormattedMessage id='domain_block_modal.they_cant_follow' defaultMessage='Nobody from this server can follow you.' /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={HistoryIcon} /></div>
<div><FormattedMessage id='domain_block_modal.they_can_interact_with_old_posts' defaultMessage='People from this server can interact with your old posts.' /></div>
</div>
</div>
</div>
<div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'>
<Button onClick={handleSecondaryClick} secondary>
<FormattedMessage id='domain_block_modal.block_account_instead' defaultMessage='Block @{name} instead' values={{ name: acct.split('@')[0] }} />
</Button>
<div className='spacer' />
<button onClick={handleCancel} className='link-button'>
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
</button>
<Button onClick={handleClick} autoFocus>
<FormattedMessage id='domain_block_modal.block' defaultMessage='Block server' />
</Button>
</div>
</div>
</div>
);
};
DomainBlockModal.propTypes = {
domain: PropTypes.string.isRequired,
accountId: PropTypes.string.isRequired,
acct: PropTypes.string.isRequired,
};
export default DomainBlockModal;

View file

@ -0,0 +1,223 @@
import { useCallback, useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react';
import HistoryIcon from '@/material-icons/400-24px/history.svg?react';
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { blockAccount } from 'flavours/glitch/actions/accounts';
import { blockDomain } from 'flavours/glitch/actions/domain_blocks';
import { closeModal } from 'flavours/glitch/actions/modal';
import { apiRequest } from 'flavours/glitch/api';
import { Button } from 'flavours/glitch/components/button';
import { Icon } from 'flavours/glitch/components/icon';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import { ShortNumber } from 'flavours/glitch/components/short_number';
import { useAppDispatch } from 'flavours/glitch/store';
interface DomainBlockPreviewResponse {
following_count: number;
followers_count: number;
}
export const DomainBlockModal: React.FC<{
domain: string;
accountId: string;
acct: string;
}> = ({ domain, accountId, acct }) => {
const dispatch = useAppDispatch();
const [loading, setLoading] = useState(true);
const [preview, setPreview] = useState<
DomainBlockPreviewResponse | 'error' | null
>(null);
const handleClick = useCallback(() => {
if (loading) {
return; // Prevent destructive action before the preview finishes loading or times out
}
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockDomain(domain));
}, [dispatch, loading, domain]);
const handleSecondaryClick = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockAccount(accountId));
}, [dispatch, accountId]);
const handleCancel = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
}, [dispatch]);
useEffect(() => {
setLoading(true);
apiRequest<DomainBlockPreviewResponse>('GET', 'v1/domain_blocks/preview', {
params: { domain },
timeout: 5000,
})
.then((data) => {
setPreview(data);
setLoading(false);
return '';
})
.catch(() => {
setPreview('error');
setLoading(false);
});
}, [setPreview, setLoading, domain]);
return (
<div className='modal-root__modal safety-action-modal' aria-live='polite'>
<div className='safety-action-modal__top'>
<div className='safety-action-modal__header'>
<div className='safety-action-modal__header__icon'>
<Icon id='' icon={DomainDisabledIcon} />
</div>
<div>
<h1>
<FormattedMessage
id='domain_block_modal.title'
defaultMessage='Block domain?'
/>
</h1>
<div>{domain}</div>
</div>
</div>
<div className='safety-action-modal__bullet-points'>
{preview &&
preview !== 'error' &&
preview.followers_count + preview.following_count > 0 && (
<div>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={PersonRemoveIcon} />
</div>
<div>
<strong>
<FormattedMessage
id='domain_block_modal.you_will_lose_num_followers'
defaultMessage='You will lose {followersCount, plural, one {{followersCountDisplay} follower} other {{followersCountDisplay} followers}} and {followingCount, plural, one {{followingCountDisplay} person you follow} other {{followingCountDisplay} people you follow}}.'
values={{
followersCount: preview.followers_count,
followersCountDisplay: (
<ShortNumber value={preview.followers_count} />
),
followingCount: preview.following_count,
followingCountDisplay: (
<ShortNumber value={preview.following_count} />
),
}}
/>
</strong>
</div>
</div>
)}
{preview === 'error' && (
<div>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={PersonRemoveIcon} />
</div>
<div>
<strong>
<FormattedMessage
id='domain_block_modal.you_will_lose_relationships'
defaultMessage='You will lose all followers and people you follow from this server.'
/>
</strong>
</div>
</div>
)}
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={CampaignIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.they_wont_know'
defaultMessage="They won't know they've been blocked."
/>
</div>
</div>
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={VisibilityOffIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.you_wont_see_posts'
defaultMessage="You won't see posts or notifications from users on this server."
/>
</div>
</div>
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={ReplyIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.they_cant_follow'
defaultMessage='Nobody from this server can follow you.'
/>
</div>
</div>
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={HistoryIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.they_can_interact_with_old_posts'
defaultMessage='People from this server can interact with your old posts.'
/>
</div>
</div>
</div>
</div>
<div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'>
<Button onClick={handleSecondaryClick} secondary>
<FormattedMessage
id='domain_block_modal.block_account_instead'
defaultMessage='Block @{name} instead'
values={{ name: acct.split('@')[0] }}
/>
</Button>
<div className='spacer' />
<button onClick={handleCancel} className='link-button'>
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
</button>
<Button onClick={handleClick} dangerous aria-busy={loading}>
{loading ? (
<LoadingIndicator />
) : (
<FormattedMessage
id='domain_block_modal.block'
defaultMessage='Block server'
/>
)}
</Button>
</div>
</div>
</div>
);
};
// eslint-disable-next-line import/no-default-export
export default DomainBlockModal;

View file

@ -122,14 +122,17 @@ class NavigationPanel extends Component {
let banner = undefined; let banner = undefined;
if(transientSingleColumn) if (transientSingleColumn) {
banner = (<div className='switch-to-advanced'> banner = (
{intl.formatMessage(messages.openedInClassicInterface)} <div className='switch-to-advanced'>
{" "} {intl.formatMessage(messages.openedInClassicInterface)}
<a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'> {" "}
{intl.formatMessage(messages.advancedInterface)} <a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'>
</a> {intl.formatMessage(messages.advancedInterface)}
</div>); </a>
</div>
);
}
return ( return (
<div className='navigation-panel'> <div className='navigation-panel'>
@ -139,55 +142,59 @@ class NavigationPanel extends Component {
</div> </div>
} }
{signedIn && ( <div className='navigation-panel__menu'>
<> {signedIn && (
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} /> <>
<NotificationsLink /> <ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} />
<FollowRequestsLink /> <NotificationsLink />
</> <FollowRequestsLink />
)} </>
)}
{trendsEnabled ? ( {trendsEnabled ? (
<ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} /> <ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} />
) : ( ) : (
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} /> <ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
)} )}
{(signedIn || timelinePreview) && ( {(signedIn || timelinePreview) && (
<ColumnLink transparent to='/public/local' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} /> <ColumnLink transparent to='/public/local' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
)} )}
{!signedIn && ( {!signedIn && (
<div className='navigation-panel__sign-in-banner'> <div className='navigation-panel__sign-in-banner'>
<hr />
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> }
</div>
)}
{signedIn && (
<>
<ColumnLink transparent to='/conversations' icon='at' iconComponent={MailIcon} activeIconComponent={MailActiveIcon} text={intl.formatMessage(messages.direct)} />
<ColumnLink transparent to='/bookmarks' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
<ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} />
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
<ListPanel />
<hr />
{!!preferencesLink && <ColumnLink transparent href={preferencesLink} icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} />}
<ColumnLink transparent onClick={onOpenSettings} icon='cogs' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.app_settings)} />
{canManageReports(permissions) && <ColumnLink optional transparent href='/admin/reports' icon='flag' iconComponent={ModerationIcon} text={intl.formatMessage(messages.moderation)} />}
{canViewAdminDashboard(permissions) && <ColumnLink optional transparent href='/admin/dashboard' icon='tachometer' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.administration)} />}
</>
)}
<div className='navigation-panel__legal'>
<hr /> <hr />
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> } <ColumnLink transparent to='/about' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.about)} />
</div> </div>
)}
{signedIn && (
<>
<ColumnLink transparent to='/conversations' icon='at' iconComponent={MailIcon} activeIconComponent={MailActiveIcon} text={intl.formatMessage(messages.direct)} />
<ColumnLink transparent to='/bookmarks' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
<ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} />
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
<ListPanel />
<hr />
{!!preferencesLink && <ColumnLink transparent href={preferencesLink} icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} />}
<ColumnLink transparent onClick={onOpenSettings} icon='cogs' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.app_settings)} />
{canManageReports(permissions) && <ColumnLink transparent href='/admin/reports' icon='flag' iconComponent={ModerationIcon} text={intl.formatMessage(messages.moderation)} />}
{canViewAdminDashboard(permissions) && <ColumnLink transparent href='/admin/dashboard' icon='tachometer' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.administration)} />}
</>
)}
<div className='navigation-panel__legal'>
<hr />
<ColumnLink transparent to='/about' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.about)} />
</div> </div>
<div className='flex-spacer' />
<NavigationPortal /> <NavigationPortal />
</div> </div>
); );

View file

@ -0,0 +1,2 @@
// Temporary until we type it correctly
export type MediaAttachment = Immutable.Map<string, unknown>;

View file

@ -10,3 +10,5 @@ export type Status = Immutable.Map<string, unknown>;
type CardShape = Required<ApiPreviewCardJSON>; type CardShape = Required<ApiPreviewCardJSON>;
export type Card = RecordOf<CardShape>; export type Card = RecordOf<CardShape>;
export type MediaAttachment = Immutable.Map<string, unknown>;

View file

@ -81,6 +81,18 @@
outline: $ui-button-icon-focus-outline; outline: $ui-button-icon-focus-outline;
} }
&--dangerous {
background-color: var(--error-background-color);
color: var(--on-error-color);
&:active,
&:focus,
&:hover {
background-color: var(--error-active-background-color);
transition: none;
}
}
&--destructive { &--destructive {
&:active, &:active,
&:focus, &:focus,
@ -230,6 +242,7 @@
flex: 0 0 auto; flex: 0 0 auto;
a { a {
display: flex;
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
@ -640,19 +653,39 @@ body > [data-popper-placement] {
} }
&__uploads { &__uploads {
display: flex;
gap: 8px;
padding: 0 12px; padding: 0 12px;
flex-wrap: wrap; aspect-ratio: 3/2;
align-self: stretch; }
align-items: flex-start;
align-content: flex-start; .media-gallery {
justify-content: center; gap: 8px;
} }
&__upload { &__upload {
flex: 1 1 0; position: relative;
min-width: calc(50% - 8px); cursor: grab;
&.dragging {
opacity: 0;
}
&.overlay {
height: 100%;
border-radius: 8px;
pointer-events: none;
}
&__drag-handle {
position: absolute;
top: 50%;
inset-inline-start: 0;
transform: translateY(-50%);
color: $white;
background: transparent;
border: 0;
padding: 8px 3px;
cursor: grab;
}
&__actions { &__actions {
display: flex; display: flex;
@ -673,8 +706,7 @@ body > [data-popper-placement] {
&__thumbnail { &__thumbnail {
width: 100%; width: 100%;
height: 144px; height: 100%;
border-radius: 6px;
background-position: center; background-position: center;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -3672,12 +3704,14 @@ $ui-header-logo-wordmark-width: 99px;
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
height: calc(100% - 20px); height: calc(100% - 20px);
overflow-y: auto; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
& > a { &__menu {
flex: 0 0 auto; flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
} }
.logo { .logo {
@ -3688,6 +3722,36 @@ $ui-header-logo-wordmark-width: 99px;
&__logo { &__logo {
margin-bottom: 12px; margin-bottom: 12px;
} }
@media screen and (height <= 710px) {
&__portal {
display: none;
}
}
@media screen and (height <= 765px) {
&__portal .trends__item:nth-child(n + 3) {
display: none;
}
}
@media screen and (height <= 820px) {
&__portal .trends__item:nth-child(n + 4) {
display: none;
}
}
@media screen and (height <= 920px) {
.column-link.column-link--optional {
display: none;
}
}
@media screen and (height <= 1040px) {
.list-panel {
display: none;
}
}
} }
.navigation-panel, .navigation-panel,
@ -4051,22 +4115,6 @@ $ui-header-logo-wordmark-width: 99px;
} }
} }
@media screen and (height <= 810px) {
.trends__item:nth-of-type(3) {
display: none;
}
}
@media screen and (height <= 720px) {
.trends__item:nth-of-type(2) {
display: none;
}
}
@media screen and (height <= 670px) {
display: none;
}
.trends__item { .trends__item {
border-bottom: 0; border-bottom: 0;
padding: 10px; padding: 10px;
@ -6259,6 +6307,7 @@ a.status-card {
.icon { .icon {
width: 24px; width: 24px;
height: 24px; height: 24px;
filter: var(--overlay-icon-shadow);
} }
&:hover, &:hover,
@ -6353,6 +6402,10 @@ a.status-card {
.icon-button { .icon-button {
color: $white; color: $white;
.icon {
filter: var(--overlay-icon-shadow);
}
&:hover, &:hover,
&:focus, &:focus,
&:active { &:active {
@ -6411,6 +6464,7 @@ a.status-card {
.media-modal__page-dot { .media-modal__page-dot {
flex: 0 0 auto; flex: 0 0 auto;
background-color: $white; background-color: $white;
filter: var(--overlay-icon-shadow);
opacity: 0.4; opacity: 0.4;
height: 6px; height: 6px;
width: 6px; width: 6px;
@ -6687,6 +6741,14 @@ a.status-card {
display: flex; display: flex;
gap: 16px; gap: 16px;
align-items: center; align-items: center;
strong {
font-weight: 700;
}
}
&--deemphasized {
color: $secondary-text-color;
} }
&__icon { &__icon {
@ -7432,72 +7494,13 @@ img.modal-warning {
inset-inline-end: 8px; inset-inline-end: 8px;
display: flex; display: flex;
gap: 2px; gap: 2px;
&--layout-2 {
.media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;
}
.media-gallery__item:nth-child(2) {
border-start-start-radius: 0;
border-end-start-radius: 0;
}
}
&--layout-3 {
.media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;
}
.media-gallery__item:nth-child(2) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
.media-gallery__item:nth-child(3) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-start-end-radius: 0;
}
}
&--layout-4 {
.media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;
border-end-start-radius: 0;
}
.media-gallery__item:nth-child(2) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
.media-gallery__item:nth-child(3) {
border-start-start-radius: 0;
border-start-end-radius: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
.media-gallery__item:nth-child(4) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-start-end-radius: 0;
}
}
} }
.media-gallery__alt__label, .media-gallery__alt__label {
.media-gallery__gifv__label { display: block;
display: flex; text-align: center;
align-items: center;
justify-content: center;
color: $white; color: $white;
border: 0;
background: rgba($black, 0.65); background: rgba($black, 0.65);
backdrop-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%); backdrop-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%);
padding: 3px 8px; padding: 3px 8px;
@ -7505,8 +7508,41 @@ img.modal-warning {
font-size: 12px; font-size: 12px;
font-weight: 700; font-weight: 700;
z-index: 1; z-index: 1;
pointer-events: none;
line-height: 20px; line-height: 20px;
cursor: pointer;
pointer-events: auto;
&--non-interactive {
pointer-events: none;
}
}
.media-gallery__alt__popover {
background: rgba($black, 0.65);
backdrop-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%);
border-radius: 4px;
box-shadow: var(--dropdown-shadow);
padding: 16px;
min-width: 16em;
min-height: 2em;
max-width: 22em;
max-height: 30em;
overflow-y: auto;
h4 {
font-size: 15px;
line-height: 20px;
font-weight: 500;
color: $white;
margin-bottom: 8px;
}
p {
font-size: 15px;
line-height: 20px;
color: rgba($white, 0.85);
white-space: pre-line;
}
} }
.attachment-list { .attachment-list {
@ -7580,10 +7616,68 @@ img.modal-warning {
width: 100%; width: 100%;
min-height: 64px; min-height: 64px;
display: grid; display: grid;
grid-template-columns: 50% 50%; grid-template-columns: 1fr 1fr;
grid-template-rows: 50% 50%; grid-template-rows: 1fr 1fr;
gap: 2px; gap: 2px;
&--layout-2 {
& > .media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;
}
& > .media-gallery__item:nth-child(2) {
border-start-start-radius: 0;
border-end-start-radius: 0;
}
}
&--layout-3 {
& > .media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;
}
& > .media-gallery__item:nth-child(2) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
& > .media-gallery__item:nth-child(3) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-start-end-radius: 0;
}
}
&--layout-4 {
& > .media-gallery__item:nth-child(1) {
border-end-end-radius: 0;
border-start-end-radius: 0;
border-end-start-radius: 0;
}
& > .media-gallery__item:nth-child(2) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
& > .media-gallery__item:nth-child(3) {
border-start-start-radius: 0;
border-start-end-radius: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
& > .media-gallery__item:nth-child(4) {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-start-end-radius: 0;
}
}
@include fullwidth-gallery; @include fullwidth-gallery;
} }
@ -7594,6 +7688,9 @@ img.modal-warning {
position: relative; position: relative;
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
outline: 1px solid var(--media-outline-color);
outline-offset: -1px;
z-index: 1;
&--tall { &--tall {
grid-row: span 2; grid-row: span 2;
@ -7610,15 +7707,44 @@ img.modal-warning {
&.letterbox { &.letterbox {
background: $base-shadow-color; background: $base-shadow-color;
} }
&--square {
aspect-ratio: 1;
}
&__overlay {
position: absolute;
top: 0;
inset-inline-start: 0;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 100%;
height: 100%;
pointer-events: none;
padding: 8px;
z-index: 1;
&--corner {
align-items: flex-start;
justify-content: flex-end;
}
.icon {
color: $white;
filter: var(--overlay-icon-shadow);
}
}
} }
.media-gallery__item-thumbnail { .media-gallery__item-thumbnail {
cursor: zoom-in; cursor: pointer;
display: block; display: block;
text-decoration: none; text-decoration: none;
color: $secondary-text-color; color: $secondary-text-color;
position: relative; position: relative;
z-index: 1; z-index: -1;
&, &,
img { img {
@ -7640,7 +7766,7 @@ img.modal-warning {
position: absolute; position: absolute;
top: 0; top: 0;
inset-inline-start: 0; inset-inline-start: 0;
z-index: 0; z-index: -2;
background: $base-overlay-background; background: $base-overlay-background;
&--hidden { &--hidden {
@ -7653,10 +7779,11 @@ img.modal-warning {
overflow: hidden; overflow: hidden;
position: relative; position: relative;
width: 100%; width: 100%;
z-index: -1;
} }
.media-gallery__item-gifv-thumbnail { .media-gallery__item-gifv-thumbnail {
cursor: zoom-in; cursor: pointer;
height: 100%; height: 100%;
width: 100%; width: 100%;
object-fit: contain; object-fit: contain;
@ -7668,13 +7795,6 @@ img.modal-warning {
} }
} }
.media-gallery__item-thumbnail-label {
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
overflow: hidden;
position: absolute;
}
/* End Media Gallery */ /* End Media Gallery */
.detailed, .detailed,
@ -7697,6 +7817,8 @@ img.modal-warning {
border-radius: 8px; border-radius: 8px;
padding-bottom: 44px; padding-bottom: 44px;
width: 100%; width: 100%;
outline: 1px solid var(--media-outline-color);
outline-offset: -1px;
&.editable { &.editable {
border-radius: 0; border-radius: 0;
@ -7753,6 +7875,7 @@ img.modal-warning {
.video-player__controls { .video-player__controls {
padding-top: 10px; padding-top: 10px;
background: transparent; background: transparent;
z-index: 1;
} }
} }
@ -7766,16 +7889,15 @@ img.modal-warning {
color: $white; color: $white;
display: flex; display: flex;
align-items: center; align-items: center;
outline: 1px solid var(--media-outline-color);
outline-offset: -1px;
z-index: 2;
&.editable { &.editable {
border-radius: 0; border-radius: 0;
height: 100% !important; height: 100% !important;
} }
&:focus {
outline: 0;
}
.detailed-status & { .detailed-status & {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -7787,7 +7909,7 @@ img.modal-warning {
display: block; display: block;
max-width: 100vw; max-width: 100vw;
max-height: 80vh; max-height: 80vh;
z-index: 1; z-index: -2;
position: relative; position: relative;
} }
@ -7814,7 +7936,7 @@ img.modal-warning {
&__controls { &__controls {
position: absolute; position: absolute;
direction: ltr; direction: ltr;
z-index: 2; z-index: -1;
bottom: 0; bottom: 0;
inset-inline-start: 0; inset-inline-start: 0;
inset-inline-end: 0; inset-inline-end: 0;
@ -8129,26 +8251,16 @@ img.modal-warning {
} }
.account-gallery__container { .account-gallery__container {
display: flex; display: grid;
flex-wrap: wrap; grid-template-columns: 1fr 1fr 1fr;
padding: 4px 2px; gap: 2px;
}
.account-gallery__item { .media-gallery__item {
border: 0; border-radius: 0;
box-sizing: border-box; }
display: block;
position: relative;
border-radius: 4px;
overflow: hidden;
margin: 2px;
&__icons { .load-more {
position: absolute; grid-column: span 3;
top: 50%;
inset-inline-start: 50%;
transform: translate(-50%, -50%);
font-size: 24px;
} }
} }

View file

@ -313,6 +313,10 @@ code {
ul { ul {
columns: 2; columns: 2;
@media screen and (max-width: $mobile-breakpoint) {
columns: 1;
}
} }
} }

View file

@ -56,7 +56,6 @@ table {
@supports not selector(::-webkit-scrollbar) { @supports not selector(::-webkit-scrollbar) {
html { html {
scrollbar-color: $action-button-color var(--background-border-color); scrollbar-color: $action-button-color var(--background-border-color);
scrollbar-width: thin;
} }
} }

View file

@ -355,6 +355,10 @@ a.table-action-link {
@media screen and (max-width: $no-gap-breakpoint) { @media screen and (max-width: $no-gap-breakpoint) {
border-top: 1px solid var(--background-border-color); border-top: 1px solid var(--background-border-color);
} }
&--no-toolbar {
border-top: 1px solid var(--background-border-color);
}
} }
@media screen and (width <= 870px) { @media screen and (width <= 870px) {

View file

@ -117,4 +117,9 @@ $dismiss-overlay-width: 4rem;
--surface-variant-active-background-color: #{lighten($ui-base-color, 4%)}; --surface-variant-active-background-color: #{lighten($ui-base-color, 4%)};
--on-surface-color: #{transparentize($ui-base-color, 0.5)}; --on-surface-color: #{transparentize($ui-base-color, 0.5)};
--avatar-border-radius: 8px; --avatar-border-radius: 8px;
--media-outline-color: #{rgba(#fcf8ff, 0.15)};
--overlay-icon-shadow: drop-shadow(0 0 8px #{rgba($base-shadow-color, 0.25)});
--error-background-color: #{darken($error-red, 16%)};
--error-active-background-color: #{darken($error-red, 12%)};
--on-error-color: #fff;
} }

View file

@ -70,6 +70,7 @@ export async function apiRequest<ApiResponse = unknown>(
args: { args: {
params?: RequestParamsOrData; params?: RequestParamsOrData;
data?: RequestParamsOrData; data?: RequestParamsOrData;
timeout?: number;
} = {}, } = {},
) { ) {
const { data } = await api().request<ApiResponse>({ const { data } = await api().request<ApiResponse>({

View file

@ -0,0 +1,67 @@
import { useState, useCallback, useRef } from 'react';
import { FormattedMessage } from 'react-intl';
import Overlay from 'react-overlays/Overlay';
import type {
OffsetValue,
UsePopperOptions,
} from 'react-overlays/esm/usePopper';
const offset = [0, 4] as OffsetValue;
const popperConfig = { strategy: 'fixed' } as UsePopperOptions;
export const AltTextBadge: React.FC<{
description: string;
}> = ({ description }) => {
const anchorRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false);
const handleClick = useCallback(() => {
setOpen((v) => !v);
}, [setOpen]);
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
return (
<>
<button
ref={anchorRef}
className='media-gallery__alt__label'
onClick={handleClick}
>
ALT
</button>
<Overlay
rootClose
onHide={handleClose}
show={open}
target={anchorRef.current}
placement='top-end'
flip
offset={offset}
popperConfig={popperConfig}
>
{({ props }) => (
<div {...props} className='hover-card-controller'>
<div
className='media-gallery__alt__popover dropdown-animation'
role='tooltip'
>
<h4>
<FormattedMessage
id='alt_text_badge.title'
defaultMessage='Alt text'
/>
</h4>
<p>{description}</p>
</div>
</div>
)}
</Overlay>
</>
);
};

View file

@ -7,6 +7,7 @@ interface BaseProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> { extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
block?: boolean; block?: boolean;
secondary?: boolean; secondary?: boolean;
dangerous?: boolean;
} }
interface PropsChildren extends PropsWithChildren<BaseProps> { interface PropsChildren extends PropsWithChildren<BaseProps> {
@ -26,6 +27,7 @@ export const Button: React.FC<Props> = ({
disabled, disabled,
block, block,
secondary, secondary,
dangerous,
className, className,
title, title,
text, text,
@ -46,6 +48,7 @@ export const Button: React.FC<Props> = ({
className={classNames('button', className, { className={classNames('button', className, {
'button-secondary': secondary, 'button-secondary': secondary,
'button--block': block, 'button--block': block,
'button--dangerous': dangerous,
})} })}
disabled={disabled} disabled={disabled}
onClick={handleClick} onClick={handleClick}

View file

@ -10,7 +10,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { AltTextBadge } from 'mastodon/components/alt_text_badge';
import { Blurhash } from 'mastodon/components/blurhash'; import { Blurhash } from 'mastodon/components/blurhash';
import { formatTime } from 'mastodon/features/video';
import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state'; import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
@ -57,7 +59,7 @@ class Item extends PureComponent {
hoverToPlay () { hoverToPlay () {
const { attachment } = this.props; const { attachment } = this.props;
return !this.getAutoPlay() && attachment.get('type') === 'gifv'; return !this.getAutoPlay() && ['gifv', 'video'].includes(attachment.get('type'));
} }
handleClick = (e) => { handleClick = (e) => {
@ -96,7 +98,7 @@ class Item extends PureComponent {
} }
if (attachment.get('description')?.length > 0) { if (attachment.get('description')?.length > 0) {
badges.push(<span key='alt' className='media-gallery__alt__label'>ALT</span>); badges.push(<AltTextBadge key='alt' description={attachment.get('description')} />);
} }
const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
@ -150,10 +152,15 @@ class Item extends PureComponent {
/> />
</a> </a>
); );
} else if (attachment.get('type') === 'gifv') { } else if (['gifv', 'video'].includes(attachment.get('type'))) {
const autoPlay = this.getAutoPlay(); const autoPlay = this.getAutoPlay();
const duration = attachment.getIn(['meta', 'original', 'duration']);
badges.push(<span key='gif' className='media-gallery__gifv__label'>GIF</span>); if (attachment.get('type') === 'gifv') {
badges.push(<span key='gif' className='media-gallery__alt__label media-gallery__alt__label--non-interactive'>GIF</span>);
} else {
badges.push(<span key='video' className='media-gallery__alt__label media-gallery__alt__label--non-interactive'>{formatTime(Math.floor(duration))}</span>);
}
thumbnail = ( thumbnail = (
<div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}> <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
@ -167,6 +174,7 @@ class Item extends PureComponent {
onClick={this.handleClick} onClick={this.handleClick}
onMouseEnter={this.handleMouseEnter} onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave} onMouseLeave={this.handleMouseLeave}
onLoadedData={this.handleImageLoad}
autoPlay={autoPlay} autoPlay={autoPlay}
playsInline playsInline
loop loop

View file

@ -4,22 +4,22 @@ import AccountNavigation from 'mastodon/features/account/navigation';
import Trends from 'mastodon/features/getting_started/containers/trends_container'; import Trends from 'mastodon/features/getting_started/containers/trends_container';
import { showTrends } from 'mastodon/initial_state'; import { showTrends } from 'mastodon/initial_state';
const DefaultNavigation: React.FC = () => const DefaultNavigation: React.FC = () => (showTrends ? <Trends /> : null);
showTrends ? (
<>
<div className='flex-spacer' />
<Trends />
</>
) : null;
export const NavigationPortal: React.FC = () => ( export const NavigationPortal: React.FC = () => (
<Switch> <div className='navigation-panel__portal'>
<Route path='/@:acct' exact component={AccountNavigation} /> <Switch>
<Route path='/@:acct/tagged/:tagged?' exact component={AccountNavigation} /> <Route path='/@:acct' exact component={AccountNavigation} />
<Route path='/@:acct/with_replies' exact component={AccountNavigation} /> <Route
<Route path='/@:acct/followers' exact component={AccountNavigation} /> path='/@:acct/tagged/:tagged?'
<Route path='/@:acct/following' exact component={AccountNavigation} /> exact
<Route path='/@:acct/media' exact component={AccountNavigation} /> component={AccountNavigation}
<Route component={DefaultNavigation} /> />
</Switch> <Route path='/@:acct/with_replies' exact component={AccountNavigation} />
<Route path='/@:acct/followers' exact component={AccountNavigation} />
<Route path='/@:acct/following' exact component={AccountNavigation} />
<Route path='/@:acct/media' exact component={AccountNavigation} />
<Route component={DefaultNavigation} />
</Switch>
</div>
); );

View file

@ -449,7 +449,25 @@ class Status extends ImmutablePureComponent {
} else if (status.get('media_attachments').size > 0) { } else if (status.get('media_attachments').size > 0) {
const language = status.getIn(['translation', 'language']) || status.get('language'); const language = status.getIn(['translation', 'language']) || status.get('language');
if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { if (['image', 'gifv'].includes(status.getIn(['media_attachments', 0, 'type'])) || status.get('media_attachments').size > 1) {
media = (
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
{Component => (
<Component
media={status.get('media_attachments')}
lang={language}
sensitive={status.get('sensitive')}
height={110}
onOpenMedia={this.handleOpenMedia}
cacheWidth={this.props.cacheMediaWidth}
defaultWidth={this.props.cachedMediaWidth}
visible={this.state.showMedia}
onToggleVisibility={this.handleToggleMediaVisibility}
/>
)}
</Bundle>
);
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
const attachment = status.getIn(['media_attachments', 0]); const attachment = status.getIn(['media_attachments', 0]);
const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
@ -501,24 +519,6 @@ class Status extends ImmutablePureComponent {
)} )}
</Bundle> </Bundle>
); );
} else {
media = (
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
{Component => (
<Component
media={status.get('media_attachments')}
lang={language}
sensitive={status.get('sensitive')}
height={110}
onOpenMedia={this.handleOpenMedia}
cacheWidth={this.props.cacheMediaWidth}
defaultWidth={this.props.cachedMediaWidth}
visible={this.state.showMedia}
onToggleVisibility={this.handleToggleMediaVisibility}
/>
)}
</Bundle>
);
} }
} else if (status.get('spoiler_text').length === 0 && status.get('card')) { } else if (status.get('spoiler_text').length === 0 && status.get('card')) {
media = ( media = (

View file

@ -43,10 +43,7 @@ class AccountNavigation extends PureComponent {
} }
return ( return (
<> <FeaturedTags accountId={accountId} tagged={tagged} />
<div className='flex-spacer' />
<FeaturedTags accountId={accountId} tagged={tagged} />
</>
); );
} }

View file

@ -1,158 +0,0 @@
import PropTypes from 'prop-types';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import AudiotrackIcon from '@/material-icons/400-24px/music_note.svg?react';
import PlayArrowIcon from '@/material-icons/400-24px/play_arrow.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { Blurhash } from 'mastodon/components/blurhash';
import { Icon } from 'mastodon/components/icon';
import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state';
export default class MediaItem extends ImmutablePureComponent {
static propTypes = {
attachment: ImmutablePropTypes.map.isRequired,
displayWidth: PropTypes.number.isRequired,
onOpenMedia: PropTypes.func.isRequired,
};
state = {
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
loaded: false,
};
handleImageLoad = () => {
this.setState({ loaded: true });
};
handleMouseEnter = e => {
if (this.hoverToPlay()) {
e.target.play();
}
};
handleMouseLeave = e => {
if (this.hoverToPlay()) {
e.target.pause();
e.target.currentTime = 0;
}
};
hoverToPlay () {
return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1;
}
handleClick = e => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault();
if (this.state.visible) {
this.props.onOpenMedia(this.props.attachment);
} else {
this.setState({ visible: true });
}
}
};
render () {
const { attachment, displayWidth } = this.props;
const { visible, loaded } = this.state;
const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
const height = width;
const status = attachment.get('status');
const title = status.get('spoiler_text') || attachment.get('description');
let thumbnail, label, icon, content;
if (!visible) {
icon = (
<span className='account-gallery__item__icons'>
<Icon id='eye-slash' icon={VisibilityOffIcon} />
</span>
);
} else {
if (['audio', 'video'].includes(attachment.get('type'))) {
content = (
<img
src={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
alt={attachment.get('description')}
lang={status.get('language')}
onLoad={this.handleImageLoad}
/>
);
if (attachment.get('type') === 'audio') {
label = <Icon id='music' icon={AudiotrackIcon} />;
} else {
label = <Icon id='play' icon={PlayArrowIcon} />;
}
} else if (attachment.get('type') === 'image') {
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
const x = ((focusX / 2) + .5) * 100;
const y = ((focusY / -2) + .5) * 100;
content = (
<img
src={attachment.get('preview_url')}
alt={attachment.get('description')}
lang={status.get('language')}
style={{ objectPosition: `${x}% ${y}%` }}
onLoad={this.handleImageLoad}
/>
);
} else if (attachment.get('type') === 'gifv') {
content = (
<video
className='media-gallery__item-gifv-thumbnail'
aria-label={attachment.get('description')}
title={attachment.get('description')}
lang={status.get('language')}
role='application'
src={attachment.get('url')}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
autoPlay={autoPlayGif}
playsInline
loop
muted
/>
);
label = 'GIF';
}
thumbnail = (
<div className='media-gallery__gifv'>
{content}
{label && (
<div className='media-gallery__item__badges'>
<span className='media-gallery__gifv__label'>{label}</span>
</div>
)}
</div>
);
}
return (
<div className='account-gallery__item' style={{ width, height }}>
<a className='media-gallery__item-thumbnail' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} onClick={this.handleClick} title={title} target='_blank' rel='noopener noreferrer'>
<Blurhash
hash={attachment.get('blurhash')}
className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })}
dummy={!useBlurhash}
/>
{visible ? thumbnail : icon}
</a>
</div>
);
}
}

View file

@ -0,0 +1,200 @@
import { useState, useCallback } from 'react';
import classNames from 'classnames';
import HeadphonesIcon from '@/material-icons/400-24px/headphones-fill.svg?react';
import MovieIcon from '@/material-icons/400-24px/movie-fill.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { AltTextBadge } from 'mastodon/components/alt_text_badge';
import { Blurhash } from 'mastodon/components/blurhash';
import { Icon } from 'mastodon/components/icon';
import { formatTime } from 'mastodon/features/video';
import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state';
import type { Status, MediaAttachment } from 'mastodon/models/status';
export const MediaItem: React.FC<{
attachment: MediaAttachment;
onOpenMedia: (arg0: MediaAttachment) => void;
}> = ({ attachment, onOpenMedia }) => {
const [visible, setVisible] = useState(
(displayMedia !== 'hide_all' &&
!attachment.getIn(['status', 'sensitive'])) ||
displayMedia === 'show_all',
);
const [loaded, setLoaded] = useState(false);
const handleImageLoad = useCallback(() => {
setLoaded(true);
}, [setLoaded]);
const handleMouseEnter = useCallback(
(e: React.MouseEvent<HTMLVideoElement>) => {
if (e.target instanceof HTMLVideoElement) {
void e.target.play();
}
},
[],
);
const handleMouseLeave = useCallback(
(e: React.MouseEvent<HTMLVideoElement>) => {
if (e.target instanceof HTMLVideoElement) {
e.target.pause();
e.target.currentTime = 0;
}
},
[],
);
const handleClick = useCallback(
(e: React.MouseEvent<HTMLAnchorElement>) => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault();
if (visible) {
onOpenMedia(attachment);
} else {
setVisible(true);
}
}
},
[attachment, visible, onOpenMedia, setVisible],
);
const status = attachment.get('status') as Status;
const description = (attachment.getIn(['translation', 'description']) ||
attachment.get('description')) as string | undefined;
const previewUrl = attachment.get('preview_url') as string;
const fullUrl = attachment.get('url') as string;
const avatarUrl = status.getIn(['account', 'avatar_static']) as string;
const lang = status.get('language') as string;
const blurhash = attachment.get('blurhash') as string;
const statusId = status.get('id') as string;
const acct = status.getIn(['account', 'acct']) as string;
const type = attachment.get('type') as string;
let thumbnail;
const badges = [];
if (description && description.length > 0) {
badges.push(<AltTextBadge key='alt' description={description} />);
}
if (!visible) {
thumbnail = (
<div className='media-gallery__item__overlay'>
<Icon id='eye-slash' icon={VisibilityOffIcon} />
</div>
);
} else if (type === 'audio') {
thumbnail = (
<>
<img
src={previewUrl || avatarUrl}
alt={description}
title={description}
lang={lang}
onLoad={handleImageLoad}
/>
<div className='media-gallery__item__overlay media-gallery__item__overlay--corner'>
<Icon id='music' icon={HeadphonesIcon} />
</div>
</>
);
} else if (type === 'image') {
const focusX = (attachment.getIn(['meta', 'focus', 'x']) || 0) as number;
const focusY = (attachment.getIn(['meta', 'focus', 'y']) || 0) as number;
const x = (focusX / 2 + 0.5) * 100;
const y = (focusY / -2 + 0.5) * 100;
thumbnail = (
<img
src={previewUrl}
alt={description}
title={description}
lang={lang}
style={{ objectPosition: `${x}% ${y}%` }}
onLoad={handleImageLoad}
/>
);
} else if (['video', 'gifv'].includes(type)) {
const duration = attachment.getIn([
'meta',
'original',
'duration',
]) as number;
thumbnail = (
<div className='media-gallery__gifv'>
<video
className='media-gallery__item-gifv-thumbnail'
aria-label={description}
title={description}
lang={lang}
src={fullUrl}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onLoadedData={handleImageLoad}
autoPlay={autoPlayGif}
playsInline
loop
muted
/>
{type === 'video' && (
<div className='media-gallery__item__overlay media-gallery__item__overlay--corner'>
<Icon id='play' icon={MovieIcon} />
</div>
)}
</div>
);
if (type === 'gifv') {
badges.push(
<span
key='gif'
className='media-gallery__alt__label media-gallery__alt__label--non-interactive'
>
GIF
</span>,
);
} else {
badges.push(
<span
key='video'
className='media-gallery__alt__label media-gallery__alt__label--non-interactive'
>
{formatTime(Math.floor(duration))}
</span>,
);
}
}
return (
<div className='media-gallery__item media-gallery__item--square'>
<Blurhash
hash={blurhash}
className={classNames('media-gallery__preview', {
'media-gallery__preview--hidden': visible && loaded,
})}
dummy={!useBlurhash}
/>
<a
className='media-gallery__item-thumbnail'
href={`/@${acct}/${statusId}`}
onClick={handleClick}
target='_blank'
rel='noopener noreferrer'
>
{thumbnail}
</a>
{badges.length > 0 && (
<div className='media-gallery__item__badges'>{badges}</div>
)}
</div>
);
};

View file

@ -20,7 +20,7 @@ import { expandAccountMediaTimeline } from '../../actions/timelines';
import HeaderContainer from '../account_timeline/containers/header_container'; import HeaderContainer from '../account_timeline/containers/header_container';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
import MediaItem from './components/media_item'; import { MediaItem } from './components/media_item';
const mapStateToProps = (state, { params: { acct, id } }) => { const mapStateToProps = (state, { params: { acct, id } }) => {
const accountId = id || state.getIn(['accounts_map', normalizeForLookup(acct)]); const accountId = id || state.getIn(['accounts_map', normalizeForLookup(acct)]);

View file

@ -1,81 +0,0 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import spring from 'react-motion/lib/spring';
import CloseIcon from '@/material-icons/400-20px/close.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import { undoUploadCompose, initMediaEditModal } from 'mastodon/actions/compose';
import { Blurhash } from 'mastodon/components/blurhash';
import { Icon } from 'mastodon/components/icon';
import Motion from 'mastodon/features/ui/util/optional_motion';
export const Upload = ({ id, onDragStart, onDragEnter, onDragEnd }) => {
const dispatch = useDispatch();
const media = useSelector(state => state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id));
const sensitive = useSelector(state => state.getIn(['compose', 'spoiler']));
const handleUndoClick = useCallback(() => {
dispatch(undoUploadCompose(id));
}, [dispatch, id]);
const handleFocalPointClick = useCallback(() => {
dispatch(initMediaEditModal(id));
}, [dispatch, id]);
const handleDragStart = useCallback(() => {
onDragStart(id);
}, [onDragStart, id]);
const handleDragEnter = useCallback(() => {
onDragEnter(id);
}, [onDragEnter, id]);
if (!media) {
return null;
}
const focusX = media.getIn(['meta', 'focus', 'x']);
const focusY = media.getIn(['meta', 'focus', 'y']);
const x = ((focusX / 2) + .5) * 100;
const y = ((focusY / -2) + .5) * 100;
const missingDescription = (media.get('description') || '').length === 0;
return (
<div className='compose-form__upload' draggable onDragStart={handleDragStart} onDragEnter={handleDragEnter} onDragEnd={onDragEnd}>
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
{({ scale }) => (
<div className='compose-form__upload__thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: !sensitive ? `url(${media.get('preview_url')})` : null, backgroundPosition: `${x}% ${y}%` }}>
{sensitive && <Blurhash
hash={media.get('blurhash')}
className='compose-form__upload__preview'
/>}
<div className='compose-form__upload__actions'>
<button type='button' className='icon-button compose-form__upload__delete' onClick={handleUndoClick}><Icon icon={CloseIcon} /></button>
<button type='button' className='icon-button' onClick={handleFocalPointClick}><Icon icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
</div>
<div className='compose-form__upload__warning'>
<button type='button' className={classNames('icon-button', { active: missingDescription })} onClick={handleFocalPointClick}>{missingDescription && <Icon icon={WarningIcon} />} ALT</button>
</div>
</div>
)}
</Motion>
</div>
);
};
Upload.propTypes = {
id: PropTypes.string,
onDragEnter: PropTypes.func,
onDragStart: PropTypes.func,
onDragEnd: PropTypes.func,
};

View file

@ -0,0 +1,130 @@
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import CloseIcon from '@/material-icons/400-20px/close.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import {
undoUploadCompose,
initMediaEditModal,
} from 'mastodon/actions/compose';
import { Blurhash } from 'mastodon/components/blurhash';
import { Icon } from 'mastodon/components/icon';
import type { MediaAttachment } from 'mastodon/models/media_attachment';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
export const Upload: React.FC<{
id: string;
dragging?: boolean;
overlay?: boolean;
tall?: boolean;
wide?: boolean;
}> = ({ id, dragging, overlay, tall, wide }) => {
const dispatch = useAppDispatch();
const media = useAppSelector(
(state) =>
state.compose // eslint-disable-line @typescript-eslint/no-unsafe-call
.get('media_attachments') // eslint-disable-line @typescript-eslint/no-unsafe-member-access
.find((item: MediaAttachment) => item.get('id') === id) as // eslint-disable-line @typescript-eslint/no-unsafe-member-access
| MediaAttachment
| undefined,
);
const sensitive = useAppSelector(
(state) => state.compose.get('spoiler') as boolean, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const handleUndoClick = useCallback(() => {
dispatch(undoUploadCompose(id));
}, [dispatch, id]);
const handleFocalPointClick = useCallback(() => {
dispatch(initMediaEditModal(id));
}, [dispatch, id]);
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id });
if (!media) {
return null;
}
const focusX = media.getIn(['meta', 'focus', 'x']) as number;
const focusY = media.getIn(['meta', 'focus', 'y']) as number;
const x = (focusX / 2 + 0.5) * 100;
const y = (focusY / -2 + 0.5) * 100;
const missingDescription =
((media.get('description') as string | undefined) ?? '').length === 0;
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div
className={classNames('compose-form__upload media-gallery__item', {
dragging,
overlay,
'media-gallery__item--tall': tall,
'media-gallery__item--wide': wide,
})}
ref={setNodeRef}
style={style}
{...attributes}
{...listeners}
>
<div
className='compose-form__upload__thumbnail'
style={{
backgroundImage: !sensitive
? `url(${media.get('preview_url') as string})`
: undefined,
backgroundPosition: `${x}% ${y}%`,
}}
>
{sensitive && (
<Blurhash
hash={media.get('blurhash') as string}
className='compose-form__upload__preview'
/>
)}
<div className='compose-form__upload__actions'>
<button
type='button'
className='icon-button compose-form__upload__delete'
onClick={handleUndoClick}
>
<Icon id='close' icon={CloseIcon} />
</button>
<button
type='button'
className='icon-button'
onClick={handleFocalPointClick}
>
<Icon id='edit' icon={EditIcon} />{' '}
<FormattedMessage id='upload_form.edit' defaultMessage='Edit' />
</button>
</div>
<div className='compose-form__upload__warning'>
<button
type='button'
className={classNames('icon-button', {
active: missingDescription,
})}
onClick={handleFocalPointClick}
>
{missingDescription && <Icon id='warning' icon={WarningIcon} />} ALT
</button>
</div>
</div>
</div>
);
};

View file

@ -1,53 +0,0 @@
import { useRef, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { changeMediaOrder } from 'mastodon/actions/compose';
import { Upload } from './upload';
import { UploadProgress } from './upload_progress';
export const UploadForm = () => {
const dispatch = useDispatch();
const mediaIds = useSelector(state => state.getIn(['compose', 'media_attachments']).map(item => item.get('id')));
const active = useSelector(state => state.getIn(['compose', 'is_uploading']));
const progress = useSelector(state => state.getIn(['compose', 'progress']));
const isProcessing = useSelector(state => state.getIn(['compose', 'is_processing']));
const dragItem = useRef();
const dragOverItem = useRef();
const handleDragStart = useCallback(id => {
dragItem.current = id;
}, [dragItem]);
const handleDragEnter = useCallback(id => {
dragOverItem.current = id;
}, [dragOverItem]);
const handleDragEnd = useCallback(() => {
dispatch(changeMediaOrder(dragItem.current, dragOverItem.current));
dragItem.current = null;
dragOverItem.current = null;
}, [dispatch, dragItem, dragOverItem]);
return (
<>
<UploadProgress active={active} progress={progress} isProcessing={isProcessing} />
{mediaIds.size > 0 && (
<div className='compose-form__uploads'>
{mediaIds.map(id => (
<Upload
key={id}
id={id}
onDragStart={handleDragStart}
onDragEnter={handleDragEnter}
onDragEnd={handleDragEnd}
/>
))}
</div>
)}
</>
);
};

View file

@ -0,0 +1,185 @@
import { useState, useCallback, useMemo } from 'react';
import { useIntl, defineMessages } from 'react-intl';
import type { List } from 'immutable';
import type {
DragStartEvent,
DragEndEvent,
UniqueIdentifier,
Announcements,
ScreenReaderInstructions,
} from '@dnd-kit/core';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
DragOverlay,
} from '@dnd-kit/core';
import {
SortableContext,
sortableKeyboardCoordinates,
rectSortingStrategy,
} from '@dnd-kit/sortable';
import { changeMediaOrder } from 'mastodon/actions/compose';
import type { MediaAttachment } from 'mastodon/models/media_attachment';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { Upload } from './upload';
import { UploadProgress } from './upload_progress';
const messages = defineMessages({
screenReaderInstructions: {
id: 'upload_form.drag_and_drop.instructions',
defaultMessage:
'To pick up a media attachment, press space or enter. While dragging, use the arrow keys to move the media attachment in any given direction. Press space or enter again to drop the media attachment in its new position, or press escape to cancel.',
},
onDragStart: {
id: 'upload_form.drag_and_drop.on_drag_start',
defaultMessage: 'Picked up media attachment {item}.',
},
onDragOver: {
id: 'upload_form.drag_and_drop.on_drag_over',
defaultMessage: 'Media attachment {item} was moved.',
},
onDragEnd: {
id: 'upload_form.drag_and_drop.on_drag_end',
defaultMessage: 'Media attachment {item} was dropped.',
},
onDragCancel: {
id: 'upload_form.drag_and_drop.on_drag_cancel',
defaultMessage:
'Dragging was cancelled. Media attachment {item} was dropped.',
},
});
export const UploadForm: React.FC = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const mediaIds = useAppSelector(
(state) =>
state.compose // eslint-disable-line @typescript-eslint/no-unsafe-call
.get('media_attachments') // eslint-disable-line @typescript-eslint/no-unsafe-member-access
.map((item: MediaAttachment) => item.get('id')) as List<string>, // eslint-disable-line @typescript-eslint/no-unsafe-member-access
);
const active = useAppSelector(
(state) => state.compose.get('is_uploading') as boolean, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const progress = useAppSelector(
(state) => state.compose.get('progress') as number, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const isProcessing = useAppSelector(
(state) => state.compose.get('is_processing') as boolean, // eslint-disable-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
);
const [activeId, setActiveId] = useState<UniqueIdentifier | null>(null);
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 5,
},
}),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
);
const handleDragStart = useCallback(
(e: DragStartEvent) => {
const { active } = e;
setActiveId(active.id);
},
[setActiveId],
);
const handleDragEnd = useCallback(
(e: DragEndEvent) => {
const { active, over } = e;
if (over && active.id !== over.id) {
dispatch(changeMediaOrder(active.id, over.id));
}
setActiveId(null);
},
[dispatch, setActiveId],
);
const accessibility: {
screenReaderInstructions: ScreenReaderInstructions;
announcements: Announcements;
} = useMemo(
() => ({
screenReaderInstructions: {
draggable: intl.formatMessage(messages.screenReaderInstructions),
},
announcements: {
onDragStart({ active }) {
return intl.formatMessage(messages.onDragStart, { item: active.id });
},
onDragOver({ active }) {
return intl.formatMessage(messages.onDragOver, { item: active.id });
},
onDragEnd({ active }) {
return intl.formatMessage(messages.onDragEnd, { item: active.id });
},
onDragCancel({ active }) {
return intl.formatMessage(messages.onDragCancel, { item: active.id });
},
},
}),
[intl],
);
return (
<>
<UploadProgress
active={active}
progress={progress}
isProcessing={isProcessing}
/>
{mediaIds.size > 0 && (
<div
className={`compose-form__uploads media-gallery media-gallery--layout-${mediaIds.size}`}
>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
accessibility={accessibility}
>
<SortableContext
items={mediaIds.toArray()}
strategy={rectSortingStrategy}
>
{mediaIds.map((id, idx) => (
<Upload
key={id}
id={id}
dragging={id === activeId}
tall={mediaIds.size < 3 || (mediaIds.size === 3 && idx === 0)}
wide={mediaIds.size === 1}
/>
))}
</SortableContext>
<DragOverlay>
{activeId ? <Upload id={activeId as string} overlay /> : null}
</DragOverlay>
</DndContext>
</div>
)}
</>
);
};

View file

@ -151,7 +151,25 @@ export const DetailedStatus: React.FC<{
if (pictureInPicture.get('inUse')) { if (pictureInPicture.get('inUse')) {
media = <PictureInPicturePlaceholder aspectRatio={attachmentAspectRatio} />; media = <PictureInPicturePlaceholder aspectRatio={attachmentAspectRatio} />;
} else if (status.get('media_attachments').size > 0) { } else if (status.get('media_attachments').size > 0) {
if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { if (
['image', 'gifv'].includes(
status.getIn(['media_attachments', 0, 'type']) as string,
) ||
status.get('media_attachments').size > 1
) {
media = (
<MediaGallery
standalone
sensitive={status.get('sensitive')}
media={status.get('media_attachments')}
lang={language}
height={300}
onOpenMedia={onOpenMedia}
visible={showMedia}
onToggleVisibility={onToggleMediaVisibility}
/>
);
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
const attachment = status.getIn(['media_attachments', 0]); const attachment = status.getIn(['media_attachments', 0]);
const description = const description =
attachment.getIn(['translation', 'description']) || attachment.getIn(['translation', 'description']) ||
@ -200,19 +218,6 @@ export const DetailedStatus: React.FC<{
onToggleVisibility={onToggleMediaVisibility} onToggleVisibility={onToggleMediaVisibility}
/> />
); );
} else {
media = (
<MediaGallery
standalone
sensitive={status.get('sensitive')}
media={status.get('media_attachments')}
lang={language}
height={300}
onOpenMedia={onOpenMedia}
visible={showMedia}
onToggleVisibility={onToggleMediaVisibility}
/>
);
} }
} else if (status.get('spoiler_text').length === 0) { } else if (status.get('spoiler_text').length === 0) {
media = ( media = (

View file

@ -99,7 +99,7 @@ export const BlockModal = ({ accountId, acct }) => {
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' /> <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
</button> </button>
<Button onClick={handleClick} autoFocus> <Button onClick={handleClick} dangerous autoFocus>
<FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' /> <FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' />
</Button> </Button>
</div> </div>

View file

@ -3,11 +3,11 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { useRouteMatch, NavLink } from 'react-router-dom'; import { useRouteMatch, NavLink } from 'react-router-dom';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, href, method, badge, transparent, ...other }) => { const ColumnLink = ({ icon, activeIcon, iconComponent, activeIconComponent, text, to, href, method, badge, transparent, optional, ...other }) => {
const match = useRouteMatch(to); const match = useRouteMatch(to);
const className = classNames('column-link', { 'column-link--transparent': transparent }); const className = classNames('column-link', { 'column-link--transparent': transparent, 'column-link--optional': optional });
const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null; const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon; const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon;
const activeIconElement = activeIcon ?? (activeIconComponent ? <Icon id={icon} icon={activeIconComponent} className='column-link__icon' /> : iconElement); const activeIconElement = activeIcon ?? (activeIconComponent ? <Icon id={icon} icon={activeIconComponent} className='column-link__icon' /> : iconElement);
@ -43,6 +43,7 @@ ColumnLink.propTypes = {
method: PropTypes.string, method: PropTypes.string,
badge: PropTypes.node, badge: PropTypes.node,
transparent: PropTypes.bool, transparent: PropTypes.bool,
optional: PropTypes.bool,
}; };
export default ColumnLink; export default ColumnLink;

View file

@ -1,106 +0,0 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react';
import HistoryIcon from '@/material-icons/400-24px/history.svg?react';
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { blockAccount } from 'mastodon/actions/accounts';
import { blockDomain } from 'mastodon/actions/domain_blocks';
import { closeModal } from 'mastodon/actions/modal';
import { Button } from 'mastodon/components/button';
import { Icon } from 'mastodon/components/icon';
export const DomainBlockModal = ({ domain, accountId, acct }) => {
const dispatch = useDispatch();
const handleClick = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockDomain(domain));
}, [dispatch, domain]);
const handleSecondaryClick = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockAccount(accountId));
}, [dispatch, accountId]);
const handleCancel = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
}, [dispatch]);
return (
<div className='modal-root__modal safety-action-modal'>
<div className='safety-action-modal__top'>
<div className='safety-action-modal__header'>
<div className='safety-action-modal__header__icon'>
<Icon icon={DomainDisabledIcon} />
</div>
<div>
<h1><FormattedMessage id='domain_block_modal.title' defaultMessage='Block domain?' /></h1>
<div>{domain}</div>
</div>
</div>
<div className='safety-action-modal__bullet-points'>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={CampaignIcon} /></div>
<div><FormattedMessage id='domain_block_modal.they_wont_know' defaultMessage="They won't know they've been blocked." /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={VisibilityOffIcon} /></div>
<div><FormattedMessage id='domain_block_modal.you_wont_see_posts' defaultMessage="You won't see posts or notifications from users on this server." /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={PersonRemoveIcon} /></div>
<div><FormattedMessage id='domain_block_modal.you_will_lose_followers' defaultMessage='All your followers from this server will be removed.' /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={ReplyIcon} /></div>
<div><FormattedMessage id='domain_block_modal.they_cant_follow' defaultMessage='Nobody from this server can follow you.' /></div>
</div>
<div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={HistoryIcon} /></div>
<div><FormattedMessage id='domain_block_modal.they_can_interact_with_old_posts' defaultMessage='People from this server can interact with your old posts.' /></div>
</div>
</div>
</div>
<div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'>
<Button onClick={handleSecondaryClick} secondary>
<FormattedMessage id='domain_block_modal.block_account_instead' defaultMessage='Block @{name} instead' values={{ name: acct.split('@')[0] }} />
</Button>
<div className='spacer' />
<button onClick={handleCancel} className='link-button'>
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
</button>
<Button onClick={handleClick} autoFocus>
<FormattedMessage id='domain_block_modal.block' defaultMessage='Block server' />
</Button>
</div>
</div>
</div>
);
};
DomainBlockModal.propTypes = {
domain: PropTypes.string.isRequired,
accountId: PropTypes.string.isRequired,
acct: PropTypes.string.isRequired,
};
export default DomainBlockModal;

View file

@ -0,0 +1,223 @@
import { useCallback, useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react';
import HistoryIcon from '@/material-icons/400-24px/history.svg?react';
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { blockAccount } from 'mastodon/actions/accounts';
import { blockDomain } from 'mastodon/actions/domain_blocks';
import { closeModal } from 'mastodon/actions/modal';
import { apiRequest } from 'mastodon/api';
import { Button } from 'mastodon/components/button';
import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { ShortNumber } from 'mastodon/components/short_number';
import { useAppDispatch } from 'mastodon/store';
interface DomainBlockPreviewResponse {
following_count: number;
followers_count: number;
}
export const DomainBlockModal: React.FC<{
domain: string;
accountId: string;
acct: string;
}> = ({ domain, accountId, acct }) => {
const dispatch = useAppDispatch();
const [loading, setLoading] = useState(true);
const [preview, setPreview] = useState<
DomainBlockPreviewResponse | 'error' | null
>(null);
const handleClick = useCallback(() => {
if (loading) {
return; // Prevent destructive action before the preview finishes loading or times out
}
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockDomain(domain));
}, [dispatch, loading, domain]);
const handleSecondaryClick = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
dispatch(blockAccount(accountId));
}, [dispatch, accountId]);
const handleCancel = useCallback(() => {
dispatch(closeModal({ modalType: undefined, ignoreFocus: false }));
}, [dispatch]);
useEffect(() => {
setLoading(true);
apiRequest<DomainBlockPreviewResponse>('GET', 'v1/domain_blocks/preview', {
params: { domain },
timeout: 5000,
})
.then((data) => {
setPreview(data);
setLoading(false);
return '';
})
.catch(() => {
setPreview('error');
setLoading(false);
});
}, [setPreview, setLoading, domain]);
return (
<div className='modal-root__modal safety-action-modal' aria-live='polite'>
<div className='safety-action-modal__top'>
<div className='safety-action-modal__header'>
<div className='safety-action-modal__header__icon'>
<Icon id='' icon={DomainDisabledIcon} />
</div>
<div>
<h1>
<FormattedMessage
id='domain_block_modal.title'
defaultMessage='Block domain?'
/>
</h1>
<div>{domain}</div>
</div>
</div>
<div className='safety-action-modal__bullet-points'>
{preview &&
preview !== 'error' &&
preview.followers_count + preview.following_count > 0 && (
<div>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={PersonRemoveIcon} />
</div>
<div>
<strong>
<FormattedMessage
id='domain_block_modal.you_will_lose_num_followers'
defaultMessage='You will lose {followersCount, plural, one {{followersCountDisplay} follower} other {{followersCountDisplay} followers}} and {followingCount, plural, one {{followingCountDisplay} person you follow} other {{followingCountDisplay} people you follow}}.'
values={{
followersCount: preview.followers_count,
followersCountDisplay: (
<ShortNumber value={preview.followers_count} />
),
followingCount: preview.following_count,
followingCountDisplay: (
<ShortNumber value={preview.following_count} />
),
}}
/>
</strong>
</div>
</div>
)}
{preview === 'error' && (
<div>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={PersonRemoveIcon} />
</div>
<div>
<strong>
<FormattedMessage
id='domain_block_modal.you_will_lose_relationships'
defaultMessage='You will lose all followers and people you follow from this server.'
/>
</strong>
</div>
</div>
)}
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={CampaignIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.they_wont_know'
defaultMessage="They won't know they've been blocked."
/>
</div>
</div>
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={VisibilityOffIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.you_wont_see_posts'
defaultMessage="You won't see posts or notifications from users on this server."
/>
</div>
</div>
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={ReplyIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.they_cant_follow'
defaultMessage='Nobody from this server can follow you.'
/>
</div>
</div>
<div className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={HistoryIcon} />
</div>
<div>
<FormattedMessage
id='domain_block_modal.they_can_interact_with_old_posts'
defaultMessage='People from this server can interact with your old posts.'
/>
</div>
</div>
</div>
</div>
<div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'>
<Button onClick={handleSecondaryClick} secondary>
<FormattedMessage
id='domain_block_modal.block_account_instead'
defaultMessage='Block @{name} instead'
values={{ name: acct.split('@')[0] }}
/>
</Button>
<div className='spacer' />
<button onClick={handleCancel} className='link-button'>
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
</button>
<Button onClick={handleClick} dangerous aria-busy={loading}>
{loading ? (
<LoadingIndicator />
) : (
<FormattedMessage
id='domain_block_modal.block'
defaultMessage='Block server'
/>
)}
</Button>
</div>
</div>
</div>
);
};
// eslint-disable-next-line import/no-default-export
export default DomainBlockModal;

View file

@ -120,14 +120,17 @@ class NavigationPanel extends Component {
let banner = undefined; let banner = undefined;
if(transientSingleColumn) if (transientSingleColumn) {
banner = (<div className='switch-to-advanced'> banner = (
{intl.formatMessage(messages.openedInClassicInterface)} <div className='switch-to-advanced'>
{" "} {intl.formatMessage(messages.openedInClassicInterface)}
<a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'> {" "}
{intl.formatMessage(messages.advancedInterface)} <a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'>
</a> {intl.formatMessage(messages.advancedInterface)}
</div>); </a>
</div>
);
}
return ( return (
<div className='navigation-panel'> <div className='navigation-panel'>
@ -141,54 +144,58 @@ class NavigationPanel extends Component {
</div> </div>
} }
{signedIn && ( <div className='navigation-panel__menu'>
<> {signedIn && (
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} /> <>
<NotificationsLink /> <ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} activeIconComponent={HomeActiveIcon} text={intl.formatMessage(messages.home)} />
<FollowRequestsLink /> <NotificationsLink />
</> <FollowRequestsLink />
)} </>
)}
{trendsEnabled ? ( {trendsEnabled ? (
<ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} /> <ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} />
) : ( ) : (
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} /> <ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
)} )}
{(signedIn || timelinePreview) && ( {(signedIn || timelinePreview) && (
<ColumnLink transparent to='/public/local' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} /> <ColumnLink transparent to='/public/local' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
)} )}
{!signedIn && ( {!signedIn && (
<div className='navigation-panel__sign-in-banner'> <div className='navigation-panel__sign-in-banner'>
<hr />
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> }
</div>
)}
{signedIn && (
<>
<ColumnLink transparent to='/conversations' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} />
<ColumnLink transparent to='/bookmarks' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
<ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} />
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
<ListPanel />
<hr />
<ColumnLink transparent href='/settings/preferences' icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} />
{canManageReports(permissions) && <ColumnLink optional transparent href='/admin/reports' icon='flag' iconComponent={ModerationIcon} text={intl.formatMessage(messages.moderation)} />}
{canViewAdminDashboard(permissions) && <ColumnLink optional transparent href='/admin/dashboard' icon='tachometer' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.administration)} />}
</>
)}
<div className='navigation-panel__legal'>
<hr /> <hr />
{ disabledAccountId ? <DisabledAccountBanner /> : <SignInBanner /> } <ColumnLink transparent to='/about' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.about)} />
</div> </div>
)}
{signedIn && (
<>
<ColumnLink transparent to='/conversations' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} />
<ColumnLink transparent to='/bookmarks' icon='bookmarks' iconComponent={BookmarksIcon} activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} />
<ColumnLink transparent to='/favourites' icon='star' iconComponent={StarIcon} activeIconComponent={StarActiveIcon} text={intl.formatMessage(messages.favourites)} />
<ColumnLink transparent to='/lists' icon='list-ul' iconComponent={ListAltIcon} activeIconComponent={ListAltActiveIcon} text={intl.formatMessage(messages.lists)} />
<ListPanel />
<hr />
<ColumnLink transparent href='/settings/preferences' icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} />
{canManageReports(permissions) && <ColumnLink transparent href='/admin/reports' icon='flag' iconComponent={ModerationIcon} text={intl.formatMessage(messages.moderation)} />}
{canViewAdminDashboard(permissions) && <ColumnLink transparent href='/admin/dashboard' icon='tachometer' iconComponent={AdministrationIcon} text={intl.formatMessage(messages.administration)} />}
</>
)}
<div className='navigation-panel__legal'>
<hr />
<ColumnLink transparent to='/about' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.about)} />
</div> </div>
<div className='flex-spacer' />
<NavigationPortal /> <NavigationPortal />
</div> </div>
); );

View file

@ -220,7 +220,6 @@
"domain_block_modal.they_cant_follow": "لا أحد من هذا الخادم يمكنه متابعتك.", "domain_block_modal.they_cant_follow": "لا أحد من هذا الخادم يمكنه متابعتك.",
"domain_block_modal.they_wont_know": "لن يَعرف أنه قد تم حظره.", "domain_block_modal.they_wont_know": "لن يَعرف أنه قد تم حظره.",
"domain_block_modal.title": "أتريد حظر النطاق؟", "domain_block_modal.title": "أتريد حظر النطاق؟",
"domain_block_modal.you_will_lose_followers": "سيتم إزالة جميع متابعيك من هذا الخادم.",
"domain_block_modal.you_wont_see_posts": "لن ترى منشورات أو إشعارات من المستخدمين على هذا الخادم.", "domain_block_modal.you_wont_see_posts": "لن ترى منشورات أو إشعارات من المستخدمين على هذا الخادم.",
"domain_pill.activitypub_lets_connect": "يتيح لك التواصل والتفاعل مع الناس ليس فقط على ماستدون، ولكن عبر تطبيقات اجتماعية مختلفة أيضا.", "domain_pill.activitypub_lets_connect": "يتيح لك التواصل والتفاعل مع الناس ليس فقط على ماستدون، ولكن عبر تطبيقات اجتماعية مختلفة أيضا.",
"domain_pill.activitypub_like_language": "إنّ ActivityPub مثل لغة ماستدون التي يتحدث بها مع شبكات اجتماعية أخرى.", "domain_pill.activitypub_like_language": "إنّ ActivityPub مثل لغة ماستدون التي يتحدث بها مع شبكات اجتماعية أخرى.",

View file

@ -219,7 +219,6 @@
"domain_block_modal.they_cant_follow": "Ніхто з гэтага сервера не зможа падпісацца на вас.", "domain_block_modal.they_cant_follow": "Ніхто з гэтага сервера не зможа падпісацца на вас.",
"domain_block_modal.they_wont_know": "Карыстальнік не будзе ведаць пра блакіроўку.", "domain_block_modal.they_wont_know": "Карыстальнік не будзе ведаць пра блакіроўку.",
"domain_block_modal.title": "Заблакіраваць дамен?", "domain_block_modal.title": "Заблакіраваць дамен?",
"domain_block_modal.you_will_lose_followers": "Усе падпісчыкі з гэтага сервера будуць выдаленыя.",
"domain_block_modal.you_wont_see_posts": "Вы не ўбачыце допісаў і апавяшчэнняў ад карыстальнікаў з гэтага сервера.", "domain_block_modal.you_wont_see_posts": "Вы не ўбачыце допісаў і апавяшчэнняў ад карыстальнікаў з гэтага сервера.",
"domain_pill.activitypub_lets_connect": "Ён дазваляе вам узаемадзейнічаць не толькі з карыстальнікамі Mastodon, але і розных іншых сацыяльных платформ.", "domain_pill.activitypub_lets_connect": "Ён дазваляе вам узаемадзейнічаць не толькі з карыстальнікамі Mastodon, але і розных іншых сацыяльных платформ.",
"domain_pill.activitypub_like_language": "ActivityPub — гэта мова, на якой Mastodon размаўляе з іншымі сацыяльнымі сеткамі.", "domain_pill.activitypub_like_language": "ActivityPub — гэта мова, на якой Mastodon размаўляе з іншымі сацыяльнымі сеткамі.",

View file

@ -220,7 +220,6 @@
"domain_block_modal.they_cant_follow": "Никого от този сървър не може да ви последва.", "domain_block_modal.they_cant_follow": "Никого от този сървър не може да ви последва.",
"domain_block_modal.they_wont_know": "Няма да узнаят, че са били блокирани.", "domain_block_modal.they_wont_know": "Няма да узнаят, че са били блокирани.",
"domain_block_modal.title": "Блокирате ли домейн?", "domain_block_modal.title": "Блокирате ли домейн?",
"domain_block_modal.you_will_lose_followers": "Всичките ви последователи от този сървър ще се премахнат.",
"domain_block_modal.you_wont_see_posts": "Няма да виждате публикации или известия от потребителите на този сървър.", "domain_block_modal.you_wont_see_posts": "Няма да виждате публикации или известия от потребителите на този сървър.",
"domain_pill.activitypub_lets_connect": "Позволява ви да се свързвате и взаимодействате с хора не само в Mastodon, но и през различни социални приложения.", "domain_pill.activitypub_lets_connect": "Позволява ви да се свързвате и взаимодействате с хора не само в Mastodon, но и през различни социални приложения.",
"domain_pill.activitypub_like_language": "ActivityPub е като език на Mastodon, говорещ с други социални мрежи.", "domain_pill.activitypub_like_language": "ActivityPub е като език на Mastodon, говорещ с други социални мрежи.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Límit de freqüència", "alert.rate_limited.title": "Límit de freqüència",
"alert.unexpected.message": "S'ha produït un error inesperat.", "alert.unexpected.message": "S'ha produït un error inesperat.",
"alert.unexpected.title": "Vaja!", "alert.unexpected.title": "Vaja!",
"alt_text_badge.title": "Text alternatiu",
"announcement.announcement": "Anunci", "announcement.announcement": "Anunci",
"attachments_list.unprocessed": "(sense processar)", "attachments_list.unprocessed": "(sense processar)",
"audio.hide": "Amaga l'àudio", "audio.hide": "Amaga l'àudio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Ningú d'aquest servidor us pot seguir.", "domain_block_modal.they_cant_follow": "Ningú d'aquest servidor us pot seguir.",
"domain_block_modal.they_wont_know": "No sabran que són blocats.", "domain_block_modal.they_wont_know": "No sabran que són blocats.",
"domain_block_modal.title": "Bloquem el domini?", "domain_block_modal.title": "Bloquem el domini?",
"domain_block_modal.you_will_lose_followers": "S'eliminaran tots els vostres seguidors d'aquest servidor.", "domain_block_modal.you_will_lose_num_followers": "Perdreu {followersCount, plural, one {{followersCountDisplay} seguidor} other {{followersCountDisplay} seguidors}} i {followingCount, plural, one {{followingCountDisplay} persona} other {{followingCountDisplay} persones}} que seguiu.",
"domain_block_modal.you_will_lose_relationships": "Perdreu seguidors i gent a qui seguiu d'aquest servidor.",
"domain_block_modal.you_wont_see_posts": "No veureu ni les publicacions ni les notificacions dels usuaris d'aquest servidor.", "domain_block_modal.you_wont_see_posts": "No veureu ni les publicacions ni les notificacions dels usuaris d'aquest servidor.",
"domain_pill.activitypub_lets_connect": "Us permet connectar i interactuar amb persones a Mastodon i també a d'altres apps socials.", "domain_pill.activitypub_lets_connect": "Us permet connectar i interactuar amb persones a Mastodon i també a d'altres apps socials.",
"domain_pill.activitypub_like_language": "ActivityPub és el llenguatge que Mastodon parla amb altres xarxes socials.", "domain_pill.activitypub_like_language": "ActivityPub és el llenguatge que Mastodon parla amb altres xarxes socials.",

View file

@ -220,7 +220,6 @@
"domain_block_modal.they_cant_follow": "Nikdo z tohoto serveru vás nemůže sledovat.", "domain_block_modal.they_cant_follow": "Nikdo z tohoto serveru vás nemůže sledovat.",
"domain_block_modal.they_wont_know": "Nebude vědět, že je zablokován*a.", "domain_block_modal.they_wont_know": "Nebude vědět, že je zablokován*a.",
"domain_block_modal.title": "Blokovat doménu?", "domain_block_modal.title": "Blokovat doménu?",
"domain_block_modal.you_will_lose_followers": "Všichni vaši sledující z tohoto serveru budou odstraněni.",
"domain_block_modal.you_wont_see_posts": "Neuvidíte příspěvky ani upozornění od uživatelů z tohoto serveru.", "domain_block_modal.you_wont_see_posts": "Neuvidíte příspěvky ani upozornění od uživatelů z tohoto serveru.",
"domain_pill.activitypub_lets_connect": "Umožňuje vám spojit se a komunikovat s lidmi nejen na Mastodonu, ale i s dalšími sociálními aplikacemi.", "domain_pill.activitypub_lets_connect": "Umožňuje vám spojit se a komunikovat s lidmi nejen na Mastodonu, ale i s dalšími sociálními aplikacemi.",
"domain_pill.activitypub_like_language": "ActivityPub je jako jazyk, kterým Mastodon mluví s jinými sociálními sítěmi.", "domain_pill.activitypub_like_language": "ActivityPub je jako jazyk, kterým Mastodon mluví s jinými sociálními sítěmi.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Ni all neb o'r gweinydd hwn eich dilyn.", "domain_block_modal.they_cant_follow": "Ni all neb o'r gweinydd hwn eich dilyn.",
"domain_block_modal.they_wont_know": "Fyddan nhw ddim yn gwybod eu bod wedi cael eu blocio.", "domain_block_modal.they_wont_know": "Fyddan nhw ddim yn gwybod eu bod wedi cael eu blocio.",
"domain_block_modal.title": "Blocio parth?", "domain_block_modal.title": "Blocio parth?",
"domain_block_modal.you_will_lose_followers": "Bydd eich holl ddilynwyr o'r gweinydd hwn yn cael eu tynnu.",
"domain_block_modal.you_wont_see_posts": "Fyddwch chi ddim yn gweld postiadau na hysbysiadau gan ddefnyddwyr ar y gweinydd hwn.", "domain_block_modal.you_wont_see_posts": "Fyddwch chi ddim yn gweld postiadau na hysbysiadau gan ddefnyddwyr ar y gweinydd hwn.",
"domain_pill.activitypub_lets_connect": "Mae'n caniatáu ichi gysylltu a rhyngweithio â phobl nid yn unig ar Mastodon, ond ar draws gwahanol apiau cymdeithasol hefyd.", "domain_pill.activitypub_lets_connect": "Mae'n caniatáu ichi gysylltu a rhyngweithio â phobl nid yn unig ar Mastodon, ond ar draws gwahanol apiau cymdeithasol hefyd.",
"domain_pill.activitypub_like_language": "Mae ActivityPub fel yr iaith y mae Mastodon yn ei siarad â rhwydweithiau cymdeithasol eraill.", "domain_pill.activitypub_like_language": "Mae ActivityPub fel yr iaith y mae Mastodon yn ei siarad â rhwydweithiau cymdeithasol eraill.",
@ -434,6 +433,8 @@
"lightbox.close": "Cau", "lightbox.close": "Cau",
"lightbox.next": "Nesaf", "lightbox.next": "Nesaf",
"lightbox.previous": "Blaenorol", "lightbox.previous": "Blaenorol",
"lightbox.zoom_in": "Chwyddo i faint gwirioneddol",
"lightbox.zoom_out": "Chwyddo i ffitio",
"limited_account_hint.action": "Dangos y proffil beth bynnag", "limited_account_hint.action": "Dangos y proffil beth bynnag",
"limited_account_hint.title": "Mae'r proffil hwn wedi cael ei guddio gan gymedrolwyr {domain}.", "limited_account_hint.title": "Mae'r proffil hwn wedi cael ei guddio gan gymedrolwyr {domain}.",
"link_preview.author": "Gan {name}", "link_preview.author": "Gan {name}",
@ -785,6 +786,7 @@
"status.edit": "Golygu", "status.edit": "Golygu",
"status.edited": "Golygwyd ddiwethaf {date}", "status.edited": "Golygwyd ddiwethaf {date}",
"status.edited_x_times": "Golygwyd {count, plural, one {count} two {count} other {{count} gwaith}}", "status.edited_x_times": "Golygwyd {count, plural, one {count} two {count} other {{count} gwaith}}",
"status.embed": "Cael y cod mewnblannu",
"status.favourite": "Ffafrio", "status.favourite": "Ffafrio",
"status.favourites": "{count, plural, one {ffefryn} other {ffefryn}}", "status.favourites": "{count, plural, one {ffefryn} other {ffefryn}}",
"status.filter": "Hidlo'r postiad hwn", "status.filter": "Hidlo'r postiad hwn",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Hastighedsbegrænset", "alert.rate_limited.title": "Hastighedsbegrænset",
"alert.unexpected.message": "En uventet fejl opstod.", "alert.unexpected.message": "En uventet fejl opstod.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"alt_text_badge.title": "Alt text",
"announcement.announcement": "Bekendtgørelse", "announcement.announcement": "Bekendtgørelse",
"attachments_list.unprocessed": "(ubehandlet)", "attachments_list.unprocessed": "(ubehandlet)",
"audio.hide": "Skjul lyd", "audio.hide": "Skjul lyd",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Ingen fra denne server kan følge dig.", "domain_block_modal.they_cant_follow": "Ingen fra denne server kan følge dig.",
"domain_block_modal.they_wont_know": "Vedkommende ser ikke den aktive blokering.", "domain_block_modal.they_wont_know": "Vedkommende ser ikke den aktive blokering.",
"domain_block_modal.title": "Blokér domæne?", "domain_block_modal.title": "Blokér domæne?",
"domain_block_modal.you_will_lose_followers": "Alle følgerne fra denne server fjernes.", "domain_block_modal.you_will_lose_num_followers": "Man vil miste {followersCount, plural, one {{followersCountDisplay} følger} other {{followersCountDisplay} følgere}} og {followingCount, plural, one {{followingCountDisplay} person, man følger} other {{followingCountDisplay} personer, man følger}}.",
"domain_block_modal.you_will_lose_relationships": "Alle følgere og personer som følges på denne server mistes.",
"domain_block_modal.you_wont_see_posts": "Indlæg eller notifikationer fra brugere på denne server vises ikke.", "domain_block_modal.you_wont_see_posts": "Indlæg eller notifikationer fra brugere på denne server vises ikke.",
"domain_pill.activitypub_lets_connect": "Det muliggør at komme i forbindelse og interagere med folk ikke kun på Mastodon, men også på tværs af forskellige sociale apps.", "domain_pill.activitypub_lets_connect": "Det muliggør at komme i forbindelse og interagere med folk ikke kun på Mastodon, men også på tværs af forskellige sociale apps.",
"domain_pill.activitypub_like_language": "ActivityPub er \"sproget\", Mastodon taler med andre sociale netværk.", "domain_pill.activitypub_like_language": "ActivityPub er \"sproget\", Mastodon taler med andre sociale netværk.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Anfragelimit überschritten", "alert.rate_limited.title": "Anfragelimit überschritten",
"alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.", "alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.",
"alert.unexpected.title": "Oha!", "alert.unexpected.title": "Oha!",
"alt_text_badge.title": "Bildbeschreibung",
"announcement.announcement": "Ankündigung", "announcement.announcement": "Ankündigung",
"attachments_list.unprocessed": "(ausstehend)", "attachments_list.unprocessed": "(ausstehend)",
"audio.hide": "Audio ausblenden", "audio.hide": "Audio ausblenden",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Niemand von diesem Server wird dir folgen können.", "domain_block_modal.they_cant_follow": "Niemand von diesem Server wird dir folgen können.",
"domain_block_modal.they_wont_know": "Es wird nicht erkennbar sein, dass diese Domain blockiert wurde.", "domain_block_modal.they_wont_know": "Es wird nicht erkennbar sein, dass diese Domain blockiert wurde.",
"domain_block_modal.title": "Domain blockieren?", "domain_block_modal.title": "Domain blockieren?",
"domain_block_modal.you_will_lose_followers": "Alle Follower von diesem Server werden entfernt.", "domain_block_modal.you_will_lose_num_followers": "Du wirst {followersCount, plural, one {{followersCountDisplay} Follower} other {{followersCountDisplay} Follower}} verlieren und {followingCount, plural, one {{followingCountDisplay} Profil} other {{followingCountDisplay} Profilen}} entfolgen.",
"domain_block_modal.you_will_lose_relationships": "Du wirst von diesem Server alle Follower und Profile, denen du dort folgst, verlieren.",
"domain_block_modal.you_wont_see_posts": "Du wirst keine Beiträge oder Benachrichtigungen von Profilen auf diesem Server sehen.", "domain_block_modal.you_wont_see_posts": "Du wirst keine Beiträge oder Benachrichtigungen von Profilen auf diesem Server sehen.",
"domain_pill.activitypub_lets_connect": "Somit kannst du dich nicht nur auf Mastodon mit Leuten verbinden und mit ihnen interagieren, sondern über alle sozialen Apps hinweg.", "domain_pill.activitypub_lets_connect": "Somit kannst du dich nicht nur auf Mastodon mit Leuten verbinden und mit ihnen interagieren, sondern über alle sozialen Apps hinweg.",
"domain_pill.activitypub_like_language": "ActivityPub ist sozusagen die Sprache, die Mastodon mit anderen sozialen Netzwerken spricht.", "domain_pill.activitypub_like_language": "ActivityPub ist sozusagen die Sprache, die Mastodon mit anderen sozialen Netzwerken spricht.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Κανείς από αυτόν τον διακομιστή δεν μπορεί να σε ακολουθήσει.", "domain_block_modal.they_cant_follow": "Κανείς από αυτόν τον διακομιστή δεν μπορεί να σε ακολουθήσει.",
"domain_block_modal.they_wont_know": "Δεν θα ξέρουν ότι έχουν αποκλειστεί.", "domain_block_modal.they_wont_know": "Δεν θα ξέρουν ότι έχουν αποκλειστεί.",
"domain_block_modal.title": "Αποκλεισμός τομέα;", "domain_block_modal.title": "Αποκλεισμός τομέα;",
"domain_block_modal.you_will_lose_followers": "Οι ακόλουθοί σου από αυτόν τον διακομιστή θα αφαιρεθούν.",
"domain_block_modal.you_wont_see_posts": "Δεν θα βλέπεις αναρτήσεις ή ειδοποιήσεις από χρήστες σε αυτόν το διακομιστή.", "domain_block_modal.you_wont_see_posts": "Δεν θα βλέπεις αναρτήσεις ή ειδοποιήσεις από χρήστες σε αυτόν το διακομιστή.",
"domain_pill.activitypub_lets_connect": "Σού επιτρέπει να συνδεθείς και να αλληλεπιδράσεις με τους ανθρώπους όχι μόνο στο Mastodon, αλλά και σε διαφορετικές κοινωνικές εφαρμογές.", "domain_pill.activitypub_lets_connect": "Σού επιτρέπει να συνδεθείς και να αλληλεπιδράσεις με τους ανθρώπους όχι μόνο στο Mastodon, αλλά και σε διαφορετικές κοινωνικές εφαρμογές.",
"domain_pill.activitypub_like_language": "Το ActivityPub είναι σαν τη γλώσσα Mastodon μιλάει με άλλα κοινωνικά δίκτυα.", "domain_pill.activitypub_like_language": "Το ActivityPub είναι σαν τη γλώσσα Mastodon μιλάει με άλλα κοινωνικά δίκτυα.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Rate limited", "alert.rate_limited.title": "Rate limited",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"alt_text_badge.title": "Alt text",
"announcement.announcement": "Announcement", "announcement.announcement": "Announcement",
"attachments_list.unprocessed": "(unprocessed)", "attachments_list.unprocessed": "(unprocessed)",
"audio.hide": "Hide audio", "audio.hide": "Hide audio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Nobody from this server can follow you.", "domain_block_modal.they_cant_follow": "Nobody from this server can follow you.",
"domain_block_modal.they_wont_know": "They won't know they've been blocked.", "domain_block_modal.they_wont_know": "They won't know they've been blocked.",
"domain_block_modal.title": "Block domain?", "domain_block_modal.title": "Block domain?",
"domain_block_modal.you_will_lose_followers": "All your followers from this server will be removed.", "domain_block_modal.you_will_lose_num_followers": "You will lose {followersCount, plural, one {{followersCountDisplay} follower} other {{followersCountDisplay} followers}} and {followingCount, plural, one {{followingCountDisplay} person you follow} other {{followingCountDisplay} people you follow}}.",
"domain_block_modal.you_will_lose_relationships": "You will lose all followers and people you follow from this server.",
"domain_block_modal.you_wont_see_posts": "You won't see posts or notifications from users on this server.", "domain_block_modal.you_wont_see_posts": "You won't see posts or notifications from users on this server.",
"domain_pill.activitypub_lets_connect": "It lets you connect and interact with people not just on Mastodon, but across different social apps too.", "domain_pill.activitypub_lets_connect": "It lets you connect and interact with people not just on Mastodon, but across different social apps too.",
"domain_pill.activitypub_like_language": "ActivityPub is like the language Mastodon speaks with other social networks.", "domain_pill.activitypub_like_language": "ActivityPub is like the language Mastodon speaks with other social networks.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Rate limited", "alert.rate_limited.title": "Rate limited",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"alt_text_badge.title": "Alt text",
"announcement.announcement": "Announcement", "announcement.announcement": "Announcement",
"attachments_list.unprocessed": "(unprocessed)", "attachments_list.unprocessed": "(unprocessed)",
"audio.hide": "Hide audio", "audio.hide": "Hide audio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Nobody from this server can follow you.", "domain_block_modal.they_cant_follow": "Nobody from this server can follow you.",
"domain_block_modal.they_wont_know": "They won't know they've been blocked.", "domain_block_modal.they_wont_know": "They won't know they've been blocked.",
"domain_block_modal.title": "Block domain?", "domain_block_modal.title": "Block domain?",
"domain_block_modal.you_will_lose_followers": "All your followers from this server will be removed.", "domain_block_modal.you_will_lose_num_followers": "You will lose {followersCount, plural, one {{followersCountDisplay} follower} other {{followersCountDisplay} followers}} and {followingCount, plural, one {{followingCountDisplay} person you follow} other {{followingCountDisplay} people you follow}}.",
"domain_block_modal.you_will_lose_relationships": "You will lose all followers and people you follow from this server.",
"domain_block_modal.you_wont_see_posts": "You won't see posts or notifications from users on this server.", "domain_block_modal.you_wont_see_posts": "You won't see posts or notifications from users on this server.",
"domain_pill.activitypub_lets_connect": "It lets you connect and interact with people not just on Mastodon, but across different social apps too.", "domain_pill.activitypub_lets_connect": "It lets you connect and interact with people not just on Mastodon, but across different social apps too.",
"domain_pill.activitypub_like_language": "ActivityPub is like the language Mastodon speaks with other social networks.", "domain_pill.activitypub_like_language": "ActivityPub is like the language Mastodon speaks with other social networks.",
@ -850,6 +852,11 @@
"upload_error.poll": "File upload not allowed with polls.", "upload_error.poll": "File upload not allowed with polls.",
"upload_form.audio_description": "Describe for people who are deaf or hard of hearing", "upload_form.audio_description": "Describe for people who are deaf or hard of hearing",
"upload_form.description": "Describe for people who are blind or have low vision", "upload_form.description": "Describe for people who are blind or have low vision",
"upload_form.drag_and_drop.instructions": "To pick up a media attachment, press space or enter. While dragging, use the arrow keys to move the media attachment in any given direction. Press space or enter again to drop the media attachment in its new position, or press escape to cancel.",
"upload_form.drag_and_drop.on_drag_cancel": "Dragging was cancelled. Media attachment {item} was dropped.",
"upload_form.drag_and_drop.on_drag_end": "Media attachment {item} was dropped.",
"upload_form.drag_and_drop.on_drag_over": "Media attachment {item} was moved.",
"upload_form.drag_and_drop.on_drag_start": "Picked up media attachment {item}.",
"upload_form.edit": "Edit", "upload_form.edit": "Edit",
"upload_form.thumbnail": "Change thumbnail", "upload_form.thumbnail": "Change thumbnail",
"upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision", "upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Mesaĝkvante limigita", "alert.rate_limited.title": "Mesaĝkvante limigita",
"alert.unexpected.message": "Neatendita eraro okazis.", "alert.unexpected.message": "Neatendita eraro okazis.",
"alert.unexpected.title": "Aj!", "alert.unexpected.title": "Aj!",
"alt_text_badge.title": "Alt-teksto",
"announcement.announcement": "Anoncoj", "announcement.announcement": "Anoncoj",
"attachments_list.unprocessed": "(neprilaborita)", "attachments_list.unprocessed": "(neprilaborita)",
"audio.hide": "Kaŝi aŭdion", "audio.hide": "Kaŝi aŭdion",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Neniu el ĉi tiu servilo povas sekvi vin.", "domain_block_modal.they_cant_follow": "Neniu el ĉi tiu servilo povas sekvi vin.",
"domain_block_modal.they_wont_know": "Ili ne scios, ke ili estas blokitaj.", "domain_block_modal.they_wont_know": "Ili ne scios, ke ili estas blokitaj.",
"domain_block_modal.title": "Ĉu bloki la domajnon?", "domain_block_modal.title": "Ĉu bloki la domajnon?",
"domain_block_modal.you_will_lose_followers": "Ĉiuj viaj sekvantoj de ĉi tiu servilo estos forigitaj.", "domain_block_modal.you_will_lose_num_followers": "Vi perdos {followersCount, plural, one {{followersCountDisplay} sekvanton} other {{followersCountDisplay} sekvantojn}} kaj {followingCount, plural, one {{followingCountDisplay} homon, kiu vi sekvas} other {{followingCountDisplay} homojn, kiuj vi sekvas}}.",
"domain_block_modal.you_will_lose_relationships": "Vi perdos ĉiujn sekvantojn kaj homojn, kiujn vi sekvas de ĉi tiu servilo.",
"domain_block_modal.you_wont_see_posts": "Vi ne vidos afiŝojn aŭ sciigojn de uzantoj sur ĉi tiu servilo.", "domain_block_modal.you_wont_see_posts": "Vi ne vidos afiŝojn aŭ sciigojn de uzantoj sur ĉi tiu servilo.",
"domain_pill.activitypub_lets_connect": "Ĝi ebligas vin konekti kaj interagi kun homoj ne nur sur Mastodon, sed ankaŭ tra diversaj sociaj apoj.", "domain_pill.activitypub_lets_connect": "Ĝi ebligas vin konekti kaj interagi kun homoj ne nur sur Mastodon, sed ankaŭ tra diversaj sociaj apoj.",
"domain_pill.activitypub_like_language": "ActivityPub estas kiel la lingvo kiun Mastodon parolas kun aliaj sociaj retoj.", "domain_pill.activitypub_like_language": "ActivityPub estas kiel la lingvo kiun Mastodon parolas kun aliaj sociaj retoj.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Acción limitada", "alert.rate_limited.title": "Acción limitada",
"alert.unexpected.message": "Ocurrió un error.", "alert.unexpected.message": "Ocurrió un error.",
"alert.unexpected.title": "¡Epa!", "alert.unexpected.title": "¡Epa!",
"alt_text_badge.title": "Texto alternativo",
"announcement.announcement": "Anuncio", "announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "[sin procesar]", "attachments_list.unprocessed": "[sin procesar]",
"audio.hide": "Ocultar audio", "audio.hide": "Ocultar audio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Nadie de este servidor puede seguirte.", "domain_block_modal.they_cant_follow": "Nadie de este servidor puede seguirte.",
"domain_block_modal.they_wont_know": "No sabrán que fueron bloqueados.", "domain_block_modal.they_wont_know": "No sabrán que fueron bloqueados.",
"domain_block_modal.title": "¿Bloquear dominio?", "domain_block_modal.title": "¿Bloquear dominio?",
"domain_block_modal.you_will_lose_followers": "Se eliminarán todos tus seguidores de este servidor.", "domain_block_modal.you_will_lose_num_followers": "Perderás {followersCount, plural, one {{followersCountDisplay} seguidor} other {{followersCountDisplay} seguidores}} y {followingCount, plural, one {{followingCountDisplay} persona a la que sigues} other {{followingCountDisplay} personas a las que sigues}}.",
"domain_block_modal.you_will_lose_relationships": "Perderás a todos los seguidores y gente a la que sigas de este servidor.",
"domain_block_modal.you_wont_see_posts": "No verás mensajes ni notificaciones de usuarios en este servidor.", "domain_block_modal.you_wont_see_posts": "No verás mensajes ni notificaciones de usuarios en este servidor.",
"domain_pill.activitypub_lets_connect": "Te permite conectar e interactuar con cuentas no solo en Mastodon, sino también a través de diferentes aplicaciones sociales.", "domain_pill.activitypub_lets_connect": "Te permite conectar e interactuar con cuentas no solo en Mastodon, sino también a través de diferentes aplicaciones sociales.",
"domain_pill.activitypub_like_language": "ActivityPub es como el idioma que Mastodon habla con otras redes sociales.", "domain_pill.activitypub_like_language": "ActivityPub es como el idioma que Mastodon habla con otras redes sociales.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Tarifa limitada", "alert.rate_limited.title": "Tarifa limitada",
"alert.unexpected.message": "Hubo un error inesperado.", "alert.unexpected.message": "Hubo un error inesperado.",
"alert.unexpected.title": "¡Ups!", "alert.unexpected.title": "¡Ups!",
"alt_text_badge.title": "Texto alternativo",
"announcement.announcement": "Anuncio", "announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "(sin procesar)", "attachments_list.unprocessed": "(sin procesar)",
"audio.hide": "Ocultar audio", "audio.hide": "Ocultar audio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Nadie de este servidor puede seguirte.", "domain_block_modal.they_cant_follow": "Nadie de este servidor puede seguirte.",
"domain_block_modal.they_wont_know": "No sabrán que han sido bloqueados.", "domain_block_modal.they_wont_know": "No sabrán que han sido bloqueados.",
"domain_block_modal.title": "¿Bloquear dominio?", "domain_block_modal.title": "¿Bloquear dominio?",
"domain_block_modal.you_will_lose_followers": "Todos tus seguidores de este servidor serán eliminados.", "domain_block_modal.you_will_lose_num_followers": "Vas a perder {followersCount, plural, one {{followersCountDisplay} seguidor} other {{followersCountDisplay} seguidores}} y {followingCount, plural, one {{followingCountDisplay} persona a la que sigues} other {{followingCountDisplay} personas a las que sigas}}.",
"domain_block_modal.you_will_lose_relationships": "Perderás todos los seguidores y las personas que sigues de este servidor.",
"domain_block_modal.you_wont_see_posts": "No verás publicaciones ni notificaciones de usuarios en este servidor.", "domain_block_modal.you_wont_see_posts": "No verás publicaciones ni notificaciones de usuarios en este servidor.",
"domain_pill.activitypub_lets_connect": "Te permite conectar e interactuar con personas no sólo en Mastodon, sino también a través de diferentes aplicaciones sociales.", "domain_pill.activitypub_lets_connect": "Te permite conectar e interactuar con personas no sólo en Mastodon, sino también a través de diferentes aplicaciones sociales.",
"domain_pill.activitypub_like_language": "ActivityPub es como el idioma que Mastodon habla con otras redes sociales.", "domain_pill.activitypub_like_language": "ActivityPub es como el idioma que Mastodon habla con otras redes sociales.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Tráfico limitado", "alert.rate_limited.title": "Tráfico limitado",
"alert.unexpected.message": "Hubo un error inesperado.", "alert.unexpected.message": "Hubo un error inesperado.",
"alert.unexpected.title": "¡Ups!", "alert.unexpected.title": "¡Ups!",
"alt_text_badge.title": "Texto alternativo",
"announcement.announcement": "Anuncio", "announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "(sin procesar)", "attachments_list.unprocessed": "(sin procesar)",
"audio.hide": "Ocultar audio", "audio.hide": "Ocultar audio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Nadie de este servidor puede seguirte.", "domain_block_modal.they_cant_follow": "Nadie de este servidor puede seguirte.",
"domain_block_modal.they_wont_know": "No sabrán que han sido bloqueados.", "domain_block_modal.they_wont_know": "No sabrán que han sido bloqueados.",
"domain_block_modal.title": "¿Bloquear dominio?", "domain_block_modal.title": "¿Bloquear dominio?",
"domain_block_modal.you_will_lose_followers": "Se eliminarán todos tus seguidores de este servidor.", "domain_block_modal.you_will_lose_num_followers": "Perderás {followersCount, plural, one {{followersCountDisplay} seguidor} other {{followersCountDisplay} seguidores}} y {followingCount, plural, one {{followingCountDisplay} persona a la que sigues} other {{followingCountDisplay} personas a las que sigues}}.",
"domain_block_modal.you_will_lose_relationships": "Perderás a todos los seguidores y gente a la que sigas de este servidor.",
"domain_block_modal.you_wont_see_posts": "No verás mensajes ni notificaciones de usuarios en este servidor.", "domain_block_modal.you_wont_see_posts": "No verás mensajes ni notificaciones de usuarios en este servidor.",
"domain_pill.activitypub_lets_connect": "Te permite conectar e interactuar con personas no sólo en Mastodon, sino también a través de diferentes aplicaciones sociales.", "domain_pill.activitypub_lets_connect": "Te permite conectar e interactuar con personas no sólo en Mastodon, sino también a través de diferentes aplicaciones sociales.",
"domain_pill.activitypub_like_language": "ActivityPub es como el idioma que Mastodon habla con otras redes sociales.", "domain_pill.activitypub_like_language": "ActivityPub es como el idioma que Mastodon habla con otras redes sociales.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Sellest serverist ei saa keegi sind jälgida.", "domain_block_modal.they_cant_follow": "Sellest serverist ei saa keegi sind jälgida.",
"domain_block_modal.they_wont_know": "Nad ei tea, et nad on blokeeritud.", "domain_block_modal.they_wont_know": "Nad ei tea, et nad on blokeeritud.",
"domain_block_modal.title": "Blokeerida domeen?", "domain_block_modal.title": "Blokeerida domeen?",
"domain_block_modal.you_will_lose_followers": "Kõik sinu sellest serverist pärit jälgijad eemaldatakse.",
"domain_block_modal.you_wont_see_posts": "Sa ei näe selle serveri kasutajate postitusi ega teavitusi.", "domain_block_modal.you_wont_see_posts": "Sa ei näe selle serveri kasutajate postitusi ega teavitusi.",
"domain_pill.activitypub_lets_connect": "See võimaldab sul ühenduda inimestega ja nendega suhelda mitte ainult Mastodonis, vaid ka teistes suhtlusrakendustes.", "domain_pill.activitypub_lets_connect": "See võimaldab sul ühenduda inimestega ja nendega suhelda mitte ainult Mastodonis, vaid ka teistes suhtlusrakendustes.",
"domain_pill.activitypub_like_language": "ActivityPub on nagu keel, mida Mastodon räägib teiste suhtlusvõrgustikega.", "domain_pill.activitypub_like_language": "ActivityPub on nagu keel, mida Mastodon räägib teiste suhtlusvõrgustikega.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Abiadura mugatua", "alert.rate_limited.title": "Abiadura mugatua",
"alert.unexpected.message": "Ustekabeko errore bat gertatu da.", "alert.unexpected.message": "Ustekabeko errore bat gertatu da.",
"alert.unexpected.title": "Ene!", "alert.unexpected.title": "Ene!",
"alt_text_badge.title": "Testu alternatiboa",
"announcement.announcement": "Iragarpena", "announcement.announcement": "Iragarpena",
"attachments_list.unprocessed": "(prozesatu gabe)", "attachments_list.unprocessed": "(prozesatu gabe)",
"audio.hide": "Ezkutatu audioa", "audio.hide": "Ezkutatu audioa",
@ -221,7 +222,6 @@
"domain_block_modal.they_cant_follow": "Zerbitzari honetako inork ezin zaitu jarraitu.", "domain_block_modal.they_cant_follow": "Zerbitzari honetako inork ezin zaitu jarraitu.",
"domain_block_modal.they_wont_know": "Ez dute jakingo blokeatuak izan direnik.", "domain_block_modal.they_wont_know": "Ez dute jakingo blokeatuak izan direnik.",
"domain_block_modal.title": "Domeinua blokeatu nahi duzu?", "domain_block_modal.title": "Domeinua blokeatu nahi duzu?",
"domain_block_modal.you_will_lose_followers": "Zerbitzari honetako jarraitzaile guztiak kenduko dira.",
"domain_block_modal.you_wont_see_posts": "Ez dituzu zerbitzari honetako erabiltzaileen argitalpenik edota jakinarazpenik ikusiko.", "domain_block_modal.you_wont_see_posts": "Ez dituzu zerbitzari honetako erabiltzaileen argitalpenik edota jakinarazpenik ikusiko.",
"domain_pill.activitypub_lets_connect": "Mastodon-en ez ezik, beste sare sozialen aplikazioetako jendearekin konektatzea eta harremanetan jartzea uzten dizu.", "domain_pill.activitypub_lets_connect": "Mastodon-en ez ezik, beste sare sozialen aplikazioetako jendearekin konektatzea eta harremanetan jartzea uzten dizu.",
"domain_pill.activitypub_like_language": "ActivityPub, Mastodon-ek beste sare sozialekin hitz egiteko erabiltzen duen hizkuntza bezalakoxea da.", "domain_pill.activitypub_like_language": "ActivityPub, Mastodon-ek beste sare sozialekin hitz egiteko erabiltzen duen hizkuntza bezalakoxea da.",

View file

@ -85,9 +85,11 @@
"alert.rate_limited.title": "محدودیت تعداد", "alert.rate_limited.title": "محدودیت تعداد",
"alert.unexpected.message": "خطایی غیرمنتظره رخ داد.", "alert.unexpected.message": "خطایی غیرمنتظره رخ داد.",
"alert.unexpected.title": "ای وای!", "alert.unexpected.title": "ای وای!",
"alt_text_badge.title": "متن جایگزین",
"announcement.announcement": "اعلامیه", "announcement.announcement": "اعلامیه",
"attachments_list.unprocessed": "(پردازش نشده)", "attachments_list.unprocessed": "(پردازش نشده)",
"audio.hide": "نهفتن صدا", "audio.hide": "نهفتن صدا",
"block_modal.remote_users_caveat": "ما از کارساز {domain} خواهیم خواست که به تصمیم شما احترام بگذارد. با این حال، تضمینی برای رعایت آن وجود ندارد زیرا برخی کارسازها ممکن است بلوک‌ها را به‌طور متفاوتی مدیریت کنند. فرسته‌های عمومی ممکن است همچنان برای کاربران که وارد نشده قابل مشاهده باشند.",
"block_modal.show_less": "نمایش کم‌تر", "block_modal.show_less": "نمایش کم‌تر",
"block_modal.show_more": "نمایش بیش‌تر", "block_modal.show_more": "نمایش بیش‌تر",
"block_modal.they_cant_mention": "نمی‌توانند نامتان را برده یا پی‌تان بگیرند.", "block_modal.they_cant_mention": "نمی‌توانند نامتان را برده یا پی‌تان بگیرند.",
@ -220,8 +222,10 @@
"domain_block_modal.they_cant_follow": "هیچ‌کسی از این کارساز نمی‌تواند پیتان بگیرد.", "domain_block_modal.they_cant_follow": "هیچ‌کسی از این کارساز نمی‌تواند پیتان بگیرد.",
"domain_block_modal.they_wont_know": "نخواهند دانست که مسدود شده‌اند.", "domain_block_modal.they_wont_know": "نخواهند دانست که مسدود شده‌اند.",
"domain_block_modal.title": "انسداد دامنه؟", "domain_block_modal.title": "انسداد دامنه؟",
"domain_block_modal.you_will_lose_followers": "همهٔ پی‌گیرندگانتان از این کارساز برداشته خواهند شد.", "domain_block_modal.you_will_lose_relationships": "شما تمام پیگیرکنندگان و افرادی که از این کارساز پیگیری می‌کنید را از دست خواهید داد.",
"domain_block_modal.you_wont_see_posts": "فرسته‌ها یا آگاهی‌ها از کاربران روی این کارساز را نخواهید دید.", "domain_block_modal.you_wont_see_posts": "فرسته‌ها یا آگاهی‌ها از کاربران روی این کارساز را نخواهید دید.",
"domain_pill.activitypub_lets_connect": "این به شما اجازه می‌دهد تا نه تنها در ماستودون، بلکه در برنامه‌های اجتماعی مختلف نیز با افراد ارتباط برقرار کرده و تعامل داشته باشید.",
"domain_pill.activitypub_like_language": "ActivityPub مانند زبانی است که ماستودون با دیگر شبکه‌های اجتماعی صحبت می‌کند.",
"domain_pill.server": "کارساز", "domain_pill.server": "کارساز",
"domain_pill.their_handle": "شناسه‌اش:", "domain_pill.their_handle": "شناسه‌اش:",
"domain_pill.their_server": "خانهٔ رقمیش. جایی که همهٔ فرسته‌هایش می‌زیند.", "domain_pill.their_server": "خانهٔ رقمیش. جایی که همهٔ فرسته‌هایش می‌زیند.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Pyyntömäärää rajoitettu", "alert.rate_limited.title": "Pyyntömäärää rajoitettu",
"alert.unexpected.message": "Tapahtui odottamaton virhe.", "alert.unexpected.message": "Tapahtui odottamaton virhe.",
"alert.unexpected.title": "Hups!", "alert.unexpected.title": "Hups!",
"alt_text_badge.title": "Vaihtoehtoinen teksti",
"announcement.announcement": "Tiedote", "announcement.announcement": "Tiedote",
"attachments_list.unprocessed": "(käsittelemätön)", "attachments_list.unprocessed": "(käsittelemätön)",
"audio.hide": "Piilota ääni", "audio.hide": "Piilota ääni",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Kukaan tältä palvelimelta ei voi seurata sinua.", "domain_block_modal.they_cant_follow": "Kukaan tältä palvelimelta ei voi seurata sinua.",
"domain_block_modal.they_wont_know": "Hän ei saa tietää tulleensa estetyksi.", "domain_block_modal.they_wont_know": "Hän ei saa tietää tulleensa estetyksi.",
"domain_block_modal.title": "Estetäänkö verkkotunnus?", "domain_block_modal.title": "Estetäänkö verkkotunnus?",
"domain_block_modal.you_will_lose_followers": "Kaikki seuraajasi tältä palvelimelta poistetaan.", "domain_block_modal.you_will_lose_num_followers": "Menetät {followersCount, plural, one {{followersCountDisplay} seuraajasi} other {{followersCountDisplay} seuraajaasi}} ja {followingCount, plural, one {{followingCountDisplay} seurattusi} other {{followingCountDisplay} seurattuasi}}.",
"domain_block_modal.you_will_lose_relationships": "Menetät kaikki tämän palvelimen seuraajasi ja seurattusi.",
"domain_block_modal.you_wont_see_posts": "Et enää näe julkaisuja etkä ilmoituksia tämän palvelimen käyttäjiltä.", "domain_block_modal.you_wont_see_posts": "Et enää näe julkaisuja etkä ilmoituksia tämän palvelimen käyttäjiltä.",
"domain_pill.activitypub_lets_connect": "Sen avulla voit muodostaa yhteyden ja olla vuorovaikutuksessa ihmisten kanssa, ei vain Mastodonissa vaan myös muissa sosiaalisissa sovelluksissa.", "domain_pill.activitypub_lets_connect": "Sen avulla voit muodostaa yhteyden ja olla vuorovaikutuksessa ihmisten kanssa, ei vain Mastodonissa vaan myös muissa sosiaalisissa sovelluksissa.",
"domain_pill.activitypub_like_language": "ActivityPub on kuin kieli, jota Mastodon puhuu muiden sosiaalisten verkostojen kanssa.", "domain_pill.activitypub_like_language": "ActivityPub on kuin kieli, jota Mastodon puhuu muiden sosiaalisten verkostojen kanssa.",

View file

@ -134,7 +134,6 @@
"dismissable_banner.public_timeline": "Ito ang mga pinakamakailang nakapublikong post mula sa mga taong nasa social web na sinusundan ng mga tao sa {domain}.", "dismissable_banner.public_timeline": "Ito ang mga pinakamakailang nakapublikong post mula sa mga taong nasa social web na sinusundan ng mga tao sa {domain}.",
"domain_block_modal.block": "Harangan ang serbiro", "domain_block_modal.block": "Harangan ang serbiro",
"domain_block_modal.title": "Harangan ang domain?", "domain_block_modal.title": "Harangan ang domain?",
"domain_block_modal.you_will_lose_followers": "Mabubura ang iyong mga tagasunod mula sa serbirong ito.",
"domain_pill.server": "Serbiro", "domain_pill.server": "Serbiro",
"embed.instructions": "I-embed ang post na ito sa iyong pook-sapot sa pamamagitan ng pagsipi ng kodigo sa ilalim.", "embed.instructions": "I-embed ang post na ito sa iyong pook-sapot sa pamamagitan ng pagsipi ng kodigo sa ilalim.",
"embed.preview": "Ito ang magiging itsura:", "embed.preview": "Ito ang magiging itsura:",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Avmarkaður títtleiki", "alert.rate_limited.title": "Avmarkaður títtleiki",
"alert.unexpected.message": "Ein óvæntaður feilur kom fyri.", "alert.unexpected.message": "Ein óvæntaður feilur kom fyri.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"alt_text_badge.title": "Annar tekstur",
"announcement.announcement": "Kunngerð", "announcement.announcement": "Kunngerð",
"attachments_list.unprocessed": "(óviðgjørt)", "attachments_list.unprocessed": "(óviðgjørt)",
"audio.hide": "Fjal ljóð", "audio.hide": "Fjal ljóð",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Eingin frá hesum ambætara kann fylgja tær.", "domain_block_modal.they_cant_follow": "Eingin frá hesum ambætara kann fylgja tær.",
"domain_block_modal.they_wont_know": "Tey vita ikki, at tey eru bannað.", "domain_block_modal.they_wont_know": "Tey vita ikki, at tey eru bannað.",
"domain_block_modal.title": "Banna økisnavni?", "domain_block_modal.title": "Banna økisnavni?",
"domain_block_modal.you_will_lose_followers": "Allir tínir fylgjarar á hesum ambætara hvørva.", "domain_block_modal.you_will_lose_num_followers": "Tú missir {followersCount, plural, one {{followersCountDisplay} fylgjara} other {{followersCountDisplay} fylgjarar}} og {followingCount, plural, one {{followingCountDisplay} persón, sum tú fylgir} other {{followingCountDisplay} persónar, sum tú fylgir}}.",
"domain_block_modal.you_will_lose_relationships": "Tú fer at missa allar fylgjarar og øll tey, tú fylgir á hesum ambætaranum.",
"domain_block_modal.you_wont_see_posts": "Tú sært ongar postar ella boð frá brúkarum á hesum ambætara.", "domain_block_modal.you_wont_see_posts": "Tú sært ongar postar ella boð frá brúkarum á hesum ambætara.",
"domain_pill.activitypub_lets_connect": "Tað letur teg fáa samband og samvirka við fólki ikki bara á Mastodon, men á øðrum sosialum appum eisini.", "domain_pill.activitypub_lets_connect": "Tað letur teg fáa samband og samvirka við fólki ikki bara á Mastodon, men á øðrum sosialum appum eisini.",
"domain_pill.activitypub_like_language": "ActivityPub er málið, sum Mastodon tosar við onnur sosial netverk.", "domain_pill.activitypub_like_language": "ActivityPub er málið, sum Mastodon tosar við onnur sosial netverk.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Personne de ce serveur ne peut vous suivre.", "domain_block_modal.they_cant_follow": "Personne de ce serveur ne peut vous suivre.",
"domain_block_modal.they_wont_know": "Il ne saura pas qu'il a été bloqué.", "domain_block_modal.they_wont_know": "Il ne saura pas qu'il a été bloqué.",
"domain_block_modal.title": "Bloquer le domaine ?", "domain_block_modal.title": "Bloquer le domaine ?",
"domain_block_modal.you_will_lose_followers": "Tous vos abonnés de ce serveur seront supprimés.",
"domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les publications ou les notifications des utilisateurs de ce serveur.", "domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les publications ou les notifications des utilisateurs de ce serveur.",
"domain_pill.activitypub_lets_connect": "Cela vous permet de vous connecter et d'interagir avec les autres non seulement sur Mastodon, mais également sur d'autres applications de réseaux sociaux.", "domain_pill.activitypub_lets_connect": "Cela vous permet de vous connecter et d'interagir avec les autres non seulement sur Mastodon, mais également sur d'autres applications de réseaux sociaux.",
"domain_pill.activitypub_like_language": "ActivityPub est comme une langue que Mastodon utilise pour communiquer avec les autres réseaux sociaux.", "domain_pill.activitypub_like_language": "ActivityPub est comme une langue que Mastodon utilise pour communiquer avec les autres réseaux sociaux.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Personne de ce serveur ne peut vous suivre.", "domain_block_modal.they_cant_follow": "Personne de ce serveur ne peut vous suivre.",
"domain_block_modal.they_wont_know": "Il ne saura pas qu'il a été bloqué.", "domain_block_modal.they_wont_know": "Il ne saura pas qu'il a été bloqué.",
"domain_block_modal.title": "Bloquer le domaine ?", "domain_block_modal.title": "Bloquer le domaine ?",
"domain_block_modal.you_will_lose_followers": "Tous vos abonnés de ce serveur seront supprimés.",
"domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les publications ou les notifications des utilisateurs de ce serveur.", "domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les publications ou les notifications des utilisateurs de ce serveur.",
"domain_pill.activitypub_lets_connect": "Cela vous permet de vous connecter et d'interagir avec les autres non seulement sur Mastodon, mais également sur d'autres applications de réseaux sociaux.", "domain_pill.activitypub_lets_connect": "Cela vous permet de vous connecter et d'interagir avec les autres non seulement sur Mastodon, mais également sur d'autres applications de réseaux sociaux.",
"domain_pill.activitypub_like_language": "ActivityPub est comme une langue que Mastodon utilise pour communiquer avec les autres réseaux sociaux.", "domain_pill.activitypub_like_language": "ActivityPub est comme une langue que Mastodon utilise pour communiquer avec les autres réseaux sociaux.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Net ien op dizze server kin jo folgje.", "domain_block_modal.they_cant_follow": "Net ien op dizze server kin jo folgje.",
"domain_block_modal.they_wont_know": "Se krije net te witten dat se blokkearre wurde.", "domain_block_modal.they_wont_know": "Se krije net te witten dat se blokkearre wurde.",
"domain_block_modal.title": "Domein blokkearje?", "domain_block_modal.title": "Domein blokkearje?",
"domain_block_modal.you_will_lose_followers": "Al jo folgers fan dizze server wurde ûntfolge.",
"domain_block_modal.you_wont_see_posts": "Jo sjogge gjin berjochten of meldingen mear fan brûkers op dizze server.", "domain_block_modal.you_wont_see_posts": "Jo sjogge gjin berjochten of meldingen mear fan brûkers op dizze server.",
"domain_pill.activitypub_lets_connect": "It soarget derfoar dat jo net allinnich mar ferbine en kommunisearje kinne mei minsken op Mastodon, mar ek mei oare sosjale apps.", "domain_pill.activitypub_lets_connect": "It soarget derfoar dat jo net allinnich mar ferbine en kommunisearje kinne mei minsken op Mastodon, mar ek mei oare sosjale apps.",
"domain_pill.activitypub_like_language": "ActivityPub is de taal dyt Mastodon mei oare sosjale netwurken sprekt.", "domain_pill.activitypub_like_language": "ActivityPub is de taal dyt Mastodon mei oare sosjale netwurken sprekt.",

View file

@ -221,7 +221,7 @@
"domain_block_modal.they_cant_follow": "Ní féidir le duine ar bith ón bhfreastalaí seo tú a leanúint.", "domain_block_modal.they_cant_follow": "Ní féidir le duine ar bith ón bhfreastalaí seo tú a leanúint.",
"domain_block_modal.they_wont_know": "Ní bheidh a fhios acu go bhfuil bac orthu.", "domain_block_modal.they_wont_know": "Ní bheidh a fhios acu go bhfuil bac orthu.",
"domain_block_modal.title": "Blocáil fearann?", "domain_block_modal.title": "Blocáil fearann?",
"domain_block_modal.you_will_lose_followers": "Bainfear do leantóirí go léir ón bhfreastalaí seo.", "domain_block_modal.you_will_lose_num_followers": "Caillfidh tú {followersCount, plural, one {{followersCountDisplay} leantóir} two {{followersCountDisplay} leantóirí} few {{followersCountDisplay} leantóirí} many {{followersCountDisplay} leantóirí} other {{followersCountDisplay} leantóirí}} agus {followingCount, plural, one {{followingCountDisplay} duine atá á leanúint agat} two {{followingCountDisplay} daoine atá á leanúint agat} few {{followingCountDisplay} daoine atá á leanúint agat} many {{followingCountDisplay} daoine atá á leanúint agat} other {{followingCountDisplay} daoine atá á leanúint agat}}.",
"domain_block_modal.you_wont_see_posts": "Ní fheicfidh tú postálacha nó fógraí ó úsáideoirí ar an bhfreastalaí seo.", "domain_block_modal.you_wont_see_posts": "Ní fheicfidh tú postálacha nó fógraí ó úsáideoirí ar an bhfreastalaí seo.",
"domain_pill.activitypub_lets_connect": "Ligeann sé duit ceangal agus idirghníomhú le daoine, ní hamháin ar Mastodon, ach thar aipeanna sóisialta éagsúla freisin.", "domain_pill.activitypub_lets_connect": "Ligeann sé duit ceangal agus idirghníomhú le daoine, ní hamháin ar Mastodon, ach thar aipeanna sóisialta éagsúla freisin.",
"domain_pill.activitypub_like_language": "Tá GníomhaíochtPub cosúil leis an teanga a labhraíonn Mastodon le líonraí sóisialta eile.", "domain_pill.activitypub_like_language": "Tá GníomhaíochtPub cosúil leis an teanga a labhraíonn Mastodon le líonraí sóisialta eile.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Chan urrainn do neach sam bith a th air an fhrithealaiche seo do leantainn.", "domain_block_modal.they_cant_follow": "Chan urrainn do neach sam bith a th air an fhrithealaiche seo do leantainn.",
"domain_block_modal.they_wont_know": "Cha bhi fios aca gun deach am bacadh.", "domain_block_modal.they_wont_know": "Cha bhi fios aca gun deach am bacadh.",
"domain_block_modal.title": "A bheil thu airson an àrainn a bhacadh?", "domain_block_modal.title": "A bheil thu airson an àrainn a bhacadh?",
"domain_block_modal.you_will_lose_followers": "Thèid a h-uile neach-leantainn agad a th air an fhrithealaiche seo a thoirt air falbh.",
"domain_block_modal.you_wont_see_posts": "Chan fhaic thu postaichean no brathan o chleachdaichean a th air an fhrithealaiche seo.", "domain_block_modal.you_wont_see_posts": "Chan fhaic thu postaichean no brathan o chleachdaichean a th air an fhrithealaiche seo.",
"domain_pill.activitypub_lets_connect": "Leigidh e leat ceangal a dhèanamh ri daoine chan ann air Mastodon a-mhàin ach air feadh aplacaidean sòisealta eile cuideachd agus conaltradh leotha.", "domain_pill.activitypub_lets_connect": "Leigidh e leat ceangal a dhèanamh ri daoine chan ann air Mastodon a-mhàin ach air feadh aplacaidean sòisealta eile cuideachd agus conaltradh leotha.",
"domain_pill.activitypub_like_language": "Tha ActivityPub coltach ri cànan a bhruidhneas Mastodon ri lìonraidhean sòisealta eile.", "domain_pill.activitypub_like_language": "Tha ActivityPub coltach ri cànan a bhruidhneas Mastodon ri lìonraidhean sòisealta eile.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Límite de intentos", "alert.rate_limited.title": "Límite de intentos",
"alert.unexpected.message": "Aconteceu un fallo non agardado.", "alert.unexpected.message": "Aconteceu un fallo non agardado.",
"alert.unexpected.title": "Vaites!", "alert.unexpected.title": "Vaites!",
"alt_text_badge.title": "Texto Alt",
"announcement.announcement": "Anuncio", "announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "(sen procesar)", "attachments_list.unprocessed": "(sen procesar)",
"audio.hide": "Agochar audio", "audio.hide": "Agochar audio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Ninguén deste servidor pode seguirte.", "domain_block_modal.they_cant_follow": "Ninguén deste servidor pode seguirte.",
"domain_block_modal.they_wont_know": "Non saberá que a bloqueaches.", "domain_block_modal.they_wont_know": "Non saberá que a bloqueaches.",
"domain_block_modal.title": "Bloquear dominio?", "domain_block_modal.title": "Bloquear dominio?",
"domain_block_modal.you_will_lose_followers": "Vanse eliminar todas as túas seguidoras deste servidor.", "domain_block_modal.you_will_lose_num_followers": "Vas perder {followersCount, plural, one {{followersCountDisplay} seguidora} other {{followersCountDisplay} seguidoras}} e {followingCount, plural, one {{followingCountDisplay} persoa que segues} other {{followingCountDisplay} persoas que segues}}.",
"domain_block_modal.you_will_lose_relationships": "Vas perder todas as seguidoras e seguimentos a persoas deste servidor.",
"domain_block_modal.you_wont_see_posts": "Non verás publicacións ou notificacións das usuarias deste servidor.", "domain_block_modal.you_wont_see_posts": "Non verás publicacións ou notificacións das usuarias deste servidor.",
"domain_pill.activitypub_lets_connect": "Permíteche conectar e interactuar con persoas non só de Mastodon, se non tamén con outras sociais.", "domain_pill.activitypub_lets_connect": "Permíteche conectar e interactuar con persoas non só de Mastodon, se non tamén con outras sociais.",
"domain_pill.activitypub_like_language": "ActivityPub é algo así como o idioma que Mastodon fala con outras redes sociais.", "domain_pill.activitypub_like_language": "ActivityPub é algo así como o idioma que Mastodon fala con outras redes sociais.",
@ -527,7 +529,7 @@
"notification.poll": "Rematou a enquisa na que votaches", "notification.poll": "Rematou a enquisa na que votaches",
"notification.reblog": "{name} compartiu a túa publicación", "notification.reblog": "{name} compartiu a túa publicación",
"notification.reblog.name_and_others_with_link": "{name} e <a>{count, plural, one {# máis} other {# máis}}</a> promoveron a túa publicación", "notification.reblog.name_and_others_with_link": "{name} e <a>{count, plural, one {# máis} other {# máis}}</a> promoveron a túa publicación",
"notification.relationships_severance_event": "Perdeuse a conexión con {name}", "notification.relationships_severance_event": "Relacións perdidas con {name}",
"notification.relationships_severance_event.account_suspension": "A administración de {from} suspendeu a {target}, o que significa que xa non vas recibir actualizacións de esa conta ou interactuar con ela.", "notification.relationships_severance_event.account_suspension": "A administración de {from} suspendeu a {target}, o que significa que xa non vas recibir actualizacións de esa conta ou interactuar con ela.",
"notification.relationships_severance_event.domain_block": "A administración de {from} bloqueou a {target}, que inclúe a {followersCount} das túas seguidoras e a {followingCount, plural, one {# conta} other {# contas}} que sigues.", "notification.relationships_severance_event.domain_block": "A administración de {from} bloqueou a {target}, que inclúe a {followersCount} das túas seguidoras e a {followingCount, plural, one {# conta} other {# contas}} que sigues.",
"notification.relationships_severance_event.learn_more": "Saber máis", "notification.relationships_severance_event.learn_more": "Saber máis",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "משתמש משרת זה לא יכול לעקוב אחריך.", "domain_block_modal.they_cant_follow": "משתמש משרת זה לא יכול לעקוב אחריך.",
"domain_block_modal.they_wont_know": "הם לא ידעו כי נחסמו.", "domain_block_modal.they_wont_know": "הם לא ידעו כי נחסמו.",
"domain_block_modal.title": "לחסום שרת?", "domain_block_modal.title": "לחסום שרת?",
"domain_block_modal.you_will_lose_followers": "כל עוקביך משרת זה יוסרו.",
"domain_block_modal.you_wont_see_posts": "לא תוכלו לראות הודעות ממשתמשים על שרת זה.", "domain_block_modal.you_wont_see_posts": "לא תוכלו לראות הודעות ממשתמשים על שרת זה.",
"domain_pill.activitypub_lets_connect": "מאפשר לך להתחבר ולהתרועע עם אחרים לא רק במסטודון, אלא גם ביישומים חברתיים שונים אחרים.", "domain_pill.activitypub_lets_connect": "מאפשר לך להתחבר ולהתרועע עם אחרים לא רק במסטודון, אלא גם ביישומים חברתיים שונים אחרים.",
"domain_pill.activitypub_like_language": "אקטיביטיפאב היא למעשה השפה בה מסטודון מדבר עם רשתות חברתיות אחרות.", "domain_pill.activitypub_like_language": "אקטיביטיפאב היא למעשה השפה בה מסטודון מדבר עם רשתות חברתיות אחרות.",

View file

@ -221,7 +221,8 @@
"domain_block_modal.they_cant_follow": "Erről a kiszolgálóról senki sem követhet.", "domain_block_modal.they_cant_follow": "Erről a kiszolgálóról senki sem követhet.",
"domain_block_modal.they_wont_know": "Nem fogja tudni, hogy letiltották.", "domain_block_modal.they_wont_know": "Nem fogja tudni, hogy letiltották.",
"domain_block_modal.title": "Letiltsuk a domaint?", "domain_block_modal.title": "Letiltsuk a domaint?",
"domain_block_modal.you_will_lose_followers": "Az ezen a kiszolgálón lévő összes követődet törölni fogjuk.", "domain_block_modal.you_will_lose_num_followers": "El fogsz veszíteni {followersCount, plural, one {{followersCountDisplay} követőt} other {{followersCountDisplay} követőt}} és {followingCount, plural, one {{followingCountDisplay} követett személyt} other {{followingCountDisplay} követett személyt}}.",
"domain_block_modal.you_will_lose_relationships": "Minden követőt és követett személyt el fogsz veszíteni erről a kiszolgálóról.",
"domain_block_modal.you_wont_see_posts": "Nem látsz majd bejegyzéseket vagy értesítéseket ennek a kiszolgálónak a felhasználóitól.", "domain_block_modal.you_wont_see_posts": "Nem látsz majd bejegyzéseket vagy értesítéseket ennek a kiszolgálónak a felhasználóitól.",
"domain_pill.activitypub_lets_connect": "Lehetővé teszi, hogy kapcsolatba lépj nem csak a Mastodonon, hanem a más közösségi alkalmazásokon lévő emberekkel is.", "domain_pill.activitypub_lets_connect": "Lehetővé teszi, hogy kapcsolatba lépj nem csak a Mastodonon, hanem a más közösségi alkalmazásokon lévő emberekkel is.",
"domain_pill.activitypub_like_language": "Az ActivityPub olyan mint egy nyelv, amelyet a Mastodon a más közösségi hálózatokkal való kommunikációra használ.", "domain_pill.activitypub_like_language": "Az ActivityPub olyan mint egy nyelv, amelyet a Mastodon a más közösségi hálózatokkal való kommunikációra használ.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Necuno de iste servitor pote sequer te.", "domain_block_modal.they_cant_follow": "Necuno de iste servitor pote sequer te.",
"domain_block_modal.they_wont_know": "Ille non sapera que ille ha essite blocate.", "domain_block_modal.they_wont_know": "Ille non sapera que ille ha essite blocate.",
"domain_block_modal.title": "Blocar dominio?", "domain_block_modal.title": "Blocar dominio?",
"domain_block_modal.you_will_lose_followers": "Tote tu sequitores de iste servitor essera removite.",
"domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes de usatores sur iste servitor.", "domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes de usatores sur iste servitor.",
"domain_pill.activitypub_lets_connect": "Illo te permitte connecter e interager con personas non solmente sur Mastodon, ma tamben sur altere applicationes social.", "domain_pill.activitypub_lets_connect": "Illo te permitte connecter e interager con personas non solmente sur Mastodon, ma tamben sur altere applicationes social.",
"domain_pill.activitypub_like_language": "ActivityPub es como le linguage commun que Mastodon parla con altere retes social.", "domain_pill.activitypub_like_language": "ActivityPub es como le linguage commun que Mastodon parla con altere retes social.",

View file

@ -219,7 +219,6 @@
"domain_block_modal.they_cant_follow": "Tidak ada seorangpun dari server ini yang dapat mengikuti anda.", "domain_block_modal.they_cant_follow": "Tidak ada seorangpun dari server ini yang dapat mengikuti anda.",
"domain_block_modal.they_wont_know": "Mereka tidak akan tahu bahwa mereka diblokir.", "domain_block_modal.they_wont_know": "Mereka tidak akan tahu bahwa mereka diblokir.",
"domain_block_modal.title": "Blokir domain?", "domain_block_modal.title": "Blokir domain?",
"domain_block_modal.you_will_lose_followers": "Semua pengikut anda dari server ini akan dihapus.",
"domain_block_modal.you_wont_see_posts": "Anda tidak akan melihat postingan atau notifikasi dari pengguna di server ini.", "domain_block_modal.you_wont_see_posts": "Anda tidak akan melihat postingan atau notifikasi dari pengguna di server ini.",
"domain_pill.activitypub_lets_connect": "Ini memungkinkan anda terhubung dan berinteraksi dengan orang-orang tidak hanya di Mastodon, tetapi juga di berbagai aplikasi sosial.", "domain_pill.activitypub_lets_connect": "Ini memungkinkan anda terhubung dan berinteraksi dengan orang-orang tidak hanya di Mastodon, tetapi juga di berbagai aplikasi sosial.",
"domain_pill.activitypub_like_language": "ActivityPub seperti bahasa yang digunakan Mastodon dengan jejaring sosial lainnya.", "domain_pill.activitypub_like_language": "ActivityPub seperti bahasa yang digunakan Mastodon dengan jejaring sosial lainnya.",

View file

@ -206,7 +206,6 @@
"domain_block_modal.they_cant_follow": "Nequi de ti-ci servitor posse sequer te.", "domain_block_modal.they_cant_follow": "Nequi de ti-ci servitor posse sequer te.",
"domain_block_modal.they_wont_know": "Ne va esser conscient pri li bloccada.", "domain_block_modal.they_wont_know": "Ne va esser conscient pri li bloccada.",
"domain_block_modal.title": "Bloccar dominia?", "domain_block_modal.title": "Bloccar dominia?",
"domain_block_modal.you_will_lose_followers": "Omni tui sequitores de ti-ci servitor va esser efaciat.",
"domain_block_modal.you_wont_see_posts": "Tu ne va vider postas ni notificationes de usatores sur ti-ci servitor.", "domain_block_modal.you_wont_see_posts": "Tu ne va vider postas ni notificationes de usatores sur ti-ci servitor.",
"domain_pill.activitypub_lets_connect": "It possibilisa tui conexiones e interactiones con persones ne solmen sur Mastodon, ma anc tra diferent social aplis.", "domain_pill.activitypub_lets_connect": "It possibilisa tui conexiones e interactiones con persones ne solmen sur Mastodon, ma anc tra diferent social aplis.",
"domain_pill.activitypub_like_language": "ActivityPub es li lingue usat de Mastodon por parlar con altri social retages.", "domain_pill.activitypub_like_language": "ActivityPub es li lingue usat de Mastodon por parlar con altri social retages.",

View file

@ -1,6 +1,7 @@
{ {
"about.blocks": "Jerata servili", "about.blocks": "Jerata servili",
"about.contact": "Kontaktajo:", "about.contact": "Kontaktajo:",
"about.disclaimer": "Mastodon esas libera, publikfonta e komercmarko di Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Expliko nedisponebla", "about.domain_blocks.no_reason_available": "Expliko nedisponebla",
"about.domain_blocks.preamble": "Mastodon generale permisas on vidar kontenajo e interagar kun uzanti de irga altra servilo en fediverso. Existas eceptioni quo facesis che ca partikulara servilo.", "about.domain_blocks.preamble": "Mastodon generale permisas on vidar kontenajo e interagar kun uzanti de irga altra servilo en fediverso. Existas eceptioni quo facesis che ca partikulara servilo.",
"about.domain_blocks.silenced.explanation": "On generale ne vidar profili e kontenajo de ca servilo, se on ne reale trovar o voluntale juntar per sequar.", "about.domain_blocks.silenced.explanation": "On generale ne vidar profili e kontenajo de ca servilo, se on ne reale trovar o voluntale juntar per sequar.",
@ -10,6 +11,7 @@
"about.not_available": "Ca informo ne igesis che ca servilo.", "about.not_available": "Ca informo ne igesis che ca servilo.",
"about.powered_by": "Necentraligita sociala ret quo povigesas da {mastodon}", "about.powered_by": "Necentraligita sociala ret quo povigesas da {mastodon}",
"about.rules": "Servilreguli", "about.rules": "Servilreguli",
"account.account_note_header": "Personala noto",
"account.add_or_remove_from_list": "Insertez o removez de listi", "account.add_or_remove_from_list": "Insertez o removez de listi",
"account.badges.bot": "Boto", "account.badges.bot": "Boto",
"account.badges.group": "Grupo", "account.badges.group": "Grupo",
@ -29,9 +31,12 @@
"account.featured_tags.last_status_never": "Nula posti", "account.featured_tags.last_status_never": "Nula posti",
"account.featured_tags.title": "Estalita hashtagi di {name}", "account.featured_tags.title": "Estalita hashtagi di {name}",
"account.follow": "Sequar", "account.follow": "Sequar",
"account.follow_back": "Anke sequez",
"account.followers": "Sequanti", "account.followers": "Sequanti",
"account.followers.empty": "Nulu sequas ca uzanto til nun.", "account.followers.empty": "Nulu sequas ca uzanto til nun.",
"account.followers_counter": "{count, plural,one {{counter} sequanto} other {{counter} sequanti}}",
"account.following": "Sequata", "account.following": "Sequata",
"account.following_counter": "{count, plural,one {{counter} sequato} other {{counter} sequati}}",
"account.follows.empty": "Ca uzanto ne sequa irgu til nun.", "account.follows.empty": "Ca uzanto ne sequa irgu til nun.",
"account.go_to_profile": "Irez al profilo", "account.go_to_profile": "Irez al profilo",
"account.hide_reblogs": "Celez repeti de @{name}", "account.hide_reblogs": "Celez repeti de @{name}",
@ -47,6 +52,7 @@
"account.mute_notifications_short": "Silencigez avizi", "account.mute_notifications_short": "Silencigez avizi",
"account.mute_short": "Silencigez", "account.mute_short": "Silencigez",
"account.muted": "Silencigata", "account.muted": "Silencigata",
"account.mutual": "Mutuala",
"account.no_bio": "Deskriptajo ne provizesis.", "account.no_bio": "Deskriptajo ne provizesis.",
"account.open_original_page": "Apertez originala pagino", "account.open_original_page": "Apertez originala pagino",
"account.posts": "Mesaji", "account.posts": "Mesaji",
@ -56,6 +62,7 @@
"account.requested_follow": "{name} demandis sequar tu", "account.requested_follow": "{name} demandis sequar tu",
"account.share": "Partigez profilo di @{name}", "account.share": "Partigez profilo di @{name}",
"account.show_reblogs": "Montrez repeti de @{name}", "account.show_reblogs": "Montrez repeti de @{name}",
"account.statuses_counter": "{count, plural,one {{counter} mesajo} other {{counter} mesaji}}",
"account.unblock": "Desblokusar @{name}", "account.unblock": "Desblokusar @{name}",
"account.unblock_domain": "Desblokusar {domain}", "account.unblock_domain": "Desblokusar {domain}",
"account.unblock_short": "Desblokusar", "account.unblock_short": "Desblokusar",
@ -78,10 +85,21 @@
"alert.rate_limited.title": "Demandi limitizita", "alert.rate_limited.title": "Demandi limitizita",
"alert.unexpected.message": "Neexpektita eroro eventis.", "alert.unexpected.message": "Neexpektita eroro eventis.",
"alert.unexpected.title": "Problemo!", "alert.unexpected.title": "Problemo!",
"alt_text_badge.title": "Alternativa texto",
"announcement.announcement": "Anunco", "announcement.announcement": "Anunco",
"attachments_list.unprocessed": "(neprocedita)", "attachments_list.unprocessed": "(neprocedita)",
"audio.hide": "Celez audio", "audio.hide": "Celez audio",
"block_modal.remote_users_caveat": "Ni questionos {domain} di la servilo por respektar vua decido. Publika posti forsan ankore estas videbla a neenirinta uzanti.",
"block_modal.show_less": "Montrar mine",
"block_modal.show_more": "Montrar plue",
"block_modal.they_cant_mention": "Oli ne povas mencionar o sequar vu.",
"block_modal.they_cant_see_posts": "Oli ne povas vidar vua mesaji e vu ne vidos vidar olia.",
"block_modal.they_will_know": "Oli povas vidar ke oli esas blokusita.",
"block_modal.title": "Blokusar uzanto?",
"block_modal.you_wont_see_mentions": "Vu ne vidos mesaji qua mencionas oli.",
"boost_modal.combo": "Vu povas pulsar {combo} por omisar co venontafoye", "boost_modal.combo": "Vu povas pulsar {combo} por omisar co venontafoye",
"boost_modal.reblog": "Ka repetar posto?",
"boost_modal.undo_reblog": "Ka retrorepetar posto?",
"bundle_column_error.copy_stacktrace": "Kopierorraporto", "bundle_column_error.copy_stacktrace": "Kopierorraporto",
"bundle_column_error.error.body": "La demandita pagino ne povas strukturigesar. Forsan ol esas eroro en kodexo hike o vidilkoncilieblesproblemo.", "bundle_column_error.error.body": "La demandita pagino ne povas strukturigesar. Forsan ol esas eroro en kodexo hike o vidilkoncilieblesproblemo.",
"bundle_column_error.error.title": "Ach!", "bundle_column_error.error.title": "Ach!",
@ -138,30 +156,47 @@
"compose_form.lock_disclaimer.lock": "klefagesas", "compose_form.lock_disclaimer.lock": "klefagesas",
"compose_form.placeholder": "Quo esas en tua spirito?", "compose_form.placeholder": "Quo esas en tua spirito?",
"compose_form.poll.duration": "Votpostoduro", "compose_form.poll.duration": "Votpostoduro",
"compose_form.poll.multiple": "Multopla selekteso",
"compose_form.poll.option_placeholder": "Selektato {number}",
"compose_form.poll.single": "Selektez un",
"compose_form.poll.switch_to_multiple": "Chanjez votposto por permisar multiselektaji", "compose_form.poll.switch_to_multiple": "Chanjez votposto por permisar multiselektaji",
"compose_form.poll.switch_to_single": "Chanjez votposto por permisar una selektajo", "compose_form.poll.switch_to_single": "Chanjez votposto por permisar una selektajo",
"compose_form.poll.type": "Stilo",
"compose_form.publish": "Posto",
"compose_form.publish_form": "Publish", "compose_form.publish_form": "Publish",
"compose_form.reply": "Respondez",
"compose_form.save_changes": "Aktualigez",
"compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.marked": "Text is hidden behind warning",
"compose_form.spoiler.unmarked": "Text is not hidden", "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Kontenajaverto (selektebla)",
"confirmation_modal.cancel": "Anulez", "confirmation_modal.cancel": "Anulez",
"confirmations.block.confirm": "Restriktez", "confirmations.block.confirm": "Restriktez",
"confirmations.delete.confirm": "Efacez", "confirmations.delete.confirm": "Efacez",
"confirmations.delete.message": "Are you sure you want to delete this status?", "confirmations.delete.message": "Are you sure you want to delete this status?",
"confirmations.delete.title": "Ka efacar posto?",
"confirmations.delete_list.confirm": "Efacez", "confirmations.delete_list.confirm": "Efacez",
"confirmations.delete_list.message": "Ka vu certe volas netempale efacar ca listo?", "confirmations.delete_list.message": "Ka vu certe volas netempale efacar ca listo?",
"confirmations.delete_list.title": "Ka efacar listo?",
"confirmations.discard_edit_media.confirm": "Efacez", "confirmations.discard_edit_media.confirm": "Efacez",
"confirmations.discard_edit_media.message": "Vu havas nesparita chanji di mediodeskript o prevido, vu volas jus efacar?", "confirmations.discard_edit_media.message": "Vu havas nesparita chanji di mediodeskript o prevido, vu volas jus efacar?",
"confirmations.edit.confirm": "Modifikez", "confirmations.edit.confirm": "Modifikez",
"confirmations.edit.message": "Modifikar nun remplasos la mesajo quon vu nune skribas. Ka vu certe volas procedar?", "confirmations.edit.message": "Modifikar nun remplasos la mesajo quon vu nune skribas. Ka vu certe volas procedar?",
"confirmations.edit.title": "Ka remplasar posto?",
"confirmations.logout.confirm": "Ekirez", "confirmations.logout.confirm": "Ekirez",
"confirmations.logout.message": "Ka tu certe volas ekirar?", "confirmations.logout.message": "Ka tu certe volas ekirar?",
"confirmations.logout.title": "Ka ekirar?",
"confirmations.mute.confirm": "Silencigez", "confirmations.mute.confirm": "Silencigez",
"confirmations.redraft.confirm": "Efacez e riskisez", "confirmations.redraft.confirm": "Efacez e riskisez",
"confirmations.redraft.message": "Ka vu certe volas efacar ca posto e riskisigar ol? Favoriziti e repeti esos perdita, e respondi al posto originala esos orfanigita.", "confirmations.redraft.message": "Ka vu certe volas efacar ca posto e riskisigar ol? Favoriziti e repeti esos perdita, e respondi al posto originala esos orfanigita.",
"confirmations.redraft.title": "Ka efacar & riskisar posto?",
"confirmations.reply.confirm": "Respondez", "confirmations.reply.confirm": "Respondez",
"confirmations.reply.message": "Respondar nun remplos mesajo quon vu nun igas. Ka vu certe volas durar?", "confirmations.reply.message": "Respondar nun remplos mesajo quon vu nun igas. Ka vu certe volas durar?",
"confirmations.reply.title": "Ka remplasar posto?",
"confirmations.unfollow.confirm": "Desequez", "confirmations.unfollow.confirm": "Desequez",
"confirmations.unfollow.message": "Ka vu certe volas desequar {name}?", "confirmations.unfollow.message": "Ka vu certe volas desequar {name}?",
"confirmations.unfollow.title": "Ka retrosequar uzanto?",
"content_warning.hide": "Celez posto",
"content_warning.show": "Montrez nur",
"conversation.delete": "Efacez konverso", "conversation.delete": "Efacez konverso",
"conversation.mark_as_read": "Markizez quale lektita", "conversation.mark_as_read": "Markizez quale lektita",
"conversation.open": "Videz konverso", "conversation.open": "Videz konverso",
@ -181,6 +216,28 @@
"dismissable_banner.explore_statuses": "Yen posti del tota reto sociala qui esas populara hodie. Posti plu nova kun plu repeti e favoriziti esas rangizita plu alte.", "dismissable_banner.explore_statuses": "Yen posti del tota reto sociala qui esas populara hodie. Posti plu nova kun plu repeti e favoriziti esas rangizita plu alte.",
"dismissable_banner.explore_tags": "Ca hashtagi bezonas plu famoza inter personi che ca e altra servili di la necentraligita situo nun.", "dismissable_banner.explore_tags": "Ca hashtagi bezonas plu famoza inter personi che ca e altra servili di la necentraligita situo nun.",
"dismissable_banner.public_timeline": "Yen la posti maxim recenta da personi che la reto sociala quin personi che {domain} sequas.", "dismissable_banner.public_timeline": "Yen la posti maxim recenta da personi che la reto sociala quin personi che {domain} sequas.",
"domain_block_modal.block": "Blokusez servilo",
"domain_block_modal.block_account_instead": "Blokusez @{name} vice",
"domain_block_modal.they_can_interact_with_old_posts": "Personi de ca servilo povas interagar kun vua desnova posti.",
"domain_block_modal.they_cant_follow": "Nulu de ca servilo povas sequar vu.",
"domain_block_modal.they_wont_know": "Lu ne savos ke lu blokusesis.",
"domain_block_modal.title": "Ka blokusar domeno?",
"domain_block_modal.you_will_lose_num_followers": "Vu desganos {followersCount, plural, one {{followersCountDisplay} sequanto} other {{followersCountDisplay} sequanti}} e {followingCount, plural, one {{followingCountDisplay} persono quan vu sequas} other {{followingCountDisplay} personi quan vu sequas}}.",
"domain_block_modal.you_will_lose_relationships": "Vu desganos omna sequanti e sequati de ca servilo.",
"domain_block_modal.you_wont_see_posts": "Vu ne vidos postoi o savigi de uzanti en ca servilo.",
"domain_pill.activitypub_lets_connect": "Ol povigas vu kuneskas e interagar kun personi ne nur sur Mastodon, o anke kun dessama socia softwari.",
"domain_pill.activitypub_like_language": "ActivityPub esas kam linguo quan Mastodon parolas kun altra socia reti.",
"domain_pill.server": "Servilo",
"domain_pill.their_handle": "Lua nomo:",
"domain_pill.their_server": "Lua komputala hemo, e havas omna lua posti.",
"domain_pill.their_username": "Lua unika identesilo sur lua servilo. Posible trovar uzanti kun sama uzantonomo sur dessama servili.",
"domain_pill.username": "Uzantonomo",
"domain_pill.whats_in_a_handle": "Quo esas nomo?",
"domain_pill.who_they_are": "Pro ke nomo esas deskripto di ulu, vu povas interagar kun personi tra socia interreto kun <button>ActivityPub-povizo</button>.",
"domain_pill.who_you_are": "Pro ke vua nomo esas deskripto pri vu e vua situo, personi povas interagar kun vu tra socia interreto kun <button>ActivityPub-povizo</button>.",
"domain_pill.your_handle": "Vua nomo:",
"domain_pill.your_server": "Vua komputerala hemo qua havas omna vua posti. On povas transferar a altra servili ulatempe e anke adportar vua sequanti.",
"domain_pill.your_username": "Vua unika identesilo sur lua servilo. Posible trovar uzanti kun sama uzantonomo sur dessama servili.",
"embed.instructions": "Embed this status on your website by copying the code below.", "embed.instructions": "Embed this status on your website by copying the code below.",
"embed.preview": "Co esas quon ol semblos tale:", "embed.preview": "Co esas quon ol semblos tale:",
"emoji_button.activity": "Ago", "emoji_button.activity": "Ago",
@ -217,6 +274,7 @@
"empty_column.list": "There is nothing in this list yet.", "empty_column.list": "There is nothing in this list yet.",
"empty_column.lists": "Vu ne havas irga listi til nun. Kande vu kreas talo, ol montresos hike.", "empty_column.lists": "Vu ne havas irga listi til nun. Kande vu kreas talo, ol montresos hike.",
"empty_column.mutes": "Vu ne silencigis irga uzanti til nun.", "empty_column.mutes": "Vu ne silencigis irga uzanti til nun.",
"empty_column.notification_requests": "Finis. Kande vu recevas nova savigi, oli aparos hike segun vua preferaji.",
"empty_column.notifications": "Tu havas ankore nula savigo. Komunikez kun altri por debutar la konverso.", "empty_column.notifications": "Tu havas ankore nula savigo. Komunikez kun altri por debutar la konverso.",
"empty_column.public": "Esas nulo hike! Skribez ulo publike, o manuale sequez uzeri de altra instaluri por plenigar ol.", "empty_column.public": "Esas nulo hike! Skribez ulo publike, o manuale sequez uzeri de altra instaluri por plenigar ol.",
"error.unexpected_crash.explanation": "Pro eroro en nia kodexo o vidilkonciliebloproblemo, ca pagino ne povas korekte montresar.", "error.unexpected_crash.explanation": "Pro eroro en nia kodexo o vidilkonciliebloproblemo, ca pagino ne povas korekte montresar.",
@ -247,12 +305,30 @@
"filter_modal.select_filter.subtitle": "Usez disponebla grupo o kreez novajo", "filter_modal.select_filter.subtitle": "Usez disponebla grupo o kreez novajo",
"filter_modal.select_filter.title": "Filtragez ca posto", "filter_modal.select_filter.title": "Filtragez ca posto",
"filter_modal.title.status": "Filtragez posto", "filter_modal.title.status": "Filtragez posto",
"filter_warning.matches_filter": "Sama kam filtrilo \"{title}\"",
"filtered_notifications_banner.pending_requests": "De {count, plural,=0 {nulu} one {1 persono} other {# personi}} quan vu forsan konocas",
"filtered_notifications_banner.title": "Filtrilita savigi",
"firehose.all": "Omno", "firehose.all": "Omno",
"firehose.local": "Ca servilo", "firehose.local": "Ca servilo",
"firehose.remote": "Altra servili", "firehose.remote": "Altra servili",
"follow_request.authorize": "Yurizar", "follow_request.authorize": "Yurizar",
"follow_request.reject": "Refuzar", "follow_request.reject": "Refuzar",
"follow_requests.unlocked_explanation": "Quankam vua konto ne klefklozesis, la {domain} laborero pensas ke vu forsan volas kontralar sequodemandi de ca konti manuale.", "follow_requests.unlocked_explanation": "Quankam vua konto ne klefklozesis, la {domain} laborero pensas ke vu forsan volas kontralar sequodemandi de ca konti manuale.",
"follow_suggestions.curated_suggestion": "Selektato de jeranto",
"follow_suggestions.dismiss": "Ne montrez pluse",
"follow_suggestions.featured_longer": "Selektesis da la grupo di {domain}",
"follow_suggestions.friends_of_friends_longer": "Populara inter personi quan vu sequas",
"follow_suggestions.hints.featured": "Ca profilo selektesis da la grupo di {domain}.",
"follow_suggestions.hints.friends_of_friends": "Ca profilo esas populara inter personi quan vu sequas.",
"follow_suggestions.hints.most_followed": "Vua profilo esas un de maxim sequita sur {domain}.",
"follow_suggestions.hints.most_interactions": "Ca profilo recente populareskis sur {domain}.",
"follow_suggestions.hints.similar_to_recently_followed": "Ca profilo esas simila a la profili quan vu recente sequis.",
"follow_suggestions.personalized_suggestion": "Personalita sugestato",
"follow_suggestions.popular_suggestion": "Populara sugestato",
"follow_suggestions.popular_suggestion_longer": "Populara sur {domain}",
"follow_suggestions.similar_to_recently_followed_longer": "Simila a profili quan vu recente sequis",
"follow_suggestions.view_all": "Videz omno",
"follow_suggestions.who_to_follow": "Sequindo",
"followed_tags": "Hashtagi sequita", "followed_tags": "Hashtagi sequita",
"footer.about": "Pri co", "footer.about": "Pri co",
"footer.directory": "Profilcheflisto", "footer.directory": "Profilcheflisto",
@ -279,6 +355,14 @@
"hashtag.follow": "Sequez hashtago", "hashtag.follow": "Sequez hashtago",
"hashtag.unfollow": "Desequez hashtago", "hashtag.unfollow": "Desequez hashtago",
"hashtags.and_other": "…e {count, plural, one {# plusa}other {# plusa}}", "hashtags.and_other": "…e {count, plural, one {# plusa}other {# plusa}}",
"hints.profiles.followers_may_be_missing": "Sequanti di ca profilo forsan ne esas hike.",
"hints.profiles.follows_may_be_missing": "Sequati di ca profilo forsan ne esas hike.",
"hints.profiles.posts_may_be_missing": "Kelka posti sur ca profilo forsan ne esas hike.",
"hints.profiles.see_more_followers": "Vidar plu multa sequanti sur {domain}",
"hints.profiles.see_more_follows": "Vidar plu multa sequati sur {domain}",
"hints.profiles.see_more_posts": "Vidar plu multa posti sur {domain}",
"hints.threads.replies_may_be_missing": "Respondi de altra servili forsan ne esas hike.",
"hints.threads.see_more": "Vidar plu multa demandi sur {domain}",
"home.column_settings.show_reblogs": "Montrar repeti", "home.column_settings.show_reblogs": "Montrar repeti",
"home.column_settings.show_replies": "Montrar respondi", "home.column_settings.show_replies": "Montrar respondi",
"home.hide_announcements": "Celez anunci", "home.hide_announcements": "Celez anunci",
@ -286,6 +370,17 @@
"home.pending_critical_update.link": "Vidar aktualigaji", "home.pending_critical_update.link": "Vidar aktualigaji",
"home.pending_critical_update.title": "Sekuresala aktualigajo gravega disponebla!", "home.pending_critical_update.title": "Sekuresala aktualigajo gravega disponebla!",
"home.show_announcements": "Montrez anunci", "home.show_announcements": "Montrez anunci",
"ignore_notifications_modal.disclaimer": "Mastodon ne povas savigar uzanti ke vu ignoris olia savigi. Ignorar savigi ne cesos ipse mesaji sendesar.",
"ignore_notifications_modal.filter_instead": "Filtrar vice",
"ignore_notifications_modal.filter_to_act_users": "Vu povos aceptar, refuzar o raportar uzanti",
"ignore_notifications_modal.filter_to_avoid_confusion": "Filtro helpas evitar posibla konfuzo",
"ignore_notifications_modal.filter_to_review_separately": "Vu povas kontrolar filtrita savigi separe",
"ignore_notifications_modal.ignore": "Ignorez savigi",
"ignore_notifications_modal.limited_accounts_title": "Ka ignorar savigi de jerita konti?",
"ignore_notifications_modal.new_accounts_title": "Ka ignorar savigi de nova konti?",
"ignore_notifications_modal.not_followers_title": "Ka ignorar savigi de personi qua ne sequas vu?",
"ignore_notifications_modal.not_following_title": "Ka ignorar savigi de personi quan vu ne sequas?",
"ignore_notifications_modal.private_mentions_title": "Ka ignorar savigi de nekonocita privata mencionii?",
"interaction_modal.description.favourite": "Kun konto che Mastodon, vu povas favorizar ca posto por savigar la autoro ke vu prizas ol e sparar ol por pose.", "interaction_modal.description.favourite": "Kun konto che Mastodon, vu povas favorizar ca posto por savigar la autoro ke vu prizas ol e sparar ol por pose.",
"interaction_modal.description.follow": "Per konto che Mastodon, vu povas sequar {name} por ganar ola posti en vua hemniuzeto.", "interaction_modal.description.follow": "Per konto che Mastodon, vu povas sequar {name} por ganar ola posti en vua hemniuzeto.",
"interaction_modal.description.reblog": "Per konto che Mastodon, vu povas repetar ca posti por dissemar lo a vua propra sequati.", "interaction_modal.description.reblog": "Per konto che Mastodon, vu povas repetar ca posti por dissemar lo a vua propra sequati.",
@ -341,9 +436,13 @@
"lightbox.close": "Klozar", "lightbox.close": "Klozar",
"lightbox.next": "Nexta", "lightbox.next": "Nexta",
"lightbox.previous": "Antea", "lightbox.previous": "Antea",
"lightbox.zoom_in": "Grandigez a reala grandeso",
"lightbox.zoom_out": "Grandigez por fitigar",
"limited_account_hint.action": "Jus montrez profilo", "limited_account_hint.action": "Jus montrez profilo",
"limited_account_hint.title": "Ca profilo celesas dal jereri di {domain}.", "limited_account_hint.title": "Ca profilo celesas dal jereri di {domain}.",
"link_preview.author": "Da {name}", "link_preview.author": "Da {name}",
"link_preview.more_from_author": "Plua de {name}",
"link_preview.shares": "{count, plural,one {{counter} posto} other {{counter} posti}}",
"lists.account.add": "Insertez a listo", "lists.account.add": "Insertez a listo",
"lists.account.remove": "Efacez de listo", "lists.account.remove": "Efacez de listo",
"lists.delete": "Efacez listo", "lists.delete": "Efacez listo",
@ -360,8 +459,19 @@
"lists.subheading": "Vua listi", "lists.subheading": "Vua listi",
"load_pending": "{count, plural, one {# nova kozo} other {# nova kozi}}", "load_pending": "{count, plural, one {# nova kozo} other {# nova kozi}}",
"loading_indicator.label": "Kargante…", "loading_indicator.label": "Kargante…",
"media_gallery.hide": "Celez",
"moved_to_account_banner.text": "Vua konto {disabledAccount} es nune desaktiva pro ke vu movis a {movedToAccount}.", "moved_to_account_banner.text": "Vua konto {disabledAccount} es nune desaktiva pro ke vu movis a {movedToAccount}.",
"mute_modal.hide_from_notifications": "Celez de savigi",
"mute_modal.hide_options": "Celez preferaji",
"mute_modal.indefinite": "Til me retrosilencigas lu",
"mute_modal.show_options": "Montrez preferaji",
"mute_modal.they_can_mention_and_follow": "Lu povas mencionar e sequar vu, ma vu ne vidos lu.",
"mute_modal.they_wont_know": "Lu ne savos ke lu silencigesis.",
"mute_modal.title": "Ka silencigar uzanto?",
"mute_modal.you_wont_see_mentions": "Vu ne vidos posti qua mencionas lu.",
"mute_modal.you_wont_see_posts": "Lu ankore povas vidar vua posti, ma vu ne vidos lua.",
"navigation_bar.about": "Pri co", "navigation_bar.about": "Pri co",
"navigation_bar.administration": "Administro",
"navigation_bar.advanced_interface": "Apertez per retintervizajo", "navigation_bar.advanced_interface": "Apertez per retintervizajo",
"navigation_bar.blocks": "Blokusita uzeri", "navigation_bar.blocks": "Blokusita uzeri",
"navigation_bar.bookmarks": "Libromarki", "navigation_bar.bookmarks": "Libromarki",
@ -378,6 +488,7 @@
"navigation_bar.follows_and_followers": "Sequati e sequanti", "navigation_bar.follows_and_followers": "Sequati e sequanti",
"navigation_bar.lists": "Listi", "navigation_bar.lists": "Listi",
"navigation_bar.logout": "Ekirar", "navigation_bar.logout": "Ekirar",
"navigation_bar.moderation": "Jero",
"navigation_bar.mutes": "Celita uzeri", "navigation_bar.mutes": "Celita uzeri",
"navigation_bar.opened_in_classic_interface": "Posti, konti e altra pagini specifika apertesas en la retovidilo klasika.", "navigation_bar.opened_in_classic_interface": "Posti, konti e altra pagini specifika apertesas en la retovidilo klasika.",
"navigation_bar.personal": "Personala", "navigation_bar.personal": "Personala",
@ -388,20 +499,71 @@
"navigation_bar.security": "Sekureso", "navigation_bar.security": "Sekureso",
"not_signed_in_indicator.not_signed_in": "Vu mustas enirar por acesar ca moyeno.", "not_signed_in_indicator.not_signed_in": "Vu mustas enirar por acesar ca moyeno.",
"notification.admin.report": "{name} raportizis {target}", "notification.admin.report": "{name} raportizis {target}",
"notification.admin.report_account": "{name} raportis {count, plural,one {1 posto} other {# posti}} de {target} pro {category}",
"notification.admin.report_account_other": "{name} raportis {count, plural,one {1 posto} other {# posti}} de {target}",
"notification.admin.report_statuses": "{name} raportis {target} pro {category}",
"notification.admin.report_statuses_other": "{name} raportis {target}",
"notification.admin.sign_up": "{name} registresis", "notification.admin.sign_up": "{name} registresis",
"notification.admin.sign_up.name_and_others": "{name} e {count, plural,one {# altru} other {#altri}} enrejistris",
"notification.favourite": "{name} favorizis tua mesajo", "notification.favourite": "{name} favorizis tua mesajo",
"notification.favourite.name_and_others_with_link": "{name} e <a>{count, plural,one {# altru} other {# altri}}</a> favorizis vua posto",
"notification.follow": "{name} sequeskis tu", "notification.follow": "{name} sequeskis tu",
"notification.follow.name_and_others": "{name} e {count, plural,one {# altru} other {#altri}} sequis vu",
"notification.follow_request": "{name} demandas sequar vu", "notification.follow_request": "{name} demandas sequar vu",
"notification.follow_request.name_and_others": "{name} e {count, plural,one {# altru} other {# altri}} volas sequar vu",
"notification.label.mention": "Mencionez",
"notification.label.private_mention": "Privata menciono",
"notification.label.private_reply": "Privata respondo",
"notification.label.reply": "Respondez",
"notification.mention": "Mencionez",
"notification.moderation-warning.learn_more": "Lernez pluse",
"notification.moderation_warning": "Vu recevis jeraverto",
"notification.moderation_warning.action_delete_statuses": "Kelka vua posti efacesis.",
"notification.moderation_warning.action_disable": "Vua konto estas desaktivigita.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Kelka vua posti markizesis quale sentoza.",
"notification.moderation_warning.action_none": "Vua konto recevis jeraverto.",
"notification.moderation_warning.action_sensitive": "Vua posti markizesos quale sentoza pos nun.",
"notification.moderation_warning.action_silence": "Vua konto limitizesis.",
"notification.moderation_warning.action_suspend": "Vua konto restriktesis.",
"notification.own_poll": "Vua votposto finigis", "notification.own_poll": "Vua votposto finigis",
"notification.poll": "Votposto quan vu partoprenis finis",
"notification.reblog": "{name} repetis tua mesajo", "notification.reblog": "{name} repetis tua mesajo",
"notification.reblog.name_and_others_with_link": "{name} e <a>{count, plural,one {# altru} other {#altri}}</a> repetis vua posto",
"notification.relationships_severance_event": "Desganis konekteso kun {name}",
"notification.relationships_severance_event.account_suspension": "Administranto de {from} restriktis {target}, do vu ne povas plue recevar novaji de lu o interagar kun lu.",
"notification.relationships_severance_event.domain_block": "Administranto de {from} blokusis {target}, e anke {followersCount} de vua sequanti e {followingCount, plural, one {# konto} other {# konti}} quan vu sequas.",
"notification.relationships_severance_event.learn_more": "Lernez pluse",
"notification.relationships_severance_event.user_domain_block": "Vu blokusis {target}, do efacis {followersCount} de vua sequanti e {followingCount, plural, one {# konto} other {#konti}} quan vu sequis.",
"notification.status": "{name} nove postigis", "notification.status": "{name} nove postigis",
"notification.update": "{name} modifikis posto", "notification.update": "{name} modifikis posto",
"notification_requests.accept": "Aceptez",
"notification_requests.accept_multiple": "{count, plural, one {Aceptar # demando…} other {Aceptar # demandi…}}",
"notification_requests.confirm_accept_multiple.button": "{count, plural, one {Aceptar demando} other {Aceptar demandi}}",
"notification_requests.confirm_accept_multiple.message": "Vu aceptos {count, plural, one {1 savigdemando} other {# savigdemandi}}. Ka vu certe volas durar?",
"notification_requests.confirm_accept_multiple.title": "Ka aceptar savigdemandi?",
"notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Ignorez demando} other {Ignorez demandi}}",
"notification_requests.confirm_dismiss_multiple.message": "Vu ignoros {count, plural, one {1 savigdemando} other {# savigdemandi}}. Vu ne povas facile ganar {count, plural, one {ol} other {oli}} pluse. Ka vu esas certe ke vu volas durar?",
"notification_requests.confirm_dismiss_multiple.title": "Ka ignorar savigdemandi?",
"notification_requests.dismiss": "Ignorez",
"notification_requests.dismiss_multiple": "{count, plural,one {Ignorez # demando…} other {Ignorez # demandi…}}",
"notification_requests.edit_selection": "Modifikez",
"notification_requests.exit_selection": "Finas",
"notification_requests.explainer_for_limited_account": "Savigi de ca konto filtresis pro ke la konto limitizesis da jeranto.",
"notification_requests.explainer_for_limited_remote_account": "Savigi de ca konto filtresis pro ke la konto o olua servilo limitizesis da jeranto.",
"notification_requests.maximize": "Parmontrez",
"notification_requests.minimize_banner": "Celez banero di filtrita savigi",
"notification_requests.notifications_from": "Savigi de {name}",
"notification_requests.title": "Filtrita savigi",
"notification_requests.view": "Videz savigi",
"notifications.clear": "Efacar savigi", "notifications.clear": "Efacar savigi",
"notifications.clear_confirmation": "Ka tu esas certa, ke tu volas efacar omna tua savigi?", "notifications.clear_confirmation": "Ka tu esas certa, ke tu volas efacar omna tua savigi?",
"notifications.clear_title": "Ka efacar savigi?",
"notifications.column_settings.admin.report": "Nova raporti:", "notifications.column_settings.admin.report": "Nova raporti:",
"notifications.column_settings.admin.sign_up": "Nova registranti:", "notifications.column_settings.admin.sign_up": "Nova registranti:",
"notifications.column_settings.alert": "Desktopavizi", "notifications.column_settings.alert": "Desktopavizi",
"notifications.column_settings.favourite": "Favoriziti:", "notifications.column_settings.favourite": "Favoriziti:",
"notifications.column_settings.filter_bar.advanced": "Montrez omna kategorii",
"notifications.column_settings.filter_bar.category": "Rapidfiltrilbaro",
"notifications.column_settings.follow": "Nova sequanti:", "notifications.column_settings.follow": "Nova sequanti:",
"notifications.column_settings.follow_request": "Nova sequodemandi:", "notifications.column_settings.follow_request": "Nova sequodemandi:",
"notifications.column_settings.mention": "Mencioni:", "notifications.column_settings.mention": "Mencioni:",
@ -427,6 +589,23 @@
"notifications.permission_denied": "Desktopavizi esas nedisplonebla pro antea refuzita vidilpermisdemando", "notifications.permission_denied": "Desktopavizi esas nedisplonebla pro antea refuzita vidilpermisdemando",
"notifications.permission_denied_alert": "Desktopavizi ne povas aktivigesar pro ke vidilpermiso refuzesis", "notifications.permission_denied_alert": "Desktopavizi ne povas aktivigesar pro ke vidilpermiso refuzesis",
"notifications.permission_required": "Desktopavizi esas nedisplonebla pro ke bezonata permiso ne donesis.", "notifications.permission_required": "Desktopavizi esas nedisplonebla pro ke bezonata permiso ne donesis.",
"notifications.policy.accept": "Aceptez",
"notifications.policy.accept_hint": "Montrez en savigi",
"notifications.policy.drop": "Ignorez",
"notifications.policy.drop_hint": "Nihiligez lu",
"notifications.policy.filter": "Filtrez",
"notifications.policy.filter_hint": "Sendez a enbuxo di filtrita savigi",
"notifications.policy.filter_limited_accounts_hint": "Limitizesis da serviljeranti",
"notifications.policy.filter_limited_accounts_title": "Jerita konti",
"notifications.policy.filter_new_accounts.hint": "Kreesis depos {days, plural, one {1 dio} other {# dii}}",
"notifications.policy.filter_new_accounts_title": "Nova konti",
"notifications.policy.filter_not_followers_hint": "Inkluzanta personi qua sequas vu min kam {days, plural, one {1 dio} other {# dii}}",
"notifications.policy.filter_not_followers_title": "Nesequanti",
"notifications.policy.filter_not_following_hint": "Til vu manuale aprobas lu",
"notifications.policy.filter_not_following_title": "Nesequati",
"notifications.policy.filter_private_mentions_hint": "Filtrita se ol ne esas respondo a vua sua menciono o se vu sequas la sendanto",
"notifications.policy.filter_private_mentions_title": "Nekonocita privata mencioni",
"notifications.policy.title": "Regular savigi de…",
"notifications_permission_banner.enable": "Aktivigez desktopavizi", "notifications_permission_banner.enable": "Aktivigez desktopavizi",
"notifications_permission_banner.how_to_control": "Por ganar avizi kande Mastodon ne esas apertita, aktivigez dekstopavizi. Vu povas precize regularar quale interakti facas deskstopavizi tra la supera {icon} butono pos oli aktivigesis.", "notifications_permission_banner.how_to_control": "Por ganar avizi kande Mastodon ne esas apertita, aktivigez dekstopavizi. Vu povas precize regularar quale interakti facas deskstopavizi tra la supera {icon} butono pos oli aktivigesis.",
"notifications_permission_banner.title": "Irga kozo ne pasas vu", "notifications_permission_banner.title": "Irga kozo ne pasas vu",
@ -464,6 +643,10 @@
"onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile", "onboarding.steps.share_profile.title": "Share your profile",
"onboarding.tips.2fa": "<strong>Ka vu savas?</strong> Vu povas sekurigar vua konto per pozar 2-faktora verifiko en preferaji de vua konto. Telefonilnombro ne bezonesis!",
"onboarding.tips.accounts_from_other_servers": "<strong>Ka vu savas?</strong> Vu povas interagar kun profili sur altra servili senrupte!",
"onboarding.tips.migration": "<strong>Ka vu savas?</strong> Se vu sentas ke {domain} ne esas apta por vu en la futuro, vu povas transferar a altra servilo di Mastodon sen malganar vua sequanti!",
"onboarding.tips.verification": "<strong>Ka vu savas?</strong> Vu povas verifikar vua konto per pozi ligilo a vua profilo di Mastodon sur vua sua retsituo e adjuntar la retsituo a vua profilo. Senpage!",
"password_confirmation.exceeds_maxlength": "La konfirmo dil pasvorto superesas la limito pri longeso di pasvorti", "password_confirmation.exceeds_maxlength": "La konfirmo dil pasvorto superesas la limito pri longeso di pasvorti",
"password_confirmation.mismatching": "La konfirmo dil pasvorto ne egalesas", "password_confirmation.mismatching": "La konfirmo dil pasvorto ne egalesas",
"picture_in_picture.restore": "Retropozez", "picture_in_picture.restore": "Retropozez",
@ -478,7 +661,15 @@
"poll_button.add_poll": "Insertez votposto", "poll_button.add_poll": "Insertez votposto",
"poll_button.remove_poll": "Efacez votposto", "poll_button.remove_poll": "Efacez votposto",
"privacy.change": "Aranjar privateso di mesaji", "privacy.change": "Aranjar privateso di mesaji",
"privacy.direct.long": "Omnu quan mencionesis en la posto",
"privacy.direct.short": "Specifika personi",
"privacy.private.long": "Nur vua sequanti",
"privacy.private.short": "Sequanti",
"privacy.public.long": "Ulu de e ne de Mastodon",
"privacy.public.short": "Publike", "privacy.public.short": "Publike",
"privacy.unlisted.additional": "Co kondutas exakte kam publika, escepte la posto ne aparos en viva novajari o gretiketi, exploro, o sercho di Mastodon, mem se vu esas volunta totkonte.",
"privacy.unlisted.long": "Min multa algoritmoridikuli",
"privacy.unlisted.short": "Deslauta publiko",
"privacy_policy.last_updated": "Antea novajo ye {date}", "privacy_policy.last_updated": "Antea novajo ye {date}",
"privacy_policy.title": "Privatesguidilo", "privacy_policy.title": "Privatesguidilo",
"recommended": "Rekomendata", "recommended": "Rekomendata",
@ -496,7 +687,9 @@
"relative_time.minutes": "{number}m", "relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s", "relative_time.seconds": "{number}s",
"relative_time.today": "hodie", "relative_time.today": "hodie",
"reply_indicator.attachments": "{count, plural, one {# atachajo} other {# atachaji}}",
"reply_indicator.cancel": "Nihiligar", "reply_indicator.cancel": "Nihiligar",
"reply_indicator.poll": "Votposto",
"report.block": "Restriktez", "report.block": "Restriktez",
"report.block_explanation": "Vu ne vidos olia posti. Oli ne povas vidar vua posti o sequar vu. Oli savos ke oli restriktesis.", "report.block_explanation": "Vu ne vidos olia posti. Oli ne povas vidar vua posti o sequar vu. Oli savos ke oli restriktesis.",
"report.categories.legal": "Legala", "report.categories.legal": "Legala",
@ -539,9 +732,13 @@
"report.unfollow_explanation": "Vu sequas ca konto. Por ne vidar olia posti en vua hemniuzeto pluse, desequez oli.", "report.unfollow_explanation": "Vu sequas ca konto. Por ne vidar olia posti en vua hemniuzeto pluse, desequez oli.",
"report_notification.attached_statuses": "{count, plural,one {{count} posti} other {{count} posti}} adjuntesas", "report_notification.attached_statuses": "{count, plural,one {{count} posti} other {{count} posti}} adjuntesas",
"report_notification.categories.legal": "Legala", "report_notification.categories.legal": "Legala",
"report_notification.categories.legal_sentence": "deslegala kontenajo",
"report_notification.categories.other": "Altra", "report_notification.categories.other": "Altra",
"report_notification.categories.other_sentence": "altra",
"report_notification.categories.spam": "Spamo", "report_notification.categories.spam": "Spamo",
"report_notification.categories.spam_sentence": "sendacho",
"report_notification.categories.violation": "Regulnesequo", "report_notification.categories.violation": "Regulnesequo",
"report_notification.categories.violation_sentence": "regulviolaco",
"report_notification.open": "Apertez raporto", "report_notification.open": "Apertez raporto",
"search.no_recent_searches": "Nula serchi recenta", "search.no_recent_searches": "Nula serchi recenta",
"search.placeholder": "Serchez", "search.placeholder": "Serchez",
@ -569,8 +766,11 @@
"server_banner.about_active_users": "Personi quo uzas ca servilo dum antea 30 dii (monate aktiva uzanti)", "server_banner.about_active_users": "Personi quo uzas ca servilo dum antea 30 dii (monate aktiva uzanti)",
"server_banner.active_users": "aktiva uzanti", "server_banner.active_users": "aktiva uzanti",
"server_banner.administered_by": "Administresis da:", "server_banner.administered_by": "Administresis da:",
"server_banner.is_one_of_many": "{domain} esas 1 de multa sendependa servili di Mastodon quan vu povas uzar por partoprenar en la fediverso.",
"server_banner.server_stats": "Servilstatistiko:", "server_banner.server_stats": "Servilstatistiko:",
"sign_in_banner.create_account": "Kreez konto", "sign_in_banner.create_account": "Kreez konto",
"sign_in_banner.follow_anyone": "On povas sequar ulu sur tota fediverso e vidar omno ye kronologia ordino. Nula algoritmi, reklami o klikatrapo omnaloke.",
"sign_in_banner.mastodon_is": "Mastodon esas la maxim bona voyo por saveskar eventi.",
"sign_in_banner.sign_in": "Enirez", "sign_in_banner.sign_in": "Enirez",
"sign_in_banner.sso_redirect": "Enirar o krear konto", "sign_in_banner.sso_redirect": "Enirar o krear konto",
"status.admin_account": "Apertez jerintervizajo por @{name}", "status.admin_account": "Apertez jerintervizajo por @{name}",
@ -580,14 +780,18 @@
"status.bookmark": "Libromarko", "status.bookmark": "Libromarko",
"status.cancel_reblog_private": "Desrepetez", "status.cancel_reblog_private": "Desrepetez",
"status.cannot_reblog": "Ca posto ne povas repetesar", "status.cannot_reblog": "Ca posto ne povas repetesar",
"status.continued_thread": "Durigita postaro",
"status.copy": "Copy link to status", "status.copy": "Copy link to status",
"status.delete": "Efacar", "status.delete": "Efacar",
"status.detailed_status": "Detala konversvido", "status.detailed_status": "Detala konversvido",
"status.direct": "Private mencionez @{name}", "status.direct": "Private mencionez @{name}",
"status.direct_indicator": "Privata menciono", "status.direct_indicator": "Privata menciono",
"status.edit": "Modifikez", "status.edit": "Modifikez",
"status.edited": "Recente modifikesis ye {date}",
"status.edited_x_times": "Modifikesis {count, plural, one {{count} foyo} other {{count} foyi}}", "status.edited_x_times": "Modifikesis {count, plural, one {{count} foyo} other {{count} foyi}}",
"status.embed": "Ganez adherkodexo",
"status.favourite": "Favorizar", "status.favourite": "Favorizar",
"status.favourites": "{count, plural, one {favorizo} other {favorizi}}",
"status.filter": "Filtragez ca posto", "status.filter": "Filtragez ca posto",
"status.history.created": "{name} kreis ye {date}", "status.history.created": "{name} kreis ye {date}",
"status.history.edited": "{name} modifikis ye {date}", "status.history.edited": "{name} modifikis ye {date}",
@ -606,9 +810,11 @@
"status.reblog": "Repetez", "status.reblog": "Repetez",
"status.reblog_private": "Repetez kun originala videbleso", "status.reblog_private": "Repetez kun originala videbleso",
"status.reblogged_by": "{name} repetis", "status.reblogged_by": "{name} repetis",
"status.reblogs": "{count, plural, one {repeto} other {repeti}}",
"status.reblogs.empty": "Nulu ja repetis ca posto. Kande ulu facas lo, lu montresos hike.", "status.reblogs.empty": "Nulu ja repetis ca posto. Kande ulu facas lo, lu montresos hike.",
"status.redraft": "Efacez e riskisigez", "status.redraft": "Efacez e riskisigez",
"status.remove_bookmark": "Efacez libromarko", "status.remove_bookmark": "Efacez libromarko",
"status.replied_in_thread": "Respondesis en postaro",
"status.replied_to": "Respondis a {name}", "status.replied_to": "Respondis a {name}",
"status.reply": "Respondar", "status.reply": "Respondar",
"status.replyAll": "Respondar a filo", "status.replyAll": "Respondar a filo",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Enginn frá þessum netþjóni getur fylgst með þér.", "domain_block_modal.they_cant_follow": "Enginn frá þessum netþjóni getur fylgst með þér.",
"domain_block_modal.they_wont_know": "Viðkomandi mun ekki vita að hann hafi verið útilokaður.", "domain_block_modal.they_wont_know": "Viðkomandi mun ekki vita að hann hafi verið útilokaður.",
"domain_block_modal.title": "Útiloka lén?", "domain_block_modal.title": "Útiloka lén?",
"domain_block_modal.you_will_lose_followers": "Allir fylgjendur þínir af þessum netþjóni verða fjarlægðir.",
"domain_block_modal.you_wont_see_posts": "Þú munt ekki sjá neinar færslur eða tilkynningar frá notendum á þessum netþjóni.", "domain_block_modal.you_wont_see_posts": "Þú munt ekki sjá neinar færslur eða tilkynningar frá notendum á þessum netþjóni.",
"domain_pill.activitypub_lets_connect": "Það gerir þér kleift að tengjast og eiga í samskiptum við fólk, ekki bara á Mastodon, heldur einnig á mörgum öðrum mismunandi samfélagsmiðlum.", "domain_pill.activitypub_lets_connect": "Það gerir þér kleift að tengjast og eiga í samskiptum við fólk, ekki bara á Mastodon, heldur einnig á mörgum öðrum mismunandi samfélagsmiðlum.",
"domain_pill.activitypub_like_language": "ActivityPub er eins og tungumál sem Mastodon notar til að tala við önnur samfélagsnet.", "domain_pill.activitypub_like_language": "ActivityPub er eins og tungumál sem Mastodon notar til að tala við önnur samfélagsnet.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Limitazione per eccesso di richieste", "alert.rate_limited.title": "Limitazione per eccesso di richieste",
"alert.unexpected.message": "Si è verificato un errore imprevisto.", "alert.unexpected.message": "Si è verificato un errore imprevisto.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"alt_text_badge.title": "Testo alternativo",
"announcement.announcement": "Annuncio", "announcement.announcement": "Annuncio",
"attachments_list.unprocessed": "(non elaborato)", "attachments_list.unprocessed": "(non elaborato)",
"audio.hide": "Nascondi audio", "audio.hide": "Nascondi audio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Nessuno da questo server può seguirti.", "domain_block_modal.they_cant_follow": "Nessuno da questo server può seguirti.",
"domain_block_modal.they_wont_know": "Non sapranno di essere stati bloccati.", "domain_block_modal.they_wont_know": "Non sapranno di essere stati bloccati.",
"domain_block_modal.title": "Bloccare il dominio?", "domain_block_modal.title": "Bloccare il dominio?",
"domain_block_modal.you_will_lose_followers": "Tutti i tuoi seguaci da questo server verranno rimossi.", "domain_block_modal.you_will_lose_num_followers": "Perderai {followersCount, plural, one {{followersCountDisplay} seguace} other {{followersCountDisplay} seguaci}} e {followingCount, plural, one {{followingCountDisplay} persona che segui} other {{followingCountDisplay} persone che segui}}.",
"domain_block_modal.you_will_lose_relationships": "Perderai tutti i seguaci e le persone che segui da questo server.",
"domain_block_modal.you_wont_see_posts": "Non vedrai post o notifiche dagli utenti su questo server.", "domain_block_modal.you_wont_see_posts": "Non vedrai post o notifiche dagli utenti su questo server.",
"domain_pill.activitypub_lets_connect": "Ti consente di connetterti e interagire con le persone non solo su Mastodon, ma anche su diverse app social.", "domain_pill.activitypub_lets_connect": "Ti consente di connetterti e interagire con le persone non solo su Mastodon, ma anche su diverse app social.",
"domain_pill.activitypub_like_language": "ActivityPub è come la lingua che Mastodon parla con altri social network.", "domain_pill.activitypub_like_language": "ActivityPub è come la lingua che Mastodon parla con altri social network.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "このサーバーのユーザーはあなたをフォローできなくなります。", "domain_block_modal.they_cant_follow": "このサーバーのユーザーはあなたをフォローできなくなります。",
"domain_block_modal.they_wont_know": "ドメインブロックは相手からはわかりません。", "domain_block_modal.they_wont_know": "ドメインブロックは相手からはわかりません。",
"domain_block_modal.title": "ドメインをブロックしますか?", "domain_block_modal.title": "ドメインをブロックしますか?",
"domain_block_modal.you_will_lose_followers": "このサーバーのフォロワーはすべてフォロー解除されます。",
"domain_block_modal.you_wont_see_posts": "このサーバーのユーザーからの投稿や通知が閲覧できなくなります。", "domain_block_modal.you_wont_see_posts": "このサーバーのユーザーからの投稿や通知が閲覧できなくなります。",
"domain_pill.activitypub_lets_connect": "Mastodonからほかのソーシャルアプリのユーザーへ、そのまた別のアプリのユーザーへと、それぞれが互いにつながり関わり合うことをこのActivityPubの仕組みが実現しています。", "domain_pill.activitypub_lets_connect": "Mastodonからほかのソーシャルアプリのユーザーへ、そのまた別のアプリのユーザーへと、それぞれが互いにつながり関わり合うことをこのActivityPubの仕組みが実現しています。",
"domain_pill.activitypub_like_language": "ActivityPubとは、Mastodonがほかのサーバーと会話をするときにしゃべる「言葉」のようなものです。", "domain_pill.activitypub_like_language": "ActivityPubとは、Mastodonがほかのサーバーと会話をするときにしゃべる「言葉」のようなものです。",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "이 서버의 누구도 나를 팔로우 할 수 없습니다.", "domain_block_modal.they_cant_follow": "이 서버의 누구도 나를 팔로우 할 수 없습니다.",
"domain_block_modal.they_wont_know": "내가 차단했다는 사실을 모를 것입니다.", "domain_block_modal.they_wont_know": "내가 차단했다는 사실을 모를 것입니다.",
"domain_block_modal.title": "도메인을 차단할까요?", "domain_block_modal.title": "도메인을 차단할까요?",
"domain_block_modal.you_will_lose_followers": "이 서버에 있는 팔로워들이 모두 제거될 것입니다.",
"domain_block_modal.you_wont_see_posts": "이 서버 사용자의 게시물이나 알림을 보지 않게 됩니다.", "domain_block_modal.you_wont_see_posts": "이 서버 사용자의 게시물이나 알림을 보지 않게 됩니다.",
"domain_pill.activitypub_lets_connect": "이것은 마스토돈 뿐만이 아니라 다른 소셜 앱들을 넘나들며 사람들을 연결하고 상호작용 할 수 있게 합니다.", "domain_pill.activitypub_lets_connect": "이것은 마스토돈 뿐만이 아니라 다른 소셜 앱들을 넘나들며 사람들을 연결하고 상호작용 할 수 있게 합니다.",
"domain_pill.activitypub_like_language": "액티비티펍은 마스토돈이 다른 소셜 네트워크와 대화할 때 쓰는 언어 같은 것입니다.", "domain_pill.activitypub_like_language": "액티비티펍은 마스토돈이 다른 소셜 네트워크와 대화할 때 쓰는 언어 같은 것입니다.",

View file

@ -58,7 +58,6 @@
"disabled_account_banner.text": "Ratio tua {disabledAccount} debilitata est.", "disabled_account_banner.text": "Ratio tua {disabledAccount} debilitata est.",
"dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
"dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
"domain_block_modal.you_will_lose_followers": "Omnes sectatores tuī ex hoc servō removēbuntur.",
"domain_block_modal.you_wont_see_posts": "Nuntios aut notificātiōnēs ab usoribus in hōc servō nōn vidēbis.", "domain_block_modal.you_wont_see_posts": "Nuntios aut notificātiōnēs ab usoribus in hōc servō nōn vidēbis.",
"domain_pill.activitypub_like_language": "ActivityPub est velut lingua quam Mastodon cum aliīs sociālibus rētibus loquitur.", "domain_pill.activitypub_like_language": "ActivityPub est velut lingua quam Mastodon cum aliīs sociālibus rētibus loquitur.",
"domain_pill.your_handle": "Tuus nominulus:", "domain_pill.your_handle": "Tuus nominulus:",

View file

@ -215,7 +215,6 @@
"domain_block_modal.they_cant_follow": "Dingun de este sirvidor puede segirte.", "domain_block_modal.they_cant_follow": "Dingun de este sirvidor puede segirte.",
"domain_block_modal.they_wont_know": "No savra ke tiene sido blokado.", "domain_block_modal.they_wont_know": "No savra ke tiene sido blokado.",
"domain_block_modal.title": "Bloka el domeno?", "domain_block_modal.title": "Bloka el domeno?",
"domain_block_modal.you_will_lose_followers": "Se efasaran todos tus suivantes de este sirvidor.",
"domain_block_modal.you_wont_see_posts": "No veras publikasyones ni avizos de utilizadores en este sirvidor.", "domain_block_modal.you_wont_see_posts": "No veras publikasyones ni avizos de utilizadores en este sirvidor.",
"domain_pill.server": "Sirvidor", "domain_pill.server": "Sirvidor",
"domain_pill.their_handle": "Su alias:", "domain_pill.their_handle": "Su alias:",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Sparta apribota.", "alert.rate_limited.title": "Sparta apribota.",
"alert.unexpected.message": "Įvyko netikėta klaida.", "alert.unexpected.message": "Įvyko netikėta klaida.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"alt_text_badge.title": "Alternatyvus tekstas",
"announcement.announcement": "Skelbimas", "announcement.announcement": "Skelbimas",
"attachments_list.unprocessed": "(neapdorotas)", "attachments_list.unprocessed": "(neapdorotas)",
"audio.hide": "Slėpti garsą", "audio.hide": "Slėpti garsą",
@ -98,7 +99,7 @@
"block_modal.you_wont_see_mentions": "Nematysi įrašus, kuriuose jie paminimi.", "block_modal.you_wont_see_mentions": "Nematysi įrašus, kuriuose jie paminimi.",
"boost_modal.combo": "Galima paspausti {combo}, kad praleisti tai kitą kartą", "boost_modal.combo": "Galima paspausti {combo}, kad praleisti tai kitą kartą",
"boost_modal.reblog": "Pasidalinti įrašą?", "boost_modal.reblog": "Pasidalinti įrašą?",
"boost_modal.undo_reblog": "Panaikinti pasidalintą įrašą?", "boost_modal.undo_reblog": "Nebepasidalinti įrašo?",
"bundle_column_error.copy_stacktrace": "Kopijuoti klaidos ataskaitą", "bundle_column_error.copy_stacktrace": "Kopijuoti klaidos ataskaitą",
"bundle_column_error.error.body": "Paprašytos puslapio nepavyko atvaizduoti. Tai gali būti dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos.", "bundle_column_error.error.body": "Paprašytos puslapio nepavyko atvaizduoti. Tai gali būti dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos.",
"bundle_column_error.error.title": "O, ne!", "bundle_column_error.error.title": "O, ne!",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Niekas iš šio serverio negali tavęs sekti.", "domain_block_modal.they_cant_follow": "Niekas iš šio serverio negali tavęs sekti.",
"domain_block_modal.they_wont_know": "Jie nežinos, kad buvo užblokuoti.", "domain_block_modal.they_wont_know": "Jie nežinos, kad buvo užblokuoti.",
"domain_block_modal.title": "Blokuoti serverį?", "domain_block_modal.title": "Blokuoti serverį?",
"domain_block_modal.you_will_lose_followers": "Visi tavo sekėjai iš šio serverio bus pašalinti.", "domain_block_modal.you_will_lose_num_followers": "Prarasi {followersCount, plural, one {{followersCountDisplay} sekėją} few {{followersCountDisplay} sekėjus} many {{followersCountDisplay} sekėjo} other {{followersCountDisplay} sekėjų}} ir {followingCount, plural, one {{followingCountDisplay} žmogų, kurį seki} few {{followingCountDisplay} žmones, kuriuos seki} many {{followingCountDisplay} žmonės, kuriuos seki} other {{followingCountDisplay} žmonių, kurių seki}}.",
"domain_block_modal.you_will_lose_relationships": "Prarasi visus sekėjus ir žmones, kuriuos seki iš šio serverio.",
"domain_block_modal.you_wont_see_posts": "Nematysi naudotojų įrašų ar pranešimų šiame serveryje.", "domain_block_modal.you_wont_see_posts": "Nematysi naudotojų įrašų ar pranešimų šiame serveryje.",
"domain_pill.activitypub_lets_connect": "Tai leidžia tau prisijungti ir bendrauti su žmonėmis ne tik „Mastodon“ platformoje, bet ir įvairiose socialinėse programėlėse.", "domain_pill.activitypub_lets_connect": "Tai leidžia tau prisijungti ir bendrauti su žmonėmis ne tik „Mastodon“ platformoje, bet ir įvairiose socialinėse programėlėse.",
"domain_pill.activitypub_like_language": "„ActivityPub“ tai tarsi kalba, kuria „Mastodon“ kalba su kitais socialiniais tinklais.", "domain_pill.activitypub_like_language": "„ActivityPub“ tai tarsi kalba, kuria „Mastodon“ kalba su kitais socialiniais tinklais.",
@ -766,7 +768,7 @@
"status.admin_status": "Atidaryti šį įrašą prižiūrėjimo sąsajoje", "status.admin_status": "Atidaryti šį įrašą prižiūrėjimo sąsajoje",
"status.block": "Blokuoti @{name}", "status.block": "Blokuoti @{name}",
"status.bookmark": "Pridėti į žymės", "status.bookmark": "Pridėti į žymės",
"status.cancel_reblog_private": "Nebepakelti", "status.cancel_reblog_private": "Nebepasidalinti",
"status.cannot_reblog": "Šis įrašas negali būti pakeltas.", "status.cannot_reblog": "Šis įrašas negali būti pakeltas.",
"status.continued_thread": "Tęsiama gijoje", "status.continued_thread": "Tęsiama gijoje",
"status.copy": "Kopijuoti nuorodą į įrašą", "status.copy": "Kopijuoti nuorodą į įrašą",

View file

@ -215,7 +215,6 @@
"domain_block_modal.they_cant_follow": "Neviens šajā serverī nevar Tev sekot.", "domain_block_modal.they_cant_follow": "Neviens šajā serverī nevar Tev sekot.",
"domain_block_modal.they_wont_know": "Viņi nezinās, ka tikuši bloķēti.", "domain_block_modal.they_wont_know": "Viņi nezinās, ka tikuši bloķēti.",
"domain_block_modal.title": "Bloķēt domēnu?", "domain_block_modal.title": "Bloķēt domēnu?",
"domain_block_modal.you_will_lose_followers": "Tiks noņemti visi tavi sekotāji no šī servera.",
"domain_pill.server": "Serveris", "domain_pill.server": "Serveris",
"domain_pill.username": "Lietotājvārds", "domain_pill.username": "Lietotājvārds",
"embed.instructions": "Iestrādā šo ziņu savā mājaslapā, kopējot zemāk redzamo kodu.", "embed.instructions": "Iestrādā šo ziņu savā mājaslapā, kopējot zemāk redzamo kodu.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Dataverkeer beperkt", "alert.rate_limited.title": "Dataverkeer beperkt",
"alert.unexpected.message": "Er deed zich een onverwachte fout voor", "alert.unexpected.message": "Er deed zich een onverwachte fout voor",
"alert.unexpected.title": "Oeps!", "alert.unexpected.title": "Oeps!",
"alt_text_badge.title": "Alt-tekst",
"announcement.announcement": "Mededeling", "announcement.announcement": "Mededeling",
"attachments_list.unprocessed": "(niet verwerkt)", "attachments_list.unprocessed": "(niet verwerkt)",
"audio.hide": "Audio verbergen", "audio.hide": "Audio verbergen",
@ -216,12 +217,13 @@
"dismissable_banner.explore_tags": "Deze hashtags winnen aan populariteit op het sociale web (fediverse). Hashtags die door meer verschillende mensen worden gebruikt staan hoger.", "dismissable_banner.explore_tags": "Deze hashtags winnen aan populariteit op het sociale web (fediverse). Hashtags die door meer verschillende mensen worden gebruikt staan hoger.",
"dismissable_banner.public_timeline": "Dit zijn de meest recente openbare berichten van accounts op het sociale web (fediverse) die door mensen op {domain} worden gevolgd.", "dismissable_banner.public_timeline": "Dit zijn de meest recente openbare berichten van accounts op het sociale web (fediverse) die door mensen op {domain} worden gevolgd.",
"domain_block_modal.block": "Server blokkeren", "domain_block_modal.block": "Server blokkeren",
"domain_block_modal.block_account_instead": "In plaats hiervan {name} blokkeren", "domain_block_modal.block_account_instead": "Alleen {name} blokkeren",
"domain_block_modal.they_can_interact_with_old_posts": "Mensen op deze server kunnen interactie hebben met jouw oude berichten.", "domain_block_modal.they_can_interact_with_old_posts": "Mensen op deze server kunnen interactie hebben met jouw oude berichten.",
"domain_block_modal.they_cant_follow": "Niemand op deze server kan jou volgen.", "domain_block_modal.they_cant_follow": "Niemand op deze server kan jou volgen.",
"domain_block_modal.they_wont_know": "Ze krijgen niet te weten dat ze worden geblokkeerd.", "domain_block_modal.they_wont_know": "Ze krijgen niet te weten dat ze worden geblokkeerd.",
"domain_block_modal.title": "Server blokkeren?", "domain_block_modal.title": "Server blokkeren?",
"domain_block_modal.you_will_lose_followers": "Al jouw volgers van deze server worden ontvolgd.", "domain_block_modal.you_will_lose_num_followers": "Je verliest {followersCount, plural, one {{followersCountDisplay} volger} other {{followersCountDisplay} volgers}} en {followingCount, plural, one {{followingCountDisplay} persoon die jij volgt} other {{followingCountDisplay} personen die jij volgt}}.",
"domain_block_modal.you_will_lose_relationships": "Je verliest alle volgers en mensen die je volgt van deze server.",
"domain_block_modal.you_wont_see_posts": "Je ziet geen berichten of meldingen meer van gebruikers op deze server.", "domain_block_modal.you_wont_see_posts": "Je ziet geen berichten of meldingen meer van gebruikers op deze server.",
"domain_pill.activitypub_lets_connect": "Het zorgt ervoor dat je niet alleen maar kunt verbinden en communiceren met mensen op Mastodon, maar ook met andere sociale apps.", "domain_pill.activitypub_lets_connect": "Het zorgt ervoor dat je niet alleen maar kunt verbinden en communiceren met mensen op Mastodon, maar ook met andere sociale apps.",
"domain_pill.activitypub_like_language": "ActivityPub is de taal die Mastodon met andere sociale netwerken spreekt.", "domain_pill.activitypub_like_language": "ActivityPub is de taal die Mastodon met andere sociale netwerken spreekt.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Ingen på denne tenaren kan fylgja deg.", "domain_block_modal.they_cant_follow": "Ingen på denne tenaren kan fylgja deg.",
"domain_block_modal.they_wont_know": "Dei veit ikkje at dei er blokkerte.", "domain_block_modal.they_wont_know": "Dei veit ikkje at dei er blokkerte.",
"domain_block_modal.title": "Blokker domenet?", "domain_block_modal.title": "Blokker domenet?",
"domain_block_modal.you_will_lose_followers": "Alle fylgjarane dine frå denne tenaren blir fjerna.",
"domain_block_modal.you_wont_see_posts": "Du vil ikkje sjå innlegg eller varslingar frå brukarar på denne tenaren.", "domain_block_modal.you_wont_see_posts": "Du vil ikkje sjå innlegg eller varslingar frå brukarar på denne tenaren.",
"domain_pill.activitypub_lets_connect": "Den lar deg kople til og samhandle med folk ikkje berre på Mastodon, men òg på tvers av forskjellige sosiale appar.", "domain_pill.activitypub_lets_connect": "Den lar deg kople til og samhandle med folk ikkje berre på Mastodon, men òg på tvers av forskjellige sosiale appar.",
"domain_pill.activitypub_like_language": "ActivityPub er som språket Mastodon snakkar med andre sosiale nettverk.", "domain_pill.activitypub_like_language": "ActivityPub er som språket Mastodon snakkar med andre sosiale nettverk.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Ingen fra denne serveren kan følge deg.", "domain_block_modal.they_cant_follow": "Ingen fra denne serveren kan følge deg.",
"domain_block_modal.they_wont_know": "De kommer ikke til å få vite at du har valgt å blokkere dem.", "domain_block_modal.they_wont_know": "De kommer ikke til å få vite at du har valgt å blokkere dem.",
"domain_block_modal.title": "Blokker domenet?", "domain_block_modal.title": "Blokker domenet?",
"domain_block_modal.you_will_lose_followers": "Alle dine følgere fra denne serveren vil bli fjernet.",
"domain_block_modal.you_wont_see_posts": "Du vil ikke se innlegg eller få varsler fra brukere på denne serveren.", "domain_block_modal.you_wont_see_posts": "Du vil ikke se innlegg eller få varsler fra brukere på denne serveren.",
"domain_pill.activitypub_lets_connect": "Den lar deg koble til og samhandle med folk ikke bare på Mastodon, men også på tvers av forskjellige sosiale apper.", "domain_pill.activitypub_lets_connect": "Den lar deg koble til og samhandle med folk ikke bare på Mastodon, men også på tvers av forskjellige sosiale apper.",
"domain_pill.activitypub_like_language": "ActivityPub er liksom språket Mastodon snakker med andre sosiale nettverk.", "domain_pill.activitypub_like_language": "ActivityPub er liksom språket Mastodon snakker med andre sosiale nettverk.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Ograniczenie liczby zapytań", "alert.rate_limited.title": "Ograniczenie liczby zapytań",
"alert.unexpected.message": "Wystąpił nieoczekiwany błąd.", "alert.unexpected.message": "Wystąpił nieoczekiwany błąd.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"alt_text_badge.title": "Tekst alternatywny",
"announcement.announcement": "Ogłoszenie", "announcement.announcement": "Ogłoszenie",
"attachments_list.unprocessed": "(nieprzetworzone)", "attachments_list.unprocessed": "(nieprzetworzone)",
"audio.hide": "Ukryj dźwięk", "audio.hide": "Ukryj dźwięk",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Nikt z tego serwera nie może Cię obserwować.", "domain_block_modal.they_cant_follow": "Nikt z tego serwera nie może Cię obserwować.",
"domain_block_modal.they_wont_know": "Użytkownik nie dowie się, że został zablokowany.", "domain_block_modal.they_wont_know": "Użytkownik nie dowie się, że został zablokowany.",
"domain_block_modal.title": "Zablokować domenę?", "domain_block_modal.title": "Zablokować domenę?",
"domain_block_modal.you_will_lose_followers": "Wszyscy twoi obserwujący z tego serwera zostaną usunięci.", "domain_block_modal.you_will_lose_num_followers": "Utracisz {followersCount, plural, one {jednego obserwującego} other {{followersCountDisplay} obserwujących}} i {followingCount, plural, one {jedną osobę którą obserwujesz} few {{followingCountDisplay} osoby które obserwujesz} other {{followingCountDisplay} osób które obserwujesz}}.",
"domain_block_modal.you_will_lose_relationships": "Utracisz wszystkich obserwujących z tego serwera i wszystkie osoby które obserwujesz na tym serwerze.",
"domain_block_modal.you_wont_see_posts": "Nie zobaczysz postów ani powiadomień od użytkowników na tym serwerze.", "domain_block_modal.you_wont_see_posts": "Nie zobaczysz postów ani powiadomień od użytkowników na tym serwerze.",
"domain_pill.activitypub_lets_connect": "Pozwala połączyć się z ludźmi na Mastodonie, jak i na innych serwisach społecznościowych.", "domain_pill.activitypub_lets_connect": "Pozwala połączyć się z ludźmi na Mastodonie, jak i na innych serwisach społecznościowych.",
"domain_pill.activitypub_like_language": "ActivityPub jest językiem używanym przez Mastodon do wymiany danych z innymi serwisami społecznościowymi.", "domain_pill.activitypub_like_language": "ActivityPub jest językiem używanym przez Mastodon do wymiany danych z innymi serwisami społecznościowymi.",

View file

@ -85,6 +85,7 @@
"alert.rate_limited.title": "Tentativas limitadas", "alert.rate_limited.title": "Tentativas limitadas",
"alert.unexpected.message": "Ocorreu um erro inesperado.", "alert.unexpected.message": "Ocorreu um erro inesperado.",
"alert.unexpected.title": "Eita!", "alert.unexpected.title": "Eita!",
"alt_text_badge.title": "Texto alternativo",
"announcement.announcement": "Comunicados", "announcement.announcement": "Comunicados",
"attachments_list.unprocessed": "(não processado)", "attachments_list.unprocessed": "(não processado)",
"audio.hide": "Ocultar áudio", "audio.hide": "Ocultar áudio",
@ -221,7 +222,8 @@
"domain_block_modal.they_cant_follow": "Ninguém deste servidor pode lhe seguir.", "domain_block_modal.they_cant_follow": "Ninguém deste servidor pode lhe seguir.",
"domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.", "domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.",
"domain_block_modal.title": "Dominio do bloco", "domain_block_modal.title": "Dominio do bloco",
"domain_block_modal.you_will_lose_followers": "Todos os seus seguidores deste servidor serão removidos.", "domain_block_modal.you_will_lose_num_followers": "Você perderá {followersCount, plural, one {{followersCountDisplay} seguidor} other {{followersCountDisplay} seguidores}} e {followingCount, plural, one {{followingCountDisplay} pessoa que você segue} other {{followingCountDisplay} pessoas que você segue}}.",
"domain_block_modal.you_will_lose_relationships": "Você irá perder todos os seguidores e pessoas que você segue neste servidor.",
"domain_block_modal.you_wont_see_posts": "Você não verá postagens ou notificações de usuários neste servidor.", "domain_block_modal.you_wont_see_posts": "Você não verá postagens ou notificações de usuários neste servidor.",
"domain_pill.activitypub_lets_connect": "Ele permite que você se conecte e interaja com pessoas não apenas no Mastodon, mas também em diferentes aplicativos sociais.", "domain_pill.activitypub_lets_connect": "Ele permite que você se conecte e interaja com pessoas não apenas no Mastodon, mas também em diferentes aplicativos sociais.",
"domain_pill.activitypub_like_language": "ActivityPub é como a linguagem que o Mastodon fala com outras redes sociais.", "domain_pill.activitypub_like_language": "ActivityPub é como a linguagem que o Mastodon fala com outras redes sociais.",

View file

@ -221,7 +221,6 @@
"domain_block_modal.they_cant_follow": "Ninguém deste servidor pode segui-lo.", "domain_block_modal.they_cant_follow": "Ninguém deste servidor pode segui-lo.",
"domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.", "domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.",
"domain_block_modal.title": "Bloquear domínio?", "domain_block_modal.title": "Bloquear domínio?",
"domain_block_modal.you_will_lose_followers": "Todos os seus seguidores deste servidor serão removidos.",
"domain_block_modal.you_wont_see_posts": "Não verá publicações ou notificações de utilizadores neste servidor.", "domain_block_modal.you_wont_see_posts": "Não verá publicações ou notificações de utilizadores neste servidor.",
"domain_pill.activitypub_lets_connect": "Permite-lhe conectar e interagir com pessoas não só no Mastodon, mas também em diferentes aplicações sociais.", "domain_pill.activitypub_lets_connect": "Permite-lhe conectar e interagir com pessoas não só no Mastodon, mas também em diferentes aplicações sociais.",
"domain_pill.activitypub_like_language": "O ActivityPub é como a linguagem que o Mastodon fala com outras redes sociais.", "domain_pill.activitypub_like_language": "O ActivityPub é como a linguagem que o Mastodon fala com outras redes sociais.",

View file

@ -220,7 +220,6 @@
"domain_block_modal.they_cant_follow": "Никто из этого сервера не может подписываться на вас.", "domain_block_modal.they_cant_follow": "Никто из этого сервера не может подписываться на вас.",
"domain_block_modal.they_wont_know": "Он не будет знать, что его заблокировали.", "domain_block_modal.they_wont_know": "Он не будет знать, что его заблокировали.",
"domain_block_modal.title": "Заблокировать домен?", "domain_block_modal.title": "Заблокировать домен?",
"domain_block_modal.you_will_lose_followers": "Все ваши подписчики с этого сервера будут удалены.",
"domain_block_modal.you_wont_see_posts": "Вы не будете видеть записи или уведомления от пользователей на этом сервере.", "domain_block_modal.you_wont_see_posts": "Вы не будете видеть записи или уведомления от пользователей на этом сервере.",
"domain_pill.activitypub_lets_connect": "Это позволяет вам общаться и взаимодействовать с людьми не только на Mastodon, но и в различных социальных приложениях.", "domain_pill.activitypub_lets_connect": "Это позволяет вам общаться и взаимодействовать с людьми не только на Mastodon, но и в различных социальных приложениях.",
"domain_pill.activitypub_like_language": "ActivityPub как язык Mastodon говорит с другими социальными сетями.", "domain_pill.activitypub_like_language": "ActivityPub как язык Mastodon говорит с другими социальными сетями.",

View file

@ -34,7 +34,9 @@
"account.follow_back": "Sledovať späť", "account.follow_back": "Sledovať späť",
"account.followers": "Sledovatelia", "account.followers": "Sledovatelia",
"account.followers.empty": "Tento účet ešte nikto nesleduje.", "account.followers.empty": "Tento účet ešte nikto nesleduje.",
"account.followers_counter": "{count, plural, one {{counter} sledujúci} other {{counter} sledujúci}}",
"account.following": "Sledovaný účet", "account.following": "Sledovaný účet",
"account.following_counter": "{count, plural, one {{counter} sledovaných} other {{counter} sledovaných}}",
"account.follows.empty": "Tento účet ešte nikoho nesleduje.", "account.follows.empty": "Tento účet ešte nikoho nesleduje.",
"account.go_to_profile": "Prejsť na profil", "account.go_to_profile": "Prejsť na profil",
"account.hide_reblogs": "Skryť zdieľania od @{name}", "account.hide_reblogs": "Skryť zdieľania od @{name}",
@ -60,6 +62,7 @@
"account.requested_follow": "{name} vás chce sledovať", "account.requested_follow": "{name} vás chce sledovať",
"account.share": "Zdieľaj profil @{name}", "account.share": "Zdieľaj profil @{name}",
"account.show_reblogs": "Zobrazovať zdieľania od @{name}", "account.show_reblogs": "Zobrazovať zdieľania od @{name}",
"account.statuses_counter": "{count, plural, one {{counter} príspevok} other {{counter} príspevkov}}",
"account.unblock": "Odblokovať @{name}", "account.unblock": "Odblokovať @{name}",
"account.unblock_domain": "Odblokovať doménu {domain}", "account.unblock_domain": "Odblokovať doménu {domain}",
"account.unblock_short": "Odblokovať", "account.unblock_short": "Odblokovať",
@ -215,7 +218,6 @@
"domain_block_modal.they_cant_follow": "Nikto z tohoto servera ťa nemôže nasledovať.", "domain_block_modal.they_cant_follow": "Nikto z tohoto servera ťa nemôže nasledovať.",
"domain_block_modal.they_wont_know": "Nebude vedieť, že bol/a zablokovaný/á.", "domain_block_modal.they_wont_know": "Nebude vedieť, že bol/a zablokovaný/á.",
"domain_block_modal.title": "Blokovať doménu?", "domain_block_modal.title": "Blokovať doménu?",
"domain_block_modal.you_will_lose_followers": "Všetci tvoji nasledovatelia z tohto servera budú odstránení.",
"domain_block_modal.you_wont_see_posts": "Neuvidíš príspevky, ani oboznámenia od užívateľov na tomto serveri.", "domain_block_modal.you_wont_see_posts": "Neuvidíš príspevky, ani oboznámenia od užívateľov na tomto serveri.",
"domain_pill.activitypub_like_language": "ActivityPub je ako jazyk, ktorým Mastodon hovorí s ostatnými sociálnymi sieťami.", "domain_pill.activitypub_like_language": "ActivityPub je ako jazyk, ktorým Mastodon hovorí s ostatnými sociálnymi sieťami.",
"domain_pill.server": "Server", "domain_pill.server": "Server",
@ -418,6 +420,7 @@
"lists.subheading": "Vaše zoznamy", "lists.subheading": "Vaše zoznamy",
"load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položiek} other {# nových položiek}}", "load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položiek} other {# nových položiek}}",
"loading_indicator.label": "Načítavanie…", "loading_indicator.label": "Načítavanie…",
"media_gallery.hide": "Skryť",
"moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálne deaktivovaný, pretože ste sa presunuli na {movedToAccount}.", "moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálne deaktivovaný, pretože ste sa presunuli na {movedToAccount}.",
"mute_modal.hide_from_notifications": "Ukryť z upozornení", "mute_modal.hide_from_notifications": "Ukryť z upozornení",
"mute_modal.hide_options": "Skryť možnosti", "mute_modal.hide_options": "Skryť možnosti",

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