mirror of
https://github.com/mastodon/mastodon.git
synced 2025-09-05 17:31:12 +00:00
Merge branch 'main' into compose-language-detection
This commit is contained in:
commit
c1d09c8d6e
|
@ -1,17 +1,21 @@
|
|||
---
|
||||
Metrics/AbcSize:
|
||||
Exclude:
|
||||
- lib/mastodon/cli/*.rb
|
||||
Enabled: false
|
||||
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/BlockNesting:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/CollectionLiteralLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
- lib/mastodon/cli/*.rb
|
||||
Enabled: false
|
||||
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
|
@ -20,4 +24,7 @@ Metrics/ModuleLength:
|
|||
Enabled: false
|
||||
|
||||
Metrics/ParameterLists:
|
||||
CountKeywordArgs: false
|
||||
Enabled: false
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: false
|
||||
|
|
|
@ -6,23 +6,6 @@
|
|||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||
Metrics/AbcSize:
|
||||
Max: 82
|
||||
|
||||
# Configuration parameters: CountBlocks, CountModifierForms, Max.
|
||||
Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'lib/tasks/mastodon.rake'
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 25
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 27
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowedVars, DefaultToNil.
|
||||
Style/FetchEnvVar:
|
||||
|
|
|
@ -806,10 +806,10 @@ GEM
|
|||
ruby-saml (1.18.1)
|
||||
nokogiri (>= 1.13.10)
|
||||
rexml
|
||||
ruby-vips (2.2.4)
|
||||
ruby-vips (2.2.5)
|
||||
ffi (~> 1.12)
|
||||
logger
|
||||
rubyzip (3.0.1)
|
||||
rubyzip (3.0.2)
|
||||
rufus-scheduler (3.9.2)
|
||||
fugit (~> 1.1, >= 1.11.1)
|
||||
safety_net_attestation (0.4.0)
|
||||
|
|
|
@ -102,6 +102,16 @@ module ApplicationHelper
|
|||
policy(record).public_send(:"#{action}?")
|
||||
end
|
||||
|
||||
def conditional_link_to(condition, name, options = {}, html_options = {}, &block)
|
||||
if condition && !current_page?(block_given? ? name : options)
|
||||
link_to(name, options, html_options, &block)
|
||||
elsif block_given?
|
||||
content_tag(:span, options, html_options, &block)
|
||||
else
|
||||
content_tag(:span, name, html_options)
|
||||
end
|
||||
end
|
||||
|
||||
def material_symbol(icon, attributes = {})
|
||||
safe_join(
|
||||
[
|
||||
|
|
|
@ -84,6 +84,7 @@ export const COMPOSE_FOCUS = 'COMPOSE_FOCUS';
|
|||
const messages = defineMessages({
|
||||
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
||||
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
||||
uploadQuote: { id: 'upload_error.quote', defaultMessage: 'File upload not allowed with quotes.' },
|
||||
open: { id: 'compose.published.open', defaultMessage: 'Open' },
|
||||
published: { id: 'compose.published.body', defaultMessage: 'Post published.' },
|
||||
saved: { id: 'compose.saved.body', defaultMessage: 'Post saved.' },
|
||||
|
@ -146,7 +147,7 @@ export function resetCompose() {
|
|||
};
|
||||
}
|
||||
|
||||
export const focusCompose = (defaultText) => (dispatch, getState) => {
|
||||
export const focusCompose = (defaultText = '') => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: COMPOSE_FOCUS,
|
||||
defaultText,
|
||||
|
@ -303,6 +304,11 @@ export function submitComposeFail(error) {
|
|||
|
||||
export function uploadCompose(files) {
|
||||
return function (dispatch, getState) {
|
||||
// Exit if there's a quote.
|
||||
if (getState().compose.get('quoted_status_id')) {
|
||||
dispatch(showAlert({ message: messages.uploadQuote }));
|
||||
return;
|
||||
}
|
||||
const uploadLimit = getState().getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']);
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const pending = getState().getIn(['compose', 'pending_media_attachments']);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import type { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
|
||||
|
@ -12,7 +14,27 @@ import {
|
|||
import type { ApiQuotePolicy } from '../api_types/quotes';
|
||||
import type { Status } from '../models/status';
|
||||
|
||||
import { ensureComposeIsVisible } from './compose';
|
||||
import { showAlert } from './alerts';
|
||||
import { focusCompose } from './compose';
|
||||
|
||||
const messages = defineMessages({
|
||||
quoteErrorUpload: {
|
||||
id: 'quote_error.upload',
|
||||
defaultMessage: 'Quoting is not allowed with media attachments.',
|
||||
},
|
||||
quoteErrorPoll: {
|
||||
id: 'quote_error.poll',
|
||||
defaultMessage: 'Quoting is not allowed with polls.',
|
||||
},
|
||||
quoteErrorQuote: {
|
||||
id: 'quote_error.quote',
|
||||
defaultMessage: 'Only one quote at a time is allowed.',
|
||||
},
|
||||
quoteErrorUnauthorized: {
|
||||
id: 'quote_error.unauthorized',
|
||||
defaultMessage: 'You are not authorized to quote this post.',
|
||||
},
|
||||
});
|
||||
|
||||
type SimulatedMediaAttachmentJSON = ApiMediaAttachmentJSON & {
|
||||
unattached?: boolean;
|
||||
|
@ -78,14 +100,43 @@ export const changeUploadCompose = createDataLoadingThunk(
|
|||
},
|
||||
);
|
||||
|
||||
export const quoteComposeByStatus = createAppThunk(
|
||||
export const quoteCompose = createAppThunk(
|
||||
'compose/quoteComposeStatus',
|
||||
(status: Status, { getState }) => {
|
||||
ensureComposeIsVisible(getState);
|
||||
(status: Status, { dispatch }) => {
|
||||
dispatch(focusCompose());
|
||||
return status;
|
||||
},
|
||||
);
|
||||
|
||||
export const quoteComposeByStatus = createAppThunk(
|
||||
(status: Status, { dispatch, getState }) => {
|
||||
const composeState = getState().compose;
|
||||
const mediaAttachments = composeState.get('media_attachments');
|
||||
|
||||
if (composeState.get('poll')) {
|
||||
dispatch(showAlert({ message: messages.quoteErrorPoll }));
|
||||
} else if (
|
||||
composeState.get('is_uploading') ||
|
||||
(mediaAttachments &&
|
||||
typeof mediaAttachments !== 'string' &&
|
||||
typeof mediaAttachments !== 'number' &&
|
||||
typeof mediaAttachments !== 'boolean' &&
|
||||
mediaAttachments.size !== 0)
|
||||
) {
|
||||
dispatch(showAlert({ message: messages.quoteErrorUpload }));
|
||||
} else if (composeState.get('quoted_status_id')) {
|
||||
dispatch(showAlert({ message: messages.quoteErrorQuote }));
|
||||
} else if (
|
||||
status.getIn(['quote_approval', 'current_user']) !== 'automatic' &&
|
||||
status.getIn(['quote_approval', 'current_user']) !== 'manual'
|
||||
) {
|
||||
dispatch(showAlert({ message: messages.quoteErrorUnauthorized }));
|
||||
} else {
|
||||
dispatch(quoteCompose(status));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const quoteComposeById = createAppThunk(
|
||||
(statusId: string, { dispatch, getState }) => {
|
||||
const status = getState().statuses.get(statusId);
|
||||
|
@ -97,6 +148,6 @@ export const quoteComposeById = createAppThunk(
|
|||
|
||||
export const quoteComposeCancel = createAction('compose/quoteComposeCancel');
|
||||
|
||||
export const setQuotePolicy = createAction<ApiQuotePolicy>(
|
||||
export const setComposeQuotePolicy = createAction<ApiQuotePolicy>(
|
||||
'compose/setQuotePolicy',
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { ApiStatusJSON } from './statuses';
|
|||
|
||||
export type ApiQuoteState = 'accepted' | 'pending' | 'revoked' | 'unauthorized';
|
||||
export type ApiQuotePolicy = 'public' | 'followers' | 'nobody' | 'unknown';
|
||||
export type ApiUserQuotePolicy = 'automatic' | 'manual' | 'denied' | 'unknown';
|
||||
|
||||
interface ApiQuoteEmptyJSON {
|
||||
state: Exclude<ApiQuoteState, 'accepted'>;
|
||||
|
@ -25,7 +26,7 @@ export type ApiQuoteJSON = ApiQuoteAcceptedJSON | ApiQuoteEmptyJSON;
|
|||
export interface ApiQuotePolicyJSON {
|
||||
automatic: ApiQuotePolicy[];
|
||||
manual: ApiQuotePolicy[];
|
||||
current_user: ApiQuotePolicy;
|
||||
current_user: ApiUserQuotePolicy;
|
||||
}
|
||||
|
||||
export function isQuotePolicy(policy: string): policy is ApiQuotePolicy {
|
||||
|
|
|
@ -133,3 +133,9 @@ export interface ApiStatusSourceJSON {
|
|||
text: string;
|
||||
spoiler_text: string;
|
||||
}
|
||||
|
||||
export function isStatusVisibility(
|
||||
visibility: string,
|
||||
): visibility is StatusVisibility {
|
||||
return ['public', 'unlisted', 'private', 'direct'].includes(visibility);
|
||||
}
|
||||
|
|
|
@ -41,13 +41,16 @@ import { IconButton } from './icon_button';
|
|||
|
||||
let id = 0;
|
||||
|
||||
type RenderItemFn<Item = MenuItem> = (
|
||||
export interface RenderItemFnHandlers {
|
||||
onClick: React.MouseEventHandler;
|
||||
onKeyUp: React.KeyboardEventHandler;
|
||||
}
|
||||
|
||||
export type RenderItemFn<Item = MenuItem> = (
|
||||
item: Item,
|
||||
index: number,
|
||||
handlers: {
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
onKeyUp: (e: React.KeyboardEvent) => void;
|
||||
},
|
||||
handlers: RenderItemFnHandlers,
|
||||
focusRefCallback?: (c: HTMLAnchorElement | HTMLButtonElement | null) => void,
|
||||
) => React.ReactNode;
|
||||
|
||||
type ItemClickFn<Item = MenuItem> = (item: Item, index: number) => void;
|
||||
|
@ -173,7 +176,7 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||
onItemClick(item, i);
|
||||
} else if (isActionItem(item)) {
|
||||
e.preventDefault();
|
||||
item.action();
|
||||
item.action(e);
|
||||
}
|
||||
},
|
||||
[onClose, onItemClick, items],
|
||||
|
@ -277,10 +280,15 @@ export const DropdownMenu = <Item = MenuItem,>({
|
|||
})}
|
||||
>
|
||||
{items.map((option, i) =>
|
||||
renderItemMethod(option, i, {
|
||||
renderItemMethod(
|
||||
option,
|
||||
i,
|
||||
{
|
||||
onClick: handleItemClick,
|
||||
onKeyUp: handleItemKeyUp,
|
||||
}),
|
||||
},
|
||||
i === 0 ? handleFocusedItemRef : undefined,
|
||||
),
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
|
@ -307,7 +315,9 @@ interface DropdownProps<Item = MenuItem> {
|
|||
forceDropdown?: boolean;
|
||||
renderItem?: RenderItemFn<Item>;
|
||||
renderHeader?: RenderHeaderFn<Item>;
|
||||
onOpen?: () => void;
|
||||
onOpen?: // Must use a union type for the full function as a union with void is not allowed.
|
||||
| ((event: React.MouseEvent | React.KeyboardEvent) => void)
|
||||
| ((event: React.MouseEvent | React.KeyboardEvent) => boolean);
|
||||
onItemClick?: ItemClickFn<Item>;
|
||||
}
|
||||
|
||||
|
@ -376,7 +386,7 @@ export const Dropdown = <Item = MenuItem,>({
|
|||
onItemClick(item, i);
|
||||
} else if (isActionItem(item)) {
|
||||
e.preventDefault();
|
||||
item.action();
|
||||
item.action(e);
|
||||
}
|
||||
},
|
||||
[handleClose, onItemClick, items],
|
||||
|
@ -389,7 +399,10 @@ export const Dropdown = <Item = MenuItem,>({
|
|||
if (open) {
|
||||
handleClose();
|
||||
} else {
|
||||
onOpen?.();
|
||||
const allow = onOpen?.(e);
|
||||
if (allow === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefetchAccountId) {
|
||||
dispatch(fetchRelationships([prefetchAccountId]));
|
||||
|
|
|
@ -110,6 +110,7 @@ class Status extends ImmutablePureComponent {
|
|||
onToggleCollapsed: PropTypes.func,
|
||||
onTranslate: PropTypes.func,
|
||||
onInteractionModal: PropTypes.func,
|
||||
onQuoteCancel: PropTypes.func,
|
||||
muted: PropTypes.bool,
|
||||
hidden: PropTypes.bool,
|
||||
unread: PropTypes.bool,
|
||||
|
@ -583,7 +584,7 @@ class Status extends ImmutablePureComponent {
|
|||
<DisplayName account={status.get('account')} />
|
||||
</Link>
|
||||
|
||||
{this.props.contextType === 'compose' && isQuotedPost && (
|
||||
{isQuotedPost && !!this.props.onQuoteCancel && (
|
||||
<IconButton
|
||||
onClick={this.handleQuoteCancel}
|
||||
className='status__quote-cancel'
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
|
||||
import type { StatusVisibility } from '@/mastodon/api_types/statuses';
|
||||
import { statusFactoryState } from '@/testing/factories';
|
||||
|
||||
import { LegacyReblogButton, StatusReblogButton } from './reblog_button';
|
||||
|
||||
interface StoryProps {
|
||||
visibility: StatusVisibility;
|
||||
quoteAllowed: boolean;
|
||||
alreadyBoosted: boolean;
|
||||
reblogCount: number;
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Status/ReblogButton',
|
||||
args: {
|
||||
visibility: 'public',
|
||||
quoteAllowed: true,
|
||||
alreadyBoosted: false,
|
||||
reblogCount: 0,
|
||||
},
|
||||
argTypes: {
|
||||
visibility: {
|
||||
name: 'Visibility',
|
||||
control: { type: 'select' },
|
||||
options: ['public', 'unlisted', 'private', 'direct'],
|
||||
},
|
||||
reblogCount: {
|
||||
name: 'Boost Count',
|
||||
description: 'More than 0 will show the counter',
|
||||
},
|
||||
quoteAllowed: {
|
||||
name: 'Quotes allowed',
|
||||
},
|
||||
alreadyBoosted: {
|
||||
name: 'Already boosted',
|
||||
},
|
||||
},
|
||||
render: (args) => (
|
||||
<StatusReblogButton
|
||||
status={argsToStatus(args)}
|
||||
counters={args.reblogCount > 0}
|
||||
/>
|
||||
),
|
||||
} satisfies Meta<StoryProps>;
|
||||
|
||||
export default meta;
|
||||
|
||||
function argsToStatus({
|
||||
reblogCount,
|
||||
visibility,
|
||||
quoteAllowed,
|
||||
alreadyBoosted,
|
||||
}: StoryProps) {
|
||||
return statusFactoryState({
|
||||
reblogs_count: reblogCount,
|
||||
visibility,
|
||||
reblogged: alreadyBoosted,
|
||||
quote_approval: {
|
||||
automatic: [],
|
||||
manual: [],
|
||||
current_user: quoteAllowed ? 'automatic' : 'denied',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Mine: Story = {
|
||||
parameters: {
|
||||
state: {
|
||||
meta: {
|
||||
me: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Legacy: Story = {
|
||||
render: (args) => (
|
||||
<LegacyReblogButton
|
||||
status={argsToStatus(args)}
|
||||
counters={args.reblogCount > 0}
|
||||
/>
|
||||
),
|
||||
};
|
373
app/javascript/mastodon/components/status/reblog_button.tsx
Normal file
373
app/javascript/mastodon/components/status/reblog_button.tsx
Normal file
|
@ -0,0 +1,373 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import type {
|
||||
FC,
|
||||
KeyboardEvent,
|
||||
MouseEvent,
|
||||
MouseEventHandler,
|
||||
SVGProps,
|
||||
} from 'react';
|
||||
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { quoteComposeById } from '@/mastodon/actions/compose_typed';
|
||||
import { toggleReblog } from '@/mastodon/actions/interactions';
|
||||
import { openModal } from '@/mastodon/actions/modal';
|
||||
import type { ActionMenuItem } from '@/mastodon/models/dropdown_menu';
|
||||
import type { Status, StatusVisibility } from '@/mastodon/models/status';
|
||||
import {
|
||||
createAppSelector,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from '@/mastodon/store';
|
||||
import { isFeatureEnabled } from '@/mastodon/utils/environment';
|
||||
import FormatQuote from '@/material-icons/400-24px/format_quote.svg?react';
|
||||
import FormatQuoteOff from '@/material-icons/400-24px/format_quote_off.svg?react';
|
||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
||||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
|
||||
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
|
||||
|
||||
import type { RenderItemFn, RenderItemFnHandlers } from '../dropdown_menu';
|
||||
import { Dropdown } from '../dropdown_menu';
|
||||
import { Icon } from '../icon';
|
||||
import { IconButton } from '../icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
all_disabled: {
|
||||
id: 'status.all_disabled',
|
||||
defaultMessage: 'Boosts and quotes are disabled',
|
||||
},
|
||||
quote: { id: 'status.quote', defaultMessage: 'Quote' },
|
||||
quote_cannot: {
|
||||
id: 'status.cannot_quote',
|
||||
defaultMessage: 'Author has disabled quoting on this post',
|
||||
},
|
||||
quote_private: {
|
||||
id: 'status.quote_private',
|
||||
defaultMessage: 'Private posts cannot be quoted',
|
||||
},
|
||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||
reblog_cancel: {
|
||||
id: 'status.cancel_reblog_private',
|
||||
defaultMessage: 'Unboost',
|
||||
},
|
||||
reblog_private: {
|
||||
id: 'status.reblog_private',
|
||||
defaultMessage: 'Boost with original visibility',
|
||||
},
|
||||
reblog_cannot: {
|
||||
id: 'status.cannot_reblog',
|
||||
defaultMessage: 'This post cannot be boosted',
|
||||
},
|
||||
});
|
||||
|
||||
interface ReblogButtonProps {
|
||||
status: Status;
|
||||
counters?: boolean;
|
||||
}
|
||||
|
||||
export const StatusReblogButton: FC<ReblogButtonProps> = ({
|
||||
status,
|
||||
counters,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const statusState = useAppSelector((state) =>
|
||||
selectStatusState(state, status),
|
||||
);
|
||||
const { isLoggedIn, isReblogged, isReblogAllowed, isQuoteAllowed } =
|
||||
statusState;
|
||||
const { iconComponent } = useMemo(
|
||||
() => reblogIconText(statusState),
|
||||
[statusState],
|
||||
);
|
||||
const disabled = !isQuoteAllowed && !isReblogAllowed;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const statusId = status.get('id') as string;
|
||||
const items: ActionMenuItem[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
text: 'reblog',
|
||||
action: (event) => {
|
||||
if (isLoggedIn) {
|
||||
dispatch(toggleReblog(statusId, event.shiftKey));
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'quote',
|
||||
action: () => {
|
||||
if (isLoggedIn) {
|
||||
dispatch(quoteComposeById(statusId));
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
[dispatch, isLoggedIn, statusId],
|
||||
);
|
||||
|
||||
const handleDropdownOpen = useCallback(
|
||||
(event: MouseEvent | KeyboardEvent) => {
|
||||
if (!isLoggedIn) {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'INTERACTION',
|
||||
modalProps: {
|
||||
type: 'reblog',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}),
|
||||
);
|
||||
} else if (event.shiftKey) {
|
||||
dispatch(toggleReblog(status.get('id'), true));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[dispatch, isLoggedIn, status],
|
||||
);
|
||||
|
||||
const renderMenuItem: RenderItemFn<ActionMenuItem> = useCallback(
|
||||
(item, index, handlers, focusRefCallback) => (
|
||||
<ReblogMenuItem
|
||||
status={status}
|
||||
index={index}
|
||||
item={item}
|
||||
handlers={handlers}
|
||||
key={`${item.text}-${index}`}
|
||||
focusRefCallback={focusRefCallback}
|
||||
/>
|
||||
),
|
||||
[status],
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
items={items}
|
||||
renderItem={renderMenuItem}
|
||||
onOpen={handleDropdownOpen}
|
||||
disabled={disabled}
|
||||
>
|
||||
<IconButton
|
||||
title={intl.formatMessage(
|
||||
!disabled ? messages.reblog : messages.all_disabled,
|
||||
)}
|
||||
icon='retweet'
|
||||
iconComponent={iconComponent}
|
||||
counter={counters ? (status.get('reblogs_count') as number) : undefined}
|
||||
active={isReblogged}
|
||||
/>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
interface ReblogMenuItemProps {
|
||||
status: Status;
|
||||
item: ActionMenuItem;
|
||||
index: number;
|
||||
handlers: RenderItemFnHandlers;
|
||||
focusRefCallback?: (c: HTMLAnchorElement | HTMLButtonElement | null) => void;
|
||||
}
|
||||
|
||||
const ReblogMenuItem: FC<ReblogMenuItemProps> = ({
|
||||
status,
|
||||
index,
|
||||
item: { text },
|
||||
handlers,
|
||||
focusRefCallback,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const statusState = useAppSelector((state) =>
|
||||
selectStatusState(state, status),
|
||||
);
|
||||
const { title, meta, iconComponent, disabled } = useMemo(
|
||||
() =>
|
||||
text === 'quote'
|
||||
? quoteIconText(statusState)
|
||||
: reblogIconText(statusState),
|
||||
[statusState, text],
|
||||
);
|
||||
const active = useMemo(
|
||||
() => text === 'reblog' && !!status.get('reblogged'),
|
||||
[status, text],
|
||||
);
|
||||
|
||||
return (
|
||||
<li
|
||||
className={classNames('dropdown-menu__item reblog-button__item', {
|
||||
disabled,
|
||||
active,
|
||||
})}
|
||||
key={`${text}-${index}`}
|
||||
>
|
||||
<button
|
||||
{...handlers}
|
||||
title={intl.formatMessage(title)}
|
||||
ref={focusRefCallback}
|
||||
disabled={disabled}
|
||||
data-index={index}
|
||||
>
|
||||
<Icon
|
||||
id={text === 'quote' ? 'quote' : 'retweet'}
|
||||
icon={iconComponent}
|
||||
/>
|
||||
<div>
|
||||
{intl.formatMessage(title)}
|
||||
{meta && (
|
||||
<span className='reblog-button__meta'>
|
||||
{intl.formatMessage(meta)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
// Legacy helpers
|
||||
|
||||
// Switch between the legacy and new reblog button based on feature flag.
|
||||
export const ReblogButton: FC<ReblogButtonProps> = (props) => {
|
||||
if (isFeatureEnabled('outgoing_quotes')) {
|
||||
return <StatusReblogButton {...props} />;
|
||||
}
|
||||
return <LegacyReblogButton {...props} />;
|
||||
};
|
||||
|
||||
export const LegacyReblogButton: FC<ReblogButtonProps> = ({
|
||||
status,
|
||||
counters,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const statusState = useAppSelector((state) =>
|
||||
selectStatusState(state, status),
|
||||
);
|
||||
|
||||
const { title, meta, iconComponent, disabled } = useMemo(
|
||||
() => reblogIconText(statusState),
|
||||
[statusState],
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleClick: MouseEventHandler = useCallback(
|
||||
(event) => {
|
||||
if (statusState.isLoggedIn) {
|
||||
dispatch(toggleReblog(status.get('id') as string, event.shiftKey));
|
||||
} else {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'INTERACTION',
|
||||
modalProps: {
|
||||
type: 'reblog',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
[dispatch, status, statusState.isLoggedIn],
|
||||
);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
disabled={disabled}
|
||||
active={!!status.get('reblogged')}
|
||||
title={intl.formatMessage(meta ?? title)}
|
||||
icon='retweet'
|
||||
iconComponent={iconComponent}
|
||||
onClick={!disabled ? handleClick : undefined}
|
||||
counter={counters ? (status.get('reblogs_count') as number) : undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// Helpers for copy and state for status.
|
||||
const selectStatusState = createAppSelector(
|
||||
[
|
||||
(state) => state.meta.get('me') as string | undefined,
|
||||
(_, status: Status) => status,
|
||||
],
|
||||
(userId, status) => {
|
||||
const isPublic = ['public', 'unlisted'].includes(
|
||||
status.get('visibility') as StatusVisibility,
|
||||
);
|
||||
const isMineAndPrivate =
|
||||
userId === status.getIn(['account', 'id']) &&
|
||||
status.get('visibility') === 'private';
|
||||
return {
|
||||
isLoggedIn: !!userId,
|
||||
isPublic,
|
||||
isMine: userId === status.getIn(['account', 'id']),
|
||||
isPrivateReblog:
|
||||
userId === status.getIn(['account', 'id']) &&
|
||||
status.get('visibility') === 'private',
|
||||
isReblogged: !!status.get('reblogged'),
|
||||
isReblogAllowed: isPublic || isMineAndPrivate,
|
||||
isQuoteAllowed:
|
||||
status.getIn(['quote_approval', 'current_user']) === 'automatic' &&
|
||||
(isPublic || isMineAndPrivate),
|
||||
};
|
||||
},
|
||||
);
|
||||
type StatusState = ReturnType<typeof selectStatusState>;
|
||||
|
||||
interface IconText {
|
||||
title: MessageDescriptor;
|
||||
meta?: MessageDescriptor;
|
||||
iconComponent: FC<SVGProps<SVGSVGElement>>;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
function reblogIconText({
|
||||
isPublic,
|
||||
isPrivateReblog,
|
||||
isReblogged,
|
||||
}: StatusState): IconText {
|
||||
if (isReblogged) {
|
||||
return {
|
||||
title: messages.reblog_cancel,
|
||||
iconComponent: isPublic ? RepeatActiveIcon : RepeatPrivateActiveIcon,
|
||||
};
|
||||
}
|
||||
const iconText: IconText = {
|
||||
title: messages.reblog,
|
||||
iconComponent: RepeatIcon,
|
||||
};
|
||||
|
||||
if (isPrivateReblog) {
|
||||
iconText.meta = messages.reblog_private;
|
||||
iconText.iconComponent = RepeatPrivateIcon;
|
||||
} else if (!isPublic) {
|
||||
iconText.meta = messages.reblog_cannot;
|
||||
iconText.iconComponent = RepeatDisabledIcon;
|
||||
iconText.disabled = true;
|
||||
}
|
||||
return iconText;
|
||||
}
|
||||
|
||||
function quoteIconText({
|
||||
isMine,
|
||||
isQuoteAllowed,
|
||||
isPublic,
|
||||
}: StatusState): IconText {
|
||||
const iconText: IconText = {
|
||||
title: messages.quote,
|
||||
iconComponent: FormatQuote,
|
||||
};
|
||||
|
||||
if (!isQuoteAllowed || (!isPublic && !isMine)) {
|
||||
iconText.meta = !isQuoteAllowed
|
||||
? messages.quote_cannot
|
||||
: messages.quote_private;
|
||||
iconText.iconComponent = FormatQuoteOff;
|
||||
iconText.disabled = true;
|
||||
}
|
||||
return iconText;
|
||||
}
|
|
@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
@ -12,15 +11,10 @@ import { connect } from 'react-redux';
|
|||
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
|
||||
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
||||
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
||||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
|
||||
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
@ -30,6 +24,7 @@ import { me } from '../initial_state';
|
|||
|
||||
import { IconButton } from './icon_button';
|
||||
import { isFeatureEnabled } from '../utils/environment';
|
||||
import { ReblogButton } from './status/reblog_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
|
@ -43,10 +38,6 @@ const messages = defineMessages({
|
|||
share: { id: 'status.share', defaultMessage: 'Share' },
|
||||
more: { id: 'status.more', defaultMessage: 'More' },
|
||||
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
|
||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
|
||||
removeFavourite: { id: 'status.remove_favourite', defaultMessage: 'Remove from favorites' },
|
||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||
|
@ -85,10 +76,9 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
identity: identityContextPropShape,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
relationship: ImmutablePropTypes.record,
|
||||
quotedAccountId: ImmutablePropTypes.string,
|
||||
quotedAccountId: PropTypes.string,
|
||||
onReply: PropTypes.func,
|
||||
onFavourite: PropTypes.func,
|
||||
onReblog: PropTypes.func,
|
||||
onDelete: PropTypes.func,
|
||||
onRevokeQuote: PropTypes.func,
|
||||
onQuotePolicyChange: PropTypes.func,
|
||||
|
@ -152,16 +142,6 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
handleReblogClick = e => {
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
} else {
|
||||
this.props.onInteractionModal('reblog', this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleBookmarkClick = () => {
|
||||
this.props.onBookmark(this.props.status);
|
||||
};
|
||||
|
@ -377,25 +357,6 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
replyTitle = intl.formatMessage(messages.replyAll);
|
||||
}
|
||||
|
||||
const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
|
||||
|
||||
let reblogTitle, reblogIconComponent;
|
||||
|
||||
if (status.get('reblogged')) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
reblogIconComponent = RepeatIcon;
|
||||
} else if (reblogPrivate) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog_private);
|
||||
reblogIconComponent = RepeatPrivateIcon;
|
||||
} else {
|
||||
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
||||
reblogIconComponent = RepeatDisabledIcon;
|
||||
}
|
||||
|
||||
|
||||
const bookmarkTitle = intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark);
|
||||
const favouriteTitle = intl.formatMessage(status.get('favourited') ? messages.removeFavourite : messages.favourite);
|
||||
const isReply = status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
|
||||
|
@ -406,7 +367,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
<IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
|
||||
</div>
|
||||
<div className='status__action-bar__button-wrapper'>
|
||||
<IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||
<ReblogButton status={status} counters={withCounters} />
|
||||
</div>
|
||||
<div className='status__action-bar__button-wrapper'>
|
||||
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={favouriteTitle} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||
|
|
|
@ -63,12 +63,21 @@ type GetStatusSelector = (
|
|||
props: { id?: string | null; contextType?: string },
|
||||
) => Status | null;
|
||||
|
||||
export const QuotedStatus: React.FC<{
|
||||
interface QuotedStatusProps {
|
||||
quote: QuoteMap;
|
||||
contextType?: string;
|
||||
variant?: 'full' | 'link';
|
||||
nestingLevel?: number;
|
||||
}> = ({ quote, contextType, nestingLevel = 1, variant = 'full' }) => {
|
||||
onQuoteCancel?: () => void; // Used for composer.
|
||||
}
|
||||
|
||||
export const QuotedStatus: React.FC<QuotedStatusProps> = ({
|
||||
quote,
|
||||
contextType,
|
||||
nestingLevel = 1,
|
||||
variant = 'full',
|
||||
onQuoteCancel,
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const quotedStatusId = quote.get('quoted_status');
|
||||
const quoteState = quote.get('state');
|
||||
|
@ -160,6 +169,7 @@ export const QuotedStatus: React.FC<{
|
|||
id={quotedStatusId}
|
||||
contextType={contextType}
|
||||
avatarSize={32}
|
||||
onQuoteCancel={onQuoteCancel}
|
||||
>
|
||||
{canRenderChildQuote && (
|
||||
<QuotedStatus
|
||||
|
|
|
@ -41,10 +41,10 @@ import {
|
|||
translateStatus,
|
||||
undoStatusTranslation,
|
||||
} from '../actions/statuses';
|
||||
import { setStatusQuotePolicy } from '../actions/statuses_typed';
|
||||
import Status from '../components/status';
|
||||
import { deleteModal } from '../initial_state';
|
||||
import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
|
||||
import { quoteComposeCancel } from '../actions/compose_typed';
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getStatus = makeGetStatus();
|
||||
|
@ -112,18 +112,18 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
|
|||
}
|
||||
},
|
||||
|
||||
onQuoteCancel() {
|
||||
if (contextType === 'compose') {
|
||||
dispatch(quoteComposeCancel());
|
||||
}
|
||||
},
|
||||
|
||||
onRevokeQuote (status) {
|
||||
dispatch(openModal({ modalType: 'CONFIRM_REVOKE_QUOTE', modalProps: { statusId: status.get('id'), quotedStatusId: status.getIn(['quote', 'quoted_status']) }}));
|
||||
},
|
||||
|
||||
onQuotePolicyChange(status) {
|
||||
dispatch(openModal({ modalType: 'COMPOSE_PRIVACY', modalProps: { statusId: status.get('id') } }));
|
||||
const statusId = status.get('id');
|
||||
const handleChange = (_, quotePolicy) => {
|
||||
dispatch(
|
||||
setStatusQuotePolicy({ policy: quotePolicy, statusId }),
|
||||
);
|
||||
}
|
||||
dispatch(openModal({ modalType: 'COMPOSE_PRIVACY', modalProps: { statusId, onChange: handleChange } }));
|
||||
},
|
||||
|
||||
onEdit (status) {
|
||||
|
|
|
@ -17,7 +17,6 @@ import AutosuggestTextarea from 'mastodon/components/autosuggest_textarea';
|
|||
import { Button } from 'mastodon/components/button';
|
||||
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
|
||||
import PollButtonContainer from '../containers/poll_button_container';
|
||||
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
|
||||
import SpoilerButtonContainer from '../containers/spoiler_button_container';
|
||||
import UploadButtonContainer from '../containers/upload_button_container';
|
||||
import { countableText } from '../util/counter';
|
||||
|
@ -31,6 +30,7 @@ import { ReplyIndicator } from './reply_indicator';
|
|||
import { UploadForm } from './upload_form';
|
||||
import { Warning } from './warning';
|
||||
import { ComposeQuotedStatus } from './quoted_post';
|
||||
import { VisibilityButton } from './visibility_button';
|
||||
|
||||
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
|
||||
|
||||
|
@ -259,7 +259,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<EditIndicator />
|
||||
|
||||
<div className='compose-form__dropdowns'>
|
||||
<PrivacyDropdownContainer disabled={this.props.isEditing} />
|
||||
<VisibilityButton disabled={this.props.isEditing} />
|
||||
<LanguageDropdown />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { Map } from 'immutable';
|
||||
|
||||
import { quoteComposeCancel } from '@/mastodon/actions/compose_typed';
|
||||
import { QuotedStatus } from '@/mastodon/components/status_quoted';
|
||||
import { useAppSelector } from '@/mastodon/store';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
|
||||
export const ComposeQuotedStatus: FC = () => {
|
||||
const quotedStatusId = useAppSelector(
|
||||
(state) => state.compose.get('quoted_status_id') as string | null,
|
||||
);
|
||||
const isEditing = useAppSelector((state) => !!state.compose.get('id'));
|
||||
const quote = useMemo(
|
||||
() =>
|
||||
quotedStatusId
|
||||
|
@ -20,8 +22,17 @@ export const ComposeQuotedStatus: FC = () => {
|
|||
: null,
|
||||
[quotedStatusId],
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
const handleQuoteCancel = useCallback(() => {
|
||||
dispatch(quoteComposeCancel());
|
||||
}, [dispatch]);
|
||||
if (!quote) {
|
||||
return null;
|
||||
}
|
||||
return <QuotedStatus quote={quote} contextType='compose' />;
|
||||
return (
|
||||
<QuotedStatus
|
||||
quote={quote}
|
||||
onQuoteCancel={!isEditing ? handleQuoteCancel : undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { changeComposeVisibility } from '@/mastodon/actions/compose';
|
||||
import { setComposeQuotePolicy } from '@/mastodon/actions/compose_typed';
|
||||
import { openModal } from '@/mastodon/actions/modal';
|
||||
import type { ApiQuotePolicy } from '@/mastodon/api_types/quotes';
|
||||
import type { StatusVisibility } from '@/mastodon/api_types/statuses';
|
||||
import { Icon } from '@/mastodon/components/icon';
|
||||
import { useAppSelector, useAppDispatch } from '@/mastodon/store';
|
||||
import { isFeatureEnabled } from '@/mastodon/utils/environment';
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
|
||||
|
||||
import type { VisibilityModalCallback } from '../../ui/components/visibility_modal';
|
||||
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
|
||||
|
||||
import { messages as privacyMessages } from './privacy_dropdown';
|
||||
|
||||
const messages = defineMessages({
|
||||
anyone_quote: {
|
||||
id: 'privacy.quote.anyone',
|
||||
defaultMessage: '{visibility}, anyone can quote',
|
||||
},
|
||||
limited_quote: {
|
||||
id: 'privacy.quote.limited',
|
||||
defaultMessage: '{visibility}, quotes limited',
|
||||
},
|
||||
disabled_quote: {
|
||||
id: 'privacy.quote.disabled',
|
||||
defaultMessage: '{visibility}, quotes disabled',
|
||||
},
|
||||
});
|
||||
|
||||
interface PrivacyDropdownProps {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const VisibilityButton: FC<PrivacyDropdownProps> = (props) => {
|
||||
if (!isFeatureEnabled('outgoing_quotes')) {
|
||||
return <PrivacyDropdownContainer {...props} />;
|
||||
}
|
||||
return <PrivacyModalButton {...props} />;
|
||||
};
|
||||
|
||||
const visibilityOptions = {
|
||||
public: {
|
||||
icon: 'globe',
|
||||
iconComponent: PublicIcon,
|
||||
value: 'public',
|
||||
text: privacyMessages.public_short,
|
||||
},
|
||||
unlisted: {
|
||||
icon: 'unlock',
|
||||
iconComponent: QuietTimeIcon,
|
||||
value: 'unlisted',
|
||||
text: privacyMessages.unlisted_short,
|
||||
},
|
||||
private: {
|
||||
icon: 'lock',
|
||||
iconComponent: LockIcon,
|
||||
value: 'private',
|
||||
text: privacyMessages.private_short,
|
||||
},
|
||||
direct: {
|
||||
icon: 'at',
|
||||
iconComponent: AlternateEmailIcon,
|
||||
value: 'direct',
|
||||
text: privacyMessages.direct_short,
|
||||
},
|
||||
};
|
||||
|
||||
const PrivacyModalButton: FC<PrivacyDropdownProps> = ({ disabled = false }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { visibility, quotePolicy } = useAppSelector((state) => ({
|
||||
visibility: state.compose.get('privacy') as StatusVisibility,
|
||||
quotePolicy: state.compose.get('quote_policy') as ApiQuotePolicy,
|
||||
}));
|
||||
|
||||
const { icon, iconComponent } = useMemo(() => {
|
||||
const option = visibilityOptions[visibility];
|
||||
return { icon: option.icon, iconComponent: option.iconComponent };
|
||||
}, [visibility]);
|
||||
const text = useMemo(() => {
|
||||
const visibilityText = intl.formatMessage(
|
||||
visibilityOptions[visibility].text,
|
||||
);
|
||||
if (visibility === 'private' || visibility === 'direct') {
|
||||
return visibilityText;
|
||||
}
|
||||
if (quotePolicy === 'nobody') {
|
||||
return intl.formatMessage(messages.disabled_quote, {
|
||||
visibility: visibilityText,
|
||||
});
|
||||
}
|
||||
if (quotePolicy !== 'public') {
|
||||
return intl.formatMessage(messages.limited_quote, {
|
||||
visibility: visibilityText,
|
||||
});
|
||||
}
|
||||
return intl.formatMessage(messages.anyone_quote, {
|
||||
visibility: visibilityText,
|
||||
});
|
||||
}, [quotePolicy, visibility, intl]);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChange: VisibilityModalCallback = useCallback(
|
||||
(newVisibility, newQuotePolicy) => {
|
||||
if (newVisibility !== visibility) {
|
||||
dispatch(changeComposeVisibility(newVisibility));
|
||||
}
|
||||
if (newQuotePolicy !== quotePolicy) {
|
||||
dispatch(setComposeQuotePolicy(newQuotePolicy));
|
||||
}
|
||||
},
|
||||
[dispatch, quotePolicy, visibility],
|
||||
);
|
||||
|
||||
const handleOpen = useCallback(() => {
|
||||
dispatch(
|
||||
openModal({
|
||||
modalType: 'COMPOSE_PRIVACY',
|
||||
modalProps: { onChange: handleChange },
|
||||
}),
|
||||
);
|
||||
}, [dispatch, handleChange]);
|
||||
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
title={intl.formatMessage(privacyMessages.change_privacy)}
|
||||
onClick={handleOpen}
|
||||
disabled={disabled}
|
||||
className={classNames('dropdown-button')}
|
||||
>
|
||||
<Icon id={icon} icon={iconComponent} />
|
||||
<span className='dropdown-button__label'>{text}</span>
|
||||
</button>
|
||||
);
|
||||
};
|
|
@ -3,23 +3,16 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
|
||||
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
|
||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
|
||||
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
||||
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
|
||||
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
|
||||
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
|
||||
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
|
||||
|
@ -27,6 +20,7 @@ import { IconButton } from '../../../components/icon_button';
|
|||
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
||||
import { me } from '../../../initial_state';
|
||||
import { isFeatureEnabled } from '@/mastodon/utils/environment';
|
||||
import { ReblogButton } from '@/mastodon/components/status/reblog_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
|
@ -35,10 +29,6 @@ const messages = defineMessages({
|
|||
direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
|
||||
mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
|
||||
removeFavourite: { id: 'status.remove_favourite', defaultMessage: 'Remove from favorites' },
|
||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||
|
@ -313,31 +303,15 @@ class ActionBar extends PureComponent {
|
|||
replyIconComponent = ReplyAllIcon;
|
||||
}
|
||||
|
||||
const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
|
||||
|
||||
let reblogTitle, reblogIconComponent;
|
||||
|
||||
if (status.get('reblogged')) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
reblogIconComponent = RepeatIcon;
|
||||
} else if (reblogPrivate) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog_private);
|
||||
reblogIconComponent = RepeatPrivateIcon;
|
||||
} else {
|
||||
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
||||
reblogIconComponent = RepeatDisabledIcon;
|
||||
}
|
||||
|
||||
const bookmarkTitle = intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark);
|
||||
const favouriteTitle = intl.formatMessage(status.get('favourited') ? messages.removeFavourite : messages.favourite);
|
||||
|
||||
return (
|
||||
<div className='detailed-status__action-bar'>
|
||||
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} /></div>
|
||||
<div className='detailed-status__button'>
|
||||
<ReblogButton status={status} />
|
||||
</div>
|
||||
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={favouriteTitle} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={bookmarkTitle} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
@ -15,7 +15,6 @@ import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?reac
|
|||
import { Hotkeys } from 'mastodon/components/hotkeys';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { TimelineHint } from 'mastodon/components/timeline_hint';
|
||||
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
@ -57,6 +56,7 @@ import {
|
|||
translateStatus,
|
||||
undoStatusTranslation,
|
||||
} from '../../actions/statuses';
|
||||
import { setStatusQuotePolicy } from '../../actions/statuses_typed';
|
||||
import ColumnHeader from '../../components/column_header';
|
||||
import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
|
||||
import { StatusQuoteManager } from '../../components/status_quoted';
|
||||
|
@ -266,8 +266,14 @@ class Status extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleQuotePolicyChange = (status) => {
|
||||
const statusId = status.get('id');
|
||||
const { dispatch } = this.props;
|
||||
dispatch(openModal({ modalType: 'COMPOSE_PRIVACY', modalProps: { statusId: status.get('id') } }));
|
||||
const handleChange = (_, quotePolicy) => {
|
||||
dispatch(
|
||||
setStatusQuotePolicy({ policy: quotePolicy, statusId }),
|
||||
);
|
||||
}
|
||||
dispatch(openModal({ modalType: 'COMPOSE_PRIVACY', modalProps: { statusId, onChange: handleChange } }));
|
||||
};
|
||||
|
||||
handleEditClick = (status) => {
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
import { forwardRef, useCallback, useId, useMemo } from 'react';
|
||||
import {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useId,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { changeComposeVisibility } from '@/mastodon/actions/compose';
|
||||
import { setStatusQuotePolicy } from '@/mastodon/actions/statuses_typed';
|
||||
import type { ApiQuotePolicy } from '@/mastodon/api_types/quotes';
|
||||
import { isQuotePolicy } from '@/mastodon/api_types/quotes';
|
||||
import { isStatusVisibility } from '@/mastodon/api_types/statuses';
|
||||
import type { StatusVisibility } from '@/mastodon/api_types/statuses';
|
||||
import { Dropdown } from '@/mastodon/components/dropdown';
|
||||
import type { SelectItem } from '@/mastodon/components/dropdown_selector';
|
||||
import { IconButton } from '@/mastodon/components/icon_button';
|
||||
import { messages as privacyMessages } from '@/mastodon/features/compose/components/privacy_dropdown';
|
||||
import {
|
||||
createAppSelector,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from '@/mastodon/store';
|
||||
import { createAppSelector, useAppSelector } from '@/mastodon/store';
|
||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||
import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modals/confirmation_modal';
|
||||
|
||||
|
@ -43,13 +49,26 @@ const messages = defineMessages({
|
|||
},
|
||||
});
|
||||
|
||||
export type VisibilityModalCallback = (
|
||||
visibility: StatusVisibility,
|
||||
quotePolicy: ApiQuotePolicy,
|
||||
) => void;
|
||||
|
||||
interface VisibilityModalProps extends BaseConfirmationModalProps {
|
||||
statusId: string;
|
||||
statusId?: string;
|
||||
onChange: VisibilityModalCallback;
|
||||
}
|
||||
|
||||
const selectStatusPolicy = createAppSelector(
|
||||
[(state) => state.statuses, (_state, statusId: string) => statusId],
|
||||
(statuses, statusId) => {
|
||||
[
|
||||
(state) => state.statuses,
|
||||
(_state, statusId?: string) => statusId,
|
||||
(state) => state.compose.get('quote_policy') as ApiQuotePolicy,
|
||||
],
|
||||
(statuses, statusId, composeQuotePolicy) => {
|
||||
if (!statusId) {
|
||||
return composeQuotePolicy;
|
||||
}
|
||||
const status = statuses.get(statusId);
|
||||
if (!status) {
|
||||
return 'public';
|
||||
|
@ -77,24 +96,25 @@ const selectStatusPolicy = createAppSelector(
|
|||
);
|
||||
|
||||
export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
({ onClose, statusId }, ref) => {
|
||||
({ onClose, onChange, statusId }, ref) => {
|
||||
const intl = useIntl();
|
||||
const currentVisibility = useAppSelector(
|
||||
(state) =>
|
||||
(state.statuses.getIn([statusId, 'visibility'], 'public') as
|
||||
const currentVisibility = useAppSelector((state) =>
|
||||
statusId
|
||||
? ((state.statuses.getIn([statusId, 'visibility'], 'public') as
|
||||
| StatusVisibility
|
||||
| undefined) ?? 'public',
|
||||
| undefined) ?? 'public')
|
||||
: (state.compose.get('privacy') as StatusVisibility),
|
||||
);
|
||||
const currentQuotePolicy = useAppSelector((state) =>
|
||||
selectStatusPolicy(state, statusId),
|
||||
);
|
||||
|
||||
const [visibility, setVisibility] = useState(currentVisibility);
|
||||
const [quotePolicy, setQuotePolicy] = useState(currentQuotePolicy);
|
||||
|
||||
const disableVisibility = !!statusId;
|
||||
const disableQuotePolicy =
|
||||
currentVisibility === 'private' || currentVisibility === 'direct';
|
||||
const isSaving = useAppSelector(
|
||||
(state) =>
|
||||
state.statuses.getIn([statusId, 'isSavingQuotePolicy']) === true,
|
||||
);
|
||||
visibility === 'private' || visibility === 'direct';
|
||||
|
||||
const visibilityItems = useMemo<SelectItem<StatusVisibility>[]>(
|
||||
() => [
|
||||
|
@ -102,21 +122,30 @@ export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
|||
value: 'public',
|
||||
text: intl.formatMessage(privacyMessages.public_short),
|
||||
meta: intl.formatMessage(privacyMessages.public_long),
|
||||
icon: 'globe',
|
||||
iconComponent: PublicIcon,
|
||||
},
|
||||
{
|
||||
value: 'unlisted',
|
||||
text: intl.formatMessage(privacyMessages.unlisted_short),
|
||||
meta: intl.formatMessage(privacyMessages.unlisted_long),
|
||||
extra: intl.formatMessage(privacyMessages.unlisted_extra),
|
||||
icon: 'unlock',
|
||||
iconComponent: QuietTimeIcon,
|
||||
},
|
||||
{
|
||||
value: 'private',
|
||||
text: intl.formatMessage(privacyMessages.private_short),
|
||||
meta: intl.formatMessage(privacyMessages.private_long),
|
||||
icon: 'lock',
|
||||
iconComponent: LockIcon,
|
||||
},
|
||||
{
|
||||
value: 'direct',
|
||||
text: intl.formatMessage(privacyMessages.direct_short),
|
||||
meta: intl.formatMessage(privacyMessages.direct_long),
|
||||
icon: 'at',
|
||||
iconComponent: AlternateEmailIcon,
|
||||
},
|
||||
],
|
||||
[intl],
|
||||
|
@ -133,24 +162,27 @@ export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
|||
[intl],
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleVisibilityChange = useCallback(
|
||||
(value: string) => {
|
||||
// Published statuses cannot change visibility.
|
||||
if (statusId) {
|
||||
return;
|
||||
const handleVisibilityChange = useCallback((value: string) => {
|
||||
if (isStatusVisibility(value)) {
|
||||
setVisibility(value);
|
||||
}
|
||||
dispatch(changeComposeVisibility(value));
|
||||
},
|
||||
[dispatch, statusId],
|
||||
);
|
||||
const handleQuotePolicyChange = useCallback(
|
||||
(value: string) => {
|
||||
if (isQuotePolicy(value) && !disableQuotePolicy) {
|
||||
void dispatch(setStatusQuotePolicy({ policy: value, statusId }));
|
||||
}, []);
|
||||
const handleQuotePolicyChange = useCallback((value: string) => {
|
||||
if (isQuotePolicy(value)) {
|
||||
setQuotePolicy(value);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Save on close
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
getCloseConfirmationMessage() {
|
||||
onChange(visibility, quotePolicy);
|
||||
return null;
|
||||
},
|
||||
[disableQuotePolicy, dispatch, statusId],
|
||||
}),
|
||||
[onChange, quotePolicy, visibility],
|
||||
);
|
||||
|
||||
const privacyDropdownId = useId();
|
||||
|
@ -192,7 +224,7 @@ export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
|||
<label
|
||||
htmlFor={privacyDropdownId}
|
||||
className={classNames('visibility-dropdown__label', {
|
||||
disabled: isSaving || !!statusId,
|
||||
disabled: disableVisibility,
|
||||
})}
|
||||
>
|
||||
<FormattedMessage
|
||||
|
@ -203,10 +235,10 @@ export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
|||
<Dropdown
|
||||
items={visibilityItems}
|
||||
classPrefix='visibility-dropdown'
|
||||
current={currentVisibility}
|
||||
current={visibility}
|
||||
onChange={handleVisibilityChange}
|
||||
title={intl.formatMessage(privacyMessages.change_privacy)}
|
||||
disabled={isSaving || !!statusId}
|
||||
disabled={disableVisibility}
|
||||
id={privacyDropdownId}
|
||||
/>
|
||||
{!!statusId && (
|
||||
|
@ -222,7 +254,7 @@ export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
|||
<label
|
||||
htmlFor={quoteDropdownId}
|
||||
className={classNames('visibility-dropdown__label', {
|
||||
disabled: disableQuotePolicy || isSaving,
|
||||
disabled: disableQuotePolicy,
|
||||
})}
|
||||
>
|
||||
<FormattedMessage
|
||||
|
@ -234,15 +266,12 @@ export const VisibilityModal: FC<VisibilityModalProps> = forwardRef(
|
|||
items={quoteItems}
|
||||
onChange={handleQuotePolicyChange}
|
||||
classPrefix='visibility-dropdown'
|
||||
current={currentQuotePolicy}
|
||||
current={quotePolicy}
|
||||
title={intl.formatMessage(messages.buttonTitle)}
|
||||
disabled={disableQuotePolicy || isSaving}
|
||||
disabled={disableQuotePolicy}
|
||||
id={quoteDropdownId}
|
||||
/>
|
||||
<QuotePolicyHelper
|
||||
policy={currentQuotePolicy}
|
||||
visibility={currentVisibility}
|
||||
/>
|
||||
<QuotePolicyHelper policy={quotePolicy} visibility={visibility} />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -836,22 +836,24 @@
|
|||
"search_results.see_all": "Праглядзець усе",
|
||||
"search_results.statuses": "Допісы",
|
||||
"search_results.title": "Шукаць \"{q}\"",
|
||||
"server_banner.about_active_users": "Людзі, якія карыстаюцца гэтым сервера на працягу апошніх 30 дзён (Штомесячна Актыўныя Карыстальнікі)",
|
||||
"server_banner.about_active_users": "Людзі, якія карысталіся гэтым серверам на працягу апошніх 30 дзён (штомесячна актыўныя карыстальнікі)",
|
||||
"server_banner.active_users": "актыўныя карыстальнікі",
|
||||
"server_banner.administered_by": "Адміністратар:",
|
||||
"server_banner.is_one_of_many": "{domain} - гэта адзін з многіх незалежных сервераў Mastodon, які Вы можаце выкарыстоўваць для ўдзелу ў федэральным сусвеце.",
|
||||
"server_banner.server_stats": "Статыстыка сервера:",
|
||||
"sign_in_banner.create_account": "Стварыць уліковы запіс",
|
||||
"sign_in_banner.follow_anyone": "Падпісвайцеся на каго захочаце ва ўсім федэральным сусвеце і глядзіце ўсё ў храналагічным парадку. Ніякіх алгарытмаў, рэкламы або клікбэйту.",
|
||||
"sign_in_banner.mastodon_is": "Mastodon - лепшы спосаб быць у курсе ўсяго, што адбываецца.",
|
||||
"sign_in_banner.mastodon_is": "Mastodon - найлепшы спосаб быць у курсе ўсяго, што адбываецца.",
|
||||
"sign_in_banner.sign_in": "Увайсці",
|
||||
"sign_in_banner.sso_redirect": "Уваход ці рэгістрацыя",
|
||||
"status.admin_account": "Адкрыць інтэрфейс мадэратара для @{name}",
|
||||
"status.admin_domain": "Адкрыць інтэрфейс мадэратара для {domain}",
|
||||
"status.admin_status": "Адкрыць гэты допіс у інтэрфейсе мадэрацыі",
|
||||
"status.all_disabled": "Пашырэнні і цытаванні адключаны",
|
||||
"status.block": "Заблакаваць @{name}",
|
||||
"status.bookmark": "Дадаць закладку",
|
||||
"status.cancel_reblog_private": "Прыбраць",
|
||||
"status.cannot_quote": "Аўтар допісу адключыў цытаванне",
|
||||
"status.cannot_reblog": "Гэты допіс нельга пашырыць",
|
||||
"status.context.load_new_replies": "Даступныя новыя адказы",
|
||||
"status.context.loading": "Правяраюцца новыя адказы",
|
||||
|
@ -880,6 +882,8 @@
|
|||
"status.mute_conversation": "Ігнараваць размову",
|
||||
"status.open": "Разгарнуць гэты допіс",
|
||||
"status.pin": "Замацаваць у профілі",
|
||||
"status.quote": "Цытаваць",
|
||||
"status.quote.cancel": "Адмяніць цытаванне",
|
||||
"status.quote_error.filtered": "Схавана адным з Вашых фільтраў",
|
||||
"status.quote_error.not_available": "Допіс недаступны",
|
||||
"status.quote_error.pending_approval": "Допіс чакае пацвярджэння",
|
||||
|
@ -887,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Цытаваны допіс чакае пацвярджэння? Захоўвайце спакой",
|
||||
"status.quote_policy_change": "Змяніць, хто можа цытаваць",
|
||||
"status.quote_post_author": "Цытаваў допіс @{name}",
|
||||
"status.quote_private": "Прыватныя допісы нельга цытаваць",
|
||||
"status.read_more": "Чытаць болей",
|
||||
"status.reblog": "Пашырыць",
|
||||
"status.reblog_private": "Пашырыць з першапачатковай бачнасцю",
|
||||
|
|
|
@ -848,9 +848,11 @@
|
|||
"status.admin_account": "Obre la interfície de moderació per a @{name}",
|
||||
"status.admin_domain": "Obre la interfície de moderació per a @{domain}",
|
||||
"status.admin_status": "Obre aquest tut a la interfície de moderació",
|
||||
"status.all_disabled": "S'han deshabilitat impulsos i cites",
|
||||
"status.block": "Bloca @{name}",
|
||||
"status.bookmark": "Marca",
|
||||
"status.cancel_reblog_private": "Desfés l'impuls",
|
||||
"status.cannot_quote": "L'autor no permet que se citi aquesta publicació",
|
||||
"status.cannot_reblog": "No es pot impulsar aquest tut",
|
||||
"status.context.load_new_replies": "Hi ha respostes noves",
|
||||
"status.context.loading": "Comprovació de més respostes",
|
||||
|
@ -879,6 +881,7 @@
|
|||
"status.mute_conversation": "Silencia la conversa",
|
||||
"status.open": "Amplia el tut",
|
||||
"status.pin": "Fixa en el perfil",
|
||||
"status.quote": "Cita",
|
||||
"status.quote.cancel": "Canceŀlar la citació",
|
||||
"status.quote_error.filtered": "No es mostra a causa d'un dels vostres filtres",
|
||||
"status.quote_error.not_available": "Publicació no disponible",
|
||||
|
@ -887,6 +890,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Publicació pendent? Mantinguem la calma",
|
||||
"status.quote_policy_change": "Canvieu qui us pot citar",
|
||||
"status.quote_post_author": "S'ha citat una publicació de @{name}",
|
||||
"status.quote_private": "No es poden citar les publicacions privades",
|
||||
"status.read_more": "Més informació",
|
||||
"status.reblog": "Impulsa",
|
||||
"status.reblog_private": "Impulsa amb la visibilitat original",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Otevřít moderátorské rozhraní pro @{name}",
|
||||
"status.admin_domain": "Otevřít moderátorské rozhraní pro {domain}",
|
||||
"status.admin_status": "Otevřít tento příspěvek v moderátorském rozhraní",
|
||||
"status.all_disabled": "Boosty a citace jsou zakázány",
|
||||
"status.block": "Blokovat @{name}",
|
||||
"status.bookmark": "Přidat do záložek",
|
||||
"status.cancel_reblog_private": "Zrušit boostnutí",
|
||||
"status.cannot_quote": "Autor zakázal citování tohoto příspěvku",
|
||||
"status.cannot_reblog": "Tento příspěvek nemůže být boostnutý",
|
||||
"status.context.load_new_replies": "K dispozici jsou nové odpovědi",
|
||||
"status.context.loading": "Hledání dalších odpovědí",
|
||||
|
@ -880,6 +882,8 @@
|
|||
"status.mute_conversation": "Skrýt konverzaci",
|
||||
"status.open": "Rozbalit tento příspěvek",
|
||||
"status.pin": "Připnout na profil",
|
||||
"status.quote": "Citovat",
|
||||
"status.quote.cancel": "Zrušit citování",
|
||||
"status.quote_error.filtered": "Skryté kvůli jednomu z vašich filtrů",
|
||||
"status.quote_error.not_available": "Příspěvek není dostupný",
|
||||
"status.quote_error.pending_approval": "Příspěvek čeká na schválení",
|
||||
|
@ -887,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Příspěvek čeká na schválení? Buďte klidní",
|
||||
"status.quote_policy_change": "Změňte, kdo může citovat",
|
||||
"status.quote_post_author": "Citovali příspěvek od @{name}",
|
||||
"status.quote_private": "Soukromé příspěvky nelze citovat",
|
||||
"status.read_more": "Číst více",
|
||||
"status.reblog": "Boostnout",
|
||||
"status.reblog_private": "Boostnout s původní viditelností",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "@{name} moderieren",
|
||||
"status.admin_domain": "{domain} moderieren",
|
||||
"status.admin_status": "Beitrag moderieren",
|
||||
"status.all_disabled": "Teilen und Zitieren von Beiträgen ist deaktiviert",
|
||||
"status.block": "@{name} blockieren",
|
||||
"status.bookmark": "Lesezeichen setzen",
|
||||
"status.cancel_reblog_private": "Beitrag nicht mehr teilen",
|
||||
"status.cannot_quote": "Autor*in hat das Zitieren dieses Beitrags deaktiviert",
|
||||
"status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
|
||||
"status.context.load_new_replies": "Neue Antworten verfügbar",
|
||||
"status.context.loading": "Weitere Antworten werden abgerufen",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Unterhaltung stummschalten",
|
||||
"status.open": "Beitrag öffnen",
|
||||
"status.pin": "Im Profil anheften",
|
||||
"status.quote": "Zitieren",
|
||||
"status.quote.cancel": "Zitat abbrechen",
|
||||
"status.quote_error.filtered": "Ausgeblendet wegen eines deiner Filter",
|
||||
"status.quote_error.not_available": "Beitrag nicht verfügbar",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Zitierter Beitrag noch nicht freigegeben? Immer mit der Ruhe",
|
||||
"status.quote_policy_change": "Ändern, wer zitieren darf",
|
||||
"status.quote_post_author": "Zitierte einen Beitrag von @{name}",
|
||||
"status.quote_private": "Private Beiträge können nicht zitiert werden",
|
||||
"status.read_more": "Gesamten Beitrag anschauen",
|
||||
"status.reblog": "Teilen",
|
||||
"status.reblog_private": "Mit der ursprünglichen Zielgruppe teilen",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Άνοιγμα διεπαφής συντονισμού για τον/την @{name}",
|
||||
"status.admin_domain": "Άνοιγμα λειτουργίας διαμεσολάβησης για {domain}",
|
||||
"status.admin_status": "Άνοιγμα αυτής της ανάρτησης σε διεπαφή συντονισμού",
|
||||
"status.all_disabled": "Ενισχύσεις και παραθέσεις είναι απενεργοποιημένες",
|
||||
"status.block": "Αποκλεισμός @{name}",
|
||||
"status.bookmark": "Σελιδοδείκτης",
|
||||
"status.cancel_reblog_private": "Ακύρωση ενίσχυσης",
|
||||
"status.cannot_quote": "Ο συντάκτης έχει απενεργοποιήσει την παράθεση σε αυτή την ανάρτηση",
|
||||
"status.cannot_reblog": "Αυτή η ανάρτηση δεν μπορεί να ενισχυθεί",
|
||||
"status.context.load_new_replies": "Νέες απαντήσεις διαθέσιμες",
|
||||
"status.context.loading": "Γίνεται έλεγχος για περισσότερες απαντήσεις",
|
||||
|
@ -880,6 +882,8 @@
|
|||
"status.mute_conversation": "Σίγαση συνομιλίας",
|
||||
"status.open": "Επέκταση ανάρτησης",
|
||||
"status.pin": "Καρφίτσωσε στο προφίλ",
|
||||
"status.quote": "Παράθεση",
|
||||
"status.quote.cancel": "Ακύρωση παράθεσης",
|
||||
"status.quote_error.filtered": "Κρυφό λόγω ενός από τα φίλτρα σου",
|
||||
"status.quote_error.not_available": "Ανάρτηση μη διαθέσιμη",
|
||||
"status.quote_error.pending_approval": "Ανάρτηση σε αναμονή",
|
||||
|
@ -887,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Παράθεση σε εκκρεμότητα; Μείνετε ψύχραιμοι",
|
||||
"status.quote_policy_change": "Αλλάξτε ποιός μπορεί να κάνει παράθεση",
|
||||
"status.quote_post_author": "Παρατίθεται μια ανάρτηση από @{name}",
|
||||
"status.quote_private": "Ιδιωτικές αναρτήσεις δεν μπορούν να παρατεθούν",
|
||||
"status.read_more": "Διάβασε περισότερα",
|
||||
"status.reblog": "Ενίσχυση",
|
||||
"status.reblog_private": "Ενίσχυση με αρχική ορατότητα",
|
||||
|
|
|
@ -738,11 +738,18 @@
|
|||
"privacy.private.short": "Followers",
|
||||
"privacy.public.long": "Anyone on and off Mastodon",
|
||||
"privacy.public.short": "Public",
|
||||
"privacy.quote.anyone": "{visibility}, anyone can quote",
|
||||
"privacy.quote.disabled": "{visibility}, quotes disabled",
|
||||
"privacy.quote.limited": "{visibility}, quotes limited",
|
||||
"privacy.unlisted.additional": "This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.",
|
||||
"privacy.unlisted.long": "Fewer algorithmic fanfares",
|
||||
"privacy.unlisted.short": "Quiet public",
|
||||
"privacy_policy.last_updated": "Last updated {date}",
|
||||
"privacy_policy.title": "Privacy Policy",
|
||||
"quote_error.poll": "Quoting is not allowed with polls.",
|
||||
"quote_error.quote": "Only one quote at a time is allowed.",
|
||||
"quote_error.unauthorized": "You are not authorized to quote this post.",
|
||||
"quote_error.upload": "Quoting is not allowed with media attachments.",
|
||||
"recommended": "Recommended",
|
||||
"refresh": "Refresh",
|
||||
"regeneration_indicator.please_stand_by": "Please stand by.",
|
||||
|
@ -849,9 +856,11 @@
|
|||
"status.admin_account": "Open moderation interface for @{name}",
|
||||
"status.admin_domain": "Open moderation interface for {domain}",
|
||||
"status.admin_status": "Open this post in the moderation interface",
|
||||
"status.all_disabled": "Boosts and quotes are disabled",
|
||||
"status.block": "Block @{name}",
|
||||
"status.bookmark": "Bookmark",
|
||||
"status.cancel_reblog_private": "Unboost",
|
||||
"status.cannot_quote": "Author has disabled quoting on this post",
|
||||
"status.cannot_reblog": "This post cannot be boosted",
|
||||
"status.context.load_new_replies": "New replies available",
|
||||
"status.context.loading": "Checking for more replies",
|
||||
|
@ -880,6 +889,7 @@
|
|||
"status.mute_conversation": "Mute conversation",
|
||||
"status.open": "Expand this post",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.quote": "Quote",
|
||||
"status.quote.cancel": "Cancel quote",
|
||||
"status.quote_error.filtered": "Hidden due to one of your filters",
|
||||
"status.quote_error.not_available": "Post unavailable",
|
||||
|
@ -888,6 +898,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Pending quote? Remain calm",
|
||||
"status.quote_policy_change": "Change who can quote",
|
||||
"status.quote_post_author": "Quoted a post by @{name}",
|
||||
"status.quote_private": "Private posts cannot be quoted",
|
||||
"status.read_more": "Read more",
|
||||
"status.reblog": "Boost",
|
||||
"status.reblog_private": "Boost with original visibility",
|
||||
|
@ -940,6 +951,7 @@
|
|||
"upload_button.label": "Add images, a video or an audio file",
|
||||
"upload_error.limit": "File upload limit exceeded.",
|
||||
"upload_error.poll": "File upload not allowed with polls.",
|
||||
"upload_error.quote": "File upload not allowed with quotes.",
|
||||
"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.",
|
||||
|
|
|
@ -568,6 +568,8 @@
|
|||
"navigation_bar.preferences": "Preferoj",
|
||||
"navigation_bar.privacy_and_reach": "Privateco kaj atingo",
|
||||
"navigation_bar.search": "Serĉi",
|
||||
"navigation_panel.expand_followed_tags": "Malfermi la menuon de sekvataj haŝetikedoj",
|
||||
"navigation_panel.expand_lists": "Malfermi la listmenuon",
|
||||
"not_signed_in_indicator.not_signed_in": "Necesas saluti por aliri tiun rimedon.",
|
||||
"notification.admin.report": "{name} raportis {target}",
|
||||
"notification.admin.report_account": "{name} raportis {count, plural, one {afiŝon} other {# afiŝojn}} de {target} por {category}",
|
||||
|
@ -837,6 +839,7 @@
|
|||
"status.block": "Bloki @{name}",
|
||||
"status.bookmark": "Aldoni al la legosignoj",
|
||||
"status.cancel_reblog_private": "Ne plu diskonigi",
|
||||
"status.cannot_quote": "La aŭtoro malŝaltis citadon en ĉi tiu afiŝo",
|
||||
"status.cannot_reblog": "Ĉi tiun afiŝon ne eblas diskonigi",
|
||||
"status.context.load_new_replies": "Disponeblaj novaj respondoj",
|
||||
"status.context.loading": "Serĉante pliajn respondojn",
|
||||
|
@ -865,9 +868,16 @@
|
|||
"status.mute_conversation": "Silentigi konversacion",
|
||||
"status.open": "Pligrandigu ĉi tiun afiŝon",
|
||||
"status.pin": "Alpingli al la profilo",
|
||||
"status.quote": "Citaĵo",
|
||||
"status.quote.cancel": "Nuligi citaĵon",
|
||||
"status.quote_error.filtered": "Kaŝita pro unu el viaj filtriloj",
|
||||
"status.quote_error.not_available": "Afiŝo ne disponebla",
|
||||
"status.quote_error.pending_approval": "Pritraktata afiŝo",
|
||||
"status.quote_error.pending_approval_popout.body": "Citaĵoj diskonigitaj tra la Fediverso povas bezoni tempon por montriĝi, ĉar malsamaj serviloj havas malsamajn protokolojn.",
|
||||
"status.quote_error.pending_approval_popout.title": "Ĉu pritraktata citaĵo? Restu trankvila",
|
||||
"status.quote_policy_change": "Ŝanĝi kiu povas citi",
|
||||
"status.quote_post_author": "Citis afiŝon de @{name}",
|
||||
"status.quote_private": "Privataj afiŝoj ne povas esti cititaj",
|
||||
"status.read_more": "Legi pli",
|
||||
"status.reblog": "Diskonigi",
|
||||
"status.reblog_private": "Diskonigi kun la sama videbleco",
|
||||
|
@ -882,6 +892,7 @@
|
|||
"status.reply": "Respondi",
|
||||
"status.replyAll": "Respondi al la fadeno",
|
||||
"status.report": "Raporti @{name}",
|
||||
"status.revoke_quote": "Forigu mian afiŝon el la afiŝo de @{name}",
|
||||
"status.sensitive_warning": "Tikla enhavo",
|
||||
"status.share": "Kundividi",
|
||||
"status.show_less_all": "Montri malpli ĉiun",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Abrir interface de moderación para @{name}",
|
||||
"status.admin_domain": "Abrir interface de moderación para {domain}",
|
||||
"status.admin_status": "Abrir este mensaje en la interface de moderación",
|
||||
"status.all_disabled": "Las adhesiones y citas están deshabilitadas",
|
||||
"status.block": "Bloquear a @{name}",
|
||||
"status.bookmark": "Marcar",
|
||||
"status.cancel_reblog_private": "Quitar adhesión",
|
||||
"status.cannot_quote": "El autor deshabilitó las citas en este mensaje",
|
||||
"status.cannot_reblog": "No se puede adherir a este mensaje",
|
||||
"status.context.load_new_replies": "Hay nuevas respuestas",
|
||||
"status.context.loading": "Buscando más respuestas",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Silenciar conversación",
|
||||
"status.open": "Expandir este mensaje",
|
||||
"status.pin": "Fijar en el perfil",
|
||||
"status.quote": "Citar",
|
||||
"status.quote.cancel": "Cancelar cita",
|
||||
"status.quote_error.filtered": "Oculto debido a uno de tus filtros",
|
||||
"status.quote_error.not_available": "Mensaje no disponible",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "¿Cita pendiente? Esperá un momento",
|
||||
"status.quote_policy_change": "Cambiá quién puede citar",
|
||||
"status.quote_post_author": "Se citó un mensaje de @{name}",
|
||||
"status.quote_private": "No se pueden citar los mensajes privados",
|
||||
"status.read_more": "Leé más",
|
||||
"status.reblog": "Adherir",
|
||||
"status.reblog_private": "Adherir a la audiencia original",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Abrir interfaz de moderación para @{name}",
|
||||
"status.admin_domain": "Abrir interfaz de moderación para {domain}",
|
||||
"status.admin_status": "Abrir este estado en la interfaz de moderación",
|
||||
"status.all_disabled": "Los impulsos y las citas están desactivadas",
|
||||
"status.block": "Bloquear a @{name}",
|
||||
"status.bookmark": "Añadir marcador",
|
||||
"status.cancel_reblog_private": "Deshacer impulso",
|
||||
"status.cannot_quote": "El autor ha desactivado las citas de esta publicación",
|
||||
"status.cannot_reblog": "Esta publicación no puede ser impulsada",
|
||||
"status.context.load_new_replies": "Nuevas respuestas disponibles",
|
||||
"status.context.loading": "Comprobando si hay más respuestas",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Silenciar conversación",
|
||||
"status.open": "Expandir estado",
|
||||
"status.pin": "Fijar",
|
||||
"status.quote": "Cita",
|
||||
"status.quote.cancel": "Cancelar cita",
|
||||
"status.quote_error.filtered": "Oculto debido a uno de tus filtros",
|
||||
"status.quote_error.not_available": "Publicación no disponible",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "¿Cita pendiente? Mantén la calma",
|
||||
"status.quote_policy_change": "Cambia quién puede citarte",
|
||||
"status.quote_post_author": "Ha citado una publicación de @{name}",
|
||||
"status.quote_private": "Las publicaciones privadas no pueden citarse",
|
||||
"status.read_more": "Leer más",
|
||||
"status.reblog": "Impulsar",
|
||||
"status.reblog_private": "Implusar a la audiencia original",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Abrir interfaz de moderación para @{name}",
|
||||
"status.admin_domain": "Abrir interfaz de moderación para {domain}",
|
||||
"status.admin_status": "Abrir esta publicación en la interfaz de moderación",
|
||||
"status.all_disabled": "Los impulsos y las citas están deshabilitados",
|
||||
"status.block": "Bloquear a @{name}",
|
||||
"status.bookmark": "Añadir marcador",
|
||||
"status.cancel_reblog_private": "Deshacer impulso",
|
||||
"status.cannot_quote": "El autor ha deshabilitado las citas en esta publicación",
|
||||
"status.cannot_reblog": "Esta publicación no se puede impulsar",
|
||||
"status.context.load_new_replies": "Hay nuevas respuestas",
|
||||
"status.context.loading": "Buscando más respuestas",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Silenciar conversación",
|
||||
"status.open": "Expandir publicación",
|
||||
"status.pin": "Fijar",
|
||||
"status.quote": "Cita",
|
||||
"status.quote.cancel": "Cancelar cita",
|
||||
"status.quote_error.filtered": "Oculto debido a uno de tus filtros",
|
||||
"status.quote_error.not_available": "Publicación no disponible",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "¿Cita pendiente? Mantén la calma",
|
||||
"status.quote_policy_change": "Cambia quién puede citarte",
|
||||
"status.quote_post_author": "Ha citado una publicación de @{name}",
|
||||
"status.quote_private": "Las publicaciones privadas no pueden ser citadas",
|
||||
"status.read_more": "Leer más",
|
||||
"status.reblog": "Impulsar",
|
||||
"status.reblog_private": "Impulsar a la audiencia original",
|
||||
|
@ -966,7 +970,7 @@
|
|||
"visibility_modal.button_title": "Configura la visibilidad",
|
||||
"visibility_modal.header": "Visibilidad e interacciones",
|
||||
"visibility_modal.helper.direct_quoting": "Las menciones privadas no se pueden citar.",
|
||||
"visibility_modal.helper.privacy_editing": "Las publicaciones no pueden cambiar su visibilidad.",
|
||||
"visibility_modal.helper.privacy_editing": "Una vez publicada, no se puede cambiar su visibilidad.",
|
||||
"visibility_modal.helper.private_quoting": "Las publicaciones para seguidores no pueden citarse.",
|
||||
"visibility_modal.helper.unlisted_quoting": "Cuando la gente te cite, su publicación tampoco se mostrará en las cronologías públicas.",
|
||||
"visibility_modal.instructions": "Controla quién puede interactuar con esta publicación. Puedes encontrar los ajustes globales en <link>Preferencias > Otros</link>.",
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
"account.followers": "پیگیرندگان",
|
||||
"account.followers.empty": "هنوز کسی پیگیر این کاربر نیست.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} پیگیرنده} other {{counter} پیگیرنده}}",
|
||||
"account.followers_you_know_counter": "{counter} را میشناسید",
|
||||
"account.followers_you_know_counter": "{counter} نفر را میشناسید",
|
||||
"account.following": "پی میگیرید",
|
||||
"account.following_counter": "{count, plural, one {{counter} پیگرفته} other {{counter} پیگرفته}}",
|
||||
"account.follows.empty": "این کاربر هنوز پیگیر کسی نیست.",
|
||||
|
@ -880,6 +880,7 @@
|
|||
"status.mute_conversation": "خموشاندن گفتوگو",
|
||||
"status.open": "گسترش این فرسته",
|
||||
"status.pin": "سنجاق به نمایه",
|
||||
"status.quote.cancel": "لغو نقل",
|
||||
"status.quote_error.filtered": "نهفته بنا بر یکی از پالایههایتان",
|
||||
"status.quote_error.not_available": "فرسته در دسترس نیست",
|
||||
"status.quote_error.pending_approval": "فرسته منتظر",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Avaa tilin @{name} moderointinäkymä",
|
||||
"status.admin_domain": "Avaa palvelimen {domain} moderointinäkymä",
|
||||
"status.admin_status": "Avaa julkaisu moderointinäkymässä",
|
||||
"status.all_disabled": "Tehostukset ja lainaukset ovat poissa käytöstä",
|
||||
"status.block": "Estä @{name}",
|
||||
"status.bookmark": "Lisää kirjanmerkki",
|
||||
"status.cancel_reblog_private": "Peru tehostus",
|
||||
"status.cannot_quote": "Julkaisun tekijä on poistanut lainaamisen käytöstä",
|
||||
"status.cannot_reblog": "Tätä julkaisua ei voi tehostaa",
|
||||
"status.context.load_new_replies": "Uusia vastauksia saatavilla",
|
||||
"status.context.loading": "Tarkistetaan lisävastauksia",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Mykistä keskustelu",
|
||||
"status.open": "Laajenna julkaisu",
|
||||
"status.pin": "Kiinnitä profiiliin",
|
||||
"status.quote": "Lainaa",
|
||||
"status.quote.cancel": "Peruuta lainaus",
|
||||
"status.quote_error.filtered": "Piilotettu jonkin asettamasi suodattimen takia",
|
||||
"status.quote_error.not_available": "Julkaisu ei saatavilla",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Odottava lainaus? Pysy rauhallisena",
|
||||
"status.quote_policy_change": "Vaihda, kuka voi lainata",
|
||||
"status.quote_post_author": "Lainaa käyttäjän @{name} julkaisua",
|
||||
"status.quote_private": "Yksityisiä julkaisuja ei voi lainata",
|
||||
"status.read_more": "Näytä enemmän",
|
||||
"status.reblog": "Tehosta",
|
||||
"status.reblog_private": "Tehosta alkuperäiselle yleisölle",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Lat kjakleiðaramarkamót upp fyri @{name}",
|
||||
"status.admin_domain": "Lat umsjónarmarkamót upp fyri {domain}",
|
||||
"status.admin_status": "Lat hendan postin upp í kjakleiðaramarkamótinum",
|
||||
"status.all_disabled": "Stimbranir og sitatir eru gjørd óvirkin",
|
||||
"status.block": "Blokera @{name}",
|
||||
"status.bookmark": "Goym",
|
||||
"status.cancel_reblog_private": "Strika stimbran",
|
||||
"status.cannot_quote": "Høvundurin hevur gjørt sitering óvirkna fyri hendan postin",
|
||||
"status.cannot_reblog": "Tað ber ikki til at stimbra hendan postin",
|
||||
"status.context.load_new_replies": "Nýggj svar tøk",
|
||||
"status.context.loading": "Kanni um tað eru fleiri svar",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Doyv samrøðu",
|
||||
"status.open": "Víðka henda postin",
|
||||
"status.pin": "Ger fastan í vangan",
|
||||
"status.quote": "Sitat",
|
||||
"status.quote.cancel": "Ógilda sitat",
|
||||
"status.quote_error.filtered": "Eitt av tínum filtrum fjalir hetta",
|
||||
"status.quote_error.not_available": "Postur ikki tøkur",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Bíðar eftir sitati? Tak tað róligt",
|
||||
"status.quote_policy_change": "Broyt hvør kann sitera",
|
||||
"status.quote_post_author": "Siteraði ein post hjá @{name}",
|
||||
"status.quote_private": "Privatir postar kunnu ikki siterast",
|
||||
"status.read_more": "Les meira",
|
||||
"status.reblog": "Stimbra",
|
||||
"status.reblog_private": "Stimbra við upprunasýni",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "פתח/י ממשק פיקוח דיון עבור @{name}",
|
||||
"status.admin_domain": "פתיחת ממשק פיקוח דיון עבור {domain}",
|
||||
"status.admin_status": "לפתוח הודעה זו במסך ניהול הדיונים",
|
||||
"status.all_disabled": "הדהודים וציטוטים כובו",
|
||||
"status.block": "חסימת @{name}",
|
||||
"status.bookmark": "סימניה",
|
||||
"status.cancel_reblog_private": "הסרת הדהוד",
|
||||
"status.cannot_quote": "יוצר\\ת ההודעה חסמו את האפשרות לצטט אותה",
|
||||
"status.cannot_reblog": "לא ניתן להדהד חצרוץ זה",
|
||||
"status.context.load_new_replies": "הגיעו תגובות חדשות",
|
||||
"status.context.loading": "מחפש תגובות חדשות",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "השתקת שיחה",
|
||||
"status.open": "הרחבת הודעה זו",
|
||||
"status.pin": "הצמדה לפרופיל שלי",
|
||||
"status.quote": "ציטוט",
|
||||
"status.quote.cancel": "ביטול הודעת ציטוט",
|
||||
"status.quote_error.filtered": "מוסתר בהתאם לסננים שלך",
|
||||
"status.quote_error.not_available": "ההודעה לא זמינה",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "ההודעה בהמתנה? המתינו ברוגע",
|
||||
"status.quote_policy_change": "הגדרת הרשאה לציטוט הודעותיך",
|
||||
"status.quote_post_author": "ההודעה צוטטה על ידי @{name}",
|
||||
"status.quote_private": "הודעות פרטיות לא ניתנות לציטוט",
|
||||
"status.read_more": "לקרוא עוד",
|
||||
"status.reblog": "הדהוד",
|
||||
"status.reblog_private": "להדהד ברמת הנראות המקורית",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"about.blocks": "Servitores moderate",
|
||||
"about.contact": "Contacto:",
|
||||
"about.default_locale": "Default",
|
||||
"about.disclaimer": "Mastodon es software libere, de codice aperte, e un marca de Mastodon gGmbH.",
|
||||
"about.domain_blocks.no_reason_available": "Ration non disponibile",
|
||||
"about.domain_blocks.preamble": "Mastodon generalmente permitte vider le contento de, e interager con, usatores de qualcunque altere servitor in le fediverso. Istes es le exceptiones que ha essite facite sur iste servitor particular.",
|
||||
|
@ -8,6 +9,7 @@
|
|||
"about.domain_blocks.silenced.title": "Limitate",
|
||||
"about.domain_blocks.suspended.explanation": "Nulle datos de iste servitor essera processate, immagazinate o excambiate, rendente omne interaction o communication con usatores de iste servitor impossibile.",
|
||||
"about.domain_blocks.suspended.title": "Suspendite",
|
||||
"about.language_label": "Lingua",
|
||||
"about.not_available": "Iste information non ha essite rendite disponibile sur iste servitor.",
|
||||
"about.powered_by": "Rete social decentralisate, actionate per {mastodon}",
|
||||
"about.rules": "Regulas del servitor",
|
||||
|
@ -41,6 +43,7 @@
|
|||
"account.followers": "Sequitores",
|
||||
"account.followers.empty": "Necuno seque ancora iste usator.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} sequitor} other {{counter} sequitores}}",
|
||||
"account.followers_you_know_counter": "{counter} que tu cognosce",
|
||||
"account.following": "Sequente",
|
||||
"account.following_counter": "{count, plural, one {{counter} sequite} other {{counter} sequites}}",
|
||||
"account.follows.empty": "Iste usator non seque ancora alcuno.",
|
||||
|
@ -216,6 +219,13 @@
|
|||
"confirmations.delete_list.confirm": "Deler",
|
||||
"confirmations.delete_list.message": "Es tu secur que tu vole deler permanentemente iste lista?",
|
||||
"confirmations.delete_list.title": "Deler lista?",
|
||||
"confirmations.discard_draft.confirm": "Discartar e continuar",
|
||||
"confirmations.discard_draft.edit.cancel": "Reprender le modification",
|
||||
"confirmations.discard_draft.edit.message": "Si tu continua, tu perdera tote le modificatione que tu ha apportate al message que tu modifica in iste momento.",
|
||||
"confirmations.discard_draft.edit.title": "Discartar le modificationes de tu message?",
|
||||
"confirmations.discard_draft.post.cancel": "Retornar al esbosso",
|
||||
"confirmations.discard_draft.post.message": "Si tu continua, tu perdera le message que tu compone in iste momento.",
|
||||
"confirmations.discard_draft.post.title": "Discartar tu esbosso de message?",
|
||||
"confirmations.discard_edit_media.confirm": "Abandonar",
|
||||
"confirmations.discard_edit_media.message": "Tu ha cambiamentos non salvate in le description o previsualisation del objecto multimedial. Abandonar los?",
|
||||
"confirmations.follow_to_list.confirm": "Sequer e adder al lista",
|
||||
|
@ -235,6 +245,9 @@
|
|||
"confirmations.remove_from_followers.confirm": "Remover sequitor",
|
||||
"confirmations.remove_from_followers.message": "{name} non plus te sequera. Es tu secur de voler continuar?",
|
||||
"confirmations.remove_from_followers.title": "Remover sequitor?",
|
||||
"confirmations.revoke_quote.confirm": "Remover message",
|
||||
"confirmations.revoke_quote.message": "Iste action non pote esser disfacite.",
|
||||
"confirmations.revoke_quote.title": "Remover message?",
|
||||
"confirmations.unfollow.confirm": "Non plus sequer",
|
||||
"confirmations.unfollow.message": "Es tu secur que tu vole cessar de sequer {name}?",
|
||||
"confirmations.unfollow.title": "Cessar de sequer le usator?",
|
||||
|
@ -279,6 +292,7 @@
|
|||
"domain_pill.your_handle": "Tu pseudonymo:",
|
||||
"domain_pill.your_server": "Tu casa digital, ubi vive tote tu messages. Non te place? Cambia de servitor a omne momento e porta tu sequitores con te.",
|
||||
"domain_pill.your_username": "Tu identificator unic sur iste servitor. Es possibile trovar usatores con le mesme nomine de usator sur servitores differente.",
|
||||
"dropdown.empty": "Selige un option",
|
||||
"embed.instructions": "Incorpora iste message sur tu sito web con le codice sequente.",
|
||||
"embed.preview": "Ecce como illlo parera:",
|
||||
"emoji_button.activity": "Activitate",
|
||||
|
@ -296,6 +310,8 @@
|
|||
"emoji_button.search_results": "Resultatos de recerca",
|
||||
"emoji_button.symbols": "Symbolos",
|
||||
"emoji_button.travel": "Viages e locos",
|
||||
"empty_column.account_featured.me": "Tu non ha ancora mittite alcun cosa in evidentia. Sapeva tu que tu pote mitter in evidentia sur tu profilo le hashtags que tu usa le plus e mesmo le contos de tu amicos?",
|
||||
"empty_column.account_featured.other": "{acct} non ha ancora mittite alcun cosa in evidentia. Sapeva tu que tu pote mitter in evidentia sur tu profilo le hashtags que tu usa le plus e mesmo le contos de tu amicos?",
|
||||
"empty_column.account_featured_other.unknown": "Iste conto non ha ancora mittite alcun cosa in evidentia.",
|
||||
"empty_column.account_hides_collections": "Le usator non ha rendite iste information disponibile",
|
||||
"empty_column.account_suspended": "Conto suspendite",
|
||||
|
@ -325,9 +341,15 @@
|
|||
"errors.unexpected_crash.copy_stacktrace": "Copiar le traciamento del pila al area de transferentia",
|
||||
"errors.unexpected_crash.report_issue": "Reportar problema",
|
||||
"explore.suggested_follows": "Personas",
|
||||
"explore.title": "In tendentia",
|
||||
"explore.trending_links": "Novas",
|
||||
"explore.trending_statuses": "Messages",
|
||||
"explore.trending_tags": "Hashtags",
|
||||
"featured_carousel.header": "{count, plural, one {Message fixate} other {Messages fixate}}",
|
||||
"featured_carousel.next": "Sequente",
|
||||
"featured_carousel.post": "Message",
|
||||
"featured_carousel.previous": "Precedente",
|
||||
"featured_carousel.slide": "{index} de {total}",
|
||||
"filter_modal.added.context_mismatch_explanation": "Iste categoria de filtros non se applica al contexto in le qual tu ha accedite a iste message. Pro filtrar le message in iste contexto tamben, modifica le filtro.",
|
||||
"filter_modal.added.context_mismatch_title": "Contexto incoherente!",
|
||||
"filter_modal.added.expired_explanation": "Iste categoria de filtros ha expirate. Tu debe modificar le data de expiration pro applicar lo.",
|
||||
|
@ -380,6 +402,8 @@
|
|||
"generic.saved": "Salvate",
|
||||
"getting_started.heading": "Prime passos",
|
||||
"hashtag.admin_moderation": "Aperir le interfacie de moderation pro #{name}",
|
||||
"hashtag.browse": "Percurrer messages in #{hashtag}",
|
||||
"hashtag.browse_from_account": "Percurrer le messages de @{name} in #{hashtag}",
|
||||
"hashtag.column_header.tag_mode.all": "e {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "o {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "sin {additional}",
|
||||
|
@ -404,6 +428,7 @@
|
|||
"hints.profiles.see_more_followers": "Vider plus de sequitores sur {domain}",
|
||||
"hints.profiles.see_more_follows": "Vider plus de sequites sur {domain}",
|
||||
"hints.profiles.see_more_posts": "Vider plus de messages sur {domain}",
|
||||
"home.column_settings.show_quotes": "Monstrar citationes",
|
||||
"home.column_settings.show_reblogs": "Monstrar impulsos",
|
||||
"home.column_settings.show_replies": "Monstrar responsas",
|
||||
"home.hide_announcements": "Celar annuncios",
|
||||
|
@ -477,6 +502,8 @@
|
|||
"keyboard_shortcuts.translate": "a traducer un message",
|
||||
"keyboard_shortcuts.unfocus": "Disfocalisar le area de composition de texto/de recerca",
|
||||
"keyboard_shortcuts.up": "Displaciar in alto in le lista",
|
||||
"learn_more_link.got_it": "Comprendite",
|
||||
"learn_more_link.learn_more": "Pro saper plus",
|
||||
"lightbox.close": "Clauder",
|
||||
"lightbox.next": "Sequente",
|
||||
"lightbox.previous": "Precedente",
|
||||
|
@ -526,8 +553,10 @@
|
|||
"mute_modal.you_wont_see_mentions": "Tu non videra le messages que mentiona iste persona.",
|
||||
"mute_modal.you_wont_see_posts": "Iste persona pote totevia vider tu messages, ma tu non videra le sues.",
|
||||
"navigation_bar.about": "A proposito",
|
||||
"navigation_bar.account_settings": "Contrasigno e securitate",
|
||||
"navigation_bar.administration": "Administration",
|
||||
"navigation_bar.advanced_interface": "Aperir in le interfacie web avantiate",
|
||||
"navigation_bar.automated_deletion": "Deletion automatic del message",
|
||||
"navigation_bar.blocks": "Usatores blocate",
|
||||
"navigation_bar.bookmarks": "Marcapaginas",
|
||||
"navigation_bar.direct": "Mentiones private",
|
||||
|
@ -537,13 +566,23 @@
|
|||
"navigation_bar.follow_requests": "Requestas de sequimento",
|
||||
"navigation_bar.followed_tags": "Hashtags sequite",
|
||||
"navigation_bar.follows_and_followers": "Sequites e sequitores",
|
||||
"navigation_bar.import_export": "Importar e exportar",
|
||||
"navigation_bar.lists": "Listas",
|
||||
"navigation_bar.live_feed_local": "Canal in directo (local)",
|
||||
"navigation_bar.live_feed_public": "Canal in directo (public)",
|
||||
"navigation_bar.logout": "Clauder session",
|
||||
"navigation_bar.moderation": "Moderation",
|
||||
"navigation_bar.more": "Plus",
|
||||
"navigation_bar.mutes": "Usatores silentiate",
|
||||
"navigation_bar.opened_in_classic_interface": "Messages, contos e altere paginas specific es aperite per predefinition in le interfacie web classic.",
|
||||
"navigation_bar.preferences": "Preferentias",
|
||||
"navigation_bar.privacy_and_reach": "Confidentialitate e portata",
|
||||
"navigation_bar.search": "Cercar",
|
||||
"navigation_bar.search_trends": "Cercar / In tendentia",
|
||||
"navigation_panel.collapse_followed_tags": "Contraher le menu de hashtags sequite",
|
||||
"navigation_panel.collapse_lists": "Contraher le menu de listas",
|
||||
"navigation_panel.expand_followed_tags": "Expander le menu de hashtags sequite",
|
||||
"navigation_panel.expand_lists": "Expander le menu de listas",
|
||||
"not_signed_in_indicator.not_signed_in": "Es necessari aperir session pro acceder a iste ressource.",
|
||||
"notification.admin.report": "{name} ha reportate {target}",
|
||||
"notification.admin.report_account": "{name} ha reportate {count, plural, one {un message} other {# messages}} de {target} per {category}",
|
||||
|
@ -565,6 +604,7 @@
|
|||
"notification.label.mention": "Mention",
|
||||
"notification.label.private_mention": "Mention private",
|
||||
"notification.label.private_reply": "Responsa private",
|
||||
"notification.label.quote": "{name} ha citate tu message",
|
||||
"notification.label.reply": "Responder",
|
||||
"notification.mention": "Mention",
|
||||
"notification.mentioned_you": "{name} te ha mentionate",
|
||||
|
@ -622,6 +662,7 @@
|
|||
"notifications.column_settings.mention": "Mentiones:",
|
||||
"notifications.column_settings.poll": "Resultatos del sondage:",
|
||||
"notifications.column_settings.push": "Notificationes push",
|
||||
"notifications.column_settings.quote": "Citationes:",
|
||||
"notifications.column_settings.reblog": "Impulsos:",
|
||||
"notifications.column_settings.show": "Monstrar in columna",
|
||||
"notifications.column_settings.sound": "Reproducer sono",
|
||||
|
@ -770,6 +811,7 @@
|
|||
"report_notification.categories.violation": "Violation del regulas",
|
||||
"report_notification.categories.violation_sentence": "violation del regulas",
|
||||
"report_notification.open": "Aperir reporto",
|
||||
"search.clear": "Rader le recerca",
|
||||
"search.no_recent_searches": "Nulle recercas recente",
|
||||
"search.placeholder": "Cercar",
|
||||
"search.quick_action.account_search": "Profilos correspondente a {x}",
|
||||
|
@ -811,6 +853,8 @@
|
|||
"status.bookmark": "Adder al marcapaginas",
|
||||
"status.cancel_reblog_private": "Disfacer impulso",
|
||||
"status.cannot_reblog": "Iste message non pote esser impulsate",
|
||||
"status.context.load_new_replies": "Nove responsas disponibile",
|
||||
"status.context.loading": "Cercante plus responsas",
|
||||
"status.continued_thread": "Continuation del discussion",
|
||||
"status.copy": "Copiar ligamine a message",
|
||||
"status.delete": "Deler",
|
||||
|
@ -836,6 +880,14 @@
|
|||
"status.mute_conversation": "Silentiar conversation",
|
||||
"status.open": "Expander iste message",
|
||||
"status.pin": "Fixar sur profilo",
|
||||
"status.quote.cancel": "Cancellar le citation",
|
||||
"status.quote_error.filtered": "Celate a causa de un de tu filtros",
|
||||
"status.quote_error.not_available": "Message indisponibile",
|
||||
"status.quote_error.pending_approval": "Message pendente",
|
||||
"status.quote_error.pending_approval_popout.body": "Le citationes distribuite a transverso le Fediverso pote tardar in apparer, perque differente servitores ha differente protocollos.",
|
||||
"status.quote_error.pending_approval_popout.title": "Citation pendente? Resta calme",
|
||||
"status.quote_policy_change": "Cambiar qui pote citar",
|
||||
"status.quote_post_author": "Ha citate un message de @{name}",
|
||||
"status.read_more": "Leger plus",
|
||||
"status.reblog": "Impulsar",
|
||||
"status.reblog_private": "Impulsar con visibilitate original",
|
||||
|
@ -850,6 +902,7 @@
|
|||
"status.reply": "Responder",
|
||||
"status.replyAll": "Responder al discussion",
|
||||
"status.report": "Reportar @{name}",
|
||||
"status.revoke_quote": "Remover mi message del message de @{name}",
|
||||
"status.sensitive_warning": "Contento sensibile",
|
||||
"status.share": "Compartir",
|
||||
"status.show_less_all": "Monstrar minus pro totes",
|
||||
|
@ -865,9 +918,13 @@
|
|||
"subscribed_languages.save": "Salvar le cambiamentos",
|
||||
"subscribed_languages.target": "Cambiar le linguas subscribite pro {target}",
|
||||
"tabs_bar.home": "Initio",
|
||||
"tabs_bar.menu": "Menu",
|
||||
"tabs_bar.notifications": "Notificationes",
|
||||
"tabs_bar.publish": "Nove message",
|
||||
"tabs_bar.search": "Cercar",
|
||||
"terms_of_service.effective_as_of": "In vigor a partir de {date}",
|
||||
"terms_of_service.title": "Conditiones de servicio",
|
||||
"terms_of_service.upcoming_changes_on": "Cambiamentos a venir le {date}",
|
||||
"time_remaining.days": "{number, plural, one {# die} other {# dies}} restante",
|
||||
"time_remaining.hours": "{number, plural, one {# hora} other {# horas}} restante",
|
||||
"time_remaining.minutes": "{number, plural, one {# minuta} other {# minutas}} restante",
|
||||
|
@ -875,7 +932,7 @@
|
|||
"time_remaining.seconds": "{number, plural, one {# secunda} other {# secundas}} restante",
|
||||
"trends.counter_by_accounts": "{count, plural, one {{counter} persona} other {{counter} personas}} in le passate {days, plural, one {die} other {{days} dies}}",
|
||||
"trends.trending_now": "Ora in tendentias",
|
||||
"ui.beforeunload": "Tu esbosso essera predite si tu exi de Mastodon.",
|
||||
"ui.beforeunload": "Tu esbosso essera perdite si tu exi de Mastodon.",
|
||||
"units.short.billion": "{count}B",
|
||||
"units.short.million": "{count}M",
|
||||
"units.short.thousand": "{count}K",
|
||||
|
@ -902,7 +959,20 @@
|
|||
"video.pause": "Pausa",
|
||||
"video.play": "Reproducer",
|
||||
"video.skip_backward": "Saltar a retro",
|
||||
"video.skip_forward": "Avantiar",
|
||||
"video.unmute": "Non plus silentiar",
|
||||
"video.volume_down": "Abassar le volumine",
|
||||
"video.volume_up": "Augmentar le volumine"
|
||||
"video.volume_up": "Augmentar le volumine",
|
||||
"visibility_modal.button_title": "Definir visibilitate",
|
||||
"visibility_modal.header": "Visibilitate e interaction",
|
||||
"visibility_modal.helper.direct_quoting": "Le mentiones private non pote esser citate.",
|
||||
"visibility_modal.helper.privacy_editing": "Le messages ja publicate non pote cambiar de visibilitate.",
|
||||
"visibility_modal.helper.private_quoting": "Le messages reservate al sequitores non pote esser citate.",
|
||||
"visibility_modal.helper.unlisted_quoting": "Quando un persona te cita, su message essera tamben celate del chronologia \"In tendentia\".",
|
||||
"visibility_modal.instructions": "Controla qui pote interager con iste message. Le parametros global se trova sub <link>Preferentias > Alteres</link>.",
|
||||
"visibility_modal.privacy_label": "Confidentialitate",
|
||||
"visibility_modal.quote_followers": "Solmente sequitores",
|
||||
"visibility_modal.quote_label": "Cambiar qui pote citar",
|
||||
"visibility_modal.quote_nobody": "Necuno",
|
||||
"visibility_modal.quote_public": "Omnes"
|
||||
}
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Opna umsjónarviðmót fyrir @{name}",
|
||||
"status.admin_domain": "Opna umsjónarviðmót fyrir @{domain}",
|
||||
"status.admin_status": "Opna þessa færslu í umsjónarviðmótinu",
|
||||
"status.all_disabled": "Endurbirtingar og tilvitnanir eru óvirkar",
|
||||
"status.block": "Útiloka @{name}",
|
||||
"status.bookmark": "Bókamerki",
|
||||
"status.cancel_reblog_private": "Taka úr endurbirtingu",
|
||||
"status.cannot_quote": "Höfundurinn hefur gert tilvitnanir óvirkar fyrir þessa færslu",
|
||||
"status.cannot_reblog": "Þessa færslu er ekki hægt að endurbirta",
|
||||
"status.context.load_new_replies": "Ný svör hafa borist",
|
||||
"status.context.loading": "Athuga með fleiri svör",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Þagga niður í samtali",
|
||||
"status.open": "Opna þessa færslu",
|
||||
"status.pin": "Festa á notandasnið",
|
||||
"status.quote": "Tilvitnun",
|
||||
"status.quote.cancel": "Hætta við tilvitnun",
|
||||
"status.quote_error.filtered": "Falið vegna einnar síu sem er virk",
|
||||
"status.quote_error.not_available": "Færsla ekki tiltæk",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Færsla í bið? Verum róleg",
|
||||
"status.quote_policy_change": "Breyttu því hver getur tilvitnað",
|
||||
"status.quote_post_author": "Vitnaði í færslu frá @{name}",
|
||||
"status.quote_private": "Ekki er hægt að vitna í einkafærslur",
|
||||
"status.read_more": "Lesa meira",
|
||||
"status.reblog": "Endurbirting",
|
||||
"status.reblog_private": "Endurbirta til upphaflegra lesenda",
|
||||
|
|
|
@ -245,6 +245,9 @@
|
|||
"confirmations.remove_from_followers.confirm": "Rimuovi il seguace",
|
||||
"confirmations.remove_from_followers.message": "{name} smetterà di seguirti. Si è sicuri di voler procedere?",
|
||||
"confirmations.remove_from_followers.title": "Rimuovi il seguace?",
|
||||
"confirmations.revoke_quote.confirm": "Elimina il post",
|
||||
"confirmations.revoke_quote.message": "Questa azione non può essere annullata.",
|
||||
"confirmations.revoke_quote.title": "Rimuovere il post?",
|
||||
"confirmations.unfollow.confirm": "Smetti di seguire",
|
||||
"confirmations.unfollow.message": "Sei sicuro di voler smettere di seguire {name}?",
|
||||
"confirmations.unfollow.title": "Smettere di seguire l'utente?",
|
||||
|
@ -289,6 +292,7 @@
|
|||
"domain_pill.your_handle": "Il tuo nome univoco:",
|
||||
"domain_pill.your_server": "La tua casa digitale, dove vivono tutti i tuoi post. Non ti piace questa? Cambia server in qualsiasi momento e porta con te anche i tuoi seguaci.",
|
||||
"domain_pill.your_username": "Il tuo identificatore univoco su questo server. È possibile trovare utenti con lo stesso nome utente su server diversi.",
|
||||
"dropdown.empty": "Seleziona un'opzione",
|
||||
"embed.instructions": "Incorpora questo post sul tuo sito web, copiando il seguente codice.",
|
||||
"embed.preview": "Ecco come apparirà:",
|
||||
"emoji_button.activity": "Attività",
|
||||
|
@ -845,9 +849,11 @@
|
|||
"status.admin_account": "Apri interfaccia di moderazione per @{name}",
|
||||
"status.admin_domain": "Apri l'interfaccia di moderazione per {domain}",
|
||||
"status.admin_status": "Apri questo post nell'interfaccia di moderazione",
|
||||
"status.all_disabled": "Condivisioni e citazioni sono disabilitate",
|
||||
"status.block": "Blocca @{name}",
|
||||
"status.bookmark": "Aggiungi segnalibro",
|
||||
"status.cancel_reblog_private": "Annulla reblog",
|
||||
"status.cannot_quote": "L'autore ha disabilitato le citazioni su questo post",
|
||||
"status.cannot_reblog": "Questo post non può essere condiviso",
|
||||
"status.context.load_new_replies": "Nuove risposte disponibili",
|
||||
"status.context.loading": "Controllo per altre risposte",
|
||||
|
@ -876,12 +882,16 @@
|
|||
"status.mute_conversation": "Silenzia conversazione",
|
||||
"status.open": "Espandi questo post",
|
||||
"status.pin": "Fissa in cima sul profilo",
|
||||
"status.quote": "Cita",
|
||||
"status.quote.cancel": "Annulla la citazione",
|
||||
"status.quote_error.filtered": "Nascosto a causa di uno dei tuoi filtri",
|
||||
"status.quote_error.not_available": "Post non disponibile",
|
||||
"status.quote_error.pending_approval": "Post in attesa",
|
||||
"status.quote_error.pending_approval_popout.body": "Le citazioni condivise in tutto il Fediverso possono richiedere del tempo per la visualizzazione, poiché server diversi hanno protocolli diversi.",
|
||||
"status.quote_error.pending_approval_popout.title": "Citazione in attesa? Resta calmo",
|
||||
"status.quote_policy_change": "Cambia chi può citare",
|
||||
"status.quote_post_author": "Citato un post di @{name}",
|
||||
"status.quote_private": "I post privati non possono essere citati",
|
||||
"status.read_more": "Leggi di più",
|
||||
"status.reblog": "Reblog",
|
||||
"status.reblog_private": "Reblog con visibilità originale",
|
||||
|
@ -896,6 +906,7 @@
|
|||
"status.reply": "Rispondi",
|
||||
"status.replyAll": "Rispondi alla conversazione",
|
||||
"status.report": "Segnala @{name}",
|
||||
"status.revoke_quote": "Rimuovi la mia citazione dal post di @{name}",
|
||||
"status.sensitive_warning": "Contenuto sensibile",
|
||||
"status.share": "Condividi",
|
||||
"status.show_less_all": "Mostra meno per tutti",
|
||||
|
@ -955,5 +966,17 @@
|
|||
"video.skip_forward": "Vai avanti",
|
||||
"video.unmute": "Muta",
|
||||
"video.volume_down": "Abbassa volume",
|
||||
"video.volume_up": "Alza volume"
|
||||
"video.volume_up": "Alza volume",
|
||||
"visibility_modal.button_title": "Imposta la visibilità",
|
||||
"visibility_modal.header": "Visibilità e interazione",
|
||||
"visibility_modal.helper.direct_quoting": "Le menzioni private non possono essere citate.",
|
||||
"visibility_modal.helper.privacy_editing": "La visibilità dei post pubblicati non può essere modificata.",
|
||||
"visibility_modal.helper.private_quoting": "I post riservati ai seguaci non possono essere citati.",
|
||||
"visibility_modal.helper.unlisted_quoting": "Quando le persone ti citano, il loro post verrà nascosto anche dalle timeline di tendenza.",
|
||||
"visibility_modal.instructions": "Controlla chi può interagire con questo post. Le impostazioni globali si trovano in <link>Preferenze > Altro</link>.",
|
||||
"visibility_modal.privacy_label": "Privacy",
|
||||
"visibility_modal.quote_followers": "Solo i seguaci",
|
||||
"visibility_modal.quote_label": "Cambia chi può citare",
|
||||
"visibility_modal.quote_nobody": "Nessuno",
|
||||
"visibility_modal.quote_public": "Chiunque"
|
||||
}
|
||||
|
|
|
@ -23,11 +23,14 @@
|
|||
"account.edit_profile": "प्रोफाइल सम्पादन गर्नुहोस्",
|
||||
"account.enable_notifications": "@{name} ले पोस्ट गर्दा मलाई सूचित गर्नुहोस्",
|
||||
"account.endorse": "प्रोफाइलमा फिचर गर्नुहोस्",
|
||||
"account.featured": "फिचर गरिएको",
|
||||
"account.featured.accounts": "प्रोफाइलहरू",
|
||||
"account.featured.hashtags": "ह्यासट्यागहरू",
|
||||
"account.featured_tags.last_status_never": "कुनै पोस्ट छैन",
|
||||
"account.follow": "फलो गर्नुहोस",
|
||||
"account.follow_back": "फलो ब्याक गर्नुहोस्",
|
||||
"account.followers": "फलोअरहरु",
|
||||
"account.followers.empty": "यस प्रयोगकर्तालाई अहिलेसम्म कसैले फलो गर्दैन।",
|
||||
"account.followers.empty": "यस प्रयोगकर्तालाई अहिलेसम्म कसैले फलो गरेको छैन।",
|
||||
"account.followers_counter": "{count, plural, one {{counter} फलोअर} other {{counter} फलोअरहरू}}",
|
||||
"account.following": "फलो गर्दै",
|
||||
"account.following_counter": "{count, plural, one {{counter} फलो गर्दै} other {{counter} फलो गर्दै}}",
|
||||
|
@ -42,6 +45,7 @@
|
|||
"account.mute_notifications_short": "सूचनाहरू म्यूट गर्नुहोस्",
|
||||
"account.mute_short": "म्युट",
|
||||
"account.muted": "म्युट गरिएको",
|
||||
"account.mutual": "तपाईंहरु एकअर्कालाई फलो गर्नुहुन्छ",
|
||||
"account.no_bio": "कुनै विवरण प्रदान गरिएको छैन।",
|
||||
"account.posts": "पोस्टहरू",
|
||||
"account.posts_with_replies": "पोस्ट र जवाफहरू",
|
||||
|
@ -53,6 +57,7 @@
|
|||
"account.statuses_counter": "{count, plural, one {{counter} पोस्ट} other {{counter} पोस्टहरू}}",
|
||||
"account.unblock": "@{name} लाई अनब्लक गर्नुहोस्",
|
||||
"account.unblock_domain": "{domain} डोमेनलाई अनब्लक गर्नुहोस्",
|
||||
"account.unblock_domain_short": "अनब्लक गर्नुहोस्",
|
||||
"account.unblock_short": "अनब्लक गर्नुहोस्",
|
||||
"account.unendorse": "प्रोफाइलमा फिचर नगर्नुहोस्",
|
||||
"account.unfollow": "अनफलो गर्नुहोस्",
|
||||
|
@ -64,6 +69,7 @@
|
|||
"admin.dashboard.retention.cohort_size": "नयाँ प्रयोगकर्ताहरू",
|
||||
"alert.rate_limited.message": "कृपया {retry_time, time, medium} पछि पुन: प्रयास गर्नुहोस्।",
|
||||
"alert.unexpected.message": "एउटा अनपेक्षित त्रुटि भयो।",
|
||||
"alt_text_modal.cancel": "रद्द गर्नुहोस्",
|
||||
"announcement.announcement": "घोषणा",
|
||||
"annual_report.summary.followers.followers": "फलोअरहरु",
|
||||
"annual_report.summary.highlighted_post.by_reblogs": "सबैभन्दा बढि बूस्ट गरिएको पोस्ट",
|
||||
|
@ -156,8 +162,13 @@
|
|||
"notification_requests.confirm_accept_multiple.title": "सूचना अनुरोधहरू स्वीकार गर्ने?",
|
||||
"notification_requests.confirm_dismiss_multiple.title": "सूचना अनुरोधहरू खारेज गर्ने?",
|
||||
"notifications.clear_title": "सूचनाहरू खाली गर्ने?",
|
||||
"notifications.column_settings.follow": "नयाँ फलोअरहरु:",
|
||||
"notifications.column_settings.follow_request": "नयाँ फलोअर अनुरोधहरु",
|
||||
"notifications.column_settings.reblog": "बूस्टहरू:",
|
||||
"notifications.filter.boosts": "बूस्टहरू",
|
||||
"privacy.private.long": "मात्र तपाईंको फलोअरहरु",
|
||||
"privacy.private.short": "फलोअरहरु",
|
||||
"reply_indicator.cancel": "रद्द गर्नुहोस्",
|
||||
"report.comment.title": "के हामीले थाहा पाउनुपर्ने अरू केही छ जस्तो लाग्छ?",
|
||||
"report.forward_hint": "यो खाता अर्को सर्भरबाट हो। त्यहाँ पनि रिपोर्टको गुमनाम प्रतिलिपि पठाउने हो?",
|
||||
"report.rules.title": "कुन नियमहरू उल्लङ्घन भइरहेका छन्?",
|
||||
|
@ -172,5 +183,6 @@
|
|||
"status.reblog": "बूस्ट गर्नुहोस्",
|
||||
"status.reblogged_by": "{name} ले बूस्ट गर्नुभएको",
|
||||
"status.reblogs": "{count, plural, one {बूस्ट} other {बूस्टहरू}}",
|
||||
"status.unmute_conversation": "कुराकानी अनम्यूट गर्नुहोस्"
|
||||
"status.unmute_conversation": "कुराकानी अनम्यूट गर्नुहोस्",
|
||||
"visibility_modal.quote_followers": "फलोअरहरु मात्र"
|
||||
}
|
||||
|
|
|
@ -350,14 +350,14 @@
|
|||
"featured_carousel.previous": "Предыдущий",
|
||||
"featured_carousel.slide": "{index} из {total}",
|
||||
"filter_modal.added.context_mismatch_explanation": "Этот фильтр не применяется в том контексте, в котором вы видели этот пост. Если вы хотите, чтобы пост был отфильтрован в текущем контексте, необходимо редактировать фильтр.",
|
||||
"filter_modal.added.context_mismatch_title": "Несоответствие контекста!",
|
||||
"filter_modal.added.context_mismatch_title": "Несоответствие контекста",
|
||||
"filter_modal.added.expired_explanation": "Этот фильтр истёк. Чтобы он был применён, вам нужно изменить срок действия фильтра.",
|
||||
"filter_modal.added.expired_title": "Истёкший фильтр!",
|
||||
"filter_modal.added.expired_title": "Истёкший фильтр",
|
||||
"filter_modal.added.review_and_configure": "Для просмотра или редактирования этого фильтра перейдите в {settings_link}.",
|
||||
"filter_modal.added.review_and_configure_title": "Настройки фильтра",
|
||||
"filter_modal.added.settings_link": "настройки",
|
||||
"filter_modal.added.short_explanation": "Этот пост был добавлен к фильтру «{title}».",
|
||||
"filter_modal.added.title": "Фильтр добавлен!",
|
||||
"filter_modal.added.title": "Фильтр добавлен",
|
||||
"filter_modal.select_filter.context_mismatch": "не применяется в этом контексте",
|
||||
"filter_modal.select_filter.expired": "истёкший",
|
||||
"filter_modal.select_filter.prompt_new": "Новый фильтр: {name}",
|
||||
|
@ -433,7 +433,7 @@
|
|||
"home.hide_announcements": "Скрыть объявления",
|
||||
"home.pending_critical_update.body": "Пожалуйста, обновите свой сервер Mastodon как можно скорее!",
|
||||
"home.pending_critical_update.link": "Посмотреть обновления",
|
||||
"home.pending_critical_update.title": "Доступно критическое обновление безопасности!",
|
||||
"home.pending_critical_update.title": "Доступно критическое обновление безопасности",
|
||||
"home.show_announcements": "Показать объявления",
|
||||
"ignore_notifications_modal.disclaimer": "Mastodon не может сообщить пользователям, что вы игнорируете их уведомления. Игнорирование уведомлений не остановит отправку самих сообщений.",
|
||||
"ignore_notifications_modal.filter_instead": "Фильтровать",
|
||||
|
@ -590,7 +590,7 @@
|
|||
"notification.admin.report_statuses_other": "{name} пожаловался (-лась) на {target}",
|
||||
"notification.admin.sign_up": "{name} зарегистрировался (-лась) на сервере",
|
||||
"notification.admin.sign_up.name_and_others": "{name} и ещё {count, plural, one {# пользователь} few {# пользователя} other {# пользователей}} зарегистрировались на сервере",
|
||||
"notification.annual_report.message": "#Wrapstodon за {year} год ждёт вас! Откройте для себя итоги и памятные моменты этого года в Mastodon!",
|
||||
"notification.annual_report.message": "#Wrapstodon за {year} год ждёт вас! Откройте для себя итоги и памятные моменты этого года в Mastodon.",
|
||||
"notification.annual_report.view": "Перейти к #Wrapstodon",
|
||||
"notification.favourite": "{name} добавил(а) ваш пост в избранное",
|
||||
"notification.favourite.name_and_others_with_link": "{name} и ещё <a>{count, plural, one {# пользователь} few {# пользователя} other {# пользователей}}</a> добавили ваш пост в избранное",
|
||||
|
@ -861,7 +861,7 @@
|
|||
"status.direct": "Упомянуть @{name} лично",
|
||||
"status.direct_indicator": "Личное упоминание",
|
||||
"status.edit": "Редактировать",
|
||||
"status.edited": "Дата последнего изменения: {date}",
|
||||
"status.edited": "Последнее изменение: {date}",
|
||||
"status.edited_x_times": "{count, plural, one {{count} изменение} many {{count} изменений} other {{count} изменения}}",
|
||||
"status.embed": "Встроить на свой сайт",
|
||||
"status.favourite": "Добавить в избранное",
|
||||
|
|
|
@ -246,6 +246,7 @@
|
|||
"confirmations.remove_from_followers.message": "{name} kommer att sluta följa dig. Är du säker på att du vill fortsätta?",
|
||||
"confirmations.remove_from_followers.title": "Ta bort följare?",
|
||||
"confirmations.revoke_quote.confirm": "Ta bort inlägg",
|
||||
"confirmations.revoke_quote.message": "Denna åtgärd kan inte ångras.",
|
||||
"confirmations.revoke_quote.title": "Ta bort inlägg?",
|
||||
"confirmations.unfollow.confirm": "Avfölj",
|
||||
"confirmations.unfollow.message": "Är du säker på att du vill avfölja {name}?",
|
||||
|
@ -603,6 +604,7 @@
|
|||
"notification.label.mention": "Nämn",
|
||||
"notification.label.private_mention": "Privat omnämnande",
|
||||
"notification.label.private_reply": "Privata svar",
|
||||
"notification.label.quote": "{name} citerade ditt inlägg",
|
||||
"notification.label.reply": "Svar",
|
||||
"notification.mention": "Nämn",
|
||||
"notification.mentioned_you": "{name} nämnde dig",
|
||||
|
@ -660,6 +662,7 @@
|
|||
"notifications.column_settings.mention": "Omnämningar:",
|
||||
"notifications.column_settings.poll": "Omröstningsresultat:",
|
||||
"notifications.column_settings.push": "Push-aviseringar",
|
||||
"notifications.column_settings.quote": "Citerat:",
|
||||
"notifications.column_settings.reblog": "Boostar:",
|
||||
"notifications.column_settings.show": "Visa i kolumnen",
|
||||
"notifications.column_settings.sound": "Spela upp ljud",
|
||||
|
@ -877,8 +880,14 @@
|
|||
"status.mute_conversation": "Tysta konversation",
|
||||
"status.open": "Utvidga detta inlägg",
|
||||
"status.pin": "Fäst i profil",
|
||||
"status.quote.cancel": "Återkalla citering",
|
||||
"status.quote_error.filtered": "Dolt på grund av ett av dina filter",
|
||||
"status.quote_error.not_available": "Inlägg ej tillgängligt",
|
||||
"status.quote_error.pending_approval": "Väntande inlägg",
|
||||
"status.quote_error.pending_approval_popout.body": "Citat som delas över Fediverse kan ta tid att visa, eftersom olika servrar har olika protokoll.",
|
||||
"status.quote_error.pending_approval_popout.title": "Väntande citat? Förbli lugn",
|
||||
"status.quote_policy_change": "Ändra vem som kan citera",
|
||||
"status.quote_post_author": "Citerade ett inlägg av @{name}",
|
||||
"status.read_more": "Läs mer",
|
||||
"status.reblog": "Boosta",
|
||||
"status.reblog_private": "Boosta med ursprunglig synlighet",
|
||||
|
@ -893,6 +902,7 @@
|
|||
"status.reply": "Svara",
|
||||
"status.replyAll": "Svara på tråden",
|
||||
"status.report": "Rapportera @{name}",
|
||||
"status.revoke_quote": "Ta bort mitt inlägg från @{name}s inlägg",
|
||||
"status.sensitive_warning": "Känsligt innehåll",
|
||||
"status.share": "Dela",
|
||||
"status.show_less_all": "Visa mindre för alla",
|
||||
|
@ -953,9 +963,16 @@
|
|||
"video.unmute": "Avtysta",
|
||||
"video.volume_down": "Volym ned",
|
||||
"video.volume_up": "Volym upp",
|
||||
"visibility_modal.button_title": "Ange synlighet",
|
||||
"visibility_modal.header": "Synlighet och interaktion",
|
||||
"visibility_modal.helper.direct_quoting": "Privata omnämnanden kan inte bli citerade.",
|
||||
"visibility_modal.helper.privacy_editing": "Publicerade inlägg kan inte ändra deras synlighet.",
|
||||
"visibility_modal.helper.private_quoting": "Inlägg som endast är synliga för följare kan inte citeras.",
|
||||
"visibility_modal.helper.unlisted_quoting": "När folk citerar dig, deras inlägg kommer också att döljas från trendiga tidslinjer.",
|
||||
"visibility_modal.instructions": "Kontrollera vem som kan interagera med det här inlägget. Globala inställningar kan hittas under <link>Inställningar > Andra</link>.",
|
||||
"visibility_modal.privacy_label": "Integritet",
|
||||
"visibility_modal.quote_followers": "Endast följare",
|
||||
"visibility_modal.quote_label": "Ändra vem som kan citera",
|
||||
"visibility_modal.quote_nobody": "Ingen",
|
||||
"visibility_modal.quote_public": "Alla"
|
||||
}
|
||||
|
|
|
@ -292,6 +292,7 @@
|
|||
"domain_pill.your_handle": "Tanıtıcınız:",
|
||||
"domain_pill.your_server": "Dijital anasayfanız, tüm gönderilerinizin yaşadığı yerdir. Bunu beğenmediniz mi? İstediğiniz zaman sunucularınızı değiştirin ve takipçilerinizi de getirin.",
|
||||
"domain_pill.your_username": "Bu sunucudaki tekil tanımlayıcınız. Farklı sunucularda aynı kullanıcı adına sahip kullanıcıları bulmak mümkündür.",
|
||||
"dropdown.empty": "Bir seçenek seçin",
|
||||
"embed.instructions": "Aşağıdaki kodu kopyalayarak bu durumu sitenize gömün.",
|
||||
"embed.preview": "İşte nasıl görüneceği:",
|
||||
"emoji_button.activity": "Aktivite",
|
||||
|
@ -848,9 +849,11 @@
|
|||
"status.admin_account": "@{name} için denetim arayüzünü açın",
|
||||
"status.admin_domain": "{domain} için denetim arayüzünü açın",
|
||||
"status.admin_status": "Denetim arayüzünde bu gönderiyi açın",
|
||||
"status.all_disabled": "Yükseltmeler ve alıntılar devre dışı bırakıldı",
|
||||
"status.block": "@{name} adlı kişiyi engelle",
|
||||
"status.bookmark": "Yer işareti ekle",
|
||||
"status.cancel_reblog_private": "Yeniden paylaşımı geri al",
|
||||
"status.cannot_quote": "Yazar bu gönderide alıntılamayı devre dışı bıraktı",
|
||||
"status.cannot_reblog": "Bu gönderi yeniden paylaşılamaz",
|
||||
"status.context.load_new_replies": "Yeni yanıtlar mevcut",
|
||||
"status.context.loading": "Daha fazla yanıt için kontrol ediliyor",
|
||||
|
@ -879,12 +882,16 @@
|
|||
"status.mute_conversation": "Sohbeti sessize al",
|
||||
"status.open": "Bu gönderiyi genişlet",
|
||||
"status.pin": "Profile sabitle",
|
||||
"status.quote": "Teklif",
|
||||
"status.quote.cancel": "Teklifi iptal et",
|
||||
"status.quote_error.filtered": "Bazı filtrelerinizden dolayı gizlenmiştir",
|
||||
"status.quote_error.not_available": "Gönderi kullanılamıyor",
|
||||
"status.quote_error.pending_approval": "Gönderi beklemede",
|
||||
"status.quote_error.pending_approval_popout.body": "Fediverse genelinde paylaşılan alıntıların görüntülenmesi zaman alabilir, çünkü farklı sunucuların farklı protokolleri vardır.",
|
||||
"status.quote_error.pending_approval_popout.title": "Bekleyen bir teklif mi var? Sakin olun.",
|
||||
"status.quote_policy_change": "Kimin alıntı yapabileceğini değiştirin",
|
||||
"status.quote_post_author": "@{name} adlı kullanıcının bir gönderisini alıntıladı",
|
||||
"status.quote_private": "Özel gönderiler alıntılanamaz",
|
||||
"status.read_more": "Devamını okuyun",
|
||||
"status.reblog": "Yeniden paylaş",
|
||||
"status.reblog_private": "Özgün görünürlük ile yeniden paylaş",
|
||||
|
@ -959,5 +966,17 @@
|
|||
"video.skip_forward": "İleriye atla",
|
||||
"video.unmute": "Sesi aç",
|
||||
"video.volume_down": "Sesi kıs",
|
||||
"video.volume_up": "Sesi yükselt"
|
||||
"video.volume_up": "Sesi yükselt",
|
||||
"visibility_modal.button_title": "Görünürlüğü ayarla",
|
||||
"visibility_modal.header": "Görünürlük ve etkileşim",
|
||||
"visibility_modal.helper.direct_quoting": "Özel gönderiler alıntılanamaz.",
|
||||
"visibility_modal.helper.privacy_editing": "Yayınlanan gönderilerin görünürlüğü değiştirilemez.",
|
||||
"visibility_modal.helper.private_quoting": "Sadece takipçilere özel paylaşımlar alıntılanamaz.",
|
||||
"visibility_modal.helper.unlisted_quoting": "İnsanlar sizden alıntı yaptığında, onların gönderileri de trend zaman tünellerinden gizlenecektir.",
|
||||
"visibility_modal.instructions": "Bu gönderiyle kimlerin etkileşimde bulunabileceğini kontrol edin. Genel ayarlara <link>Tercihler > Diğer</link> bölümünden ulaşabilirsiniz.",
|
||||
"visibility_modal.privacy_label": "Gizlilik",
|
||||
"visibility_modal.quote_followers": "Sadece takipçiler",
|
||||
"visibility_modal.quote_label": "Kimin alıntı yapabileceğini değiştirin",
|
||||
"visibility_modal.quote_nobody": "Kimseden",
|
||||
"visibility_modal.quote_public": "Herkesten"
|
||||
}
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "Mở giao diện quản trị @{name}",
|
||||
"status.admin_domain": "Mở giao diện quản trị @{domain}",
|
||||
"status.admin_status": "Mở tút này trong giao diện quản trị",
|
||||
"status.all_disabled": "Đăng lại và trích dẫn bị tắt",
|
||||
"status.block": "Chặn @{name}",
|
||||
"status.bookmark": "Lưu",
|
||||
"status.cancel_reblog_private": "Bỏ đăng lại",
|
||||
"status.cannot_quote": "Tác giả không cho phép trích dẫn tút này",
|
||||
"status.cannot_reblog": "Không thể đăng lại tút này",
|
||||
"status.context.load_new_replies": "Có những trả lời mới",
|
||||
"status.context.loading": "Kiểm tra nhiều trả lời hơn",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "Tắt thông báo",
|
||||
"status.open": "Mở tút",
|
||||
"status.pin": "Ghim lên hồ sơ",
|
||||
"status.quote": "Trích dẫn",
|
||||
"status.quote.cancel": "Bỏ trích dẫn",
|
||||
"status.quote_error.filtered": "Bị ẩn vì một bộ lọc của bạn",
|
||||
"status.quote_error.not_available": "Tút không khả dụng",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "Đang chờ trích dẫn? Hãy bình tĩnh",
|
||||
"status.quote_policy_change": "Thay đổi người có thể trích dẫn",
|
||||
"status.quote_post_author": "Trích dẫn từ tút của @{name}",
|
||||
"status.quote_private": "Không thể trích dẫn nhắn riêng",
|
||||
"status.read_more": "Đọc tiếp",
|
||||
"status.reblog": "Đăng lại",
|
||||
"status.reblog_private": "Đăng lại (Riêng tư)",
|
||||
|
|
|
@ -849,9 +849,11 @@
|
|||
"status.admin_account": "開啟 @{name} 的管理介面",
|
||||
"status.admin_domain": "開啟 {domain} 的管理介面",
|
||||
"status.admin_status": "於管理介面開啟此嘟文",
|
||||
"status.all_disabled": "已停用轉嘟與引用",
|
||||
"status.block": "封鎖 @{name}",
|
||||
"status.bookmark": "書籤",
|
||||
"status.cancel_reblog_private": "取消轉嘟",
|
||||
"status.cannot_quote": "作者已停用引用此嘟文",
|
||||
"status.cannot_reblog": "這則嘟文無法被轉嘟",
|
||||
"status.context.load_new_replies": "有新回嘟",
|
||||
"status.context.loading": "正在檢查更多回嘟",
|
||||
|
@ -880,6 +882,7 @@
|
|||
"status.mute_conversation": "靜音對話",
|
||||
"status.open": "展開此嘟文",
|
||||
"status.pin": "釘選至個人檔案頁面",
|
||||
"status.quote": "引用",
|
||||
"status.quote.cancel": "取消引用嘟文",
|
||||
"status.quote_error.filtered": "由於您的過濾器,該嘟文被隱藏",
|
||||
"status.quote_error.not_available": "無法取得該嘟文",
|
||||
|
@ -888,6 +891,7 @@
|
|||
"status.quote_error.pending_approval_popout.title": "引用嘟文正在發送中?別著急,請稍候片刻",
|
||||
"status.quote_policy_change": "變更可以引用的人",
|
||||
"status.quote_post_author": "已引用 @{name} 之嘟文",
|
||||
"status.quote_private": "無法引用私人嘟文",
|
||||
"status.read_more": "閱讀更多",
|
||||
"status.reblog": "轉嘟",
|
||||
"status.reblog_private": "依照原嘟可見性轉嘟",
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import type { KeyboardEvent, MouseEvent, TouchEvent } from 'react';
|
||||
|
||||
interface BaseMenuItem {
|
||||
text: string;
|
||||
dangerous?: boolean;
|
||||
}
|
||||
|
||||
export interface ActionMenuItem extends BaseMenuItem {
|
||||
action: () => void;
|
||||
action: (event: MouseEvent | KeyboardEvent | TouchEvent) => void;
|
||||
}
|
||||
|
||||
export interface LinkMenuItem extends BaseMenuItem {
|
||||
|
|
|
@ -2,9 +2,9 @@ import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrde
|
|||
|
||||
import {
|
||||
changeUploadCompose,
|
||||
quoteComposeByStatus,
|
||||
quoteCompose,
|
||||
quoteComposeCancel,
|
||||
setQuotePolicy,
|
||||
setComposeQuotePolicy,
|
||||
} from 'mastodon/actions/compose_typed';
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
|
@ -329,22 +329,15 @@ export const composeReducer = (state = initialState, action) => {
|
|||
return state.set('is_changing_upload', true);
|
||||
} else if (changeUploadCompose.rejected.match(action)) {
|
||||
return state.set('is_changing_upload', false);
|
||||
} else if (quoteComposeByStatus.match(action)) {
|
||||
} else if (quoteCompose.match(action)) {
|
||||
const status = action.payload;
|
||||
if (
|
||||
status.getIn(['quote_approval', 'current_user']) === 'automatic' &&
|
||||
state.get('media_attachments').size === 0 &&
|
||||
!state.get('is_uploading') &&
|
||||
!state.get('poll')
|
||||
) {
|
||||
return state
|
||||
.set('quoted_status_id', status.get('id'))
|
||||
.set('spoiler', status.get('sensitive'))
|
||||
.set('spoiler_text', status.get('spoiler_text'));
|
||||
}
|
||||
} else if (quoteComposeCancel.match(action)) {
|
||||
return state.set('quoted_status_id', null);
|
||||
} else if (setQuotePolicy.match(action)) {
|
||||
} else if (setComposeQuotePolicy.match(action)) {
|
||||
return state.set('quote_policy', action.payload);
|
||||
}
|
||||
|
||||
|
@ -520,6 +513,7 @@ export const composeReducer = (state = initialState, action) => {
|
|||
map.set('sensitive', action.status.get('sensitive'));
|
||||
map.set('language', action.status.get('language'));
|
||||
map.set('id', null);
|
||||
map.set('quoted_status_id', action.status.getIn(['quote', 'quoted_status']));
|
||||
// Mastodon-authored posts can be expected to have at most one automatic approval policy
|
||||
map.set('quote_policy', action.status.getIn(['quote_approval', 'automatic', 0]) || 'nobody');
|
||||
|
||||
|
@ -551,6 +545,7 @@ export const composeReducer = (state = initialState, action) => {
|
|||
map.set('idempotencyKey', uuid());
|
||||
map.set('sensitive', action.status.get('sensitive'));
|
||||
map.set('language', action.status.get('language'));
|
||||
map.set('quoted_status_id', action.status.getIn(['quote', 'quoted_status']));
|
||||
// Mastodon-authored posts can be expected to have at most one automatic approval policy
|
||||
map.set('quote_policy', action.status.getIn(['quote_approval', 'automatic', 0]) || 'nobody');
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ export const getFilters = createSelector(
|
|||
(_, { contextType }: { contextType: string }) => contextType,
|
||||
],
|
||||
(filters, contextType) => {
|
||||
if (!contextType || contextType === 'compose') {
|
||||
if (!contextType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"><path d="M791-56 425-422 320-240h-92l92-160q-66 0-113-47t-47-113q0-27 8.5-51t23.5-44L56-791l56-57 736 736-57 56Zm-55-281L520-553v-7q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T818-480l-82 143ZM320-500q6 0 12-1t11-3l-79-79q-2 5-3 11t-1 12q0 25 17.5 42.5T320-500Zm360 0q25 0 42.5-17.5T740-560q0-25-17.5-42.5T680-620q-25 0-42.5 17.5T620-560q0 25 17.5 42.5T680-500Zm-374-41Zm374-19Z"/></svg>
|
After Width: | Height: | Size: 488 B |
|
@ -1482,18 +1482,19 @@ body > [data-popper-placement] {
|
|||
|
||||
border-bottom: 0;
|
||||
|
||||
.status__content,
|
||||
.status__action-bar,
|
||||
.media-gallery,
|
||||
.video-player,
|
||||
.audio-player,
|
||||
.attachment-list,
|
||||
.picture-in-picture-placeholder,
|
||||
.more-from-author,
|
||||
.status-card,
|
||||
.hashtag-bar,
|
||||
.content-warning,
|
||||
.filter-warning {
|
||||
& > .status__content,
|
||||
& > .status__action-bar,
|
||||
& > .media-gallery,
|
||||
& > .video-player,
|
||||
& > .audio-player,
|
||||
& > .attachment-list,
|
||||
& > .picture-in-picture-placeholder,
|
||||
& > .more-from-author,
|
||||
& > .status-card,
|
||||
& > .hashtag-bar,
|
||||
& > .content-warning,
|
||||
& > .filter-warning,
|
||||
& > .status__quote {
|
||||
margin-inline-start: var(--thread-margin);
|
||||
width: calc(100% - var(--thread-margin));
|
||||
}
|
||||
|
@ -2383,6 +2384,7 @@ a .account__avatar {
|
|||
.detailed-status__display-name,
|
||||
.detailed-status__datetime,
|
||||
.detailed-status__application,
|
||||
.detailed-status__link,
|
||||
.account__display-name {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
@ -2415,7 +2417,8 @@ a.account__display-name {
|
|||
}
|
||||
|
||||
.detailed-status__application,
|
||||
.detailed-status__datetime {
|
||||
.detailed-status__datetime,
|
||||
.detailed-status__link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
@ -2601,8 +2604,9 @@ a.account__display-name {
|
|||
}
|
||||
|
||||
.status__relative-time,
|
||||
.detailed-status__datetime {
|
||||
&:hover {
|
||||
.detailed-status__datetime,
|
||||
.detailed-status__link {
|
||||
&:is(a):hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
@ -2856,12 +2860,45 @@ a.account__display-name {
|
|||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
&:not(:disabled) {
|
||||
background: var(--dropdown-border-color);
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
color: $dark-text-color;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.reblog-button {
|
||||
&__item {
|
||||
width: 280px;
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.active:not(.disabled) {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.inline-account {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
@ -5478,7 +5515,10 @@ a.status-card {
|
|||
|
||||
.privacy-dropdown__option__content,
|
||||
.privacy-dropdown__option__content strong,
|
||||
.privacy-dropdown__option__additional {
|
||||
.privacy-dropdown__option__additional,
|
||||
.visibility-dropdown__option__content,
|
||||
.visibility-dropdown__option__content strong,
|
||||
.visibility-dropdown__option__additional {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
|
@ -5492,13 +5532,15 @@ a.status-card {
|
|||
}
|
||||
}
|
||||
|
||||
.privacy-dropdown__option__icon {
|
||||
.privacy-dropdown__option__icon,
|
||||
.visibility-dropdown__option__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.privacy-dropdown__option__content {
|
||||
.privacy-dropdown__option__content,
|
||||
.visibility-dropdown__option__content {
|
||||
flex: 1 1 auto;
|
||||
color: $darker-text-color;
|
||||
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import type { ApiRelationshipJSON } from '@/mastodon/api_types/relationships';
|
||||
import type { ApiStatusJSON } from '@/mastodon/api_types/statuses';
|
||||
import type {
|
||||
CustomEmojiData,
|
||||
UnicodeEmojiData,
|
||||
} from '@/mastodon/features/emoji/types';
|
||||
import { createAccountFromServerJSON } from '@/mastodon/models/account';
|
||||
import type { Status } from '@/mastodon/models/status';
|
||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
||||
|
||||
type FactoryOptions<T> = {
|
||||
|
@ -51,6 +55,36 @@ export const accountFactoryState = (
|
|||
options: FactoryOptions<ApiAccountJSON> = {},
|
||||
) => createAccountFromServerJSON(accountFactory(options));
|
||||
|
||||
export const statusFactory: FactoryFunction<ApiStatusJSON> = ({
|
||||
id,
|
||||
...data
|
||||
} = {}) => ({
|
||||
id: id ?? '1',
|
||||
created_at: '2023-01-01T00:00:00.000Z',
|
||||
sensitive: false,
|
||||
visibility: 'public',
|
||||
language: 'en',
|
||||
uri: 'https://example.com/status/1',
|
||||
url: 'https://example.com/status/1',
|
||||
replies_count: 0,
|
||||
reblogs_count: 0,
|
||||
favorites_count: 0,
|
||||
account: accountFactory(),
|
||||
media_attachments: [],
|
||||
mentions: [],
|
||||
tags: [],
|
||||
emojis: [],
|
||||
content: '<p>This is a test status.</p>',
|
||||
...data,
|
||||
});
|
||||
|
||||
export const statusFactoryState = (
|
||||
options: FactoryOptions<ApiStatusJSON> = {},
|
||||
) =>
|
||||
ImmutableMap<string, unknown>(
|
||||
statusFactory(options) as unknown as Record<string, unknown>,
|
||||
) as unknown as Status;
|
||||
|
||||
export const relationshipsFactory: FactoryFunction<ApiRelationshipJSON> = ({
|
||||
id,
|
||||
...data
|
||||
|
|
|
@ -45,6 +45,10 @@ class StatusEdit < ApplicationRecord
|
|||
delegate :local?, :application, :edited?, :edited_at,
|
||||
:discarded?, :visibility, :language, to: :status
|
||||
|
||||
def with_media?
|
||||
ordered_media_attachments.any?
|
||||
end
|
||||
|
||||
def emojis
|
||||
return @emojis if defined?(@emojis)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
has_one :role, serializer: REST::RoleSerializer
|
||||
|
||||
def meta # rubocop:disable Metrics/AbcSize
|
||||
def meta
|
||||
store = default_meta_store
|
||||
|
||||
if object.current_account
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
- if status.ordered_media_attachments.first.video?
|
||||
= render_video_component(status, visible: false)
|
||||
- elsif status.ordered_media_attachments.first.audio?
|
||||
= render_audio_component(status)
|
||||
- else
|
||||
= render_media_gallery_component(status, visible: false)
|
|
@ -1,53 +0,0 @@
|
|||
.batch-table__row
|
||||
%label.batch-table__row__select.batch-checkbox
|
||||
= f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id
|
||||
.batch-table__row__content
|
||||
.status__card
|
||||
- if status.reblog?
|
||||
.status__prepend
|
||||
= material_symbol('repeat')
|
||||
= t('statuses.boosted_from_html', acct_link: admin_account_inline_link_to(status.proper.account, path: admin_account_status_path(status.proper.account.id, status.proper.id)))
|
||||
- elsif status.reply? && status.in_reply_to_id.present?
|
||||
.status__prepend
|
||||
= material_symbol('reply')
|
||||
= t('admin.statuses.replied_to_html', acct_link: admin_account_inline_link_to(status.in_reply_to_account, path: status.thread.present? ? admin_account_status_path(status.thread.account_id, status.in_reply_to_id) : nil))
|
||||
.status__content><
|
||||
- if status.proper.spoiler_text.blank?
|
||||
= prerender_custom_emojis(status_content_format(status.proper), status.proper.emojis)
|
||||
- else
|
||||
%details<
|
||||
%summary><
|
||||
%strong> Content warning: #{prerender_custom_emojis(h(status.proper.spoiler_text), status.proper.emojis)}
|
||||
= prerender_custom_emojis(status_content_format(status.proper), status.proper.emojis)
|
||||
|
||||
- unless status.proper.ordered_media_attachments.empty?
|
||||
= render partial: 'admin/reports/media_attachments', locals: { status: status.proper }
|
||||
|
||||
.detailed-status__meta
|
||||
- if status.application
|
||||
= status.application.name
|
||||
·
|
||||
|
||||
= link_to admin_account_status_path(status.account.id, status), class: 'detailed-status__datetime' do
|
||||
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
|
||||
- if status.edited?
|
||||
·
|
||||
= link_to t('statuses.edited_at_html', date: content_tag(:time, l(status.edited_at), datetime: status.edited_at.iso8601, title: l(status.edited_at), class: 'formatted')),
|
||||
admin_account_status_path(status.account_id, status),
|
||||
class: 'detailed-status__datetime'
|
||||
- if status.discarded?
|
||||
·
|
||||
%span.negative-hint= t('admin.statuses.deleted')
|
||||
·
|
||||
|
||||
= material_symbol visibility_icon(status)
|
||||
= t("statuses.visibilities.#{status.visibility}")
|
||||
·
|
||||
|
||||
= link_to ActivityPub::TagManager.instance.url_for(status.proper), class: 'detailed-status__link', rel: 'noopener' do
|
||||
= t('admin.statuses.view_publicly')
|
||||
|
||||
- if status.proper.sensitive?
|
||||
·
|
||||
= material_symbol('visibility_off')
|
||||
= t('stream_entries.sensitive_content')
|
|
@ -57,7 +57,7 @@
|
|||
- if @statuses.empty?
|
||||
= nothing_here 'nothing-here--under-tabs'
|
||||
- else
|
||||
= render partial: 'admin/reports/status', collection: @statuses, locals: { f: f }
|
||||
= render partial: 'admin/shared/status_batch_row', collection: @statuses, as: :status, locals: { f: f }
|
||||
|
||||
- if @report.unresolved?
|
||||
%hr.spacer/
|
||||
|
|
40
app/views/admin/shared/_status.html.haml
Normal file
40
app/views/admin/shared/_status.html.haml
Normal file
|
@ -0,0 +1,40 @@
|
|||
-# locals: (status:)
|
||||
|
||||
.status__card><
|
||||
- if status.reblog?
|
||||
.status__prepend
|
||||
= material_symbol('repeat')
|
||||
= t('statuses.boosted_from_html', acct_link: admin_account_inline_link_to(status.proper.account, path: admin_account_status_path(status.proper.account.id, status.proper.id)))
|
||||
- elsif status.reply? && status.in_reply_to_id.present?
|
||||
.status__prepend
|
||||
= material_symbol('reply')
|
||||
= t('admin.statuses.replied_to_html', acct_link: admin_account_inline_link_to(status.in_reply_to_account, path: status.thread.present? ? admin_account_status_path(status.thread.account_id, status.in_reply_to_id) : nil))
|
||||
|
||||
= render partial: 'admin/shared/status_content', locals: { status: status.proper }
|
||||
|
||||
.detailed-status__meta
|
||||
- if status.application
|
||||
= status.application.name
|
||||
·
|
||||
= conditional_link_to can?(:show, status), admin_account_status_path(status.account.id, status), class: 'detailed-status__datetime' do
|
||||
%time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }><= l(status.created_at)
|
||||
- if status.edited?
|
||||
·
|
||||
= conditional_link_to can?(:show, status), admin_account_status_path(status.account.id, status, { anchor: 'history' }), class: 'detailed-status__datetime' do
|
||||
%span><= t('statuses.edited_at_html', date: content_tag(:time, l(status.edited_at), datetime: status.edited_at.iso8601, title: l(status.edited_at), class: 'relative-formatted'))
|
||||
- if status.discarded?
|
||||
·
|
||||
%span.negative-hint= t('admin.statuses.deleted')
|
||||
- unless status.reblog?
|
||||
·
|
||||
%span<
|
||||
= material_symbol(visibility_icon(status))
|
||||
= t("statuses.visibilities.#{status.visibility}")
|
||||
- if status.proper.sensitive?
|
||||
·
|
||||
= material_symbol('visibility_off')
|
||||
= t('stream_entries.sensitive_content')
|
||||
- unless status.direct_visibility?
|
||||
·
|
||||
= link_to ActivityPub::TagManager.instance.url_for(status.proper), class: 'detailed-status__link', target: 'blank', rel: 'noopener' do
|
||||
= t('admin.statuses.view_publicly')
|
7
app/views/admin/shared/_status_attachments.html.haml
Normal file
7
app/views/admin/shared/_status_attachments.html.haml
Normal file
|
@ -0,0 +1,7 @@
|
|||
- if status.with_media?
|
||||
- if status.ordered_media_attachments.first.video?
|
||||
= render_video_component(status, visible: false)
|
||||
- elsif status.ordered_media_attachments.first.audio?
|
||||
= render_audio_component(status)
|
||||
- else
|
||||
= render_media_gallery_component(status, visible: false)
|
5
app/views/admin/shared/_status_batch_row.html.haml
Normal file
5
app/views/admin/shared/_status_batch_row.html.haml
Normal file
|
@ -0,0 +1,5 @@
|
|||
.batch-table__row
|
||||
%label.batch-table__row__select.batch-checkbox
|
||||
= f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id
|
||||
.batch-table__row__content
|
||||
= render partial: 'admin/shared/status', object: status
|
10
app/views/admin/shared/_status_content.html.haml
Normal file
10
app/views/admin/shared/_status_content.html.haml
Normal file
|
@ -0,0 +1,10 @@
|
|||
.status__content><
|
||||
- if status.spoiler_text.present?
|
||||
%details<
|
||||
%summary><
|
||||
%strong> Content warning: #{prerender_custom_emojis(h(status.spoiler_text), status.emojis)}
|
||||
= prerender_custom_emojis(status_content_format(status), status.emojis)
|
||||
= render partial: 'admin/shared/status_attachments', locals: { status: status.proper }
|
||||
- else
|
||||
= prerender_custom_emojis(status_content_format(status), status.emojis)
|
||||
= render partial: 'admin/shared/status_attachments', locals: { status: status.proper }
|
|
@ -9,17 +9,7 @@
|
|||
%time.formatted{ datetime: status_edit.created_at.iso8601, title: l(status_edit.created_at) }= l(status_edit.created_at)
|
||||
|
||||
.status
|
||||
.status__content><
|
||||
- if status_edit.spoiler_text.blank?
|
||||
= prerender_custom_emojis(status_content_format(status_edit), status_edit.emojis)
|
||||
- else
|
||||
%details<
|
||||
%summary><
|
||||
%strong> Content warning: #{prerender_custom_emojis(h(status_edit.spoiler_text), status_edit.emojis)}
|
||||
= prerender_custom_emojis(status_content_format(status_edit), status_edit.emojis)
|
||||
|
||||
- unless status_edit.ordered_media_attachments.empty?
|
||||
= render partial: 'admin/reports/media_attachments', locals: { status: status_edit }
|
||||
= render partial: 'admin/shared/status_content', locals: { status: status_edit }
|
||||
|
||||
.detailed-status__meta
|
||||
%time.formatted{ datetime: status_edit.created_at.iso8601, title: l(status_edit.created_at) }= l(status_edit.created_at)
|
||||
|
|
|
@ -47,6 +47,6 @@
|
|||
- if @statuses.empty?
|
||||
= nothing_here 'nothing-here--under-tabs'
|
||||
- else
|
||||
= render partial: 'admin/reports/status', collection: @statuses, locals: { f: f }
|
||||
= render partial: 'admin/shared/status_batch_row', collection: @statuses, as: :status, locals: { f: f }
|
||||
|
||||
= paginate @statuses
|
||||
|
|
|
@ -53,52 +53,11 @@
|
|||
|
||||
%h3= t('admin.statuses.contents')
|
||||
|
||||
.status__card
|
||||
- if @status.reblog?
|
||||
.status__prepend
|
||||
= material_symbol('repeat')
|
||||
= t('statuses.boosted_from_html', acct_link: admin_account_inline_link_to(@status.proper.account, path: admin_account_status_path(@status.proper.account.id, @status.proper.id)))
|
||||
- elsif @status.reply? && @status.in_reply_to_id.present?
|
||||
.status__prepend
|
||||
= material_symbol('reply')
|
||||
= t('admin.statuses.replied_to_html', acct_link: admin_account_inline_link_to(@status.in_reply_to_account, path: @status.thread.present? ? admin_account_status_path(@status.thread.account_id, @status.in_reply_to_id) : nil))
|
||||
.status__content><
|
||||
- if @status.proper.spoiler_text.blank?
|
||||
= prerender_custom_emojis(status_content_format(@status.proper), @status.proper.emojis)
|
||||
- else
|
||||
%details<
|
||||
%summary><
|
||||
%strong> Content warning: #{prerender_custom_emojis(h(@status.proper.spoiler_text), @status.proper.emojis)}
|
||||
= prerender_custom_emojis(status_content_format(@status.proper), @status.proper.emojis)
|
||||
|
||||
- unless @status.proper.ordered_media_attachments.empty?
|
||||
= render partial: 'admin/reports/media_attachments', locals: { status: @status.proper }
|
||||
|
||||
.detailed-status__meta
|
||||
- if @status.application
|
||||
= @status.application.name
|
||||
·
|
||||
%span.detailed-status__datetime
|
||||
%time.formatted{ datetime: @status.created_at.iso8601, title: l(@status.created_at) }= l(@status.created_at)
|
||||
- if @status.edited?
|
||||
·
|
||||
%span.detailed-status__datetime
|
||||
= t('statuses.edited_at_html', date: content_tag(:time, l(@status.edited_at), datetime: @status.edited_at.iso8601, title: l(@status.edited_at), class: 'formatted'))
|
||||
- if @status.discarded?
|
||||
·
|
||||
%span.negative-hint= t('admin.statuses.deleted')
|
||||
- unless @status.reblog?
|
||||
·
|
||||
= material_symbol(visibility_icon(@status))
|
||||
= t("statuses.visibilities.#{@status.visibility}")
|
||||
- if @status.proper.sensitive?
|
||||
·
|
||||
= material_symbol('visibility_off')
|
||||
= t('stream_entries.sensitive_content')
|
||||
= render partial: 'admin/shared/status', object: @status
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
%h3= t('admin.statuses.history')
|
||||
%h3#history= t('admin.statuses.history')
|
||||
- if @status.edits.empty?
|
||||
%p= t('admin.statuses.no_history')
|
||||
- else
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Disable httplog in production unless log_level is `debug`
|
||||
if !Rails.env.production? || Rails.configuration.log_level == :debug
|
||||
# Disable in production unless log level is `debug`
|
||||
if Rails.env.local? || Rails.logger.debug?
|
||||
require 'httplog'
|
||||
|
||||
HttpLog.configure do |config|
|
||||
|
|
|
@ -49,6 +49,10 @@ ia:
|
|||
attributes:
|
||||
reblog:
|
||||
taken: del message jam existe
|
||||
terms_of_service:
|
||||
attributes:
|
||||
effective_date:
|
||||
too_soon: es troppo tosto, debe esser plus tarde que %{date}
|
||||
user:
|
||||
attributes:
|
||||
date_of_birth:
|
||||
|
|
|
@ -72,7 +72,7 @@ fa:
|
|||
revoke: آیا مطمئن هستید؟
|
||||
index:
|
||||
authorized_at: تایید شده در %{date}
|
||||
description_html: اینها نرمافزار هایی هستند که میتوانند به حساب کاربری شما با استفاده از رابط نرمافزاری دسترسی پیدا کنند. اگر نرمافزار های در اینجا هستند که نمیشناسید، یا نرمافزاری که رفتار مشکوک دارد، میتوانید دسترسی اش را باطل کنید.
|
||||
description_html: اینها برنامههاییند که میتوانند با استفاده از میانای برنامهنویسی به حسابتان دسترسی پیدا کنند. اگر برنامهای هست که نمیشناسیدش یا رفتار بدی دارد میتوانید دسترسیش را باطل کنید.
|
||||
last_used_at: آخرین استفاده در %{date}
|
||||
never_used: هرگز استفاده نشده
|
||||
scopes: اجازهها
|
||||
|
|
|
@ -1915,7 +1915,7 @@ en:
|
|||
public: Everyone
|
||||
title: '%{name}: "%{quote}"'
|
||||
visibilities:
|
||||
direct: Direct
|
||||
direct: Private mention
|
||||
private: Followers-only
|
||||
private_long: Only show to followers
|
||||
public: Public
|
||||
|
|
|
@ -2090,4 +2090,4 @@ eo:
|
|||
not_enabled: Vi ankoraŭ ne ŝaltis WebAuth
|
||||
not_supported: Ĉi tiu legilo ne povas uzi sekurecŝlosilojn
|
||||
otp_required: Por uzi sekurecŝlosilojn, ebligu 2-faktoran autentigon unue.
|
||||
registered_on: Registrigita je %{date}
|
||||
registered_on: Registrita je %{date}
|
||||
|
|
|
@ -190,6 +190,7 @@ ia:
|
|||
create_relay: Crear repetitor
|
||||
create_unavailable_domain: Crear dominio indisponibile
|
||||
create_user_role: Crear un rolo
|
||||
create_username_block: Crear regula de nomine de usator
|
||||
demote_user: Degradar usator
|
||||
destroy_announcement: Deler annuncio
|
||||
destroy_canonical_email_block: Deler blocada de e-mail
|
||||
|
@ -203,6 +204,7 @@ ia:
|
|||
destroy_status: Deler message
|
||||
destroy_unavailable_domain: Deler dominio indisponibile
|
||||
destroy_user_role: Destruer rolo
|
||||
destroy_username_block: Deler regula de nomine de usator
|
||||
disable_2fa_user: Disactivar A2F
|
||||
disable_custom_emoji: Disactivar emoji personalisate
|
||||
disable_relay: Disactivar repetitor
|
||||
|
@ -237,6 +239,7 @@ ia:
|
|||
update_report: Actualisar le reporto
|
||||
update_status: Actualisar le message
|
||||
update_user_role: Actualisar rolo
|
||||
update_username_block: Actualisar regula de nomine de usator
|
||||
actions:
|
||||
approve_appeal_html: "%{name} approbava appello del decision de moderation de %{target}"
|
||||
approve_user_html: "%{name} approbava inscription de %{target}"
|
||||
|
@ -255,6 +258,7 @@ ia:
|
|||
create_relay_html: "%{name} ha create un repetitor %{target}"
|
||||
create_unavailable_domain_html: "%{name} stoppava livration al dominio %{target}"
|
||||
create_user_role_html: "%{name} creava rolo de %{target}"
|
||||
create_username_block_html: "%{name} ha addite un regula pro nomines de usator que contine %{target}"
|
||||
demote_user_html: "%{name} degradava usator %{target}"
|
||||
destroy_announcement_html: "%{name} deleva annuncio %{target}"
|
||||
destroy_canonical_email_block_html: "%{name} disblocava le e-mail con le hash %{target}"
|
||||
|
@ -268,6 +272,7 @@ ia:
|
|||
destroy_status_html: "%{name} removeva un message de %{target}"
|
||||
destroy_unavailable_domain_html: "%{name} reprendeva le livration al dominio %{target}"
|
||||
destroy_user_role_html: "%{name} deleva le rolo %{target}"
|
||||
destroy_username_block_html: "%{name} ha removite un regula pro nomines de usator que contine %{target}"
|
||||
disable_2fa_user_html: "%{name} disactivava le authentication a duo factores pro le usator %{target}"
|
||||
disable_custom_emoji_html: "%{name} disactivava le emoji %{target}"
|
||||
disable_relay_html: "%{name} ha disactivate le repetitor %{target}"
|
||||
|
@ -302,6 +307,7 @@ ia:
|
|||
update_report_html: "%{name} actualisava reporto %{target}"
|
||||
update_status_html: "%{name} actualisava message de %{target}"
|
||||
update_user_role_html: "%{name} cambiava rolo de %{target}"
|
||||
update_username_block_html: "%{name} ha actualisate un regula pro nomines de usator que contine %{target}"
|
||||
deleted_account: conto delite
|
||||
empty: Nulle registros trovate.
|
||||
filter_by_action: Filtrar per action
|
||||
|
@ -321,7 +327,7 @@ ia:
|
|||
preview:
|
||||
disclaimer: Proque le usatores non pote optar pro non reciper los, le notificationes per e-mail debe limitar se a annuncios importante tal como le notificationes de violation de datos personal o de clausura de servitores.
|
||||
explanation_html: 'Le e-mail essera inviate a <strong>%{display_count} usatores</strong>. Le sequente texto essera includite in le e-mail:'
|
||||
title: Previder le notification de annuncio
|
||||
title: Previsualisar le notification de annuncio
|
||||
publish: Publicar
|
||||
published_msg: Annuncio publicate con successo!
|
||||
scheduled_for: Programmate pro %{time}
|
||||
|
@ -480,6 +486,36 @@ ia:
|
|||
new:
|
||||
title: Importar blocadas de dominio
|
||||
no_file: Necun file seligite
|
||||
fasp:
|
||||
debug:
|
||||
callbacks:
|
||||
created_at: Data de creation
|
||||
delete: Deler
|
||||
ip: Adresse IP
|
||||
request_body: Corpore del requesta
|
||||
title: Depurar le appellos de retorno
|
||||
providers:
|
||||
active: Active
|
||||
base_url: URL de base
|
||||
callback: Appello de retorno
|
||||
delete: Deler
|
||||
edit: Modificar fornitor
|
||||
finish_registration: Terminar registration
|
||||
name: Nomine
|
||||
providers: Fornitores
|
||||
public_key_fingerprint: Dactylogramma del clave public
|
||||
registration_requested: Registration requestate
|
||||
registrations:
|
||||
confirm: Confirmar
|
||||
description: Tu ha recipite un registration de un FSAF. Rejecta lo si tu non lo ha initiate. Si tu lo ha initiate, compara attentemente le nomine e le dactylogramma del clave ante de confirmar le registration.
|
||||
reject: Rejectar
|
||||
title: Confirmar registration FSAF
|
||||
save: Salvar
|
||||
select_capabilities: Selige capacitates
|
||||
sign_in: Aperir session
|
||||
status: Stato
|
||||
title: Fornitores de Servicios Auxiliar del Fediverso
|
||||
title: FSAF
|
||||
follow_recommendations:
|
||||
description_html: "<strong>Le recommendationes de sequimento adjuta le nove usatores a trovar rapidemente contento interessante.</strong> Quando un usator non ha un historia sufficiente de interactiones con alteres pro formar recommendationes personalisate de sequimento, iste contos es recommendate. Illos se recalcula cata die a partir de un mixtura de contos con le plus grande numero de ingagiamentos recente e le numero de sequitores local le plus alte pro un lingua date."
|
||||
language: Pro le lingua
|
||||
|
@ -548,6 +584,13 @@ ia:
|
|||
all: Toto
|
||||
limited: Limitate
|
||||
title: Moderation
|
||||
moderation_notes:
|
||||
create: Adder nota de moderation
|
||||
created_msg: Le nota de moderation del servitor ha essite create.
|
||||
description_html: Vider e lassar notas pro altere moderatores e pro referentia futur
|
||||
destroyed_msg: Le nota de moderation del servitor ha essite delite.
|
||||
placeholder: Informationes sur iste servitor, actiones prendite o altere cosas que pote adjutar te a moderar iste servitor in le futuro.
|
||||
title: Notas de moderation
|
||||
private_comment: Commento private
|
||||
public_comment: Commento public
|
||||
purge: Purgar
|
||||
|
@ -756,11 +799,17 @@ ia:
|
|||
title: Rolos
|
||||
rules:
|
||||
add_new: Adder regula
|
||||
add_translation: Adder traduction
|
||||
delete: Deler
|
||||
description_html: Ben que multes affirma de haber legite e acceptate le conditiones de servicio, generalmente le gente non los lege completemente usque un problema surge. <strong>Facilita le visibilitate del regulas de tu servitor in un colpo de oculo forniente los in un lista a punctos.</strong> Tenta mantener le regulas individual curte e simple, ma sin divider los in multe punctos separate.
|
||||
edit: Modificar regula
|
||||
empty: Necun regula del servitor ha essite definite ancora.
|
||||
move_down: Displaciar a basso
|
||||
move_up: Displaciar in alto
|
||||
title: Regulas del servitor
|
||||
translation: Traduction
|
||||
translations: Traductiones
|
||||
translations_explanation: Tu ha le option de adder traductiones al regulas. Le valor predefinite apparera si necun version traducite es disponibile. Per favor sempre assecura te que omne traduction fornite es ben synchronisate con le valor predefinite.
|
||||
settings:
|
||||
about:
|
||||
manage_rules: Gerer le regulas del servitor
|
||||
|
@ -877,7 +926,7 @@ ia:
|
|||
database_schema_check:
|
||||
message_html: Il ha migrationes de base de datos pendente. Per favor exeque los pro assecurar que le application se comporta como expectate
|
||||
elasticsearch_analysis_index_mismatch:
|
||||
message_html: Le parametros del analisator del indice de Elasticsearch es obsolete. Executa <code>tootctl search deploy --only-mapping --only=%{value}</code>
|
||||
message_html: Le parametros del analysator del indice de Elasticsearch es obsolete. Executa <code>tootctl search deploy --only-mapping --only=%{value}</code>
|
||||
elasticsearch_health_red:
|
||||
message_html: Le aggregation Elasticsearch es malsan (stato rubie), le functiones de recerca es indisponibile
|
||||
elasticsearch_health_yellow:
|
||||
|
@ -942,7 +991,7 @@ ia:
|
|||
changelog: Lo que ha cambiate
|
||||
create: Usar tu proprie
|
||||
current: Actual
|
||||
draft: Provisori
|
||||
draft: Esbosso
|
||||
generate: Usar modello
|
||||
generates:
|
||||
action: Generar
|
||||
|
@ -965,7 +1014,7 @@ ia:
|
|||
title: Previsualisar le notification sur le conditiones de servicio
|
||||
publish: Publicar
|
||||
published_on_html: Publicate le %{date}
|
||||
save_draft: Salvar version provisori
|
||||
save_draft: Salvar esbosso
|
||||
title: Conditiones de servicio
|
||||
title: Administration
|
||||
trends:
|
||||
|
@ -1043,8 +1092,17 @@ ia:
|
|||
title: Recommendationes e tendentias
|
||||
trending: In tendentia
|
||||
username_blocks:
|
||||
add_new: Adder nove
|
||||
block_registrations: Blocar registrationes
|
||||
comparison:
|
||||
contains: Contine
|
||||
equals: Es equal a
|
||||
contains_html: Contine %{string}
|
||||
created_msg: Regula de nomine de usator create con successo
|
||||
delete: Deler
|
||||
edit:
|
||||
title: Modificar regula de nomine de usator
|
||||
matches_exactly_html: Es equal a %{string}
|
||||
new:
|
||||
create: Crear regula
|
||||
title: Crear nove regula de nomine de usator
|
||||
|
@ -1316,6 +1374,10 @@ ia:
|
|||
basic_information: Information basic
|
||||
hint_html: "<strong>Personalisa lo que le personas vide sur tu profilo public e presso tu messages.</strong> Il es plus probabile que altere personas te seque e interage con te quando tu ha un profilo complete e un photo."
|
||||
other: Alteres
|
||||
emoji_styles:
|
||||
auto: Automatic
|
||||
native: Native
|
||||
twemoji: Twemoji
|
||||
errors:
|
||||
'400': Le requesta que tu ha inviate non es valide o es mal formate.
|
||||
'403': Tu non ha le permission de acceder a iste pagina.
|
||||
|
@ -1625,6 +1687,10 @@ ia:
|
|||
title: Nove mention
|
||||
poll:
|
||||
subject: Un sondage de %{name} ha finite
|
||||
quote:
|
||||
body: 'Tu message ha essite citate per %{name}:'
|
||||
subject: "%{name} ha citate tu message"
|
||||
title: Nove citation
|
||||
reblog:
|
||||
body: "%{name} ha impulsate tu message:"
|
||||
subject: "%{name} ha impulsate tu message"
|
||||
|
@ -1835,6 +1901,7 @@ ia:
|
|||
edited_at_html: Modificate le %{date}
|
||||
errors:
|
||||
in_reply_not_found: Le message a que tu tenta responder non pare exister.
|
||||
quoted_status_not_found: Le message que tu tenta citar non pare exister.
|
||||
over_character_limit: limite de characteres de %{max} excedite
|
||||
pin_errors:
|
||||
direct: Messages que es solo visibile a usatores mentionate non pote esser appunctate
|
||||
|
@ -1842,6 +1909,8 @@ ia:
|
|||
ownership: Le message de alcuno altere non pote esser appunctate
|
||||
reblog: Un impulso non pote esser affixate
|
||||
quote_policies:
|
||||
followers: Solmente tu sequitores
|
||||
nobody: Necuno
|
||||
public: Omnes
|
||||
title: "%{name}: “%{quote}”"
|
||||
visibilities:
|
||||
|
@ -1897,7 +1966,7 @@ ia:
|
|||
terms_of_service:
|
||||
title: Conditiones de servicio
|
||||
terms_of_service_interstitial:
|
||||
future_preamble_html: Nos face alcun cambios a nostre conditiones de servicio, que entrara in vigor in <strong>%{date}</strong>. Nos te invita a revider le conditiones actualisate.
|
||||
future_preamble_html: Nos apporta alcun modificationes a nostre conditiones de servicio, le quales entrara in vigor le <strong>%{date}</strong>. Nos te invita a revider le conditiones actualisate.
|
||||
past_preamble_html: Nos ha cambiate nostre conditiones de servicio desde tu ultime visita. Nos te invita a revider le conditiones actualisate.
|
||||
review_link: Revider le conditiones de servicio
|
||||
title: Le conditiones de servicio de %{domain} cambia
|
||||
|
@ -1969,7 +2038,7 @@ ia:
|
|||
agreement: Si tu continua a usar %{domain}, tu accepta iste conditiones. Si tu non es de accordo con le conditiones actualisate, tu pote sempre eliminar tu conto pro terminar tu accordo con %{domain}.
|
||||
changelog: 'In summario, ecce lo que iste actualisation significa pro te:'
|
||||
description: 'Tu recipe iste e-mail proque nos face alcun cambios al nostre conditiones de servicio a %{domain}. Iste actualisationes entrara in vigor in %{date}. Nos te invita a revider integralmente le conditiones actualisate hic:'
|
||||
description_html: Tu recipe iste e-mail proque nos face alcun cambios al nostre conditiones de servicio a %{domain}. Iste actualisationes entrara in vigor in <strong>%{date}</strong>. Nos te invita a revider <a href="%{path}" target="_blank">integralmente le conditiones actualisate hic</a>.
|
||||
description_html: Tu recipe iste message perque nos apporta alcun modificationes a nostre conditiones de servicio sur %{domain}. Iste actualisationes entrara in vigor le <strong>%{date}</strong>. Nos te invita a <a href="%{path}" target="_blank">leger integralmente le conditiones actualisate</a>.
|
||||
sign_off: Le equipa de %{domain}
|
||||
subject: Actualisationes de nostre conditiones de servicio
|
||||
subtitle: Le conditiones de servicio de %{domain} ha cambiate
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
ru:
|
||||
about:
|
||||
about_mastodon_html: 'Социальная сеть будущего: никакой рекламы или слежки со стороны корпораций, этичный дизайн и децентрализация! С Mastodon ваши данные находятся только под вашим контролем!'
|
||||
about_mastodon_html: 'Социальная сеть будущего: никакой рекламы или слежки со стороны корпораций, этичный дизайн и децентрализация. С Mastodon ваши данные находятся только под вашим контролем!'
|
||||
contact_missing: Не указано
|
||||
contact_unavailable: N/A
|
||||
hosted_on: Сервер Mastodon на сайте %{domain}
|
||||
|
@ -350,9 +350,9 @@ ru:
|
|||
copy: Копировать
|
||||
copy_failed_msg: Не удалось создать локальную копию эмодзи
|
||||
create_new_category: Создать новую категорию
|
||||
created_msg: Эмодзи добавлен!
|
||||
created_msg: Эмодзи добавлен
|
||||
delete: Удалить
|
||||
destroyed_msg: Эмодзи удалён!
|
||||
destroyed_msg: Эмодзи удалён
|
||||
disable: Отключить
|
||||
disabled: Отключён
|
||||
disabled_msg: Эмодзи отключён
|
||||
|
@ -375,7 +375,7 @@ ru:
|
|||
unlist: Из списка
|
||||
unlisted: Не в списке
|
||||
update_failed_msg: Не удалось обновить эмодзи
|
||||
updated_msg: Эмодзи обновлён!
|
||||
updated_msg: Эмодзи обновлён
|
||||
upload: Загрузить
|
||||
dashboard:
|
||||
active_users: активные пользователи
|
||||
|
@ -1247,7 +1247,7 @@ ru:
|
|||
delete_account: Удалить учётную запись
|
||||
delete_account_html: Вы можете <a href="%{path}">удалить свою учётную запись</a>. Перед удалением у вас будет запрошено подтверждение.
|
||||
description:
|
||||
prefix_invited_by_user: Вы получили приглашение на сервер Mastodon от @%{name}!
|
||||
prefix_invited_by_user: Вы получили приглашение на сервер Mastodon от @%{name}.
|
||||
prefix_sign_up: Зарегистрируйтесь в Mastodon прямо сейчас!
|
||||
suffix: С учётной записью вы сможете подписываться на людей, публиковать посты и обмениваться сообщениями с пользователями любого сервера Mastodon — и не только!
|
||||
didnt_get_confirmation: Не получили письмо со ссылкой для подтверждения?
|
||||
|
@ -1497,7 +1497,7 @@ ru:
|
|||
one: Выбран <strong>%{count}</strong> элемент, соответствующий вашему запросу.
|
||||
other: Выбраны все <strong>%{count}</strong> элементов, соответствующих вашему запросу.
|
||||
cancel: Отмена
|
||||
changes_saved_msg: Изменения сохранены!
|
||||
changes_saved_msg: Изменения сохранены
|
||||
confirm: Подтвердить
|
||||
copy: Копировать
|
||||
delete: Удалить
|
||||
|
@ -1731,7 +1731,7 @@ ru:
|
|||
subject: "%{name} добавил(а) ваш пост в избранное"
|
||||
title: Ваш пост добавили в избранное
|
||||
follow:
|
||||
body: "%{name} теперь подписан(а) на вас!"
|
||||
body: "%{name} теперь подписан(а) на вас"
|
||||
subject: "%{name} теперь подписан(а) на вас"
|
||||
title: Новый подписчик
|
||||
follow_request:
|
||||
|
@ -1965,7 +1965,7 @@ ru:
|
|||
many: 'содержались запрещённые хэштеги: %{tags}'
|
||||
one: 'содержался запрещённый хэштег: %{tags}'
|
||||
other: 'содержались запрещённые хэштеги: %{tags}'
|
||||
edited_at_html: 'Дата последнего изменения: %{date}'
|
||||
edited_at_html: 'Последнее изменение: %{date}'
|
||||
errors:
|
||||
in_reply_not_found: Пост, на который вы собирались ответить, был удалён или не существует.
|
||||
quoted_status_not_found: Пост, который вы собирались процитировать, был удалён или не существует.
|
||||
|
|
|
@ -56,10 +56,12 @@ ia:
|
|||
scopes: Le APIs al quales le application habera accesso. Si tu selige un ambito de nivello superior, non es necessari seliger ambitos individual.
|
||||
setting_aggregate_reblogs: Non monstrar nove impulsos pro messages que ha essite recentemente impulsate (affecta solmente le impulsos novemente recipite)
|
||||
setting_always_send_emails: Normalmente, le notificationes de e-mail non es inviate quando tu activemente usa Mastodon
|
||||
setting_default_quote_policy: Iste parametro solo habera effecto pro messages create con le proxime version de Mastodon, ma tu pote seliger tu preferentia anticipatemente.
|
||||
setting_default_sensitive: Le medios sensibile es celate de ordinario e pote esser revelate con un clic
|
||||
setting_display_media_default: Celar le medios marcate como sensibile
|
||||
setting_display_media_hide_all: Sempre celar contento multimedial
|
||||
setting_display_media_show_all: Sempre monstrar contento multimedial
|
||||
setting_emoji_style: Como monstrar emojis. “Automatic” tentara usar emojis native, ma recurre al Twemojis pro navigatores ancian.
|
||||
setting_system_scrollbars_ui: Se applica solmente al navigatores de scriptorio basate sur Safari e Chrome
|
||||
setting_use_blurhash: Le imagines degradate se basa sur le colores del visuales celate, ma illos offusca tote le detalios
|
||||
setting_use_pending_items: Requirer un clic pro monstrar nove messages in vice de rolar automaticamente le fluxo
|
||||
|
@ -134,16 +136,23 @@ ia:
|
|||
name: Tu pote solmente cambiar le litteras inter majusculas e minusculas, per exemplo, pro render lo plus legibile
|
||||
terms_of_service:
|
||||
changelog: Pote esser structurate con le syntaxe Markdown.
|
||||
effective_date: Un periodo rationabile pote variar inter 10 e 30 dies post le data al qual tu notifica tu usatores.
|
||||
text: Pote esser structurate con le syntaxe Markdown.
|
||||
terms_of_service_generator:
|
||||
admin_email: Le avisos juridic include le contra-avisos, ordinantias judiciari, demandas de retiro e demandas de application del lege.
|
||||
arbitration_address: Pote esser le mesme que le adresse physic supra, o “N/A” si se usa e-mail.
|
||||
arbitration_website: Pote esser un formulario web, o “N/A” si se usa e-mail.
|
||||
choice_of_law: Citate, region, territorio o stato cuje leges substantive interne governa omne disputa juridic.
|
||||
dmca_address: Pro operatores in le SUA, usa le adresse registrate in le Directorio de Agentes Designate pro le DMCA (DMCA Designated Agent Directory). Un adresse de cassa postal es disponibile per requesta directe; usa le Requesta de Exemption de Cassa Postal pro Agentes Designate del DMCA (DMCA Designated Agent Post Office Box Waiver Request) pro inviar un message electronic al Officio del Derecto de Autor (Copyright Office) e describer que tu es un moderator de contento que travalia de casa e qui time vengiantias o represalias pro tu actiones, necessitante le uso un cassa postal pro remover tu adresse personal del vista public.
|
||||
dmca_email: Pote esser le mesme adresse de e-mail usate pro “Adresse de e-mail pro avisos juridic” supra.
|
||||
domain: Identification unic del servicio in linea que tu forni.
|
||||
jurisdiction: Indica le pais ubi vive le persona qui paga le facturas. Si se tracta de un interprisa o altere organisation, indica le pais ubi illo es incorporate, e le citate, region, territorio o stato del maniera appropriate pro le pais.
|
||||
min_age: Non deberea esser infra le etate minime requirite per le leges de tu jurisdiction.
|
||||
user:
|
||||
chosen_languages: Si marcate, solmente le messages in le linguas seligite apparera in chronologias public
|
||||
date_of_birth:
|
||||
one: Nos debe assecurar que tu ha al minus %{count} anno pro usar %{domain}. Nos non va immagazinar isto.
|
||||
other: Nos debe assecurar que tu ha al minus %{count} annos pro usar %{domain}. Nos non va immagazinar isto.
|
||||
role: Le rolo controla qual permissos le usator ha.
|
||||
user_role:
|
||||
color: Color a esser usate pro le rolo in omne parte del UI, como RGB in formato hexadecimal
|
||||
|
@ -151,6 +160,10 @@ ia:
|
|||
name: Nomine public del rolo, si rolo es definite a esser monstrate como insignia
|
||||
permissions_as_keys: Usatores con iste rolo habera accesso a...
|
||||
position: Rolo superior decide resolution de conflicto in certe situationes. Certe actiones pote solo esser exequite sur rolos con un prioritate inferior
|
||||
username_block:
|
||||
allow_with_approval: In loco de impedir totalmente le inscription, le inscriptiones correspondente requirera tu approbation
|
||||
comparison: Tene in conto le “problema Scunthorpe” si tu bloca correspondentias partial
|
||||
username: Correspondera independentemente de majusculas e minusculas e de homoglyphos commun como "4" pro "a" or "3" pro "e"
|
||||
webhook:
|
||||
events: Selige le eventos a inviar
|
||||
template: Compone tu proprie carga utile JSON per interpolation de variabile. Lassar blanc pro JSON predefinite.
|
||||
|
@ -233,6 +246,7 @@ ia:
|
|||
setting_display_media_default: Predefinite
|
||||
setting_display_media_hide_all: Celar toto
|
||||
setting_display_media_show_all: Monstrar toto
|
||||
setting_emoji_style: Stilo de emojis
|
||||
setting_expand_spoilers: Sempre expander messages marcate con avisos de contento
|
||||
setting_hide_network: Cela tu rete social
|
||||
setting_missing_alt_text_modal: Monstrar un dialogo de confirmation ante de publicar multimedia sin texto alternative
|
||||
|
@ -271,6 +285,7 @@ ia:
|
|||
favicon: Favicon
|
||||
mascot: Personalisar le mascotte (hereditage)
|
||||
media_cache_retention_period: Periodo de retention del cache multimedial
|
||||
min_age: Etate minime requirite
|
||||
peers_api_enabled: Publicar le lista de servitores discoperite in le API
|
||||
profile_directory: Activar directorio de profilos
|
||||
registrations_mode: Qui pote inscriber se
|
||||
|
@ -314,6 +329,7 @@ ia:
|
|||
follow_request: Alcuno ha demandate de sequer te
|
||||
mention: Alcuno te ha mentionate
|
||||
pending_account: Nove conto besonia de revision
|
||||
quote: Alcuno te ha citate
|
||||
reblog: Alcuno ha impulsate tu message
|
||||
report: Un nove reporto es inviate
|
||||
software_updates:
|
||||
|
@ -360,6 +376,10 @@ ia:
|
|||
name: Nomine
|
||||
permissions_as_keys: Permissiones
|
||||
position: Prioritate
|
||||
username_block:
|
||||
allow_with_approval: Permitter registrationes con approbation
|
||||
comparison: Methodo de comparation
|
||||
username: Parola a comparar
|
||||
webhook:
|
||||
events: Eventos activate
|
||||
template: Modello de carga utile
|
||||
|
|
|
@ -56,6 +56,7 @@ sv:
|
|||
scopes: 'Vilka API: er applikationen kommer tillåtas åtkomst till. Om du väljer en omfattning på högstanivån behöver du inte välja individuella sådana.'
|
||||
setting_aggregate_reblogs: Visa inte nya boostar för inlägg som nyligen blivit boostade (påverkar endast nymottagna boostar)
|
||||
setting_always_send_emails: E-postnotiser kommer vanligtvis inte skickas när du aktivt använder Mastodon
|
||||
setting_default_quote_policy: Denna inställning kommer bara att träda i kraft för inlägg som skapats med nästa Mastodon-version, men du kan förbereda dina önskemål.
|
||||
setting_default_sensitive: Känslig media döljs som standard och kan visas med ett klick
|
||||
setting_display_media_default: Dölj media markerad som känslig
|
||||
setting_display_media_hide_all: Dölj alltid all media
|
||||
|
@ -149,6 +150,9 @@ sv:
|
|||
min_age: Bör inte vara lägre än den minimiålder som krävs enligt lagarna i din jurisdiktion.
|
||||
user:
|
||||
chosen_languages: Vid aktivering visas bara inlägg på dina valda språk i offentliga tidslinjer
|
||||
date_of_birth:
|
||||
one: Vi måste se till att du är minst %{count} för att använda %{domain}. Vi sparar inte denna information.
|
||||
other: Vi måste se till att du är minst %{count} för att använda %{domain}. Vi lagrar inte denna information.
|
||||
role: Rollen styr vilka behörigheter användaren har.
|
||||
user_role:
|
||||
color: Färgen som ska användas för rollen i användargränssnittet, som RGB i hex-format
|
||||
|
@ -156,6 +160,10 @@ sv:
|
|||
name: Offentligt namn på rollen, om rollen är inställd på att visas som ett emblem
|
||||
permissions_as_keys: Användare med denna roll kommer ha tillgång till...
|
||||
position: Högre roll avgör konfliktlösning i vissa situationer. Vissa åtgärder kan endast utföras på roller med lägre prioritet
|
||||
username_block:
|
||||
allow_with_approval: I stället för att förhindra direkt registrering kräver matchning av registreringar ditt godkännande
|
||||
comparison: Var uppmärksam på Scunthorpe Problem vid blockering av partiella matcher
|
||||
username: Matchas oavsett versaler och gemena bokstäver som ”4” för ”a” eller ’3’ för ”e”
|
||||
webhook:
|
||||
events: Välj händelser att skicka
|
||||
template: Skriv din egen JSON-nyttolast med variabel interpolation. Lämna tomt för förvald JSON.
|
||||
|
@ -321,6 +329,7 @@ sv:
|
|||
follow_request: Någon begärt att följa dig
|
||||
mention: Någon nämnt dig
|
||||
pending_account: Ett nytt konto behöver granskas
|
||||
quote: Någon citerade dig
|
||||
reblog: Någon boostade ditt inlägg
|
||||
report: En ny rapport har skickats
|
||||
software_updates:
|
||||
|
@ -369,6 +378,7 @@ sv:
|
|||
position: Prioritet
|
||||
username_block:
|
||||
allow_with_approval: Tillåt registreringar med godkännande
|
||||
comparison: Jämförelsesätt
|
||||
username: Ord att matcha
|
||||
webhook:
|
||||
events: Aktiverade händelser
|
||||
|
|
|
@ -258,6 +258,7 @@ sv:
|
|||
create_relay_html: "%{name} skapade ombudet %{target}"
|
||||
create_unavailable_domain_html: "%{name} stoppade leverans till domänen %{target}"
|
||||
create_user_role_html: "%{name} skapade rollen %{target}"
|
||||
create_username_block_html: "%{name} lade till regel för användarnamn som innehåller %{target}"
|
||||
demote_user_html: "%{name} nedgraderade användare %{target}"
|
||||
destroy_announcement_html: "%{name} raderade kungörelsen %{target}"
|
||||
destroy_canonical_email_block_html: "%{name} avblockerade e-post med hash%{target}"
|
||||
|
@ -271,6 +272,7 @@ sv:
|
|||
destroy_status_html: "%{name} tog bort inlägget av %{target}"
|
||||
destroy_unavailable_domain_html: "%{name} återupptog leverans till domänen %{target}"
|
||||
destroy_user_role_html: "%{name} raderade rollen %{target}"
|
||||
destroy_username_block_html: "%{name} borttagna regler för användarnamn som innehåller %{target}"
|
||||
disable_2fa_user_html: "%{name} inaktiverade tvåfaktorsautentiseringskrav för användaren %{target}"
|
||||
disable_custom_emoji_html: "%{name} inaktiverade emoji %{target}"
|
||||
disable_relay_html: "%{name} inaktiverade ombudet %{target}"
|
||||
|
@ -305,6 +307,7 @@ sv:
|
|||
update_report_html: "%{name} uppdaterade rapporten %{target}"
|
||||
update_status_html: "%{name} uppdaterade inlägget av %{target}"
|
||||
update_user_role_html: "%{name} ändrade rollen %{target}"
|
||||
update_username_block_html: "%{name} uppdaterade regel för användarnamn som innehåller %{target}"
|
||||
deleted_account: raderat konto
|
||||
empty: Inga loggar hittades.
|
||||
filter_by_action: Filtrera efter åtgärd
|
||||
|
@ -1090,15 +1093,23 @@ sv:
|
|||
trending: Trendande
|
||||
username_blocks:
|
||||
add_new: Lägg till ny
|
||||
block_registrations: Registrerade blockeringar
|
||||
comparison:
|
||||
contains: Innehåller
|
||||
equals: Lika
|
||||
contains_html: Innehåller %{string}
|
||||
created_msg: Användarnamnsregel har skapats
|
||||
delete: Ta bort
|
||||
edit:
|
||||
title: Redigera användarnamnsregel
|
||||
matches_exactly_html: Lika med %{string}
|
||||
new:
|
||||
create: Skapa regel
|
||||
title: Skapa ny användarnamnsregel
|
||||
no_username_block_selected: Inga användarnamn regler ändrades eftersom ingen valdes
|
||||
not_permitted: Ej tillåtet
|
||||
title: Regler för användarnamn
|
||||
updated_msg: Uppdaterade användarnamnsregel
|
||||
warning_presets:
|
||||
add_new: Lägg till ny
|
||||
delete: Radera
|
||||
|
@ -1676,6 +1687,10 @@ sv:
|
|||
title: Ny omnämning
|
||||
poll:
|
||||
subject: En undersökning av %{name} har avslutats
|
||||
quote:
|
||||
body: 'Ditt inlägg citerades av %{name}:'
|
||||
subject: "%{name} citerade ditt inlägg"
|
||||
title: Nytt citat
|
||||
reblog:
|
||||
body: 'Ditt inlägg boostades av %{name}:'
|
||||
subject: "%{name} boostade ditt inlägg"
|
||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -5945,9 +5945,9 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"core-js@npm:^3.30.2, core-js@npm:^3.45.0":
|
||||
version: 3.45.0
|
||||
resolution: "core-js@npm:3.45.0"
|
||||
checksum: 10c0/118350f9f1d81f42a1276590d6c217dca04c789fdb8074c82e53056b1a784948769a62b16b98493fd73e8a988545432f302bca798571e56ad881b9c039a5a83c
|
||||
version: 3.45.1
|
||||
resolution: "core-js@npm:3.45.1"
|
||||
checksum: 10c0/c38e5fae5a05ee3a129c45e10056aafe61dbb15fd35d27e0c289f5490387541c89741185e0aeb61acb558559c6697e016c245cca738fa169a73f2b06cd30e6b6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -13818,8 +13818,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"vite-plugin-pwa@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "vite-plugin-pwa@npm:1.0.2"
|
||||
version: 1.0.3
|
||||
resolution: "vite-plugin-pwa@npm:1.0.3"
|
||||
dependencies:
|
||||
debug: "npm:^4.3.6"
|
||||
pretty-bytes: "npm:^6.1.1"
|
||||
|
@ -13834,13 +13834,13 @@ __metadata:
|
|||
peerDependenciesMeta:
|
||||
"@vite-pwa/assets-generator":
|
||||
optional: true
|
||||
checksum: 10c0/e4f2f4dfff843ee2585a0d89e74187168ba20da77cd0d127ce7ad7eebcf5a68b2bf09000afb6bb86d43a2034fea9f568cd6db2a2d4b47a72e175d999a5e07eb1
|
||||
checksum: 10c0/03fc24bd12ae4a4130979da4877e3dabddf13d7d6ff2bd68e5e8497a2643b8874a78e6c2502874277ddf2f28593d0a3b025d78af2335bdcc5d2966295784fd46
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite-plugin-static-copy@npm:^3.1.1":
|
||||
version: 3.1.1
|
||||
resolution: "vite-plugin-static-copy@npm:3.1.1"
|
||||
version: 3.1.2
|
||||
resolution: "vite-plugin-static-copy@npm:3.1.2"
|
||||
dependencies:
|
||||
chokidar: "npm:^3.6.0"
|
||||
fs-extra: "npm:^11.3.0"
|
||||
|
@ -13849,7 +13849,7 @@ __metadata:
|
|||
tinyglobby: "npm:^0.2.14"
|
||||
peerDependencies:
|
||||
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
checksum: 10c0/a4dd5d31212b037d4902d55c2ee83866e496857bf948f258599dc24ec61b4628cf0dd23e4a7d7dc189d33ad1489427e976fa95e4db61b394d0be4f63077ef92c
|
||||
checksum: 10c0/1a65f4c9d291cc27483a5b225b1ac5610edc3aa2f13fa3a76a77327874c83bbee52e1011ee0bf5b0168b9b7b974213d49fe800e44af398cfbcb6607814b45c5b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user