Merge branch 'glitch-soc' into develop

This commit is contained in:
Jeremy Kescher 2024-02-28 23:01:31 +01:00
commit de1e89f42c
No known key found for this signature in database
GPG key ID: 80A419A7A613DFA4
196 changed files with 1314 additions and 1454 deletions

View file

@ -5,7 +5,7 @@
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"features": {
"ghcr.io/devcontainers/features/sshd:1": {},
"ghcr.io/devcontainers/features/sshd:1": {}
},
"runServices": ["app", "db", "redis"],
@ -15,16 +15,16 @@
"portsAttributes": {
"3000": {
"label": "web",
"onAutoForward": "notify",
"onAutoForward": "notify"
},
"4000": {
"label": "stream",
"onAutoForward": "silent",
},
"onAutoForward": "silent"
}
},
"otherPortsAttributes": {
"onAutoForward": "silent",
"onAutoForward": "silent"
},
"remoteEnv": {
@ -33,7 +33,7 @@
"STREAMING_API_BASE_URL": "https://${localEnv:CODESPACE_NAME}-4000.app.github.dev",
"DISABLE_FORGERY_REQUEST_PROTECTION": "true",
"ES_ENABLED": "",
"LIBRE_TRANSLATE_ENDPOINT": "",
"LIBRE_TRANSLATE_ENDPOINT": ""
},
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
@ -43,7 +43,7 @@
"customizations": {
"vscode": {
"settings": {},
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"],
},
},
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
}
}
}

View file

@ -5,7 +5,7 @@
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"features": {
"ghcr.io/devcontainers/features/sshd:1": {},
"ghcr.io/devcontainers/features/sshd:1": {}
},
"forwardPorts": [3000, 4000],
@ -14,17 +14,17 @@
"3000": {
"label": "web",
"onAutoForward": "notify",
"requireLocalPort": true,
"requireLocalPort": true
},
"4000": {
"label": "stream",
"onAutoForward": "silent",
"requireLocalPort": true,
},
"requireLocalPort": true
}
},
"otherPortsAttributes": {
"onAutoForward": "silent",
"onAutoForward": "silent"
},
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
@ -34,7 +34,7 @@
"customizations": {
"vscode": {
"settings": {},
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"],
},
},
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
}
}
}

View file

@ -355,7 +355,6 @@ module.exports = defineConfig({
'plugin:import/typescript',
'plugin:promise/recommended',
'plugin:jsdoc/recommended-typescript',
'plugin:prettier/recommended',
],
parserOptions: {
@ -364,6 +363,9 @@ module.exports = defineConfig({
},
rules: {
// Disable formatting rules that have been enabled in the base config
'indent': 'off',
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],

18
.github/workflows/format-check.yml vendored Normal file
View file

@ -0,0 +1,18 @@
name: Check formatting
on:
push:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Check formatting with Prettier
run: yarn format:check

View file

@ -43,4 +43,4 @@ jobs:
- run: echo "::add-matcher::.github/stylelint-matcher.json"
- name: Stylelint
run: yarn lint:sass
run: yarn lint:css

View file

@ -36,4 +36,4 @@ jobs:
- name: Run haml-lint
run: |
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
bundle exec haml-lint
bundle exec haml-lint --reporter github

View file

@ -1,38 +0,0 @@
name: JSON Linting
on:
push:
branches-ignore:
- 'dependabot/**'
- 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.json'
- '.github/workflows/lint-json.yml'
- '!app/javascript/mastodon/locales/*.json'
pull_request:
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.json'
- '.github/workflows/lint-json.yml'
- '!app/javascript/mastodon/locales/*.json'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Prettier
run: yarn lint:json

View file

@ -1,38 +0,0 @@
name: Markdown Linting
on:
push:
branches-ignore:
- 'dependabot/**'
- 'renovate/**'
paths:
- '.github/workflows/lint-md.yml'
- '.nvmrc'
- '.prettier*'
- '**/*.md'
- '!AUTHORS.md'
- 'package.json'
- 'yarn.lock'
pull_request:
paths:
- '.github/workflows/lint-md.yml'
- '.nvmrc'
- '.prettier*'
- '**/*.md'
- '!AUTHORS.md'
- 'package.json'
- 'yarn.lock'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Prettier
run: yarn lint:md

View file

@ -1,40 +0,0 @@
name: YML Linting
on:
push:
branches-ignore:
- 'dependabot/**'
- 'renovate/**'
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.yaml'
- '**/*.yml'
- '.github/workflows/lint-yml.yml'
- '!config/locales/*.yml'
pull_request:
paths:
- 'package.json'
- 'yarn.lock'
- '.nvmrc'
- '.prettier*'
- '**/*.yaml'
- '**/*.yml'
- '.github/workflows/lint-yml.yml'
- '!config/locales/*.yml'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
- name: Prettier
run: yarn lint:yml

View file

@ -54,6 +54,13 @@
# Ignore Docker option files
docker-compose.override.yml
# Ignore public
/public/assets
/public/emoji
/public/packs
/public/packs-test
/public/system
# Ignore emoji map file
/app/javascript/mastodon/features/emoji/emoji_map.json
@ -74,6 +81,7 @@ app/javascript/styles/mastodon/reset.scss
# Ignore the generated AUTHORS.md
AUTHORS.md
# Process a few selected JS files
!lint-staged.config.js
# Ignore glitch-soc emoji map file

View file

@ -333,7 +333,7 @@ GEM
http-form_data (2.3.0)
http_accept_language (2.1.1)
httpclient (2.8.3)
httplog (1.6.2)
httplog (1.6.3)
rack (>= 2.0)
rainbow (>= 2.0.0)
i18n (1.14.1)
@ -744,7 +744,7 @@ GEM
terrapin (1.0.1)
climate_control
test-prof (1.3.1)
thor (1.3.0)
thor (1.3.1)
tilt (2.3.0)
timeout (0.4.1)
tpm-key_attestation (0.12.0)

View file

@ -180,7 +180,7 @@ class ApplicationController < ActionController::Base
use_pack 'error'
render 'errors/self_destruct', layout: 'auth', status: 410, formats: [:html]
end
format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[410] }, status: code }
format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[410] }, status: 410 }
end
end

View file

