Merge tag 'v4.2.1' into chinwag-4.2
This commit is contained in:
commit
3d68228417
142 changed files with 2265 additions and 1664 deletions
|
@ -289,10 +289,6 @@ RSpec/MultipleMemoizedHelpers:
|
|||
RSpec/NestedGroups:
|
||||
Max: 6
|
||||
|
||||
RSpec/PendingWithoutReason:
|
||||
Exclude:
|
||||
- 'spec/models/account_spec.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/ApplicationController:
|
||||
Exclude:
|
||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -2,6 +2,46 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [4.2.1] - 2023-10-10
|
||||
|
||||
### Added
|
||||
|
||||
- Add redirection on `/deck` URLs for logged-out users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27128))
|
||||
- Add support for v4.2.0 migrations to `tootctl maintenance fix-duplicates` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27147))
|
||||
|
||||
### Changed
|
||||
|
||||
- Change some worker lock TTLs to be shorter-lived ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27246))
|
||||
- Change user archive export allowed period from 7 days to 6 days ([suddjian](https://github.com/mastodon/mastodon/pull/27200))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix duplicate reports being sent when reporting some remote posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27355))
|
||||
- Fix clicking on already-opened thread post scrolling to the top of the thread ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27331), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27338), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27350))
|
||||
- Fix some remote posts getting truncated ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27307))
|
||||
- Fix some cases of infinite scroll code trying to fetch inaccessible posts in a loop ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27286))
|
||||
- Fix `Vary` headers not being set on some redirects ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27272))
|
||||
- Fix mentions being matched in some URL query strings ([mjankowski](https://github.com/mastodon/mastodon/pull/25656))
|
||||
- Fix unexpected linebreak in version string in the Web UI ([vmstan](https://github.com/mastodon/mastodon/pull/26986))
|
||||
- Fix double scroll bars in some columns in advanced interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27187))
|
||||
- Fix boosts of local users being filtered in account timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27204))
|
||||
- Fix multiple instances of the trend refresh scheduler sometimes running at once ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27253))
|
||||
- Fix importer returning negative row estimates ([jgillich](https://github.com/mastodon/mastodon/pull/27258))
|
||||
- Fix incorrectly keeping outdated update notices absent from the API endpoint ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27021))
|
||||
- Fix import progress not updating on certain failures ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27247))
|
||||
- Fix websocket connections being incorrectly decremented twice on errors ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/27238))
|
||||
- Fix explore prompt appearing because of posts being received out of order ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27211))
|
||||
- Fix explore prompt sometimes showing up when the home TL is loading ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27062))
|
||||
- Fix link handling of mentions in user profiles when logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27185))
|
||||
- Fix filtering audit log for entries about disabling 2FA ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27186))
|
||||
- Fix notification toasts not respecting reduce-motion ([c960657](https://github.com/mastodon/mastodon/pull/27178))
|
||||
- Fix retention dashboard not displaying correct month ([vmstan](https://github.com/mastodon/mastodon/pull/27180))
|
||||
- Fix tIME chunk not being properly removed from PNG uploads ([TheEssem](https://github.com/mastodon/mastodon/pull/27111))
|
||||
- Fix division by zero in video in bitrate computation code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27129))
|
||||
- Fix inefficient queries in “Follows and followers” as well as several admin pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27116), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27306))
|
||||
- Fix ActiveRecord using two connection pools when no replica is defined ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27061))
|
||||
- Fix the search documentation URL in system checks ([renchap](https://github.com/mastodon/mastodon/pull/27036))
|
||||
|
||||
## [4.2.0] - 2023-09-21
|
||||
|
||||
The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by [@danielmbrasil](https://github.com/danielmbrasil), [@mjankowski](https://github.com/mjankowski), [@nschonni](https://github.com/nschonni), [@renchap](https://github.com/renchap), and [@takayamaki](https://github.com/takayamaki).
|
||||
|
|
|
@ -691,7 +691,7 @@ GEM
|
|||
rubyzip (>= 1.2.2, < 3.0)
|
||||
websocket (~> 1.0)
|
||||
semantic_range (3.0.0)
|
||||
sidekiq (6.5.9)
|
||||
sidekiq (6.5.10)
|
||||
connection_pool (>= 2.2.5, < 3)
|
||||
rack (~> 2.0)
|
||||
redis (>= 4.5.0, < 5)
|
||||
|
|
|
@ -15,6 +15,7 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
|
|||
|
||||
| Version | Supported |
|
||||
| ------- | ---------------- |
|
||||
| 4.2.x | Yes |
|
||||
| 4.1.x | Yes |
|
||||
| 4.0.x | Until 2023-10-31 |
|
||||
| 3.5.x | Until 2023-12-31 |
|
||||
|
|
|
@ -4,10 +4,10 @@ module WebAppControllerConcern
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
prepend_before_action :redirect_unauthenticated_to_permalinks!
|
||||
before_action :set_app_body_class
|
||||
|
||||
vary_by 'Accept, Accept-Language, Cookie'
|
||||
|
||||
before_action :redirect_unauthenticated_to_permalinks!
|
||||
before_action :set_app_body_class
|
||||
end
|
||||
|
||||
def skip_csrf_meta_tags?
|
||||
|
@ -22,7 +22,9 @@ module WebAppControllerConcern
|
|||
return if user_signed_in? && current_account.moved_to_account_id.nil?
|
||||
|
||||
redirect_path = PermalinkRedirector.new(request.path).redirect_path
|
||||
return if redirect_path.blank?
|
||||
|
||||
redirect_to(redirect_path) if redirect_path.present?
|
||||
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
|
||||
redirect_to(redirect_path)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
class FollowerAccountsController < ApplicationController
|
||||
include AccountControllerConcern
|
||||
include SignatureVerification
|
||||
include WebAppControllerConcern
|
||||
|
||||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
class FollowingAccountsController < ApplicationController
|
||||
include AccountControllerConcern
|
||||
include SignatureVerification
|
||||
include WebAppControllerConcern
|
||||
|
||||
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
|
||||
|
||||
|
|
|
@ -1,11 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DatabaseHelper
|
||||
def replica_enabled?
|
||||
ENV['REPLICA_DB_NAME'] || ENV.fetch('REPLICA_DATABASE_URL', nil)
|
||||
end
|
||||
module_function :replica_enabled?
|
||||
|
||||
def with_read_replica(&block)
|
||||
ApplicationRecord.connected_to(role: :reading, prevent_writes: true, &block)
|
||||
if replica_enabled?
|
||||
ApplicationRecord.connected_to(role: :reading, prevent_writes: true, &block)
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def with_primary(&block)
|
||||
ApplicationRecord.connected_to(role: :writing, &block)
|
||||
if replica_enabled?
|
||||
ApplicationRecord.connected_to(role: :writing, &block)
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -147,6 +147,10 @@ export const openURL = (value, history, onFailure) => (dispatch, getState) => {
|
|||
const signedIn = !!getState().getIn(['meta', 'me']);
|
||||
|
||||
if (!signedIn) {
|
||||
if (onFailure) {
|
||||
onFailure();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,21 @@ describe('computeHashtagBarForStatus', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('does not truncate the contents when the last child is a text node', () => {
|
||||
const status = createStatus(
|
||||
'this is a #<a class="zrl" href="https://example.com/search?tag=test">test</a>. Some more text',
|
||||
['test'],
|
||||
);
|
||||
|
||||
const { hashtagsInBar, statusContentProps } =
|
||||
computeHashtagBarForStatus(status);
|
||||
|
||||
expect(hashtagsInBar).toEqual([]);
|
||||
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
|
||||
`"this is a #<a class="zrl" href="https://example.com/search?tag=test">test</a>. Some more text"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('extract tags from the last line', () => {
|
||||
const status = createStatus(
|
||||
'<p>Simple text</p><p><a href="test">#hashtag</a></p>',
|
||||
|
|
|
@ -9,11 +9,12 @@ import api from 'mastodon/api';
|
|||
import { roundTo10 } from 'mastodon/utils/numbers';
|
||||
|
||||
const dateForCohort = cohort => {
|
||||
const timeZone = 'UTC';
|
||||
switch(cohort.frequency) {
|
||||
case 'day':
|
||||
return <FormattedDate value={cohort.period} month='long' day='2-digit' />;
|
||||
return <FormattedDate value={cohort.period} month='long' day='2-digit' timeZone={timeZone} />;
|
||||
default:
|
||||
return <FormattedDate value={cohort.period} month='long' year='numeric' />;
|
||||
return <FormattedDate value={cohort.period} month='long' year='numeric' timeZone={timeZone} />;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -22,12 +22,6 @@ export default class Column extends PureComponent {
|
|||
scrollable = document.scrollingElement;
|
||||
} else {
|
||||
scrollable = this.node.querySelector('.scrollable');
|
||||
|
||||
// Some columns have nested `.scrollable` containers, with the outer one
|
||||
// being a wrapper while the actual scrollable content is deeper.
|
||||
if (scrollable.classList.contains('scrollable--flex')) {
|
||||
scrollable = scrollable?.querySelector('.scrollable') || scrollable;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scrollable) {
|
||||
|
|
|
@ -109,7 +109,7 @@ export function computeHashtagBarForStatus(status: StatusLike): {
|
|||
|
||||
const lastChild = template.content.lastChild;
|
||||
|
||||
if (!lastChild) return defaultResult;
|
||||
if (!lastChild || lastChild.nodeType === Node.TEXT_NODE) return defaultResult;
|
||||
|
||||
template.content.removeChild(lastChild);
|
||||
const contentWithoutLastLine = template;
|
||||
|
|
|
@ -73,7 +73,7 @@ class ScrollableList extends PureComponent {
|
|||
const clientHeight = this.getClientHeight();
|
||||
const offset = scrollHeight - scrollTop - clientHeight;
|
||||
|
||||
if (400 > offset && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading) {
|
||||
if (scrollTop > 0 && offset < 400 && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading) {
|
||||
this.props.onLoadMore();
|
||||
}
|
||||
|
||||
|
|
|
@ -67,47 +67,45 @@ class Explore extends PureComponent {
|
|||
<Search />
|
||||
</div>
|
||||
|
||||
<div className='scrollable scrollable--flex' data-nosnippet>
|
||||
{isSearching ? (
|
||||
<SearchResults />
|
||||
) : (
|
||||
<>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/explore'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_statuses' defaultMessage='Posts' />
|
||||
{isSearching ? (
|
||||
<SearchResults />
|
||||
) : (
|
||||
<>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/explore'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_statuses' defaultMessage='Posts' />
|
||||
</NavLink>
|
||||
|
||||
<NavLink exact to='/explore/tags'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_tags' defaultMessage='Hashtags' />
|
||||
</NavLink>
|
||||
|
||||
{signedIn && (
|
||||
<NavLink exact to='/explore/suggestions'>
|
||||
<FormattedMessage tagName='div' id='explore.suggested_follows' defaultMessage='People' />
|
||||
</NavLink>
|
||||
)}
|
||||
|
||||
<NavLink exact to='/explore/tags'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_tags' defaultMessage='Hashtags' />
|
||||
</NavLink>
|
||||
<NavLink exact to='/explore/links'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_links' defaultMessage='News' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
{signedIn && (
|
||||
<NavLink exact to='/explore/suggestions'>
|
||||
<FormattedMessage tagName='div' id='explore.suggested_follows' defaultMessage='People' />
|
||||
</NavLink>
|
||||
)}
|
||||
<Switch>
|
||||
<Route path='/explore/tags' component={Tags} />
|
||||
<Route path='/explore/links' component={Links} />
|
||||
<Route path='/explore/suggestions' component={Suggestions} />
|
||||
<Route exact path={['/explore', '/explore/posts', '/search']}>
|
||||
<Statuses multiColumn={multiColumn} />
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
<NavLink exact to='/explore/links'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_links' defaultMessage='News' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<Switch>
|
||||
<Route path='/explore/tags' component={Tags} />
|
||||
<Route path='/explore/links' component={Links} />
|
||||
<Route path='/explore/suggestions' component={Suggestions} />
|
||||
<Route exact path={['/explore', '/explore/posts', '/search']}>
|
||||
<Statuses multiColumn={multiColumn} />
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content={isSearching ? 'noindex' : 'all'} />
|
||||
</Helmet>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content={isSearching ? 'noindex' : 'all'} />
|
||||
</Helmet>
|
||||
</>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ class Links extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='explore__links'>
|
||||
<div className='explore__links scrollable' data-nosnippet>
|
||||
{banner}
|
||||
|
||||
{isLoading ? (<LoadingIndicator />) : links.map((link, i) => (
|
||||
|
|
|
@ -204,7 +204,7 @@ class Results extends PureComponent {
|
|||
<button onClick={this.handleSelectStatuses} className={type === 'statuses' ? 'active' : undefined}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
|
||||
</div>
|
||||
|
||||
<div className='explore__search-results'>
|
||||
<div className='explore__search-results' data-nosnippet>
|
||||
<ScrollableList
|
||||
scrollKey='search-results'
|
||||
isLoading={isLoading}
|
||||
|
|
|
@ -42,7 +42,7 @@ class Suggestions extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='explore__suggestions'>
|
||||
<div className='explore__suggestions scrollable' data-nosnippet>
|
||||
{isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => (
|
||||
<AccountCard key={suggestion.get('account')} id={suggestion.get('account')} />
|
||||
))}
|
||||
|
|
|
@ -51,7 +51,7 @@ class Tags extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='explore__links'>
|
||||
<div className='scrollable explore__links' data-nosnippet>
|
||||
{banner}
|
||||
|
||||
{isLoading ? (<LoadingIndicator />) : hashtags.map(hashtag => (
|
||||
|
|
|
@ -169,32 +169,30 @@ const Firehose = ({ feedType, multiColumn }) => {
|
|||
<ColumnSettings />
|
||||
</ColumnHeader>
|
||||
|
||||
<div className='scrollable scrollable--flex'>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/public/local'>
|
||||
<FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
|
||||
</NavLink>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/public/local'>
|
||||
<FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
|
||||
</NavLink>
|
||||
|
||||
<NavLink exact to='/public/remote'>
|
||||
<FormattedMessage tagName='div' id='firehose.remote' defaultMessage='Other servers' />
|
||||
</NavLink>
|
||||
<NavLink exact to='/public/remote'>
|
||||
<FormattedMessage tagName='div' id='firehose.remote' defaultMessage='Other servers' />
|
||||
</NavLink>
|
||||
|
||||
<NavLink exact to='/public'>
|
||||
<FormattedMessage tagName='div' id='firehose.all' defaultMessage='All' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<StatusListContainer
|
||||
prepend={prependBanner}
|
||||
timelineId={`${feedType}${onlyMedia ? ':media' : ''}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
trackScroll
|
||||
scrollKey='firehose'
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
/>
|
||||
<NavLink exact to='/public'>
|
||||
<FormattedMessage tagName='div' id='firehose.all' defaultMessage='All' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<StatusListContainer
|
||||
prepend={prependBanner}
|
||||
timelineId={`${feedType}${onlyMedia ? ':media' : ''}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
trackScroll
|
||||
scrollKey='firehose'
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
/>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='noindex' />
|
||||
|
|
|
@ -39,8 +39,17 @@ const getHomeFeedSpeed = createSelector([
|
|||
], (statusIds, pendingStatusIds, statusMap) => {
|
||||
const recentStatusIds = pendingStatusIds.size > 0 ? pendingStatusIds : statusIds;
|
||||
const statuses = recentStatusIds.filter(id => id !== null).map(id => statusMap.get(id)).filter(status => status?.get('account') !== me).take(20);
|
||||
const oldest = new Date(statuses.getIn([statuses.size - 1, 'created_at'], 0));
|
||||
const newest = new Date(statuses.getIn([0, 'created_at'], 0));
|
||||
|
||||
if (statuses.isEmpty()) {
|
||||
return {
|
||||
gap: 0,
|
||||
newest: new Date(0),
|
||||
};
|
||||
}
|
||||
|
||||
const datetimes = statuses.map(status => status.get('created_at', 0));
|
||||
const oldest = new Date(datetimes.min());
|
||||
const newest = new Date(datetimes.max());
|
||||
const averageGap = (newest - oldest) / (1000 * (statuses.size + 1)); // Average gap between posts on first page in seconds
|
||||
|
||||
return {
|
||||
|
@ -55,8 +64,10 @@ const homeTooSlow = createSelector([
|
|||
getHomeFeedSpeed,
|
||||
], (isLoading, isPartial, speed) =>
|
||||
!isLoading && !isPartial // Only if the home feed has finished loading
|
||||
&& (speed.gap > (30 * 60) // If the average gap between posts is more than 20 minutes
|
||||
|| (Date.now() - speed.newest) > (1000 * 3600)) // If the most recent post is from over an hour ago
|
||||
&& (
|
||||
(speed.gap > (30 * 60) // If the average gap between posts is more than 30 minutes
|
||||
|| (Date.now() - speed.newest) > (1000 * 3600)) // If the most recent post is from over an hour ago
|
||||
)
|
||||
);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
|
|
@ -220,6 +220,8 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
componentDidMount () {
|
||||
attachFullscreenListener(this.onFullScreenChange);
|
||||
|
||||
this._scrollStatusIntoView();
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||
|
@ -579,10 +581,10 @@ class Status extends ImmutablePureComponent {
|
|||
this.node = c;
|
||||
};
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { status, ancestorsIds, multiColumn } = this.props;
|
||||
_scrollStatusIntoView () {
|
||||
const { status, multiColumn } = this.props;
|
||||
|
||||
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) {
|
||||
if (status) {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.node?.querySelector('.detailed-status__wrapper')?.scrollIntoView(true);
|
||||
|
||||
|
@ -599,6 +601,14 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { status, ancestorsIds } = this.props;
|
||||
|
||||
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) {
|
||||
this._scrollStatusIntoView();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
detachFullscreenListener(this.onFullScreenChange);
|
||||
}
|
||||
|
@ -607,6 +617,22 @@ class Status extends ImmutablePureComponent {
|
|||
this.setState({ fullscreen: isFullscreen() });
|
||||
};
|
||||
|
||||
shouldUpdateScroll = (prevRouterProps, { location }) => {
|
||||
// Do not change scroll when opening a modal
|
||||
if (location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scroll to focused post if it is loaded
|
||||
const child = this.node?.querySelector('.detailed-status__wrapper');
|
||||
if (child) {
|
||||
return [0, child.offsetTop];
|
||||
}
|
||||
|
||||
// Do not scroll otherwise, `componentDidUpdate` will take care of that
|
||||
return false;
|
||||
};
|
||||
|
||||
render () {
|
||||
let ancestors, descendants;
|
||||
const { isLoading, status, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
|
||||
|
@ -660,7 +686,7 @@ class Status extends ImmutablePureComponent {
|
|||
)}
|
||||
/>
|
||||
|
||||
<ScrollContainer scrollKey='thread'>
|
||||
<ScrollContainer scrollKey='thread' shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
|
||||
{ancestors}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ class LinkFooter extends PureComponent {
|
|||
{DividingCircle}
|
||||
<a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
|
||||
{DividingCircle}
|
||||
v{version}
|
||||
<span class='version'>v{version}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
"account.domain_blocked": "Блокиран домейн",
|
||||
"account.edit_profile": "Редактиране на профила",
|
||||
"account.enable_notifications": "Известяване при публикуване от @{name}",
|
||||
"account.endorse": "Характеристика на профила",
|
||||
"account.endorse": "Представи в профила",
|
||||
"account.featured_tags.last_status_at": "Последна публикация на {date}",
|
||||
"account.featured_tags.last_status_never": "Няма публикации",
|
||||
"account.featured_tags.title": "Главни хаштагове на {name}",
|
||||
|
@ -393,7 +393,7 @@
|
|||
"media_gallery.toggle_visible": "Скриване на {number, plural, one {изображение} other {изображения}}",
|
||||
"moved_to_account_banner.text": "Вашият акаунт {disabledAccount} сега е изключен, защото се преместихте в {movedToAccount}.",
|
||||
"mute_modal.duration": "Времетраене",
|
||||
"mute_modal.hide_notifications": "Скривате ли известията от потребителя?",
|
||||
"mute_modal.hide_notifications": "Скриване на известия от този потребител?",
|
||||
"mute_modal.indefinite": "Неопределено",
|
||||
"navigation_bar.about": "Относно",
|
||||
"navigation_bar.advanced_interface": "Отваряне в разширен уебинтерфейс",
|
||||
|
|
|
@ -190,6 +190,7 @@
|
|||
"conversation.open": "কথপোকথন দেখান",
|
||||
"conversation.with": "{names} এর সঙ্গে",
|
||||
"copypaste.copied": "অনুলিপিকৃত",
|
||||
"copypaste.copy_to_clipboard": "ক্লিপবোর্ডে কপি করুন",
|
||||
"directory.federated": "পরিচিত ফেডিভারসের থেকে",
|
||||
"directory.local": "শুধু {domain} থেকে",
|
||||
"directory.new_arrivals": "নতুন আগত",
|
||||
|
|
|
@ -321,7 +321,7 @@
|
|||
"interaction_modal.login.action": "Torna a l'inici",
|
||||
"interaction_modal.login.prompt": "Domini del teu servidor domèstic, p.ex. mastodon.social",
|
||||
"interaction_modal.no_account_yet": "No a Mastodon?",
|
||||
"interaction_modal.on_another_server": "En un servidor diferent",
|
||||
"interaction_modal.on_another_server": "A un altre servidor",
|
||||
"interaction_modal.on_this_server": "En aquest servidor",
|
||||
"interaction_modal.sign_in": "No has iniciat sessió en aquest servidor. On tens el teu compte?",
|
||||
"interaction_modal.sign_in_hint": "Ajuda: Aquesta és la web on vas registrar-te. Si no ho recordes, mira el correu electrònic de benvinguda en la teva safata d'entrada. També pots introduïr el teu nom d'usuari complet! (per ex. @Mastodon@mastodon.social)",
|
||||
|
@ -391,7 +391,7 @@
|
|||
"load_pending": "{count, plural, one {# element nou} other {# elements nous}}",
|
||||
"loading_indicator.label": "Es carrega...",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Amaga la imatge} other {Amaga les imatges}}",
|
||||
"moved_to_account_banner.text": "El teu compte {disabledAccount} està actualment desactivat perquè l'has traslladat a {movedToAccount}.",
|
||||
"moved_to_account_banner.text": "El teu compte {disabledAccount} està desactivat perquè l'has mogut a {movedToAccount}.",
|
||||
"mute_modal.duration": "Durada",
|
||||
"mute_modal.hide_notifications": "Amagar les notificacions d'aquest usuari?",
|
||||
"mute_modal.indefinite": "Indefinit",
|
||||
|
@ -426,8 +426,8 @@
|
|||
"notification.admin.sign_up": "{name} s'ha registrat",
|
||||
"notification.favourite": "{name} ha afavorit el teu tut",
|
||||
"notification.follow": "{name} et segueix",
|
||||
"notification.follow_request": "{name} ha sol·licitat seguir-te",
|
||||
"notification.mention": "{name} t'ha mencionat",
|
||||
"notification.follow_request": "{name} ha sol·licitat de seguir-te",
|
||||
"notification.mention": "{name} t'ha esmentat",
|
||||
"notification.own_poll": "La teva enquesta ha finalitzat",
|
||||
"notification.poll": "Ha finalitzat una enquesta en què has votat",
|
||||
"notification.reblog": "{name} t'ha impulsat",
|
||||
|
@ -451,7 +451,7 @@
|
|||
"notifications.column_settings.show": "Mostra a la columna",
|
||||
"notifications.column_settings.sound": "Reprodueix so",
|
||||
"notifications.column_settings.status": "Nous tuts:",
|
||||
"notifications.column_settings.unread_notifications.category": "Notificacions no llegides",
|
||||
"notifications.column_settings.unread_notifications.category": "Notificacions pendents de llegir",
|
||||
"notifications.column_settings.unread_notifications.highlight": "Destaca les notificacions no llegides",
|
||||
"notifications.column_settings.update": "Edicions:",
|
||||
"notifications.filter.all": "Totes",
|
||||
|
@ -473,25 +473,25 @@
|
|||
"onboarding.action.back": "Porta'm enrere",
|
||||
"onboarding.actions.back": "Porta'm enrere",
|
||||
"onboarding.actions.go_to_explore": "Mira què és tendència",
|
||||
"onboarding.actions.go_to_home": "Vés a la teva línia de temps inici",
|
||||
"onboarding.actions.go_to_home": "Ves a la teva línia de temps",
|
||||
"onboarding.compose.template": "Hola Mastodon!",
|
||||
"onboarding.follows.empty": "Malauradament, cap resultat pot ser mostrat ara mateix. Pots provar de fer servir la cerca o visitar la pàgina Explora per a trobar gent a qui seguir o provar-ho de nou més tard.",
|
||||
"onboarding.follows.lead": "Tu tens cura de la teva línia de temps inici. Com més gent segueixis, més activa i interessant serà. Aquests perfils poden ser un bon punt d'inici—sempre pots acabar deixant-los de seguir!",
|
||||
"onboarding.follows.title": "Popular a Mastodon",
|
||||
"onboarding.follows.lead": "La teva línia de temps inici només està a les teves mans. Com més gent segueixis, més activa i interessant serà. Aquests perfils poden ser un bon punt d'inici—sempre pots acabar deixant de seguir-los!:",
|
||||
"onboarding.follows.title": "Personalitza la pantalla d'inci",
|
||||
"onboarding.share.lead": "Permet que la gent sàpiga com trobar-te a Mastodon!",
|
||||
"onboarding.share.message": "Sóc {username} a #Mastodon! Vine i segueix-me a {url}",
|
||||
"onboarding.share.next_steps": "Possibles passes següents:",
|
||||
"onboarding.share.title": "Comparteix el teu perfil",
|
||||
"onboarding.start.lead": "El teu nou compte a Mastodon ja està preparat. Aquí tens com en pots treure tot el suc:",
|
||||
"onboarding.start.lead": "El teu nou compte ja està preparat a Mastodon, la xarxa social on tu—no un algorisme—té tot el control. Aquí tens com en pots treure tot el suc:",
|
||||
"onboarding.start.skip": "Vols saltar-te tota la resta?",
|
||||
"onboarding.start.title": "Llestos!",
|
||||
"onboarding.steps.follow_people.body": "Tu tens cura de la teva línia de temps. Omple-la de gent interessant.",
|
||||
"onboarding.steps.follow_people.title": "{count, plural, zero {No segueixes cap persona} one {Segeueixes ona persona} other {Segueixes # persones}}",
|
||||
"onboarding.steps.publish_status.body": "Saluda el món.",
|
||||
"onboarding.steps.follow_people.body": "Mastodon va de seguir a gent interessant.",
|
||||
"onboarding.steps.follow_people.title": "Personalitza la pantalla d'inci",
|
||||
"onboarding.steps.publish_status.body": "Saluda al món amb text, fotos, vídeos o enquestes {emoji}",
|
||||
"onboarding.steps.publish_status.title": "Fes el teu primer tut",
|
||||
"onboarding.steps.setup_profile.body": "És més fàcil que altres interaccionin amb tu si tens un perfil complet.",
|
||||
"onboarding.steps.setup_profile.body": "És més fàcil que altres interactuïn amb tu si tens un perfil complet.",
|
||||
"onboarding.steps.setup_profile.title": "Personalitza el perfil",
|
||||
"onboarding.steps.share_profile.body": "Permet als teus amics de saber com trobar-te a Mastodon!",
|
||||
"onboarding.steps.share_profile.body": "Fer saber als teus amics com trobar-te a Mastodon",
|
||||
"onboarding.steps.share_profile.title": "Comparteix el teu perfil",
|
||||
"onboarding.tips.2fa": "<strong>Ho sabies?</strong> Pots securitzar el teu compte activant l'autenticació de doble factor en la configuració del teu perfil. Funciona amb qualsevol aplicació TOTP de la teva elecció, no cal número de telèfon!",
|
||||
"onboarding.tips.accounts_from_other_servers": "<strong>Ho sabies?</strong> Com Mastodon és descentralitzat, et pots trobar amb perfils que són a servidors diferents del teu. I, tanmateix, també hi pots interactuar sense cap problema! El servidor és la segona part del seu nom d'usuari!",
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
"column.directory": "Prozkoumat profily",
|
||||
"column.domain_blocks": "Blokované domény",
|
||||
"column.favourites": "Oblíbené",
|
||||
"column.firehose": "Živé kanály l",
|
||||
"column.firehose": "Živé kanály",
|
||||
"column.follow_requests": "Žádosti o sledování",
|
||||
"column.home": "Domů",
|
||||
"column.lists": "Seznamy",
|
||||
|
@ -585,6 +585,7 @@
|
|||
"search.quick_action.open_url": "Otevřít URL v Mastodonu",
|
||||
"search.quick_action.status_search": "Příspěvky odpovídající {x}",
|
||||
"search.search_or_paste": "Hledat nebo vložit URL",
|
||||
"search_popout.full_text_search_disabled_message": "Nedostupné na {domain}.",
|
||||
"search_popout.language_code": "Kód jazyka podle ISO",
|
||||
"search_popout.options": "Možnosti hledání",
|
||||
"search_popout.quick_actions": "Rychlé akce",
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
"compose_form.hashtag_warning": "Dieser Beitrag wird unter keinem Hashtag sichtbar sein, weil er nicht öffentlich ist. Nur öffentliche Beiträge können nach Hashtags durchsucht werden.",
|
||||
"compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Andere können dir folgen und deine Beiträge sehen, die nur für Follower bestimmt sind.",
|
||||
"compose_form.lock_disclaimer.lock": "geschützt",
|
||||
"compose_form.placeholder": "Was gibt's Neues?",
|
||||
"compose_form.placeholder": "Was gibt’s Neues?",
|
||||
"compose_form.poll.add_option": "Auswahl",
|
||||
"compose_form.poll.duration": "Umfragedauer",
|
||||
"compose_form.poll.option_placeholder": "{number}. Auswahl",
|
||||
|
@ -286,7 +286,7 @@
|
|||
"footer.source_code": "Quellcode anzeigen",
|
||||
"footer.status": "Status",
|
||||
"generic.saved": "Gespeichert",
|
||||
"getting_started.heading": "Auf geht's!",
|
||||
"getting_started.heading": "Auf geht’s!",
|
||||
"hashtag.column_header.tag_mode.all": "und {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "oder {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "ohne {additional}",
|
||||
|
@ -360,7 +360,7 @@
|
|||
"keyboard_shortcuts.requests": "Liste der Follower-Anfragen aufrufen",
|
||||
"keyboard_shortcuts.search": "Suchleiste fokussieren",
|
||||
"keyboard_shortcuts.spoilers": "Feld für Inhaltswarnung anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.start": "„Auf geht's!“ öffnen",
|
||||
"keyboard_shortcuts.start": "„Auf geht’s!“ öffnen",
|
||||
"keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen/ausblenden",
|
||||
"keyboard_shortcuts.toot": "Neuen Beitrag erstellen",
|
||||
|
@ -383,7 +383,7 @@
|
|||
"lists.new.create": "Neue Liste erstellen",
|
||||
"lists.new.title_placeholder": "Titel der neuen Liste",
|
||||
"lists.replies_policy.followed": "Alle folgenden Profile",
|
||||
"lists.replies_policy.list": "Mitglieder*innen der Liste",
|
||||
"lists.replies_policy.list": "Mitglieder der Liste",
|
||||
"lists.replies_policy.none": "Niemanden",
|
||||
"lists.replies_policy.title": "Antworten anzeigen für:",
|
||||
"lists.search": "Suche nach Leuten, denen du folgst",
|
||||
|
|
|
@ -590,6 +590,7 @@
|
|||
"search.quick_action.open_url": "Open URL in Mastodon",
|
||||
"search.quick_action.status_search": "Posts matching {x}",
|
||||
"search.search_or_paste": "Search or paste URL",
|
||||
"search_popout.full_text_search_disabled_message": "Unavailable on {domain}.",
|
||||
"search_popout.language_code": "ISO language code",
|
||||
"search_popout.options": "Search options",
|
||||
"search_popout.quick_actions": "Quick actions",
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
"column.direct": "Privataj mencioj",
|
||||
"column.directory": "Foliumi la profilojn",
|
||||
"column.domain_blocks": "Blokitaj domajnoj",
|
||||
"column.favourites": "Stelumoj",
|
||||
"column.firehose": "Vivantaj fluoj",
|
||||
"column.follow_requests": "Petoj de sekvado",
|
||||
"column.home": "Hejmo",
|
||||
|
@ -136,6 +137,7 @@
|
|||
"compose.language.search": "Serĉi lingvojn...",
|
||||
"compose.published.body": "Afiŝo publikigita.",
|
||||
"compose.published.open": "Malfermi",
|
||||
"compose.saved.body": "Afiŝo konservita.",
|
||||
"compose_form.direct_message_warning_learn_more": "Lerni pli",
|
||||
"compose_form.encryption_warning": "La afiŝoj en Mastodon ne estas tutvoje ĉifritaj. Ne kunhavigu tiklajn informojn ĉe Mastodon.",
|
||||
"compose_form.hashtag_warning": "Ĉi tiu afiŝo ne estos listigita en neniu kradvorto ĉar ĝi ne estas publika. Nur publikaj afiŝoj povas esti serĉitaj per kradvortoj.",
|
||||
|
@ -180,6 +182,7 @@
|
|||
"confirmations.mute.explanation": "Tio kaŝos la mesaĝojn de la uzanto kaj la mesaĝojn kiuj mencias rin, sed ri ankoraŭ rajtos vidi viajn mesaĝojn kaj sekvi vin.",
|
||||
"confirmations.mute.message": "Ĉu vi certas, ke vi volas silentigi {name}?",
|
||||
"confirmations.redraft.confirm": "Forigi kaj reskribi",
|
||||
"confirmations.redraft.message": "Ĉu vi certas ke vi volas forigi tiun afiŝon kaj reskribi ĝin? Ĉiuj diskonigoj kaj stelumoj estos perditaj, kaj respondoj al la originala mesaĝo estos senparentaj.",
|
||||
"confirmations.reply.confirm": "Respondi",
|
||||
"confirmations.reply.message": "Respondi nun anstataŭigos la skribatan afiŝon. Ĉu vi certas, ke vi volas daŭrigi?",
|
||||
"confirmations.unfollow.confirm": "Ne plu sekvi",
|
||||
|
@ -548,6 +551,7 @@
|
|||
"search.search_or_paste": "Serĉu aŭ algluu URL-on",
|
||||
"search_popout.quick_actions": "Rapidaj agoj",
|
||||
"search_popout.recent": "Lastaj serĉoj",
|
||||
"search_popout.user": "uzanto",
|
||||
"search_results.accounts": "Profiloj",
|
||||
"search_results.all": "Ĉiuj",
|
||||
"search_results.hashtags": "Kradvortoj",
|
||||
|
|
|
@ -302,8 +302,8 @@
|
|||
"hashtag.follow": "Seguir etiqueta",
|
||||
"hashtag.unfollow": "Dejar de seguir etiqueta",
|
||||
"hashtags.and_other": "…y {count, plural, other {# más}}",
|
||||
"home.actions.go_to_explore": "Mirá qué está en tendencia",
|
||||
"home.actions.go_to_suggestions": "Encontrá cuentas para seguir",
|
||||
"home.actions.go_to_explore": "Ver qué está en tendencia",
|
||||
"home.actions.go_to_suggestions": "Encontrar cuentas para seguir",
|
||||
"home.column_settings.basic": "Básico",
|
||||
"home.column_settings.show_reblogs": "Mostrar adhesiones",
|
||||
"home.column_settings.show_replies": "Mostrar respuestas",
|
||||
|
|
|
@ -379,7 +379,7 @@
|
|||
"lists.delete": "Borrar lista",
|
||||
"lists.edit": "Editar lista",
|
||||
"lists.edit.submit": "Cambiar título",
|
||||
"lists.exclusive": "Ocultar estas publicaciones en inicio",
|
||||
"lists.exclusive": "Ocultar estas publicaciones de inicio",
|
||||
"lists.new.create": "Añadir lista",
|
||||
"lists.new.title_placeholder": "Título de la nueva lista",
|
||||
"lists.replies_policy.followed": "Cualquier usuario seguido",
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
"account.add_or_remove_from_list": "افزودن یا برداشتن از سیاههها",
|
||||
"account.badges.bot": "روبات",
|
||||
"account.badges.group": "گروه",
|
||||
"account.block": "مسدود کردن @{name}",
|
||||
"account.block_domain": "مسدود کردن دامنهٔ {domain}",
|
||||
"account.block": "انسداد @{name}",
|
||||
"account.block_domain": "انسداد دامنهٔ {domain}",
|
||||
"account.block_short": "انسداد",
|
||||
"account.blocked": "مسدود شده",
|
||||
"account.blocked": "مسدود",
|
||||
"account.browse_more_on_origin_server": "مرور بیشتر روی نمایهٔ اصلی",
|
||||
"account.cancel_follow_request": "رد کردن درخواست پیگیری",
|
||||
"account.direct": "خصوصی از @{name} نام ببرید",
|
||||
"account.direct": "اشارهٔ خصوصی به @{name}",
|
||||
"account.disable_notifications": "آگاه کردن من هنگام فرستههای @{name} را متوقّف کن",
|
||||
"account.domain_blocked": "دامنه مسدود شد",
|
||||
"account.edit_profile": "ویرایش نمایه",
|
||||
|
@ -46,7 +46,7 @@
|
|||
"account.link_verified_on": "مالکیت این پیوند در {date} بررسی شد",
|
||||
"account.locked_info": "این حساب خصوصی است. صاحبش تصمیم میگیرد که چه کسی پیگیرش باشد.",
|
||||
"account.media": "رسانه",
|
||||
"account.mention": "نامبردن از @{name}",
|
||||
"account.mention": "اشاره به @{name}",
|
||||
"account.moved_to": "{name} نشان داده که حساب جدیدش این است:",
|
||||
"account.mute": "خموشاندن @{name}",
|
||||
"account.mute_notifications_short": "خموشی آگاهیها",
|
||||
|
@ -110,7 +110,7 @@
|
|||
"column.blocks": "کاربران مسدود شده",
|
||||
"column.bookmarks": "نشانکها",
|
||||
"column.community": "خط زمانی محلّی",
|
||||
"column.direct": "خصوصی نام ببرید",
|
||||
"column.direct": "اشارههای خصوصی",
|
||||
"column.directory": "مرور نمایهها",
|
||||
"column.domain_blocks": "دامنههای مسدود شده",
|
||||
"column.favourites": "برگزیدهها",
|
||||
|
@ -137,6 +137,7 @@
|
|||
"compose.language.search": "جستوجوی زبانها…",
|
||||
"compose.published.body": "فرسته منتشر شد.",
|
||||
"compose.published.open": "گشودن",
|
||||
"compose.saved.body": "فرسته ذخیره شد.",
|
||||
"compose_form.direct_message_warning_learn_more": "بیشتر بدانید",
|
||||
"compose_form.encryption_warning": "فرستههای ماستودون رمزگذاری سرتاسری نشدهاند. هیچ اطّلاعات حساسی را روی ماستودون همرسانی نکنید.",
|
||||
"compose_form.hashtag_warning": "از آنجا که این فرسته عمومی نیست زیر هیچ برچسبی سیاهه نخواهد شد. تنها فرستههای عمومی میتوانند با برچسب جستوجو شوند.",
|
||||
|
@ -147,7 +148,7 @@
|
|||
"compose_form.poll.duration": "مدت نظرسنجی",
|
||||
"compose_form.poll.option_placeholder": "گزینهٔ {number}",
|
||||
"compose_form.poll.remove_option": "برداشتن این گزینه",
|
||||
"compose_form.poll.switch_to_multiple": "تبدیل به نظرسنجی چندگزینهای",
|
||||
"compose_form.poll.switch_to_multiple": "تغییر نظرسنجی برای اجازه به چندین گزینه",
|
||||
"compose_form.poll.switch_to_single": "تبدیل به نظرسنجی تکگزینهای",
|
||||
"compose_form.publish": "انتشار",
|
||||
"compose_form.publish_form": "انتشار",
|
||||
|
@ -160,8 +161,8 @@
|
|||
"compose_form.spoiler.unmarked": "افزودن هشدار محتوا",
|
||||
"compose_form.spoiler_placeholder": "هشدارتان را اینجا بنویسید",
|
||||
"confirmation_modal.cancel": "لغو",
|
||||
"confirmations.block.block_and_report": "مسدود کردن و گزارش",
|
||||
"confirmations.block.confirm": "مسدود کردن",
|
||||
"confirmations.block.block_and_report": "انسداد و گزارش",
|
||||
"confirmations.block.confirm": "انسداد",
|
||||
"confirmations.block.message": "مطمئنید که میخواهید {name} را مسدود کنید؟",
|
||||
"confirmations.cancel_follow_request.confirm": "رد کردن درخواست",
|
||||
"confirmations.cancel_follow_request.message": "مطمئنید که می خواهید درخواست پیگیری {name} را لغو کنید؟",
|
||||
|
@ -171,7 +172,7 @@
|
|||
"confirmations.delete_list.message": "مطمئنید میخواهید این سیاهه را برای همیشه حذف کنید؟",
|
||||
"confirmations.discard_edit_media.confirm": "دور انداختن",
|
||||
"confirmations.discard_edit_media.message": "تغییرات ذخیره نشدهای در توضیحات یا پیشنمایش رسانه دارید. همگی نادیده گرفته شوند؟",
|
||||
"confirmations.domain_block.confirm": "مسدود کردن تمام دامنه",
|
||||
"confirmations.domain_block.confirm": "انسداد تمام دامنه",
|
||||
"confirmations.domain_block.message": "آیا جدی جدی میخواهید تمام دامنهٔ {domain} را مسدود کنید؟ در بیشتر موارد مسدود کردن یا خموشاندن چند حساب خاص کافی است و توصیه میشود. پس از این کار شما هیچ محتوایی را از این دامنه در خط زمانی عمومی یا آگاهیهایتان نخواهید دید. پیگیرانتان از این دامنه هم برداشته خواهند شد.",
|
||||
"confirmations.edit.confirm": "ویرایش",
|
||||
"confirmations.edit.message": "در صورت ویرایش، پیامی که در حال نوشتنش بودید از بین خواهد رفت. میخواهید ادامه دهید؟",
|
||||
|
@ -181,6 +182,7 @@
|
|||
"confirmations.mute.explanation": "این کار فرستههای آنها و فرستههایی را که از آنها نام برده پنهان میکند، ولی آنها همچنان اجازه دارند فرستههای شما را ببینند و شما را پیگیری کنند.",
|
||||
"confirmations.mute.message": "مطمئنید میخواهید {name} را بخموشانید؟",
|
||||
"confirmations.redraft.confirm": "حذف و بازنویسی",
|
||||
"confirmations.redraft.message": "مطمئنید که میخواهید این فرسته را حذف کنید و از نو بنویسید؟ با این کار تقویتها و پسندهایش از دست رفته و پاسخها به آن بیمرجع میشود.",
|
||||
"confirmations.reply.confirm": "پاسخ",
|
||||
"confirmations.reply.message": "اگر الان پاسخ دهید، چیزی که در حال نوشتنش بودید پاک خواهد شد. میخواهید ادامه دهید؟",
|
||||
"confirmations.unfollow.confirm": "پینگرفتن",
|
||||
|
@ -294,16 +296,23 @@
|
|||
"hashtag.column_settings.tag_mode.any": "هرکدام از اینها",
|
||||
"hashtag.column_settings.tag_mode.none": "هیچکدام از اینها",
|
||||
"hashtag.column_settings.tag_toggle": "افزودن برچسبهایی بیشتر به این ستون",
|
||||
"hashtag.counter_by_accounts": "{count, plural, one {{counter} مشارکت کننده} other {{counter} مشارکت کننده}}",
|
||||
"hashtag.counter_by_uses": "{count, plural, one {{counter} فرسته} other {{counter} فرسته}}",
|
||||
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} فرسته} other {{counter} فرسته}} امروز",
|
||||
"hashtag.follow": "پیگرفتن برچسب",
|
||||
"hashtag.unfollow": "پینگرفتن برچسب",
|
||||
"hashtags.and_other": "…و {count, plural, other {# بیشتر}}",
|
||||
"home.actions.go_to_explore": "ببینید چه داغ است",
|
||||
"home.actions.go_to_suggestions": "یافتن افراد برای پیگیری",
|
||||
"home.column_settings.basic": "پایهای",
|
||||
"home.column_settings.show_reblogs": "نمایش تقویتها",
|
||||
"home.column_settings.show_replies": "نمایش پاسخها",
|
||||
"home.explore_prompt.body": "خوراک خانگیتان ترکیبی از فرستهها از برچسبهایی که برای پیگیری گزیدهاید، افرادی که پی میگیرید و فرستههایی که تقویت میکنند را خواهد داشت. اگر خیلی خلوت به نظر میرسد، شاید بخواهید:",
|
||||
"home.explore_prompt.title": "این پایگاه خانگیتان در ماستودون است.",
|
||||
"home.hide_announcements": "نهفتن اعلامیهها",
|
||||
"home.pending_critical_update.body": "لطفاً کارساز ماستودونتان را در نخستین فرصت بهروز کنید!",
|
||||
"home.pending_critical_update.link": "دیدن بهروز رسانیها",
|
||||
"home.pending_critical_update.title": "بهروز رسانی امنیتی بحرانی موجود است!",
|
||||
"home.show_announcements": "نمایش اعلامیهها",
|
||||
"interaction_modal.description.favourite": "با حسابی روی ماستودون میتوانید این فرسته را برگزیده تا نگارنده بداند قدردانش هستید و برای آینده ذخیرهاش میکنید.",
|
||||
"interaction_modal.description.follow": "با حسابی روی ماستودون میتوانید {name} را برای دریافت فرستههایش در خوراک خانگیتان دنبال کنید.",
|
||||
|
@ -314,6 +323,8 @@
|
|||
"interaction_modal.no_account_yet": "در ماستودون نیست؟",
|
||||
"interaction_modal.on_another_server": "روی کارسازی دیگر",
|
||||
"interaction_modal.on_this_server": "روی این کارساز",
|
||||
"interaction_modal.sign_in": "شما در این کارساز وارد نشدهاید. حسابتان کجا میزبانی شده؟",
|
||||
"interaction_modal.sign_in_hint": "نکته: میزبانتان، پایگاه وبیست که رویش ثبتنام کردهاید. اگر به خاطر نمیآورید، به رایانامهٔ خوشآمد در صندوق ورودیتان بنگرید. همچنین میتوانید نام کاربری کاملتان (چون @Mastodon@mastodon.social) را وارد کنید!",
|
||||
"interaction_modal.title.favourite": "فرستههای برگزیدهٔ {name}",
|
||||
"interaction_modal.title.follow": "پیگیری {name}",
|
||||
"interaction_modal.title.reblog": "تقویت فرستهٔ {name}",
|
||||
|
@ -327,9 +338,10 @@
|
|||
"keyboard_shortcuts.column": "برای تمرکز روی یک فرسته در یکی از ستونها",
|
||||
"keyboard_shortcuts.compose": "تمرکز روی محیط نوشتن",
|
||||
"keyboard_shortcuts.description": "توضیح",
|
||||
"keyboard_shortcuts.direct": "باز کردن ستون اشارههای خصوصی",
|
||||
"keyboard_shortcuts.direct": "برای گشودن ستون اشارههای خصوصی",
|
||||
"keyboard_shortcuts.down": "پایین بردن در سیاهه",
|
||||
"keyboard_shortcuts.enter": "گشودن فرسته",
|
||||
"keyboard_shortcuts.favourite": "پسندیدن فرسته",
|
||||
"keyboard_shortcuts.favourites": "گشودن فهرست برگزیدهها",
|
||||
"keyboard_shortcuts.federated": "گشودن خط زمانی همگانی",
|
||||
"keyboard_shortcuts.heading": "میانبرهای صفحهکلید",
|
||||
|
@ -337,7 +349,7 @@
|
|||
"keyboard_shortcuts.hotkey": "میانبر",
|
||||
"keyboard_shortcuts.legend": "نمایش این نشانه",
|
||||
"keyboard_shortcuts.local": "گشودن خط زمانی محلّی",
|
||||
"keyboard_shortcuts.mention": "نامبردن نویسنده",
|
||||
"keyboard_shortcuts.mention": "اشاره به نویسنده",
|
||||
"keyboard_shortcuts.muted": "گشودن فهرست کاربران خموش",
|
||||
"keyboard_shortcuts.my_profile": "گشودن نمایهتان",
|
||||
"keyboard_shortcuts.notifications": "گشودن ستون آگاهیها",
|
||||
|
@ -361,7 +373,7 @@
|
|||
"lightbox.previous": "قبلی",
|
||||
"limited_account_hint.action": "به هر روی نمایه نشان داده شود",
|
||||
"limited_account_hint.title": "این نمایه از سوی ناظمهای {domain} پنهان شده.",
|
||||
"link_preview.author": "بر اساس {name}",
|
||||
"link_preview.author": "از {name}",
|
||||
"lists.account.add": "افزودن به سیاهه",
|
||||
"lists.account.remove": "برداشتن از سیاهه",
|
||||
"lists.delete": "حذف سیاهه",
|
||||
|
@ -402,6 +414,7 @@
|
|||
"navigation_bar.lists": "سیاههها",
|
||||
"navigation_bar.logout": "خروج",
|
||||
"navigation_bar.mutes": "کاربران خموشانده",
|
||||
"navigation_bar.opened_in_classic_interface": "فرستهها، حسابها و دیگر صفحههای خاص به طور پیشگزیده در میانای وب کلاسیک گشوده میشوند.",
|
||||
"navigation_bar.personal": "شخصی",
|
||||
"navigation_bar.pins": "فرستههای سنجاق شده",
|
||||
"navigation_bar.preferences": "ترجیحات",
|
||||
|
@ -411,11 +424,11 @@
|
|||
"not_signed_in_indicator.not_signed_in": "برای دسترسی به این منبع باید وارد شوید.",
|
||||
"notification.admin.report": "{name}، {target} را گزارش داد",
|
||||
"notification.admin.sign_up": "{name} ثبت نام کرد",
|
||||
"notification.favourite": "{name} نوشتهٔ شما را پسندید",
|
||||
"notification.favourite": "{name} فرستهتان را برگزید",
|
||||
"notification.follow": "{name} پیگیرتان شد",
|
||||
"notification.follow_request": "{name} میخواهد پیگیر شما باشد",
|
||||
"notification.mention": "{name} از شما نام برد",
|
||||
"notification.own_poll": "نظرسنجی شما به پایان رسید",
|
||||
"notification.follow_request": "{name} درخواست پیگیریتان را داد",
|
||||
"notification.mention": "{name} به شما اشاره کرد",
|
||||
"notification.own_poll": "نظرسنجیتان پایان یافت",
|
||||
"notification.poll": "نظرسنجیای که در آن رأی دادید به پایان رسیده است",
|
||||
"notification.reblog": "{name} فرستهتان را تقویت کرد",
|
||||
"notification.status": "{name} چیزی فرستاد",
|
||||
|
@ -431,7 +444,7 @@
|
|||
"notifications.column_settings.filter_bar.show_bar": "نمایش نوار پالایه",
|
||||
"notifications.column_settings.follow": "پیگیرندگان جدید:",
|
||||
"notifications.column_settings.follow_request": "درخواستهای جدید پیگیری:",
|
||||
"notifications.column_settings.mention": "نامبردنها:",
|
||||
"notifications.column_settings.mention": "اشارهها:",
|
||||
"notifications.column_settings.poll": "نتایج نظرسنجی:",
|
||||
"notifications.column_settings.push": "آگاهیهای ارسالی",
|
||||
"notifications.column_settings.reblog": "تقویتها:",
|
||||
|
@ -445,7 +458,7 @@
|
|||
"notifications.filter.boosts": "تقویتها",
|
||||
"notifications.filter.favourites": "برگزیدهها",
|
||||
"notifications.filter.follows": "پیگرفتگان",
|
||||
"notifications.filter.mentions": "نامبردنها",
|
||||
"notifications.filter.mentions": "اشارهها",
|
||||
"notifications.filter.polls": "نتایج نظرسنجی",
|
||||
"notifications.filter.statuses": "بهروز رسانیها از کسانی که پیگیرشانید",
|
||||
"notifications.grant_permission": "اعطای مجوز.",
|
||||
|
@ -480,10 +493,10 @@
|
|||
"onboarding.steps.setup_profile.title": "Customize your profile",
|
||||
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
|
||||
"onboarding.steps.share_profile.title": "Share your profile",
|
||||
"onboarding.tips.2fa": "<strong>آیا میدانستید؟</strong> شما میتوانید با رفتن به تنظیمات حساب و فعال کردن احراز هویت دوعاملی، حساب خود را ایمن کنید؟ این قابلیت با هر نرمافزار TOTP دلخواه شما کار ميکند و نیازی به شماره تلفن ندارد!",
|
||||
"onboarding.tips.accounts_from_other_servers": "<strong>آیا میدانستید؟</strong> چون ماستودون نامتمرکز است، بعضی از پروفایلهایی که با آنها برخورد میکنید درواقع روی کارساز هایی متفاوت از کارساز شما میزبانی میشوند. و شما همچنان میتوانید با آنها به شکل راحت و روان تعامل کنید! کارساز آنها در نیمه دوم نام کاربریشان است!",
|
||||
"onboarding.tips.migration": "<strong>آیا میدانستید؟</strong> اگر احساس میکنید {domain} انتخاب کارساز خوبی برای آیندهتان نیست، میتوانید بدون از دست دادن پیگیرهایتان به یک کارساز ماستودون دیگر مهاجرت کنید. شما حتی میتوانید کارساز خودتان را میزبانی کنید!",
|
||||
"onboarding.tips.verification": "<strong>آیا میدانستید؟</strong> شما میتوانید حساب خود را با قراردادن پیوندی به نمایه ماستودونتان روی وبسایت خود، و اضافه کردن وبسایتتان به نمایه خود تایید کنید. بدون نیاز به هیچ کارمزد یا سندی!",
|
||||
"onboarding.tips.2fa": "<strong>آیا میدانستید؟</strong> میتوانید با پریایی هویتسنجی دو عاملی در تنظیمات حساب، حسابتان را ایمن کنید؟ این قابلیت با هر نرمافزار TOTP دلخواه کار کرده و نیازی به شماره تلفن ندارد!",
|
||||
"onboarding.tips.accounts_from_other_servers": "<strong>آیا میدانستید؟</strong> از آنجا که ماستودون نامتمرکز است، برخی نمایهها که به آنها برمیخورید روی کارسازهایی متفاوت از شما میزبانی میشوند و باز هم میتوانید بدون مشکل با آنها تعامل داشته باشید! کارسازشان در نیمه دوم نام کاربریشان است!",
|
||||
"onboarding.tips.migration": "<strong>آیا میدانستید؟</strong> اگر احساس میکنید {domain} انتخاب کارساز خوبی برای آیندهتان نیست، میتوانید بدون از دست دادن پیگیرهایتان به کارساز ماستودون دیگری مهاجرت کنید. حتا میتوانید کارساز خودتان را میزبانی کنید!",
|
||||
"onboarding.tips.verification": "<strong>آیا میدانستید؟</strong> میتوانید حسابتان را با گذاشتن پیوندی به نمایهٔ ماستودونتان روی پایگاه وب خود و افزودن پایگاه وبتان به نمایهتان تأیید کنید. بدون نیاز به هیچ کارمزد یا سندی!",
|
||||
"password_confirmation.exceeds_maxlength": "تأییدیه گذرواژه از حداکثر طول گذرواژه بیشتر است",
|
||||
"password_confirmation.mismatching": "تایید گذرواژه با گذرواژه مطابقت ندارد",
|
||||
"picture_in_picture.restore": "برگرداندن",
|
||||
|
@ -523,8 +536,9 @@
|
|||
"relative_time.seconds": "{number} ثانیه",
|
||||
"relative_time.today": "امروز",
|
||||
"reply_indicator.cancel": "لغو",
|
||||
"report.block": "مسدود کردن",
|
||||
"report.block": "انسداد",
|
||||
"report.block_explanation": "شما فرستههایشان را نخواهید دید. آنها نمیتوانند فرستههایتان را ببینند یا شما را پیبگیرند. آنها میتوانند بگویند که مسدود شدهاند.",
|
||||
"report.categories.legal": "حقوقی",
|
||||
"report.categories.other": "غیره",
|
||||
"report.categories.spam": "هرزنامه",
|
||||
"report.categories.violation": "محتوا یک یا چند قانون کارساز را نقض میکند",
|
||||
|
@ -576,12 +590,18 @@
|
|||
"search.quick_action.open_url": "باز کردن پیوند در ماستودون",
|
||||
"search.quick_action.status_search": "فرستههای جور با {x}",
|
||||
"search.search_or_paste": "جستوجو یا جایگذاری نشانی",
|
||||
"search_popout.full_text_search_disabled_message": "روی {domain} موجود نیست.",
|
||||
"search_popout.language_code": "کد زبان ایزو",
|
||||
"search_popout.options": "گزینههای جستوجو",
|
||||
"search_popout.quick_actions": "کنشهای سریع",
|
||||
"search_popout.recent": "جستوجوهای اخیر",
|
||||
"search_popout.specific_date": "تاریخ مشخص",
|
||||
"search_popout.user": "کاربر",
|
||||
"search_results.accounts": "نمایهها",
|
||||
"search_results.all": "همه",
|
||||
"search_results.hashtags": "برچسبها",
|
||||
"search_results.nothing_found": "چیزی برای این عبارت جستوجو یافت نشد",
|
||||
"search_results.see_all": "دیدن همه",
|
||||
"search_results.statuses": "فرستهها",
|
||||
"search_results.title": "جستوجو برای {q}",
|
||||
"server_banner.about_active_users": "افرادی که در ۳۰ روز گذشته از این کارساز استفاده کردهاند (کاربران فعّال ماهانه)",
|
||||
|
@ -597,15 +617,15 @@
|
|||
"status.admin_account": "گشودن واسط مدیریت برای @{name}",
|
||||
"status.admin_domain": "گشودن واسط مدیریت برای {domain}",
|
||||
"status.admin_status": "گشودن این فرسته در واسط مدیریت",
|
||||
"status.block": "مسدود کردن @{name}",
|
||||
"status.block": "انسداد @{name}",
|
||||
"status.bookmark": "نشانک",
|
||||
"status.cancel_reblog_private": "ناتقویت",
|
||||
"status.cannot_reblog": "این فرسته قابل تقویت نیست",
|
||||
"status.copy": "رونوشت از پیوند فرسته",
|
||||
"status.delete": "حذف",
|
||||
"status.detailed_status": "نمایش کامل گفتگو",
|
||||
"status.direct": "خصوصی به @{name} اشاره کنید",
|
||||
"status.direct_indicator": "اشاره خصوصی",
|
||||
"status.direct": "اشارهٔ خصوصی به @{name}",
|
||||
"status.direct_indicator": "اشارهٔ خصوصی",
|
||||
"status.edit": "ویرایش",
|
||||
"status.edited": "ویرایش شده در {date}",
|
||||
"status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد",
|
||||
|
@ -620,7 +640,7 @@
|
|||
"status.media.open": "کلیک برای گشودن",
|
||||
"status.media.show": "کلیک برای نمایش",
|
||||
"status.media_hidden": "رسانهٔ نهفته",
|
||||
"status.mention": "نامبردن از @{name}",
|
||||
"status.mention": "اشاره به @{name}",
|
||||
"status.more": "بیشتر",
|
||||
"status.mute": "خموشاندن @{name}",
|
||||
"status.mute_conversation": "خموشاندن گفتوگو",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"about.blocks": "Moderoidut palvelimet",
|
||||
"about.contact": "Yhteystiedot:",
|
||||
"about.blocks": "Valvotut palvelimet",
|
||||
"about.contact": "Yhteydenotto:",
|
||||
"about.disclaimer": "Mastodon on vapaa avoimen lähdekoodin ohjelmisto ja Mastodon gGmbH:n tavaramerkki.",
|
||||
"about.domain_blocks.no_reason_available": "Syytä ei ole ilmoitettu",
|
||||
"about.domain_blocks.preamble": "Yleisesti Mastodonin avulla voidaan tarkastella minkä tahansa muun fediverse-palvelinten sisältöä ja vuorovaikuttaa eri palvelinten käyttäjien kanssa. Nämä ovat tälle palvelimelle määritetyt poikkeukset.",
|
||||
"about.domain_blocks.preamble": "Mastodonin avulla voidaan yleensä tarkastella minkä tahansa fediversumiin kuuluvan palvelimen sisältöä ja vuorovaikuttaa eri palvelinten käyttäjien kanssa. Nämä ovat tälle palvelimelle määritetyt poikkeukset.",
|
||||
"about.domain_blocks.silenced.explanation": "Et yleensä näe tämän palvelimen profiileja ja sisältöä, jollet erityisesti etsi juuri sitä tai liity siihen seuraamalla.",
|
||||
"about.domain_blocks.silenced.title": "Rajoitettu",
|
||||
"about.domain_blocks.suspended.explanation": "Mitään tämän palvelimen tietoja ei käsitellä, tallenneta tai vaihdeta, mikä tekee vuorovaikutuksesta ja viestinnästä sen käyttäjien kanssa mahdotonta.",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"account.badges.bot": "Botti",
|
||||
"account.badges.group": "Ryhmä",
|
||||
"account.block": "Estä @{name}",
|
||||
"account.block_domain": "Estä palvelu {domain}",
|
||||
"account.block_domain": "Estä verkkotunnus {domain}",
|
||||
"account.block_short": "Estä",
|
||||
"account.blocked": "Estetty",
|
||||
"account.browse_more_on_origin_server": "Selaile lisää alkuperäisellä palvelimella",
|
||||
|
@ -25,11 +25,11 @@
|
|||
"account.disable_notifications": "Lopeta ilmoittamasta minulle, kun @{name} julkaisee",
|
||||
"account.domain_blocked": "Verkkotunnus estetty",
|
||||
"account.edit_profile": "Muokkaa profiilia",
|
||||
"account.enable_notifications": "Ilmoita kun käyttäjä @{name} julkaisee viestin",
|
||||
"account.enable_notifications": "Ilmoita minulle, kun @{name} julkaisee",
|
||||
"account.endorse": "Suosittele profiilissasi",
|
||||
"account.featured_tags.last_status_at": "Viimeisin viesti {date}",
|
||||
"account.featured_tags.last_status_never": "Ei viestejä",
|
||||
"account.featured_tags.title": "Käyttäjän {name} esillä olevat aihetunnisteet",
|
||||
"account.featured_tags.last_status_at": "Viimeisin julkaisu {date}",
|
||||
"account.featured_tags.last_status_never": "Ei julkaisuja",
|
||||
"account.featured_tags.title": "Käyttäjän {name} esille nostetut aihetunnisteet",
|
||||
"account.follow": "Seuraa",
|
||||
"account.followers": "seuraaja(t)",
|
||||
"account.followers.empty": "Kukaan ei seuraa tätä käyttäjää vielä.",
|
||||
|
@ -54,21 +54,21 @@
|
|||
"account.muted": "Mykistetty",
|
||||
"account.no_bio": "Kuvausta ei ole annettu.",
|
||||
"account.open_original_page": "Avaa alkuperäinen sivu",
|
||||
"account.posts": "viesti(t)",
|
||||
"account.posts_with_replies": "Viestit ja vastaukset",
|
||||
"account.posts": "Julkaisut",
|
||||
"account.posts_with_replies": "Julkaisut ja vastaukset",
|
||||
"account.report": "Raportoi @{name}",
|
||||
"account.requested": "Odottaa hyväksyntää. Peruuta seuraamispyyntö klikkaamalla",
|
||||
"account.requested": "Odottaa hyväksyntää. Peruuta seuraamispyyntö napsauttamalla",
|
||||
"account.requested_follow": "{name} on pyytänyt lupaa seurata sinua",
|
||||
"account.share": "Jaa käyttäjän @{name} profiili",
|
||||
"account.show_reblogs": "Näytä tehostukset käyttäjältä @{name}",
|
||||
"account.statuses_counter": "{count, plural, one {{counter} viesti} other {{counter} viestiä}}",
|
||||
"account.show_reblogs": "Näytä käyttäjän @{name} tehostukset",
|
||||
"account.statuses_counter": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}",
|
||||
"account.unblock": "Poista esto: @{name}",
|
||||
"account.unblock_domain": "Salli palvelu {domain}",
|
||||