mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-15 16:59:41 +00:00
Merge commit 'c87b05282909383353a9561e97b9f18a2db0766d' into glitch-soc/merge-upstream
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import Rails from '@rails/ujs';
|
||||
import { decode, ValidationError } from 'blurhash';
|
||||
import { on } from 'delegated-events';
|
||||
|
||||
import ready from '../mastodon/ready';
|
||||
|
||||
@@ -24,10 +24,9 @@ const setAnnouncementEndsAttributes = (target: HTMLInputElement) => {
|
||||
}
|
||||
};
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'input[type="datetime-local"]#announcement_starts_at',
|
||||
on(
|
||||
'change',
|
||||
'input[type="datetime-local"]#announcement_starts_at',
|
||||
({ target }) => {
|
||||
if (target instanceof HTMLInputElement)
|
||||
setAnnouncementEndsAttributes(target);
|
||||
@@ -63,7 +62,7 @@ const hideSelectAll = () => {
|
||||
if (hiddenField) hiddenField.value = '0';
|
||||
};
|
||||
|
||||
Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
|
||||
on('change', '#batch_checkbox_all', ({ target }) => {
|
||||
if (!(target instanceof HTMLInputElement)) return;
|
||||
|
||||
const selectAllMatchingElement = document.querySelector(
|
||||
@@ -85,7 +84,7 @@ Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
|
||||
}
|
||||
});
|
||||
|
||||
Rails.delegate(document, '.batch-table__select-all button', 'click', () => {
|
||||
on('click', '.batch-table__select-all button', () => {
|
||||
const hiddenField = document.querySelector<HTMLInputElement>(
|
||||
'#select_all_matching',
|
||||
);
|
||||
@@ -113,7 +112,7 @@ Rails.delegate(document, '.batch-table__select-all button', 'click', () => {
|
||||
}
|
||||
});
|
||||
|
||||
Rails.delegate(document, batchCheckboxClassName, 'change', () => {
|
||||
on('change', batchCheckboxClassName, () => {
|
||||
const checkAllElement = document.querySelector<HTMLInputElement>(
|
||||
'input#batch_checkbox_all',
|
||||
);
|
||||
@@ -140,14 +139,9 @@ Rails.delegate(document, batchCheckboxClassName, 'change', () => {
|
||||
}
|
||||
});
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'.filter-subset--with-select select',
|
||||
'change',
|
||||
({ target }) => {
|
||||
if (target instanceof HTMLSelectElement) target.form?.submit();
|
||||
},
|
||||
);
|
||||
on('change', '.filter-subset--with-select select', ({ target }) => {
|
||||
if (target instanceof HTMLSelectElement) target.form?.submit();
|
||||
});
|
||||
|
||||
const onDomainBlockSeverityChange = (target: HTMLSelectElement) => {
|
||||
const rejectMediaDiv = document.querySelector(
|
||||
@@ -168,11 +162,11 @@ const onDomainBlockSeverityChange = (target: HTMLSelectElement) => {
|
||||
}
|
||||
};
|
||||
|
||||
Rails.delegate(document, '#domain_block_severity', 'change', ({ target }) => {
|
||||
on('change', '#domain_block_severity', ({ target }) => {
|
||||
if (target instanceof HTMLSelectElement) onDomainBlockSeverityChange(target);
|
||||
});
|
||||
|
||||
const onEnableBootstrapTimelineAccountsChange = (target: HTMLInputElement) => {
|
||||
function onEnableBootstrapTimelineAccountsChange(target: HTMLInputElement) {
|
||||
const bootstrapTimelineAccountsField =
|
||||
document.querySelector<HTMLInputElement>(
|
||||
'#form_admin_settings_bootstrap_timeline_accounts',
|
||||
@@ -194,12 +188,11 @@ const onEnableBootstrapTimelineAccountsChange = (target: HTMLInputElement) => {
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'#form_admin_settings_enable_bootstrap_timeline_accounts',
|
||||
on(
|
||||
'change',
|
||||
'#form_admin_settings_enable_bootstrap_timeline_accounts',
|
||||
({ target }) => {
|
||||
if (target instanceof HTMLInputElement)
|
||||
onEnableBootstrapTimelineAccountsChange(target);
|
||||
@@ -239,11 +232,11 @@ const onChangeRegistrationMode = (target: HTMLSelectElement) => {
|
||||
});
|
||||
};
|
||||
|
||||
const convertUTCDateTimeToLocal = (value: string) => {
|
||||
function convertUTCDateTimeToLocal(value: string) {
|
||||
const date = new Date(value + 'Z');
|
||||
const twoChars = (x: number) => x.toString().padStart(2, '0');
|
||||
return `${date.getFullYear()}-${twoChars(date.getMonth() + 1)}-${twoChars(date.getDate())}T${twoChars(date.getHours())}:${twoChars(date.getMinutes())}`;
|
||||
};
|
||||
}
|
||||
|
||||
function convertLocalDatetimeToUTC(value: string) {
|
||||
const date = new Date(value);
|
||||
@@ -251,14 +244,9 @@ function convertLocalDatetimeToUTC(value: string) {
|
||||
return fullISO8601.slice(0, fullISO8601.indexOf('T') + 6);
|
||||
}
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'#form_admin_settings_registrations_mode',
|
||||
'change',
|
||||
({ target }) => {
|
||||
if (target instanceof HTMLSelectElement) onChangeRegistrationMode(target);
|
||||
},
|
||||
);
|
||||
on('change', '#form_admin_settings_registrations_mode', ({ target }) => {
|
||||
if (target instanceof HTMLSelectElement) onChangeRegistrationMode(target);
|
||||
});
|
||||
|
||||
async function mountReactComponent(element: Element) {
|
||||
const componentName = element.getAttribute('data-admin-component');
|
||||
@@ -305,7 +293,7 @@ ready(() => {
|
||||
if (registrationMode) onChangeRegistrationMode(registrationMode);
|
||||
|
||||
const checkAllElement = document.querySelector<HTMLInputElement>(
|
||||
'input#batch_checkbox_all',
|
||||
'#batch_checkbox_all',
|
||||
);
|
||||
if (checkAllElement) {
|
||||
const allCheckboxes = Array.from(
|
||||
@@ -318,7 +306,7 @@ ready(() => {
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector('a#add-instance-button')
|
||||
.querySelector<HTMLAnchorElement>('a#add-instance-button')
|
||||
?.addEventListener('click', (e) => {
|
||||
const domain = document.querySelector<HTMLInputElement>(
|
||||
'input[type="text"]#by_domain',
|
||||
@@ -342,7 +330,7 @@ ready(() => {
|
||||
}
|
||||
});
|
||||
|
||||
Rails.delegate(document, 'form', 'submit', ({ target }) => {
|
||||
on('submit', 'form', ({ target }) => {
|
||||
if (target instanceof HTMLFormElement)
|
||||
target
|
||||
.querySelectorAll<HTMLInputElement>('input[type="datetime-local"]')
|
||||
|
||||
@@ -4,8 +4,8 @@ import { IntlMessageFormat } from 'intl-messageformat';
|
||||
import type { MessageDescriptor, PrimitiveType } from 'react-intl';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import Rails from '@rails/ujs';
|
||||
import axios from 'axios';
|
||||
import { on } from 'delegated-events';
|
||||
import { throttle } from 'lodash';
|
||||
|
||||
import { timeAgoString } from '../mastodon/components/relative_timestamp';
|
||||
@@ -175,10 +175,9 @@ function loaded() {
|
||||
});
|
||||
}
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'input#user_account_attributes_username',
|
||||
on(
|
||||
'input',
|
||||
'input#user_account_attributes_username',
|
||||
throttle(
|
||||
({ target }) => {
|
||||
if (!(target instanceof HTMLInputElement)) return;
|
||||
@@ -202,60 +201,47 @@ function loaded() {
|
||||
),
|
||||
);
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'#user_password,#user_password_confirmation',
|
||||
'input',
|
||||
() => {
|
||||
const password = document.querySelector<HTMLInputElement>(
|
||||
'input#user_password',
|
||||
);
|
||||
const confirmation = document.querySelector<HTMLInputElement>(
|
||||
'input#user_password_confirmation',
|
||||
);
|
||||
if (!confirmation || !password) return;
|
||||
on('input', '#user_password,#user_password_confirmation', () => {
|
||||
const password = document.querySelector<HTMLInputElement>(
|
||||
'input#user_password',
|
||||
);
|
||||
const confirmation = document.querySelector<HTMLInputElement>(
|
||||
'input#user_password_confirmation',
|
||||
);
|
||||
if (!confirmation || !password) return;
|
||||
|
||||
if (
|
||||
confirmation.value &&
|
||||
confirmation.value.length > password.maxLength
|
||||
) {
|
||||
confirmation.setCustomValidity(
|
||||
formatMessage(messages.passwordExceedsLength),
|
||||
);
|
||||
} else if (password.value && password.value !== confirmation.value) {
|
||||
confirmation.setCustomValidity(
|
||||
formatMessage(messages.passwordDoesNotMatch),
|
||||
);
|
||||
} else {
|
||||
confirmation.setCustomValidity('');
|
||||
}
|
||||
},
|
||||
);
|
||||
if (confirmation.value && confirmation.value.length > password.maxLength) {
|
||||
confirmation.setCustomValidity(
|
||||
formatMessage(messages.passwordExceedsLength),
|
||||
);
|
||||
} else if (password.value && password.value !== confirmation.value) {
|
||||
confirmation.setCustomValidity(
|
||||
formatMessage(messages.passwordDoesNotMatch),
|
||||
);
|
||||
} else {
|
||||
confirmation.setCustomValidity('');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'#edit_profile input[type=file]',
|
||||
'change',
|
||||
({ target }) => {
|
||||
if (!(target instanceof HTMLInputElement)) return;
|
||||
on('change', '#edit_profile input[type=file]', ({ target }) => {
|
||||
if (!(target instanceof HTMLInputElement)) return;
|
||||
|
||||
const avatar = document.querySelector<HTMLImageElement>(
|
||||
`img#${target.id}-preview`,
|
||||
);
|
||||
const avatar = document.querySelector<HTMLImageElement>(
|
||||
`img#${target.id}-preview`,
|
||||
);
|
||||
|
||||
if (!avatar) return;
|
||||
if (!avatar) return;
|
||||
|
||||
let file: File | undefined;
|
||||
if (target.files) file = target.files[0];
|
||||
let file: File | undefined;
|
||||
if (target.files) file = target.files[0];
|
||||
|
||||
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
|
||||
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
|
||||
|
||||
if (url) avatar.src = url;
|
||||
},
|
||||
);
|
||||
if (url) avatar.src = url;
|
||||
});
|
||||
|
||||
Rails.delegate(document, '.input-copy input', 'click', ({ target }) => {
|
||||
on('click', '.input-copy input', ({ target }) => {
|
||||
if (!(target instanceof HTMLInputElement)) return;
|
||||
|
||||
target.focus();
|
||||
@@ -263,7 +249,7 @@ Rails.delegate(document, '.input-copy input', 'click', ({ target }) => {
|
||||
target.setSelectionRange(0, target.value.length);
|
||||
});
|
||||
|
||||
Rails.delegate(document, '.input-copy button', 'click', ({ target }) => {
|
||||
on('click', '.input-copy button', ({ target }) => {
|
||||
if (!(target instanceof HTMLButtonElement)) return;
|
||||
|
||||
const input = target.parentNode?.querySelector<HTMLInputElement>(
|
||||
@@ -312,22 +298,22 @@ const toggleSidebar = () => {
|
||||
sidebar.classList.toggle('visible');
|
||||
};
|
||||
|
||||
Rails.delegate(document, '.sidebar__toggle__icon', 'click', () => {
|
||||
on('click', '.sidebar__toggle__icon', () => {
|
||||
toggleSidebar();
|
||||
});
|
||||
|
||||
Rails.delegate(document, '.sidebar__toggle__icon', 'keydown', (e) => {
|
||||
on('keydown', '.sidebar__toggle__icon', (e) => {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
toggleSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
Rails.delegate(document, 'img.custom-emoji', 'mouseover', ({ target }) => {
|
||||
on('mouseover', 'img.custom-emoji', ({ target }) => {
|
||||
if (target instanceof HTMLImageElement && target.dataset.original)
|
||||
target.src = target.dataset.original;
|
||||
});
|
||||
Rails.delegate(document, 'img.custom-emoji', 'mouseout', ({ target }) => {
|
||||
on('mouseout', 'img.custom-emoji', ({ target }) => {
|
||||
if (target instanceof HTMLImageElement && target.dataset.static)
|
||||
target.src = target.dataset.static;
|
||||
});
|
||||
@@ -376,22 +362,17 @@ const setInputHint = (
|
||||
}
|
||||
};
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'#account_statuses_cleanup_policy_enabled',
|
||||
'change',
|
||||
({ target }) => {
|
||||
if (!(target instanceof HTMLInputElement) || !target.form) return;
|
||||
on('change', '#account_statuses_cleanup_policy_enabled', ({ target }) => {
|
||||
if (!(target instanceof HTMLInputElement) || !target.form) return;
|
||||
|
||||
target.form
|
||||
.querySelectorAll<
|
||||
HTMLInputElement | HTMLSelectElement
|
||||
>('input:not([type=hidden], #account_statuses_cleanup_policy_enabled), select')
|
||||
.forEach((input) => {
|
||||
setInputDisabled(input, !target.checked);
|
||||
});
|
||||
},
|
||||
);
|
||||
target.form
|
||||
.querySelectorAll<
|
||||
HTMLInputElement | HTMLSelectElement
|
||||
>('input:not([type=hidden], #account_statuses_cleanup_policy_enabled), select')
|
||||
.forEach((input) => {
|
||||
setInputDisabled(input, !target.checked);
|
||||
});
|
||||
});
|
||||
|
||||
const updateDefaultQuotePrivacyFromPrivacy = (
|
||||
privacySelect: EventTarget | null,
|
||||
@@ -414,18 +395,13 @@ const updateDefaultQuotePrivacyFromPrivacy = (
|
||||
}
|
||||
};
|
||||
|
||||
Rails.delegate(
|
||||
document,
|
||||
'#user_settings_attributes_default_privacy',
|
||||
'change',
|
||||
({ target }) => {
|
||||
updateDefaultQuotePrivacyFromPrivacy(target);
|
||||
},
|
||||
);
|
||||
on('change', '#user_settings_attributes_default_privacy', ({ target }) => {
|
||||
updateDefaultQuotePrivacyFromPrivacy(target);
|
||||
});
|
||||
|
||||
// Empty the honeypot fields in JS in case something like an extension
|
||||
// automatically filled them.
|
||||
Rails.delegate(document, '#registration_new_user,#new_user', 'submit', () => {
|
||||
on('submit', '#registration_new_user,#new_user', () => {
|
||||
[
|
||||
'user_website',
|
||||
'user_confirm_password',
|
||||
@@ -439,7 +415,7 @@ Rails.delegate(document, '#registration_new_user,#new_user', 'submit', () => {
|
||||
});
|
||||
});
|
||||
|
||||
Rails.delegate(document, '.rules-list button', 'click', ({ target }) => {
|
||||
on('click', '.rules-list button', ({ target }) => {
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -675,8 +675,8 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
|
||||
|
||||
dispatch(useEmoji(suggestion));
|
||||
} else if (suggestion.type === 'hashtag') {
|
||||
completion = suggestion.name.slice(token.length - 1);
|
||||
startPosition = position + token.length;
|
||||
completion = token + suggestion.name.slice(token.length - 1);
|
||||
startPosition = position - 1;
|
||||
} else if (suggestion.type === 'account') {
|
||||
completion = `@${getState().getIn(['accounts', suggestion.id, 'acct'])}`;
|
||||
startPosition = position - 1;
|
||||
|
||||
@@ -112,7 +112,7 @@ export function normalizeStatus(status, normalOldStatus, { bogusQuotePolicy = fa
|
||||
}
|
||||
|
||||
if (normalOldStatus) {
|
||||
normalStatus.quote_approval ||= normalOldStatus.quote_approval;
|
||||
normalStatus.quote_approval ||= normalOldStatus.get('quote_approval');
|
||||
|
||||
const list = normalOldStatus.get('media_attachments');
|
||||
if (normalStatus.media_attachments && list) {
|
||||
|
||||
@@ -50,6 +50,7 @@ const AutosuggestTextarea = forwardRef(({
|
||||
onKeyUp,
|
||||
onKeyDown,
|
||||
onPaste,
|
||||
onDrop,
|
||||
onFocus,
|
||||
autoFocus = true,
|
||||
lang,
|
||||
@@ -153,6 +154,12 @@ const AutosuggestTextarea = forwardRef(({
|
||||
onPaste(e);
|
||||
}, [onPaste]);
|
||||
|
||||
const handleDrop = useCallback((e) => {
|
||||
if (onDrop) {
|
||||
onDrop(e);
|
||||
}
|
||||
}, [onDrop]);
|
||||
|
||||
// Show the suggestions again whenever they change and the textarea is focused
|
||||
useEffect(() => {
|
||||
if (suggestions.size > 0 && textareaRef.current === document.activeElement) {
|
||||
@@ -204,6 +211,7 @@ const AutosuggestTextarea = forwardRef(({
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onPaste={handlePaste}
|
||||
onDrop={handleDrop}
|
||||
dir='auto'
|
||||
aria-autocomplete='list'
|
||||
aria-label={placeholder}
|
||||
@@ -235,6 +243,7 @@ AutosuggestTextarea.propTypes = {
|
||||
onKeyUp: PropTypes.func,
|
||||
onKeyDown: PropTypes.func,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
onDrop: PropTypes.func,
|
||||
onFocus:PropTypes.func,
|
||||
autoFocus: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
|
||||
@@ -27,12 +27,14 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
|
||||
}) => {
|
||||
// Handle hashtags
|
||||
if (
|
||||
text.startsWith('#') ||
|
||||
prevText?.endsWith('#') ||
|
||||
text.startsWith('#') ||
|
||||
prevText?.endsWith('#')
|
||||
(text.startsWith('#') ||
|
||||
prevText?.endsWith('#') ||
|
||||
text.startsWith('#') ||
|
||||
prevText?.endsWith('#')) &&
|
||||
!text.includes('%')
|
||||
) {
|
||||
const hashtag = text.slice(1).trim();
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={classNames('mention hashtag', className)}
|
||||
@@ -69,7 +71,7 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
|
||||
return (
|
||||
<a
|
||||
{...props}
|
||||
href={href}
|
||||
href={encodeURI(href)}
|
||||
title={href}
|
||||
className={classNames('unhandled-link', className)}
|
||||
target='_blank'
|
||||
|
||||
@@ -64,6 +64,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
onSuggestionSelected: PropTypes.func.isRequired,
|
||||
onChangeSpoilerText: PropTypes.func.isRequired,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
onDrop: PropTypes.func.isRequired,
|
||||
onPickEmoji: PropTypes.func.isRequired,
|
||||
autoFocus: PropTypes.bool,
|
||||
withoutNavigation: PropTypes.bool,
|
||||
@@ -102,6 +103,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
handleKeyDownPost = (e) => {
|
||||
if (e.key.toLowerCase() === 'enter' && (e.ctrlKey || e.metaKey)) {
|
||||
this.handleSubmit();
|
||||
e.preventDefault();
|
||||
}
|
||||
this.blurOnEscape(e);
|
||||
};
|
||||
@@ -248,7 +250,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, onPaste, autoFocus, withoutNavigation, maxChars, isSubmitting } = this.props;
|
||||
const { intl, onPaste, onDrop, autoFocus, withoutNavigation, maxChars, isSubmitting } = this.props;
|
||||
const { highlighted } = this.state;
|
||||
|
||||
return (
|
||||
@@ -304,6 +306,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
onDrop={onDrop}
|
||||
autoFocus={autoFocus}
|
||||
lang={this.props.lang}
|
||||
className='compose-form__input'
|
||||
|
||||
@@ -18,6 +18,23 @@ import ComposeForm from '../components/compose_form';
|
||||
|
||||
const urlLikeRegex = /^https?:\/\/[^\s]+\/[^\s]+$/i;
|
||||
|
||||
const processPasteOrDrop = (transfer, e, dispatch) => {
|
||||
if (transfer && transfer.files.length === 1) {
|
||||
dispatch(uploadCompose(transfer.files));
|
||||
e.preventDefault();
|
||||
} else if (transfer && transfer.files.length === 0) {
|
||||
const data = transfer.getData('text/plain');
|
||||
if (!data.match(urlLikeRegex)) return;
|
||||
|
||||
try {
|
||||
const url = new URL(data);
|
||||
dispatch(pasteLinkCompose({ url }));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
text: state.getIn(['compose', 'text']),
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
@@ -85,20 +102,11 @@ const mapDispatchToProps = (dispatch, props) => ({
|
||||
},
|
||||
|
||||
onPaste (e) {
|
||||
if (e.clipboardData && e.clipboardData.files.length === 1) {
|
||||
dispatch(uploadCompose(e.clipboardData.files));
|
||||
e.preventDefault();
|
||||
} else if (e.clipboardData && e.clipboardData.files.length === 0) {
|
||||
const data = e.clipboardData.getData('text/plain');
|
||||
if (!data.match(urlLikeRegex)) return;
|
||||
processPasteOrDrop(e.clipboardData, e, dispatch);
|
||||
},
|
||||
|
||||
try {
|
||||
const url = new URL(data);
|
||||
dispatch(pasteLinkCompose({ url }));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
onDrop (e) {
|
||||
processPasteOrDrop(e.dataTransfer, e, dispatch);
|
||||
},
|
||||
|
||||
onPickEmoji (position, data, needsSpace) {
|
||||
|
||||
@@ -157,6 +157,8 @@
|
||||
"bundle_modal_error.close": "Tanca",
|
||||
"bundle_modal_error.message": "S'ha produït un error en carregar aquesta pantalla.",
|
||||
"bundle_modal_error.retry": "Torna-ho a provar",
|
||||
"carousel.current": "<sr>Diapositiva</sr> {current, number} / {max, number}",
|
||||
"carousel.slide": "Diapositiva {current, number} de {max, number}",
|
||||
"closed_registrations.other_server_instructions": "Com que Mastodon és descentralitzat, pots crear un compte en un altre servidor i continuar interactuant amb aquest.",
|
||||
"closed_registrations_modal.description": "No es pot crear un compte a {domain} ara mateix, però tingues en compte que no necessites específicament un compte a {domain} per a usar Mastodon.",
|
||||
"closed_registrations_modal.find_another_server": "Troba un altre servidor",
|
||||
@@ -173,6 +175,8 @@
|
||||
"column.edit_list": "Edita la llista",
|
||||
"column.favourites": "Favorits",
|
||||
"column.firehose": "Tuts en directe",
|
||||
"column.firehose_local": "Canal en directe per a aquest servidor",
|
||||
"column.firehose_singular": "Canal en directe",
|
||||
"column.follow_requests": "Peticions de seguir-te",
|
||||
"column.home": "Inici",
|
||||
"column.list_members": "Gestiona els membres de la llista",
|
||||
@@ -245,8 +249,13 @@
|
||||
"confirmations.missing_alt_text.secondary": "Publica-la igualment",
|
||||
"confirmations.missing_alt_text.title": "Hi voleu afegir text alternatiu?",
|
||||
"confirmations.mute.confirm": "Silencia",
|
||||
"confirmations.private_quote_notify.cancel": "Torna a l'edició",
|
||||
"confirmations.private_quote_notify.message": "La persona que citeu i altres mencionades rebran una notificació i podran veure la vostra publicació, encara que no us segueixen.",
|
||||
"confirmations.private_quote_notify.title": "Voleu compartir amb seguidors i usuaris mencionats?",
|
||||
"confirmations.quiet_post_quote_info.dismiss": "No m'ho tornis a recordar",
|
||||
"confirmations.quiet_post_quote_info.got_it": "Entesos",
|
||||
"confirmations.quiet_post_quote_info.message": "Quan citeu una publicació pública en mode silenciós, la vostra publicació s'amagarà de les línies de temps de tendències.",
|
||||
"confirmations.quiet_post_quote_info.title": "Citació d'una publicació pública en mode silenciós",
|
||||
"confirmations.redraft.confirm": "Esborra i reescriu",
|
||||
"confirmations.redraft.message": "Segur que vols eliminar aquest tut i tornar a escriure'l? Es perdran tots els impulsos i els favorits, i les respostes al tut original quedaran aïllades.",
|
||||
"confirmations.redraft.title": "Esborrar i reescriure la publicació?",
|
||||
@@ -332,6 +341,7 @@
|
||||
"empty_column.bookmarked_statuses": "Encara no has marcat cap tut. Quan en marquis un, apareixerà aquí.",
|
||||
"empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per posar-ho tot en marxa!",
|
||||
"empty_column.direct": "Encara no tens mencions privades. Quan n'enviïs o en rebis una, et sortirà aquí.",
|
||||
"empty_column.disabled_feed": "Aquest canal ha estat desactivat per l'administració del vostre servidor.",
|
||||
"empty_column.domain_blocks": "Encara no hi ha dominis blocats.",
|
||||
"empty_column.explore_statuses": "No hi ha res en tendència ara mateix. Revisa-ho més tard!",
|
||||
"empty_column.favourited_statuses": "Encara no has afavorit cap tut. Quan ho facis, apareixerà aquí.",
|
||||
@@ -357,6 +367,7 @@
|
||||
"explore.trending_statuses": "Tuts",
|
||||
"explore.trending_tags": "Etiquetes",
|
||||
"featured_carousel.header": "{count, plural, one {Publicació fixada} other {Publicacions fixades}}",
|
||||
"featured_carousel.slide": "Publicació {current, number} de {max, number}",
|
||||
"filter_modal.added.context_mismatch_explanation": "Aquesta categoria de filtre no s'aplica al context en què has accedit a aquest tut. Si també vols que el tut es filtri en aquest context, hauràs d'editar el filtre.",
|
||||
"filter_modal.added.context_mismatch_title": "El context no coincideix!",
|
||||
"filter_modal.added.expired_explanation": "La categoria d'aquest filtre ha caducat, necessitaràs canviar la seva data de caducitat per a aplicar-la.",
|
||||
@@ -456,6 +467,7 @@
|
||||
"ignore_notifications_modal.not_following_title": "Voleu ignorar les notificacions de qui no seguiu?",
|
||||
"ignore_notifications_modal.private_mentions_title": "Voleu ignorar les notificacions de mencions privades no sol·licitades?",
|
||||
"info_button.label": "Ajuda",
|
||||
"info_button.what_is_alt_text": "<h1>Què és el text alternatiu?</h1> <p>El text alternatiu proporciona descripcions d'imatges per a persones amb discapacitat visual, connexions de poca amplada de banda o aquelles que busquen un context addicional.</p> <p>Podeu millorar l'accessibilitat i la comprensió per a tothom escrivint un text alternatiu clar, concís i objectiu.</p> <ul> <li>Descriviu els elements importants</li> <li>Utilitzeu frases senzilles</li> <li>Resumiu el text en imatges</li> <li>Eviteu la informació redundant</li> <li>Centreu-vos en les tendències i els aspectes clau dels elements visuals complexos (com ara diagrames o mapes)</li> </ul>",
|
||||
"interaction_modal.action": "Per a interactuar amb la publicació de {name} cal que inicieu la sessió en el servidor que feu servir.",
|
||||
"interaction_modal.go": "Endavant",
|
||||
"interaction_modal.no_account_yet": "Encara no teniu cap compte?",
|
||||
@@ -1000,10 +1012,14 @@
|
||||
"video.volume_down": "Abaixa el volum",
|
||||
"video.volume_up": "Apuja el volum",
|
||||
"visibility_modal.button_title": "Establiu la visibilitat",
|
||||
"visibility_modal.direct_quote_warning.text": "Si deseu la configuració actual, la cita incrustada es convertirà en un enllaç.",
|
||||
"visibility_modal.direct_quote_warning.title": "Les cites no es poden incrustar a les mencions privades",
|
||||
"visibility_modal.header": "Visibilitat i interacció",
|
||||
"visibility_modal.helper.direct_quoting": "No es poden citar mencions privades fetes a Mastondon.",
|
||||
"visibility_modal.helper.privacy_editing": "La visibilitat no es pot canviar després de publicar una publicació.",
|
||||
"visibility_modal.helper.privacy_private_self_quote": "Les autocites de publicacions privades no es poden fer públiques.",
|
||||
"visibility_modal.helper.private_quoting": "No es poden citar publicacions fetes a Mastodon només per a seguidors.",
|
||||
"visibility_modal.helper.unlisted_quoting": "Quan la gent et citi les seves publicacions estaran amagades de les línies de temps de tendències.",
|
||||
"visibility_modal.helper.unlisted_quoting": "Quan la gent us citi, les seves publicacions quedaran amagades de les línies de temps de tendències.",
|
||||
"visibility_modal.instructions": "Controleu qui pot interactuar amb aquesta publicació. També podeu aplicar la configuració a totes les publicacions futures navegant a <link>Preferències > Valors per defecte de publicació</link>.",
|
||||
"visibility_modal.privacy_label": "Visibilitat",
|
||||
"visibility_modal.quote_followers": "Només seguidors",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"alt_text_modal.cancel": "Abbrechen",
|
||||
"alt_text_modal.change_thumbnail": "Vorschaubild ändern",
|
||||
"alt_text_modal.describe_for_people_with_hearing_impairments": "Beschreibe den Inhalt für Menschen mit Schwerhörigkeit …",
|
||||
"alt_text_modal.describe_for_people_with_visual_impairments": "Beschreibe den Inhalt für Menschen mit Sehschwäche …",
|
||||
"alt_text_modal.describe_for_people_with_visual_impairments": "Beschreibe den Inhalt für Menschen, die blind oder sehbehindert sind …",
|
||||
"alt_text_modal.done": "Fertig",
|
||||
"announcement.announcement": "Ankündigung",
|
||||
"annual_report.summary.archetype.booster": "Trendjäger*in",
|
||||
@@ -245,7 +245,7 @@
|
||||
"confirmations.logout.message": "Möchtest du dich wirklich abmelden?",
|
||||
"confirmations.logout.title": "Abmelden?",
|
||||
"confirmations.missing_alt_text.confirm": "Bildbeschreibung hinzufügen",
|
||||
"confirmations.missing_alt_text.message": "Dein Beitrag enthält Medien ohne Bildbeschreibung. Mit Alt-Texten erreichst du auch Menschen mit einer Sehschwäche.",
|
||||
"confirmations.missing_alt_text.message": "Dein Beitrag enthält Medien ohne Bildbeschreibung. Mit ALT-Texten erreichst Du auch Menschen, die blind oder sehbehindert sind.",
|
||||
"confirmations.missing_alt_text.secondary": "Trotzdem veröffentlichen",
|
||||
"confirmations.missing_alt_text.title": "Bildbeschreibung hinzufügen?",
|
||||
"confirmations.mute.confirm": "Stummschalten",
|
||||
@@ -470,7 +470,7 @@
|
||||
"ignore_notifications_modal.not_following_title": "Benachrichtigungen von Profilen ignorieren, denen du nicht folgst?",
|
||||
"ignore_notifications_modal.private_mentions_title": "Benachrichtigungen von unerwünschten privaten Erwähnungen ignorieren?",
|
||||
"info_button.label": "Hilfe",
|
||||
"info_button.what_is_alt_text": "<h1>Was ist Alt-Text?</h1> <p>Alt-Text bietet Bildbeschreibungen für Personen mit einer Sehschwäche, einer schlechten Internetverbindung und für alle, die zusätzlichen Kontext möchten.</p> <p>Du kannst die Zugänglichkeit und die Verständlichkeit für alle verbessern, indem du eine klare, genaue und objektive Bildbeschreibung hinzufügst.</p> <ul> <li>Erfasse wichtige Elemente</li> <li>Fasse Text in Bildern zusammen</li> <li>Verwende einen korrekten Satzbau</li> <li>Vermeide unwichtige Informationen</li> <li>Konzentriere dich bei komplexen Darstellungen (z. B. Diagramme oder Karten) auf Trends und wichtige Erkenntnisse</li> </ul>",
|
||||
"info_button.what_is_alt_text": "<h1>Was ist Alt-Text?</h1> <p>Der Alt-Text bietet Bildbeschreibungen für blinde und sehbehinderte Menschen, aber auch für alle mit einer schlechten Internetverbindung und wer einen zusätzlichen Kontext möchten.</p> <p>Du kannst die Barrierefreiheit und Verständlichkeit für alle verbessern, indem du eine klare, genaue und objektive Bildbeschreibung erstellst.</p> <ul> <li>Erfasse wichtige Elemente</li> <li>Fasse Texte bildlich zusammen</li> <li>Verwende einen korrekten Satzbau</li> <li>Vermeide unwichtige und überflüssige Informationen</li> <li>Konzentriere dich bei komplexen Darstellungen (z. B. bei Diagrammen oder Karten) auf Veränderungen und Schlüsselwörter</li> </ul>",
|
||||
"interaction_modal.action": "Melde dich auf deinem Mastodon-Server an, damit du mit dem Beitrag von {name} interagieren kannst.",
|
||||
"interaction_modal.go": "Los",
|
||||
"interaction_modal.no_account_yet": "Du hast noch kein Konto?",
|
||||
|
||||
@@ -1029,7 +1029,7 @@
|
||||
"visibility_modal.helper.unlisted_quoting": "Cuando otras cuentas te citen, sus publicaciones también se ocultarán de las líneas temporales de tendencias.",
|
||||
"visibility_modal.instructions": "Controlá quién puede interactuar con este mensaje. También podés aplicar los ajustes para todos los mensajes futuros accediendo a <link>«Configuración» > «Configuración predeterminada de mensajes»</link>.",
|
||||
"visibility_modal.privacy_label": "Visibilidad",
|
||||
"visibility_modal.quote_followers": "Solo para seguidores",
|
||||
"visibility_modal.quote_followers": "Solo seguidores",
|
||||
"visibility_modal.quote_label": "Quién puede citar",
|
||||
"visibility_modal.quote_nobody": "Solo yo",
|
||||
"visibility_modal.quote_public": "Cualquier cuenta",
|
||||
|
||||
@@ -903,6 +903,7 @@
|
||||
"status.edited_x_times": "Curtha in eagar {count, plural, one {{count} uair amháin} two {{count} uair} few {{count} uair} many {{count} uair} other {{count} uair}}",
|
||||
"status.embed": "Faigh cód leabú",
|
||||
"status.favourite": "Is fearr leat",
|
||||
"status.favourites_count": "{count, plural,\n one {{counter} cheanán}\n two {{counter} cheanáin}\n few {{counter} ceanáin}\n many {{counter} ceanán}\n other {{counter} ceanáin}\n}",
|
||||
"status.filter": "Déan scagadh ar an bpostáil seo",
|
||||
"status.history.created": "Chruthaigh {name} {date}",
|
||||
"status.history.edited": "Curtha in eagar ag {name} in {date}",
|
||||
@@ -937,12 +938,14 @@
|
||||
"status.quotes.empty": "Níl an post seo luaite ag aon duine go fóill. Nuair a dhéanann duine é, taispeánfar anseo é.",
|
||||
"status.quotes.local_other_disclaimer": "Ní thaispeánfar sleachta ar dhiúltaigh an t-údar dóibh.",
|
||||
"status.quotes.remote_other_disclaimer": "Níl ráthaíocht ann go dtaispeánfar anseo ach sleachta ó {domain}. Ní thaispeánfar sleachta ar dhiúltaigh an t-údar dóibh.",
|
||||
"status.quotes_count": "{count, plural,\n one {{counter} athfhriotal}\n two {{counter} athfhriotail}\n few {{counter} athfhriotail}\n many {{counter} athfhriotal}\n other {{counter} athfhriotail}\n}",
|
||||
"status.read_more": "Léan a thuilleadh",
|
||||
"status.reblog": "Treisiú",
|
||||
"status.reblog_or_quote": "Borradh nó luachan",
|
||||
"status.reblog_private": "Roinn arís le do leanúna",
|
||||
"status.reblogged_by": "Mhol {name}",
|
||||
"status.reblogs.empty": "Níor mhol éinne an phostáil seo fós. Nuair a mholfaidh duine éigin í, taispeánfar anseo é sin.",
|
||||
"status.reblogs_count": "{count, plural,\n one {{counter} athfhriotal}\n two {{counter} athfhriotail}\n few {{counter} athfhriotail}\n many {{counter} athfhriotal}\n other {{counter} athfhriotail}\n}",
|
||||
"status.redraft": "Scrios ⁊ athdhréachtaigh",
|
||||
"status.remove_bookmark": "Bain leabharmharc",
|
||||
"status.remove_favourite": "Bain ó cheanáin",
|
||||
|
||||
@@ -903,6 +903,7 @@
|
||||
"status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
|
||||
"status.embed": "O código a incluír",
|
||||
"status.favourite": "Favorecer",
|
||||
"status.favourites_count": "{count, plural, one {{counter} favorecemento} other {{counter} favorecementos}}",
|
||||
"status.filter": "Filtrar esta publicación",
|
||||
"status.history.created": "{name} creouno o {date}",
|
||||
"status.history.edited": "{name} editouno o {date}",
|
||||
@@ -937,12 +938,14 @@
|
||||
"status.quotes.empty": "Aínda ninguén citou esta publicación. Cando alguén o faga aparecerá aquí.",
|
||||
"status.quotes.local_other_disclaimer": "Non se mostrarán as citas rexeitadas pola autora.",
|
||||
"status.quotes.remote_other_disclaimer": "Só se garante que se mostren as citas do dominio {domain}. Non se mostrarán as citas rexeitadas pola persoa autora.",
|
||||
"status.quotes_count": "{count, plural, one {{counter} cita} other {{counter} citas}}",
|
||||
"status.read_more": "Ler máis",
|
||||
"status.reblog": "Promover",
|
||||
"status.reblog_or_quote": "Promover ou citar",
|
||||
"status.reblog_private": "Volver a compartir coas túas seguidoras",
|
||||
"status.reblogged_by": "{name} promoveu",
|
||||
"status.reblogs.empty": "Aínda ninguén promoveu esta publicación. Cando alguén o faga, amosarase aquí.",
|
||||
"status.reblogs_count": "{count, plural, one {{counter} promoción} other {{counter} promocións}}",
|
||||
"status.redraft": "Eliminar e reescribir",
|
||||
"status.remove_bookmark": "Eliminar marcador",
|
||||
"status.remove_favourite": "Retirar das favoritas",
|
||||
|
||||
@@ -880,6 +880,7 @@
|
||||
"status.quote_error.filtered": "あなたのフィルター設定によって非表示になっています",
|
||||
"status.quote_error.pending_approval": "承認待ちの投稿",
|
||||
"status.quote_noun": "引用",
|
||||
"status.quote_policy_change": "引用できるユーザーの変更",
|
||||
"status.quote_post_author": "{name} の投稿を引用",
|
||||
"status.quote_private": "非公開の投稿は引用できません",
|
||||
"status.quotes.local_other_disclaimer": "投稿者が拒否した引用は表示されません。",
|
||||
|
||||
@@ -252,6 +252,7 @@
|
||||
"confirmations.private_quote_notify.cancel": "편집으로 돌아가기",
|
||||
"confirmations.private_quote_notify.confirm": "게시",
|
||||
"confirmations.private_quote_notify.do_not_show_again": "이 메시지를 다시 표시하지 않음",
|
||||
"confirmations.private_quote_notify.message": "인용하려는 사람과 멘션된 사람들은 나를 팔로우하지 않더라도 게시물에 대한 알림을 받으며 내용을 볼 수 있습니다.",
|
||||
"confirmations.quiet_post_quote_info.dismiss": "다시 보지 않기",
|
||||
"confirmations.quiet_post_quote_info.got_it": "알겠습니다",
|
||||
"confirmations.quiet_post_quote_info.message": "조용한 공개 게시물을 인용하면 그 게시물은 유행 타임라인에서 나타나지 않을 것입니다.",
|
||||
|
||||
@@ -639,7 +639,7 @@
|
||||
"privacy.private.long": "Tikai Tavi sekotāji",
|
||||
"privacy.private.short": "Sekotāji",
|
||||
"privacy.public.long": "Jebkurš Mastodon un ārpus tā",
|
||||
"privacy.public.short": "Redzams visiem",
|
||||
"privacy.public.short": "Publisks",
|
||||
"privacy.quote.anyone": "{visibility}, jebkurš var citēt",
|
||||
"privacy.quote.disabled": "{visibility}, aizliegta citēšana",
|
||||
"privacy.quote.limited": "{visibility}, ierobežota citēšana",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"about.default_locale": "Padrão",
|
||||
"about.disclaimer": "Mastodon é um software de código aberto e livre, e uma marca registrada de Mastodon gGmbH.",
|
||||
"about.domain_blocks.no_reason_available": "Razão não disponível",
|
||||
"about.domain_blocks.preamble": "O Mastodon geralmente permite que você veja o conteúdo e interaja com usuários de qualquer outro servidor no fediverso. Estas são as exceções deste servidor em específico.",
|
||||
"about.domain_blocks.preamble": "O \"Mastodon\" geralmente permite que você veja o conteúdo e interaja com usuários de qualquer outro servidor no \"fediverso\". Estas são as exceções deste servidor em específico.",
|
||||
"about.domain_blocks.silenced.explanation": "Você geralmente não verá perfis e conteúdo deste servidor, a menos que você o procure explicitamente ou opte por seguir.",
|
||||
"about.domain_blocks.silenced.title": "Limitado",
|
||||
"about.domain_blocks.suspended.explanation": "Nenhum dado desse servidor será processado, armazenado ou trocado, impossibilitando qualquer interação ou comunicação com os usuários deste servidor.",
|
||||
@@ -36,7 +36,7 @@
|
||||
"account.familiar_followers_two": "Seguido por {name1} e {name2}",
|
||||
"account.featured": "Em destaque",
|
||||
"account.featured.accounts": "Perfis",
|
||||
"account.featured.hashtags": "Hashtags",
|
||||
"account.featured.hashtags": "\"Hashtags\"",
|
||||
"account.featured_tags.last_status_at": "Última publicação em {date}",
|
||||
"account.featured_tags.last_status_never": "Sem publicações",
|
||||
"account.follow": "Seguir",
|
||||
@@ -49,7 +49,7 @@
|
||||
"account.followers": "Seguidores",
|
||||
"account.followers.empty": "Nada aqui.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
|
||||
"account.followers_you_know_counter": "{counter} que você sabe",
|
||||
"account.followers_you_know_counter": "{counter} que você conhece",
|
||||
"account.following": "Seguindo",
|
||||
"account.following_counter": "{count, plural, one {{counter} seguindo} other {{counter} seguindo}}",
|
||||
"account.follows.empty": "Nada aqui.",
|
||||
@@ -903,6 +903,7 @@
|
||||
"status.edited_x_times": "Editado {count, plural, one {{count} hora} other {{count} vezes}}",
|
||||
"status.embed": "Obter código de incorporação",
|
||||
"status.favourite": "Favorita",
|
||||
"status.favourites_count": "{count, plural, one {{counter} favorito} other {{counter} favoritos}}",
|
||||
"status.filter": "Filtrar esta publicação",
|
||||
"status.history.created": "{name} criou {date}",
|
||||
"status.history.edited": "{name} editou {date}",
|
||||
@@ -937,12 +938,14 @@
|
||||
"status.quotes.empty": "Ninguém citou essa publicação até agora. Quando alguém citar aparecerá aqui.",
|
||||
"status.quotes.local_other_disclaimer": "Citações rejeitadas pelo autor não serão exibidas.",
|
||||
"status.quotes.remote_other_disclaimer": "Apenas citações do {domain} têm a garantia de serem exibidas aqui. Citações rejeitadas pelo autor não serão exibidas.",
|
||||
"status.quotes_count": "{count, plural, one {{counter} mencionar} other {{counter} menções}}",
|
||||
"status.read_more": "Ler mais",
|
||||
"status.reblog": "Dar boost",
|
||||
"status.reblog_or_quote": "Acelerar ou citar",
|
||||
"status.reblog_private": "Compartilhar novamente com seus seguidores",
|
||||
"status.reblogged_by": "{name} deu boost",
|
||||
"status.reblogs.empty": "Nada aqui. Quando alguém der boost, o usuário aparecerá aqui.",
|
||||
"status.reblogs_count": "{count, plural, one {{counter} impulsionar} other {{counter} impulsionados}}",
|
||||
"status.redraft": "Excluir e rascunhar",
|
||||
"status.remove_bookmark": "Remover do Salvos",
|
||||
"status.remove_favourite": "Remover dos favoritos",
|
||||
|
||||
Reference in New Issue
Block a user