@ -45,7 +45,7 @@ const hideSelectAll = () => {
Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
[].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => {
document.querySelectorAll(batchCheckboxClassName).forEach((content) => {
content.checked = target.checked;
});
@ -80,8 +80,11 @@ Rails.delegate(document, batchCheckboxClassName, 'change', () => {
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
if (checkAllElement) {
checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
const allCheckboxes = Array.from(
document.querySelectorAll(batchCheckboxClassName)
);
checkAllElement.checked = allCheckboxes.every((content) => content.checked);
checkAllElement.indeterminate = !checkAllElement.checked && allCheckboxes.some((content) => content.checked);
if (selectAllMatchingElement) {
if (checkAllElement.checked) {
@ -93,18 +96,6 @@ Rails.delegate(document, batchCheckboxClassName, 'change', () => {
}
});
Rails.delegate(document, '.media-spoiler-show-button', 'click', () => {
[].forEach.call(document.querySelectorAll('button.media-spoiler'), (element) => {
element.click();
});
});
Rails.delegate(document, '.media-spoiler-hide-button', 'click', () => {
[].forEach.call(document.querySelectorAll('.spoiler-button.spoiler-button--visible button'), (element) => {
element.click();
});
});
Rails.delegate(document, '.filter-subset--with-select select', 'change', ({ target }) => {
target.form.submit();
});
@ -144,11 +135,11 @@ Rails.delegate(document, '#form_admin_settings_enable_bootstrap_timeline_account
const onChangeRegistrationMode = (target) => {
const enabled = target.value === 'approved';
[].forEach.call(document.querySelectorAll('.form_admin_settings_registrations_mode .warning-hint'), (warning_hint) => {
document.querySelectorAll('.form_admin_settings_registrations_mode .warning-hint').forEach((warning_hint) => {
warning_hint.style.display = target.value === 'open' ? 'inline' : 'none';
});
[].forEach.call(document.querySelectorAll('#form_admin_settings_require_invite_text'), (input) => {
document.querySelectorAll('#form_admin_settings_require_invite_text').forEach((input) => {
input.disabled = !enabled;
if (enabled) {
let element = input;
@ -194,8 +185,9 @@ ready(() => {
const checkAllElement = document.querySelector('#batch_checkbox_all');
if (checkAllElement) {
checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
const allCheckboxes = Array.from(document.querySelectorAll(batchCheckboxClassName));
checkAllElement.checked = allCheckboxes.every( (content) => content.checked);
checkAllElement.indeterminate = !checkAllElement.checked && allCheckboxes.some((content) => content.checked);
}
document.querySelector('a#add-instance-button')?.addEventListener('click', (e) => {
@ -208,7 +200,7 @@ ready(() => {
}
});
[].forEach.call(document.querySelectorAll('input[type="datetime-local"]'), element => {
document.querySelectorAll('input[type="datetime-local"]').forEach(element => {
if (element.value) {
element.value = convertUTCDateTimeToLocal(element.value);
}
@ -218,7 +210,7 @@ ready(() => {
});
Rails.delegate(document, 'form', 'submit', ({ target }) => {
[].forEach.call(target.querySelectorAll('input[type="datetime-local"]'), element => {
target.querySelectorAll('input[type="datetime-local"]').forEach(element => {
if (element.value && element.validity.valid) {
element.value = convertLocalDatetimeToUTC(element.value);
}

View file

@ -80,7 +80,7 @@ export default class MediaContainer extends PureComponent {
return (
<IntlProvider>
<>
{[].map.call(components, (component, i) => {
{Array.from(components).map((component, i) => {
const componentName = component.getAttribute('data-component');
const Component = MEDIA_COMPONENTS[componentName];
const { media, card, poll, hashtag, ...props } = JSON.parse(component.getAttribute('data-props'));

View file

@ -5,7 +5,7 @@ import Overlay from 'react-overlays/Overlay';
import { IconButton } from 'flavours/glitch/components/icon_button';
import DropdownMenu from './dropdown_menu';
import { PrivacyDropdownMenu } from './privacy_dropdown_menu';
export const DropdownIconButton = ({ value, disabled, icon, onChange, iconComponent, title, options }) => {
const containerRef = useRef(null);
@ -53,7 +53,7 @@ export const DropdownIconButton = ({ value, disabled, icon, onChange, iconCompon
{({ props, placement }) => (
<div {...props}>
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
<DropdownMenu
<PrivacyDropdownMenu
items={options}
value={value}
onClose={handleClose}

View file

@ -1,125 +0,0 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import classNames from 'classnames';
import { supportsPassiveEvents } from 'detect-passive-events';
import { Icon } from 'flavours/glitch/components/icon';
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
// copied from PrivacyDropdown; will require refactor with upstream down the line
class DropdownMenu extends PureComponent {
static propTypes = {
style: PropTypes.object,
items: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
};
handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) {
this.props.onClose();
e.stopPropagation();
}
};
handleKeyDown = e => {
const { items } = this.props;
const value = e.currentTarget.getAttribute('data-index');
const index = items.findIndex(item => {
return (item.value === value);
});
let element = null;
switch(e.key) {
case 'Escape':
this.props.onClose();
break;
case 'Enter':
this.handleClick(e);
break;
case 'ArrowDown':
element = this.node.childNodes[index + 1] || this.node.firstChild;
break;
case 'ArrowUp':
element = this.node.childNodes[index - 1] || this.node.lastChild;
break;
case 'Tab':
if (e.shiftKey) {
element = this.node.childNodes[index - 1] || this.node.lastChild;
} else {
element = this.node.childNodes[index + 1] || this.node.firstChild;
}
break;
case 'Home':
element = this.node.firstChild;
break;
case 'End':
element = this.node.lastChild;
break;
}
if (element) {
element.focus();
this.props.onChange(element.getAttribute('data-index'));
e.preventDefault();
e.stopPropagation();
}
};
handleClick = e => {
const value = e.currentTarget.getAttribute('data-index');
e.preventDefault();
this.props.onClose();
this.props.onChange(value);
};
componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
}
componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
}
setRef = c => {
this.node = c;
};
setFocusRef = c => {
this.focusedItem = c;
};
render () {
const { style, items, value } = this.props;
return (
<div style={{ ...style }} role='listbox' ref={this.setRef}>
{items.map(item => (
<div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
<div className='privacy-dropdown__option__icon'>
<Icon id={item.icon} icon={item.iconComponent} />
</div>
<div className='privacy-dropdown__option__content'>
<strong>{item.text}</strong>
{item.meta}
</div>
</div>
))}
</div>
);
}
}
export default DropdownMenu;

View file

@ -141,6 +141,7 @@ class LanguageDropdownMenu extends PureComponent {
case 'Escape':
onClose();
break;
case ' ':
case 'Enter':
this.handleClick(e);
break;

View file

@ -5,16 +5,16 @@ import { injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import { supportsPassiveEvents } from 'detect-passive-events';
import Overlay from 'react-overlays/Overlay';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import InfoIcon from '@/material-icons/400-24px/info.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 { Icon } from 'flavours/glitch/components/icon';
import { PrivacyDropdownMenu } from './privacy_dropdown_menu';
const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' },
@ -28,126 +28,6 @@ const messages = defineMessages({
unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: '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.' },
});
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
class PrivacyDropdownMenu extends PureComponent {
static propTypes = {
style: PropTypes.object,
items: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
};
handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) {
this.props.onClose();
e.stopPropagation();
}
};
handleKeyDown = e => {
const { items } = this.props;
const value = e.currentTarget.getAttribute('data-index');
const index = items.findIndex(item => {
return (item.value === value);
});
let element = null;
switch(e.key) {
case 'Escape':
this.props.onClose();
break;
case 'Enter':
this.handleClick(e);
break;
case 'ArrowDown':
element = this.node.childNodes[index + 1] || this.node.firstChild;
break;
case 'ArrowUp':
element = this.node.childNodes[index - 1] || this.node.lastChild;
break;
case 'Tab':
if (e.shiftKey) {
element = this.node.childNodes[index - 1] || this.node.lastChild;
} else {
element = this.node.childNodes[index + 1] || this.node.firstChild;
}
break;
case 'Home':
element = this.node.firstChild;
break;
case 'End':
element = this.node.lastChild;
break;
}
if (element) {
element.focus();
this.props.onChange(element.getAttribute('data-index'));
e.preventDefault();
e.stopPropagation();
}
};
handleClick = e => {
const value = e.currentTarget.getAttribute('data-index');
e.preventDefault();
this.props.onClose();
this.props.onChange(value);
};
componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
}
componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
}
setRef = c => {
this.node = c;
};
setFocusRef = c => {
this.focusedItem = c;
};
render () {
const { style, items, value } = this.props;
return (
<div style={{ ...style }} role='listbox' ref={this.setRef}>
{items.map(item => (
<div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
<div className='privacy-dropdown__option__icon'>
<Icon id={item.icon} icon={item.iconComponent} />
</div>
<div className='privacy-dropdown__option__content'>
<strong>{item.text}</strong>
{item.meta}
</div>
{item.extra && (
<div className='privacy-dropdown__option__additional' title={item.extra}>
<Icon id='info-circle' icon={InfoIcon} />
</div>
)}
</div>
))}
</div>
);
}
}
class PrivacyDropdown extends PureComponent {
static propTypes = {

View file

@ -0,0 +1,128 @@
import PropTypes from 'prop-types';
import { useCallback, useEffect, useRef, useState } from 'react';
import classNames from 'classnames';
import { supportsPassiveEvents } from 'detect-passive-events';
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import { Icon } from 'flavours/glitch/components/icon';
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
export const PrivacyDropdownMenu = ({ style, items, value, onClose, onChange }) => {
const nodeRef = useRef(null);
const focusedItemRef = useRef(null);
const [currentValue, setCurrentValue] = useState(value);
const handleDocumentClick = useCallback((e) => {
if (nodeRef.current && !nodeRef.current.contains(e.target)) {
onClose();
e.stopPropagation();
}
}, [nodeRef, onClose]);
const handleClick = useCallback((e) => {
const value = e.currentTarget.getAttribute('data-index');
e.preventDefault();
onClose();
onChange(value);
}, [onClose, onChange]);
const handleKeyDown = useCallback((e) => {
const value = e.currentTarget.getAttribute('data-index');
const index = items.findIndex(item => (item.value === value));
let element = null;
switch (e.key) {
case 'Escape':
onClose();
break;
case ' ':
case 'Enter':
handleClick(e);
break;
case 'ArrowDown':
element = nodeRef.current.childNodes[index + 1] || nodeRef.current.firstChild;
break;
case 'ArrowUp':
element = nodeRef.current.childNodes[index - 1] || nodeRef.current.lastChild;
break;
case 'Tab':
if (e.shiftKey) {
element = nodeRef.current.childNodes[index + 1] || nodeRef.current.firstChild;
} else {
element = nodeRef.current.childNodes[index - 1] || nodeRef.current.lastChild;
}
break;
case 'Home':
element = nodeRef.current.firstChild;
break;
case 'End':
element = nodeRef.current.lastChild;
break;
}
if (element) {
element.focus();
setCurrentValue(element.getAttribute('data-index'));
e.preventDefault();
e.stopPropagation();
}
}, [nodeRef, items, onClose, handleClick, setCurrentValue]);
useEffect(() => {
document.addEventListener('click', handleDocumentClick, { capture: true });
document.addEventListener('touchend', handleDocumentClick, listenerOptions);
focusedItemRef.current?.focus({ preventScroll: true });
return () => {
document.removeEventListener('click', handleDocumentClick, { capture: true });
document.removeEventListener('touchend', handleDocumentClick, listenerOptions);
};
}, [handleDocumentClick]);
return (
<ul style={{ ...style }} role='listbox' ref={nodeRef}>
{items.map(item => (
<li
role='option'
tabIndex={0}
key={item.value}
data-index={item.value}
onKeyDown={handleKeyDown}
onClick={handleClick}
className={classNames('privacy-dropdown__option', { active: item.value === currentValue })}
aria-selected={item.value === currentValue}
ref={item.value === currentValue ? focusedItemRef : null}
>
<div className='privacy-dropdown__option__icon'>
<Icon id={item.icon} icon={item.iconComponent} />
</div>
<div className='privacy-dropdown__option__content'>
<strong>{item.text}</strong>
{item.meta}
</div>
{item.extra && (
<div className='privacy-dropdown__option__additional' title={item.extra}>
<Icon id='info-circle' icon={InfoIcon} />
</div>
)}
</li>
))}
</ul>
);
};
PrivacyDropdownMenu.propTypes = {
style: PropTypes.object,
items: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
};

View file

@ -8,8 +8,8 @@ import { IconButton } from 'flavours/glitch/components/icon_button';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
const messages = defineMessages({
enable_threaded_mode: { id: 'compose.enable_threaded_mode', defaultMessage: 'Enable threaded more' },
disable_threaded_mode: { id: 'compose.disable_threaded_mode', defaultMessage: 'Disable threaded more' },
enable_threaded_mode: { id: 'compose.enable_threaded_mode', defaultMessage: 'Enable threaded mode' },
disable_threaded_mode: { id: 'compose.disable_threaded_mode', defaultMessage: 'Disable threaded mode' },
});
export const ThreadModeButton = () => {

View file

@ -7,10 +7,14 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import PhotoLibraryIcon from '@/material-icons/400-20px/photo_library.svg?react';
import { IconButton } from 'flavours/glitch/components/icon_button';
import BrushIcon from '@/material-icons/400-24px/brush.svg?react';
import UploadFileIcon from '@/material-icons/400-24px/upload_file.svg?react';
import { DropdownIconButton } from './dropdown_icon_button';
const messages = defineMessages({
upload: { id: 'upload_button.label', defaultMessage: 'Add images, a video or an audio file' },
doodle: { id: 'compose.attach.doodle', defaultMessage: 'Draw something' },
});
const makeMapStateToProps = () => {
@ -21,16 +25,12 @@ const makeMapStateToProps = () => {
return mapStateToProps;
};
const iconStyle = {
height: null,
lineHeight: '27px',
};
class UploadButton extends ImmutablePureComponent {
static propTypes = {
disabled: PropTypes.bool,
onSelectFile: PropTypes.func.isRequired,
onDoodleOpen: PropTypes.func.isRequired,
style: PropTypes.object,
resetFileKey: PropTypes.number,
acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
@ -43,8 +43,12 @@ class UploadButton extends ImmutablePureComponent {
}
};
handleClick = () => {
this.fileElement.click();
handleSelect = (value) => {
if (value === 'upload') {
this.fileElement.click();
} else {
this.props.onDoodleOpen();
}
};
setRef = (c) => {
@ -56,9 +60,32 @@ class UploadButton extends ImmutablePureComponent {
const message = intl.formatMessage(messages.upload);
const options = [
{
icon: 'cloud-upload',
iconComponent: UploadFileIcon,
value: 'upload',
text: intl.formatMessage(messages.upload),
},
{
icon: 'paint-brush',
iconComponent: BrushIcon,
value: 'doodle',
text: intl.formatMessage(messages.doodle),
},
];
return (
<div className='compose-form__upload-button'>
<IconButton icon='paperclip' iconComponent={PhotoLibraryIcon} title={message} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
<DropdownIconButton
icon='paperclip'
iconComponent={PhotoLibraryIcon}
title={message}
disabled={disabled}
onChange={this.handleSelect}
value='upload'
options={options}
/>
<label>
<span style={{ display: 'none' }}>{message}</span>
<input

View file

@ -1,6 +1,7 @@
import { connect } from 'react-redux';
import { uploadCompose } from '../../../actions/compose';
import { openModal } from '../../../actions/modal';
import UploadButton from '../components/upload_button';
const mapStateToProps = state => ({
@ -14,6 +15,12 @@ const mapDispatchToProps = dispatch => ({
dispatch(uploadCompose(files));
},
onDoodleOpen() {
dispatch(openModal({
modalType: 'DOODLE',
modalProps: { noEsc: true, noClose: true },
}));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(UploadButton);

View file

@ -36,7 +36,7 @@ Object.keys(emojiIndex.emojis).forEach(key => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
if (Object.prototype.hasOwnProperty.call(emoji, '1')) {
if (Object.hasOwn(emoji, '1')) {
emoji = emoji['1'];
}
@ -88,7 +88,7 @@ Object.keys(emojiIndex.emojis).forEach(key => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
if (Object.prototype.hasOwnProperty.call(emoji, '1')) {
if (Object.hasOwn(emoji, '1')) {
emoji = emoji['1'];
}

View file

@ -135,19 +135,19 @@ function getData(emoji, skin, set) {
}
}
if (Object.prototype.hasOwnProperty.call(data.short_names, emoji)) {
if (Object.hasOwn(data.short_names, emoji)) {
emoji = data.short_names[emoji];
}
if (Object.prototype.hasOwnProperty.call(data.emojis, emoji)) {
if (Object.hasOwn(data.emojis, emoji)) {
emojiData = data.emojis[emoji];
}
} else if (emoji.id) {
if (Object.prototype.hasOwnProperty.call(data.short_names, emoji.id)) {
if (Object.hasOwn(data.short_names, emoji.id)) {
emoji.id = data.short_names[emoji.id];
}
if (Object.prototype.hasOwnProperty.call(data.emojis, emoji.id)) {
if (Object.hasOwn(data.emojis, emoji.id)) {
emojiData = data.emojis[emoji.id];
skin = skin || emoji.skin;
}
@ -216,7 +216,7 @@ function deepMerge(a, b) {
let originalValue = a[key],
value = originalValue;
if (Object.prototype.hasOwnProperty.call(b, key)) {
if (Object.hasOwn(b, key)) {
value = b[key];
}

View file

@ -9,8 +9,8 @@ import { NavLink, Switch, Route } from 'react-router-dom';
import { connect } from 'react-redux';
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import TagIcon from '@/material-icons/400-24px/tag.svg?react';
import Column from 'flavours/glitch/components/column';
import ColumnHeader from 'flavours/glitch/components/column_header';
import Search from 'flavours/glitch/features/compose/containers/search_container';
@ -59,8 +59,8 @@ class Explore extends PureComponent {
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon={isSearching ? 'search' : 'hashtag'}
iconComponent={isSearching ? SearchIcon : TagIcon}
icon={isSearching ? 'search' : 'explore'}
iconComponent={isSearching ? SearchIcon : ExploreIcon}
title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)}
onClick={this.handleHeaderClick}
multiColumn={multiColumn}

View file

@ -158,7 +158,7 @@ class GettingStarted extends ImmutablePureComponent {
}
if (showTrends) {
navItems.push(<ColumnLink key='explore' icon='hashtag' iconComponent={TagIcon} text={intl.formatMessage(messages.explore)} to='/explore' />);
navItems.push(<ColumnLink key='explore' icon='explore' iconComponent={TagIcon} text={intl.formatMessage(messages.explore)} to='/explore' />);
}
if (signedIn) {

View file

@ -4,6 +4,7 @@ import { Component } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
import MailIcon from '@/material-icons/400-24px/mail.svg?react';
@ -13,7 +14,6 @@ import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import SettingsIcon from '@/material-icons/400-24px/settings-fill.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import TagIcon from '@/material-icons/400-24px/tag.svg?react';
import { NavigationPortal } from 'flavours/glitch/components/navigation_portal';
import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state';
import { transientSingleColumn } from 'flavours/glitch/is_mobile';
@ -91,7 +91,7 @@ class NavigationPanel extends Component {
)}
{trendsEnabled ? (
<ColumnLink transparent to={(signedIn || timelinePreview) ? '/explore' : '/explore/tags'} icon='hashtag' iconComponent={TagIcon} text={intl.formatMessage(messages.explore)} />
<ColumnLink transparent to={(signedIn || timelinePreview) ? '/explore' : '/explore/tags'} icon='explore' iconComponent={ExploreIcon} text={intl.formatMessage(messages.explore)} />
) : (
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
)}

View file

@ -14,6 +14,7 @@
"column_subheading.lists": "Lists",
"column_subheading.navigation": "Navigation",
"community.column_settings.allow_local_only": "Show local-only toots",
"compose.attach.doodle": "Draw something",
"compose.change_federation": "Change federation settings",
"compose.content-type.change": "Change advanced formatting options",
"compose.content-type.html": "HTML",
@ -22,8 +23,8 @@
"compose.content-type.markdown_meta": "Format your posts using Markdown",
"compose.content-type.plain": "Plain text",
"compose.content-type.plain_meta": "Write with no advanced formatting",
"compose.disable_threaded_mode": "Disable threaded more",
"compose.enable_threaded_mode": "Enable threaded more",
"compose.disable_threaded_mode": "Disable threaded mode",
"compose.enable_threaded_mode": "Enable threaded mode",
"compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
"compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",

View file

@ -4,9 +4,9 @@ import { createRoot } from 'react-dom/client';
import ready from 'flavours/glitch/ready';
ready(() => {
[].forEach.call(document.querySelectorAll('[data-admin-component]'), element => {
document.querySelectorAll('[data-admin-component]').forEach(element => {
const componentName = element.getAttribute('data-admin-component');
const { ...componentProps } = JSON.parse(element.getAttribute('data-props'));
const componentProps = JSON.parse(element.getAttribute('data-props'));
import('flavours/glitch/containers/admin_component').then(({ default: AdminComponent }) => {
return import('flavours/glitch/components/admin/' + componentName).then(({ default: Component }) => {

View file

@ -68,11 +68,11 @@ function main() {
return messageFormat.format(values);
};
[].forEach.call(document.querySelectorAll('.emojify'), (content) => {
document.querySelectorAll('.emojify').forEach((content) => {
content.innerHTML = emojify(content.innerHTML);
});
[].forEach.call(document.querySelectorAll('time.formatted'), (content) => {
document.querySelectorAll('time.formatted').forEach((content) => {
const datetime = new Date(content.getAttribute('datetime'));
const formattedDate = dateTimeFormat.format(datetime);
@ -89,7 +89,7 @@ function main() {
};
const todayFormat = new IntlMessageFormat(localeData['relative_format.today'] || 'Today at {time}', locale);
[].forEach.call(document.querySelectorAll('time.relative-formatted'), (content) => {
document.querySelectorAll('time.relative-formatted').forEach((content) => {
const datetime = new Date(content.getAttribute('datetime'));
let formattedContent;
@ -106,7 +106,7 @@ function main() {
content.textContent = formattedContent;
});
[].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
document.querySelectorAll('time.time-ago').forEach((content) => {
const datetime = new Date(content.getAttribute('datetime'));
const now = new Date();
@ -122,8 +122,8 @@ function main() {
if (reactComponents.length > 0) {
import(/* webpackChunkName: "containers/media_container" */ 'flavours/glitch/containers/media_container')
.then(({ default: MediaContainer }) => {
[].forEach.call(reactComponents, (component) => {
[].forEach.call(component.children, (child) => {
reactComponents.forEach((component) => {
Array.from(component.children).forEach((child) => {
component.removeChild(child);
});
});
@ -188,7 +188,7 @@ function main() {
return false;
});
[].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
document.querySelectorAll('.status__content__spoiler-link').forEach((spoilerLink) => {
const statusEl = spoilerLink.parentNode.parentNode;
const message = (statusEl.dataset.spoiler === 'expanded') ? (localeData['status.show_less'] || 'Show less') : (localeData['status.show_more'] || 'Show more');
spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();

View file

@ -324,6 +324,23 @@ $content-width: 840px;
padding-bottom: 0;
margin-bottom: 0;
border-bottom: 0;
.comment {
display: block;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 4px;
&.private-comment {
display: block;
color: $darker-text-color;
}
&.public-comment {
display: block;
color: $secondary-text-color;
}
}
}
& > p {
@ -643,17 +660,6 @@ body,
margin-bottom: 5px;
margin-inline-end: 5px;
}
.media-spoiler-toggle-buttons {
margin-inline-start: auto;
.button {
overflow: visible;
margin-bottom: 5px;
margin-inline-start: 5px;
float: right;
}
}
}
.back-link {
@ -1076,6 +1082,7 @@ a.name-tag,
display: flex;
justify-content: space-between;
margin-bottom: 0;
word-break: break-word;
}
&__permissions {

View file

@ -5737,7 +5737,6 @@ a.status-card {
.icon {
position: absolute;
top: 12px + 2px;
inset-inline-start: 16px - 2px;
display: inline-block;
opacity: 0;
transition: all 100ms linear;
@ -5747,11 +5746,16 @@ a.status-card {
color: $darker-text-color;
cursor: default;
pointer-events: none;
margin-inline-start: 16px - 2px;
&.active {
pointer-events: auto;
opacity: 1;
}
@media screen and (min-width: $no-gap-breakpoint) {
inset-inline-start: 16px - 2px;
}
}
.icon-search {
@ -6521,6 +6525,7 @@ a.status-card {
.report-modal__comment {
box-sizing: border-box;
width: 50%;
min-width: 50%;
@media screen and (width <= 480px) {
width: 100%;
@ -6589,6 +6594,14 @@ a.status-card {
min-height: 100px;
max-height: 50vh;
border: 0;
@media screen and (height <= 600px) {
max-height: 20vh;
}
@media screen and (max-width: $no-columns-breakpoint) {
max-height: 20vh;
}
}
.setting-toggle {

View file

@ -1080,6 +1080,7 @@ code {
&__type {
color: $darker-text-color;
word-break: break-word;
}
}

View file

@ -40,7 +40,7 @@ function render(
ui: React.ReactElement,
{ locale = 'en', signedIn = true, ...renderOptions } = {},
) {
const Wrapper = (props: { children: React.ReactElement }) => {
const Wrapper = (props: { children: React.ReactNode }) => {
return (
<MemoryRouter>
<IntlProvider locale={locale}>

View file

@ -80,7 +80,7 @@ export default class MediaContainer extends PureComponent {
return (
<IntlProvider>
<>
{[].map.call(components, (component, i) => {
{Array.from(components).map((component, i) => {
const componentName = component.getAttribute('data-component');
const Component = MEDIA_COMPONENTS[componentName];
const { media, card, poll, hashtag, ...props } = JSON.parse(component.getAttribute('data-props'));

View file

@ -141,6 +141,7 @@ class LanguageDropdownMenu extends PureComponent {
case 'Escape':
onClose();
break;
case ' ':
case 'Enter':
this.handleClick(e);
break;

View file

@ -5,16 +5,16 @@ import { injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import { supportsPassiveEvents } from 'detect-passive-events';
import Overlay from 'react-overlays/Overlay';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import InfoIcon from '@/material-icons/400-24px/info.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 { Icon } from 'mastodon/components/icon';
import { PrivacyDropdownMenu } from './privacy_dropdown_menu';
const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' },
@ -28,126 +28,6 @@ const messages = defineMessages({
unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: '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.' },
});
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
class PrivacyDropdownMenu extends PureComponent {
static propTypes = {
style: PropTypes.object,
items: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
};
handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) {
this.props.onClose();
e.stopPropagation();
}
};
handleKeyDown = e => {
const { items } = this.props;
const value = e.currentTarget.getAttribute('data-index');
const index = items.findIndex(item => {
return (item.value === value);
});
let element = null;
switch(e.key) {
case 'Escape':
this.props.onClose();
break;
case 'Enter':
this.handleClick(e);
break;
case 'ArrowDown':
element = this.node.childNodes[index + 1] || this.node.firstChild;
break;
case 'ArrowUp':
element = this.node.childNodes[index - 1] || this.node.lastChild;
break;
case 'Tab':
if (e.shiftKey) {
element = this.node.childNodes[index - 1] || this.node.lastChild;
} else {
element = this.node.childNodes[index + 1] || this.node.firstChild;
}
break;
case 'Home':
element = this.node.firstChild;
break;
case 'End':
element = this.node.lastChild;
break;
}
if (element) {
element.focus();
this.props.onChange(element.getAttribute('data-index'));
e.preventDefault();
e.stopPropagation();
}
};
handleClick = e => {
const value = e.currentTarget.getAttribute('data-index');
e.preventDefault();
this.props.onClose();
this.props.onChange(value);
};
componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
}
componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
}
setRef = c => {
this.node = c;
};
setFocusRef = c => {
this.focusedItem = c;
};
render () {
const { style, items, value } = this.props;
return (
<div style={{ ...style }} role='listbox' ref={this.setRef}>
{items.map(item => (
<div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
<div className='privacy-dropdown__option__icon'>
<Icon id={item.icon} icon={item.iconComponent} />
</div>
<div className='privacy-dropdown__option__content'>
<strong>{item.text}</strong>
{item.meta}
</div>
{item.extra && (
<div className='privacy-dropdown__option__additional' title={item.extra}>
<Icon id='info-circle' icon={InfoIcon} />
</div>
)}
</div>
))}
</div>
);
}
}
class PrivacyDropdown extends PureComponent {
static propTypes = {

View file

@ -0,0 +1,128 @@
import PropTypes from 'prop-types';
import { useCallback, useEffect, useRef, useState } from 'react';
import classNames from 'classnames';
import { supportsPassiveEvents } from 'detect-passive-events';
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import { Icon } from 'mastodon/components/icon';
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
export const PrivacyDropdownMenu = ({ style, items, value, onClose, onChange }) => {
const nodeRef = useRef(null);
const focusedItemRef = useRef(null);
const [currentValue, setCurrentValue] = useState(value);
const handleDocumentClick = useCallback((e) => {
if (nodeRef.current && !nodeRef.current.contains(e.target)) {
onClose();
e.stopPropagation();
}
}, [nodeRef, onClose]);
const handleClick = useCallback((e) => {
const value = e.currentTarget.getAttribute('data-index');
e.preventDefault();
onClose();
onChange(value);
}, [onClose, onChange]);
const handleKeyDown = useCallback((e) => {
const value = e.currentTarget.getAttribute('data-index');
const index = items.findIndex(item => (item.value === value));
let element = null;
switch (e.key) {
case 'Escape':
onClose();
break;
case ' ':
case 'Enter':
handleClick(e);
break;
case 'ArrowDown':
element = nodeRef.current.childNodes[index + 1] || nodeRef.current.firstChild;
break;
case 'ArrowUp':
element = nodeRef.current.childNodes[index - 1] || nodeRef.current.lastChild;
break;
case 'Tab':
if (e.shiftKey) {
element = nodeRef.current.childNodes[index + 1] || nodeRef.current.firstChild;
} else {
element = nodeRef.current.childNodes[index - 1] || nodeRef.current.lastChild;
}
break;
case 'Home':
element = nodeRef.current.firstChild;
break;
case 'End':
element = nodeRef.current.lastChild;
break;
}
if (element) {
element.focus();
setCurrentValue(element.getAttribute('data-index'));
e.preventDefault();
e.stopPropagation();
}
}, [nodeRef, items, onClose, handleClick, setCurrentValue]);
useEffect(() => {
document.addEventListener('click', handleDocumentClick, { capture: true });
document.addEventListener('touchend', handleDocumentClick, listenerOptions);
focusedItemRef.current?.focus({ preventScroll: true });
return () => {
document.removeEventListener('click', handleDocumentClick, { capture: true });
document.removeEventListener('touchend', handleDocumentClick, listenerOptions);
};
}, [handleDocumentClick]);
return (
<ul style={{ ...style }} role='listbox' ref={nodeRef}>
{items.map(item => (
<li
role='option'
tabIndex={0}
key={item.value}
data-index={item.value}
onKeyDown={handleKeyDown}
onClick={handleClick}
className={classNames('privacy-dropdown__option', { active: item.value === currentValue })}
aria-selected={item.value === currentValue}
ref={item.value === currentValue ? focusedItemRef : null}
>
<div className='privacy-dropdown__option__icon'>
<Icon id={item.icon} icon={item.iconComponent} />
</div>
<div className='privacy-dropdown__option__content'>
<strong>{item.text}</strong>
{item.meta}
</div>
{item.extra && (
<div className='privacy-dropdown__option__additional' title={item.extra}>
<Icon id='info-circle' icon={InfoIcon} />
</div>
)}
</li>
))}
</ul>
);
};
PrivacyDropdownMenu.propTypes = {
style: PropTypes.object,
items: PropTypes.array.isRequired,
value: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
};

View file

@ -36,7 +36,7 @@ Object.keys(emojiIndex.emojis).forEach(key => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
if (Object.prototype.hasOwnProperty.call(emoji, '1')) {
if (Object.hasOwn(emoji, '1')) {
emoji = emoji['1'];
}
@ -88,7 +88,7 @@ Object.keys(emojiIndex.emojis).forEach(key => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
if (Object.prototype.hasOwnProperty.call(emoji, '1')) {
if (Object.hasOwn(emoji, '1')) {
emoji = emoji['1'];
}

View file

@ -135,19 +135,19 @@ function getData(emoji, skin, set) {
}
}
if (Object.prototype.hasOwnProperty.call(data.short_names, emoji)) {
if (Object.hasOwn(data.short_names, emoji)) {
emoji = data.short_names[emoji];
}
if (Object.prototype.hasOwnProperty.call(data.emojis, emoji)) {
if (Object.hasOwn(data.emojis, emoji)) {
emojiData = data.emojis[emoji];
}
} else if (emoji.id) {
if (Object.prototype.hasOwnProperty.call(data.short_names, emoji.id)) {
if (Object.hasOwn(data.short_names, emoji.id)) {
emoji.id = data.short_names[emoji.id];
}
if (Object.prototype.hasOwnProperty.call(data.emojis, emoji.id)) {
if (Object.hasOwn(data.emojis, emoji.id)) {
emojiData = data.emojis[emoji.id];
skin = skin || emoji.skin;
}
@ -216,7 +216,7 @@ function deepMerge(a, b) {
let originalValue = a[key],
value = originalValue;
if (Object.prototype.hasOwnProperty.call(b, key)) {
if (Object.hasOwn(b, key)) {
value = b[key];
}

View file

@ -58,7 +58,7 @@ class Explore extends PureComponent {
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon={isSearching ? 'search' : 'hashtag'}
icon={isSearching ? 'search' : 'explore'}
iconComponent={isSearching ? SearchIcon : ExploreIcon}
title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)}
onClick={this.handleHeaderClick}

View file

@ -112,7 +112,7 @@ class GettingStarted extends ImmutablePureComponent {
if (showTrends) {
navItems.push(
<ColumnLink key='explore' icon='hashtag' iconComponent={TagIcon} text={intl.formatMessage(messages.explore)} to='/explore' />,
<ColumnLink key='explore' icon='explore' iconComponent={TagIcon} text={intl.formatMessage(messages.explore)} to='/explore' />,
);
}

View file

@ -94,7 +94,7 @@ class NavigationPanel extends Component {
)}
{trendsEnabled ? (
<ColumnLink transparent to={(signedIn || timelinePreview) ? '/explore' : '/explore/tags'} icon='hashtag' iconComponent={ExploreIcon} text={intl.formatMessage(messages.explore)} />
<ColumnLink transparent to={(signedIn || timelinePreview) ? '/explore' : '/explore/tags'} icon='explore' iconComponent={ExploreIcon} text={intl.formatMessage(messages.explore)} />
) : (
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
)}

View file

@ -500,7 +500,7 @@
"onboarding.share.message": "{username} naiz #Mastodon-en! Jarrai nazazu hemen: {url}",
"onboarding.share.next_steps": "Hurrengo urrats posibleak:",
"onboarding.share.title": "Partekatu zure profila",
"onboarding.start.lead": "Zure Mastodoneko kontu berria prest dago. Jakin nola atera diezaioekun etekin handiena hemen:",
"onboarding.start.lead": "Mastodonen parte zara orain, bakarra eta deszentralizatua den sare-sozialaren plataforma, non zuk, eta ez algoritmo batek, zeure esperientzia pertsonaliza dezakezun. Igaro ezazu muga soziala:",
"onboarding.start.skip": "Urrats guztiak saltatu nahi dituzu?",
"onboarding.start.title": "Lortu duzu!",
"onboarding.steps.follow_people.body": "Zure jarioa zuk pertsonalizatzen duzu. Bete dezagun jende interesgarriaz.",

View file

@ -110,7 +110,7 @@
"column.about": "درباره",
"column.blocks": "کاربران مسدود شده",
"column.bookmarks": "نشانک‌ها",
"column.community": "خط زمانی محلّی",
"column.community": "خط زمانی محلی",
"column.direct": "اشاره‌های خصوصی",
"column.directory": "مرور نمایه‌ها",
"column.domain_blocks": "دامنه‌های مسدود شده",
@ -131,7 +131,7 @@
"column_header.show_settings": "نمایش تنظیمات",
"column_header.unpin": "برداشتن سنجاق",
"column_subheading.settings": "تنظیمات",
"community.column_settings.local_only": "فقط محلّی",
"community.column_settings.local_only": "فقط محلی",
"community.column_settings.media_only": "فقط رسانه",
"community.column_settings.remote_only": "تنها دوردست",
"compose.language.change": "تغییر زبان",
@ -228,7 +228,7 @@
"empty_column.account_unavailable": "نمایهٔ موجود نیست",
"empty_column.blocks": "هنوز کسی را مسدود نکرده‌اید.",
"empty_column.bookmarked_statuses": "هنوز هیچ فرستهٔ نشانه‌گذاری شده‌ای ندارید. هنگامی که فرسته‌ای را نشانه‌گذاری کنید، این‌جا نشان داده خواهد شد.",
"empty_column.community": "خط زمانی محلّی خالی است. چیزی بنویسید تا چرخش بچرخد!",
"empty_column.community": "خط زمانی محلی خالیست. چیزی نوشته تا چرخش بچرخد!",
"empty_column.direct": "هنوز هیچ اشاره خصوصی‌ای ندارید. هنگامی که چنین پیامی بگیرید یا بفرستید این‌جا نشان داده خواهد شد.",
"empty_column.domain_blocks": "هنوز هیچ دامنه‌ای مسدود نشده است.",
"empty_column.explore_statuses": "الآن چیزی پرطرفدار نیست. بعداً دوباره بررسی کنید!",
@ -277,6 +277,17 @@
"follow_request.authorize": "اجازه دهید",
"follow_request.reject": "رد کنید",
"follow_requests.unlocked_explanation": "با این که حسابتان قفل نیست، کارکنان {domain} فکر کردند که ممکن است بخواهید درخواست‌ها از این حساب‌ها را به صورت دستی بازبینی کنید.",
"follow_suggestions.curated_suggestion": "گزینش سردبیر",
"follow_suggestions.dismiss": "دیگر نشان داده نشود",
"follow_suggestions.hints.featured": "این نمایه به دست گروه {domain} دستچین شده.",
"follow_suggestions.hints.friends_of_friends": "این نمایه بین کسانی که پی می‌گیرید محبوب است.",
"follow_suggestions.hints.most_followed": "این نمایه روی {domain} بسیار پی‌گرفته شده.",
"follow_suggestions.hints.most_interactions": "این نمایه اخیراُ روی {domain} توجّه زیادی گرفته.",
"follow_suggestions.hints.similar_to_recently_followed": "این نمایه شبیه نمایه‌هاییست که اخیراً پی‌گرفته‌اید.",
"follow_suggestions.personalized_suggestion": "پیشنهاد شخصی",
"follow_suggestions.popular_suggestion": "پیشنهاد محبوب",
"follow_suggestions.view_all": "دیدن همه",
"follow_suggestions.who_to_follow": "افرادی برای پی‌گیری",
"followed_tags": "برچسب‌های پی‌گرفته",
"footer.about": "درباره",
"footer.directory": "فهرست نمایه‌ها",
@ -345,7 +356,7 @@
"keyboard_shortcuts.home": "گشودن خط زمانی خانگی",
"keyboard_shortcuts.hotkey": "میان‌بر",
"keyboard_shortcuts.legend": "نمایش این نشانه",
"keyboard_shortcuts.local": "گشودن خط زمانی محلّی",
"keyboard_shortcuts.local": "گشودن خط زمانی محلی",
"keyboard_shortcuts.mention": "اشاره به نویسنده",
"keyboard_shortcuts.muted": "گشودن فهرست کاربران خموش",
"keyboard_shortcuts.my_profile": "گشودن نمایه‌تان",
@ -396,7 +407,7 @@
"navigation_bar.advanced_interface": "بازکردن در رابط کاربری وب پیشرفته",
"navigation_bar.blocks": "کاربران مسدود شده",
"navigation_bar.bookmarks": "نشانک‌ها",
"navigation_bar.community_timeline": "خط زمانی محلّی",
"navigation_bar.community_timeline": "خط زمانی محلی",
"navigation_bar.compose": "نوشتن فرستهٔ تازه",
"navigation_bar.direct": "اشاره‌های خصوصی",
"navigation_bar.discover": "گشت و گذار",
@ -475,8 +486,10 @@
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon",
"onboarding.profile.discoverable": "نمایه خود را قابل نمایش کنید",
"onboarding.profile.discoverable_hint": "خواسته‌اید روی ماستودون کشف شوید. ممکن است فرسته‌هایتان در نتیحهٔ جست‌وجوها و فرسته‌های داغ ظاهر شده و نمایه‌تان به افرادی با علایق مشابهتان پیشنهاد شود.",
"onboarding.profile.display_name": "نام نمایشی",
"onboarding.profile.display_name_hint": "نام کامل یا نام باحالتان…",
"onboarding.profile.lead": "همواره می‌توانید این مورد را در تنظیمات که گزینه‌ّای شخصی سازی بیش‌تری نیز دارد کامل کنید.",
"onboarding.profile.note": "درباره شما",
"onboarding.profile.note_hint": "می‌توانید افراد دیگر را @نام‌بردن یا #برچسب بزنید…",
"onboarding.profile.save_and_continue": "ذخیره کن و ادامه بده",
@ -522,6 +535,7 @@
"privacy.private.short": "پی‌گیرندگان",
"privacy.public.long": "هرکسی در و بیرون از ماستودون",
"privacy.public.short": "عمومی",
"privacy.unlisted.additional": "درست مثل عمومی رفتار می‌کند؛ جز این که فرسته در برچسب‌ها یا خوراک‌های زنده، کشف یا جست‌وجوی ماستودون ظاهر نخواهد شد. حتا اگر کلیّت نمایه‌تان اجازه داده باشد.",
"privacy.unlisted.long": "سروصدای الگوریتمی کم‌تر",
"privacy.unlisted.short": "عمومی ساکت",
"privacy_policy.last_updated": "آخرین به‌روز رسانی در {date}",
@ -541,6 +555,7 @@
"relative_time.minutes": "{number} دقیقه",
"relative_time.seconds": "{number} ثانیه",
"relative_time.today": "امروز",
"reply_indicator.attachments": "{count, plural, one {# پیوست} other {# پیوست}}",
"reply_indicator.cancel": "لغو",
"reply_indicator.poll": "نظرسنجی",
"report.block": "انسداد",

View file

@ -277,6 +277,17 @@
"follow_request.authorize": "Autoriser",
"follow_request.reject": "Rejeter",
"follow_requests.unlocked_explanation": "Même si votre compte nest pas privé, léquipe de {domain} a pensé que vous pourriez vouloir peut-être consulter manuellement les demandes d'abonnement de ces comptes.",
"follow_suggestions.curated_suggestion": "Choix du staff",
"follow_suggestions.dismiss": "Ne plus afficher",
"follow_suggestions.hints.featured": "Ce profil a été sélectionné par l'équipe de {domain}.",
"follow_suggestions.hints.friends_of_friends": "Ce profil est populaire parmi les personnes que vous suivez.",
"follow_suggestions.hints.most_followed": "Ce profil est l'un des plus suivis sur {domain}.",
"follow_suggestions.hints.most_interactions": "Ce profil a récemment fait l'objet d'une grande attention sur {domain}.",
"follow_suggestions.hints.similar_to_recently_followed": "Ce profil est similaire aux profils que vous avez suivis le plus récemment.",
"follow_suggestions.personalized_suggestion": "Suggestion personnalisée",
"follow_suggestions.popular_suggestion": "Suggestion populaire",
"follow_suggestions.view_all": "Tout afficher",
"follow_suggestions.who_to_follow": "Qui suivre",
"followed_tags": "Hashtags suivis",
"footer.about": "À propos",
"footer.directory": "Annuaire des profils",

View file

@ -277,6 +277,17 @@
"follow_request.authorize": "Accepter",
"follow_request.reject": "Rejeter",
"follow_requests.unlocked_explanation": "Même si votre compte nest pas privé, léquipe de {domain} a pensé que vous pourriez vouloir consulter manuellement les demandes de suivi de ces comptes.",
"follow_suggestions.curated_suggestion": "Choix du staff",
"follow_suggestions.dismiss": "Ne plus afficher",
"follow_suggestions.hints.featured": "Ce profil a été sélectionné par l'équipe de {domain}.",
"follow_suggestions.hints.friends_of_friends": "Ce profil est populaire parmi les personnes que vous suivez.",
"follow_suggestions.hints.most_followed": "Ce profil est l'un des plus suivis sur {domain}.",
"follow_suggestions.hints.most_interactions": "Ce profil a récemment fait l'objet d'une grande attention sur {domain}.",
"follow_suggestions.hints.similar_to_recently_followed": "Ce profil est similaire aux profils que vous avez suivis le plus récemment.",
"follow_suggestions.personalized_suggestion": "Suggestion personnalisée",
"follow_suggestions.popular_suggestion": "Suggestion populaire",
"follow_suggestions.view_all": "Tout afficher",
"follow_suggestions.who_to_follow": "Qui suivre",
"followed_tags": "Hashtags suivis",
"footer.about": "À propos",
"footer.directory": "Annuaire des profils",

View file

@ -277,7 +277,13 @@
"follow_request.authorize": "Autorisar",
"follow_request.reject": "Rejecter",
"follow_requests.unlocked_explanation": "Benque tu conto ne es cludet, li administratores de {domain} pensat que tu fórsan vell voler tractar seque-petitiones de tis-ci contos manualmen.",
"follow_suggestions.curated_suggestion": "Selection del employates",
"follow_suggestions.dismiss": "Ne monstrar plu",
"follow_suggestions.hints.featured": "Ti-ci profil ha esset selectet directmen del equip de {domain}.",
"follow_suggestions.hints.friends_of_friends": "Ti-ci profil es populari ínter tis qui tu seque.",
"follow_suggestions.hints.most_followed": "Ti-ci profil es un del max sequet sur {domain}.",
"follow_suggestions.hints.most_interactions": "Ti-ci profil ha recivet mult atention recentmen sur {domain}.",
"follow_suggestions.hints.similar_to_recently_followed": "Ti-ci profil es simil al profiles queles tu ha recentmen sequet.",
"follow_suggestions.personalized_suggestion": "Personalisat suggestion",
"follow_suggestions.popular_suggestion": "Populari suggestion",
"follow_suggestions.view_all": "Vider omnicos",

View file

@ -22,6 +22,7 @@
"account.followers": "Imeḍfaren",
"account.followers.empty": "Ar tura, ulac yiwen i yeṭṭafaṛen amseqdac-agi.",
"account.followers_counter": "{count, plural, one {{count} n umeḍfar} other {{count} n imeḍfaren}}",
"account.following": "Yeṭṭafaṛ",
"account.following_counter": "{count, plural, one {{counter} yettwaḍfaren} other {{counter} yettwaḍfaren}}",
"account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.",
"account.go_to_profile": "Ddu ɣer umaɣnu",
@ -39,6 +40,7 @@
"account.posts_with_replies": "Tisuffaɣ d tririyin",
"account.report": "Cetki ɣef @{name}",
"account.requested": "Di laɛḍil ad yettwaqbel. Ssit i wakken ad yefsex usuter n uḍfar",
"account.requested_follow": "{name} yessuter ad k-yeḍfer",
"account.share": "Bḍu amaɣnu n @{name}",
"account.show_reblogs": "Ssken-d inebḍa n @{name}",
"account.statuses_counter": "{count, plural, one {{counter} n tsuffeɣt} other {{counter} n tsuffaɣ}}",
@ -57,7 +59,7 @@
"alert.unexpected.title": "Ayhuh!",
"announcement.announcement": "Ulɣu",
"audio.hide": "Ffer amesli",
"boost_modal.combo": "Tzemreḍ ad tetekkiḍ ɣef {combo} akken ad tessurfeḍ aya tikelt-nniḍen",
"boost_modal.combo": "Tzemreḍ ad tsiteḍ ɣef {combo} akken ad tzegleḍ aya tikelt i d-iteddun",
"bundle_column_error.copy_stacktrace": "Nɣel tuccḍa n uneqqis",
"bundle_column_error.error.title": "Uh, ala !",
"bundle_column_error.network.title": "Tuccḍa deg uẓeṭṭa",
@ -73,7 +75,8 @@
"column.blocks": "Imiḍanen yettusḥebsen",
"column.bookmarks": "Ticraḍ",
"column.community": "Tasuddemt tadigant",
"column.directory": "Inig deg imaɣnuten",
"column.direct": "Tabdarin tusligin",
"column.directory": "Inig deg imeɣna",
"column.domain_blocks": "Taɣulin yeffren",
"column.favourites": "Imenyafen",
"column.follow_requests": "Isuturen n teḍfeṛt",
@ -105,6 +108,7 @@
"compose_form.lock_disclaimer.lock": "yettwacekkel",
"compose_form.placeholder": "D acu i itezzin deg wallaɣ?",
"compose_form.poll.duration": "Tanzagt n tefrant",
"compose_form.poll.multiple": "Aṭas n ufran",
"compose_form.poll.option_placeholder": "Taxtiṛt {number}",
"compose_form.poll.single": "Fren yiwen",
"compose_form.publish": "Suffeɣ",
@ -187,17 +191,20 @@
"explore.trending_links": "Isallen",
"explore.trending_statuses": "Tisuffaɣ",
"explore.trending_tags": "Ihacṭagen",
"filter_modal.added.review_and_configure_title": "Iɣewwaṛen n imzizdig",
"filter_modal.added.settings_link": "asebter n yiɣewwaṛen",
"filter_modal.select_filter.prompt_new": "Taggayt tamaynutt : {name}",
"filter_modal.select_filter.search": "Nadi neɣ snulfu-d",
"firehose.all": "Akk",
"firehose.local": "Deg uqeddac-ayi",
"firehose.remote": "Iqeddacen nniḍen",
"follow_request.authorize": "Ssireg",
"follow_request.reject": "Agi",
"follow_suggestions.dismiss": "Ur ttɛawad ara ad t-id-sekneṭ",
"follow_suggestions.who_to_follow": "Menhu ara ḍefṛeḍ",
"followed_tags": "Ihacṭagen yettwaḍfaren",
"footer.about": "Ɣef",
"footer.directory": "Akaram n imaɣnuten",
"footer.directory": "Akaram n imeɣna",
"footer.get_app": "Awi-d asnas",
"footer.invite": "Ɛreḍ-d kra n yimdanen",
"footer.keyboard_shortcuts": "Inegzumen n unasiw",
@ -221,9 +228,12 @@
"home.column_settings.show_reblogs": "Ssken-d beṭṭu",
"home.column_settings.show_replies": "Ssken-d tiririyin",
"home.hide_announcements": "Ffer ulɣuyen",
"home.pending_critical_update.body": "Ma ulac aɣilif, leqqem aqeddac-ik Mastodon akken kan tzemreḍ !",
"home.show_announcements": "Ssken-d ulɣuyen",
"interaction_modal.no_account_yet": "Ulac-ik·ikem deg Maṣṭudun?",
"interaction_modal.on_another_server": "Deg uqeddac nniḍen",
"interaction_modal.on_this_server": "Deg uqeddac-ayi",
"interaction_modal.sign_in": "Ur tekcimeḍ ara ɣer uqeddac-a. Anda yella umiḍan-ik·im ?",
"interaction_modal.title.follow": "Ḍfer {name}",
"intervals.full.days": "{number, plural, one {# n wass} other {# n wussan}}",
"intervals.full.hours": "{number, plural, one {# n usarag} other {# n yesragen}}",
@ -351,12 +361,15 @@
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon",
"onboarding.profile.display_name": "Isem ara d-yettwaskanen",
"onboarding.share.message": "Nekk d {username} deg #Mastodon! Ḍfer iyi-d sya {url}",
"onboarding.share.title": "Bḍu amaɣnu-inek·inem",
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
"onboarding.start.skip": "Want to skip right ahead?",
"onboarding.start.title": "Tseggmeḍ-tt !",
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
"onboarding.steps.publish_status.body": "Say hello to the world.",
"onboarding.steps.publish_status.title": "Aru tasuffeɣt-inek·inem tamezwarutt",
"onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.",
"onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
@ -371,7 +384,10 @@
"poll_button.add_poll": "Rnu asenqed",
"poll_button.remove_poll": "Kkes asenqed",
"privacy.change": "Seggem tabaḍnit n yizen",
"privacy.direct.long": "Wid akk i d-yettwabdaren deg tuffeɣt",
"privacy.private.long": "Ala wid i k-yeṭṭafaṛen",
"privacy.private.short": "Imeḍfaren",
"privacy.public.long": "Kra n win yellan deg Masṭudun neɣ berra-s",
"privacy.public.short": "Azayez",
"privacy_policy.title": "Tasertit tabaḍnit",
"refresh": "Smiren",
@ -404,9 +420,14 @@
"report_notification.categories.other": "Ayen nniḍen",
"report_notification.categories.spam": "Aspam",
"report_notification.open": "Ldi aneqqis",
"search.no_recent_searches": "Ulac inadiyen ineggura",
"search.placeholder": "Nadi",
"search.search_or_paste": "Nadi neɣ senṭeḍ URL",
"search_popout.language_code": "Tangalt ISO n tutlayt",
"search_popout.options": "Iwellihen n unadi",
"search_popout.recent": "Inadiyen ineggura",
"search_popout.user": "amseqdac",
"search_results.accounts": "Imeɣna",
"search_results.all": "Akk",
"search_results.hashtags": "Ihacṭagen",
"search_results.see_all": "Wali-ten akk",
@ -471,7 +492,7 @@
"timeline_hint.resources.followers": "Imeḍfaṛen",
"timeline_hint.resources.follows": "T·Yeṭafaṛ",
"timeline_hint.resources.statuses": "Tisuffaɣ tiqdimin",
"trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}",
"trends.counter_by_accounts": "{count, plural, one {{counter} n wemdan} other {{counter} n medden}} deg {days, plural, one {ass} other {{days} n wussan}} iɛeddan",
"trends.trending_now": "Ayen mucaɛen tura",
"ui.beforeunload": "Arewway-ik·im ad iruḥ ma yella tefeɣ-d deg Maṣṭudun.",
"units.short.billion": "{count}B",

View file

@ -212,7 +212,7 @@
"emoji_button.custom": "사용자 지정",
"emoji_button.flags": "깃발",
"emoji_button.food": "음식과 마실것",
"emoji_button.label": "에모지 추가",
"emoji_button.label": "에모지 추가",
"emoji_button.nature": "자연",
"emoji_button.not_found": "해당하는 에모지가 없습니다",
"emoji_button.objects": "물건",
@ -536,7 +536,7 @@
"privacy.public.long": "마스토돈 내외 모두",
"privacy.public.short": "공개",
"privacy.unlisted.additional": "공개와 똑같지만 게시물이 실시간 피드나 해시태그, 둘러보기, (계정 설정에서 허용했더라도) 마스토돈 검색에서 제외됩니다.",
"privacy.unlisted.long": "더 적은 알고리즘 팡파",
"privacy.unlisted.long": "더 적은 알고리즘 팡파",
"privacy.unlisted.short": "조용한 공개",
"privacy_policy.last_updated": "{date}에 마지막으로 업데이트됨",
"privacy_policy.title": "개인정보처리방침",

View file

@ -277,7 +277,11 @@
"follow_request.authorize": "Benarkan",
"follow_request.reject": "Tolak",
"follow_requests.unlocked_explanation": "Walaupun akaun anda tidak dikunci, kakitangan {domain} merasakan anda mungkin ingin menyemak permintaan ikutan daripada akaun ini secara manual.",
"follow_suggestions.curated_suggestion": "",
"follow_suggestions.dismiss": "Jangan papar lagi",
"follow_suggestions.hints.featured": "Profil{domain.",
"follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.",
"follow_suggestions.hints.most_followed": ".",
"follow_suggestions.personalized_suggestion": "Cadangan peribadi",
"follow_suggestions.popular_suggestion": "Cadangan terkenal",
"follow_suggestions.view_all": "Lihat semua",

View file

@ -40,7 +40,7 @@
"account.following_counter": "{count, plural, one {Fylgjer {counter}} other {Fylgjer {counter}}}",
"account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.",
"account.go_to_profile": "Gå til profil",
"account.hide_reblogs": "Skjul framhevingar frå @{name}",
"account.hide_reblogs": "Gøym framhevingar frå @{name}",
"account.in_memoriam": "Til minne om.",
"account.joined_short": "Vart med",
"account.languages": "Endre språktingingar",
@ -113,7 +113,7 @@
"column.community": "Lokal tidsline",
"column.direct": "Private omtaler",
"column.directory": "Sjå gjennom profilar",
"column.domain_blocks": "Skjulte domene",
"column.domain_blocks": "Blokkerte domene",
"column.favourites": "Favorittar",
"column.firehose": "Tidslinjer",
"column.follow_requests": "Fylgjeførespurnadar",
@ -124,7 +124,7 @@
"column.pins": "Festa tut",
"column.public": "Samla tidsline",
"column_back_button.label": "Attende",
"column_header.hide_settings": "Gøym innstillingar",
"column_header.hide_settings": "Gøym innstillingane",
"column_header.moveLeft_settings": "Flytt kolonne til venstre",
"column_header.moveRight_settings": "Flytt kolonne til høgre",
"column_header.pin": "Fest",
@ -171,14 +171,14 @@
"confirmations.delete_list.message": "Er du sikker på at du vil sletta denne lista for alltid?",
"confirmations.discard_edit_media.confirm": "Forkast",
"confirmations.discard_edit_media.message": "Du har ulagra endringar i mediaskildringa eller førehandsvisinga. Vil du forkasta dei likevel?",
"confirmations.domain_block.confirm": "Skjul alt frå domenet",
"confirmations.domain_block.confirm": "Blokker heile domenet",
"confirmations.domain_block.message": "Er du heilt, heilt sikker på at du vil skjula heile {domain}? I dei fleste tilfelle er det godt nok og føretrekt med nokre få målretta blokkeringar eller målbindingar. Du kjem ikkje til å sjå innhald frå domenet i fødererte tidsliner eller i varsla dine. Fylgjarane dine frå domenet vert fjerna.",
"confirmations.edit.confirm": "Rediger",
"confirmations.edit.message": "Å redigera no vil overskriva den meldinga du er i ferd med å skriva. Er du sikker på at du vil halda fram?",
"confirmations.logout.confirm": "Logg ut",
"confirmations.logout.message": "Er du sikker på at du vil logga ut?",
"confirmations.mute.confirm": "Målbind",
"confirmations.mute.explanation": "Dette vil skjula innlegg som kjem frå og som nemner dei, men vil framleis la dei sjå innlegga dine og fylgje deg.",
"confirmations.mute.explanation": "Dette vil gøyma innlegga deira og innlegg som nemner dei, men dei vil framleis kunna sjå innlegga dine og fylgja deg.",
"confirmations.mute.message": "Er du sikker på at du vil målbinda {name}?",
"confirmations.redraft.confirm": "Slett & skriv på nytt",
"confirmations.redraft.message": "Er du sikker på at du vil sletta denne statusen og skriva han på nytt? Då misser du favorittar og framhevingar, og svar til det opprinnelege innlegget vert foreldrelause.",
@ -230,7 +230,7 @@
"empty_column.bookmarked_statuses": "Du har ikkje lagra noko bokmerke enno. Når du set bokmerke på eit innlegg, dukkar det opp her.",
"empty_column.community": "Den lokale tidslina er tom. Skriv noko offentleg å få ballen til å rulle!",
"empty_column.direct": "Du har ingen private omtaler enda. Etter du har sendt eller mottatt en, så vil den dukke opp her.",
"empty_column.domain_blocks": "Det er ingen skjulte domene til no.",
"empty_column.domain_blocks": "Det er ingen blokkerte domene enno.",
"empty_column.explore_statuses": "Ingenting er i støytet nett no. Prøv igjen seinare!",
"empty_column.favourited_statuses": "Du har ingen favoritt-statusar ennå. Når du merkjer ein som favoritt, dukkar han opp her.",
"empty_column.favourites": "Ingen har merkt denne statusen som favoritt enno. Når nokon gjer det, dukkar dei opp her.",
@ -277,7 +277,13 @@
"follow_request.authorize": "Autoriser",
"follow_request.reject": "Avvis",
"follow_requests.unlocked_explanation": "Sjølv om kontoen din ikkje er låst tenkte dei som driv {domain} at du kanskje ville gå gjennom førespurnadar frå desse kontoane manuelt.",
"follow_suggestions.curated_suggestion": "Utvalt av staben",
"follow_suggestions.dismiss": "Ikkje vis igjen",
"follow_suggestions.hints.featured": "Denne profilen er handplukka av folka på {domain}.",
"follow_suggestions.hints.friends_of_friends": "Denne profilen er populær hjå dei du fylgjer.",
"follow_suggestions.hints.most_followed": "Mange på {domain} fylgjer denne profilen.",
"follow_suggestions.hints.most_interactions": "Denne profilen har nyss fått mykje merksemd på {domain}.",
"follow_suggestions.hints.similar_to_recently_followed": "Denne profilen liknar på dei andre profilane du har fylgt i det siste.",
"follow_suggestions.personalized_suggestion": "Personleg forslag",
"follow_suggestions.popular_suggestion": "Populært forslag",
"follow_suggestions.view_all": "Vis alle",
@ -395,7 +401,7 @@
"media_gallery.toggle_visible": "{number, plural, one {Skjul bilete} other {Skjul bilete}}",
"moved_to_account_banner.text": "Kontoen din, {disabledAccount} er for tida deaktivert fordi du har flytta til {movedToAccount}.",
"mute_modal.duration": "Varigheit",
"mute_modal.hide_notifications": "Skjul varsel frå denne brukaren?",
"mute_modal.hide_notifications": "Gøym varsel frå denne brukaren?",
"mute_modal.indefinite": "På ubestemt tid",
"navigation_bar.about": "Om",
"navigation_bar.advanced_interface": "Opne i avansert nettgrensesnitt",
@ -479,7 +485,8 @@
"onboarding.follows.empty": "Me kan ikkje visa deg nokon resultat no. Du kan prøva å søkja eller bla gjennom utforsk-sida for å finna folk å fylgja, eller du kan prøva att seinare.",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon",
"onboarding.profile.discoverable": "Gjør min profil synlig",
"onboarding.profile.discoverable": "Gjer profilen min synleg",
"onboarding.profile.discoverable_hint": "Når du vel å gjera profilen din synleg på Mastodon, vil innlegga dine syna i søkjeresultat og populære innlegg, og profilen din kan bli føreslegen for folk med liknande interesser som deg.",
"onboarding.profile.display_name": "Synleg namn",
"onboarding.profile.display_name_hint": "Det fulle namnet eller kallenamnet ditt…",
"onboarding.profile.lead": "Du kan alltid fullføra dette seinare i innstillingane, og der er det endå fleire tilpassingsalternativ.",
@ -528,11 +535,12 @@
"privacy.private.short": "Følgjarar",
"privacy.public.long": "Kven som helst på og av Mastodon",
"privacy.public.short": "Offentleg",
"privacy.unlisted.additional": "Dette er akkurat som offentleg, bortsett frå at innlegga ikkje dukkar opp i direktestraumar eller merkelappar, i oppdagingar eller Mastodon-søk, sjølv om du har sagt ja til at kontoen skal vera synleg.",
"privacy.unlisted.long": "Færre algoritmiske fanfarar",
"privacy.unlisted.short": "Stille offentleg",
"privacy_policy.last_updated": "Sist oppdatert {date}",
"privacy_policy.title": "Personvernsreglar",
"recommended": "Anbefalt",
"recommended": "Tilrådd",
"refresh": "Oppdater",
"regeneration_indicator.label": "Lastar…",
"regeneration_indicator.sublabel": "Heimetidslina di vert førebudd!",
@ -605,7 +613,7 @@
"search.quick_action.status_search": "Innlegg som samsvarer med {x}",
"search.search_or_paste": "Søk eller lim inn URL",
"search_popout.full_text_search_disabled_message": "Ikkje tilgjengeleg på {domain}.",
"search_popout.full_text_search_logged_out_message": "Bare tilgjengelig når man er logget inn.",
"search_popout.full_text_search_logged_out_message": "Berre tilgjengeleg når du er logga inn.",
"search_popout.language_code": "ISO-språkkode",
"search_popout.options": "Søkjealternativ",
"search_popout.quick_actions": "Hurtighandlinger",
@ -654,7 +662,7 @@
"status.load_more": "Last inn meir",
"status.media.open": "Klikk for å opne",
"status.media.show": "Klikk for å vise",
"status.media_hidden": "Medium gøymd",
"status.media_hidden": "Mediet er gøymt",
"status.mention": "Nemn @{name}",
"status.more": "Meir",
"status.mute": "Målbind @{name}",

View file

@ -277,6 +277,7 @@
"follow_request.authorize": "Povoľ prístup",
"follow_request.reject": "Odmietni",
"follow_requests.unlocked_explanation": "Síce Váš učet nie je uzamknutý, ale {domain} tím si myslel že môžete chcieť skontrolovať žiadosti o sledovanie z týchto účtov manuálne.",
"follow_suggestions.curated_suggestion": "Staff pick",
"follow_suggestions.dismiss": "Znovu nezobrazuj",
"follow_suggestions.personalized_suggestion": "Prispôsobené odporúčania",
"follow_suggestions.popular_suggestion": "Populárne návrhy",

View file

@ -2,25 +2,25 @@
"about.blocks": "Siū kuán-tsè ê su-hāu-khì",
"about.contact": "Liân-lo̍k:",
"about.disclaimer": "Ling-khí-tshiūnn sī tsi̍t-ê khai-guân nńg-théi ê siong-phiau sī Mastodon gGmbH.",
"account.badges.bot": "Bot",
"account.cancel_follow_request": "Withdraw follow request",
"account.badges.bot": "Tsū-tōng-ê",
"account.cancel_follow_request": "Mài-koh tui-tsong",
"account.media": "Mûi-thé",
"account.mention": "Thê-khí @{name}",
"account.posts": "Toots",
"account.posts_with_replies": "Toots and replies",
"account.requested": "Awaiting approval",
"account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}",
"account_note.placeholder": "Click to add a note",
"column.pins": "Pinned toot",
"community.column_settings.media_only": "Media only",
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
"compose_form.placeholder": "What is on your mind?",
"compose_form.publish_form": "Publish",
"compose_form.spoiler.marked": "Text is hidden behind warning",
"compose_form.spoiler.unmarked": "Text is not hidden",
"confirmations.delete.message": "Are you sure you want to delete this status?",
"confirmations.domain_block.confirm": "Hide entire domain",
"account.posts": "Huah-siann",
"account.posts_with_replies": "Huah-siann kah huê-ìng",
"account.requested": "Tán-thāi phue-tsún",
"account.statuses_counter": "{count, plural, one {{counter} Huah-siann} other {{counter} Huah-siann}}",
"account_note.placeholder": "Tiám tsi̍t-ē ka-thiam pī-tsù",
"column.pins": "Tah thâu-tsîng ê huah-siann",
"community.column_settings.media_only": "Kan-na muî-thé",
"compose_form.encryption_warning": "Tī Mastodon tah huah-siann m̄-sī tuan-tuì-tuan ka-pì ê. M̄-thang tī Mastodon hun-hióng jīm-hô bín-kám ê tsū-sìn.",
"compose_form.hashtag_warning": "Tsit-ê huah-siann in-uī m̄-sī kong-khai ê, sóo-í buē tī jīm-hô tsú-tê piau-tshiam hián-sī. Kan-na kong-khai ê huah-siann ē-tàng hōo tsú-tê piau-tshiam tshâ-tshuē.",
"compose_form.placeholder": "Lí teh siūnn siánn?",
"compose_form.publish_form": "Huah--tshut-khì",
"compose_form.spoiler.marked": "Î-tû luē-iông kíng-kò",
"compose_form.spoiler.unmarked": "Tsing-ka luē-iông kíng-kò",
"confirmations.delete.message": "Lí kám bueh thâi-tiāu tsi̍t-ē huah-siann?",
"confirmations.domain_block.confirm": "Hong-só tsíng-kò bāng-hi̍k",
"dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
"dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
"embed.instructions": "Embed this status on your website by copying the code below.",
@ -46,7 +46,7 @@
"keyboard_shortcuts.muted": "to open muted users list",
"keyboard_shortcuts.my_profile": "to open your profile",
"keyboard_shortcuts.notifications": "to open notifications column",
"keyboard_shortcuts.open_media": "to open media",
"keyboard_shortcuts.open_media": "Khui muî-thé",
"keyboard_shortcuts.pinned": "to open pinned toots list",
"keyboard_shortcuts.profile": "to open author's profile",
"keyboard_shortcuts.reply": "to reply",

View file

@ -1,5 +1,5 @@
{
"about.blocks": "เซิร์ฟเวอร์ที่มีการควบคุม",
"about.blocks": "เซิร์ฟเวอร์ที่มีการกลั่นกรอง",
"about.contact": "ติดต่อ:",
"about.disclaimer": "Mastodon เป็นซอฟต์แวร์เสรี โอเพนซอร์ส และเครื่องหมายการค้าของ Mastodon gGmbH",
"about.domain_blocks.no_reason_available": "เหตุผลไม่พร้อมใช้งาน",
@ -380,7 +380,7 @@
"lightbox.next": "ถัดไป",
"lightbox.previous": "ก่อนหน้า",
"limited_account_hint.action": "แสดงโปรไฟล์ต่อไป",
"limited_account_hint.title": "มีการซ่อนโปรไฟล์นี้โดยผู้ควบคุมของ {domain}",
"limited_account_hint.title": "มีการซ่อนโปรไฟล์นี้โดยผู้กลั่นกรองของ {domain}",
"link_preview.author": "โดย {name}",
"lists.account.add": "เพิ่มไปยังรายการ",
"lists.account.remove": "เอาออกจากรายการ",
@ -637,9 +637,9 @@
"sign_in_banner.sign_in": "เข้าสู่ระบบ",
"sign_in_banner.sso_redirect": "เข้าสู่ระบบหรือลงทะเบียน",
"sign_in_banner.text": "เข้าสู่ระบบเพื่อติดตามโปรไฟล์หรือแฮชแท็ก ชื่นชอบ แชร์ และตอบกลับโพสต์ คุณยังสามารถโต้ตอบจากบัญชีของคุณในเซิร์ฟเวอร์อื่น",
"status.admin_account": "เปิดส่วนติดต่อการควบคุมสำหรับ @{name}",
"status.admin_domain": "เปิดส่วนติดต่อการควบคุมสำหรับ {domain}",
"status.admin_status": "เปิดโพสต์นี้ในส่วนติดต่อการควบคุม",
"status.admin_account": "เปิดส่วนติดต่อการกลั่นกรองสำหรับ @{name}",
"status.admin_domain": "เปิดส่วนติดต่อการกลั่นกรองสำหรับ {domain}",
"status.admin_status": "เปิดโพสต์นี้ในส่วนติดต่อการกลั่นกรอง",
"status.block": "ปิดกั้น @{name}",
"status.bookmark": "เพิ่มที่คั่นหน้า",
"status.cancel_reblog_private": "เลิกดัน",

View file

@ -214,7 +214,7 @@
"emoji_button.food": "Yiyecek ve İçecek",
"emoji_button.label": "İfade ekle",
"emoji_button.nature": "Doğa",
"emoji_button.not_found": "İfade yok!! (╯°□°)╯︵ ┻━┻",
"emoji_button.not_found": "Eşleşen emoji yok",
"emoji_button.objects": "Nesneler",
"emoji_button.people": "Kullanıcılar",
"emoji_button.recent": "Sık kullanılan",
@ -348,7 +348,7 @@
"keyboard_shortcuts.description": "Açıklama",
"keyboard_shortcuts.direct": "özel değinmeler sütununu açmak için",
"keyboard_shortcuts.down": "Listede aşağıya inmek için",
"keyboard_shortcuts.enter": "gönderiyi aç",
"keyboard_shortcuts.enter": "Gönderiyi açınız",
"keyboard_shortcuts.favourite": "Gönderiyi favorilerine ekle",
"keyboard_shortcuts.favourites": "Gözde listeni aç",
"keyboard_shortcuts.federated": "Federe akışı aç",

View file

@ -40,7 +40,7 @@ function render(
ui: React.ReactElement,
{ locale = 'en', signedIn = true, ...renderOptions } = {},
) {
const Wrapper = (props: { children: React.ReactElement }) => {
const Wrapper = (props: { children: React.ReactNode }) => {
return (
<MemoryRouter>
<IntlProvider locale={locale}>

View file

@ -4,7 +4,7 @@ import { createRoot } from 'react-dom/client';
import ready from '../mastodon/ready';
ready(() => {
[].forEach.call(document.querySelectorAll('[data-admin-component]'), element => {
document.querySelectorAll('[data-admin-component]').forEach(element => {
const componentName = element.getAttribute('data-admin-component');
const componentProps = JSON.parse(element.getAttribute('data-props'));

View file

@ -57,11 +57,11 @@ function loaded() {
return messageFormat.format(values);
};
[].forEach.call(document.querySelectorAll('.emojify'), (content) => {
document.querySelectorAll('.emojify').forEach((content) => {
content.innerHTML = emojify(content.innerHTML);
});
[].forEach.call(document.querySelectorAll('time.formatted'), (content) => {
document.querySelectorAll('time.formatted').forEach((content) => {
const datetime = new Date(content.getAttribute('datetime'));
const formattedDate = dateTimeFormat.format(datetime);
@ -78,7 +78,7 @@ function loaded() {
};
const todayFormat = new IntlMessageFormat(localeData['relative_format.today'] || 'Today at {time}', locale);
[].forEach.call(document.querySelectorAll('time.relative-formatted'), (content) => {
document.querySelectorAll('time.relative-formatted').forEach((content) => {
const datetime = new Date(content.getAttribute('datetime'));
let formattedContent;
@ -95,7 +95,7 @@ function loaded() {
content.textContent = formattedContent;
});
[].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
document.querySelectorAll('time.time-ago').forEach((content) => {
const datetime = new Date(content.getAttribute('datetime'));
const now = new Date();
@ -112,8 +112,8 @@ function loaded() {
if (reactComponents.length > 0) {
import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container')
.then(({ default: MediaContainer }) => {
[].forEach.call(reactComponents, (component) => {
[].forEach.call(component.children, (child) => {
reactComponents.forEach((component) => {
Array.from(component.children).forEach((child) => {
component.removeChild(child);
});
});
@ -169,7 +169,7 @@ function loaded() {
return false;
});
[].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
document.querySelectorAll('.status__content__spoiler-link').forEach((spoilerLink) => {
const statusEl = spoilerLink.parentNode.parentNode;
const message = (statusEl.dataset.spoiler === 'expanded') ? (localeData['status.show_less'] || 'Show less') : (localeData['status.show_more'] || 'Show more');
spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();

View file

@ -324,6 +324,23 @@ $content-width: 840px;
padding-bottom: 0;
margin-bottom: 0;
border-bottom: 0;
.comment {
display: block;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 4px;
&.private-comment {
display: block;
color: $darker-text-color;
}
&.public-comment {
display: block;
color: $secondary-text-color;
}
}
}
& > p {
@ -638,16 +655,6 @@ body,
input.button {
margin: 0 5px 5px 0;
}
.media-spoiler-toggle-buttons {
margin-inline-start: auto;
.button {
overflow: visible;
margin: 0 0 5px 5px;
float: right;
}
}
}
.back-link {
@ -1070,6 +1077,7 @@ a.name-tag,
display: flex;
justify-content: space-between;
margin-bottom: 0;
word-break: break-word;
}
&__permissions {

View file

@ -5195,7 +5195,6 @@ a.status-card {
.icon {
position: absolute;
top: 12px + 2px;
inset-inline-start: 16px - 2px;
display: inline-block;
opacity: 0;
transition: all 100ms linear;
@ -5205,11 +5204,16 @@ a.status-card {
color: $darker-text-color;
cursor: default;
pointer-events: none;
margin-inline-start: 16px - 2px;
&.active {
pointer-events: auto;
opacity: 1;
}
@media screen and (min-width: $no-gap-breakpoint) {
inset-inline-start: 16px - 2px;
}
}
.icon-search {
@ -5965,6 +5969,7 @@ a.status-card {
.report-modal__comment {
box-sizing: border-box;
width: 50%;
min-width: 50%;
@media screen and (width <= 480px) {
width: 100%;
@ -6033,6 +6038,14 @@ a.status-card {
min-height: 100px;
max-height: 50vh;
border: 0;
@media screen and (height <= 600px) {
max-height: 20vh;
}
@media screen and (max-width: $no-columns-breakpoint) {
max-height: 20vh;
}
}
.setting-toggle {

View file

@ -1078,6 +1078,7 @@ code {
&__type {
color: $darker-text-color;
word-break: break-word;
}
}

View file

@ -50,7 +50,7 @@ class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics:
WHERE date_trunc('day', media_attachments.created_at)::date = axis.period
AND #{account_domain_sql(params[:include_subdomains])}
)
SELECT SUM(size) FROM new_media_attachments
SELECT COALESCE(SUM(size), 0) FROM new_media_attachments
) AS value
FROM (
SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period

View file

@ -34,10 +34,10 @@ class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::Base
INNER JOIN accounts ON statuses.account_id = accounts.id
WHERE statuses_tags.tag_id = :tag_id
AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id
AND date_trunc('day', statuses.created_at)::date = axis.day
AND date_trunc('day', statuses.created_at)::date = axis.period
)
FROM (
SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, ('1 day')::interval) AS day
SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
) as axis
SQL
end

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
module RankedTrend
extend ActiveSupport::Concern
included do
scope :by_rank, -> { order(rank: :desc) }
scope :ranked_below, ->(value) { where(rank: ..value) }
end
class_methods do
def recalculate_ordered_rank
connection
.exec_update(<<~SQL.squish)
UPDATE #{table_name}
SET rank = inner_ordered.calculated_rank
FROM (
SELECT id, row_number() OVER w AS calculated_rank
FROM #{table_name}
WINDOW w AS (
PARTITION BY language
ORDER BY score DESC
)
) inner_ordered
WHERE #{table_name}.id = inner_ordered.id
SQL
end
end
end

View file

@ -12,6 +12,8 @@
# language :string
#
class PreviewCardTrend < ApplicationRecord
include RankedTrend
belongs_to :preview_card
scope :allowed, -> { where(allowed: true) }
end

View file

@ -14,6 +14,8 @@
#
class StatusTrend < ApplicationRecord
include RankedTrend
belongs_to :status
belongs_to :account

View file

@ -81,12 +81,12 @@ class Trends::Links < Trends::Base
# Now that all trends have up-to-date scores, and all the ones below the threshold have
# been removed, we can recalculate their positions
PreviewCardTrend.connection.exec_update('UPDATE preview_card_trends SET rank = t0.calculated_rank FROM (SELECT id, row_number() OVER w AS calculated_rank FROM preview_card_trends WINDOW w AS (PARTITION BY language ORDER BY score DESC)) t0 WHERE preview_card_trends.id = t0.id')
PreviewCardTrend.recalculate_ordered_rank
end
def request_review
PreviewCardTrend.pluck('distinct language').flat_map do |language|
score_at_threshold = PreviewCardTrend.where(language: language, allowed: true).order(rank: :desc).where('rank <= ?', options[:review_threshold]).first&.score || 0
score_at_threshold = PreviewCardTrend.where(language: language, allowed: true).by_rank.ranked_below(options[:review_threshold]).first&.score || 0
preview_card_trends = PreviewCardTrend.where(language: language, allowed: false).joins(:preview_card)
preview_card_trends.filter_map do |trend|

View file

@ -74,12 +74,12 @@ class Trends::Statuses < Trends::Base
# Now that all trends have up-to-date scores, and all the ones below the threshold have
# been removed, we can recalculate their positions
StatusTrend.connection.exec_update('UPDATE status_trends SET rank = t0.calculated_rank FROM (SELECT id, row_number() OVER w AS calculated_rank FROM status_trends WINDOW w AS (PARTITION BY language ORDER BY score DESC)) t0 WHERE status_trends.id = t0.id')
StatusTrend.recalculate_ordered_rank
end
def request_review
StatusTrend.pluck('distinct language').flat_map do |language|
score_at_threshold = StatusTrend.where(language: language, allowed: true).order(rank: :desc).where('rank <= ?', options[:review_threshold]).first&.score || 0
score_at_threshold = StatusTrend.where(language: language, allowed: true).by_rank.ranked_below(options[:review_threshold]).first&.score || 0
status_trends = StatusTrend.where(language: language, allowed: false).joins(:status).includes(status: :account)
status_trends.filter_map do |trend|

View file

@ -7,6 +7,10 @@
%small
- if instance.domain_block
= instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
- if instance.domain_block.public_comment.present?
%span.comment.public-comment #{t('admin.domain_blocks.public_comment')}: #{instance.domain_block.public_comment}
- if instance.domain_block.private_comment.present?
%span.comment.private-comment #{t('admin.domain_blocks.private_comment')}: #{instance.domain_block.private_comment}
- elsif instance.domain_allow
= t('admin.accounts.whitelisted')
- else

View file

@ -16,7 +16,7 @@
%small= t('accounts.followers', count: account.followers_count).downcase
%td.accounts-table__count
- if account.last_status_at.present?
%time.time-ago{ datetime: account.last_status_at.to_date.iso8601, title: l(account.last_status_at.to_date) }= l account.last_status_at
%time.time-ago{ datetime: account.last_status_at.to_date.iso8601, title: l(account.last_status_at.to_date) }= l account.last_status_at.to_date
- else
\-
%small= t('accounts.last_active')

View file

@ -53,3 +53,7 @@ el:
position:
elevated: δεν μπορεί να είναι μεγαλύτερο από τον τρέχοντα ρόλο σας
own_role: δεν μπορεί να αλλάξει με τον τρέχοντα ρόλο σας
webhook:
attributes:
events:
invalid_permissions: δεν μπορείτε να συμπεριλάβετε συμβάντα για τα οποία δεν έχετε τα δικαιώματα

View file

@ -795,6 +795,7 @@ be:
disabled: Нікому
users: Лакальным карыстальнікам, якія ўвайшлі
registrations:
moderation_recommandation: Пераканайцеся, што ў вас ёсць адэкватная і аператыўная каманда мадэратараў, перш чым адчыняць рэгістрацыю для ўсіх жадаючых!
preamble: Кантралюйце, хто можа ствараць уліковы запіс на вашым серверы.
title: Рэгістрацыя
registrations_mode:
@ -802,6 +803,7 @@ be:
approved: Для рэгістрацыі патрабуецца пацвярджэнне
none: Нікому не магчыма зарэгістравацца
open: Любому магчыма зарэгістравацца
warning_hint: Мы рэкамендуем выкарыстоўваць рэжым "для рэгістрацыі патрабуецца пацвярджэнне", калі вы не ўпэўненыя, што ваша каманда мадэратараў зможа своечасова спраўляцца са спамам і шкоднымі рэгістрацыямі.
security:
authorized_fetch: Патрабаваць аўтэнтыфікацыю ад федэратыўных сервераў
authorized_fetch_hint: Патрабаванне аўтэнтыфікацыі ад федэратыўных сервераў дазваляе больш строга выконваць блакіроўкі як на ўзроўні карыстача, так і на ўзроўні сервера. Аднак пры гэтым зніжаецца прадукцыйнасць, памяншаецца ахоп вашых адказаў на допісы і могуць узнікнуць праблемы сумяшчальнасці з некаторымі федэратыўнымі сэрвісамі. Акрамя таго, гэта не перашкодзіць атрымліваць вашыя публічныя допісы і ўліковыя запісы.
@ -1002,6 +1004,9 @@ be:
title: Вэбхукі
webhook: Вэбхук
admin_mailer:
auto_close_registrations:
body: У сувязі з адсутнасцю актыўнасці мадэратараў у апошні час, рэгістрацыя на %{instance} была аўтаматычна пераведзена ў рэжым, які патрабуе ручной праверкі, каб прадухіліць выкарыстанне %{instance} у якасці платформы для патэнцыйных зламыснікаў. Вы можаце ў любы момант пераключыць яго назад ў рэжым "вольная рэгістрацыя".
subject: Рэгістрацыі для %{instance} былі аўтаматычна пераведзены ў рэжым "патрабуецца пацвярджэнне"
new_appeal:
actions:
delete_statuses: выдаліць іх допісы

View file

@ -969,8 +969,8 @@ ca:
webhook: Webhook
admin_mailer:
auto_close_registrations:
body: A causa de la manca d'activitat recent dels moderadors, les altes a %{instance} han passat automàticament a necessitar una revisió manual, per tal d'evitar que %{instance} es faci servir com a plataforma de potencials mals actuants. Podeu revertir-ho a altes obertes en qualsevol moment.
subject: Les altes a %{instance} han passat automàticament a necessitar aprovació
body: A causa de la manca d'activitat recent dels moderadors, s'ha passat el procés d'alta de %{instance} al mode de revisió manual, a fi d'evitar que malfactors l'utilitzin com a plataforma. Podeu obrir el procés de registre en qualsevol moment.
subject: S'ha passat el procés d'alta de %{instance} al mode de validació manual
new_appeal:
actions:
delete_statuses: eliminar els seus tuts

View file

@ -500,7 +500,7 @@ da:
instance_accounts_dimension: Mest fulgte konti
instance_accounts_measure: gemte konti
instance_followers_measure: vores følgere dér
instance_follows_measure: deres følgere r
instance_follows_measure: deres følgere her
instance_languages_dimension: Topsprog
instance_media_attachments_measure: gemte medievedhæftninger
instance_reports_measure: anmeldelser af dem
@ -1445,7 +1445,7 @@ da:
before: 'Inder der fortsættes, læs venligst disse notater omhyggeligt:'
cooldown: Efter flytningen er der en venteperiode, hvor kontoen ikke kan flyttes igen
disabled_account: Efterfølgende er din nuværende konto ikke fuldt funktionsdygtig, der er dog adgang til dataeksport samt genaktivering.
followers: Denne handling vil flytte alle følgere fra den aktuelle konto til den nye ditto
followers: Denne handling vil flytte alle følgere fra den aktuelle konto til den nye konto
only_redirect_html: Alternativt kan du <a href="%{path}">oprette en omdirigering for din profil alene</a>.
other_data: Ingen øvrige data flyttes automatisk
redirect: Din nuværende kontoprofil opdateres med en omdirigeringsnotits og ekskluderes fra søgninger

View file

@ -2,16 +2,17 @@
fa:
devise:
confirmations:
confirmed: نشانی ایمیل شما با موفقیت تأیید شد.
send_instructions: تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید نشانی ایمیل خود را تأیید کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
send_paranoid_instructions: اگر ایمیل شما در پایگاه دادهٔ ما موجود باشد، تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید نشانی ایمیل خود را تأیید کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
confirmed: نشانی رایانامه‌تان با موفقیت تأیید شد.
send_instructions: تا دقایقی دیگر رایانامه‌ای با دستورالعمل تأیید نشانی رایانامه‌تان دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
send_paranoid_instructions: اگر نشانی رایانامه‌تان در پایگاه داده‌مان وجود داشته باشد، تا دقایقی دیگر تا دقایقی دیگر رایانامه‌ای با دستورالعمل تأیید نشانی رایانامه‌تان دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
failure:
already_authenticated: همین الآن هم وارد شده‌اید.
inactive: حساب شما هنوز فعال نشده است.
already_authenticated: از پیش وارد شده‌اید.
inactive: هنوز حسابتان فعّال نشده.
invalid: "%{authentication_keys} یا گذرواژه نامعتبر."
last_attempt: پیش از آن که حساب شما قفل شود، یک فرصت دیگر دارید.
locked: حساب شما قفل شده است.
locked: حسابتان قفل شده.
not_found_in_database: "%{authentication_keys} یا گذرواژه نامعتبر."
omniauth_user_creation_failure: خطای ایجاد حسابی برای این هویت.
pending: حساب شما همچنان در دست بررسی است.
timeout: مهلت این ورود شما به سر رسید. برای ادامه، دوباره وارد شوید.
unauthenticated: برای ادامه باید وارد شوید یا ثبت نام کنید.
@ -24,37 +25,42 @@ fa:
explanation_when_pending: شما با این نشانی ایمیل برای %{host} درخواست دعوت‌نامه داده‌اید. اگر ایمیل خود را تأیید کنید، ما درخواست شما را بررسی خواهیم کرد. تا وقتی بررسی تمام نشده، شما نمی‌توانید به حساب خود وارد شوید. اگر درخواست شما رد شود، ما اطلاعاتی را که از شما داریم پاک خواهیم کرد پس نیازی به کاری از سمت شما نخواهد بود. اگر شما چنین درخواستی نداده‌اید، لطفاً این ایمیل را نادیده بگیرید.
extra_html: لطفاً همچنین <a href="%{terms_path}">قوانین کارساز</a> و <a href="%{policy_path}">شرایط خدمتمان</a> را بررسی کنید.
subject: 'ماستودون: دستورالعمل تأیید برای %{instance}'
title: تأیید نشانی ایمیل
title: تأیید نشانی رایانامه
email_changed:
explanation: 'نشانی ایمیل حساب شما تغییر می‌کند به:'
extra: اگر شما ایمیل خود را عوض نکردید، شاید کسی به حساب شما دسترسی پیدا کرده است. در این صورت لطفاً هر چه زودتر گذرواژه حسابتان را عوض کنید. اگر گذرواژه‌تان دیگر کار نمی‌کند، لطفاً با مدیر سرور تماس بگیرید.
subject: 'ماستودون: نشانی ایمیل عوض شد'
title: نشانی ایمیل تازه
explanation: 'نشانی رایانامهٔ حسابتان تغییر می‌کند به:'
extra: اگر رایانامه‌تان را عوض نکرده‌اید، ممکن است کسی به حسابتان دسترسی پیدا کرده باشد. لطفاً فوراُ گذرواژه‌تان را عوض کرده و اگر از حسابتان بیرون مانده‌اید با مدیر کارساز تماس بگیرید.
subject: 'ماستودون: رایانامه عوض شد'
title: نشانی جدید رایانامه
password_change:
explanation: گذرواژه حساب شما تغییر کرد.
extra: اگر شما گذرواژه حسابتان را تغییر ندادید، شاید کسی به حساب شما دسترسی پیدا کرده است. در این صورت لطفاً هر چه زودتر گذرواژه حسابتان را عوض کنید. اگر گذرواژه‌تان دیگر کار نمی‌کند، لطفاً با مدیر سرور تماس بگیرید.
subject: 'ماستودون: گذرواژه‌تان عوض شد'
title: گذرواژه‌تان عوض شد
explanation: گذرواژهٔ حسابتان عوض شده.
extra: اگر گذرواژه‌تان را عوض نکرده‌اید، ممکن است کسی به حسابتان دسترسی پیدا کرده باشد. لطفاً فوراُ گذرواژه‌تان را عوض کرده و اگر از حسابتان بیرون مانده‌اید با مدیر کارساز تماس بگیرید.
subject: 'ماستودون: گذرواژه عوض شد'
title: گذرواژه عوض شد
reconfirmation_instructions:
explanation: نشانی تازه را تأیید کنید تا ایمیل‌تان عوض شود.
extra: اگر شما باعث این تغییر نبودید، لطفاً این ایمیل را نادیده بگیرید. تا زمانی که شما پیوند بالا را باز نکنید، نشانی ایمیل مربوط به حساب شما عوض نخواهد شد.
explanation: برای تغییر رایانامه‌تان نشانی جدید را تأیید کنید.
extra: اگر خودتان چنین درخواستی نداده‌اید لطفاً از این رایانامه چشم بپوشید. نشانی رایانامهٔ حساب ماستودون تا وقتی به پیوند بالا دسترسی پیدا نکنید عوض نخواهد شد.
subject: 'ماستودون: تأیید رایانامه برای %{instance}'
title: تأیید نشانی ایمیل
title: تأیید نشانی رایانامه
reset_password_instructions:
action: تغییر گذرواژه
explanation: شما گذرواژه تازه‌ای برای حسابتان درخواست کردید.
extra: اگر شما چنین درخواستی نکردید، لطفاً این ایمیل را نادیده بگیرید. تا زمانی که شما پیوند بالا را باز نکنید و گذرواژه تازه‌ای نسازید، گذرواژه شما عوض نخواهد شد.
subject: 'ماستودون: راهنمایی برای بازنشانی گذرواژه'
explanation: درخواست گذرواژه‌ای تازه‌ای برای حسابتان کرده‌اید.
extra: اگر خودتان چنین درخواستی نداده‌اید لطفاً از این رایانامه چشم بپوشید. گذرواژه‌تان تا وقتی به پیوند بالا دسترسی پیدا نکرده و گذرواژهٔ جدیدی نسازید عوض نخواهد شد.
subject: 'ماستودون: دستورالعمل‌های بازنشانی گذرواژه'
title: بازنشانی گذرواژه
two_factor_disabled:
subject: 'ماستودون: تأیید هویت دو مرحله‌ای از کار افتاد'
title: ورود دومرحله‌ای غیرفعال
explanation: ورود اکنون تنها با نشانی رایانامه و گذرواژه ممکن است.
subject: 'ماستودون: هویت‌سنجی دو مرحله‌ای از کار افتاده'
subtitle: هویت‌سنجی دو مرحله‌ای برای حسابتان از کار افتاده.
title: ورود دومرحله‌ای از کار افتاده
two_factor_enabled:
subject: 'ماستودون: تأیید هویت دومرحله‌ای به کار افتاد'
title: ورود دومرحله‌ای فعال
explanation: ورود نیازمند ژتونی تولید شده به دست کارهٔ TOTP جفت‌شده است.
subject: 'ماستودون: هویت‌سنجی دومرحله‌ای به کار افتاده'
subtitle: هویت‌سنجی دو عاملی برای حسابتان به کار افتاده.
title: ورود دومرحله‌ای به کار افتاده
two_factor_recovery_codes_changed:
explanation: کدهای بازیابی پیشین نامعتبر شده و کدهای جدیدی ساخته شدند.
subject: 'ماستودون: کدهای بازیابی برای تأیید هویت دو مرحله‌ای دوباره ساخته شدند'
subtitle: کدهای بازیابی پیشین از اعتبار ساقط شده و کدهایی جدید ایجاد شدند.
title: کدهای بازیابی تأیید هویت دو مرحله‌ای عوض شده‌اند
unlock_instructions:
subject: 'ماستودون: دستورالعمل‌های قفل‌گشایی'
@ -68,9 +74,13 @@ fa:
subject: 'ماستودون: کلید امنیتی حذف شد'
title: یکی از کلیدهای امنیتیتان حذف شد
webauthn_disabled:
explanation: هویت‌سنجی با کلیدهای امنیتی برای حسابتان از کار افتاده.
extra: ورود اکنون تنها با ژتون تولید شده به دست کارهٔ TOTP جفت‌شده ممکن است.
subject: 'ماستودون: تأیید هویت با کلیدهای امنیتی از کار افتاد'
title: کلیدهای امنیتی از کار افتادند
webauthn_enabled:
explanation: هویت‌سنجی با کلیدهای امنیتی برای حسابتان به کار افتاده.
extra: کلید امنیتیتان اکنون می‌تواند برای ورود استفاده شود.
subject: 'ماستودون: تأیید هویت با کلید امنیتی به کار افتاد'
title: کلیدهای امنیتی به کار افتادند
omniauth_callbacks:
@ -86,22 +96,22 @@ fa:
destroyed: بدرود! حساب شما با موفقیت لغو شد. امیدواریم دوباره شما را ببینیم.
signed_up: خوش آمدید! شما با موفقیت ثبت نام کردید.
signed_up_but_inactive: خوش آمدید! با موفقیت ثبت نام کردید. ولی هنوز وارد نشده‌اید؛ چرا که حسابتان هنوز فعال نشده است.
signed_up_but_locked: خوش آمدید! با موفقیت ثبت نام کردید. ولی هنوز وارد نشده‌اید؛ چرا که حسابتان قفل است.
signed_up_but_pending: پیغامی که دارای یک پیوند برای تأیید است به نشانی ایمیل شما فرستاده شده. پس از این‌که پیوند را باز کردید، ما درخواست شما را بررسی خواهیم کرد. اگر درخواست شما پذیرفته شود، به شما خواهیم گفت.
signed_up_but_unconfirmed: پیامی با یک پیوند تأیید به نشانی ایمیل شما فرستاده شده. لطفاً پیوند موجود در ایمیل را دنبال کنید تا حسابتان فعال شود. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
update_needs_confirmation: شما با موفقیت حسابتان را به‌روز کردید، ولی لازم است که ما نشانی ایمیل تازهٔ شما را تأیید کنیم. لطفاً ایمیل خود را ببینید و پیوند موجود در ایمیل را دنبال کنید تا تا نشانی ایمیل تازهٔ شما تأیید شود. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
updated: حسابتان با موفقبت به‌روز شد.
signed_up_but_locked: با موفّقیت ثبت‌نام کرده‌اید. با این حال نمی‌توان واردتان کرد؛ چرا که حسابتان قفل است.
signed_up_but_pending: پیامی با پیوند تأیید به نشانی رایانامه‌تان فرستاده شده. پس از زدن پیوند درخواستتان را بازبینی خواهیم کرد. در صورت پذیرش آگاه خواهید شد.
signed_up_but_unconfirmed: پیامی با پیوند تأیید به نشانی رایانامه‌تان فرستاده شده. لطفاً برای فعّال کردن حسابتان پیوند را بزنید. اگر این رایانامه را نگرفته‌اید شاخهٔ هرزنامه‌ها را بررسی کنید.
update_needs_confirmation: حسابتان را با موفّقیت به‌روز کردید؛‌ ولی باید نشانی رایانامهٔ جدیتان را تأیید کنیم. لطفاً رایانامه‌تان را بررسی کرده و برای تأیید نشانی رایانهٔ جدیدتان پیوند را بزنید. اگر این رایانامه را نگرفته‌اید شاخهٔ هرزنامه‌ها را بررسی کنید.
updated: حسابتان با موفّقیت به‌روز شد.
sessions:
already_signed_out: با موفقیت خارج شدید.
signed_in: با موفقیت وارد شدید.
signed_out: با موفقیت خارج شدید.
already_signed_out: با موفّقیت خارج شدید.
signed_in: با موفّقیت وارد شدید.
signed_out: با موفّقیت خارج شدید.
unlocks:
send_instructions: تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید قفل حساب خود را باز کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
send_paranoid_instructions: اگر حساب شما موجود باشد، تا دقایقی دیگر ایمیلی خواهید گرفت که به شما می‌گوید چگونه باید قفل آن را باز کنید. اگر این ایمیل نیامد، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
unlocked: قفل حساب شما با موفقیت باز شد. لطفاً برای ادامه وارد سیستم شوید.
send_instructions: تا دقایقی دیگر رایانامه‌ای با دستورالعمل قفل‌گشایی حسابتان دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
send_paranoid_instructions: اگر حسابتان وجود داشته باشد تا دقایقی دیگر رایانامه‌ای با دستورالعمل قفل‌گشاییش دریافت خواهید کرد. اگر این رایانامه را نگرفتید، لطفاً پوشهٔ هرزنامه‌هایتان را بررسی کنید.
unlocked: حسابتان با موفّقیت قفل‌گشایی شد. لطفاً برای ادامه وارد شوید.
errors:
messages:
already_confirmed: تأیید شده، لطفاً وارد شوید
already_confirmed: از پیش تأیید شده. لطفاً ورود را بیازمایید
confirmation_period_expired: باید ظرف %{period} تأیید شود، لطفاً دوباره درخواست دهید
expired: مهلتش به سر رسید، لطفاً دوباره درخواست دهید
not_found: پیدا نشد

View file

@ -12,6 +12,7 @@ nn:
last_attempt: Du har eitt forsøk igjen før kontoen din vert låst.
locked: Kontoen din er låst.
not_found_in_database: Ugyldig %{authentication_keys} eller passord.
omniauth_user_creation_failure: Greidde ikkje laga konto for denne identiteten.
pending: Kontoen din er vert gjennomgått enno.
timeout: Økta di er utgått. Logg inn omatt for å halde fram.
unauthenticated: Du må logge inn eller registere deg før du kan halde fram.
@ -47,19 +48,19 @@ nn:
subject: 'Mastodon: Instuksjonar for å endra passord'
title: Attstilling av passord
two_factor_disabled:
explanation: Innlogging er nå mulig med kun e-postadresse og passord.
explanation: No kan du logga inn med berre epostadresse og passord.
subject: 'Mastodon: To-faktor-autentisering deaktivert'
subtitle: To-faktor autentisering for din konto har blitt deaktivert.
subtitle: Tofaktorinnlogging for denne kontoen er skrudd av.
title: 2FA deaktivert
two_factor_enabled:
explanation: En token generert av den sammenkoblede TOTP-appen vil være påkrevd for innlogging.
explanation: Du treng ein kode frå den tilkopla tofaktor-appen din for å logga inn.
subject: 'Mastodon: To-faktor-autentisering aktivert'
subtitle: Tofaktorautentisering er aktivert for din konto.
subtitle: Tofaktorpålogging er skrudd på for kontoen din.
title: 2FA aktivert
two_factor_recovery_codes_changed:
explanation: Dei førre gjenopprettingskodane er ugyldige og nye er genererte.
subject: 'Mastodon: To-faktor-gjenopprettingskodar har vorte genererte på nytt'
subtitle: De forrige gjenopprettingskodene er gjort ugyldige og nye er generert.
subtitle: Dei førre innloggingskodane er ikkje gyldige lenger, og nye kodar er laga.
title: 2FA-gjenopprettingskodane er endra
unlock_instructions:
subject: 'Mastodon: Instruksjonar for å opne kontoen igjen'
@ -73,13 +74,13 @@ nn:
subject: 'Mastodon: Sikkerheitsnøkkel sletta'
title: Ein av sikkerheitsnøklane dine har blitt sletta
webauthn_disabled:
explanation: Autentisering med sikkerhetsnøkler er deaktivert for kontoen din.
extra: Innlogging er nå mulig med kun tilgangstoken generert av den sammenkoblede TOTP-appen.
explanation: Innlogging med tryggingsnykjel er skrudd av for kontoen din.
extra: No kan du logga inn med berre kodane som er laga av den tilkopla tofaktor-appen din.
subject: 'Mastodon: Autentisering med sikkerheitsnøklar vart skrudd av'
title: Sikkerheitsnøklar deaktivert
webauthn_enabled:
explanation: Sikkerhetsnøkkelautentisering har blitt aktivert for kontoen din.
extra: Sikkerhetsnøkkelen din kan nå bli brukt for innlogging.
explanation: Innlogging med tryggingsnyklar er skrudd på for kontoen din.
extra: No kan du bruka tryggingsnykjelen din for å logga inn.
subject: 'Mastodon: Sikkerheitsnøkkelsautentisering vart skrudd på'
title: Sikkerheitsnøklar aktivert
omniauth_callbacks:

View file

@ -12,6 +12,7 @@ pt-BR:
last_attempt: Você tem mais uma tentativa antes de sua conta ser bloqueada.
locked: Sua conta está bloqueada.
not_found_in_database: "%{authentication_keys} ou senha inválida."
omniauth_user_creation_failure: Erro ao criar uma conta para esta identidade.
pending: Sua conta está sendo revisada.
timeout: Sua sessão expirou. Por favor, entre novamente para continuar.
unauthenticated: Você precisa entrar ou criar uma conta antes de continuar.

View file

@ -156,13 +156,13 @@ th:
admin:read:ip_blocks: อ่านข้อมูลที่ละเอียดอ่อนของการปิดกั้น IP ทั้งหมด
admin:read:reports: อ่านข้อมูลที่ละเอียดอ่อนของรายงานและบัญชีที่ได้รับการรายงานทั้งหมด
admin:write: ปรับเปลี่ยนข้อมูลทั้งหมดในเซิร์ฟเวอร์
admin:write:accounts: ทำการกระทำการควบคุมบัญชี
admin:write:canonical_email_blocks: ทำการกระทำการควบคุมการปิดกั้นอีเมลมาตรฐาน
admin:write:domain_allows: ทำการกระทำการควบคุมการอนุญาตโดเมน
admin:write:domain_blocks: ทำการกระทำการควบคุมการปิดกั้นโดเมน
admin:write:email_domain_blocks: ทำการกระทำการควบคุมการปิดกั้นโดเมนอีเมล
admin:write:ip_blocks: ทำการกระทำการควบคุมการปิดกั้น IP
admin:write:reports: ทำการกระทำการควบคุมรายงาน
admin:write:accounts: ทำการกระทำการกลั่นกรองต่อบัญชี
admin:write:canonical_email_blocks: ทำการกระทำการกลั่นกรองต่อการปิดกั้นอีเมลมาตรฐาน
admin:write:domain_allows: ทำการกระทำการกลั่นกรองต่อการอนุญาตโดเมน
admin:write:domain_blocks: ทำการกระทำการกลั่นกรองต่อการปิดกั้นโดเมน
admin:write:email_domain_blocks: ทำการกระทำการกลั่นกรองต่อการปิดกั้นโดเมนอีเมล
admin:write:ip_blocks: ทำการกระทำการกลั่นกรองต่อการปิดกั้น IP
admin:write:reports: ทำการกระทำการกลั่นกรองต่อรายงาน
crypto: ใช้การเข้ารหัสแบบต้นทางถึงปลายทาง
follow: ปรับเปลี่ยนความสัมพันธ์ของบัญชี
push: รับการแจ้งเตือนแบบผลักของคุณ

View file

@ -309,6 +309,7 @@ el:
unpublish: Αναίρεση δημοσίευσης
unpublished_msg: Επιτυχής ακύρωση δημοσίευσης ανακοίνωσης!
updated_msg: Επιτυχής ενημέρωση ανακοίνωσης!
critical_update_pending: Κρίσιμη ενημέρωση σε αναμονή
custom_emojis:
assign_category: Ανάθεση κατηγορίας
by_domain: Τομέας
@ -382,6 +383,15 @@ el:
undo: Αφαίρεση συναλλαγής με τον τομέα
domain_blocks:
add_new: Προσθήκη νέου αποκλεισμού τομέα
confirm_suspension:
cancel: Άκυρο
confirm: Αναστολή
permanent_action: Η κατάργηση της αναστολής δε θα αποκαταστήσει καθόλου δεδομένα ή σχέση.
preamble_html: Πρόκειται να αναστείλετε το <strong>%{domain}</strong> και τους υποτομείς του.
remove_all_data: Αυτό θα αφαιρέσει όλο το περιεχόμενο, τα μέσα και τα δεδομένα προφίλ για τους λογαριασμούς αυτού του τομέα από το διακομιστή σας.
stop_communication: Ο διακομιστής σας θα σταματήσει να επικοινωνεί με αυτούς τους διακομιστές.
title: Επιβεβαίωση αποκλεισμού τομέα για %{domain}
undo_relationships: Αυτό θα αναιρέσει οποιαδήποτε σχέση ακολουθίας μεταξύ των λογαριασμών αυτών των διακομιστών και των δικών σας.
created_msg: Ο αποκλεισμός τομέα είναι υπό επεξεργασία
destroyed_msg: Ο αποκλεισμός τομέα αναιρέθηκε
domain: Τομέας
@ -415,6 +425,7 @@ el:
view: Εμφάνιση αποκλεισμού τομέα
email_domain_blocks:
add_new: Προσθήκη νέου
allow_registrations_with_approval: Να επιτρέπονται εγγραφές με έγκριση
attempts_over_week:
one: "%{count} προσπάθεια την τελευταία εβδομάδα"
other: "%{count} προσπάθειες εγγραφής την τελευταία εβδομάδα"
@ -524,6 +535,7 @@ el:
total_reported: Αναφορές προς εκείνους
total_storage: Συνημμένα πολυμέσα
totals_time_period_hint_html: Τα σύνολα που εμφανίζονται παρακάτω περιλαμβάνουν στοιχεία από την αρχή.
unknown_instance: Προς το παρόν δεν υπάρχει καμία εγγραφή αυτού του τομέα σε αυτόν το διακομιστή.
invites:
deactivate_all: Απενεργοποίηση όλων
filter:
@ -600,6 +612,7 @@ el:
created_at: Αναφέρθηκε
delete_and_resolve: Διαγραφή αναρτήσεων
forwarded: Προωθημένα
forwarded_replies_explanation: Αυτή η αναφορά είναι από απομακρυσμένο χρήστη και για απομακρυσμένο περιεχόμενο. Σας έχει διαβιβαστεί, επειδή το αναφερόμενο περιεχόμενο απαντά σε έναν από τους χρήστες σας.
forwarded_to: Προώθημένα προς %{domain}
mark_as_resolved: Σημείωση ως επιλυμένο
mark_as_sensitive: Σήμανση ως ευαίσθητο

View file

@ -1847,7 +1847,7 @@ es:
edit_profile_step: Puedes personalizar tu perfil subiendo una foto de perfil, cambiando tu nombre de usuario y mucho más. Puedes optar por revisar a los nuevos seguidores antes de que puedan seguirte.
explanation: Aquí hay algunos consejos para empezar
final_action: Empezar a publicar
final_step: "¡Empieza a publicar! Incluso sin seguidores, tus publicaciones públicas pueden ser vistas por otros, por ejemplo en la línea de tiempo local o en etiquetas. Tal vez quieras presentarte con la etiqueta de #introducciones."
final_step: "¡Empieza a publicar! Incluso sin seguidores, tus publicaciones públicas pueden ser vistas por otros, por ejemplo en la línea de tiempo local o en etiquetas. Tal vez quieras presentarte con la etiqueta de #presentación."
full_handle: Su sobrenombre completo
full_handle_hint: Esto es lo que le dirías a tus amigos para que ellos puedan enviarte mensajes o seguirte desde otra instancia.
subject: Bienvenido a Mastodon

View file

@ -769,6 +769,7 @@ eu:
disabled: Inori ez
users: Saioa hasita duten erabiltzaile lokalei
registrations:
moderation_recommandation: Mesedez, ziurtatu moderazio-talde egokia eta erreaktiboa duzula erregistroak guztiei ireki aurretik!
preamble: Kontrolatu nork sortu dezakeen kontua zerbitzarian.
title: Izen emateak
registrations_mode:
@ -776,6 +777,7 @@ eu:
approved: Izena emateko onarpena behar da
none: Ezin du inork izena eman
open: Edonork eman dezake izena
warning_hint: "“Izena emateko onarpena behar da” erabiltzea gomendatzen dugu, baldin eta ez badakizu ziur zure moderazio-taldeak spama eta erregistro maltzurrak arrazoizko denboran erantzun ditzakeela."
security:
authorized_fetch: Eskatu autentifikazioa federatutako zerbitzarietatik
authorized_fetch_hint: Zerbitzari federatuen autentifikazioa eskatzeak erabiltzaile-mailako zein zerbitzari-mailako blokeak zorrotzago betearaztea ahalbidetzen du. Hala ere, horrek errendimendu galera dakar, zure erantzunen irismena murrizten du eta baliteke federatutako zerbitzu batzuekin bateragarritasun-arazoak sortu ahal izatea. Horrez gain, horrek ez du eragotziko aktore dedikatuek zure mezu eta kontu publikoak eskuratzea.

View file

@ -80,7 +80,7 @@ fa:
joined: عضو شده در
location:
all: همه
local: محلّی
local: محلی
remote: کارسازهای دیگر
title: مکان
login_status: وضعیت ورود
@ -415,13 +415,14 @@ fa:
public_comment: یادداشت عمومی
public_comment_hint: یادداشتی دربارهٔ محدودیت روی این دامین برای عموم، در صورتی که فهرست دامین‌های محدود شده منتشر شود.
reject_media: نپذیرفتن پرونده‌های رسانه‌ای
reject_media_hint: پرونده‌های رسانه‌ای ذخیره‌شدهٔ محلّی را پاک کرده و از بارگیریشان در آینده خودداری می‌کند. بی‌تأثیر روی معلق‌ها
reject_media_hint: پرونده‌های رسانه‌ای ذخیره‌شدهٔ محلی را پاک کرده و از بارگیریشان در آینده خودداری می‌کند. بی‌تأثیر روی معلّق‌ها
reject_reports: نپذیرفتن گزارش‌ها
reject_reports_hint: گزارش‌هایی را که از این دامنه می‌آید نادیده می‌گیرد. بی‌تأثیر برای معلق‌شده‌ها
undo: واگردانی مسدودسازی دامین
view: دیدن مسدودسازی دامنه
email_domain_blocks:
add_new: افزودن تازه
allow_registrations_with_approval: اجازهٔ ثبت‌نام با تأیید
attempts_over_week:
one: "%{count} تلاش در هفتهٔ گذشته"
other: "%{count} تلاش ورود در هفتهٔ گذشته"

View file

@ -767,6 +767,7 @@ fi:
disabled: Ei kenellekkään
users: Kirjautuneille paikallisille käyttäjille
registrations:
moderation_recommandation: Varmista, että sinulla on riittävä ja toimintavalmis joukko moderaattoreita ennen kuin avaat rekisteröitymiset kaikille!
preamble: Määritä, kuka voi luoda tilin palvelimellesi.
title: Rekisteröityminen
registrations_mode:
@ -774,6 +775,7 @@ fi:
approved: Rekisteröinti vaatii hyväksynnän
none: Kukaan ei voi rekisteröityä
open: Kaikki voivat rekisteröityä
warning_hint: Suosittelemme käyttämään asetusta “Rekisteröinti vaatii hyväksynnän” ellet ole varma siitä, että moderaattorit ovat valmiina käsittelemään roskapostia ja haittarekisteröitymisiä oikea-aikaisesti.
security:
authorized_fetch: Vaadi todennus liittoutuvilta palvelimilta
authorized_fetch_hint: Todennuksen vaatiminen liittoutuvilta palvelimilta mahdollistaa sekä käyttäjä- että palvelintason estojen tiukemman valvonnan. Tämä tapahtuu kuitenkin suorituskyvyn kustannuksella, vähentää vastauksiesi tavoittavuutta ja voi aiheuttaa yhteensopivuusongelmia joidenkin liittoutuvien palvelujen kanssa. Tämä ei myöskään estä omistautuneita toimijoita hakemasta julkisia julkaisujasi ja tilejäsi.
@ -966,6 +968,9 @@ fi:
title: Webhookit
webhook: Webhook
admin_mailer:
auto_close_registrations:
body: Viimeaikaisen moderaattoritoiminnan puutteen vuoksi %{instance} rekisteröinnit on vaihdettu automaattisesti manuaaliseen tarkasteluun, jotta %{instance} ei käytetä mahdollisien huonojen toimijoiden alustana. Voit vaihtaa sen takaisin avaamalla rekisteröinnit milloin tahansa.
subject: Rekisteröinnit %{instance} on automaattisesti vaihdettu vaatimaan hyväksyntää
new_appeal:
actions:
delete_statuses: poistaa hänen julkaisunsa

View file

@ -767,6 +767,7 @@ fo:
disabled: Til ongan
users: Fyri lokalum brúkarum, sum eru ritaðir inn
registrations:
moderation_recommandation: Vinarliga tryggja tær, at tú hevur eitt nøktandi og klárt umsjónartoymi, áðreen tú letur upp fyri skrásetingum frá øllum!
preamble: Stýr, hvør kann stovna eina kontu á tínum ambætara.
title: Skrásetingar
registrations_mode:
@ -774,6 +775,7 @@ fo:
approved: Góðkenning kravd fyri tilmelding
none: Eingin kann tilmelda seg
open: Øll kunnu tilmelda seg
warning_hint: Vit mæla til at brúka "Góðkenning kravd fyri tilmelding" uttan so at tú er fullvís/ur í, at umsjónartoymið hjá tær kann handfara ruskpost og óndsinnaðar skrásetingar so hvørt.
security:
authorized_fetch: Krev samgildi frá sameindum ambætarum
authorized_fetch_hint: At krevja samgildi frá sameindum ambætarum ger strangari útinning av blokkum bæði á brúkara- og ambætara-stigi møguliga. Tó so, kostnaðurin er ein avriksstraffur, minkar um hvussu langt svarini hjá tær røkka og kann viðføra sambæristruplleikar við summar sameindar tænastur. Harafturat forðar hetta ikki teimum, ið miðvíst leggja seg eftir at heinta tínar almennu postar og kontur.

View file

@ -795,6 +795,7 @@ he:
disabled: לאף אחד
users: למשתמשים מקומיים מחוברים
registrations:
moderation_recommandation: יש לוודא שלאתר יש צוות מנחות ומנחי שיחה מספק ושירותי בטרם תבחרו לפתוח הרשמה לכולם!
preamble: שליטה בהרשאות יצירת חשבון בשרת שלך.
title: הרשמות
registrations_mode:
@ -802,6 +803,7 @@ he:
approved: נדרש אישור הרשמה
none: אף אחד לא יכול להרשם
open: כל אחד יכול להרשם
warning_hint: אנו ממליצים להפעיל דרישה לאישור ידני של הרשמה אלא אם אתם מאמינים שצוות הנחיית השיחות שלכם יוכל להסתדר בזריזות עם מפיצי תכנים פוגעניים או פרסומיים על בסיס קבוע.
security:
authorized_fetch: לדרוש הזדהות מול שרתים בפדרציה
authorized_fetch_hint: הדרישה להזדהות מול שרתים בפדרציה מאפשרת חסימה יותר יעילה ברמת המשתמש וברמת שרת. עם זאת, הדרישה באה עם מחיר של נפילת ביצועים, מקטינה את מעגל התפוצה של התשובות שלך, ועשויה ליצור אי תאימות מול שירותים אחרים בפדרציה. בנוסף, זה לא ימנע מצדדים החלטיים לקבל גישת קריאה להודעות ופרופילים ציבוריים.

View file

@ -767,7 +767,7 @@ hu:
disabled: Senkinek
users: Bejelentkezett helyi felhasználóknak
registrations:
moderation_recommandation: Győződjünk meg arról, hogy megfelelő és reaktív moderátor csapatunk van, mielőtt mindenki számára megnyitjuk a regisztrációt!
moderation_recommandation: Győződj meg arról, hogy megfelelő és gyors reagálású moderátor csapatod van, mielőtt mindenki számára megnyitod a regisztrációt!
preamble: Szabályozd, hogy ki hozhat létre fiókot a kiszolgálón.
title: Regisztrációk
registrations_mode:
@ -775,7 +775,7 @@ hu:
approved: A regisztráció engedélyhez kötött
none: Senki sem regisztrálhat
open: Bárki regisztrálhat
warning_hint: Célszerű a "Jóváhagyás szükséges a regisztrációhoz” lehetőség használata, hkivéve, ha biztos vagyunk abban, hogy a moderátor csapat időben tudja kezelni a szemetet és a rosszindulatú regisztrációkat.
warning_hint: Javasoljuk a "Jóváhagyás szükséges a regisztrációhoz” lehetőség használatát, hacsak nem vagy biztos abban, hogy a moderátor csapatod időben tudja kezelni a szemetet és a rosszindulatú regisztrációkat.
security:
authorized_fetch: Hitelesítés szükséges a föderációs kiszolgálóktól
authorized_fetch_hint: A föderációs szerverek hitelesítésének szükségessége lehetővé teszi mind a felhasználói mind a szerver szintű blokkok szigorúbb végrehajtását. Ez azonban a teljesítménybüntetés árán jár, csökkenti a válaszok elérhetőségét és kompatibilitási problémákat vethet fel egyes föderációs szolgáltatásokkal. Emellett ez nem akadályozza meg a dedikált szereplőket abban, hogy nyilvános bejegyzéseiket és fiókjaikat letöltsék.

View file

@ -767,6 +767,7 @@ ie:
disabled: A necun
users: A local usatores qui ha initiat session
registrations:
moderation_recommandation: Ples assecurar que tu have un equip de moderation quel es adequat e reactiv ante que tu aperte registrationes a omnes!
preamble: Decider qui posse crear un conto che vor servitor.
title: Registrationes
registrations_mode:
@ -774,6 +775,7 @@ ie:
approved: Aprobation besonat por adhesion
none: Nequi posse registrar se
open: Quicunc posse registrar se
warning_hint: Noi recomanda usar "Aprobation besonat por inscrir" si tu ne es confident que tui equip de moderation posse gerer spam e maliciosi registrationes in un curt témpor.
security:
authorized_fetch: Postular autentication de federat servitores
authorized_fetch_hint: Postular autentication de federat servitores possibilisa plu strict infortiament de ambi usatori e servitori bloccas. Támen, ti fórsan va limitar li potentie de vor servitor, reducter li atingement de vor responses, e possibilmen introducter problemas de compatibilitá con quelc federat servicies. Additionalmen, ti ne va preventer dedicat actores de accesser vor public postas e contos.
@ -966,6 +968,9 @@ ie:
title: Webcrocs
webhook: Webcroc
admin_mailer:
auto_close_registrations:
body: Pro un manca de recent activitá moderatori, registrationes sur %{instance} ha esset automaticmen changeat al mode quel besona un manual recension, por que %{instance} ne mey esser usat quam un platform por malfatores. Tu posse rechangear a apert registrationes quandecunc.
subject: Registrationes por %{instance} ha esset automaticmen changeat al mode quel besona aprobation
new_appeal:
actions:
delete_statuses: deleter su postas

View file

@ -11,7 +11,7 @@ kab:
followers:
one: Umeḍfaṛ
other: Imeḍfaṛen
following: Yeṭafaṛ
following: Yeṭafaṛ
last_active: armud aneggaru
nothing_here: Ulac kra da!
posts:
@ -542,7 +542,7 @@ kab:
add_new: Rnu amaynut
filters:
contexts:
account: Imuɣna
account: Imeɣna
notifications: Ilɣa
thread: Idiwenniyen
edit:
@ -654,7 +654,7 @@ kab:
relationships:
activity: Armud n umiḍan
followers: Imeḍfaṛen
following: Yeṭafaṛ
following: Yeṭafaṛ
invited: Yettwancad
last_active: Armud aneggaru
most_recent: Melmi kan

View file

@ -755,6 +755,7 @@ ko:
disabled: 아무에게도 안 함
users: 로그인 한 사용자에게
registrations:
moderation_recommandation: 모두에게 가입을 열기 전에 적절하고 반응이 빠른 중재 팀을 데리고 있는지 확인해 주세요!
preamble: 누가 이 서버에 계정을 만들 수 있는지 제어합니다.
title: 가입
registrations_mode:
@ -762,6 +763,7 @@ ko:
approved: 가입하려면 승인이 필요함
none: 아무도 가입 할 수 없음
open: 누구나 가입 할 수 있음
warning_hint: 당신의 중재 팀이 스팸이나 악의적인 가입을 시기적절하게 처리할 수 있다고 자신할 수 없다면 "가입이 승인을 요구하도록" 설정하는 것을 추천합니다.
security:
authorized_fetch: 연합된 서버들에게서 인증 필수
authorized_fetch_hint: 연합된 서버들에게서 인증을 요구하는 것은 사용자 레벨과 서버 레벨의 차단을 좀 더 확실하게 해줍니다. 한편으로는 성능적인 페널티, 답글의 전달 범위 감소, 몇몇 연합된 서비스들과의 호환성 문제가 있을 가능성이 있습니다. 추가적으로 이 기능은 전용 액터가 공개된 게시물이나 계정을 페치하는 것은 막지 않습니다.
@ -950,6 +952,9 @@ ko:
title: 웹훅
webhook: 웹훅
admin_mailer:
auto_close_registrations:
body: 최근 모더레이터 활동 부족으로, %{instance}가 안좋은 일에 사용되는 것을 방지하기 위해 %{instance}의 가입이 수동 심사를 요구하도록 자동으로 변경되었습니다. 언제든지 가입을 다시 열 수 있습니다.
subject: "%{instance}의 가입이 승인을 필요로 하도록 자동으로 변경되었습니다"
new_appeal:
actions:
delete_statuses: 게시물을 삭제하는 것

View file

@ -767,6 +767,7 @@ lad:
disabled: A dinguno
users: Para los utilizadores lokales ke entrado en su kuento
registrations:
moderation_recommandation: Por favor, asigurate ke tyenes una taifa de moderasyon adekuada i reaktiva antes de avrir los enrejistramyentos a todos!
preamble: Kontrola ken puede kriyar un kuento en tu sirvidor.
title: Enrejistramientos
registrations_mode:
@ -774,6 +775,7 @@ lad:
approved: Se rekiere achetasion para enrejistrarse
none: Permete a los utilizadores trokar la konfigurasyon del sitio
open: Kualkiera puede enrejistrarse
warning_hint: Rekomendamos el uzo de "Se rekiere achetasion para enrejistrarse" a manko ke estes siguro ke tu taifa de moderasyon puede moderar el spam i los enrejistramyentos malisiozos en un tyempo razonavle.
security:
authorized_fetch: Rekere autentifikasyon de sirvidores federados
authorized_fetch_hint: Rekerir autentifikasyon de sirvidores federados permite un forsamyento mas estrikto de los blokos a nivel de utilizador i a nivel de sirvidor. Malgrado esto, el koste de esto es una penalizasyon de efisyensya, reduksyon del alkanse de tus repuestas i puede introduzir problemas de kompatibilita kon algunos sirvisyos federados. Ademas, esto no impidira ke aktores dedikados obtengan tus kuentos publikos i publikasyones publikas.
@ -966,6 +968,9 @@ lad:
title: Webhooks
webhook: Webhook
admin_mailer:
auto_close_registrations:
body: Por la falta de moderadores aktivos, los enrejistramyentos en %{instance} tyenen sido trokados otomatikamente para rekerir revizyon manuala, para ke %{instance} no se utilize potensyalmente komo platforma por malos aktores. Puedes trokarlo de muevo para avrir los enrejistramyentos en kualseker momento.
subject: Enrejistramyentos de %{instance} fueron otomatikamente trokados i agora nesesitan aprovasyon
new_appeal:
actions:
delete_statuses: para supremir sus mesajes

View file

@ -281,6 +281,8 @@ lt:
desc_html: Tai priklauso nuo hCaptcha išorinių skriptų, kurie gali kelti susirūpinimą dėl saugumo ir privatumo. Be to, <strong>dėl to registracijos procesas kai kuriems žmonėms (ypač neįgaliesiems) gali būti gerokai sunkiau prieinami</strong>. Dėl šių priežasčių apsvarstyk alternatyvias priemones, pavyzdžiui, patvirtinimu arba kvietimu grindžiamą registraciją.
domain_blocks:
all: Visiems
registrations:
moderation_recommandation: Prieš atidarant registraciją visiems, įsitikink, kad turi tinkamą ir reaguojančią prižiūrėjimo komandą!
software_updates:
description: Rekomenduojama nuolat atnaujinti Mastodon diegyklę, kad galėtum naudotis naujausiais pataisymais ir funkcijomis. Be to, kartais labai svarbu laiku naujinti Mastodon, kad būtų išvengta saugumo problemų. Dėl šių priežasčių Mastodon kas 30 minučių tikrina, ar yra atnaujinimų, ir praneša tau apie tai pagal tavo el. pašto pranešimų parinktis.
statuses:
@ -304,6 +306,9 @@ lt:
edit_preset: Keisti įspėjimo nustatymus
title: Valdyti įspėjimo nustatymus
admin_mailer:
auto_close_registrations:
body: Dėl pastarojo meto peržiūrėtojų aktyvumo trūkumo %{instance} registracija buvo automatiškai pakeista į reikalaujančią rankinės būdo peržiūros, kad %{instance} nebūtų naudojama kaip platforma potencialiems blogiems veikėjams. Bet kuriuo metu gali ją vėl perjungti į atvirą registraciją.
subject: "%{instance} registracijos automatiškai pakeistos į reikalaujančias patvirtinimo"
new_report:
body: "%{reporter} parašė skundą apie %{target}"
body_remote: Kažkas iš %{domain} parašė skundą apie %{target}

View file

@ -31,7 +31,7 @@ nn:
created_msg: Moderatormerknad er laga!
destroyed_msg: Moderatormerknad er utsletta!
accounts:
add_email_domain_block: Gøym e-postdomene
add_email_domain_block: Blokker e-postdomene
approve: Godtak
approved_msg: Godkjende %{username} sin registreringssøknad
are_you_sure: Er du sikker?
@ -767,13 +767,15 @@ nn:
disabled: Til ingen
users: Til lokale brukarar som er logga inn
registrations:
moderation_recommandation: Pass på at du har mange og kjappe redaktørar og moderatorar på laget ditt før du opnar for allmenn registrering!
preamble: Kontroller kven som kan oppretta konto på tenaren din.
title: Registreringar
registrations_mode:
modes:
approved: Godkjenning kreves for påmelding
approved: Godkjenning krevst for å registrera seg
none: Ingen kan melda seg inn
open: Kven som helst kan melda seg inn
warning_hint: Me rår til at du bruker "Godkjenning krevst for å registrera seg" viss du ikkje er sikker på at moderatorane kan handtera søppel og illmeinte registreringar kvikt.
security:
authorized_fetch: Krev autentisering frå fødererte tenarar
authorized_fetch_hint: Krav om autentisering frå fødererte tenarar gjer det mogleg med strengare handheving av blokkering, både på brukar- og tenar-nivå. Likevel, dette har ein kostnad når det gjeld yting, reduserer rekkevidda til svara dine og kan medføra kompabilitetsproblem med enkelte fødererte tenester. Dette vil heller ikkje hindra dei som verkeleg vil i å henta dei offentlege innlegga eller kontoane dine.
@ -1450,7 +1452,7 @@ nn:
moderation:
title: Moderasjon
move_handler:
carry_blocks_over_text: Denne brukaren flytta frå %{acct}, som du gøymde.
carry_blocks_over_text: Denne brukaren flytta frå %{acct}, som du hadde blokkert.
carry_mutes_over_text: Denne brukeren flyttet fra %{acct}, som du hadde dempet.
copy_account_note_text: 'Denne brukeren flyttet fra %{acct}, her var dine tidligere notater om dem:'
navigation:
@ -1537,7 +1539,7 @@ nn:
privacy:
hint_html: "<strong>Tilpass korleis du vil at andre skal finna profilen og innlegga dine.</strong> Mastodon har fleire funksjonar du kan ta i bruk for å få kontakt med eit større publikum. Sjå gjerne gjennom innstillingane slik at du er sikker på at dei passar til deg og din bruk."
privacy: Personvern
privacy_hint_html: Ha kontroll over kor mykje du vil dela. Folk finn interessante profilar og fine appar ved å sjå gjennom kva andre fylgjer og kva appar dei legg ut innlegg med, men det kan henda du vil gøyma desse opplysingane.
privacy_hint_html: Kontroller kor mykje du vil dela. Folk finn interessante profilar og fine appar ved å sjå gjennom kva andre fylgjer og kva appar dei legg ut innlegg med, men det kan henda du vil gøyma desse opplysingane.
reach: Nå andre
reach_hint_html: Hald styring med om du vil at andre skal kunna oppdaga og fylgja deg. Vil du at innlegga dine skal stå på Utforsk-sida? Vil du at andre skal sjå deg i tilrådingane for kven dei skal fylgja? Vil du ta imot nye fylgjarar automatisk, eller vil du kontrollera kvar einskild fylgjar?
search: Søk
@ -1550,8 +1552,8 @@ nn:
limit_reached: Grensen for forskjellige reaksjoner nådd
unrecognized_emoji: er ikke en gjenkjent emoji
redirects:
prompt: Hvis du stoler på denne lenken, så trykk på den for å fortsette.
title: Du forlater %{instance}.
prompt: Viss du stolar på denne lenka, klikkar du på ho for å halda fram.
title: No forlèt du %{instance}.
relationships:
activity: Kontoaktivitet
confirm_follow_selected_followers: Er du sikker på at du ynskjer å fylgja dei valde fylgjarane?
@ -1781,7 +1783,7 @@ nn:
webauthn: Sikkerhetsnøkler
user_mailer:
appeal_approved:
action: Kontoinnstillinger
action: Kontoinnstillingar
explanation: Apellen på prikken mot din kontor på %{strike_date} som du la inn på %{appeal_date} har blitt godkjend. Din konto er nok ein gong i god stand.
subject: Din klage fra %{date} er godkjent
subtitle: Kontoen din er tilbake i god stand.
@ -1789,11 +1791,11 @@ nn:
appeal_rejected:
explanation: Klagen på advarselen mot din konto den %{strike_date} som du sendte inn den %{appeal_date} har blitt avvist.
subject: Din klage fra %{date} er avvist
subtitle: Anken din har blitt avvist.
subtitle: Klaga di vart avvist.
title: Anke avvist
backup_ready:
explanation: Du etterspurte en fullstendig sikkerhetskopi av din Mastodon-konto.
extra: Den er nå klar for nedlasting!
explanation: Du ba om ein fullstendig tryggingskopi av Mastodon-kontoen din.
extra: No kan du lasta han ned!
subject: Arkivet ditt er klart til å lastes ned
title: Nedlasting av arkiv
failed_2fa:

View file

@ -767,6 +767,7 @@ pt-BR:
disabled: Para ninguém
users: Para usuários locais logados
registrations:
moderation_recommandation: Por favor, certifique-se de ter uma equipe de moderação adequada e reativa antes de abrir as inscrições para todos!
preamble: Controle quem pode criar uma conta no seu servidor.
title: Inscrições
registrations_mode:

View file

@ -968,6 +968,9 @@ pt-PT:
title: Webhooks
webhook: Webhook
admin_mailer:
auto_close_registrations:
body: Devido à falta de atividade recente dos moderadores, as inscrições em %{instance} foram automaticamente alteradas para requererem revisão manual, para evitar que %{instance} seja utilizada como plataforma para potenciais maus atores. Pode voltar a alterar para inscrições abertas em qualquer altura.
subject: As incrições em %{instance} foram automaticamente alteradas para requererem aprovação
new_appeal:
actions:
delete_statuses: para eliminar as suas publicações

View file

@ -31,14 +31,12 @@ an:
text: Nomás puetz apelar una amonestación una vegada
defaults:
autofollow: Los usuarios que se rechistren per medio d'a invitación te seguirán automaticament
avatar: PNG, GIF u JPG. Maximo %{size}. Será escalau a %{dimensions}px
bot: Esta cuenta executa prencipalment accions automatizadas y podría no estar monitorizada
context: Un u multiples contextos en os quals ha d'aplicar-se lo filtro
current_password: Per razons de seguranza per favor ingrese la clau d'a cuenta actual
current_username: Pa confirmar, per favor ingrese lo nombre d'usuario d'a cuenta actual
digest: Solo ninviau dimpués d'un largo periodo d'inactividat y nomás si has recibiu mensaches personals entre la tuya ausencia
email: Se le ninviará un correu de confirmación
header: PNG, GIF u JPG. Maximo %{size}. Será escalau a %{dimensions}px
inbox_url: Copia la URL d'a pachina prencipal d'o relés que quiers utilizar
irreversible: Las publicacions filtradas desapareixerán irreversiblement, mesmo si este filtro ye eliminau mas abance
locale: L'idioma d'a interficie d'usuario, correus y notificacions push

View file

@ -39,14 +39,12 @@ ar:
text: يمكنك الطعن في عقوبة مرة واحدة فقط
defaults:
autofollow: سوف يتابعك تلقائيًا الأشخاص الذين يقومون بالتسجيل من خلال الدعوة
avatar: ملف PNG أو GIF أو JPG. حجمه على أقصى تصدير %{size}. سيتم تصغيره إلى %{dimensions}px
bot: يقوم هذا الحساب أساسا بإجراءات آلية وقد لا يتم مراقبته
context: واحد أو أكثر من السياقات التي يجب أن ينطبق عليها عامل التصفية
current_password: لأسباب أمنية ، يرجى إدخال الكلمة السرية الخاصة بالحساب الحالي
current_username: يرجى إدخال اسم المستخدم الخاص بالحساب الحالي قصد التأكيد
digest: تُرسَل إليك بعد مُضيّ مدة مِن خمول نشاطك و فقط إذا ما تلقيت رسائل شخصية مباشِرة أثناء فترة غيابك مِن الشبكة
email: سوف تتلقى رسالة إلكترونية للتأكيد
header: ملف PNG أو GIF أو JPG. حجمه على أقصى تصدير %{size}. سيتم تصغيره إلى %{dimensions}px
inbox_url: نسخ العنوان الذي تريد استخدامه مِن صفحة الاستقبال للمُرحَّل
irreversible: المنشورات التي تم تصفيتها ستختفي لا محالة حتى و إن تمت إزالة عامِل التصفية لاحقًا
locale: لغة واجهة المستخدم و الرسائل الإلكترونية و الإشعارات

View file

@ -18,10 +18,8 @@ ast:
text: Pues usar la sintaxis de los artículos. Ten en cuenta l'espaciu que l'anunciu va ocupar na pantalla del usuariu/a
defaults:
autofollow: Les persones que se rexistren pente la invitación van siguite automáticamente
avatar: Ficheros PNG, GIF o JPG de %{size} como muncho. La semeya va redimensionase a %{dimensions} px
bot: Avisa a otres persones de qu'esta cuenta fai principalmente aiciones automatizaes ya de que ye posible que nun tean supervisaes
digest: Namás s'unvia dempués d'un periodu llongu d'inactividá ya namás si recibiesti dalgún mensaxe personal demientres la to ausencia
header: Ficheros PNG, GIF o JPG de %{size} como muncho. La semeya va redimensionase a %{dimensions} px
irreversible: Los artículos peñeraos desapaecen de forma irreversible, magar que la peñera se quite dempués
locale: La llingua de la interfaz, los mensaxes per corréu electrónicu ya los avisos push
password: Usa polo menos 8 caráuteres

View file

@ -39,14 +39,14 @@ be:
text: Вы можаце абскардзіць рашэнне толькі адзін раз
defaults:
autofollow: Людзі, якія зарэгістраваліся праз запрашэнне, аўтаматычна падпішуцца на вас
avatar: PNG, GIF ці JPG. Не больш за %{size}. Будзе сціснуты да памеру %{dimensions}} пікселяў
avatar: WEBP, PNG, GIF ці JPG. Не больш за %{size}. Будзе сціснуты да памеру %{dimensions}} пікселяў
bot: Паведаміць іншым, што гэты ўліковы запіс у асноўным выконвае аўтаматычныя дзеянні і можа не кантралявацца
context: Адзін ці некалькі кантэкстаў, да якіх трэба прымяніць фільтр
current_password: У мэтах бяспекі, калі ласка, увядзіце пароль бягучага ўліковага запісу
current_username: Каб пацвердзіць, увядзіце, калі ласка імя карыстальніка бягучага ўліковага запісу
digest: Будзе даслана толькі пасля доўгага перыяду неактыўнасці і толькі калі вы атрымалі асабістыя паведамленні падчас вашай адсутнасці
email: Пацвярджэнне будзе выслана па электроннай пошце
header: PNG, GIF ці JPG. Не больш за %{size}. Будзе сціснуты да памеру %{dimensions}} пікселяў
header: WEBP, PNG, GIF ці JPG. Не больш за %{size}. Будзе сціснуты да памеру %{dimensions}} пікселяў
inbox_url: Капіраваць URL са старонкі рэтранслятара, якім вы хочаце карыстацца
irreversible: Адфільтраваныя пасты прападуць незваротна, нават калі фільтр потым будзе выдалены
locale: Мова карыстальніцкага інтэрфейсу, электронных паведамленняў і апавяшчэнняў

View file

@ -9,7 +9,7 @@ bg:
indexable: Вашите обществени публикации може да се появят в резултатите от търсене в Mastodon. Взаимодействалите с публикациите ви може да ги търсят независимо.
note: 'Може да @споменавате други хора или #хаштагове.'
show_collections: Хората ще може да разглеждат през вашите последвания и последователи. Хората, които сте следвали, ще видят, че ги следвате независимо от това.
unlocked: Хората ще може да ви следват без да се изисква одобрение. Размаркирайте, ако искате да преглеждате заявките за последване и изберете дали да приемете или отхвърлите новите последователи.
unlocked: Хората ще могат да ви последват без изискване на одобрение. Размаркирайте, ако искате да преглеждате заявките за последване и изберете дали да приемете или отхвърлите новите последователи.
account_alias:
acct: Посочете потребителско_име@домейн на акаунта си, от който искате да се преместите
account_migration:
@ -39,14 +39,14 @@ bg:
text: Може да възразите срещу провинение само веднъж
defaults:
autofollow: Хората, които се регистрират чрез поканата, автоматично ще ви последват
avatar: PNG, GIF или JPG. До най-много %{size}. Ще се смали до %{dimensions} пиксела
avatar: WEBP, PNG, GIF или JPG. До най-много %{size}. Ще се смали до %{dimensions} пиксела
bot: Сигнализиране до другите, че акаунтът изпълнява предимно автоматизирани деяния и може да не се наблюдава
context: Един или повече контексти, към които да се приложи филтърът
current_password: От съображения за сигурност, въведете паролата на текущия акаунт
current_username: Въведете потребителското име на текущия профил, за да потвърдите
digest: Изпраща се само след дълъг период на бездействие и само ако сте получили лични съобщения във ваше отсъствие
email: Ще ви се изпрати имейл за потвърждение
header: PNG, GIF или JPG. До най-много %{size}. Ще се смали до %{dimensions} пиксела
header: WEBP, PNG, GIF или JPG. До най-много %{size}. Ще се смали до %{dimensions} пиксела
inbox_url: Копирайте URL адреса от заглавната страница на предаващия сървър, който искате да използвате
irreversible: Филтрираните публикации ще изчезнат безвъзвратно, дори филтърът да бъде премахнат по-късно
locale: Езикът на потребителския интерфейс, известиятата по имейл и насочените известия

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