Merge remote-tracking branch 'parent/main' into upstream-20241216
This commit is contained in:
commit
3784ad273c
555 changed files with 7564 additions and 3363 deletions
|
@ -60,6 +60,10 @@ window.addEventListener('message', (e) => {
|
|||
|
||||
const data = e.data;
|
||||
|
||||
// Only set overflow to `hidden` once we got the expected `message` so the post can still be scrolled if
|
||||
// embedded without parent Javascript support
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
// We use a timeout to allow for the React page to render before calculating the height
|
||||
afterInitialRender(() => {
|
||||
window.parent.postMessage(
|
||||
|
|
|
@ -2,6 +2,8 @@ import { useCallback } from 'react';
|
|||
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { isFulfilled, isRejected } from '@reduxjs/toolkit';
|
||||
|
||||
import { openURL } from 'mastodon/actions/search';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
|
@ -28,12 +30,22 @@ export const useLinks = () => {
|
|||
);
|
||||
|
||||
const handleMentionClick = useCallback(
|
||||
(element: HTMLAnchorElement) => {
|
||||
dispatch(
|
||||
openURL(element.href, history, () => {
|
||||
async (element: HTMLAnchorElement) => {
|
||||
const result = await dispatch(openURL({ url: element.href }));
|
||||
|
||||
if (isFulfilled(result)) {
|
||||
if (result.payload.accounts[0]) {
|
||||
history.push(`/@${result.payload.accounts[0].acct}`);
|
||||
} else if (result.payload.statuses[0]) {
|
||||
history.push(
|
||||
`/@${result.payload.statuses[0].account.acct}/${result.payload.statuses[0].id}`,
|
||||
);
|
||||
} else {
|
||||
window.location.href = element.href;
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else if (isRejected(result)) {
|
||||
window.location.href = element.href;
|
||||
}
|
||||
},
|
||||
[dispatch, history],
|
||||
);
|
||||
|
@ -48,7 +60,7 @@ export const useLinks = () => {
|
|||
|
||||
if (isMentionClick(target)) {
|
||||
e.preventDefault();
|
||||
handleMentionClick(target);
|
||||
void handleMentionClick(target);
|
||||
} else if (isHashtagClick(target)) {
|
||||
e.preventDefault();
|
||||
handleHashtagClick(target);
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { createPollFromServerJSON } from 'mastodon/models/poll';
|
||||
|
||||
import { importAccounts } from '../accounts_typed';
|
||||
|
||||
import { normalizeStatus, normalizePoll } from './normalizer';
|
||||
import { normalizeStatus } from './normalizer';
|
||||
import { importPolls } from './polls';
|
||||
|
||||
export const STATUS_IMPORT = 'STATUS_IMPORT';
|
||||
export const STATUSES_IMPORT = 'STATUSES_IMPORT';
|
||||
export const POLLS_IMPORT = 'POLLS_IMPORT';
|
||||
export const FILTERS_IMPORT = 'FILTERS_IMPORT';
|
||||
|
||||
function pushUnique(array, object) {
|
||||
|
@ -25,10 +27,6 @@ export function importFilters(filters) {
|
|||
return { type: FILTERS_IMPORT, filters };
|
||||
}
|
||||
|
||||
export function importPolls(polls) {
|
||||
return { type: POLLS_IMPORT, polls };
|
||||
}
|
||||
|
||||
export function importFetchedAccount(account) {
|
||||
return importFetchedAccounts([account]);
|
||||
}
|
||||
|
@ -77,7 +75,7 @@ export function importFetchedStatuses(statuses) {
|
|||
}
|
||||
|
||||
if (status.poll?.id) {
|
||||
pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id])));
|
||||
pushUnique(polls, createPollFromServerJSON(status.poll, getState().polls.get(status.poll.id)));
|
||||
}
|
||||
|
||||
if (status.card) {
|
||||
|
@ -87,15 +85,9 @@ export function importFetchedStatuses(statuses) {
|
|||
|
||||
statuses.forEach(processStatus);
|
||||
|
||||
dispatch(importPolls(polls));
|
||||
dispatch(importPolls({ polls }));
|
||||
dispatch(importFetchedAccounts(accounts));
|
||||
dispatch(importStatuses(normalStatuses));
|
||||
dispatch(importFilters(filters));
|
||||
};
|
||||
}
|
||||
|
||||
export function importFetchedPoll(poll) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(importPolls([normalizePoll(poll, getState().getIn(['polls', poll.id]))]));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
import escapeTextContentForBrowser from 'escape-html';
|
||||
|
||||
import { makeEmojiMap } from 'mastodon/models/custom_emoji';
|
||||
|
||||
import emojify from '../../features/emoji/emoji';
|
||||
import { expandSpoilers, me } from '../../initial_state';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
||||
const makeEmojiMap = emojis => emojis.reduce((obj, emoji) => {
|
||||
obj[`:${emoji.shortcode}:`] = emoji;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
export function searchTextFromRawStatus (status) {
|
||||
const spoilerText = status.spoiler_text || '';
|
||||
const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||
|
@ -140,38 +137,6 @@ export function normalizeStatusTranslation(translation, status) {
|
|||
return normalTranslation;
|
||||
}
|
||||
|
||||
export function normalizePoll(poll, normalOldPoll) {
|
||||
const normalPoll = { ...poll };
|
||||
const emojiMap = makeEmojiMap(poll.emojis);
|
||||
|
||||
normalPoll.options = poll.options.map((option, index) => {
|
||||
const normalOption = {
|
||||
...option,
|
||||
voted: poll.own_votes && poll.own_votes.includes(index),
|
||||
titleHtml: emojify(escapeTextContentForBrowser(option.title), emojiMap),
|
||||
};
|
||||
|
||||
if (normalOldPoll && normalOldPoll.getIn(['options', index, 'title']) === option.title) {
|
||||
normalOption.translation = normalOldPoll.getIn(['options', index, 'translation']);
|
||||
}
|
||||
|
||||
return normalOption;
|
||||
});
|
||||
|
||||
return normalPoll;
|
||||
}
|
||||
|
||||
export function normalizePollOptionTranslation(translation, poll) {
|
||||
const emojiMap = makeEmojiMap(poll.get('emojis').toJS());
|
||||
|
||||
const normalTranslation = {
|
||||
...translation,
|
||||
titleHtml: emojify(escapeTextContentForBrowser(translation.title), emojiMap),
|
||||
};
|
||||
|
||||
return normalTranslation;
|
||||
}
|
||||
|
||||
export function normalizeAnnouncement(announcement) {
|
||||
const normalAnnouncement = { ...announcement };
|
||||
const emojiMap = makeEmojiMap(normalAnnouncement.emojis);
|
||||
|
|
7
app/javascript/mastodon/actions/importer/polls.ts
Normal file
7
app/javascript/mastodon/actions/importer/polls.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import type { Poll } from 'mastodon/models/poll';
|
||||
|
||||
export const importPolls = createAction<{ polls: Poll[] }>(
|
||||
'poll/importMultiple',
|
||||
);
|
|
@ -1,61 +0,0 @@
|
|||
import api from '../api';
|
||||
|
||||
import { importFetchedPoll } from './importer';
|
||||
|
||||
export const POLL_VOTE_REQUEST = 'POLL_VOTE_REQUEST';
|
||||
export const POLL_VOTE_SUCCESS = 'POLL_VOTE_SUCCESS';
|
||||
export const POLL_VOTE_FAIL = 'POLL_VOTE_FAIL';
|
||||
|
||||
export const POLL_FETCH_REQUEST = 'POLL_FETCH_REQUEST';
|
||||
export const POLL_FETCH_SUCCESS = 'POLL_FETCH_SUCCESS';
|
||||
export const POLL_FETCH_FAIL = 'POLL_FETCH_FAIL';
|
||||
|
||||
export const vote = (pollId, choices) => (dispatch) => {
|
||||
dispatch(voteRequest());
|
||||
|
||||
api().post(`/api/v1/polls/${pollId}/votes`, { choices })
|
||||
.then(({ data }) => {
|
||||
dispatch(importFetchedPoll(data));
|
||||
dispatch(voteSuccess(data));
|
||||
})
|
||||
.catch(err => dispatch(voteFail(err)));
|
||||
};
|
||||
|
||||
export const fetchPoll = pollId => (dispatch) => {
|
||||
dispatch(fetchPollRequest());
|
||||
|
||||
api().get(`/api/v1/polls/${pollId}`)
|
||||
.then(({ data }) => {
|
||||
dispatch(importFetchedPoll(data));
|
||||
dispatch(fetchPollSuccess(data));
|
||||
})
|
||||
.catch(err => dispatch(fetchPollFail(err)));
|
||||
};
|
||||
|
||||
export const voteRequest = () => ({
|
||||
type: POLL_VOTE_REQUEST,
|
||||
});
|
||||
|
||||
export const voteSuccess = poll => ({
|
||||
type: POLL_VOTE_SUCCESS,
|
||||
poll,
|
||||
});
|
||||
|
||||
export const voteFail = error => ({
|
||||
type: POLL_VOTE_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const fetchPollRequest = () => ({
|
||||
type: POLL_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
export const fetchPollSuccess = poll => ({
|
||||
type: POLL_FETCH_SUCCESS,
|
||||
poll,
|
||||
});
|
||||
|
||||
export const fetchPollFail = error => ({
|
||||
type: POLL_FETCH_FAIL,
|
||||
error,
|
||||
});
|
40
app/javascript/mastodon/actions/polls.ts
Normal file
40
app/javascript/mastodon/actions/polls.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { apiGetPoll, apiPollVote } from 'mastodon/api/polls';
|
||||
import type { ApiPollJSON } from 'mastodon/api_types/polls';
|
||||
import { createPollFromServerJSON } from 'mastodon/models/poll';
|
||||
import {
|
||||
createAppAsyncThunk,
|
||||
createDataLoadingThunk,
|
||||
} from 'mastodon/store/typed_functions';
|
||||
|
||||
import { importPolls } from './importer/polls';
|
||||
|
||||
export const importFetchedPoll = createAppAsyncThunk(
|
||||
'poll/importFetched',
|
||||
(args: { poll: ApiPollJSON }, { dispatch, getState }) => {
|
||||
const { poll } = args;
|
||||
|
||||
dispatch(
|
||||
importPolls({
|
||||
polls: [createPollFromServerJSON(poll, getState().polls.get(poll.id))],
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const vote = createDataLoadingThunk(
|
||||
'poll/vote',
|
||||
({ pollId, choices }: { pollId: string; choices: string[] }) =>
|
||||
apiPollVote(pollId, choices),
|
||||
async (poll, { dispatch, discardLoadData }) => {
|
||||
await dispatch(importFetchedPoll({ poll }));
|
||||
return discardLoadData;
|
||||
},
|
||||
);
|
||||
|
||||
export const fetchPoll = createDataLoadingThunk(
|
||||
'poll/fetch',
|
||||
({ pollId }: { pollId: string }) => apiGetPoll(pollId),
|
||||
async (poll, { dispatch }) => {
|
||||
await dispatch(importFetchedPoll({ poll }));
|
||||
},
|
||||
);
|
|
@ -1,215 +0,0 @@
|
|||
import { fromJS } from 'immutable';
|
||||
|
||||
import { searchHistory } from 'mastodon/settings';
|
||||
|
||||
import api from '../api';
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts, importFetchedStatuses } from './importer';
|
||||
|
||||
export const SEARCH_CHANGE = 'SEARCH_CHANGE';
|
||||
export const SEARCH_CLEAR = 'SEARCH_CLEAR';
|
||||
export const SEARCH_SHOW = 'SEARCH_SHOW';
|
||||
|
||||
export const SEARCH_FETCH_REQUEST = 'SEARCH_FETCH_REQUEST';
|
||||
export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS';
|
||||
export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL';
|
||||
|
||||
export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST';
|
||||
export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
|
||||
export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL';
|
||||
|
||||
export const SEARCH_HISTORY_UPDATE = 'SEARCH_HISTORY_UPDATE';
|
||||
|
||||
export function changeSearch(value) {
|
||||
return {
|
||||
type: SEARCH_CHANGE,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
export function clearSearch() {
|
||||
return {
|
||||
type: SEARCH_CLEAR,
|
||||
};
|
||||
}
|
||||
|
||||
export function submitSearch(type) {
|
||||
return (dispatch, getState) => {
|
||||
const value = getState().getIn(['search', 'value']);
|
||||
const signedIn = !!getState().getIn(['meta', 'me']);
|
||||
|
||||
if (value.length === 0) {
|
||||
dispatch(fetchSearchSuccess({ accounts: [], statuses: [], hashtags: [] }, '', type));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchSearchRequest(type));
|
||||
|
||||
api().get('/api/v2/search', {
|
||||
params: {
|
||||
q: value,
|
||||
resolve: signedIn,
|
||||
limit: 11,
|
||||
type,
|
||||
},
|
||||
}).then(response => {
|
||||
if (response.data.accounts) {
|
||||
dispatch(importFetchedAccounts(response.data.accounts));
|
||||
}
|
||||
|
||||
if (response.data.statuses) {
|
||||
dispatch(importFetchedStatuses(response.data.statuses));
|
||||
}
|
||||
|
||||
dispatch(fetchSearchSuccess(response.data, value, type));
|
||||
dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(fetchSearchFail(error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSearchRequest(searchType) {
|
||||
return {
|
||||
type: SEARCH_FETCH_REQUEST,
|
||||
searchType,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSearchSuccess(results, searchTerm, searchType) {
|
||||
return {
|
||||
type: SEARCH_FETCH_SUCCESS,
|
||||
results,
|
||||
searchType,
|
||||
searchTerm,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSearchFail(error) {
|
||||
return {
|
||||
type: SEARCH_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export const expandSearch = type => (dispatch, getState) => {
|
||||
const value = getState().getIn(['search', 'value']);
|
||||
const offset = getState().getIn(['search', 'results', type]).size - 1;
|
||||
|
||||
dispatch(expandSearchRequest(type));
|
||||
|
||||
api().get('/api/v2/search', {
|
||||
params: {
|
||||
q: value,
|
||||
type,
|
||||
offset,
|
||||
limit: 11,
|
||||
},
|
||||
}).then(({ data }) => {
|
||||
if (data.accounts) {
|
||||
dispatch(importFetchedAccounts(data.accounts));
|
||||
}
|
||||
|
||||
if (data.statuses) {
|
||||
dispatch(importFetchedStatuses(data.statuses));
|
||||
}
|
||||
|
||||
dispatch(expandSearchSuccess(data, value, type));
|
||||
dispatch(fetchRelationships(data.accounts.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(expandSearchFail(error));
|
||||
});
|
||||
};
|
||||
|
||||
export const expandSearchRequest = (searchType) => ({
|
||||
type: SEARCH_EXPAND_REQUEST,
|
||||
searchType,
|
||||
});
|
||||
|
||||
export const expandSearchSuccess = (results, searchTerm, searchType) => ({
|
||||
type: SEARCH_EXPAND_SUCCESS,
|
||||
results,
|
||||
searchTerm,
|
||||
searchType,
|
||||
});
|
||||
|
||||
export const expandSearchFail = error => ({
|
||||
type: SEARCH_EXPAND_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const showSearch = () => ({
|
||||
type: SEARCH_SHOW,
|
||||
});
|
||||
|
||||
export const openURL = (value, history, onFailure) => (dispatch, getState) => {
|
||||
const signedIn = !!getState().getIn(['meta', 'me']);
|
||||
|
||||
if (!signedIn) {
|
||||
if (onFailure) {
|
||||
onFailure();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchSearchRequest());
|
||||
|
||||
api().get('/api/v2/search', { params: { q: value, resolve: true } }).then(response => {
|
||||
if (response.data.accounts?.length > 0) {
|
||||
dispatch(importFetchedAccounts(response.data.accounts));
|
||||
history.push(`/@${response.data.accounts[0].acct}`);
|
||||
} else if (response.data.statuses?.length > 0) {
|
||||
dispatch(importFetchedStatuses(response.data.statuses));
|
||||
history.push(`/@${response.data.statuses[0].account.acct}/${response.data.statuses[0].id}`);
|
||||
} else if (onFailure) {
|
||||
onFailure();
|
||||
}
|
||||
|
||||
dispatch(fetchSearchSuccess(response.data, value));
|
||||
}).catch(err => {
|
||||
dispatch(fetchSearchFail(err));
|
||||
|
||||
if (onFailure) {
|
||||
onFailure();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const clickSearchResult = (q, type) => (dispatch, getState) => {
|
||||
const previous = getState().getIn(['search', 'recent']);
|
||||
|
||||
if (previous.some(x => x.get('q') === q && x.get('type') === type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const me = getState().getIn(['meta', 'me']);
|
||||
const current = previous.add(fromJS({ type, q })).takeLast(4);
|
||||
|
||||
searchHistory.set(me, current.toJS());
|
||||
dispatch(updateSearchHistory(current));
|
||||
};
|
||||
|
||||
export const forgetSearchResult = q => (dispatch, getState) => {
|
||||
const previous = getState().getIn(['search', 'recent']);
|
||||
const me = getState().getIn(['meta', 'me']);
|
||||
const current = previous.filterNot(result => result.get('q') === q);
|
||||
|
||||
searchHistory.set(me, current.toJS());
|
||||
dispatch(updateSearchHistory(current));
|
||||
};
|
||||
|
||||
export const updateSearchHistory = recent => ({
|
||||
type: SEARCH_HISTORY_UPDATE,
|
||||
recent,
|
||||
});
|
||||
|
||||
export const hydrateSearch = () => (dispatch, getState) => {
|
||||
const me = getState().getIn(['meta', 'me']);
|
||||
const history = searchHistory.get(me);
|
||||
|
||||
if (history !== null) {
|
||||
dispatch(updateSearchHistory(history));
|
||||
}
|
||||
};
|
151
app/javascript/mastodon/actions/search.ts
Normal file
151
app/javascript/mastodon/actions/search.ts
Normal file
|
@ -0,0 +1,151 @@
|
|||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { apiGetSearch } from 'mastodon/api/search';
|
||||
import type { ApiSearchType } from 'mastodon/api_types/search';
|
||||
import type {
|
||||
RecentSearch,
|
||||
SearchType as RecentSearchType,
|
||||
} from 'mastodon/models/search';
|
||||
import { searchHistory } from 'mastodon/settings';
|
||||
import {
|
||||
createDataLoadingThunk,
|
||||
createAppAsyncThunk,
|
||||
} from 'mastodon/store/typed_functions';
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts, importFetchedStatuses } from './importer';
|
||||
|
||||
export const SEARCH_HISTORY_UPDATE = 'SEARCH_HISTORY_UPDATE';
|
||||
|
||||
export const submitSearch = createDataLoadingThunk(
|
||||
'search/submit',
|
||||
async ({ q, type }: { q: string; type?: ApiSearchType }, { getState }) => {
|
||||
const signedIn = !!getState().meta.get('me');
|
||||
|
||||
return apiGetSearch({
|
||||
q,
|
||||
type,
|
||||
resolve: signedIn,
|
||||
limit: 11,
|
||||
});
|
||||
},
|
||||
(data, { dispatch }) => {
|
||||
if (data.accounts.length > 0) {
|
||||
dispatch(importFetchedAccounts(data.accounts));
|
||||
dispatch(fetchRelationships(data.accounts.map((account) => account.id)));
|
||||
}
|
||||
|
||||
if (data.statuses.length > 0) {
|
||||
dispatch(importFetchedStatuses(data.statuses));
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
useLoadingBar: false,
|
||||
},
|
||||
);
|
||||
|
||||
export const expandSearch = createDataLoadingThunk(
|
||||
'search/expand',
|
||||
async ({ type }: { type: ApiSearchType }, { getState }) => {
|
||||
const q = getState().search.q;
|
||||
const results = getState().search.results;
|
||||
const offset = results?.[type].length;
|
||||
|
||||
return apiGetSearch({
|
||||
q,
|
||||
type,
|
||||
limit: 11,
|
||||
offset,
|
||||
});
|
||||
},
|
||||
(data, { dispatch }) => {
|
||||
if (data.accounts.length > 0) {
|
||||
dispatch(importFetchedAccounts(data.accounts));
|
||||
dispatch(fetchRelationships(data.accounts.map((account) => account.id)));
|
||||
}
|
||||
|
||||
if (data.statuses.length > 0) {
|
||||
dispatch(importFetchedStatuses(data.statuses));
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
useLoadingBar: true,
|
||||
},
|
||||
);
|
||||
|
||||
export const openURL = createDataLoadingThunk(
|
||||
'search/openURL',
|
||||
({ url }: { url: string }, { getState }) => {
|
||||
const signedIn = !!getState().meta.get('me');
|
||||
|
||||
return apiGetSearch({
|
||||
q: url,
|
||||
resolve: signedIn,
|
||||
limit: 1,
|
||||
});
|
||||
},
|
||||
(data, { dispatch }) => {
|
||||
if (data.accounts.length > 0) {
|
||||
dispatch(importFetchedAccounts(data.accounts));
|
||||
} else if (data.statuses.length > 0) {
|
||||
dispatch(importFetchedStatuses(data.statuses));
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
useLoadingBar: true,
|
||||
},
|
||||
);
|
||||
|
||||
export const clickSearchResult = createAppAsyncThunk(
|
||||
'search/clickResult',
|
||||
(
|
||||
{ q, type }: { q: string; type?: RecentSearchType },
|
||||
{ dispatch, getState },
|
||||
) => {
|
||||
const previous = getState().search.recent;
|
||||
|
||||
if (previous.some((x) => x.q === q && x.type === type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const me = getState().meta.get('me') as string;
|
||||
const current = [{ type, q }, ...previous].slice(0, 4);
|
||||
|
||||
searchHistory.set(me, current);
|
||||
dispatch(updateSearchHistory(current));
|
||||
},
|
||||
);
|
||||
|
||||
export const forgetSearchResult = createAppAsyncThunk(
|
||||
'search/forgetResult',
|
||||
(q: string, { dispatch, getState }) => {
|
||||
const previous = getState().search.recent;
|
||||
const me = getState().meta.get('me') as string;
|
||||
const current = previous.filter((result) => result.q !== q);
|
||||
|
||||
searchHistory.set(me, current);
|
||||
dispatch(updateSearchHistory(current));
|
||||
},
|
||||
);
|
||||
|
||||
export const updateSearchHistory = createAction<RecentSearch[]>(
|
||||
'search/updateHistory',
|
||||
);
|
||||
|
||||
export const hydrateSearch = createAppAsyncThunk(
|
||||
'search/hydrate',
|
||||
(_args, { dispatch, getState }) => {
|
||||
const me = getState().meta.get('me') as string;
|
||||
const history = searchHistory.get(me) as RecentSearch[] | null;
|
||||
|
||||
if (history !== null) {
|
||||
dispatch(updateSearchHistory(history));
|
||||
}
|
||||
},
|
||||
);
|
11
app/javascript/mastodon/api/instance.ts
Normal file
11
app/javascript/mastodon/api/instance.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { apiRequestGet } from 'mastodon/api';
|
||||
import type {
|
||||
ApiTermsOfServiceJSON,
|
||||
ApiPrivacyPolicyJSON,
|
||||
} from 'mastodon/api_types/instance';
|
||||
|
||||
export const apiGetTermsOfService = () =>
|
||||
apiRequestGet<ApiTermsOfServiceJSON>('v1/instance/terms_of_service');
|
||||
|
||||
export const apiGetPrivacyPolicy = () =>
|
||||
apiRequestGet<ApiPrivacyPolicyJSON>('v1/instance/privacy_policy');
|
10
app/javascript/mastodon/api/polls.ts
Normal file
10
app/javascript/mastodon/api/polls.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { apiRequestGet, apiRequestPost } from 'mastodon/api';
|
||||
import type { ApiPollJSON } from 'mastodon/api_types/polls';
|
||||
|
||||
export const apiGetPoll = (pollId: string) =>
|
||||
apiRequestGet<ApiPollJSON>(`/v1/polls/${pollId}`);
|
||||
|
||||
export const apiPollVote = (pollId: string, choices: string[]) =>
|
||||
apiRequestPost<ApiPollJSON>(`/v1/polls/${pollId}/votes`, {
|
||||
choices,
|
||||
});
|
16
app/javascript/mastodon/api/search.ts
Normal file
16
app/javascript/mastodon/api/search.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { apiRequestGet } from 'mastodon/api';
|
||||
import type {
|
||||
ApiSearchType,
|
||||
ApiSearchResultsJSON,
|
||||
} from 'mastodon/api_types/search';
|
||||
|
||||
export const apiGetSearch = (params: {
|
||||
q: string;
|
||||
resolve?: boolean;
|
||||
type?: ApiSearchType;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}) =>
|
||||
apiRequestGet<ApiSearchResultsJSON>('v2/search', {
|
||||
...params,
|
||||
});
|
9
app/javascript/mastodon/api_types/instance.ts
Normal file
9
app/javascript/mastodon/api_types/instance.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export interface ApiTermsOfServiceJSON {
|
||||
updated_at: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ApiPrivacyPolicyJSON {
|
||||
updated_at: string;
|
||||
content: string;
|
||||
}
|
|
@ -18,6 +18,6 @@ export interface ApiPollJSON {
|
|||
options: ApiPollOptionJSON[];
|
||||
emojis: ApiCustomEmojiJSON[];
|
||||
|
||||
voted: boolean;
|
||||
own_votes: number[];
|
||||
voted?: boolean;
|
||||
own_votes?: number[];
|
||||
}
|
||||
|
|
11
app/javascript/mastodon/api_types/search.ts
Normal file
11
app/javascript/mastodon/api_types/search.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import type { ApiAccountJSON } from './accounts';
|
||||
import type { ApiStatusJSON } from './statuses';
|
||||
import type { ApiHashtagJSON } from './tags';
|
||||
|
||||
export type ApiSearchType = 'accounts' | 'statuses' | 'hashtags';
|
||||
|
||||
export interface ApiSearchResultsJSON {
|
||||
accounts: ApiAccountJSON[];
|
||||
statuses: ApiStatusJSON[];
|
||||
hashtags: ApiHashtagJSON[];
|
||||
}
|
|
@ -36,7 +36,7 @@ export default class AttachmentList extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener noreferrer'>
|
||||
<a href={displayUrl} target='_blank' rel='noopener'>
|
||||
{compact && <Icon id='link' icon={LinkIcon} />}
|
||||
{compact && ' ' }
|
||||
{displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />}
|
||||
|
|
|
@ -124,7 +124,7 @@ class DropdownMenu extends PureComponent {
|
|||
|
||||
return (
|
||||
<li className={classNames('dropdown-menu__item', { 'dropdown-menu__item--dangerous': dangerous })} key={`${text}-${i}`}>
|
||||
<a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
|
||||
<a href={href} target={target} data-method={method} rel='noopener' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
|
||||
{text}
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -98,7 +98,7 @@ export default class ErrorBoundary extends PureComponent {
|
|||
)}
|
||||
</p>
|
||||
|
||||
<p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied ? 'copied' : ''}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
|
||||
<p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied ? 'copied' : ''}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
|
|
|
@ -92,7 +92,7 @@ export const FollowButton: React.FC<{
|
|||
<a
|
||||
href='/settings/profile'
|
||||
target='_blank'
|
||||
rel='noreferrer noopener'
|
||||
rel='noopener'
|
||||
className='button button-secondary'
|
||||
>
|
||||
{label}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
|||
|
||||
import { ShortNumber } from 'mastodon/components/short_number';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
import type { Hashtag as HashtagType } from 'mastodon/models/tags';
|
||||
|
||||
interface SilentErrorBoundaryProps {
|
||||
children: React.ReactNode;
|
||||
|
@ -80,6 +81,22 @@ export const ImmutableHashtag = ({ hashtag }: ImmutableHashtagProps) => (
|
|||
/>
|
||||
);
|
||||
|
||||
export const CompatibilityHashtag: React.FC<{
|
||||
hashtag: HashtagType;
|
||||
}> = ({ hashtag }) => (
|
||||
<Hashtag
|
||||
name={hashtag.name}
|
||||
to={`/tags/${hashtag.name}`}
|
||||
people={
|
||||
(hashtag.history[0].accounts as unknown as number) * 1 +
|
||||
((hashtag.history[1]?.accounts ?? 0) as unknown as number) * 1
|
||||
}
|
||||
history={hashtag.history
|
||||
.map((day) => (day.uses as unknown as number) * 1)
|
||||
.reverse()}
|
||||
/>
|
||||
);
|
||||
|
||||
export interface HashtagProps {
|
||||
className?: string;
|
||||
description?: React.ReactNode;
|
||||
|
|
|
@ -122,7 +122,7 @@ class Item extends PureComponent {
|
|||
if (attachment.get('type') === 'unknown') {
|
||||
return (
|
||||
<div className={classNames('media-gallery__item', { standalone, 'media-gallery__item--tall': height === 100, 'media-gallery__item--wide': width === 100 })} key={attachment.get('id')}>
|
||||
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={description} lang={lang} target='_blank' rel='noopener noreferrer'>
|
||||
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={description} lang={lang} target='_blank' rel='noopener'>
|
||||
<Blurhash
|
||||
hash={attachment.get('blurhash')}
|
||||
className='media-gallery__preview'
|
||||
|
@ -154,7 +154,7 @@ class Item extends PureComponent {
|
|||
href={attachment.get('remote_url') || originalUrl}
|
||||
onClick={this.handleClick}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
rel='noopener'
|
||||
>
|
||||
<img
|
||||
src={previewUrl}
|
||||
|
|
|
@ -33,15 +33,10 @@ const messages = defineMessages({
|
|||
},
|
||||
});
|
||||
|
||||
const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
|
||||
obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
class Poll extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
poll: ImmutablePropTypes.map.isRequired,
|
||||
poll: ImmutablePropTypes.record.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
lang: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
|
@ -150,7 +145,7 @@ class Poll extends ImmutablePureComponent {
|
|||
let titleHtml = option.getIn(['translation', 'titleHtml']) || option.get('titleHtml');
|
||||
|
||||
if (!titleHtml) {
|
||||
const emojiMap = makeEmojiMap(poll);
|
||||
const emojiMap = emojiMap(poll);
|
||||
titleHtml = emojify(escapeTextContentForBrowser(title), emojiMap);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class ServerBanner extends PureComponent {
|
|||
return (
|
||||
<div className='server-banner'>
|
||||
<div className='server-banner__introduction'>
|
||||
<FormattedMessage id='server_banner.is_one_of_many' defaultMessage='{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank'>Mastodon</a> }} />
|
||||
<FormattedMessage id='server_banner.is_one_of_many' defaultMessage='{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank' rel='noopener'>Mastodon</a> }} />
|
||||
</div>
|
||||
|
||||
<Link to='/about'>
|
||||
|
|
|
@ -302,7 +302,7 @@ class Status extends ImmutablePureComponent {
|
|||
if (e?.button === 0 && !(e?.ctrlKey || e?.metaKey)) {
|
||||
history.push(path);
|
||||
} else if (e?.button === 1 || (e?.button === 0 && (e?.ctrlKey || e?.metaKey))) {
|
||||
window.open(path, '_blank', 'noreferrer noopener');
|
||||
window.open(path, '_blank', 'noopener');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -9,14 +9,14 @@ import Poll from 'mastodon/components/poll';
|
|||
const mapDispatchToProps = (dispatch, { pollId }) => ({
|
||||
refresh: debounce(
|
||||
() => {
|
||||
dispatch(fetchPoll(pollId));
|
||||
dispatch(fetchPoll({ pollId }));
|
||||
},
|
||||
1000,
|
||||
{ leading: true },
|
||||
),
|
||||
|
||||
onVote (choices) {
|
||||
dispatch(vote(pollId, choices));
|
||||
dispatch(vote({ pollId, choices }));
|
||||
},
|
||||
|
||||
onInteractionModal (type, status) {
|
||||
|
@ -32,7 +32,7 @@ const mapDispatchToProps = (dispatch, { pollId }) => ({
|
|||
});
|
||||
|
||||
const mapStateToProps = (state, { pollId }) => ({
|
||||
poll: state.getIn(['polls', pollId]),
|
||||
poll: state.polls.get(pollId),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Poll);
|
||||
|
|
|
@ -20,7 +20,7 @@ import Column from 'mastodon/components/column';
|
|||
import { Icon } from 'mastodon/components/icon';
|
||||
import { ServerHeroImage } from 'mastodon/components/server_hero_image';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
import LinkFooter from 'mastodon/features/ui/components/link_footer';
|
||||
import { LinkFooter} from 'mastodon/features/ui/components/link_footer';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.about', defaultMessage: 'About' },
|
||||
|
@ -173,7 +173,7 @@ class About extends PureComponent {
|
|||
<div className='about__header'>
|
||||
<ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} srcSet={server.getIn(['thumbnail', 'versions'])?.map((value, key) => `${value} ${key.replace('@', '')}`).join(', ')} className='about__header__hero' />
|
||||
<h1>{isLoading ? <Skeleton width='10ch' /> : server.get('domain')}</h1>
|
||||
<p><FormattedMessage id='about.powered_by' defaultMessage='Decentralized social media powered by {mastodon}' values={{ mastodon: <a href='https://joinmastodon.org' className='about__mail' target='_blank'>Mastodon</a> }} /></p>
|
||||
<p><FormattedMessage id='about.powered_by' defaultMessage='Decentralized social media powered by {mastodon}' values={{ mastodon: <a href='https://joinmastodon.org' className='about__mail' target='_blank' rel='noopener'>Mastodon</a> }} /></p>
|
||||
</div>
|
||||
|
||||
<div className='about__meta'>
|
||||
|
|
|
@ -6,6 +6,7 @@ import classNames from 'classnames';
|
|||
import { Helmet } from 'react-helmet';
|
||||
import { NavLink, withRouter } from 'react-router-dom';
|
||||
|
||||
import { isFulfilled, isRejected } from '@reduxjs/toolkit';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
|
@ -233,8 +234,20 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
const link = e.currentTarget;
|
||||
|
||||
onOpenURL(link.href, history, () => {
|
||||
window.location = link.href;
|
||||
onOpenURL(link.href).then((result) => {
|
||||
if (isFulfilled(result)) {
|
||||
if (result.payload.accounts[0]) {
|
||||
history.push(`/@${result.payload.accounts[0].acct}`);
|
||||
} else if (result.payload.statuses[0]) {
|
||||
history.push(`/@${result.payload.statuses[0].account.acct}/${result.payload.statuses[0].id}`);
|
||||
} else {
|
||||
window.location = link.href;
|
||||
}
|
||||
} else if (isRejected(result)) {
|
||||
window.location = link.href;
|
||||
}
|
||||
}).catch(() => {
|
||||
// Nothing
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -450,7 +463,7 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
<div className='account__header__bar'>
|
||||
<div className='account__header__tabs'>
|
||||
<a className='avatar' href={account.get('avatar')} rel='noopener noreferrer' target='_blank' onClick={this.handleAvatarClick}>
|
||||
<a className='avatar' href={account.get('avatar')} rel='noopener' target='_blank' onClick={this.handleAvatarClick}>
|
||||
<Avatar account={suspended || hidden ? undefined : account} size={90} />
|
||||
</a>
|
||||
|
||||
|
|
|
@ -174,8 +174,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
}));
|
||||
},
|
||||
|
||||
onOpenURL (url, routerHistory, onFailure) {
|
||||
dispatch(openURL(url, routerHistory, onFailure));
|
||||
onOpenURL (url) {
|
||||
return dispatch(openURL({ url }));
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -1,405 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage, FormattedList } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain, searchEnabled } from 'mastodon/initial_state';
|
||||
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
|
||||
placeholderSignedIn: { id: 'search.search_or_paste', defaultMessage: 'Search or paste URL' },
|
||||
});
|
||||
|
||||
const labelForRecentSearch = search => {
|
||||
switch(search.get('type')) {
|
||||
case 'account':
|
||||
return `@${search.get('q')}`;
|
||||
case 'hashtag':
|
||||
return `#${search.get('q')}`;
|
||||
default:
|
||||
return search.get('q');
|
||||
}
|
||||
};
|
||||
|
||||
class Search extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
value: PropTypes.string.isRequired,
|
||||
recent: ImmutablePropTypes.orderedSet,
|
||||
submitted: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onOpenURL: PropTypes.func.isRequired,
|
||||
onClickSearchResult: PropTypes.func.isRequired,
|
||||
onForgetSearchResult: PropTypes.func.isRequired,
|
||||
onClear: PropTypes.func.isRequired,
|
||||
onShow: PropTypes.func.isRequired,
|
||||
openInRoute: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
singleColumn: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
state = {
|
||||
expanded: false,
|
||||
selectedOption: -1,
|
||||
options: [],
|
||||
};
|
||||
|
||||
defaultOptions = [
|
||||
{ key: 'prompt-has', label: <><mark>has:</mark> <FormattedList type='disjunction' value={['media', 'poll', 'embed']} /></>, action: e => { e.preventDefault(); this._insertText('has:'); } },
|
||||
{ key: 'prompt-is', label: <><mark>is:</mark> <FormattedList type='disjunction' value={['reply', 'sensitive']} /></>, action: e => { e.preventDefault(); this._insertText('is:'); } },
|
||||
{ key: 'prompt-my', label: <><mark>my:</mark> <FormattedList type='disjunction' value={['favourited', 'bookmarked', 'boosted']} /></>, action: e => { e.preventDefault(); this._insertText('my:'); } },
|
||||
{ key: 'prompt-language', label: <><mark>language:</mark> <FormattedMessage id='search_popout.language_code' defaultMessage='ISO language code' /></>, action: e => { e.preventDefault(); this._insertText('language:'); } },
|
||||
{ key: 'prompt-from', label: <><mark>from:</mark> <FormattedMessage id='search_popout.user' defaultMessage='user' /></>, action: e => { e.preventDefault(); this._insertText('from:'); } },
|
||||
{ key: 'prompt-domain', label: <><mark>domain:</mark> <FormattedMessage id='search_popout.domain' defaultMessage='domain' /></>, action: e => { e.preventDefault(); this._insertText('domain:'); } },
|
||||
{ key: 'prompt-before', label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:'); } },
|
||||
{ key: 'prompt-during', label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:'); } },
|
||||
{ key: 'prompt-after', label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:'); } },
|
||||
{ key: 'prompt-in', label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library', 'public']} /></>, action: e => { e.preventDefault(); this._insertText('in:'); } },
|
||||
{ key: 'prompt-order', label: <><mark>order:</mark> <FormattedList type='disjunction' value={['desc', 'asc']} /></>, action: e => { e.preventDefault(); this._insertText('order:'); } },
|
||||
];
|
||||
|
||||
setRef = c => {
|
||||
this.searchForm = c;
|
||||
};
|
||||
|
||||
handleChange = ({ target }) => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(target.value);
|
||||
|
||||
this._calculateOptions(target.value);
|
||||
};
|
||||
|
||||
handleClear = e => {
|
||||
const { value, submitted, onClear } = this.props;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (value.length > 0 || submitted) {
|
||||
onClear();
|
||||
this.setState({ options: [], selectedOption: -1 });
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
const { selectedOption } = this.state;
|
||||
const options = searchEnabled ? this._getOptions().concat(this.defaultOptions) : this._getOptions();
|
||||
|
||||
switch(e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
this._unfocus();
|
||||
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
|
||||
if (options.length > 0) {
|
||||
this.setState({ selectedOption: Math.min(selectedOption + 1, options.length - 1) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
|
||||
if (options.length > 0) {
|
||||
this.setState({ selectedOption: Math.max(selectedOption - 1, -1) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
|
||||
if (selectedOption === -1) {
|
||||
this._submit();
|
||||
} else if (options.length > 0) {
|
||||
options[selectedOption].action(e);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Delete':
|
||||
if (selectedOption > -1 && options.length > 0) {
|
||||
const search = options[selectedOption];
|
||||
|
||||
if (typeof search.forget === 'function') {
|
||||
e.preventDefault();
|
||||
search.forget(e);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
handleFocus = () => {
|
||||
const { onShow, singleColumn } = this.props;
|
||||
|
||||
this.setState({ expanded: true, selectedOption: -1 });
|
||||
onShow();
|
||||
|
||||
if (this.searchForm && !singleColumn) {
|
||||
const { left, right } = this.searchForm.getBoundingClientRect();
|
||||
|
||||
if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) {
|
||||
this.searchForm.scrollIntoView();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlur = () => {
|
||||
this.setState({ expanded: false, selectedOption: -1 });
|
||||
};
|
||||
|
||||
handleHashtagClick = () => {
|
||||
const { value, onClickSearchResult, history } = this.props;
|
||||
|
||||
const query = value.trim().replace(/^#/, '');
|
||||
|
||||
history.push(`/tags/${query}`);
|
||||
onClickSearchResult(query, 'hashtag');
|
||||
this._unfocus();
|
||||
};
|
||||
|
||||
handleAccountClick = () => {
|
||||
const { value, onClickSearchResult, history } = this.props;
|
||||
|
||||
const query = value.trim().replace(/^@/, '');
|
||||
|
||||
history.push(`/@${query}`);
|
||||
onClickSearchResult(query, 'account');
|
||||
this._unfocus();
|
||||
};
|
||||
|
||||
handleURLClick = () => {
|
||||
const { value, onOpenURL, history } = this.props;
|
||||
|
||||
onOpenURL(value, history);
|
||||
this._unfocus();
|
||||
};
|
||||
|
||||
handleStatusSearch = () => {
|
||||
this._submit('statuses');
|
||||
};
|
||||
|
||||
handleAccountSearch = () => {
|
||||
this._submit('accounts');
|
||||
};
|
||||
|
||||
handleRecentSearchClick = search => {
|
||||
const { onChange, history } = this.props;
|
||||
|
||||
if (search.get('type') === 'account') {
|
||||
history.push(`/@${search.get('q')}`);
|
||||
} else if (search.get('type') === 'hashtag') {
|
||||
history.push(`/tags/${search.get('q')}`);
|
||||
} else {
|
||||
onChange(search.get('q'));
|
||||
this._submit(search.get('type'));
|
||||
}
|
||||
|
||||
this._unfocus();
|
||||
};
|
||||
|
||||
handleForgetRecentSearchClick = search => {
|
||||
const { onForgetSearchResult } = this.props;
|
||||
|
||||
onForgetSearchResult(search.get('q'));
|
||||
};
|
||||
|
||||
_unfocus () {
|
||||
document.querySelector('.ui').parentElement.focus();
|
||||
}
|
||||
|
||||
_insertText (text) {
|
||||
const { value, onChange } = this.props;
|
||||
|
||||
if (value === '') {
|
||||
onChange(text);
|
||||
} else if (value[value.length - 1] === ' ') {
|
||||
onChange(`${value}${text}`);
|
||||
} else {
|
||||
onChange(`${value} ${text}`);
|
||||
}
|
||||
}
|
||||
|
||||
_submit (type) {
|
||||
const { onSubmit, openInRoute, value, onClickSearchResult, history } = this.props;
|
||||
|
||||
onSubmit(type);
|
||||
|
||||
if (value) {
|
||||
onClickSearchResult(value, type);
|
||||
}
|
||||
|
||||
if (openInRoute) {
|
||||
history.push('/search');
|
||||
}
|
||||
|
||||
this._unfocus();
|
||||
}
|
||||
|
||||
_getOptions () {
|
||||
const { options } = this.state;
|
||||
|
||||
if (options.length > 0) {
|
||||
return options;
|
||||
}
|
||||
|
||||
const { recent } = this.props;
|
||||
|
||||
return recent.toArray().map(search => ({
|
||||
key: `${search.get('type')}/${search.get('q')}`,
|
||||
|
||||
label: labelForRecentSearch(search),
|
||||
|
||||
action: () => this.handleRecentSearchClick(search),
|
||||
|
||||
forget: e => {
|
||||
e.stopPropagation();
|
||||
this.handleForgetRecentSearchClick(search);
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
_calculateOptions (value) {
|
||||
const { signedIn } = this.props.identity;
|
||||
const trimmedValue = value.trim();
|
||||
const options = [];
|
||||
|
||||
if (trimmedValue.length > 0) {
|
||||
const couldBeURL = trimmedValue.startsWith('https://') && !trimmedValue.includes(' ');
|
||||
|
||||
if (couldBeURL) {
|
||||
options.push({ key: 'open-url', label: <FormattedMessage id='search.quick_action.open_url' defaultMessage='Open URL in Mastodon' />, action: this.handleURLClick });
|
||||
}
|
||||
|
||||
const couldBeHashtag = (trimmedValue.startsWith('#') && trimmedValue.length > 1) || trimmedValue.match(HASHTAG_REGEX);
|
||||
|
||||
if (couldBeHashtag) {
|
||||
options.push({ key: 'go-to-hashtag', label: <FormattedMessage id='search.quick_action.go_to_hashtag' defaultMessage='Go to hashtag {x}' values={{ x: <mark>#{trimmedValue.replace(/^#/, '')}</mark> }} />, action: this.handleHashtagClick });
|
||||
}
|
||||
|
||||
const couldBeUsername = trimmedValue.match(/^@?[a-z0-9_-]+(@[^\s]+)?$/i);
|
||||
|
||||
if (couldBeUsername) {
|
||||
options.push({ key: 'go-to-account', label: <FormattedMessage id='search.quick_action.go_to_account' defaultMessage='Go to profile {x}' values={{ x: <mark>@{trimmedValue.replace(/^@/, '')}</mark> }} />, action: this.handleAccountClick });
|
||||
}
|
||||
|
||||
const couldBeStatusSearch = searchEnabled;
|
||||
|
||||
if (couldBeStatusSearch && signedIn) {
|
||||
options.push({ key: 'status-search', label: <FormattedMessage id='search.quick_action.status_search' defaultMessage='Posts matching {x}' values={{ x: <mark>{trimmedValue}</mark> }} />, action: this.handleStatusSearch });
|
||||
}
|
||||
|
||||
const couldBeUserSearch = true;
|
||||
|
||||
if (couldBeUserSearch) {
|
||||
options.push({ key: 'account-search', label: <FormattedMessage id='search.quick_action.account_search' defaultMessage='Profiles matching {x}' values={{ x: <mark>{trimmedValue}</mark> }} />, action: this.handleAccountSearch });
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ options });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl, value, submitted, recent } = this.props;
|
||||
const { expanded, options, selectedOption } = this.state;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
const hasValue = value.length > 0 || submitted;
|
||||
|
||||
return (
|
||||
<div className={classNames('search', { active: expanded })}>
|
||||
<input
|
||||
ref={this.setRef}
|
||||
className='search__input'
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
|
||||
aria-label={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
|
||||
value={value}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
/>
|
||||
|
||||
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||
<Icon id='search' icon={SearchIcon} className={hasValue ? '' : 'active'} />
|
||||
<Icon id='times-circle' icon={CancelIcon} className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||
</div>
|
||||
|
||||
<div className='search__popout'>
|
||||
{options.length === 0 && (
|
||||
<>
|
||||
<h4><FormattedMessage id='search_popout.recent' defaultMessage='Recent searches' /></h4>
|
||||
|
||||
<div className='search__popout__menu'>
|
||||
{recent.size > 0 ? this._getOptions().map(({ label, key, action, forget }, i) => (
|
||||
<button key={key} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}>
|
||||
<span>{label}</span>
|
||||
<button className='icon-button' onMouseDown={forget}><Icon id='times' icon={CloseIcon} /></button>
|
||||
</button>
|
||||
)) : (
|
||||
<div className='search__popout__menu__message'>
|
||||
<FormattedMessage id='search.no_recent_searches' defaultMessage='No recent searches' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{options.length > 0 && (
|
||||
<>
|
||||
<h4><FormattedMessage id='search_popout.quick_actions' defaultMessage='Quick actions' /></h4>
|
||||
|
||||
<div className='search__popout__menu'>
|
||||
{options.map(({ key, label, action }, i) => (
|
||||
<button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === i })}>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4>
|
||||
|
||||
{searchEnabled && signedIn ? (
|
||||
<div className='search__popout__menu'>
|
||||
{this.defaultOptions.map(({ key, label, action }, i) => (
|
||||
<button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === ((options.length || recent.size) + i) })}>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className='search__popout__menu__message'>
|
||||
{searchEnabled ? (
|
||||
<FormattedMessage id='search_popout.full_text_search_logged_out_message' defaultMessage='Only available when logged in.' />
|
||||
) : (
|
||||
<FormattedMessage id='search_popout.full_text_search_disabled_message' defaultMessage='Not available on {domain}.' values={{ domain }} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(withIdentity(injectIntl(Search)));
|
635
app/javascript/mastodon/features/compose/components/search.tsx
Normal file
635
app/javascript/mastodon/features/compose/components/search.tsx
Normal file
|
@ -0,0 +1,635 @@
|
|||
import { useCallback, useState, useRef } from 'react';
|
||||
|
||||
import {
|
||||
defineMessages,
|
||||
useIntl,
|
||||
FormattedMessage,
|
||||
FormattedList,
|
||||
} from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { isFulfilled } from '@reduxjs/toolkit';
|
||||
|
||||
import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
|
||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import {
|
||||
clickSearchResult,
|
||||
forgetSearchResult,
|
||||
openURL,
|
||||
} from 'mastodon/actions/search';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { useIdentity } from 'mastodon/identity_context';
|
||||
import { domain, searchEnabled } from 'mastodon/initial_state';
|
||||
import type { RecentSearch, SearchType } from 'mastodon/models/search';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
|
||||
placeholderSignedIn: {
|
||||
id: 'search.search_or_paste',
|
||||
defaultMessage: 'Search or paste URL',
|
||||
},
|
||||
});
|
||||
|
||||
const labelForRecentSearch = (search: RecentSearch) => {
|
||||
switch (search.type) {
|
||||
case 'account':
|
||||
return `@${search.q}`;
|
||||
case 'hashtag':
|
||||
return `#${search.q}`;
|
||||
default:
|
||||
return search.q;
|
||||
}
|
||||
};
|
||||
|
||||
const unfocus = () => {
|
||||
document.querySelector('.ui')?.parentElement?.focus();
|
||||
};
|
||||
|
||||
interface SearchOption {
|
||||
key: string;
|
||||
label: React.ReactNode;
|
||||
action: (e: React.MouseEvent | React.KeyboardEvent) => void;
|
||||
forget?: (e: React.MouseEvent | React.KeyboardEvent) => void;
|
||||
}
|
||||
|
||||
export const Search: React.FC<{
|
||||
singleColumn: boolean;
|
||||
initialValue?: string;
|
||||
}> = ({ singleColumn, initialValue }) => {
|
||||
const intl = useIntl();
|
||||
const recent = useAppSelector((state) => state.search.recent);
|
||||
const { signedIn } = useIdentity();
|
||||
const dispatch = useAppDispatch();
|
||||
const history = useHistory();
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
const [value, setValue] = useState(initialValue ?? '');
|
||||
const hasValue = value.length > 0;
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState(-1);
|
||||
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
||||
const searchOptions: SearchOption[] = [];
|
||||
|
||||
if (searchEnabled) {
|
||||
searchOptions.push(
|
||||
{
|
||||
key: 'prompt-has',
|
||||
label: (
|
||||
<>
|
||||
<mark>has:</mark>{' '}
|
||||
<FormattedList
|
||||
type='disjunction'
|
||||
value={['media', 'poll', 'embed']}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('has:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-is',
|
||||
label: (
|
||||
<>
|
||||
<mark>is:</mark>{' '}
|
||||
<FormattedList type='disjunction' value={['reply', 'sensitive']} />
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('is:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-my',
|
||||
label: (
|
||||
<>
|
||||
<mark>my:</mark>{' '}
|
||||
<FormattedList type='disjunction' value={['fav', 'bm']} />
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('my:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-language',
|
||||
label: (
|
||||
<>
|
||||
<mark>language:</mark>{' '}
|
||||
<FormattedMessage
|
||||
id='search_popout.language_code'
|
||||
defaultMessage='ISO language code'
|
||||
/>
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('language:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-from',
|
||||
label: (
|
||||
<>
|
||||
<mark>from:</mark>{' '}
|
||||
<FormattedMessage id='search_popout.user' defaultMessage='user' />
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('from:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-domain',
|
||||
label: (
|
||||
<>
|
||||
<mark>language:</mark>{' '}
|
||||
<FormattedMessage
|
||||
id='search_popout.domain'
|
||||
defaultMessage='user domain'
|
||||
/>
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('domain:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-before',
|
||||
label: (
|
||||
<>
|
||||
<mark>before:</mark>{' '}
|
||||
<FormattedMessage
|
||||
id='search_popout.specific_date'
|
||||
defaultMessage='specific date'
|
||||
/>
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('before:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-during',
|
||||
label: (
|
||||
<>
|
||||
<mark>during:</mark>{' '}
|
||||
<FormattedMessage
|
||||
id='search_popout.specific_date'
|
||||
defaultMessage='specific date'
|
||||
/>
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('during:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-after',
|
||||
label: (
|
||||
<>
|
||||
<mark>after:</mark>{' '}
|
||||
<FormattedMessage
|
||||
id='search_popout.specific_date'
|
||||
defaultMessage='specific date'
|
||||
/>
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('after:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-in',
|
||||
label: (
|
||||
<>
|
||||
<mark>in:</mark>{' '}
|
||||
<FormattedList
|
||||
type='disjunction'
|
||||
value={['all', 'library', 'public']}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('in:');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompt-order',
|
||||
label: (
|
||||
<>
|
||||
<mark>order:</mark>{' '}
|
||||
<FormattedList type='disjunction' value={['desc', 'asc']} />
|
||||
</>
|
||||
),
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
insertText('order:');
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const recentOptions: SearchOption[] = recent.map((search) => ({
|
||||
key: `${search.type}/${search.q}`,
|
||||
label: labelForRecentSearch(search),
|
||||
action: () => {
|
||||
setValue(search.q);
|
||||
|
||||
if (search.type === 'account') {
|
||||
history.push(`/@${search.q}`);
|
||||
} else if (search.type === 'hashtag') {
|
||||
history.push(`/tags/${search.q}`);
|
||||
} else {
|
||||
const queryParams = new URLSearchParams({ q: search.q });
|
||||
if (search.type) queryParams.set('type', search.type);
|
||||
history.push({ pathname: '/search', search: queryParams.toString() });
|
||||
}
|
||||
|
||||
unfocus();
|
||||
},
|
||||
forget: (e) => {
|
||||
e.stopPropagation();
|
||||
void dispatch(forgetSearchResult(search.q));
|
||||
},
|
||||
}));
|
||||
|
||||
const navigableOptions = hasValue
|
||||
? quickActions.concat(searchOptions)
|
||||
: recentOptions.concat(quickActions, searchOptions);
|
||||
|
||||
const insertText = (text: string) => {
|
||||
setValue((currentValue) => {
|
||||
if (currentValue === '') {
|
||||
return text;
|
||||
} else if (currentValue.endsWith(' ')) {
|
||||
return `${currentValue}${text}`;
|
||||
} else {
|
||||
return `${currentValue} ${text}`;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submit = useCallback(
|
||||
(q: string, type?: SearchType) => {
|
||||
void dispatch(clickSearchResult({ q, type }));
|
||||
const queryParams = new URLSearchParams({ q });
|
||||
if (type) queryParams.set('type', type);
|
||||
history.push({ pathname: '/search', search: queryParams.toString() });
|
||||
unfocus();
|
||||
},
|
||||
[dispatch, history],
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setValue(value);
|
||||
|
||||
const trimmedValue = value.trim();
|
||||
const newQuickActions = [];
|
||||
|
||||
if (trimmedValue.length > 0) {
|
||||
const couldBeURL =
|
||||
trimmedValue.startsWith('https://') && !trimmedValue.includes(' ');
|
||||
|
||||
if (couldBeURL) {
|
||||
newQuickActions.push({
|
||||
key: 'open-url',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='search.quick_action.open_url'
|
||||
defaultMessage='Open URL in Mastodon'
|
||||
/>
|
||||
),
|
||||
action: async () => {
|
||||
const result = await dispatch(openURL({ url: trimmedValue }));
|
||||
|
||||
if (isFulfilled(result)) {
|
||||
if (result.payload.accounts[0]) {
|
||||
history.push(`/@${result.payload.accounts[0].acct}`);
|
||||
} else if (result.payload.statuses[0]) {
|
||||
history.push(
|
||||
`/@${result.payload.statuses[0].account.acct}/${result.payload.statuses[0].id}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unfocus();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const couldBeHashtag =
|
||||
(trimmedValue.startsWith('#') && trimmedValue.length > 1) ||
|
||||
trimmedValue.match(HASHTAG_REGEX);
|
||||
|
||||
if (couldBeHashtag) {
|
||||
newQuickActions.push({
|
||||
key: 'go-to-hashtag',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='search.quick_action.go_to_hashtag'
|
||||
defaultMessage='Go to hashtag {x}'
|
||||
values={{ x: <mark>#{trimmedValue.replace(/^#/, '')}</mark> }}
|
||||
/>
|
||||
),
|
||||
action: () => {
|
||||
const query = trimmedValue.replace(/^#/, '');
|
||||
history.push(`/tags/${query}`);
|
||||
void dispatch(clickSearchResult({ q: query, type: 'hashtag' }));
|
||||
unfocus();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const couldBeUsername = /^@?[a-z0-9_-]+(@[^\s]+)?$/i.exec(trimmedValue);
|
||||
|
||||
if (couldBeUsername) {
|
||||
newQuickActions.push({
|
||||
key: 'go-to-account',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='search.quick_action.go_to_account'
|
||||
defaultMessage='Go to profile {x}'
|
||||
values={{ x: <mark>@{trimmedValue.replace(/^@/, '')}</mark> }}
|
||||
/>
|
||||
),
|
||||
action: () => {
|
||||
const query = trimmedValue.replace(/^@/, '');
|
||||
history.push(`/@${query}`);
|
||||
void dispatch(clickSearchResult({ q: query, type: 'account' }));
|
||||
unfocus();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const couldBeStatusSearch = searchEnabled;
|
||||
|
||||
if (couldBeStatusSearch && signedIn) {
|
||||
newQuickActions.push({
|
||||
key: 'status-search',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='search.quick_action.status_search'
|
||||
defaultMessage='Posts matching {x}'
|
||||
values={{ x: <mark>{trimmedValue}</mark> }}
|
||||
/>
|
||||
),
|
||||
action: () => {
|
||||
submit(trimmedValue, 'statuses');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
newQuickActions.push({
|
||||
key: 'account-search',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='search.quick_action.account_search'
|
||||
defaultMessage='Profiles matching {x}'
|
||||
values={{ x: <mark>{trimmedValue}</mark> }}
|
||||
/>
|
||||
),
|
||||
action: () => {
|
||||
submit(trimmedValue, 'accounts');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setQuickActions(newQuickActions);
|
||||
},
|
||||
[dispatch, history, signedIn, setValue, setQuickActions, submit],
|
||||
);
|
||||
|
||||
const handleClear = useCallback(() => {
|
||||
setValue('');
|
||||
setQuickActions([]);
|
||||
setSelectedOption(-1);
|
||||
}, [setValue, setQuickActions, setSelectedOption]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
unfocus();
|
||||
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
|
||||
if (navigableOptions.length > 0) {
|
||||
setSelectedOption(
|
||||
Math.min(selectedOption + 1, navigableOptions.length - 1),
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
|
||||
if (navigableOptions.length > 0) {
|
||||
setSelectedOption(Math.max(selectedOption - 1, -1));
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
|
||||
if (selectedOption === -1) {
|
||||
submit(value);
|
||||
} else if (navigableOptions.length > 0) {
|
||||
navigableOptions[selectedOption]?.action(e);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Delete':
|
||||
if (selectedOption > -1 && navigableOptions.length > 0) {
|
||||
const search = navigableOptions[selectedOption];
|
||||
|
||||
if (typeof search?.forget === 'function') {
|
||||
e.preventDefault();
|
||||
search.forget(e);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
},
|
||||
[navigableOptions, value, selectedOption, setSelectedOption, submit],
|
||||
);
|
||||
|
||||
const handleFocus = useCallback(() => {
|
||||
setExpanded(true);
|
||||
setSelectedOption(-1);
|
||||
|
||||
if (searchInputRef.current && !singleColumn) {
|
||||
const { left, right } = searchInputRef.current.getBoundingClientRect();
|
||||
|
||||
if (
|
||||
left < 0 ||
|
||||
right > (window.innerWidth || document.documentElement.clientWidth)
|
||||
) {
|
||||
searchInputRef.current.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}, [setExpanded, setSelectedOption, singleColumn]);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
setExpanded(false);
|
||||
setSelectedOption(-1);
|
||||
}, [setExpanded, setSelectedOption]);
|
||||
|
||||
return (
|
||||
<form className={classNames('search', { active: expanded })}>
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
className='search__input'
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(
|
||||
signedIn ? messages.placeholderSignedIn : messages.placeholder,
|
||||
)}
|
||||
aria-label={intl.formatMessage(
|
||||
signedIn ? messages.placeholderSignedIn : messages.placeholder,
|
||||
)}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
|
||||
<button type='button' className='search__icon' onClick={handleClear}>
|
||||
<Icon
|
||||
id='search'
|
||||
icon={SearchIcon}
|
||||
className={hasValue ? '' : 'active'}
|
||||
/>
|
||||
<Icon
|
||||
id='times-circle'
|
||||
icon={CancelIcon}
|
||||
className={hasValue ? 'active' : ''}
|
||||
aria-label={intl.formatMessage(messages.placeholder)}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div className='search__popout'>
|
||||
{!hasValue && (
|
||||
<>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id='search_popout.recent'
|
||||
defaultMessage='Recent searches'
|
||||
/>
|
||||
</h4>
|
||||
|
||||
<div className='search__popout__menu'>
|
||||
{recentOptions.length > 0 ? (
|
||||
recentOptions.map(({ label, key, action, forget }, i) => (
|
||||
<button
|
||||
key={key}
|
||||
onMouseDown={action}
|
||||
className={classNames(
|
||||
'search__popout__menu__item search__popout__menu__item--flex',
|
||||
{ selected: selectedOption === i },
|
||||
)}
|
||||
>
|
||||
<span>{label}</span>
|
||||
<button className='icon-button' onMouseDown={forget}>
|
||||
<Icon id='times' icon={CloseIcon} />
|
||||
</button>
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<div className='search__popout__menu__message'>
|
||||
<FormattedMessage
|
||||
id='search.no_recent_searches'
|
||||
defaultMessage='No recent searches'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{quickActions.length > 0 && (
|
||||
<>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id='search_popout.quick_actions'
|
||||
defaultMessage='Quick actions'
|
||||
/>
|
||||
</h4>
|
||||
|
||||
<div className='search__popout__menu'>
|
||||
{quickActions.map(({ key, label, action }, i) => (
|
||||
<button
|
||||
key={key}
|
||||
onMouseDown={action}
|
||||
className={classNames('search__popout__menu__item', {
|
||||
selected: selectedOption === i,
|
||||
})}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id='search_popout.options'
|
||||
defaultMessage='Search options'
|
||||
/>
|
||||
</h4>
|
||||
|
||||
{searchEnabled && signedIn ? (
|
||||
<div className='search__popout__menu'>
|
||||
{searchOptions.map(({ key, label, action }, i) => (
|
||||
<button
|
||||
key={key}
|
||||
onMouseDown={action}
|
||||
className={classNames('search__popout__menu__item', {
|
||||
selected:
|
||||
selectedOption ===
|
||||
(quickActions.length || recent.length) + i,
|
||||
})}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className='search__popout__menu__message'>
|
||||
{searchEnabled ? (
|
||||
<FormattedMessage
|
||||
id='search_popout.full_text_search_logged_out_message'
|
||||
defaultMessage='Only available when logged in.'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='search_popout.full_text_search_disabled_message'
|
||||
defaultMessage='Not available on {domain}.'
|
||||
values={{ domain }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
|
@ -1,93 +0,0 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import TagIcon from '@/material-icons/400-24px/tag.svg?react';
|
||||
import { expandSearch } from 'mastodon/actions/search';
|
||||
import { Account } from 'mastodon/components/account';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LoadMore } from 'mastodon/components/load_more';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { SearchSection } from 'mastodon/features/explore/components/search_section';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import { ImmutableHashtag as Hashtag } from '../../../components/hashtag';
|
||||
import StatusContainer from '../../../containers/status_container';
|
||||
|
||||
const INITIAL_PAGE_LIMIT = 10;
|
||||
|
||||
const withoutLastResult = list => {
|
||||
if (list.size > INITIAL_PAGE_LIMIT && list.size % INITIAL_PAGE_LIMIT === 1) {
|
||||
return list.skipLast(1);
|
||||
} else {
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
export const SearchResults = () => {
|
||||
const results = useAppSelector((state) => state.getIn(['search', 'results']));
|
||||
const isLoading = useAppSelector((state) => state.getIn(['search', 'isLoading']));
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleLoadMoreAccounts = useCallback(() => {
|
||||
dispatch(expandSearch('accounts'));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleLoadMoreStatuses = useCallback(() => {
|
||||
dispatch(expandSearch('statuses'));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleLoadMoreHashtags = useCallback(() => {
|
||||
dispatch(expandSearch('hashtags'));
|
||||
}, [dispatch]);
|
||||
|
||||
let accounts, statuses, hashtags;
|
||||
|
||||
if (results.get('accounts') && results.get('accounts').size > 0) {
|
||||
accounts = (
|
||||
<SearchSection title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}>
|
||||
{withoutLastResult(results.get('accounts')).map(accountId => <Account key={accountId} id={accountId} />)}
|
||||
{(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={handleLoadMoreAccounts} />}
|
||||
</SearchSection>
|
||||
);
|
||||
}
|
||||
|
||||
if (results.get('hashtags') && results.get('hashtags').size > 0) {
|
||||
hashtags = (
|
||||
<SearchSection title={<><Icon id='hashtag' icon={TagIcon} /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}>
|
||||
{withoutLastResult(results.get('hashtags')).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
|
||||
{(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={handleLoadMoreHashtags} />}
|
||||
</SearchSection>
|
||||
);
|
||||
}
|
||||
|
||||
if (results.get('statuses') && results.get('statuses').size > 0) {
|
||||
statuses = (
|
||||
<SearchSection title={<><Icon id='quote-right' icon={FindInPageIcon} /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}>
|
||||
{withoutLastResult(results.get('statuses')).map(statusId => <StatusContainer key={statusId} id={statusId} />)}
|
||||
{(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={handleLoadMoreStatuses} />}
|
||||
</SearchSection>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='search-results'>
|
||||
{!accounts && !hashtags && !statuses && (
|
||||
isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<div className='empty-column-indicator'>
|
||||
<FormattedMessage id='search_results.nothing_found' defaultMessage='Could not find anything for these search terms' />
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{accounts}
|
||||
{hashtags}
|
||||
{statuses}
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
|
@ -1,59 +0,0 @@
|
|||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
changeSearch,
|
||||
clearSearch,
|
||||
submitSearch,
|
||||
showSearch,
|
||||
openURL,
|
||||
clickSearchResult,
|
||||
forgetSearchResult,
|
||||
} from 'mastodon/actions/search';
|
||||
|
||||
import Search from '../components/search';
|
||||
|
||||
const getRecentSearches = createSelector(
|
||||
state => state.getIn(['search', 'recent']),
|
||||
recent => recent.reverse(),
|
||||
);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
value: state.getIn(['search', 'value']),
|
||||
submitted: state.getIn(['search', 'submitted']),
|
||||
recent: getRecentSearches(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
||||
onChange (value) {
|
||||
dispatch(changeSearch(value));
|
||||
},
|
||||
|
||||
onClear () {
|
||||
dispatch(clearSearch());
|
||||
},
|
||||
|
||||
onSubmit (type) {
|
||||
dispatch(submitSearch(type));
|
||||
},
|
||||
|
||||
onShow () {
|
||||
dispatch(showSearch());
|
||||
},
|
||||
|
||||
onOpenURL (q, routerHistory) {
|
||||
dispatch(openURL(q, routerHistory));
|
||||
},
|
||||
|
||||
onClickSearchResult (q, type) {
|
||||
dispatch(clickSearchResult(q, type));
|
||||
},
|
||||
|
||||
onForgetSearchResult (q) {
|
||||
dispatch(forgetSearchResult(q));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Search);
|
|
@ -9,8 +9,6 @@ import { Link } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import spring from 'react-motion/lib/spring';
|
||||
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||
import LogoutIcon from '@/material-icons/400-24px/logout.svg?react';
|
||||
|
@ -26,11 +24,9 @@ import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
|
|||
import { changeComposing, mountCompose, unmountCompose } from '../../actions/compose';
|
||||
import { mascot } from '../../initial_state';
|
||||
import { isMobile } from '../../is_mobile';
|
||||
import Motion from '../ui/util/optional_motion';
|
||||
|
||||
import { SearchResults } from './components/search_results';
|
||||
import { Search } from './components/search';
|
||||
import ComposeFormContainer from './containers/compose_form_container';
|
||||
import SearchContainer from './containers/search_container';
|
||||
|
||||
const messages = defineMessages({
|
||||
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
||||
|
@ -43,9 +39,8 @@ const messages = defineMessages({
|
|||
compose: { id: 'navigation_bar.compose', defaultMessage: 'Compose new post' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
const mapStateToProps = (state) => ({
|
||||
columns: state.getIn(['settings', 'columns']),
|
||||
showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : false,
|
||||
});
|
||||
|
||||
class Compose extends PureComponent {
|
||||
|
@ -54,7 +49,6 @@ class Compose extends PureComponent {
|
|||
dispatch: PropTypes.func.isRequired,
|
||||
columns: ImmutablePropTypes.list.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
showSearch: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
@ -88,7 +82,7 @@ class Compose extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { multiColumn, showSearch, intl } = this.props;
|
||||
const { multiColumn, intl } = this.props;
|
||||
|
||||
if (multiColumn) {
|
||||
const { columns } = this.props;
|
||||
|
@ -113,7 +107,7 @@ class Compose extends PureComponent {
|
|||
<a href='/auth/sign_out' className='drawer__tab' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)} onClick={this.handleLogoutClick}><Icon id='sign-out' icon={LogoutIcon} /></a>
|
||||
</nav>
|
||||
|
||||
{multiColumn && <SearchContainer /> }
|
||||
{multiColumn && <Search /> }
|
||||
|
||||
<div className='drawer__pager'>
|
||||
<div className='drawer__inner' onFocus={this.onFocus}>
|
||||
|
@ -123,14 +117,6 @@ class Compose extends PureComponent {
|
|||
<img alt='' draggable='false' src={mascot || elephantUIPlane} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
|
||||
{({ x }) => (
|
||||
<div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
|
||||
<SearchResults />
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -90,8 +90,8 @@ describe('emoji', () => {
|
|||
});
|
||||
|
||||
it('keeps ordering as expected (issue fixed by PR 20677)', () => {
|
||||
expect(emojify('<p>💕 <a class="hashtag" href="https://example.com/tags/foo" rel="nofollow noopener noreferrer" target="_blank">#<span>foo</span></a> test: foo.</p>'))
|
||||
.toEqual('<p><picture><img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg"></picture> <a class="hashtag" href="https://example.com/tags/foo" rel="nofollow noopener noreferrer" target="_blank">#<span>foo</span></a> test: foo.</p>');
|
||||
expect(emojify('<p>💕 <a class="hashtag" href="https://example.com/tags/foo" rel="nofollow noopener" target="_blank">#<span>foo</span></a> test: foo.</p>'))
|
||||
.toEqual('<p><picture><img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg"></picture> <a class="hashtag" href="https://example.com/tags/foo" rel="nofollow noopener" target="_blank">#<span>foo</span></a> test: foo.</p>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export const SearchSection = ({ title, onClickMore, children }) => (
|
||||
<div className='search-results__section'>
|
||||
<div className='search-results__section__header'>
|
||||
<h3>{title}</h3>
|
||||
{onClickMore && <button onClick={onClickMore}><FormattedMessage id='search_results.see_all' defaultMessage='See all' /></button>}
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
SearchSection.propTypes = {
|
||||
title: PropTypes.node.isRequired,
|
||||
onClickMore: PropTypes.func,
|
||||
children: PropTypes.children,
|
||||
};
|
|
@ -1,114 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
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 Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import Search from 'mastodon/features/compose/containers/search_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { trendsEnabled } from 'mastodon/initial_state';
|
||||
|
||||
import Links from './links';
|
||||
import SearchResults from './results';
|
||||
import Statuses from './statuses';
|
||||
import Suggestions from './suggestions';
|
||||
import Tags from './tags';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'explore.title', defaultMessage: 'Explore' },
|
||||
searchResults: { id: 'explore.search_results', defaultMessage: 'Search results' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
layout: state.getIn(['meta', 'layout']),
|
||||
isSearching: state.getIn(['search', 'submitted']) || !trendsEnabled,
|
||||
});
|
||||
|
||||
class Explore extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
intl: PropTypes.object.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
isSearching: PropTypes.bool,
|
||||
};
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { intl, multiColumn, isSearching } = this.props;
|
||||
const { signedIn } = this.props.identity;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
<ColumnHeader
|
||||
icon={isSearching ? 'search' : 'explore'}
|
||||
iconComponent={isSearching ? SearchIcon : ExploreIcon}
|
||||
title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)}
|
||||
onClick={this.handleHeaderClick}
|
||||
multiColumn={multiColumn}
|
||||
/>
|
||||
|
||||
<div className='explore__search-header'>
|
||||
<Search />
|
||||
</div>
|
||||
|
||||
{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/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>
|
||||
</>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withIdentity(connect(mapStateToProps)(injectIntl(Explore)));
|
105
app/javascript/mastodon/features/explore/index.tsx
Normal file
105
app/javascript/mastodon/features/explore/index.tsx
Normal file
|
@ -0,0 +1,105 @@
|
|||
import { useCallback, useRef } from 'react';
|
||||
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { NavLink, Switch, Route } from 'react-router-dom';
|
||||
|
||||
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
||||
import { Column } from 'mastodon/components/column';
|
||||
import type { ColumnRef } from 'mastodon/components/column';
|
||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||
import { Search } from 'mastodon/features/compose/components/search';
|
||||
import { useIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import Links from './links';
|
||||
import Statuses from './statuses';
|
||||
import Suggestions from './suggestions';
|
||||
import Tags from './tags';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'explore.title', defaultMessage: 'Explore' },
|
||||
});
|
||||
|
||||
const Explore: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
||||
const { signedIn } = useIdentity();
|
||||
const intl = useIntl();
|
||||
const columnRef = useRef<ColumnRef>(null);
|
||||
|
||||
const handleHeaderClick = useCallback(() => {
|
||||
columnRef.current?.scrollTop();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
ref={columnRef}
|
||||
label={intl.formatMessage(messages.title)}
|
||||
>
|
||||
<ColumnHeader
|
||||
icon={'explore'}
|
||||
iconComponent={ExploreIcon}
|
||||
title={intl.formatMessage(messages.title)}
|
||||
onClick={handleHeaderClick}
|
||||
multiColumn={multiColumn}
|
||||
/>
|
||||
|
||||
<div className='explore__search-header'>
|
||||
<Search singleColumn />
|
||||
</div>
|
||||
|
||||
<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/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']}>
|
||||
<Statuses multiColumn={multiColumn} />
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='all' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default Explore;
|
|
@ -1,232 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import TagIcon from '@/material-icons/400-24px/tag.svg?react';
|
||||
import { submitSearch, expandSearch } from 'mastodon/actions/search';
|
||||
import { Account } from 'mastodon/components/account';
|
||||
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||
import Status from 'mastodon/containers/status_container';
|
||||
|
||||
import { SearchSection } from './components/search_section';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'search_results.title', defaultMessage: 'Search for {q}' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isLoading: state.getIn(['search', 'isLoading']),
|
||||
results: state.getIn(['search', 'results']),
|
||||
q: state.getIn(['search', 'searchTerm']),
|
||||
submittedType: state.getIn(['search', 'type']),
|
||||
});
|
||||
|
||||
const INITIAL_PAGE_LIMIT = 10;
|
||||
const INITIAL_DISPLAY = 4;
|
||||
|
||||
const hidePeek = list => {
|
||||
if (list.size > INITIAL_PAGE_LIMIT && list.size % INITIAL_PAGE_LIMIT === 1) {
|
||||
return list.skipLast(1);
|
||||
} else {
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
const renderAccounts = accounts => hidePeek(accounts).map(id => (
|
||||
<Account key={id} id={id} />
|
||||
));
|
||||
|
||||
const renderHashtags = hashtags => hidePeek(hashtags).map(hashtag => (
|
||||
<Hashtag key={hashtag.get('name')} hashtag={hashtag} />
|
||||
));
|
||||
|
||||
const renderStatuses = statuses => hidePeek(statuses).map(id => (
|
||||
<Status key={id} id={id} contextType='explore' />
|
||||
));
|
||||
|
||||
class Results extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
results: ImmutablePropTypes.contains({
|
||||
accounts: ImmutablePropTypes.orderedSet,
|
||||
statuses: ImmutablePropTypes.orderedSet,
|
||||
hashtags: ImmutablePropTypes.orderedSet,
|
||||
}),
|
||||
isLoading: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
q: PropTypes.string,
|
||||
intl: PropTypes.object,
|
||||
submittedType: PropTypes.oneOf(['accounts', 'statuses', 'hashtags']),
|
||||
};
|
||||
|
||||
state = {
|
||||
type: this.props.submittedType || 'all',
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (props.submittedType !== state.type) {
|
||||
return {
|
||||
type: props.submittedType || 'all',
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
handleSelectAll = () => {
|
||||
const { submittedType, dispatch } = this.props;
|
||||
|
||||
// If we originally searched for a specific type, we need to resubmit
|
||||
// the query to get all types of results
|
||||
if (submittedType) {
|
||||
dispatch(submitSearch());
|
||||
}
|
||||
|
||||
this.setState({ type: 'all' });
|
||||
};
|
||||
|
||||
handleSelectAccounts = () => {
|
||||
const { submittedType, dispatch } = this.props;
|
||||
|
||||
// If we originally searched for something else (but not everything),
|
||||
// we need to resubmit the query for this specific type
|
||||
if (submittedType !== 'accounts') {
|
||||
dispatch(submitSearch('accounts'));
|
||||
}
|
||||
|
||||
this.setState({ type: 'accounts' });
|
||||
};
|
||||
|
||||
handleSelectHashtags = () => {
|
||||
const { submittedType, dispatch } = this.props;
|
||||
|
||||
// If we originally searched for something else (but not everything),
|
||||
// we need to resubmit the query for this specific type
|
||||
if (submittedType !== 'hashtags') {
|
||||
dispatch(submitSearch('hashtags'));
|
||||
}
|
||||
|
||||
this.setState({ type: 'hashtags' });
|
||||
};
|
||||
|
||||
handleSelectStatuses = () => {
|
||||
const { submittedType, dispatch } = this.props;
|
||||
|
||||
// If we originally searched for something else (but not everything),
|
||||
// we need to resubmit the query for this specific type
|
||||
if (submittedType !== 'statuses') {
|
||||
dispatch(submitSearch('statuses'));
|
||||
}
|
||||
|
||||
this.setState({ type: 'statuses' });
|
||||
};
|
||||
|
||||
handleLoadMoreAccounts = () => this._loadMore('accounts');
|
||||
handleLoadMoreStatuses = () => this._loadMore('statuses');
|
||||
handleLoadMoreHashtags = () => this._loadMore('hashtags');
|
||||
|
||||
_loadMore (type) {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(expandSearch(type));
|
||||
}
|
||||
|
||||
handleLoadMore = () => {
|
||||
const { type } = this.state;
|
||||
|
||||
if (type !== 'all') {
|
||||
this._loadMore(type);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, isLoading, q, results } = this.props;
|
||||
const { type } = this.state;
|
||||
|
||||
// We request 1 more result than we display so we can tell if there'd be a next page
|
||||
const hasMore = type !== 'all' ? results.get(type, ImmutableList()).size > INITIAL_PAGE_LIMIT && results.get(type).size % INITIAL_PAGE_LIMIT === 1 : false;
|
||||
|
||||
let filteredResults;
|
||||
|
||||
const accounts = results.get('accounts', ImmutableList());
|
||||
const hashtags = results.get('hashtags', ImmutableList());
|
||||
const statuses = results.get('statuses', ImmutableList());
|
||||
|
||||
switch(type) {
|
||||
case 'all':
|
||||
filteredResults = (accounts.size + hashtags.size + statuses.size) > 0 ? (
|
||||
<>
|
||||
{accounts.size > 0 && (
|
||||
<SearchSection key='accounts' title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>} onClickMore={this.handleLoadMoreAccounts}>
|
||||
{accounts.take(INITIAL_DISPLAY).map(id => <Account key={id} id={id} />)}
|
||||
</SearchSection>
|
||||
)}
|
||||
|
||||
{hashtags.size > 0 && (
|
||||
<SearchSection key='hashtags' title={<><Icon id='hashtag' icon={TagIcon} /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>} onClickMore={this.handleLoadMoreHashtags}>
|
||||
{hashtags.take(INITIAL_DISPLAY).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
|
||||
</SearchSection>
|
||||
)}
|
||||
|
||||
{statuses.size > 0 && (
|
||||
<SearchSection key='statuses' title={<><Icon id='quote-right' icon={FindInPageIcon} /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>} onClickMore={this.handleLoadMoreStatuses}>
|
||||
{statuses.take(INITIAL_DISPLAY).map(id => <Status key={id} id={id} contextType='explore' />)}
|
||||
</SearchSection>
|
||||
)}
|
||||
</>
|
||||
) : [];
|
||||
break;
|
||||
case 'accounts':
|
||||
filteredResults = renderAccounts(accounts);
|
||||
break;
|
||||
case 'hashtags':
|
||||
filteredResults = renderHashtags(hashtags);
|
||||
break;
|
||||
case 'statuses':
|
||||
filteredResults = renderStatuses(statuses);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='account__section-headline'>
|
||||
<button onClick={this.handleSelectAll} className={type === 'all' ? 'active' : undefined}><FormattedMessage id='search_results.all' defaultMessage='All' /></button>
|
||||
<button onClick={this.handleSelectAccounts} className={type === 'accounts' ? 'active' : undefined}><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></button>
|
||||
<button onClick={this.handleSelectHashtags} className={type === 'hashtags' ? 'active' : undefined}><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></button>
|
||||
<button onClick={this.handleSelectStatuses} className={type === 'statuses' ? 'active' : undefined}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
|
||||
</div>
|
||||
|
||||
<div className='explore__search-results' data-nosnippet>
|
||||
<ScrollableList
|
||||
scrollKey='search-results'
|
||||
isLoading={isLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
emptyMessage={<FormattedMessage id='search_results.nothing_found' defaultMessage='Could not find anything for these search terms' />}
|
||||
bindToDocument
|
||||
>
|
||||
{filteredResults}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title, { q })}</title>
|
||||
</Helmet>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Results));
|
|
@ -85,7 +85,7 @@ class ContentWithRouter extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
link.setAttribute('target', '_blank');
|
||||
link.setAttribute('rel', 'noopener noreferrer');
|
||||
link.setAttribute('rel', 'noopener');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import AntennaIcon from '@/material-icons/400-24px/wifi.svg?react';
|
|||
import { fetchFollowRequests } from 'mastodon/actions/accounts';
|
||||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import LinkFooter from 'mastodon/features/ui/components/link_footer';
|
||||
import { LinkFooter } from 'mastodon/features/ui/components/link_footer';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions';
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ export const RelationshipsSeveranceEvent = ({ type, target, followingCount, foll
|
|||
|
||||
<div className='notification-group__main'>
|
||||
<p>{intl.formatMessage(messages[type], { from: <strong>{domain}</strong>, target: <strong>{target}</strong>, followingCount, followersCount })}</p>
|
||||
<a href='/severed_relationships' target='_blank' rel='noopener noreferrer' className='link-button'><FormattedMessage id='notification.relationships_severance_event.learn_more' defaultMessage='Learn more' /></a>
|
||||
<a href='/severed_relationships' target='_blank' rel='noopener' className='link-button'><FormattedMessage id='notification.relationships_severance_event.learn_more' defaultMessage='Learn more' /></a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -55,7 +55,7 @@ class Report extends ImmutablePureComponent {
|
|||
</div>
|
||||
|
||||
<div className='notification__report__actions'>
|
||||
<a href={`/admin/reports/${report.get('id')}`} className='button' target='_blank' rel='noopener noreferrer'>{intl.formatMessage(messages.openReport)}</a>
|
||||
<a href={`/admin/reports/${report.get('id')}`} className='button' target='_blank' rel='noopener'>{intl.formatMessage(messages.openReport)}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -70,7 +70,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
|
|||
if (button === 0 && !(ctrlKey || metaKey)) {
|
||||
history.push(path);
|
||||
} else if (button === 1 || (button === 0 && (ctrlKey || metaKey))) {
|
||||
window.open(path, '_blank', 'noreferrer noopener');
|
||||
window.open(path, '_blank', 'noopener');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,34 @@ const labelRenderer: LabelRenderer = (displayedName, total, seeMoreHref) => {
|
|||
);
|
||||
};
|
||||
|
||||
const privateLabelRenderer: LabelRenderer = (
|
||||
displayedName,
|
||||
total,
|
||||
seeMoreHref,
|
||||
) => {
|
||||
if (total === 1)
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.favourite_pm'
|
||||
defaultMessage='{name} favorited your private mention'
|
||||
values={{ name: displayedName }}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.favourite_pm.name_and_others_with_link'
|
||||
defaultMessage='{name} and <a>{count, plural, one {# other} other {# others}}</a> favorited your private mention'
|
||||
values={{
|
||||
name: displayedName,
|
||||
count: total - 1,
|
||||
a: (chunks) =>
|
||||
seeMoreHref ? <Link to={seeMoreHref}>{chunks}</Link> : chunks,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const NotificationFavourite: React.FC<{
|
||||
notification: NotificationGroupFavourite;
|
||||
unread: boolean;
|
||||
|
@ -44,6 +72,10 @@ export const NotificationFavourite: React.FC<{
|
|||
?.acct,
|
||||
);
|
||||
|
||||
const isPrivateMention = useAppSelector(
|
||||
(state) => state.statuses.getIn([statusId, 'visibility']) === 'direct',
|
||||
);
|
||||
|
||||
return (
|
||||
<NotificationGroupWithStatus
|
||||
type='favourite'
|
||||
|
@ -53,7 +85,7 @@ export const NotificationFavourite: React.FC<{
|
|||
statusId={notification.statusId}
|
||||
timestamp={notification.latest_page_notification_at}
|
||||
count={notification.notifications_count}
|
||||
labelRenderer={labelRenderer}
|
||||
labelRenderer={isPrivateMention ? privateLabelRenderer : labelRenderer}
|
||||
labelSeeMoreHref={
|
||||
statusAccount ? `/@${statusAccount}/${statusId}/favourites` : undefined
|
||||
}
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { FormattedMessage, FormattedDate, injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import api from 'mastodon/api';
|
||||
import Column from 'mastodon/components/column';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'privacy_policy.title', defaultMessage: 'Privacy Policy' },
|
||||
});
|
||||
|
||||
class PrivacyPolicy extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object,
|
||||
multiColumn: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
content: null,
|
||||
lastUpdated: null,
|
||||
isLoading: true,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
api().get('/api/v1/instance/privacy_policy').then(({ data }) => {
|
||||
this.setState({ content: data.content, lastUpdated: data.updated_at, isLoading: false });
|
||||
}).catch(() => {
|
||||
this.setState({ isLoading: false });
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl, multiColumn } = this.props;
|
||||
const { isLoading, content, lastUpdated } = this.state;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<h3><FormattedMessage id='privacy_policy.title' defaultMessage='Privacy Policy' /></h3>
|
||||
<p><FormattedMessage id='privacy_policy.last_updated' defaultMessage='Last updated {date}' values={{ date: isLoading ? <Skeleton width='10ch' /> : <FormattedDate value={lastUpdated} year='numeric' month='short' day='2-digit' /> }} /></p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='privacy-policy__body prose'
|
||||
dangerouslySetInnerHTML={{ __html: content }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='all' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(PrivacyPolicy);
|
90
app/javascript/mastodon/features/privacy_policy/index.tsx
Normal file
90
app/javascript/mastodon/features/privacy_policy/index.tsx
Normal file
|
@ -0,0 +1,90 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
FormattedMessage,
|
||||
FormattedDate,
|
||||
useIntl,
|
||||
defineMessages,
|
||||
} from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import { apiGetPrivacyPolicy } from 'mastodon/api/instance';
|
||||
import type { ApiPrivacyPolicyJSON } from 'mastodon/api_types/instance';
|
||||
import { Column } from 'mastodon/components/column';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'privacy_policy.title', defaultMessage: 'Privacy Policy' },
|
||||
});
|
||||
|
||||
const PrivacyPolicy: React.FC<{
|
||||
multiColumn: boolean;
|
||||
}> = ({ multiColumn }) => {
|
||||
const intl = useIntl();
|
||||
const [response, setResponse] = useState<ApiPrivacyPolicyJSON>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
apiGetPrivacyPolicy()
|
||||
.then((data) => {
|
||||
setResponse(data);
|
||||
setLoading(false);
|
||||
return '';
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
label={intl.formatMessage(messages.title)}
|
||||
>
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id='privacy_policy.title'
|
||||
defaultMessage='Privacy Policy'
|
||||
/>
|
||||
</h3>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='privacy_policy.last_updated'
|
||||
defaultMessage='Last updated {date}'
|
||||
values={{
|
||||
date: loading ? (
|
||||
<Skeleton width='10ch' />
|
||||
) : (
|
||||
<FormattedDate
|
||||
value={response?.updated_at}
|
||||
year='numeric'
|
||||
month='short'
|
||||
day='2-digit'
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{response && (
|
||||
<div
|
||||
className='privacy-policy__body prose'
|
||||
dangerouslySetInnerHTML={{ __html: response.content }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='all' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default PrivacyPolicy;
|
|
@ -0,0 +1,23 @@
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export const SearchSection: React.FC<{
|
||||
title: React.ReactNode;
|
||||
onClickMore?: () => void;
|
||||
children: React.ReactNode;
|
||||
}> = ({ title, onClickMore, children }) => (
|
||||
<div className='search-results__section'>
|
||||
<div className='search-results__section__header'>
|
||||
<h3>{title}</h3>
|
||||
{onClickMore && (
|
||||
<button onClick={onClickMore}>
|
||||
<FormattedMessage
|
||||
id='search_results.see_all'
|
||||
defaultMessage='See all'
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
304
app/javascript/mastodon/features/search/index.tsx
Normal file
304
app/javascript/mastodon/features/search/index.tsx
Normal file
|
@ -0,0 +1,304 @@
|
|||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import { useSearchParam } from '@/hooks/useSearchParam';
|
||||
import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
|
||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import TagIcon from '@/material-icons/400-24px/tag.svg?react';
|
||||
import { submitSearch, expandSearch } from 'mastodon/actions/search';
|
||||
import type { ApiSearchType } from 'mastodon/api_types/search';
|
||||
import { Account } from 'mastodon/components/account';
|
||||
import { Column } from 'mastodon/components/column';
|
||||
import type { ColumnRef } from 'mastodon/components/column';
|
||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||
import { CompatibilityHashtag as Hashtag } from 'mastodon/components/hashtag';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||
import Status from 'mastodon/containers/status_container';
|
||||
import { Search } from 'mastodon/features/compose/components/search';
|
||||
import type { Hashtag as HashtagType } from 'mastodon/models/tags';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import { SearchSection } from './components/search_section';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'search_results.title', defaultMessage: 'Search for "{q}"' },
|
||||
});
|
||||
|
||||
const INITIAL_PAGE_LIMIT = 10;
|
||||
const INITIAL_DISPLAY = 4;
|
||||
|
||||
const hidePeek = <T,>(list: T[]) => {
|
||||
if (
|
||||
list.length > INITIAL_PAGE_LIMIT &&
|
||||
list.length % INITIAL_PAGE_LIMIT === 1
|
||||
) {
|
||||
return list.slice(0, -2);
|
||||
} else {
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
const renderAccounts = (accountIds: string[]) =>
|
||||
hidePeek<string>(accountIds).map((id) => <Account key={id} id={id} />);
|
||||
|
||||
const renderHashtags = (hashtags: HashtagType[]) =>
|
||||
hidePeek<HashtagType>(hashtags).map((hashtag) => (
|
||||
<Hashtag key={hashtag.name} hashtag={hashtag} />
|
||||
));
|
||||
|
||||
const renderStatuses = (statusIds: string[]) =>
|
||||
hidePeek<string>(statusIds).map((id) => (
|
||||
// @ts-expect-error inferred props are wrong
|
||||
<Status key={id} id={id} contextType='explore' />
|
||||
));
|
||||
|
||||
type SearchType = 'all' | ApiSearchType;
|
||||
|
||||
const typeFromParam = (param?: string): SearchType => {
|
||||
if (param && ['all', 'accounts', 'statuses', 'hashtags'].includes(param)) {
|
||||
return param as SearchType;
|
||||
} else {
|
||||
return 'all';
|
||||
}
|
||||
};
|
||||
|
||||
export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
|
||||
multiColumn,
|
||||
}) => {
|
||||
const columnRef = useRef<ColumnRef>(null);
|
||||
const intl = useIntl();
|
||||
const [q] = useSearchParam('q');
|
||||
const [type, setType] = useSearchParam('type');
|
||||
const isLoading = useAppSelector((state) => state.search.loading);
|
||||
const results = useAppSelector((state) => state.search.results);
|
||||
const dispatch = useAppDispatch();
|
||||
const mappedType = typeFromParam(type);
|
||||
const trimmedValue = q?.trim() ?? '';
|
||||
|
||||
useEffect(() => {
|
||||
if (trimmedValue.length > 0) {
|
||||
void dispatch(
|
||||
submitSearch({
|
||||
q: trimmedValue,
|
||||
type: mappedType === 'all' ? undefined : mappedType,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [dispatch, trimmedValue, mappedType]);
|
||||
|
||||
const handleHeaderClick = useCallback(() => {
|
||||
columnRef.current?.scrollTop();
|
||||
}, []);
|
||||
|
||||
const handleSelectAll = useCallback(() => {
|
||||
setType(null);
|
||||
}, [setType]);
|
||||
|
||||
const handleSelectAccounts = useCallback(() => {
|
||||
setType('accounts');
|
||||
}, [setType]);
|
||||
|
||||
const handleSelectHashtags = useCallback(() => {
|
||||
setType('hashtags');
|
||||
}, [setType]);
|
||||
|
||||
const handleSelectStatuses = useCallback(() => {
|
||||
setType('statuses');
|
||||
}, [setType]);
|
||||
|
||||
const handleLoadMore = useCallback(() => {
|
||||
if (mappedType !== 'all') {
|
||||
void dispatch(expandSearch({ type: mappedType }));
|
||||
}
|
||||
}, [dispatch, mappedType]);
|
||||
|
||||
// We request 1 more result than we display so we can tell if there'd be a next page
|
||||
const hasMore =
|
||||
mappedType !== 'all' && results
|
||||
? results[mappedType].length > INITIAL_PAGE_LIMIT &&
|
||||
results[mappedType].length % INITIAL_PAGE_LIMIT === 1
|
||||
: false;
|
||||
|
||||
let filteredResults;
|
||||
|
||||
if (results) {
|
||||
switch (mappedType) {
|
||||
case 'all':
|
||||
filteredResults =
|
||||
results.accounts.length +
|
||||
results.hashtags.length +
|
||||
results.statuses.length >
|
||||
0 ? (
|
||||
<>
|
||||
{results.accounts.length > 0 && (
|
||||
<SearchSection
|
||||
key='accounts'
|
||||
title={
|
||||
<>
|
||||
<Icon id='users' icon={PeopleIcon} />
|
||||
<FormattedMessage
|
||||
id='search_results.accounts'
|
||||
defaultMessage='Profiles'
|
||||
/>
|
||||
</>
|
||||
}
|
||||
onClickMore={handleSelectAccounts}
|
||||
>
|
||||
{results.accounts.slice(0, INITIAL_DISPLAY).map((id) => (
|
||||
<Account key={id} id={id} />
|
||||
))}
|
||||
</SearchSection>
|
||||
)}
|
||||
|
||||
{results.hashtags.length > 0 && (
|
||||
<SearchSection
|
||||
key='hashtags'
|
||||
title={
|
||||
<>
|
||||
<Icon id='hashtag' icon={TagIcon} />
|
||||
<FormattedMessage
|
||||
id='search_results.hashtags'
|
||||
defaultMessage='Hashtags'
|
||||
/>
|
||||
</>
|
||||
}
|
||||
onClickMore={handleSelectHashtags}
|
||||
>
|
||||
{results.hashtags.slice(0, INITIAL_DISPLAY).map((hashtag) => (
|
||||
<Hashtag key={hashtag.name} hashtag={hashtag} />
|
||||
))}
|
||||
</SearchSection>
|
||||
)}
|
||||
|
||||
{results.statuses.length > 0 && (
|
||||
<SearchSection
|
||||
key='statuses'
|
||||
title={
|
||||
<>
|
||||
<Icon id='quote-right' icon={FindInPageIcon} />
|
||||
<FormattedMessage
|
||||
id='search_results.statuses'
|
||||
defaultMessage='Posts'
|
||||
/>
|
||||
</>
|
||||
}
|
||||
onClickMore={handleSelectStatuses}
|
||||
>
|
||||
{results.statuses.slice(0, INITIAL_DISPLAY).map((id) => (
|
||||
// @ts-expect-error inferred props are wrong
|
||||
<Status key={id} id={id} contextType='explore' />
|
||||
))}
|
||||
</SearchSection>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
[]
|
||||
);
|
||||
break;
|
||||
case 'accounts':
|
||||
filteredResults = renderAccounts(results.accounts);
|
||||
break;
|
||||
case 'hashtags':
|
||||
filteredResults = renderHashtags(results.hashtags);
|
||||
break;
|
||||
case 'statuses':
|
||||
filteredResults = renderStatuses(results.statuses);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
ref={columnRef}
|
||||
label={intl.formatMessage(messages.title, { q })}
|
||||
>
|
||||
<ColumnHeader
|
||||
icon={'search'}
|
||||
iconComponent={SearchIcon}
|
||||
title={intl.formatMessage(messages.title, { q })}
|
||||
onClick={handleHeaderClick}
|
||||
multiColumn={multiColumn}
|
||||
/>
|
||||
|
||||
<div className='explore__search-header'>
|
||||
<Search singleColumn initialValue={trimmedValue} />
|
||||
</div>
|
||||
|
||||
<div className='account__section-headline'>
|
||||
<button
|
||||
onClick={handleSelectAll}
|
||||
className={mappedType === 'all' ? 'active' : undefined}
|
||||
>
|
||||
<FormattedMessage id='search_results.all' defaultMessage='All' />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSelectAccounts}
|
||||
className={mappedType === 'accounts' ? 'active' : undefined}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='search_results.accounts'
|
||||
defaultMessage='Profiles'
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSelectHashtags}
|
||||
className={mappedType === 'hashtags' ? 'active' : undefined}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='search_results.hashtags'
|
||||
defaultMessage='Hashtags'
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSelectStatuses}
|
||||
className={mappedType === 'statuses' ? 'active' : undefined}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='search_results.statuses'
|
||||
defaultMessage='Posts'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='explore__search-results' data-nosnippet>
|
||||
<ScrollableList
|
||||
scrollKey='search-results'
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading && !results}
|
||||
onLoadMore={handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
emptyMessage={
|
||||
trimmedValue.length > 0 ? (
|
||||
<FormattedMessage
|
||||
id='search_results.no_results'
|
||||
defaultMessage='No results.'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='search_results.no_search_yet'
|
||||
defaultMessage='Try searching for posts, profiles or hashtags.'
|
||||
/>
|
||||
)
|
||||
}
|
||||
bindToDocument
|
||||
>
|
||||
{filteredResults}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title, { q })}</title>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default SearchResults;
|
|
@ -61,7 +61,7 @@ const Embed: React.FC<{ id: string }> = ({ id }) => {
|
|||
className='embed__overlay'
|
||||
href={permalink}
|
||||
target='_blank'
|
||||
rel='noreferrer noopener'
|
||||
rel='noopener'
|
||||
aria-label=''
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -208,7 +208,7 @@ export default class Card extends PureComponent {
|
|||
<div className='status-card__actions' onClick={this.handleEmbedClick} role='none'>
|
||||
<div>
|
||||
<button type='button' onClick={this.handleEmbedClick}><Icon id='play' icon={PlayArrowIcon} /></button>
|
||||
<a href={card.get('url')} onClick={this.handleExternalLinkClick} target='_blank' rel='noopener noreferrer'><Icon id='external-link' icon={OpenInNewIcon} /></a>
|
||||
<a href={card.get('url')} onClick={this.handleExternalLinkClick} target='_blank' rel='noopener'><Icon id='external-link' icon={OpenInNewIcon} /></a>
|
||||
</div>
|
||||
</div>
|
||||
) : spoilerButton}
|
||||
|
@ -219,7 +219,7 @@ export default class Card extends PureComponent {
|
|||
return (
|
||||
<div className={classNames('status-card', { expanded: largeImage })} ref={this.setRef} onClick={revealed ? null : this.handleReveal} role={revealed ? 'button' : null}>
|
||||
{embed}
|
||||
<a href={card.get('url')} target='_blank' rel='noopener noreferrer'>{description}</a>
|
||||
<a href={card.get('url')} target='_blank' rel='noopener'>{description}</a>
|
||||
</div>
|
||||
);
|
||||
} else if (card.get('image')) {
|
||||
|
@ -239,7 +239,7 @@ export default class Card extends PureComponent {
|
|||
|
||||
return (
|
||||
<>
|
||||
<a href={card.get('url')} className={classNames('status-card', { expanded: largeImage, bottomless: showAuthor })} target='_blank' rel='noopener noreferrer' ref={this.setRef}>
|
||||
<a href={card.get('url')} className={classNames('status-card', { expanded: largeImage, bottomless: showAuthor })} target='_blank' rel='noopener' ref={this.setRef}>
|
||||
{embed}
|
||||
{description}
|
||||
</a>
|
||||
|
|
95
app/javascript/mastodon/features/terms_of_service/index.tsx
Normal file
95
app/javascript/mastodon/features/terms_of_service/index.tsx
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
FormattedMessage,
|
||||
FormattedDate,
|
||||
useIntl,
|
||||
defineMessages,
|
||||
} from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import { apiGetTermsOfService } from 'mastodon/api/instance';
|
||||
import type { ApiTermsOfServiceJSON } from 'mastodon/api_types/instance';
|
||||
import { Column } from 'mastodon/components/column';
|
||||
import { Skeleton } from 'mastodon/components/skeleton';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'terms_of_service.title', defaultMessage: 'Terms of Service' },
|
||||
});
|
||||
|
||||
const TermsOfService: React.FC<{
|
||||
multiColumn: boolean;
|
||||
}> = ({ multiColumn }) => {
|
||||
const intl = useIntl();
|
||||
const [response, setResponse] = useState<ApiTermsOfServiceJSON>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
apiGetTermsOfService()
|
||||
.then((data) => {
|
||||
setResponse(data);
|
||||
setLoading(false);
|
||||
return '';
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!loading && !response) {
|
||||
return <BundleColumnError multiColumn={multiColumn} errorType='routing' />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
label={intl.formatMessage(messages.title)}
|
||||
>
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id='terms_of_service.title'
|
||||
defaultMessage='Terms of Service'
|
||||
/>
|
||||
</h3>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='privacy_policy.last_updated'
|
||||
defaultMessage='Last updated {date}'
|
||||
values={{
|
||||
date: loading ? (
|
||||
<Skeleton width='10ch' />
|
||||
) : (
|
||||
<FormattedDate
|
||||
value={response?.updated_at}
|
||||
year='numeric'
|
||||
month='short'
|
||||
day='2-digit'
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{response && (
|
||||
<div
|
||||
className='privacy-policy__body prose'
|
||||
dangerouslySetInnerHTML={{ __html: response.content }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='all' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default TermsOfService;
|
|
@ -24,7 +24,7 @@ export default class ActionsModal extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<li key={`${text}-${i}`}>
|
||||
<a href={href} target='_blank' rel='noopener noreferrer' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
|
||||
<a href={href} target='_blank' rel='noopener' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
|
||||
{icon && <IconButton title={text} icon={icon} iconComponent={iconComponent} role='presentation' tabIndex={-1} inverted />}
|
||||
<div>
|
||||
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
|
||||
|
|
|
@ -5,12 +5,11 @@ import { connect } from 'react-redux';
|
|||
|
||||
import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/compose';
|
||||
import ServerBanner from 'mastodon/components/server_banner';
|
||||
import { Search } from 'mastodon/features/compose/components/search';
|
||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
||||
import { LinkFooter } from 'mastodon/features/ui/components/link_footer';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
|
||||
import LinkFooter from './link_footer';
|
||||
|
||||
class ComposePanel extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
|
@ -42,7 +41,7 @@ class ComposePanel extends PureComponent {
|
|||
|
||||
return (
|
||||
<div className='compose-panel' onFocus={this.onFocus}>
|
||||
<SearchContainer openInRoute />
|
||||
<Search openInRoute />
|
||||
|
||||
{!signedIn && (
|
||||
<>
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state';
|
||||
import { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onLogout () {
|
||||
dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT' }));
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
class LinkFooter extends PureComponent {
|
||||
static propTypes = {
|
||||
identity: identityContextPropShape,
|
||||
multiColumn: PropTypes.bool,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
handleLogoutClick = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.props.onLogout();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
render () {
|
||||
const { signedIn, permissions } = this.props.identity;
|
||||
const { multiColumn } = this.props;
|
||||
|
||||
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
|
||||
const canProfileDirectory = profileDirectory;
|
||||
|
||||
const DividingCircle = <span aria-hidden>{' · '}</span>;
|
||||
|
||||
return (
|
||||
<div className='link-footer'>
|
||||
<p>
|
||||
<strong>{domain}</strong>:
|
||||
{' '}
|
||||
<Link to='/about' target={multiColumn ? '_blank' : undefined}><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
|
||||
{statusPageUrl && (
|
||||
<>
|
||||
{DividingCircle}
|
||||
<a href={statusPageUrl} target='_blank' rel='noopener'><FormattedMessage id='footer.status' defaultMessage='Status' /></a>
|
||||
</>
|
||||
)}
|
||||
{canInvite && (
|
||||
<>
|
||||
{DividingCircle}
|
||||
<a href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
|
||||
</>
|
||||
)}
|
||||
{canProfileDirectory && (
|
||||
<>
|
||||
{DividingCircle}
|
||||
<Link to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
|
||||
</>
|
||||
)}
|
||||
{DividingCircle}
|
||||
<Link to='/privacy-policy' target={multiColumn ? '_blank' : undefined}><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Mastodon</strong>:
|
||||
{' '}
|
||||
<a href='https://joinmastodon.org' target='_blank'><FormattedMessage id='footer.about' defaultMessage='About' /></a>
|
||||
{DividingCircle}
|
||||
<a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='footer.get_app' defaultMessage='Get the app' /></a>
|
||||
{DividingCircle}
|
||||
<Link to='/keyboard-shortcuts'><FormattedMessage id='footer.keyboard_shortcuts' defaultMessage='Keyboard shortcuts' /></Link>
|
||||
{DividingCircle}
|
||||
<a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
|
||||
{DividingCircle}
|
||||
<span className='version'>v{version}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter)));
|
101
app/javascript/mastodon/features/ui/components/link_footer.tsx
Normal file
101
app/javascript/mastodon/features/ui/components/link_footer.tsx
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
domain,
|
||||
version,
|
||||
source_url,
|
||||
statusPageUrl,
|
||||
profile_directory as canProfileDirectory,
|
||||
termsOfServiceEnabled,
|
||||
} from 'mastodon/initial_state';
|
||||
|
||||
const DividingCircle: React.FC = () => <span aria-hidden>{' · '}</span>;
|
||||
|
||||
export const LinkFooter: React.FC<{
|
||||
multiColumn: boolean;
|
||||
}> = ({ multiColumn }) => {
|
||||
return (
|
||||
<div className='link-footer'>
|
||||
<p>
|
||||
<strong>{domain}</strong>:{' '}
|
||||
<Link to='/about' target={multiColumn ? '_blank' : undefined}>
|
||||
<FormattedMessage id='footer.about' defaultMessage='About' />
|
||||
</Link>
|
||||
{statusPageUrl && (
|
||||
<>
|
||||
<DividingCircle />
|
||||
<a href={statusPageUrl} target='_blank' rel='noopener'>
|
||||
<FormattedMessage id='footer.status' defaultMessage='Status' />
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
{canProfileDirectory && (
|
||||
<>
|
||||
<DividingCircle />
|
||||
<Link to='/directory'>
|
||||
<FormattedMessage
|
||||
id='footer.directory'
|
||||
defaultMessage='Profiles directory'
|
||||
/>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
<DividingCircle />
|
||||
<Link
|
||||
to='/privacy-policy'
|
||||
target={multiColumn ? '_blank' : undefined}
|
||||
rel='privacy-policy'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='footer.privacy_policy'
|
||||
defaultMessage='Privacy policy'
|
||||
/>
|
||||
</Link>
|
||||
{termsOfServiceEnabled && (
|
||||
<>
|
||||
<DividingCircle />
|
||||
<Link
|
||||
to='/terms-of-service'
|
||||
target={multiColumn ? '_blank' : undefined}
|
||||
rel='terms-of-service'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='footer.terms_of_service'
|
||||
defaultMessage='Terms of service'
|
||||
/>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Mastodon</strong>:{' '}
|
||||
<a href='https://joinmastodon.org' target='_blank' rel='noopener'>
|
||||
<FormattedMessage id='footer.about' defaultMessage='About' />
|
||||
</a>
|
||||
<DividingCircle />
|
||||
<a href='https://joinmastodon.org/apps' target='_blank' rel='noopener'>
|
||||
<FormattedMessage id='footer.get_app' defaultMessage='Get the app' />
|
||||
</a>
|
||||
<DividingCircle />
|
||||
<Link to='/keyboard-shortcuts'>
|
||||
<FormattedMessage
|
||||
id='footer.keyboard_shortcuts'
|
||||
defaultMessage='Keyboard shortcuts'
|
||||
/>
|
||||
</Link>
|
||||
<DividingCircle />
|
||||
<a href={source_url} rel='noopener' target='_blank'>
|
||||
<FormattedMessage
|
||||
id='footer.source_code'
|
||||
defaultMessage='View source code'
|
||||
/>
|
||||
</a>
|
||||
<DividingCircle />
|
||||
<span className='version'>v{version}</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -80,6 +80,7 @@ import {
|
|||
OnboardingProfile,
|
||||
OnboardingFollows,
|
||||
Explore,
|
||||
Search,
|
||||
About,
|
||||
PrivacyPolicy,
|
||||
CommunityTimeline,
|
||||
|
@ -89,6 +90,7 @@ import {
|
|||
CircleMembers,
|
||||
BookmarkCategoryEdit,
|
||||
ReactionDeck,
|
||||
TermsOfService,
|
||||
} from './util/async-components';
|
||||
import { ColumnsContextProvider } from './util/columns_context';
|
||||
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
|
||||
|
@ -217,6 +219,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||
<WrappedRoute path='/about' component={About} content={children} />
|
||||
<WrappedRoute path='/privacy-policy' component={PrivacyPolicy} content={children} />
|
||||
<WrappedRoute path='/terms-of-service' component={TermsOfService} content={children} />
|
||||
|
||||
<WrappedRoute path={['/home', '/timelines/home']} component={HomeTimeline} content={children} />
|
||||
<Redirect from='/timelines/public' to='/public' exact />
|
||||
|
@ -259,7 +262,8 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
<WrappedRoute path={['/start', '/start/profile']} exact component={OnboardingProfile} content={children} />
|
||||
<WrappedRoute path='/start/follows' component={OnboardingFollows} content={children} />
|
||||
<WrappedRoute path='/directory' component={Directory} content={children} />
|
||||
<WrappedRoute path={['/explore', '/search']} component={Explore} content={children} />
|
||||
<WrappedRoute path='/explore' component={Explore} content={children} />
|
||||
<WrappedRoute path='/search' component={Search} content={children} />
|
||||
<WrappedRoute path={['/publish', '/statuses/new']} component={Compose} content={children} />
|
||||
|
||||
<WrappedRoute path={['/@:acct', '/accounts/:id']} exact component={AccountTimeline} content={children} />
|
||||
|
|
|
@ -230,6 +230,10 @@ export function Explore () {
|
|||
return import(/* webpackChunkName: "features/explore" */'../../explore');
|
||||
}
|
||||
|
||||
export function Search () {
|
||||
return import(/* webpackChunkName: "features/explore" */'../../search');
|
||||
}
|
||||
|
||||
export function FilterModal () {
|
||||
return import(/*webpackChunkName: "modals/filter_modal" */'../components/filter_modal');
|
||||
}
|
||||
|
@ -254,6 +258,10 @@ export function PrivacyPolicy () {
|
|||
return import(/*webpackChunkName: "features/privacy_policy" */'../../privacy_policy');
|
||||
}
|
||||
|
||||
export function TermsOfService () {
|
||||
return import(/*webpackChunkName: "features/terms_of_service" */'../../terms_of_service');
|
||||
}
|
||||
|
||||
export function NotificationRequests () {
|
||||
return import(/*webpackChunkName: "features/notifications/requests" */'../../notifications/requests');
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@
|
|||
* @property {boolean=} use_pending_items
|
||||
* @property {string} version
|
||||
* @property {string} sso_redirect
|
||||
* @property {string} status_page_url
|
||||
* @property {boolean} terms_of_service_enabled
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -165,10 +167,9 @@ export const usePendingItems = getMeta('use_pending_items');
|
|||
export const version = getMeta('version');
|
||||
export const languages = initialState?.languages;
|
||||
export const criticalUpdatesPending = initialState?.critical_updates_pending;
|
||||
// @ts-expect-error
|
||||
export const statusPageUrl = getMeta('status_page_url');
|
||||
export const sso_redirect = getMeta('sso_redirect');
|
||||
|
||||
export const termsOfServiceEnabled = getMeta('terms_of_service_enabled');
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
|
|
|
@ -152,7 +152,6 @@
|
|||
"empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
|
||||
"empty_column.list": "Hierdie lys is nog leeg. Nuwe plasings deur lyslede sal voortaan hier verskyn.",
|
||||
"empty_column.notifications": "Jy het nog geen kennisgewings nie. Interaksie van ander mense met jou, sal hier vertoon.",
|
||||
"explore.search_results": "Soekresultate",
|
||||
"explore.suggested_follows": "Mense",
|
||||
"explore.trending_links": "Nuus",
|
||||
"filter_modal.added.settings_link": "instellings bladsy",
|
||||
|
@ -162,7 +161,6 @@
|
|||
"footer.about": "Oor",
|
||||
"footer.directory": "Profielgids",
|
||||
"footer.get_app": "Kry die app",
|
||||
"footer.invite": "Nooi ander",
|
||||
"footer.keyboard_shortcuts": "Kortpadsleutels",
|
||||
"footer.privacy_policy": "Privaatheidsbeleid",
|
||||
"footer.source_code": "Wys bronkode",
|
||||
|
@ -259,9 +257,7 @@
|
|||
"search.search_or_paste": "Soek of plak URL",
|
||||
"search_results.all": "Alles",
|
||||
"search_results.hashtags": "Hutsetiket",
|
||||
"search_results.nothing_found": "Hierdie soekwoorde lewer niks op nie",
|
||||
"search_results.statuses": "Plasings",
|
||||
"search_results.title": "Soek {q}",
|
||||
"server_banner.administered_by": "Administrasie deur:",
|
||||
"sign_in_banner.sign_in": "Sign in",
|
||||
"status.admin_status": "Open hierdie plasing as moderator",
|
||||
|
|
|
@ -192,7 +192,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Intenta deshabilitar-los y recarga la pachina. Si ixo no aduya, podrías usar Mastodon a traviés d'un navegador web diferent u aplicación nativa.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copiar lo seguimiento de pila en o portafuellas",
|
||||
"errors.unexpected_crash.report_issue": "Informar d'un problema/error",
|
||||
"explore.search_results": "Resultaus de busqueda",
|
||||
"explore.title": "Explorar",
|
||||
"explore.trending_links": "Noticias",
|
||||
"explore.trending_statuses": "Publicacions",
|
||||
|
@ -219,7 +218,6 @@
|
|||
"footer.about": "Sobre",
|
||||
"footer.directory": "Directorio de perfils",
|
||||
"footer.get_app": "Obtener l'aplicación",
|
||||
"footer.invite": "Convidar chent",
|
||||
"footer.keyboard_shortcuts": "Alcorces de teclau",
|
||||
"footer.privacy_policy": "Politica de privacidat",
|
||||
"footer.source_code": "Veyer codigo fuent",
|
||||
|
@ -432,9 +430,7 @@
|
|||
"search_popout.full_text_search_logged_out_message": "Nomás disponible iniciando la sesión.",
|
||||
"search_results.all": "Totz",
|
||||
"search_results.hashtags": "Etiquetas",
|
||||
"search_results.nothing_found": "No se podió trobar cosa pa estes termins de busqueda",
|
||||
"search_results.statuses": "Publicacions",
|
||||
"search_results.title": "Buscar {q}",
|
||||
"server_banner.about_active_users": "Usuarios activos en o servidor entre los zaguers 30 días (Usuarios Activos Mensuals)",
|
||||
"server_banner.active_users": "usuarios activos",
|
||||
"server_banner.administered_by": "Administrau per:",
|
||||
|
|
|
@ -274,7 +274,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "حاول تعطيلهم وإنعاش الصفحة. إن لم ينجح ذلك، يمكنك دائمًا استخدام ماستدون عبر متصفح آخر أو تطبيق أصلي.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "انسخ تتبع الارتباطات إلى الحافظة",
|
||||
"errors.unexpected_crash.report_issue": "الإبلاغ عن خلل",
|
||||
"explore.search_results": "نتائج البحث",
|
||||
"explore.suggested_follows": "أشخاص",
|
||||
"explore.title": "استكشف",
|
||||
"explore.trending_links": "المُستجدّات",
|
||||
|
@ -322,7 +321,6 @@
|
|||
"footer.about": "عن",
|
||||
"footer.directory": "دليل الصفحات التعريفية",
|
||||
"footer.get_app": "احصل على التطبيق",
|
||||
"footer.invite": "دعوة أشخاص",
|
||||
"footer.keyboard_shortcuts": "اختصارات لوحة المفاتيح",
|
||||
"footer.privacy_policy": "سياسة الخصوصية",
|
||||
"footer.source_code": "الاطلاع على الشفرة المصدرية",
|
||||
|
@ -681,10 +679,8 @@
|
|||
"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": "الأشخاص الذين يستخدمون هذا الخادم خلال الأيام الثلاثين الأخيرة (المستخدمون النشطون شهريًا)",
|
||||
"server_banner.active_users": "مستخدم نشط",
|
||||
"server_banner.administered_by": "يُديره:",
|
||||
|
|
|
@ -159,7 +159,6 @@
|
|||
"error.unexpected_crash.explanation_addons": "Esta páxina nun se pudo amosar correutamente. Ye probable que dalgún complementu del restolador o dalguna ferramienta de traducción automática produxere esti error.",
|
||||
"error.unexpected_crash.next_steps": "Prueba a anovar la páxina. Si nun sirve, ye posible que tovía seyas a usar Mastodon pente otru restolador o una aplicación nativa.",
|
||||
"error.unexpected_crash.next_steps_addons": "Prueba a desactivalos y a anovar la páxina. Si nun sirve, ye posible que tovía seyas a usar Mastodon pente otru restolador o una aplicación nativa.",
|
||||
"explore.search_results": "Resultaos de la busca",
|
||||
"explore.suggested_follows": "Perfiles",
|
||||
"explore.title": "Esploración",
|
||||
"explore.trending_links": "Noticies",
|
||||
|
@ -196,7 +195,6 @@
|
|||
"footer.about": "Tocante a",
|
||||
"footer.directory": "Direutoriu de perfiles",
|
||||
"footer.get_app": "Consiguir l'aplicación",
|
||||
"footer.invite": "Convidar a persones",
|
||||
"footer.keyboard_shortcuts": "Atayos del tecláu",
|
||||
"footer.privacy_policy": "Política de privacidá",
|
||||
"footer.source_code": "Ver el códigu fonte",
|
||||
|
@ -389,10 +387,8 @@
|
|||
"search_results.accounts": "Perfiles",
|
||||
"search_results.all": "Too",
|
||||
"search_results.hashtags": "Etiquetes",
|
||||
"search_results.nothing_found": "Nun se pudo atopar nada con esos términos de busca",
|
||||
"search_results.see_all": "Ver too",
|
||||
"search_results.statuses": "Artículos",
|
||||
"search_results.title": "Busca de: {q}",
|
||||
"server_banner.server_stats": "Estadístiques del sirvidor:",
|
||||
"sign_in_banner.create_account": "Crear una cuenta",
|
||||
"sign_in_banner.sso_redirect": "Aniciar la sesión o rexistrase",
|
||||
|
|
1
app/javascript/mastodon/locales/az.json
Normal file
1
app/javascript/mastodon/locales/az.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -278,7 +278,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Паспрабуйце выключыць іх і аднавіць старонку. Калі гэта не дапаможа, вы можаце карыстацца Мастадонт праз другі браўзер ці аплікацыю.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Дадаць дыягнастычны стэк у буфер абмену",
|
||||
"errors.unexpected_crash.report_issue": "Паведаміць аб праблеме",
|
||||
"explore.search_results": "Вынікі пошуку",
|
||||
"explore.suggested_follows": "Людзі",
|
||||
"explore.title": "Агляд",
|
||||
"explore.trending_links": "Навіны",
|
||||
|
@ -328,7 +327,6 @@
|
|||
"footer.about": "Пра нас",
|
||||
"footer.directory": "Дырэкторыя профіляў",
|
||||
"footer.get_app": "Спампаваць праграму",
|
||||
"footer.invite": "Запрасіць людзей",
|
||||
"footer.keyboard_shortcuts": "Спалучэнні клавіш",
|
||||
"footer.privacy_policy": "Палітыка прыватнасці",
|
||||
"footer.source_code": "Прагледзець зыходны код",
|
||||
|
@ -686,10 +684,8 @@
|
|||
"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": "Людзі, якія карыстаюцца гэтым сервера на працягу апошніх 30 дзён (Штомесячна Актыўныя Карыстальнікі)",
|
||||
"server_banner.active_users": "актыўныя карыстальнікі",
|
||||
"server_banner.administered_by": "Адміністратар:",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"annual_report.summary.thanks": "Благодарим, че сте част от Mastodon!",
|
||||
"attachments_list.unprocessed": "(необработено)",
|
||||
"audio.hide": "Скриване на звука",
|
||||
"block_modal.remote_users_caveat": "Ще поискаме сървърът {domain} да почита решението ви. Съгласието обаче не се гарантира откак някои сървъри могат да боравят с блоковете по различен начин. Обществените публикации още може да се виждат от невлезли в системата потребители.",
|
||||
"block_modal.remote_users_caveat": "Ще приканим сървъра {domain} да уважава решението ви. За съжаление не можем да гарантираме това защото някои сървъри могат да третират блокиранията по различен начин. Публичните постове може да продължат да бъдат видими за потребители, които не са се регистрирали.",
|
||||
"block_modal.show_less": "Повече на показ",
|
||||
"block_modal.show_more": "По-малко на показ",
|
||||
"block_modal.they_cant_mention": "Те не могат да ви споменават или последват.",
|
||||
|
@ -255,14 +255,14 @@
|
|||
"domain_pill.activitypub_lets_connect": "Позволява ви да се свързвате и взаимодействате с хора не само в Mastodon, но и през различни социални приложения.",
|
||||
"domain_pill.activitypub_like_language": "ActivityPub е като език на Mastodon, говорещ с други социални мрежи.",
|
||||
"domain_pill.server": "Сървър",
|
||||
"domain_pill.their_handle": "Тяхната ръчка:",
|
||||
"domain_pill.their_handle": "Техният адрес:",
|
||||
"domain_pill.their_server": "Цифровият им дом, където живеят всичките им публикации.",
|
||||
"domain_pill.their_username": "Неповторимият им идентификатор на сървъра им. Възможно е да се намерят потребители със същото потребителско име на други сървъри.",
|
||||
"domain_pill.username": "Потребителско име",
|
||||
"domain_pill.whats_in_a_handle": "Какво е в ръчката?",
|
||||
"domain_pill.who_they_are": "Откак ръчките казват кой кой е и къде е, то може да взаимодействате с хора през социаното уебпространство на <button>захранваните платформи от ActivityPub</button>.",
|
||||
"domain_pill.who_you_are": "Тъй като вашата ръчка казва кои сте и къде сте, то може да взаимодействате с хора през социаното уебпространство на <button>захранваните платформи от ActivityPub</button>.",
|
||||
"domain_pill.your_handle": "Вашата ръчка:",
|
||||
"domain_pill.whats_in_a_handle": "Как се съставя адресът?",
|
||||
"domain_pill.who_they_are": "Адресът показва за някой кой е той и къде се намира. Това ви позволява да общувате с всички в социалната мрежа от <button>платформите поддържащи ActivityPub</button>.",
|
||||
"domain_pill.who_you_are": "Адресът ви показва кой сте и къде се намирате. Това ви позволява да общувате с всички в социалната мрежа от <button>платформите поддържащи ActivityPub</button>.",
|
||||
"domain_pill.your_handle": "Вашият адрес:",
|
||||
"domain_pill.your_server": "Цифровият ви дом, където живеят всичките ви публикации. Не харесвате ли този? Прехвърляте се на сървъри по всяко време и докарвате последователите си също.",
|
||||
"domain_pill.your_username": "Неповторимият ви идентификатор на този сървър. Възможно е да се намерят потребители със същото потребителско име на други сървъри.",
|
||||
"embed.instructions": "Вградете публикацията в уебсайта си, копирайки кода долу.",
|
||||
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Опитайте се да ги изключите и да опресните страницата. Ако това не помогне, то още може да използвате Mastodon чрез различен браузър или приложение.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Копиране на трасето на стека в буферната памет",
|
||||
"errors.unexpected_crash.report_issue": "Сигнал за проблем",
|
||||
"explore.search_results": "Резултати от търсенето",
|
||||
"explore.suggested_follows": "Хора",
|
||||
"explore.title": "Разглеждане",
|
||||
"explore.trending_links": "Новини",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Относно",
|
||||
"footer.directory": "Директория на профилите",
|
||||
"footer.get_app": "Вземане на приложението",
|
||||
"footer.invite": "Поканване на хора",
|
||||
"footer.keyboard_shortcuts": "Клавишни комбинации",
|
||||
"footer.privacy_policy": "Политика за поверителност",
|
||||
"footer.source_code": "Преглед на изходния код",
|
||||
"footer.status": "Състояние",
|
||||
"footer.terms_of_service": "Условия на услугата",
|
||||
"generic.saved": "Запазено",
|
||||
"getting_started.heading": "Първи стъпки",
|
||||
"hashtag.admin_moderation": "Отваряне на модериращия интерфейс за #{name}",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "Преглед на #Wrapstodon",
|
||||
"notification.favourite": "{name} направи любима публикацията ви",
|
||||
"notification.favourite.name_and_others_with_link": "{name} и <a>{count, plural, one {# друг} other {# други}}</a> направиха любима ваша публикация",
|
||||
"notification.favourite_pm": "{name} хареса вашето лично споменаване",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} и <a>{count, plural, one {# друг} other {# други}}</a> харесаха вашето частно споменаване",
|
||||
"notification.follow": "{name} ви последва",
|
||||
"notification.follow.name_and_others": "{name} и <a>{count, plural, one {# друг} other {# други}}</a> ви последваха",
|
||||
"notification.follow_request": "{name} поиска да ви последва",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Профили",
|
||||
"search_results.all": "Всичко",
|
||||
"search_results.hashtags": "Хаштагове",
|
||||
"search_results.nothing_found": "Не може да се намери каквото и да било за тези термини при търсене",
|
||||
"search_results.no_results": "Няма намерени резултати.",
|
||||
"search_results.no_search_yet": "Опитайте да потърсите постове, профили или хаштагове.",
|
||||
"search_results.see_all": "Поглед на всички",
|
||||
"search_results.statuses": "Публикации",
|
||||
"search_results.title": "Търсене за {q}",
|
||||
"search_results.title": "Търсене на \"{q}\"",
|
||||
"server_banner.about_active_users": "Ползващите сървъра през последните 30 дни (дейните месечно потребители)",
|
||||
"server_banner.active_users": "дейни потребители",
|
||||
"server_banner.administered_by": "Администрира се от:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Промяна на абонираните езици за {target}",
|
||||
"tabs_bar.home": "Начало",
|
||||
"tabs_bar.notifications": "Известия",
|
||||
"terms_of_service.title": "Условия на услугата",
|
||||
"time_remaining.days": "{number, plural, one {остава # ден} other {остават # дни}}",
|
||||
"time_remaining.hours": "{number, plural, one {остава # час} other {остават # часа}}",
|
||||
"time_remaining.minutes": "{number, plural, one {остава # минута} other {остават # минути}}",
|
||||
|
|
|
@ -230,7 +230,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Klaskit azbevaat ar bajenn. Ma n'ez a ket en-dro e c'hallit klask ober gant Mastodon dre ur merdeer disheñvel pe dre an arload genidik.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Eilañ ar roudoù diveugañ er golver",
|
||||
"errors.unexpected_crash.report_issue": "Danevellañ ur fazi",
|
||||
"explore.search_results": "Disoc'hoù an enklask",
|
||||
"explore.suggested_follows": "Tud",
|
||||
"explore.title": "Furchal",
|
||||
"explore.trending_links": "Keleier",
|
||||
|
@ -263,7 +262,6 @@
|
|||
"footer.about": "Diwar-benn",
|
||||
"footer.directory": "Kavlec'h ar profiloù",
|
||||
"footer.get_app": "Pellgargañ an arload",
|
||||
"footer.invite": "Pediñ tud",
|
||||
"footer.keyboard_shortcuts": "Berradennoù klavier",
|
||||
"footer.privacy_policy": "Reolennoù prevezded",
|
||||
"footer.source_code": "Gwelet ar c'hod mammenn",
|
||||
|
@ -524,10 +522,8 @@
|
|||
"search_results.accounts": "Profiloù",
|
||||
"search_results.all": "Pep tra",
|
||||
"search_results.hashtags": "Hashtagoù",
|
||||
"search_results.nothing_found": "Disoc'h ebet gant ar gerioù-se",
|
||||
"search_results.see_all": "Gwelet pep tra",
|
||||
"search_results.statuses": "Toudoù",
|
||||
"search_results.title": "Klask {q}",
|
||||
"server_banner.active_users": "implijerien·ezed oberiant",
|
||||
"server_banner.administered_by": "Meret gant :",
|
||||
"server_banner.server_stats": "Stadegoù ar servijer :",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Prova de desactivar-los i actualitza la pàgina. Si això no serveix, és possible que encara puguis fer servir Mastodon amb un altre navegador o una aplicació nativa.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copia stacktrace al porta-retalls",
|
||||
"errors.unexpected_crash.report_issue": "Informa d'un problema",
|
||||
"explore.search_results": "Resultats de la cerca",
|
||||
"explore.suggested_follows": "Persones",
|
||||
"explore.title": "Explora",
|
||||
"explore.trending_links": "Notícies",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Quant a",
|
||||
"footer.directory": "Directori de perfils",
|
||||
"footer.get_app": "Aconsegueix l'app",
|
||||
"footer.invite": "Convida persones",
|
||||
"footer.keyboard_shortcuts": "Dreceres de teclat",
|
||||
"footer.privacy_policy": "Política de privadesa",
|
||||
"footer.source_code": "Mostra el codi font",
|
||||
"footer.status": "Estat",
|
||||
"footer.terms_of_service": "Condicions de servei",
|
||||
"generic.saved": "Desat",
|
||||
"getting_started.heading": "Primeres passes",
|
||||
"hashtag.admin_moderation": "Obre la interfície de moderació per a #{name}",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "Visualitzeu #Wrapstodon",
|
||||
"notification.favourite": "{name} ha afavorit el teu tut",
|
||||
"notification.favourite.name_and_others_with_link": "{name} i <a>{count, plural, one {# altre} other {# altres}}</a> han afavorit la vostra publicació",
|
||||
"notification.favourite_pm": "{name} ha afavorit la vostra menció privada",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} i <a>{count, plural, one {un altre} other {# altres}}</a> han afavorit la vostra menció",
|
||||
"notification.follow": "{name} et segueix",
|
||||
"notification.follow.name_and_others": "{name} i <a>{count, plural, one {# altre} other {# altres}}</a> us han seguit",
|
||||
"notification.follow_request": "{name} ha sol·licitat de seguir-te",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Perfils",
|
||||
"search_results.all": "Tots",
|
||||
"search_results.hashtags": "Etiquetes",
|
||||
"search_results.nothing_found": "No s'ha pogut trobar res per a aquests termes de cerca",
|
||||
"search_results.no_results": "Cap resultat.",
|
||||
"search_results.no_search_yet": "Proveu de cercar publicacions, perfils o etiquetes.",
|
||||
"search_results.see_all": "Veure'ls tots",
|
||||
"search_results.statuses": "Tuts",
|
||||
"search_results.title": "Cerca de {q}",
|
||||
"search_results.title": "Cerca de “{q}”",
|
||||
"server_banner.about_active_users": "Gent que ha fet servir aquest servidor en els darrers 30 dies (Usuaris Actius Mensuals)",
|
||||
"server_banner.active_users": "usuaris actius",
|
||||
"server_banner.administered_by": "Administrat per:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Canvia les llengües subscrites per a {target}",
|
||||
"tabs_bar.home": "Inici",
|
||||
"tabs_bar.notifications": "Notificacions",
|
||||
"terms_of_service.title": "Condicions de servei",
|
||||
"time_remaining.days": "{number, plural, one {# dia restant} other {# dies restants}}",
|
||||
"time_remaining.hours": "{number, plural, one {# hora restant} other {# hores restants}}",
|
||||
"time_remaining.minutes": "{number, plural, one {# minut restant} other {# minuts restants}}",
|
||||
|
|
|
@ -226,7 +226,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "هەوڵدە لەکاریان بخەیت و لاپەڕەکە تازە بکەوە. ئەگەر ئەمە یارمەتیدەر نەبوو، لەوانەیە هێشتا بتوانیت ماستۆدۆن بەکاربێنیت لە ڕێگەی وێبگەڕەکانی دیکە یان نەرمەکالاکانی ئەسڵی.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "کۆپیکردنی ستێکتراسی بۆ کلیپ بۆرد",
|
||||
"errors.unexpected_crash.report_issue": "کێشەی گوزارشت",
|
||||
"explore.search_results": "ئەنجامەکانی گەڕان",
|
||||
"explore.suggested_follows": "خەڵک",
|
||||
"explore.title": "گەڕان",
|
||||
"explore.trending_links": "هەواڵەکان",
|
||||
|
@ -262,7 +261,6 @@
|
|||
"footer.about": "دەربارە",
|
||||
"footer.directory": "ڕابەری پەڕەی ناساندن",
|
||||
"footer.get_app": "بەرنامەکە بەدەست بێنە",
|
||||
"footer.invite": "بانگهێشتکردنی خەڵک",
|
||||
"footer.keyboard_shortcuts": "کورتەڕێکانی تەختەکلیک",
|
||||
"footer.privacy_policy": "سیاسەتی تایبەتمەندێتی",
|
||||
"footer.source_code": "پیشاندانی کۆدی سەرچاوە",
|
||||
|
@ -487,9 +485,7 @@
|
|||
"search_results.accounts": "پرۆفایلەکان",
|
||||
"search_results.all": "هەموو",
|
||||
"search_results.hashtags": "هەشتاگ",
|
||||
"search_results.nothing_found": "هیچ بۆ ئەم زاراوە گەڕانانە نەدۆزراوەتەوە",
|
||||
"search_results.statuses": "توتەکان",
|
||||
"search_results.title": "گەڕان بەدوای {q}",
|
||||
"server_banner.about_active_users": "ئەو کەسانەی لە ماوەی ٣٠ ڕۆژی ڕابردوودا ئەم سێرڤەرە بەکاردەهێنن (بەکارهێنەرانی چالاک مانگانە)",
|
||||
"server_banner.active_users": "بەکارهێنەرانی چالاک",
|
||||
"server_banner.administered_by": "بەڕێوەبردن لەلایەن:",
|
||||
|
|
|
@ -272,7 +272,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Zkuste je vypnout a stránku obnovit. Pokud to nepomůže, zkuste otevřít Mastodon v jiném prohlížeči nebo nativní aplikaci.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Zkopírovat stacktrace do schránky",
|
||||
"errors.unexpected_crash.report_issue": "Nahlásit problém",
|
||||
"explore.search_results": "Výsledky hledání",
|
||||
"explore.suggested_follows": "Lidé",
|
||||
"explore.title": "Objevit",
|
||||
"explore.trending_links": "Zprávy",
|
||||
|
@ -320,7 +319,6 @@
|
|||
"footer.about": "O aplikaci",
|
||||
"footer.directory": "Adresář profilů",
|
||||
"footer.get_app": "Získat aplikaci",
|
||||
"footer.invite": "Pozvat lidi",
|
||||
"footer.keyboard_shortcuts": "Klávesové zkratky",
|
||||
"footer.privacy_policy": "Zásady ochrany osobních údajů",
|
||||
"footer.source_code": "Zobrazit zdrojový kód",
|
||||
|
@ -652,10 +650,8 @@
|
|||
"search_results.accounts": "Profily",
|
||||
"search_results.all": "Vše",
|
||||
"search_results.hashtags": "Hashtagy",
|
||||
"search_results.nothing_found": "Pro tyto hledané výrazy nebylo nic nenalezeno",
|
||||
"search_results.see_all": "Zobrazit vše",
|
||||
"search_results.statuses": "Příspěvky",
|
||||
"search_results.title": "Hledat {q}",
|
||||
"server_banner.about_active_users": "Lidé používající tento server během posledních 30 dní (měsíční aktivní uživatelé)",
|
||||
"server_banner.active_users": "aktivní uživatelé",
|
||||
"server_banner.administered_by": "Spravováno:",
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "hashnod a ddefnyddiwyd fwyaf",
|
||||
"annual_report.summary.most_used_hashtag.none": "Dim",
|
||||
"annual_report.summary.new_posts.new_posts": "postiadau newydd",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Mae hynny'n eich rhoi chi ar y brig</topLabel><percentage></percentage><bottomLabel> o ddefnyddiwr {domain}.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Ni fyddwn yn dweud wrth Bernie.",
|
||||
"annual_report.summary.thanks": "Diolch am fod yn rhan o Mastodon!",
|
||||
"attachments_list.unprocessed": "(heb eu prosesu)",
|
||||
|
@ -128,6 +129,7 @@
|
|||
"bundle_column_error.routing.body": "Nid oedd modd canfod y dudalen honno. Ydych chi'n siŵr fod yr URL yn y bar cyfeiriad yn gywir?",
|
||||
"bundle_column_error.routing.title": "404",
|
||||
"bundle_modal_error.close": "Cau",
|
||||
"bundle_modal_error.message": "Aeth rhywbeth o'i le wrth lwytho'r sgrin hon.",
|
||||
"bundle_modal_error.retry": "Ceisiwch eto",
|
||||
"closed_registrations.other_server_instructions": "Gan fod Mastodon yn ddatganoledig, gallwch greu cyfrif ar weinydd arall a dal i ryngweithio gyda hwn.",
|
||||
"closed_registrations_modal.description": "Ar hyn o bryd nid yw'n bosib creu cyfrif ar {domain}, ond cadwch mewn cof nad oes raid i chi gael cyfrif yn benodol ar {domain} i ddefnyddio Mastodon.",
|
||||
|
@ -203,6 +205,9 @@
|
|||
"confirmations.edit.confirm": "Golygu",
|
||||
"confirmations.edit.message": "Bydd golygu nawr yn trosysgrifennu'r neges rydych yn ei ysgrifennu ar hyn o bryd. Ydych chi'n siŵr eich bod eisiau gwneud hyn?",
|
||||
"confirmations.edit.title": "Trosysgrifo'r postiad?",
|
||||
"confirmations.follow_to_list.confirm": "Dilyn ac ychwanegu at y rhestr",
|
||||
"confirmations.follow_to_list.message": "Mae angen i chi fod yn dilyn {name} i'w ychwanegu at restr.",
|
||||
"confirmations.follow_to_list.title": "Dilyn defnyddiwr?",
|
||||
"confirmations.logout.confirm": "Allgofnodi",
|
||||
"confirmations.logout.message": "Ydych chi'n siŵr eich bod am allgofnodi?",
|
||||
"confirmations.logout.title": "Allgofnodi?",
|
||||
|
@ -234,6 +239,10 @@
|
|||
"disabled_account_banner.text": "Mae eich cyfrif {disabledAccount} wedi ei analluogi ar hyn o bryd.",
|
||||
"dismissable_banner.community_timeline": "Dyma'r postiadau cyhoeddus diweddaraf gan bobl sydd â chyfrifon ar {domain}.",
|
||||
"dismissable_banner.dismiss": "Cau",
|
||||
"dismissable_banner.explore_links": "Y straeon newyddion hyn yw'r rhai sy'n cael eu rhannu fwyaf ar y ffederasiwn heddiw. Mae straeon newyddion mwy diweddar sy'n cael eu postio gan fwy o amrywiaeth o bobl yn cael eu graddio'n uwch.",
|
||||
"dismissable_banner.explore_statuses": "Mae'r postiadau hyn o bob rhan o'r ffedysawd yn cael mwy o sylw heddiw. Mae postiadau mwy diweddar sydd â mwy o hybu a ffefrynnu'n cael eu graddio'n uwch.",
|
||||
"dismissable_banner.explore_tags": "Mae'r hashnodau hyn ar gynnydd y ffedysawd heddiw. Mae hashnodau sy'n cael eu defnyddio gan fwy o bobl amrywiol yn cael eu graddio'n uwch.",
|
||||
"dismissable_banner.public_timeline": "Dyma'r postiadau cyhoeddus diweddaraf gan bobl ar y ffedysawd y mae pobl ar {domain} yn eu dilyn.",
|
||||
"domain_block_modal.block": "Blocio gweinydd",
|
||||
"domain_block_modal.block_account_instead": "Blocio @{name} yn ei le",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Gall pobl o'r gweinydd hwn ryngweithio â'ch hen bostiadau.",
|
||||
|
@ -300,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Ceisiwch eu hanalluogi ac adnewyddu'r dudalen. Os nad yw hynny'n helpu, efallai y byddwch yn dal i allu defnyddio Mastodon trwy borwr neu ap cynhenid arall.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copïo'r olrhain stac i'r clipfwrdd",
|
||||
"errors.unexpected_crash.report_issue": "Rhoi gwybod am broblem",
|
||||
"explore.search_results": "Canlyniadau chwilio",
|
||||
"explore.suggested_follows": "Pobl",
|
||||
"explore.title": "Darganfod",
|
||||
"explore.trending_links": "Newyddion",
|
||||
|
@ -350,13 +358,14 @@
|
|||
"footer.about": "Ynghylch",
|
||||
"footer.directory": "Cyfeiriadur proffiliau",
|
||||
"footer.get_app": "Lawrlwytho'r ap",
|
||||
"footer.invite": "Gwahodd pobl",
|
||||
"footer.keyboard_shortcuts": "Bysellau brys",
|
||||
"footer.privacy_policy": "Polisi preifatrwydd",
|
||||
"footer.source_code": "Gweld y cod ffynhonnell",
|
||||
"footer.status": "Statws",
|
||||
"footer.terms_of_service": "Telerau gwasanaeth",
|
||||
"generic.saved": "Wedi'i Gadw",
|
||||
"getting_started.heading": "Dechrau",
|
||||
"hashtag.admin_moderation": "Agor rhyngwyneb cymedroli #{name}",
|
||||
"hashtag.column_header.tag_mode.all": "a {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "neu {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "heb {additional}",
|
||||
|
@ -486,6 +495,7 @@
|
|||
"lists.replies_policy.list": "Aelodau'r rhestr",
|
||||
"lists.replies_policy.none": "Neb",
|
||||
"lists.save": "Cadw",
|
||||
"lists.search": "Chwilio",
|
||||
"lists.show_replies_to": "Cynhwyswch atebion gan aelodau'r rhestr i",
|
||||
"load_pending": "{count, plural, one {# eitem newydd} other {# eitem newydd}}",
|
||||
"loading_indicator.label": "Yn llwytho…",
|
||||
|
@ -539,6 +549,7 @@
|
|||
"notification.annual_report.view": "Gweld #Wrapstodon",
|
||||
"notification.favourite": "Ffafriodd {name} eich postiad",
|
||||
"notification.favourite.name_and_others_with_link": "Ffafriodd {name} a <a>{count, plural, one {# arall} other {# arall}}</a> eich postiad",
|
||||
"notification.favourite_pm": "Mae {name} wedi ffefrynnu eich cyfeiriad preifat",
|
||||
"notification.follow": "Dilynodd {name} chi",
|
||||
"notification.follow.name_and_others": "Mae {name} a <a>{count, plural, zero {}one {# arall} two {# arall} few {# arall} many {# others} other {# arall}}</a> nawr yn eich dilyn chi",
|
||||
"notification.follow_request": "Mae {name} wedi gwneud cais i'ch dilyn",
|
||||
|
@ -770,10 +781,8 @@
|
|||
"search_results.accounts": "Proffilau",
|
||||
"search_results.all": "Popeth",
|
||||
"search_results.hashtags": "Hashnodau",
|
||||
"search_results.nothing_found": "Methu dod o hyd i unrhyw beth ar gyfer y termau chwilio hyn",
|
||||
"search_results.see_all": "Gweld y cyfan",
|
||||
"search_results.statuses": "Postiadau",
|
||||
"search_results.title": "Chwilio am {q}",
|
||||
"server_banner.about_active_users": "Pobl sy'n defnyddio'r gweinydd hwn yn ystod y 30 diwrnod diwethaf (Defnyddwyr Gweithredol Misol)",
|
||||
"server_banner.active_users": "defnyddwyr gweithredol",
|
||||
"server_banner.administered_by": "Gweinyddir gan:",
|
||||
|
@ -846,6 +855,7 @@
|
|||
"subscribed_languages.target": "Newid ieithoedd tanysgrifio {target}",
|
||||
"tabs_bar.home": "Cartref",
|
||||
"tabs_bar.notifications": "Hysbysiadau",
|
||||
"terms_of_service.title": "Telerau Gwasanaeth",
|
||||
"time_remaining.days": "{number, plural, one {# diwrnod} other {# diwrnod}} ar ôl",
|
||||
"time_remaining.hours": "{number, plural, one {# awr} other {# awr}} ar ôl",
|
||||
"time_remaining.minutes": "{number, plural, one {# munud} other {# munud}} ar ôl",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Prøv at deaktivere dem og genindlæse siden. Hvis det ikke hjælper, kan Mastodon muligvis stadig bruges via en anden browser eller app.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopiér stacktrace til udklipsholderen",
|
||||
"errors.unexpected_crash.report_issue": "Anmeld problem",
|
||||
"explore.search_results": "Søgeresultater",
|
||||
"explore.suggested_follows": "Personer",
|
||||
"explore.title": "Udforsk",
|
||||
"explore.trending_links": "Nyheder",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Om",
|
||||
"footer.directory": "Profiloversigt",
|
||||
"footer.get_app": "Hent appen",
|
||||
"footer.invite": "Invitér personer",
|
||||
"footer.keyboard_shortcuts": "Tastaturgenveje",
|
||||
"footer.privacy_policy": "Privatlivspolitik",
|
||||
"footer.source_code": "Vis kildekode",
|
||||
"footer.status": "Status",
|
||||
"footer.terms_of_service": "Tjenestevilkår",
|
||||
"generic.saved": "Gemt",
|
||||
"getting_started.heading": "Startmenu",
|
||||
"hashtag.admin_moderation": "Åbn modereringsbrugerflade for #{name}",
|
||||
|
@ -549,6 +548,8 @@
|
|||
"notification.annual_report.view": "Vis #Wrapstodon",
|
||||
"notification.favourite": "{name} favoritmarkerede dit indlæg",
|
||||
"notification.favourite.name_and_others_with_link": "{name} og <a>{count, plural, one {# anden} other {# andre}}</a> gjorde dit indlæg til favorit",
|
||||
"notification.favourite_pm": "{name} favoritmarkerede din private omtale",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} og <a>{count, plural, one {# anden} other {# andre}}</a> favoritmarkerede dit indlæg",
|
||||
"notification.follow": "{name} begyndte at følge dig",
|
||||
"notification.follow.name_and_others": "{name} og <a>{count, plural, one {# andre} other {# andre}}</a> begyndte at følge dig",
|
||||
"notification.follow_request": "{name} har anmodet om at følge dig",
|
||||
|
@ -780,10 +781,11 @@
|
|||
"search_results.accounts": "Profiler",
|
||||
"search_results.all": "Alle",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Ingen resultater for disse søgeord",
|
||||
"search_results.no_results": "Ingen resultater.",
|
||||
"search_results.no_search_yet": "Prøv at søge efter indlæg, profiler eller hashtags.",
|
||||
"search_results.see_all": "Vis alle",
|
||||
"search_results.statuses": "Indlæg",
|
||||
"search_results.title": "Søg efter {q}",
|
||||
"search_results.title": "Søg efter \"{q}\"",
|
||||
"server_banner.about_active_users": "Folk, som brugte denne server de seneste 30 dage (månedlige aktive brugere)",
|
||||
"server_banner.active_users": "aktive brugere",
|
||||
"server_banner.administered_by": "Håndteres af:",
|
||||
|
@ -856,6 +858,7 @@
|
|||
"subscribed_languages.target": "Skift abonnementssprog for {target}",
|
||||
"tabs_bar.home": "Hjem",
|
||||
"tabs_bar.notifications": "Notifikationer",
|
||||
"terms_of_service.title": "Tjenestevilkår",
|
||||
"time_remaining.days": "{number, plural, one {# dag} other {# dage}} tilbage",
|
||||
"time_remaining.hours": "{number, plural, one {# time} other {# timer}} tilbage",
|
||||
"time_remaining.minutes": "{number, plural, one {# minut} other {# minutter}} tilbage",
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
"account.unmute": "Stummschaltung von @{name} aufheben",
|
||||
"account.unmute_notifications_short": "Stummschaltung der Benachrichtigungen aufheben",
|
||||
"account.unmute_short": "Stummschaltung aufheben",
|
||||
"account_note.placeholder": "Notiz durch Klicken hinzufügen",
|
||||
"account_note.placeholder": "Klicken, um Notiz hinzuzufügen",
|
||||
"admin.dashboard.daily_retention": "Verweildauer der Benutzer*innen pro Tag nach der Registrierung",
|
||||
"admin.dashboard.monthly_retention": "Verweildauer der Benutzer*innen pro Monat nach der Registrierung",
|
||||
"admin.dashboard.retention.average": "Durchschnitt",
|
||||
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Versuche, das Add-on oder Übersetzungswerkzeug zu deaktivieren und lade die Seite anschließend neu. Sollte das Problem weiter bestehen, kannst du das Webinterface von Mastodon vermutlich über einen anderen Browser erreichen – oder du verwendest eine mobile (native) App.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Fehlerdiagnose in die Zwischenablage kopieren",
|
||||
"errors.unexpected_crash.report_issue": "Fehler melden",
|
||||
"explore.search_results": "Suchergebnisse",
|
||||
"explore.suggested_follows": "Profile",
|
||||
"explore.title": "Entdecken",
|
||||
"explore.trending_links": "Neuigkeiten",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Über",
|
||||
"footer.directory": "Profilverzeichnis",
|
||||
"footer.get_app": "App herunterladen",
|
||||
"footer.invite": "Leute einladen",
|
||||
"footer.keyboard_shortcuts": "Tastenkombinationen",
|
||||
"footer.privacy_policy": "Datenschutzerklärung",
|
||||
"footer.source_code": "Quellcode anzeigen",
|
||||
"footer.status": "Status",
|
||||
"footer.terms_of_service": "Nutzungsbedingungen",
|
||||
"generic.saved": "Gespeichert",
|
||||
"getting_started.heading": "Auf gehts!",
|
||||
"hashtag.admin_moderation": "#{name} moderieren",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "#Wrapstodon ansehen",
|
||||
"notification.favourite": "{name} favorisierte deinen Beitrag",
|
||||
"notification.favourite.name_and_others_with_link": "{name} und <a>{count, plural, one {# weiteres Profil} other {# weitere Profile}}</a> favorisierten deinen Beitrag",
|
||||
"notification.favourite_pm": "{name} favorisierte deine private Erwähnung",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} und <a>{count, plural, one {# weiteres Profil} other {# weitere Profile}}</a> favorisierten deine private Erwähnung",
|
||||
"notification.follow": "{name} folgt dir",
|
||||
"notification.follow.name_and_others": "{name} und <a>{count, plural, one {# weiteres Profil} other {# weitere Profile}}</a> folgen dir",
|
||||
"notification.follow_request": "{name} möchte dir folgen",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Profile",
|
||||
"search_results.all": "Alles",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Nichts zu diesen Suchbegriffen gefunden",
|
||||
"search_results.no_results": "Keine Ergebnisse.",
|
||||
"search_results.no_search_yet": "Suche nach Beiträgen, Profilen oder Hashtags.",
|
||||
"search_results.see_all": "Alle ansehen",
|
||||
"search_results.statuses": "Beiträge",
|
||||
"search_results.title": "Suchergebnisse für {q}",
|
||||
"search_results.title": "Nach „{q}“ suchen",
|
||||
"server_banner.about_active_users": "Personen, die diesen Server in den vergangenen 30 Tagen verwendet haben (monatlich aktive Nutzer*innen)",
|
||||
"server_banner.active_users": "aktive Profile",
|
||||
"server_banner.administered_by": "Verwaltet von:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Abonnierte Sprachen für {target} ändern",
|
||||
"tabs_bar.home": "Startseite",
|
||||
"tabs_bar.notifications": "Benachrichtigungen",
|
||||
"terms_of_service.title": "Nutzungsbedingungen",
|
||||
"time_remaining.days": "noch {number, plural, one {# Tag} other {# Tage}}",
|
||||
"time_remaining.hours": "noch {number, plural, one {# Stunde} other {# Stunden}}",
|
||||
"time_remaining.minutes": "noch {number, plural, one {# Minute} other {# Minuten}}",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Δοκίμασε να τα απενεργοποιήσεις και ανανέωσε τη σελίδα. Αν αυτό δεν βοηθήσει, ίσως να μπορέσεις να χρησιμοποιήσεις το Mastodon μέσω διαφορετικού φυλλομετρητή ή κάποιας εφαρμογής.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Αντιγραφή μηνυμάτων κώδικα στο πρόχειρο",
|
||||
"errors.unexpected_crash.report_issue": "Αναφορά προβλήματος",
|
||||
"explore.search_results": "Αποτελέσματα αναζήτησης",
|
||||
"explore.suggested_follows": "Άτομα",
|
||||
"explore.title": "Εξερεύνηση",
|
||||
"explore.trending_links": "Νέα",
|
||||
|
@ -359,7 +358,6 @@
|
|||
"footer.about": "Σχετικά με",
|
||||
"footer.directory": "Κατάλογος προφίλ",
|
||||
"footer.get_app": "Αποκτήστε την εφαρμογή",
|
||||
"footer.invite": "Προσκάλεσε άτομα",
|
||||
"footer.keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου",
|
||||
"footer.privacy_policy": "Πολιτική απορρήτου",
|
||||
"footer.source_code": "Προβολή πηγαίου κώδικα",
|
||||
|
@ -781,10 +779,8 @@
|
|||
"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": "Άτομα που χρησιμοποιούν αυτόν τον διακομιστή κατά τις τελευταίες 30 ημέρες (Μηνιαία Ενεργοί Χρήστες)",
|
||||
"server_banner.active_users": "ενεργοί χρήστες",
|
||||
"server_banner.administered_by": "Διαχειριστής:",
|
||||
|
|
|
@ -300,7 +300,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
|
||||
"errors.unexpected_crash.report_issue": "Report issue",
|
||||
"explore.search_results": "Search results",
|
||||
"explore.suggested_follows": "People",
|
||||
"explore.title": "Explore",
|
||||
"explore.trending_links": "News",
|
||||
|
@ -350,7 +349,6 @@
|
|||
"footer.about": "About",
|
||||
"footer.directory": "Profiles directory",
|
||||
"footer.get_app": "Get the app",
|
||||
"footer.invite": "Invite people",
|
||||
"footer.keyboard_shortcuts": "Keyboard shortcuts",
|
||||
"footer.privacy_policy": "Privacy policy",
|
||||
"footer.source_code": "View source code",
|
||||
|
@ -770,10 +768,8 @@
|
|||
"search_results.accounts": "Profiles",
|
||||
"search_results.all": "All",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Could not find anything for these search terms",
|
||||
"search_results.see_all": "See all",
|
||||
"search_results.statuses": "Posts",
|
||||
"search_results.title": "Search for {q}",
|
||||
"server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
|
||||
"server_banner.active_users": "active users",
|
||||
"server_banner.administered_by": "Administered by:",
|
||||
|
|
|
@ -454,7 +454,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
|
||||
"errors.unexpected_crash.report_issue": "Report issue",
|
||||
"explore.search_results": "Search results",
|
||||
"explore.suggested_follows": "People",
|
||||
"explore.title": "Explore",
|
||||
"explore.trending_links": "News",
|
||||
|
@ -504,11 +503,11 @@
|
|||
"footer.about": "About",
|
||||
"footer.directory": "Profiles directory",
|
||||
"footer.get_app": "Get the app",
|
||||
"footer.invite": "Invite people",
|
||||
"footer.keyboard_shortcuts": "Keyboard shortcuts",
|
||||
"footer.privacy_policy": "Privacy policy",
|
||||
"footer.source_code": "View source code",
|
||||
"footer.status": "Status",
|
||||
"footer.terms_of_service": "Terms of service",
|
||||
"generic.saved": "Saved",
|
||||
"getting_started.heading": "Getting started",
|
||||
"hashtag.admin_moderation": "Open moderation interface for #{name}",
|
||||
|
@ -713,6 +712,8 @@
|
|||
"notification.emoji_reaction.name_and_others_with_link": "{name} and <a>{count, plural, one {# other} other {# others}}</a> reacted your post with emoji",
|
||||
"notification.favourite": "{name} favorited your post",
|
||||
"notification.favourite.name_and_others_with_link": "{name} and <a>{count, plural, one {# other} other {# others}}</a> favorited your post",
|
||||
"notification.favourite_pm": "{name} favorited your private mention",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} and <a>{count, plural, one {# other} other {# others}}</a> favorited your private mention",
|
||||
"notification.follow": "{name} followed you",
|
||||
"notification.follow.name_and_others": "{name} and <a>{count, plural, one {# other} other {# others}}</a> followed you",
|
||||
"notification.follow_request": "{name} has requested to follow you",
|
||||
|
@ -969,10 +970,11 @@
|
|||
"search_results.accounts": "Profiles",
|
||||
"search_results.all": "All",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Could not find anything for these search terms",
|
||||
"search_results.no_results": "No results.",
|
||||
"search_results.no_search_yet": "Try searching for posts, profiles or hashtags.",
|
||||
"search_results.see_all": "See all",
|
||||
"search_results.statuses": "Posts",
|
||||
"search_results.title": "Search for {q}",
|
||||
"search_results.title": "Search for \"{q}\"",
|
||||
"searchability.change": "Change post searchability",
|
||||
"searchability.direct.long": "Nobody can find, but you can",
|
||||
"searchability.direct.short": "Self only",
|
||||
|
@ -1075,6 +1077,7 @@
|
|||
"subscribed_languages.target": "Change subscribed languages for {target}",
|
||||
"tabs_bar.home": "Home",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"terms_of_service.title": "Terms of Service",
|
||||
"time_remaining.days": "{number, plural, one {# day} other {# days}} left",
|
||||
"time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
|
||||
"time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"about.domain_blocks.suspended.title": "Suspendita",
|
||||
"about.not_available": "Ĉi tiu informo ne estas disponebla ĉe ĉi tiu servilo.",
|
||||
"about.powered_by": "Malcentrigita socia retejo pere de {mastodon}",
|
||||
"about.rules": "Regularo de la servilo",
|
||||
"about.rules": "Reguloj de la servilo",
|
||||
"account.account_note_header": "Personaj notoj",
|
||||
"account.add_or_remove_from_list": "Aldoni al aŭ forigi el listoj",
|
||||
"account.badges.bot": "Aŭtomata",
|
||||
|
@ -87,9 +87,14 @@
|
|||
"alert.unexpected.title": "Aj!",
|
||||
"alt_text_badge.title": "Alt-teksto",
|
||||
"announcement.announcement": "Anonco",
|
||||
"annual_report.summary.archetype.booster": "La Ĉasanto de Mojoso",
|
||||
"annual_report.summary.archetype.lurker": "La vidanto",
|
||||
"annual_report.summary.archetype.oracle": "La Orakolo",
|
||||
"annual_report.summary.archetype.pollster": "La balotenketisto",
|
||||
"annual_report.summary.archetype.replier": "La plej societema",
|
||||
"annual_report.summary.followers.followers": "sekvantoj",
|
||||
"annual_report.summary.followers.total": "{count} tute",
|
||||
"annual_report.summary.here_it_is": "Jen via resumo de {year}:",
|
||||
"annual_report.summary.highlighted_post.by_favourites": "plej ŝatata afiŝo",
|
||||
"annual_report.summary.highlighted_post.by_reblogs": "plej diskonigita afiŝo",
|
||||
"annual_report.summary.highlighted_post.by_replies": "afiŝo kun la plej multaj respondoj",
|
||||
|
@ -98,6 +103,7 @@
|
|||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "plej uzata kradvorto",
|
||||
"annual_report.summary.most_used_hashtag.none": "Nenio",
|
||||
"annual_report.summary.new_posts.new_posts": "novaj afiŝoj",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Tio metas vin en la plej</topLabel><percentage></percentage><bottomLabel>de {domain} uzantoj.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Ni ne diros al Zamenhof.",
|
||||
"annual_report.summary.thanks": "Dankon pro esti parto de Mastodon!",
|
||||
"attachments_list.unprocessed": "(neprilaborita)",
|
||||
|
@ -233,8 +239,10 @@
|
|||
"disabled_account_banner.text": "Via konto {disabledAccount} estas nune malvalidigita.",
|
||||
"dismissable_banner.community_timeline": "Jen la plej novaj publikaj afiŝoj de uzantoj, kies kontojn gastigas {domain}.",
|
||||
"dismissable_banner.dismiss": "Eksigi",
|
||||
"dismissable_banner.explore_links": "Ĉi tiuj revuaĵoj plejkunhaviĝas en la fediverso hodiaŭ. Pli novaj revuaĵoj afiŝis de pli da homoj metis pli alte.",
|
||||
"dismissable_banner.explore_statuses": "Ĉi tiuj afiŝoj populariĝas sur la fediverso hodiaŭ. Pli novaj afiŝoj kun pli da diskonigoj kaj stemuloj estas rangigitaj pli alte.",
|
||||
"dismissable_banner.explore_tags": "Ĉi tiuj kradvortoj populariĝas sur la fediverso hodiaŭ. Kradvortoj, kiuj estas uzataj de pli malsamaj homoj, estas rangigitaj pli alte.",
|
||||
"dismissable_banner.public_timeline": "Ĉi tiuj estas la plej ĵusaj publikaj afiŝoj de homoj en la fediverso, kiujn la homoj en {domain} sekvas.",
|
||||
"domain_block_modal.block": "Bloki servilon",
|
||||
"domain_block_modal.block_account_instead": "Bloki @{name} anstataŭe",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Homoj de ĉi tiu servilo povas interagi kun viaj malnovaj afiŝoj.",
|
||||
|
@ -301,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Provu malaktivigi ilin kaj tiam refreŝigi la paĝon. Se tio ne helpas, vi ankoraŭ povus uzi Mastodon per malsama retumilo aŭ operaciuma aplikajo.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopii stakspuron en tondujo",
|
||||
"errors.unexpected_crash.report_issue": "Raporti problemon",
|
||||
"explore.search_results": "Serĉaj rezultoj",
|
||||
"explore.suggested_follows": "Homoj",
|
||||
"explore.title": "Esplori",
|
||||
"explore.trending_links": "Novaĵoj",
|
||||
|
@ -351,13 +358,14 @@
|
|||
"footer.about": "Pri",
|
||||
"footer.directory": "Profilujo",
|
||||
"footer.get_app": "Akiri la apon",
|
||||
"footer.invite": "Inviti homojn",
|
||||
"footer.keyboard_shortcuts": "Fulmoklavoj",
|
||||
"footer.privacy_policy": "Politiko de privateco",
|
||||
"footer.source_code": "Montri fontkodon",
|
||||
"footer.status": "Stato",
|
||||
"footer.terms_of_service": "Kondiĉoj de uzado",
|
||||
"generic.saved": "Konservita",
|
||||
"getting_started.heading": "Por komenci",
|
||||
"hashtag.admin_moderation": "Malfermi fasadon de moderigado por #{name}",
|
||||
"hashtag.column_header.tag_mode.all": "kaj {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "aŭ {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "sen {additional}",
|
||||
|
@ -442,7 +450,7 @@
|
|||
"keyboard_shortcuts.notifications": "Malfermu la sciigajn kolumnon",
|
||||
"keyboard_shortcuts.open_media": "Malfermu plurmedion",
|
||||
"keyboard_shortcuts.pinned": "Malfermu alpinglitajn afiŝojn-liston",
|
||||
"keyboard_shortcuts.profile": "Malfermu la profilon de aŭtoro",
|
||||
"keyboard_shortcuts.profile": "Malfermu la profilon de aŭtoroprofilo",
|
||||
"keyboard_shortcuts.reply": "Respondu al afiŝo",
|
||||
"keyboard_shortcuts.requests": "Malfermi la liston de petoj por sekvado",
|
||||
"keyboard_shortcuts.search": "Enfokusigi la serĉbreton",
|
||||
|
@ -488,6 +496,7 @@
|
|||
"lists.replies_policy.none": "Neniu",
|
||||
"lists.save": "Konservi",
|
||||
"lists.search": "Ŝerci",
|
||||
"lists.show_replies_to": "Inkludi respondojn de listomembroj al",
|
||||
"load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}",
|
||||
"loading_indicator.label": "Ŝargado…",
|
||||
"media_gallery.hide": "Kaŝi",
|
||||
|
@ -536,9 +545,12 @@
|
|||
"notification.admin.report_statuses_other": "{name} raportis {target}",
|
||||
"notification.admin.sign_up": "{name} kreis konton",
|
||||
"notification.admin.sign_up.name_and_others": "{name} kaj {count, plural, one {# alia} other {# aliaj}} kreis konton",
|
||||
"notification.annual_report.message": "Via {year} #Wrapstodon atendas! Malkovru viajn bonaĵojn kaj memorindajn momentojn en Mastodon!",
|
||||
"notification.annual_report.view": "Vidu #Wrapstodon",
|
||||
"notification.favourite": "{name} stelumis vian afiŝon",
|
||||
"notification.favourite": "{name} ŝatis vian afiŝon",
|
||||
"notification.favourite.name_and_others_with_link": "{name} kaj <a>{count, plural, one {# alia} other {# aliaj}}</a> ŝatis vian afiŝon",
|
||||
"notification.favourite_pm": "{name} ŝatis vian privatan mencion",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} kaj <a>{count, plural, one {# alia} other {# aliaj}}</a> ŝatis vian privatan mencion",
|
||||
"notification.follow": "{name} eksekvis vin",
|
||||
"notification.follow.name_and_others": "{name} kaj <a>{count, plural, one {# alia} other {# aliaj}}</a> sekvis vin",
|
||||
"notification.follow_request": "{name} petis sekvi vin",
|
||||
|
@ -770,10 +782,8 @@
|
|||
"search_results.accounts": "Profiloj",
|
||||
"search_results.all": "Ĉiuj",
|
||||
"search_results.hashtags": "Kradvortoj",
|
||||
"search_results.nothing_found": "Povis trovi nenion por ĉi tiuj serĉaj terminoj",
|
||||
"search_results.see_all": "Vidu ĉiujn",
|
||||
"search_results.statuses": "Afiŝoj",
|
||||
"search_results.title": "Serĉ-rezultoj por {q}",
|
||||
"server_banner.about_active_users": "Personoj uzantaj ĉi tiun servilon dum la lastaj 30 tagoj (Aktivaj Uzantoj Monate)",
|
||||
"server_banner.active_users": "aktivaj uzantoj",
|
||||
"server_banner.administered_by": "Administrata de:",
|
||||
|
@ -846,6 +856,7 @@
|
|||
"subscribed_languages.target": "Ŝanĝu abonitajn lingvojn por {target}",
|
||||
"tabs_bar.home": "Hejmo",
|
||||
"tabs_bar.notifications": "Sciigoj",
|
||||
"terms_of_service.title": "Kondiĉoj de uzado",
|
||||
"time_remaining.days": "{number, plural, one {# tago} other {# tagoj}} restas",
|
||||
"time_remaining.hours": "{number, plural, one {# horo} other {# horoj}} restas",
|
||||
"time_remaining.minutes": "{number, plural, one {# minuto} other {# minutoj}} restas",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Intentá deshabilitarlos y recargá la página. Si eso no ayuda, podés usar Mastodon a través de un navegador web diferente o aplicación nativa.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copiar stacktrace al portapapeles",
|
||||
"errors.unexpected_crash.report_issue": "Informar problema",
|
||||
"explore.search_results": "Resultados de búsqueda",
|
||||
"explore.suggested_follows": "Cuentas",
|
||||
"explore.title": "Explorá",
|
||||
"explore.trending_links": "Noticias",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Información",
|
||||
"footer.directory": "Directorio de perfiles",
|
||||
"footer.get_app": "Conseguí la aplicación",
|
||||
"footer.invite": "Invitá a gente",
|
||||
"footer.keyboard_shortcuts": "Atajos de teclado",
|
||||
"footer.privacy_policy": "Política de privacidad",
|
||||
"footer.source_code": "Ver código fuente",
|
||||
"footer.status": "Estado",
|
||||
"footer.terms_of_service": "Términos del servicio",
|
||||
"generic.saved": "Guardado",
|
||||
"getting_started.heading": "Inicio de Mastodon",
|
||||
"hashtag.admin_moderation": "Abrir interfaz de moderación para #{name}",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "Ver #Wrapstodon",
|
||||
"notification.favourite": "{name} marcó tu mensaje como favorito",
|
||||
"notification.favourite.name_and_others_with_link": "{name} y <a>{count, plural, one {# cuenta más} other {# cuentas más}}</a> marcaron tu mensaje como favorito",
|
||||
"notification.favourite_pm": "{name} ha marcado como favorita tu mención privada",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} y <a>{count, plural, one {# más} other {# más}}</a> han marcado como favorita tu mención privada",
|
||||
"notification.follow": "{name} te empezó a seguir",
|
||||
"notification.follow.name_and_others": "{name} y <a>{count, plural, one {# cuenta más} other {# cuentas más}}</a> te están siguiendo",
|
||||
"notification.follow_request": "{name} solicitó seguirte",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Perfiles",
|
||||
"search_results.all": "Todos",
|
||||
"search_results.hashtags": "Etiquetas",
|
||||
"search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
|
||||
"search_results.no_results": "Sin resultados.",
|
||||
"search_results.no_search_yet": "Intenta buscar publicaciones, perfiles o etiquetas.",
|
||||
"search_results.see_all": "Ver todo",
|
||||
"search_results.statuses": "Mensajes",
|
||||
"search_results.title": "Buscar {q}",
|
||||
"search_results.title": "Búsqueda de \"{q}\"",
|
||||
"server_banner.about_active_users": "Personas usando este servidor durante los últimos 30 días (Usuarios Activos Mensuales)",
|
||||
"server_banner.active_users": "usuarios activos",
|
||||
"server_banner.administered_by": "Administrado por:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Cambiar idiomas suscritos para {target}",
|
||||
"tabs_bar.home": "Principal",
|
||||
"tabs_bar.notifications": "Notificaciones",
|
||||
"terms_of_service.title": "Términos del servicio",
|
||||
"time_remaining.days": "{number, plural,one {queda # día} other {quedan # días}}",
|
||||
"time_remaining.hours": "{number, plural,one {queda # hora} other {quedan # horas}}",
|
||||
"time_remaining.minutes": "{number, plural,one {queda # minuto} other {quedan # minutos}}",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Intenta deshabilitarlos y recarga la página. Si eso no ayuda, podrías usar Mastodon a través de un navegador web diferente o aplicación nativa.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copiar el seguimiento de pila en el portapapeles",
|
||||
"errors.unexpected_crash.report_issue": "Informar problema",
|
||||
"explore.search_results": "Resultados de búsqueda",
|
||||
"explore.suggested_follows": "Personas",
|
||||
"explore.title": "Descubrir",
|
||||
"explore.trending_links": "Noticias",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Acerca de",
|
||||
"footer.directory": "Directorio de perfiles",
|
||||
"footer.get_app": "Obtener la aplicación",
|
||||
"footer.invite": "Invitar personas",
|
||||
"footer.keyboard_shortcuts": "Atajos de teclado",
|
||||
"footer.privacy_policy": "Política de privacidad",
|
||||
"footer.source_code": "Ver código fuente",
|
||||
"footer.status": "Estado",
|
||||
"footer.terms_of_service": "Condiciones del servicio",
|
||||
"generic.saved": "Guardado",
|
||||
"getting_started.heading": "Primeros pasos",
|
||||
"hashtag.admin_moderation": "Abrir interfaz de moderación para #{name}",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "Ver #Wrapstodon",
|
||||
"notification.favourite": "{name} marcó como favorita tu publicación",
|
||||
"notification.favourite.name_and_others_with_link": "{name} y <a>{count, plural, one {# otro} other {# otros}}</a> marcaron tu publicación como favorita",
|
||||
"notification.favourite_pm": "{name} marcó como favorito tu mención privada",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} y <a>{count, plural, one {# otro} other {# otros}}</a> marcaron como favorito tu mención privada",
|
||||
"notification.follow": "{name} te empezó a seguir",
|
||||
"notification.follow.name_and_others": "{name} y <a>{count, plural, one {# otro} other {# otros}}</a> te han seguido",
|
||||
"notification.follow_request": "{name} ha solicitado seguirte",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Perfiles",
|
||||
"search_results.all": "Todos",
|
||||
"search_results.hashtags": "Etiquetas",
|
||||
"search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
|
||||
"search_results.no_results": "Sin resultados.",
|
||||
"search_results.no_search_yet": "Intenta buscar publicaciones, perfiles o etiquetas.",
|
||||
"search_results.see_all": "Ver todos",
|
||||
"search_results.statuses": "Publicaciones",
|
||||
"search_results.title": "Buscar {q}",
|
||||
"search_results.title": "Búsqueda de \"{q}\"",
|
||||
"server_banner.about_active_users": "Personas utilizando este servidor durante los últimos 30 días (Usuarios Activos Mensuales)",
|
||||
"server_banner.active_users": "usuarios activos",
|
||||
"server_banner.administered_by": "Administrado por:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Cambiar idiomas suscritos para {target}",
|
||||
"tabs_bar.home": "Inicio",
|
||||
"tabs_bar.notifications": "Notificaciones",
|
||||
"terms_of_service.title": "Condiciones del servicio",
|
||||
"time_remaining.days": "{number, plural, one {# día restante} other {# días restantes}}",
|
||||
"time_remaining.hours": "{number, plural, one {# hora restante} other {# horas restantes}}",
|
||||
"time_remaining.minutes": "{number, plural, one {# minuto restante} other {# minutos restantes}}",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Intenta deshabilitarlos y recarga la página. Si eso no ayuda, podrías usar Mastodon a través de un navegador web diferente o aplicación nativa.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copiar el seguimiento de pila en el portapapeles",
|
||||
"errors.unexpected_crash.report_issue": "Informar de un problema/error",
|
||||
"explore.search_results": "Resultados de búsqueda",
|
||||
"explore.suggested_follows": "Personas",
|
||||
"explore.title": "Explorar",
|
||||
"explore.trending_links": "Noticias",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Acerca de",
|
||||
"footer.directory": "Directorio de perfiles",
|
||||
"footer.get_app": "Obtener la aplicación",
|
||||
"footer.invite": "Invitar personas",
|
||||
"footer.keyboard_shortcuts": "Atajos de teclado",
|
||||
"footer.privacy_policy": "Política de privacidad",
|
||||
"footer.source_code": "Ver código fuente",
|
||||
"footer.status": "Estado",
|
||||
"footer.terms_of_service": "Términos del servicio",
|
||||
"generic.saved": "Guardado",
|
||||
"getting_started.heading": "Primeros pasos",
|
||||
"hashtag.admin_moderation": "Abrir interfaz de moderación para #{name}",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "Ver #Wrapstodon",
|
||||
"notification.favourite": "{name} marcó como favorita tu publicación",
|
||||
"notification.favourite.name_and_others_with_link": "{name} y <a>{count, plural, one {# más} other {# más}}</a> marcaron tu publicación como favorita",
|
||||
"notification.favourite_pm": "{name} ha marcado como favorita tu mención privada",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} y <a>{count, plural, one {# más} other {# más}}</a> han marcado como favorita tu mención privada",
|
||||
"notification.follow": "{name} te empezó a seguir",
|
||||
"notification.follow.name_and_others": "{name} y <a>{count, plural, one {# otro} other {# otros}}</a> te siguieron",
|
||||
"notification.follow_request": "{name} ha solicitado seguirte",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Perfiles",
|
||||
"search_results.all": "Todos",
|
||||
"search_results.hashtags": "Etiquetas",
|
||||
"search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
|
||||
"search_results.no_results": "Sin resultados.",
|
||||
"search_results.no_search_yet": "Intenta buscar publicaciones, perfiles o etiquetas.",
|
||||
"search_results.see_all": "Ver todos",
|
||||
"search_results.statuses": "Publicaciones",
|
||||
"search_results.title": "Buscar {q}",
|
||||
"search_results.title": "Búsqueda de \"{q}\"",
|
||||
"server_banner.about_active_users": "Usuarios activos en el servidor durante los últimos 30 días (Usuarios Activos Mensuales)",
|
||||
"server_banner.active_users": "usuarios activos",
|
||||
"server_banner.administered_by": "Administrado por:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Cambiar idiomas suscritos para {target}",
|
||||
"tabs_bar.home": "Inicio",
|
||||
"tabs_bar.notifications": "Notificaciones",
|
||||
"terms_of_service.title": "Términos del servicio",
|
||||
"time_remaining.days": "{number, plural, one {# día restante} other {# días restantes}}",
|
||||
"time_remaining.hours": "{number, plural, one {# hora restante} other {# horas restantes}}",
|
||||
"time_remaining.minutes": "{number, plural, one {# minuto restante} other {# minutos restantes}}",
|
||||
|
|
|
@ -305,7 +305,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Proovi need välja lülitada ja leht uuesti laadida. Kui sellest pole abi, võib siiski võimalik olla Mastodoni kasutada mõne teise lehitseja või rakendusega.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopeeri stacktrace lõikelauale",
|
||||
"errors.unexpected_crash.report_issue": "Teavita veast",
|
||||
"explore.search_results": "Otsitulemused",
|
||||
"explore.suggested_follows": "Inimesed",
|
||||
"explore.title": "Avasta",
|
||||
"explore.trending_links": "Uudised",
|
||||
|
@ -355,7 +354,6 @@
|
|||
"footer.about": "Teave",
|
||||
"footer.directory": "Profiilikataloog",
|
||||
"footer.get_app": "Tõmba äpp",
|
||||
"footer.invite": "Kutsu liituma",
|
||||
"footer.keyboard_shortcuts": "Kiirklahvid",
|
||||
"footer.privacy_policy": "Isikuandmete kaitse",
|
||||
"footer.source_code": "Lähtekood",
|
||||
|
@ -752,10 +750,8 @@
|
|||
"search_results.accounts": "Profiilid",
|
||||
"search_results.all": "Kõik",
|
||||
"search_results.hashtags": "Sildid",
|
||||
"search_results.nothing_found": "Otsisõnadele vastavat sisu ei leitud",
|
||||
"search_results.see_all": "Vaata kõiki",
|
||||
"search_results.statuses": "Postitused",
|
||||
"search_results.title": "{q} otsing",
|
||||
"server_banner.about_active_users": "Inimesed, kes kasutavad seda serverit viimase 30 päeva jooksul (kuu aktiivsed kasutajad)",
|
||||
"server_banner.active_users": "aktiivsed kasutajad",
|
||||
"server_banner.administered_by": "Administraator:",
|
||||
|
|
|
@ -277,7 +277,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Saiatu desgaitu eta orria berritzen. Horrek ez badu laguntzen, agian Mastodon erabiltzeko aukera duzu oraindik ere beste nabigatzaile bat edo aplikazio natibo bat erabilita.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopiatu irteera arbelera",
|
||||
"errors.unexpected_crash.report_issue": "Eman arazoaren berri",
|
||||
"explore.search_results": "Bilaketaren emaitzak",
|
||||
"explore.suggested_follows": "Jendea",
|
||||
"explore.title": "Arakatu",
|
||||
"explore.trending_links": "Berriak",
|
||||
|
@ -326,7 +325,6 @@
|
|||
"footer.about": "Honi buruz",
|
||||
"footer.directory": "Profil-direktorioa",
|
||||
"footer.get_app": "Eskuratu aplikazioa",
|
||||
"footer.invite": "Gonbidatu jendea",
|
||||
"footer.keyboard_shortcuts": "Lasterbideak",
|
||||
"footer.privacy_policy": "Pribatutasun politika",
|
||||
"footer.source_code": "Ikusi iturburu kodea",
|
||||
|
@ -710,10 +708,8 @@
|
|||
"search_results.accounts": "Profilak",
|
||||
"search_results.all": "Guztiak",
|
||||
"search_results.hashtags": "Traolak",
|
||||
"search_results.nothing_found": "Ez da emaitzarik aurkitu bilaketa-termino horientzat",
|
||||
"search_results.see_all": "Ikusi guztiak",
|
||||
"search_results.statuses": "Bidalketak",
|
||||
"search_results.title": "Bilatu {q}",
|
||||
"server_banner.about_active_users": "Azken 30 egunetan zerbitzari hau erabili duen jendea (hilabeteko erabiltzaile aktiboak)",
|
||||
"server_banner.active_users": "erabiltzaile aktibo",
|
||||
"server_banner.administered_by": "Administratzailea(k):",
|
||||
|
|
|
@ -87,7 +87,24 @@
|
|||
"alert.unexpected.title": "ای وای!",
|
||||
"alt_text_badge.title": "متن جایگزین",
|
||||
"announcement.announcement": "اعلامیه",
|
||||
"annual_report.summary.archetype.booster": "باحالیاب",
|
||||
"annual_report.summary.archetype.lurker": "کمپیدا",
|
||||
"annual_report.summary.archetype.oracle": "غیبگو",
|
||||
"annual_report.summary.archetype.pollster": "نظرسنج",
|
||||
"annual_report.summary.archetype.replier": "پاسخگو",
|
||||
"annual_report.summary.followers.followers": "دنبال کننده",
|
||||
"annual_report.summary.followers.total": "در مجموع {count}",
|
||||
"annual_report.summary.here_it_is": "بازبینی {year}تان:",
|
||||
"annual_report.summary.highlighted_post.by_favourites": "پرپسندترین فرسته",
|
||||
"annual_report.summary.highlighted_post.by_reblogs": "پرتقویتترین فرسته",
|
||||
"annual_report.summary.highlighted_post.by_replies": "پرپاسخترین فرسته",
|
||||
"annual_report.summary.most_used_app.most_used_app": "پراستفادهترین کاره",
|
||||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "پراستفادهترین برچسب",
|
||||
"annual_report.summary.most_used_hashtag.none": "هیچکدام",
|
||||
"annual_report.summary.new_posts.new_posts": "فرستهٔ جدید",
|
||||
"annual_report.summary.percentile.text": "<topLabel>بین کاربران {domain} جزو</topLabel><percentage></percentage><bottomLabel>برتر هستید.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "به برنی خبر نمیدهیم.",
|
||||
"annual_report.summary.thanks": "سپاس که بخشی از ماستودون هستید!",
|
||||
"attachments_list.unprocessed": "(پردازش نشده)",
|
||||
"audio.hide": "نهفتن صدا",
|
||||
"block_modal.remote_users_caveat": "ما از کارساز {domain} خواهیم خواست که به تصمیم شما احترام بگذارد. با این حال، تضمینی برای رعایت آن وجود ندارد زیرا برخی کارسازها ممکن است بلوکها را بهطور متفاوتی مدیریت کنند. فرستههای عمومی ممکن است همچنان برای کاربران که وارد نشده قابل مشاهده باشند.",
|
||||
|
@ -111,6 +128,7 @@
|
|||
"bundle_column_error.routing.body": "صفحهٔ درخواستی پیدا نشد. مطمئنید که نشانی را درست وارد کردهاید؟",
|
||||
"bundle_column_error.routing.title": "۴۰۴",
|
||||
"bundle_modal_error.close": "بستن",
|
||||
"bundle_modal_error.message": "هنگام بار کردن این صفحه، اشتباهی رخ داد.",
|
||||
"bundle_modal_error.retry": "تلاش دوباره",
|
||||
"closed_registrations.other_server_instructions": "از آنجا که ماستودون نامتمرکز است، میتوانید حسابی روی کارسازی دیگر ساخته و همچنان با اینیکی در تعامل باشید.",
|
||||
"closed_registrations_modal.description": "هماکنون امکان ساخت حساب روی {domain} وجود ندارد؛ ولی لطفاً به خاطر داشته باشید که برای استفاده از ماستودون، نیازی به داشتن حساب روی {domain} نیست.",
|
||||
|
@ -121,13 +139,16 @@
|
|||
"column.blocks": "کاربران مسدود شده",
|
||||
"column.bookmarks": "نشانکها",
|
||||
"column.community": "خط زمانی محلی",
|
||||
"column.create_list": "ایجاد سیاهه",
|
||||
"column.direct": "اشارههای خصوصی",
|
||||
"column.directory": "مرور نمایهها",
|
||||
"column.domain_blocks": "دامنههای مسدود شده",
|
||||
"column.edit_list": "ویرایش سیاهه",
|
||||
"column.favourites": "برگزیدهها",
|
||||
"column.firehose": "خوراکهای زنده",
|
||||
"column.follow_requests": "درخواستهای پیگیری",
|
||||
"column.home": "خانه",
|
||||
"column.list_members": "مدیریت اعضای سیاهه",
|
||||
"column.lists": "سیاههها",
|
||||
"column.mutes": "کاربران خموش",
|
||||
"column.notifications": "آگاهیها",
|
||||
|
@ -183,6 +204,9 @@
|
|||
"confirmations.edit.confirm": "ویرایش",
|
||||
"confirmations.edit.message": "در صورت ویرایش، پیامی که در حال نوشتنش بودید از بین خواهد رفت. میخواهید ادامه دهید؟",
|
||||
"confirmations.edit.title": "رونویسی فرسته؟",
|
||||
"confirmations.follow_to_list.confirm": "پیگیری و افزودن به سیاهه",
|
||||
"confirmations.follow_to_list.message": "برای افزودن {name} به سیاهه باید پیش گرفته باشید.",
|
||||
"confirmations.follow_to_list.title": "پیگیری کاربر؟",
|
||||
"confirmations.logout.confirm": "خروج از حساب",
|
||||
"confirmations.logout.message": "مطمئنید میخواهید خارج شوید؟",
|
||||
"confirmations.logout.title": "خروج؟",
|
||||
|
@ -198,6 +222,7 @@
|
|||
"confirmations.unfollow.title": "ناپیگیری کاربر؟",
|
||||
"content_warning.hide": "نهفتن فرسته",
|
||||
"content_warning.show": "در هر صورت نشان داده شود",
|
||||
"content_warning.show_more": "نمایش بیشتر",
|
||||
"conversation.delete": "حذف گفتگو",
|
||||
"conversation.mark_as_read": "علامتگذاری به عنوان خوانده شده",
|
||||
"conversation.open": "دیدن گفتگو",
|
||||
|
@ -213,6 +238,10 @@
|
|||
"disabled_account_banner.text": "حسابتان {disabledAccount} اکنون از کار افتاده.",
|
||||
"dismissable_banner.community_timeline": "اینها جدیدترین فرستههای عمومی از افرادیند که حسابهایشان به دست {domain} میزبانی میشود.",
|
||||
"dismissable_banner.dismiss": "دور انداختن",
|
||||
"dismissable_banner.explore_links": "امروز این روایتهای خبری بیشتر روی وب اجتماعی همرسانی میشوند. روایتهای خبری جدیدتری که به دست افراد بیشتری فرستاده شدهاند، بالاتر قرار گرفتهاند.",
|
||||
"dismissable_banner.explore_statuses": "امروز این فرستهها روی وب اجتماعی جذّابند. فرستههای جدیدتری که بیشتر برگزیده و تقویت شده باشند، بالاتر قرار گرفتهاند.",
|
||||
"dismissable_banner.explore_tags": "امروز این برچسبها روی وب اجتماعی جذّابند. برچسبهایی که به دست افراد بیشتری استفاده شده باشند، بالاتر قرار گرفتهاند.",
|
||||
"dismissable_banner.public_timeline": "اینها جدیدترین فرستههای عمومی از افرادی روی وب اجتماعیند که اعضای {domain} پی میگیرندشان.",
|
||||
"domain_block_modal.block": "انسداد کارساز",
|
||||
"domain_block_modal.block_account_instead": "انسداد @{name} به جایش",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "افزارد روی این کراساز میتوانند با فرستههای قدیمیتان تعامل داشته باشند.",
|
||||
|
@ -270,6 +299,7 @@
|
|||
"empty_column.home": "خط زمانی خانگیتان خالی است! برای پر کردنش، افراد بیشتری را پی بگیرید. {suggestions}",
|
||||
"empty_column.list": "هنوز چیزی در این سیاهه نیست. هنگامی که اعضایش فرستههای جدیدی بفرستند، اینجا ظاهر خواهند شد.",
|
||||
"empty_column.mutes": "هنوز هیچ کاربری را خموش نکردهاید.",
|
||||
"empty_column.notification_requests": "همه چیز تمیز است! هیچچیزی اینجا نیست. هنگامی که آگاهیهای جدیدی دریافت کنید، بسته به تنظیماتتان اینجا ظاهر خواهند شد.",
|
||||
"empty_column.notifications": "هنوز هیچ آگاهیآی ندارید. هنگامی که دیگران با شما برهمکنش داشته باشند،اینحا خواهید دیدش.",
|
||||
"empty_column.public": "اینجا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران کارسازهای دیگر را پیگیری کنید تا اینجا پُر شود",
|
||||
"error.unexpected_crash.explanation": "به خاطر اشکالی در کدهای ما یا ناسازگاری با مرورگر شما، این صفحه به درستی نمایش نیافت.",
|
||||
|
@ -278,7 +308,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "لطفاً از کارشان انداخته و صفحه را نوسازی کنید. اگر کمکی نکرد، شاید همچنان بتوانید با مرورگری دیگر یا با کارهای بومی از ماستودون استفاده کنید.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "رونوشت از جزئیات اشکال",
|
||||
"errors.unexpected_crash.report_issue": "گزارش مشکل",
|
||||
"explore.search_results": "نتایج جستوجو",
|
||||
"explore.suggested_follows": "افراد",
|
||||
"explore.title": "کاوش",
|
||||
"explore.trending_links": "اخبار",
|
||||
|
@ -300,6 +329,7 @@
|
|||
"filter_modal.select_filter.subtitle": "استفاده از یک دستهً موجود یا ایجاد دستهای جدید",
|
||||
"filter_modal.select_filter.title": "پالایش این فرسته",
|
||||
"filter_modal.title.status": "پالایش یک فرسته",
|
||||
"filter_warning.matches_filter": "مطابق با پالایهٔ «<span>{title}</span>»",
|
||||
"filtered_notifications_banner.pending_requests": "از {count, plural, =0 {هیچکسی} one {فردی} other {# نفر}} که ممکن است بشناسید",
|
||||
"filtered_notifications_banner.title": "آگاهیهای پالوده",
|
||||
"firehose.all": "همه",
|
||||
|
@ -327,13 +357,13 @@
|
|||
"footer.about": "درباره",
|
||||
"footer.directory": "فهرست نمایهها",
|
||||
"footer.get_app": "گرفتن کاره",
|
||||
"footer.invite": "دعوت دیگران",
|
||||
"footer.keyboard_shortcuts": "میانبرهای صفحهکلید",
|
||||
"footer.privacy_policy": "سیاست محرمانگی",
|
||||
"footer.source_code": "نمایش کد مبدأ",
|
||||
"footer.status": "وضعیت",
|
||||
"generic.saved": "ذخیره شده",
|
||||
"getting_started.heading": "آغاز کنید",
|
||||
"hashtag.admin_moderation": "گشودن میانای نظارت برای #{name}",
|
||||
"hashtag.column_header.tag_mode.all": "و {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "یا {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "بدون {additional}",
|
||||
|
@ -375,6 +405,7 @@
|
|||
"interaction_modal.description.follow": "با حسابی روی ماستودون میتوانید {name} را برای دریافت فرستههایش در خوراک خانگیتان دنبال کنید.",
|
||||
"interaction_modal.description.reblog": "با حسابی روی ماستودون میتوانید این فرسته را با پیگیران خودتان همرسانی کنید.",
|
||||
"interaction_modal.description.reply": "با حسابی روی ماستودون میتوانید به این فرسته پاسخ دهید.",
|
||||
"interaction_modal.description.vote": "با حسابی روی ماستودون میتوانید در این نظرسنجی شرکت کنید.",
|
||||
"interaction_modal.login.action": "رفتن به خانه",
|
||||
"interaction_modal.login.prompt": "دامنهٔ کارساز شخصیتان چون mastodon.social",
|
||||
"interaction_modal.no_account_yet": "در ماستودون نیست؟",
|
||||
|
@ -386,6 +417,7 @@
|
|||
"interaction_modal.title.follow": "پیگیری {name}",
|
||||
"interaction_modal.title.reblog": "تقویت فرستهٔ {name}",
|
||||
"interaction_modal.title.reply": "پاسخ به فرستهٔ {name}",
|
||||
"interaction_modal.title.vote": "رأی دادن در نظرسنجی {name}",
|
||||
"intervals.full.days": "{number, plural, one {# روز} other {# روز}}",
|
||||
"intervals.full.hours": "{number, plural, one {# ساعت} other {# ساعت}}",
|
||||
"intervals.full.minutes": "{number, plural, one {# دقیقه} other {# دقیقه}}",
|
||||
|
@ -433,13 +465,31 @@
|
|||
"link_preview.author": "از {name}",
|
||||
"link_preview.more_from_author": "بیشتر از {name}",
|
||||
"link_preview.shares": "{count, plural, one {{counter} فرسته} other {{counter} فرسته}}",
|
||||
"lists.add_member": "افزودن",
|
||||
"lists.add_to_list": "افزودن به سیاهه",
|
||||
"lists.add_to_lists": "افزودن {name} به سیاههها",
|
||||
"lists.create": "ایجاد",
|
||||
"lists.create_a_list_to_organize": "ایحاد فهرستی جدید برای سازماندهی خوراک خانگیتان",
|
||||
"lists.create_list": "ایجاد سیاهه",
|
||||
"lists.delete": "حذف سیاهه",
|
||||
"lists.done": "انجام شد",
|
||||
"lists.edit": "ویرایش سیاهه",
|
||||
"lists.exclusive": "نهفتن اعضا در خانه",
|
||||
"lists.exclusive_hint": "اگر کسی در این سیاهه باشد، در خوراک خانگیتان نهفته تا از نمایش دویارهٔ فرستههایش خودداری شود.",
|
||||
"lists.find_users_to_add": "یافتن کاربرانی برای افزودن",
|
||||
"lists.list_members": "اعضای سیاهه",
|
||||
"lists.list_members_count": "{count, plural,one {# عضو}other {# عضو}}",
|
||||
"lists.list_name": "نام سیاهه",
|
||||
"lists.new_list_name": "نام سیاههٔ جدید",
|
||||
"lists.no_lists_yet": "هنوز هیچ سیاههای نیست.",
|
||||
"lists.no_members_yet": "هنوز هیچ عضوی نیست.",
|
||||
"lists.no_results_found": "هیچ نتیجهای پیدا نشد.",
|
||||
"lists.remove_member": "حذف",
|
||||
"lists.replies_policy.followed": "هر کاربر پیگرفته",
|
||||
"lists.replies_policy.list": "اعضای سیاهه",
|
||||
"lists.replies_policy.none": "هیچ کدام",
|
||||
"lists.save": "ذخیره",
|
||||
"lists.search": "جستوجو",
|
||||
"load_pending": "{count, plural, one {# مورد جدید} other {# مورد جدید}}",
|
||||
"loading_indicator.label": "در حال بارگذاری…",
|
||||
"media_gallery.hide": "نهفتن",
|
||||
|
@ -485,6 +535,8 @@
|
|||
"notification.admin.report_statuses_other": "{name}، {target} را گزارش داد",
|
||||
"notification.admin.sign_up": "{name} ثبت نام کرد",
|
||||
"notification.admin.sign_up.name_and_others": "{name} و {count, plural, one {# نفر دیگر} other {# نفر دیگر}} ثبتنام کردند",
|
||||
"notification.annual_report.message": "آمار #Wrapstodon {year}تان منتظر است! لحظههای به یاد ماندنی و نقاط پررنگ سال را روی ماستودون رونمایی کنید!",
|
||||
"notification.annual_report.view": "دیدن #Wrapstodon",
|
||||
"notification.favourite": "{name} فرستهتان را برگزید",
|
||||
"notification.favourite.name_and_others_with_link": "{name} و <a>{count, plural, one {# نفر دیگر} other {# نفر دیگر}}</a> فرستهتان را برگزیدند",
|
||||
"notification.follow": "{name} پیگیرتان شد",
|
||||
|
@ -710,10 +762,8 @@
|
|||
"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": "افرادی که در ۳۰ روز گذشته از این کارساز استفاده کردهاند (کاربران فعّال ماهانه)",
|
||||
"server_banner.active_users": "کاربر فعّال",
|
||||
"server_banner.administered_by": "به مدیریت:",
|
||||
|
@ -770,7 +820,7 @@
|
|||
"status.sensitive_warning": "محتوای حساس",
|
||||
"status.share": "همرسانی",
|
||||
"status.show_less_all": "نمایش کمتر همه",
|
||||
"status.show_more_all": "نمایش بیشتر همه",
|
||||
"status.show_more_all": "نمایش بیشتر همه",
|
||||
"status.show_original": "نمایش اصلی",
|
||||
"status.title.with_attachments": "{user} {attachmentCount, plural, one {یک پیوست} other {{attachmentCount} پیوست}} فرستاد",
|
||||
"status.translate": "ترجمه",
|
||||
|
|
|
@ -308,7 +308,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Yritä poistaa ne käytöstä, ja virkistä sitten sivunlataus. Mikäli ongelma jatkuu, voit mahdollisesti käyttää Mastodonia eri selaimella tai natiivilla sovelluksella.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopioi pinon jäljitys leikepöydälle",
|
||||
"errors.unexpected_crash.report_issue": "Ilmoita ongelmasta",
|
||||
"explore.search_results": "Hakutulokset",
|
||||
"explore.suggested_follows": "Käyttäjät",
|
||||
"explore.title": "Selaa",
|
||||
"explore.trending_links": "Uutiset",
|
||||
|
@ -358,11 +357,11 @@
|
|||
"footer.about": "Tietoja",
|
||||
"footer.directory": "Profiilihakemisto",
|
||||
"footer.get_app": "Hanki sovellus",
|
||||
"footer.invite": "Kutsu käyttäjiä",
|
||||
"footer.keyboard_shortcuts": "Pikanäppäimet",
|
||||
"footer.privacy_policy": "Tietosuojakäytäntö",
|
||||
"footer.source_code": "Näytä lähdekoodi",
|
||||
"footer.status": "Tila",
|
||||
"footer.terms_of_service": "Käyttöehdot",
|
||||
"generic.saved": "Tallennettu",
|
||||
"getting_started.heading": "Näin pääset alkuun",
|
||||
"hashtag.admin_moderation": "Avaa tunnisteen #{name} moderointinäkymä",
|
||||
|
@ -549,6 +548,8 @@
|
|||
"notification.annual_report.view": "Näytä #Wrapstodon",
|
||||
"notification.favourite": "{name} lisäsi julkaisusi suosikkeihinsa",
|
||||
"notification.favourite.name_and_others_with_link": "{name} ja <a>{count, plural, one {# muu} other {# muuta}}</a> lisäsivät julkaisusi suosikkeihinsa",
|
||||
"notification.favourite_pm": "{name} lisäsi yksityismainintasi suosikkeihinsa",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} ja <a>{count, plural, one {# muu} other {# muuta}}</a> lisäsivät yksityismainintasi suosikkeihinsa",
|
||||
"notification.follow": "{name} seurasi sinua",
|
||||
"notification.follow.name_and_others": "{name} ja <a>{count, plural, one {# muu} other {# muuta}}</a> seurasivat sinua",
|
||||
"notification.follow_request": "{name} on pyytänyt lupaa seurata sinua",
|
||||
|
@ -780,10 +781,11 @@
|
|||
"search_results.accounts": "Profiilit",
|
||||
"search_results.all": "Kaikki",
|
||||
"search_results.hashtags": "Aihetunnisteet",
|
||||
"search_results.nothing_found": "Hakusi ei tuottanut tuloksia",
|
||||
"search_results.no_results": "Ei tuloksia.",
|
||||
"search_results.no_search_yet": "Kokeile hakea julkaisuja, profiileja tai aihetunnisteita.",
|
||||
"search_results.see_all": "Näytä kaikki",
|
||||
"search_results.statuses": "Julkaisut",
|
||||
"search_results.title": "Hae {q}",
|
||||
"search_results.title": "Hae ”{q}”",
|
||||
"server_banner.about_active_users": "Palvelimen käyttäjät viimeisten 30 päivän ajalta (kuukauden aktiiviset käyttäjät)",
|
||||
"server_banner.active_users": "aktiivista käyttäjää",
|
||||
"server_banner.administered_by": "Ylläpitäjä:",
|
||||
|
@ -856,6 +858,7 @@
|
|||
"subscribed_languages.target": "Vaihda tilattuja kieliä käyttäjältä {target}",
|
||||
"tabs_bar.home": "Koti",
|
||||
"tabs_bar.notifications": "Ilmoitukset",
|
||||
"terms_of_service.title": "Käyttöehdot",
|
||||
"time_remaining.days": "{number, plural, one {# päivä} other {# päivää}} jäljellä",
|
||||
"time_remaining.hours": "{number, plural, one {# tunti} other {# tuntia}} jäljellä",
|
||||
"time_remaining.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} jäljellä",
|
||||
|
|
|
@ -180,7 +180,6 @@
|
|||
"empty_column.home": "Walang laman ang timeline ng tahanan mo! Sumunod sa marami pang tao para mapunan ito.",
|
||||
"empty_column.list": "Wala pang laman ang listahang ito. Kapag naglathala ng mga bagong post ang mga miyembro ng listahang ito, makikita iyon dito.",
|
||||
"errors.unexpected_crash.report_issue": "Iulat ang isyu",
|
||||
"explore.search_results": "Mga resulta ng paghahanap",
|
||||
"explore.suggested_follows": "Mga tao",
|
||||
"explore.title": "Tuklasin",
|
||||
"explore.trending_links": "Mga balita",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Royn at gera tey óvirkin og lesa síðuna inn av nýggjum. Hjálpir tað ikki, so kann vera, at tað ber til at brúka Mastodon við einum øðrum kaga ella við eini app.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Avrita stakkaslóðina til setiborðið",
|
||||
"errors.unexpected_crash.report_issue": "Fráboða trupulleika",
|
||||
"explore.search_results": "Leitiúrslit",
|
||||
"explore.suggested_follows": "Fólk",
|
||||
"explore.title": "Rannsaka",
|
||||
"explore.trending_links": "Tíðindi",
|
||||
|
@ -359,7 +358,6 @@
|
|||
"footer.about": "Um",
|
||||
"footer.directory": "Vangaskrá",
|
||||
"footer.get_app": "Heinta appina",
|
||||
"footer.invite": "Bjóða fólki",
|
||||
"footer.keyboard_shortcuts": "Knappasnarvegir",
|
||||
"footer.privacy_policy": "Privatlívspolitikkur",
|
||||
"footer.source_code": "Vís keldukotuna",
|
||||
|
@ -550,6 +548,8 @@
|
|||
"notification.annual_report.view": "Sí #Wrapstodon",
|
||||
"notification.favourite": "{name} dámdi postin hjá tær",
|
||||
"notification.favourite.name_and_others_with_link": "{name} og <a>{count, plural, one {# annar/onnur} other {# onnur}}</a> yndisfrámerktu postin hjá tær",
|
||||
"notification.favourite_pm": "{name} yndismerkti tína privatu umrøðu",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} og <a>{count, plural, one {# annar yndismerkti} other {# onnur yndismerktu}}</a> tína privatu umrøðu",
|
||||
"notification.follow": "{name} fylgdi tær",
|
||||
"notification.follow.name_and_others": "{name} og <a>{count, plural, one {# annar/onnur} other {# onnur}}</a> fylgdu tær",
|
||||
"notification.follow_request": "{name} biður um at fylgja tær",
|
||||
|
@ -781,10 +781,8 @@
|
|||
"search_results.accounts": "Vangar",
|
||||
"search_results.all": "Alt",
|
||||
"search_results.hashtags": "Frámerki",
|
||||
"search_results.nothing_found": "Hesi leitiorð góvu ongi úrslit",
|
||||
"search_results.see_all": "Sí øll",
|
||||
"search_results.statuses": "Postar",
|
||||
"search_results.title": "Leita eftir {q}",
|
||||
"server_banner.about_active_users": "Fólk, sum hava brúkt hendan ambætaran seinastu 30 dagarnar (mánaðarligir virknir brúkarar)",
|
||||
"server_banner.active_users": "virknir brúkarar",
|
||||
"server_banner.administered_by": "Umsitari:",
|
||||
|
|
|
@ -85,10 +85,13 @@
|
|||
"alert.rate_limited.title": "Débit limité",
|
||||
"alert.unexpected.message": "Une erreur inattendue s’est produite.",
|
||||
"alert.unexpected.title": "Oups!",
|
||||
"alt_text_badge.title": "Texte Alt",
|
||||
"alt_text_badge.title": "Texte alternatif",
|
||||
"announcement.announcement": "Annonce",
|
||||
"annual_report.summary.archetype.booster": "Le chasseur de sang-froid",
|
||||
"annual_report.summary.archetype.lurker": "Le faucheur",
|
||||
"annual_report.summary.archetype.oracle": "L’oracle",
|
||||
"annual_report.summary.archetype.pollster": "Le sondeur",
|
||||
"annual_report.summary.archetype.replier": "Le papillon social",
|
||||
"annual_report.summary.followers.followers": "abonné·e·s",
|
||||
"annual_report.summary.followers.total": "{count} au total",
|
||||
"annual_report.summary.here_it_is": "Voici votre récap de {year} :",
|
||||
|
@ -99,7 +102,8 @@
|
|||
"annual_report.summary.most_used_app.most_used_app": "appli la plus utilisée",
|
||||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "hashtag le plus utilisé",
|
||||
"annual_report.summary.most_used_hashtag.none": "Aucun",
|
||||
"annual_report.summary.new_posts.new_posts": "nouveaux posts",
|
||||
"annual_report.summary.new_posts.new_posts": "nouveaux messages",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Cela vous place dans le top</topLabel><pourcentage></percentage><bottomLabel>des utilisateurs de {domain}.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Nous ne le dirons pas à Bernie.",
|
||||
"annual_report.summary.thanks": "Merci de faire partie de Mastodon!",
|
||||
"attachments_list.unprocessed": "(non traité)",
|
||||
|
@ -108,10 +112,10 @@
|
|||
"block_modal.show_less": "Afficher moins",
|
||||
"block_modal.show_more": "Afficher plus",
|
||||
"block_modal.they_cant_mention": "Il ne peut pas vous mentionner ou vous suivre.",
|
||||
"block_modal.they_cant_see_posts": "Il peut toujours voir vos publications, mais vous ne verrez pas les siennes.",
|
||||
"block_modal.they_cant_see_posts": "Il peut toujours voir vos messages, mais vous ne verrez pas les siens.",
|
||||
"block_modal.they_will_know": "Il peut voir qu'il est bloqué.",
|
||||
"block_modal.title": "Bloquer l'utilisateur·rice ?",
|
||||
"block_modal.you_wont_see_mentions": "Vous ne verrez pas les publications qui le mentionne.",
|
||||
"block_modal.title": "Bloquer le compte ?",
|
||||
"block_modal.you_wont_see_mentions": "Vous ne verrez pas les messages qui le mentionne.",
|
||||
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour sauter ceci la prochaine fois",
|
||||
"boost_modal.reblog": "Booster le message ?",
|
||||
"boost_modal.undo_reblog": "Annuler le boost du message ?",
|
||||
|
@ -125,6 +129,7 @@
|
|||
"bundle_column_error.routing.body": "La page demandée est introuvable. Êtes-vous sûr que l’URL dans la barre d’adresse est correcte?",
|
||||
"bundle_column_error.routing.title": "404",
|
||||
"bundle_modal_error.close": "Fermer",
|
||||
"bundle_modal_error.message": "Un problème s'est produit lors du chargement de cet écran.",
|
||||
"bundle_modal_error.retry": "Réessayer",
|
||||
"closed_registrations.other_server_instructions": "Puisque Mastodon est décentralisé, vous pouvez créer un compte sur un autre serveur et interagir quand même avec celui-ci.",
|
||||
"closed_registrations_modal.description": "Créer un compte sur {domain} est présentement impossible, néanmoins souvenez-vous que vous n'avez pas besoin d'un compte spécifiquement sur {domain} pour utiliser Mastodon.",
|
||||
|
@ -191,7 +196,7 @@
|
|||
"confirmations.block.confirm": "Bloquer",
|
||||
"confirmations.delete.confirm": "Supprimer",
|
||||
"confirmations.delete.message": "Voulez-vous vraiment supprimer cette publication?",
|
||||
"confirmations.delete.title": "Supprimer la publication ?",
|
||||
"confirmations.delete.title": "Supprimer le message ?",
|
||||
"confirmations.delete_list.confirm": "Supprimer",
|
||||
"confirmations.delete_list.message": "Voulez-vous vraiment supprimer définitivement cette liste?",
|
||||
"confirmations.delete_list.title": "Supprimer la liste ?",
|
||||
|
@ -200,6 +205,9 @@
|
|||
"confirmations.edit.confirm": "Éditer",
|
||||
"confirmations.edit.message": "Modifier maintenant écrasera votre message en cours de rédaction. Voulez-vous vraiment continuer ?",
|
||||
"confirmations.edit.title": "Remplacer le message ?",
|
||||
"confirmations.follow_to_list.confirm": "Suivre et ajouter à la liste",
|
||||
"confirmations.follow_to_list.message": "Vous devez suivre {name} pour l'ajouter à une liste.",
|
||||
"confirmations.follow_to_list.title": "Suivre l'utilisateur ?",
|
||||
"confirmations.logout.confirm": "Se déconnecter",
|
||||
"confirmations.logout.message": "Voulez-vous vraiment vous déconnecter?",
|
||||
"confirmations.logout.title": "Se déconnecter ?",
|
||||
|
@ -214,8 +222,8 @@
|
|||
"confirmations.unfollow.message": "Voulez-vous vraiment arrêter de suivre {name}?",
|
||||
"confirmations.unfollow.title": "Se désabonner de l'utilisateur·rice ?",
|
||||
"content_warning.hide": "Masquer le message",
|
||||
"content_warning.show": "Afficher quand même",
|
||||
"content_warning.show_more": "Déplier",
|
||||
"content_warning.show": "Montrer quand même",
|
||||
"content_warning.show_more": "Montrer plus",
|
||||
"conversation.delete": "Supprimer cette conversation",
|
||||
"conversation.mark_as_read": "Marquer comme lu",
|
||||
"conversation.open": "Afficher cette conversation",
|
||||
|
@ -231,15 +239,19 @@
|
|||
"disabled_account_banner.text": "Votre compte {disabledAccount} est présentement désactivé.",
|
||||
"dismissable_banner.community_timeline": "Voici les publications publiques les plus récentes de personnes dont les comptes sont hébergés par {domain}.",
|
||||
"dismissable_banner.dismiss": "Rejeter",
|
||||
"dismissable_banner.explore_links": "Ces nouvelles sont les plus partagées sur le fediverse aujourd'hui. Les nouvelles plus récentes postées par un plus grand nombre de personnes sont mieux classées.",
|
||||
"dismissable_banner.explore_statuses": "Ces messages provenant de l'ensemble du fediverse gagnent en popularité aujourd'hui. Les messages les plus récents qui ont reçu le plus d'encouragements et de favoris sont mieux classés.",
|
||||
"dismissable_banner.explore_tags": "Ces hashtags gagnent du terrain sur le fediverse aujourd'hui. Les hashtags qui sont utilisés par un plus grand nombre de personnes différentes sont mieux classés.",
|
||||
"dismissable_banner.public_timeline": "Il s'agit des messages publics les plus récents publiés par des personnes sur le fediverse que les personnes sur {domain} suivent.",
|
||||
"domain_block_modal.block": "Bloquer le serveur",
|
||||
"domain_block_modal.block_account_instead": "Bloquer @{name} à la place",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Les personnes de ce serveur peuvent interagir avec vos anciennes publications.",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Les personnes de ce serveur peuvent interagir avec vos anciens messages.",
|
||||
"domain_block_modal.they_cant_follow": "Personne de ce serveur ne peut vous suivre.",
|
||||
"domain_block_modal.they_wont_know": "Il ne saura pas qu'il a été bloqué.",
|
||||
"domain_block_modal.title": "Bloquer le domaine ?",
|
||||
"domain_block_modal.you_will_lose_num_followers": "Vous allez perdre {followersCount, plural, one {{followersCountDisplay} abonné·e} other {{followersCountDisplay} abonné·e·s}} et {followingCount, plural, one {{followingCountDisplay} personne que vous suivez} other {{followingCountDisplay} personnes que vous suivez}}.",
|
||||
"domain_block_modal.you_will_lose_relationships": "Vous allez perdre tous les abonné·e·s et les personnes que vous suivez sur ce serveur.",
|
||||
"domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les publications ou les notifications des utilisateur·rice·s de ce serveur.",
|
||||
"domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les messages ou les notifications des utilisateur·rice·s de ce serveur.",
|
||||
"domain_pill.activitypub_lets_connect": "Cela vous permet de vous connecter et d'interagir avec les autres non seulement sur Mastodon, mais également sur d'autres applications de réseaux sociaux.",
|
||||
"domain_pill.activitypub_like_language": "ActivityPub est comme une langue que Mastodon utilise pour communiquer avec les autres réseaux sociaux.",
|
||||
"domain_pill.server": "Serveur",
|
||||
|
@ -297,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Essayez de les désactiver et de rafraîchir la page. Si cela ne vous aide pas, vous pouvez toujours utiliser Mastodon via un autre navigateur ou une application native.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copier la trace d'appels dans le presse-papier",
|
||||
"errors.unexpected_crash.report_issue": "Signaler un problème",
|
||||
"explore.search_results": "Résultats",
|
||||
"explore.suggested_follows": "Personnes",
|
||||
"explore.title": "Explorer",
|
||||
"explore.trending_links": "Nouvelles",
|
||||
|
@ -347,13 +358,14 @@
|
|||
"footer.about": "À propos",
|
||||
"footer.directory": "Annuaire des profils",
|
||||
"footer.get_app": "Télécharger l’application",
|
||||
"footer.invite": "Inviter des gens",
|
||||
"footer.keyboard_shortcuts": "Raccourcis clavier",
|
||||
"footer.privacy_policy": "Politique de confidentialité",
|
||||
"footer.source_code": "Voir le code source",
|
||||
"footer.status": "État",
|
||||
"footer.terms_of_service": "Conditions d’utilisation",
|
||||
"generic.saved": "Sauvegardé",
|
||||
"getting_started.heading": "Pour commencer",
|
||||
"hashtag.admin_moderation": "Ouvrir l'interface de modération pour #{name}",
|
||||
"hashtag.column_header.tag_mode.all": "et {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "ou {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "sans {additional}",
|
||||
|
@ -472,6 +484,7 @@
|
|||
"lists.exclusive_hint": "Si quelqu'un est dans cette liste, les cacher dans votre fil pour éviter de voir leurs messages deux fois.",
|
||||
"lists.find_users_to_add": "Trouver des utilisateurs à ajouter",
|
||||
"lists.list_members": "Lister les membres",
|
||||
"lists.list_members_count": "{count, plural, one {# member} other {# members}}",
|
||||
"lists.list_name": "Nom de la liste",
|
||||
"lists.new_list_name": "Nom de la nouvelle liste",
|
||||
"lists.no_lists_yet": "Aucune liste pour l'instant.",
|
||||
|
@ -482,6 +495,8 @@
|
|||
"lists.replies_policy.list": "Membres de la liste",
|
||||
"lists.replies_policy.none": "Personne",
|
||||
"lists.save": "Enregistrer",
|
||||
"lists.search": "Recherche",
|
||||
"lists.show_replies_to": "Inclure les réponses des membres de la liste à",
|
||||
"load_pending": "{count, plural, one {# nouvel élément} other {# nouveaux éléments}}",
|
||||
"loading_indicator.label": "Chargement…",
|
||||
"media_gallery.hide": "Masquer",
|
||||
|
@ -530,9 +545,12 @@
|
|||
"notification.admin.report_statuses_other": "{name} a signalé {target}",
|
||||
"notification.admin.sign_up": "{name} s'est inscrit·e",
|
||||
"notification.admin.sign_up.name_and_others": "{name} et {count, plural, one {# autre} other {# autres}} se sont inscrit",
|
||||
"notification.annual_report.message": "Votre {year} #Wrapstodon attend ! Dévoilez les moments forts et mémorables de votre année sur Mastodon !",
|
||||
"notification.annual_report.view": "Voir #Wrapstodon",
|
||||
"notification.favourite": "{name} a ajouté votre publication à ses favoris",
|
||||
"notification.favourite.name_and_others_with_link": "{name} et <a>{count, plural, one {# autre} other {# autres}}</a> ont mis votre message en favori",
|
||||
"notification.favourite_pm": "{name} a mis votre mention privée en favori",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} et <a>{count, plural, one {# autre} other {# autres}}</a> ont mis votre mention privée en favori",
|
||||
"notification.follow": "{name} vous suit",
|
||||
"notification.follow.name_and_others": "{name} et <a>{count, plural, one {# autre} other {# autres}}</a> vous suivent",
|
||||
"notification.follow_request": "{name} a demandé à vous suivre",
|
||||
|
@ -641,6 +659,7 @@
|
|||
"onboarding.follows.done": "Terminé",
|
||||
"onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer de rechercher ou de parcourir la page \"Explorer\" pour trouver des personnes à suivre, ou réessayer plus tard.",
|
||||
"onboarding.follows.search": "Recherche",
|
||||
"onboarding.follows.title": "Suivre des personnes pour commencer",
|
||||
"onboarding.profile.discoverable": "Rendre mon profil découvrable",
|
||||
"onboarding.profile.discoverable_hint": "Lorsque vous acceptez d'être découvert sur Mastodon, vos messages peuvent apparaître dans les résultats de recherche et les tendances, et votre profil peut être suggéré à des personnes ayant des intérêts similaires aux vôtres.",
|
||||
"onboarding.profile.display_name": "Nom affiché",
|
||||
|
@ -763,10 +782,11 @@
|
|||
"search_results.accounts": "Profils",
|
||||
"search_results.all": "Tout",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Aucun résultat avec ces mots-clés",
|
||||
"search_results.no_results": "Aucun résultat.",
|
||||
"search_results.no_search_yet": "Essayez de rechercher des messages, des profils ou des hashtags.",
|
||||
"search_results.see_all": "Afficher tout",
|
||||
"search_results.statuses": "Publications",
|
||||
"search_results.title": "Rechercher {q}",
|
||||
"search_results.title": "Résultat de Recherche pour \"{q}\"",
|
||||
"server_banner.about_active_users": "Personnes utilisant ce serveur au cours des 30 derniers jours (Comptes actifs mensuellement)",
|
||||
"server_banner.active_users": "comptes actifs",
|
||||
"server_banner.administered_by": "Administré par:",
|
||||
|
@ -839,6 +859,7 @@
|
|||
"subscribed_languages.target": "Changer les langues abonnées pour {target}",
|
||||
"tabs_bar.home": "Accueil",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"terms_of_service.title": "Conditions d'utilisation",
|
||||
"time_remaining.days": "{number, plural, one {# jour restant} other {# jours restants}}",
|
||||
"time_remaining.hours": "{number, plural, one {# heure restante} other {# heures restantes}}",
|
||||
"time_remaining.minutes": "{number, plural, one {# minute restante} other {# minutes restantes}}",
|
||||
|
@ -860,6 +881,7 @@
|
|||
"upload_form.drag_and_drop.on_drag_cancel": "Le glissement a été annulé. La pièce jointe {item} n'a pas été ajoutée.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "La pièce jointe du média {item} a été déplacée.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "La pièce jointe du média {item} a été déplacée.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "A récupéré la pièce jointe {item}.",
|
||||
"upload_form.edit": "Modifier",
|
||||
"upload_form.thumbnail": "Changer la vignette",
|
||||
"upload_form.video_description": "Décrire pour les personnes ayant des problèmes de vue ou d'audition",
|
||||
|
|
|
@ -85,10 +85,13 @@
|
|||
"alert.rate_limited.title": "Nombre de requêtes limité",
|
||||
"alert.unexpected.message": "Une erreur inattendue s’est produite.",
|
||||
"alert.unexpected.title": "Oups !",
|
||||
"alt_text_badge.title": "Texte Alt",
|
||||
"alt_text_badge.title": "Texte alternatif",
|
||||
"announcement.announcement": "Annonce",
|
||||
"annual_report.summary.archetype.booster": "Le chasseur de sang-froid",
|
||||
"annual_report.summary.archetype.lurker": "Le faucheur",
|
||||
"annual_report.summary.archetype.oracle": "L’oracle",
|
||||
"annual_report.summary.archetype.pollster": "Le sondeur",
|
||||
"annual_report.summary.archetype.replier": "Le papillon social",
|
||||
"annual_report.summary.followers.followers": "abonné·e·s",
|
||||
"annual_report.summary.followers.total": "{count} au total",
|
||||
"annual_report.summary.here_it_is": "Voici votre récap de {year} :",
|
||||
|
@ -99,7 +102,8 @@
|
|||
"annual_report.summary.most_used_app.most_used_app": "appli la plus utilisée",
|
||||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "hashtag le plus utilisé",
|
||||
"annual_report.summary.most_used_hashtag.none": "Aucun",
|
||||
"annual_report.summary.new_posts.new_posts": "nouveaux posts",
|
||||
"annual_report.summary.new_posts.new_posts": "nouveaux messages",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Cela vous place dans le top</topLabel><pourcentage></percentage><bottomLabel>des utilisateurs de {domain}.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Nous ne le dirons pas à Bernie.",
|
||||
"annual_report.summary.thanks": "Merci de faire partie de Mastodon!",
|
||||
"attachments_list.unprocessed": "(non traité)",
|
||||
|
@ -108,10 +112,10 @@
|
|||
"block_modal.show_less": "Afficher moins",
|
||||
"block_modal.show_more": "Afficher plus",
|
||||
"block_modal.they_cant_mention": "Il ne peut pas vous mentionner ou vous suivre.",
|
||||
"block_modal.they_cant_see_posts": "Il peut toujours voir vos publications, mais vous ne verrez pas les siennes.",
|
||||
"block_modal.they_cant_see_posts": "Il peut toujours voir vos messages, mais vous ne verrez pas les siens.",
|
||||
"block_modal.they_will_know": "Il peut voir qu'il est bloqué.",
|
||||
"block_modal.title": "Bloquer l'utilisateur·rice ?",
|
||||
"block_modal.you_wont_see_mentions": "Vous ne verrez pas les publications qui le mentionne.",
|
||||
"block_modal.title": "Bloquer le compte ?",
|
||||
"block_modal.you_wont_see_mentions": "Vous ne verrez pas les messages qui le mentionne.",
|
||||
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci la prochaine fois",
|
||||
"boost_modal.reblog": "Booster le message ?",
|
||||
"boost_modal.undo_reblog": "Annuler le boost du message ?",
|
||||
|
@ -125,6 +129,7 @@
|
|||
"bundle_column_error.routing.body": "La page demandée est introuvable. Êtes-vous sûr que l’URL dans la barre d’adresse est correcte ?",
|
||||
"bundle_column_error.routing.title": "404",
|
||||
"bundle_modal_error.close": "Fermer",
|
||||
"bundle_modal_error.message": "Un problème s'est produit lors du chargement de cet écran.",
|
||||
"bundle_modal_error.retry": "Réessayer",
|
||||
"closed_registrations.other_server_instructions": "Puisque Mastodon est décentralisé, vous pouvez créer un compte sur un autre serveur et interagir quand même avec celui-ci.",
|
||||
"closed_registrations_modal.description": "Créer un compte sur {domain} est actuellement impossible, néanmoins souvenez-vous que vous n'avez pas besoin d'un compte spécifiquement sur {domain} pour utiliser Mastodon.",
|
||||
|
@ -181,7 +186,7 @@
|
|||
"compose_form.poll.switch_to_single": "Modifier le sondage pour autoriser qu'un seul choix",
|
||||
"compose_form.poll.type": "Style",
|
||||
"compose_form.publish": "Publier",
|
||||
"compose_form.publish_form": "Nouvelle publication",
|
||||
"compose_form.publish_form": "Nouveau message",
|
||||
"compose_form.reply": "Répondre",
|
||||
"compose_form.save_changes": "Mettre à jour",
|
||||
"compose_form.spoiler.marked": "Enlever l’avertissement de contenu",
|
||||
|
@ -191,7 +196,7 @@
|
|||
"confirmations.block.confirm": "Bloquer",
|
||||
"confirmations.delete.confirm": "Supprimer",
|
||||
"confirmations.delete.message": "Voulez-vous vraiment supprimer ce message ?",
|
||||
"confirmations.delete.title": "Supprimer la publication ?",
|
||||
"confirmations.delete.title": "Supprimer le message ?",
|
||||
"confirmations.delete_list.confirm": "Supprimer",
|
||||
"confirmations.delete_list.message": "Voulez-vous vraiment supprimer définitivement cette liste ?",
|
||||
"confirmations.delete_list.title": "Supprimer la liste ?",
|
||||
|
@ -200,12 +205,15 @@
|
|||
"confirmations.edit.confirm": "Modifier",
|
||||
"confirmations.edit.message": "Modifier maintenant écrasera votre message en cours de rédaction. Voulez-vous vraiment continuer ?",
|
||||
"confirmations.edit.title": "Remplacer le message ?",
|
||||
"confirmations.follow_to_list.confirm": "Suivre et ajouter à la liste",
|
||||
"confirmations.follow_to_list.message": "Vous devez suivre {name} pour l'ajouter à une liste.",
|
||||
"confirmations.follow_to_list.title": "Suivre l'utilisateur ?",
|
||||
"confirmations.logout.confirm": "Se déconnecter",
|
||||
"confirmations.logout.message": "Voulez-vous vraiment vous déconnecter ?",
|
||||
"confirmations.logout.title": "Se déconnecter ?",
|
||||
"confirmations.mute.confirm": "Masquer",
|
||||
"confirmations.redraft.confirm": "Supprimer et ré-écrire",
|
||||
"confirmations.redraft.message": "Êtes-vous sûr·e de vouloir effacer cette publication pour la réécrire ? Ses partages ainsi que ses mises en favori seront perdus et ses réponses seront orphelines.",
|
||||
"confirmations.redraft.message": "Voulez-vous vraiment supprimer le message pour le réécrire ? Ses partages ainsi que ses mises en favori seront perdues, et ses réponses seront orphelines.",
|
||||
"confirmations.redraft.title": "Supprimer et réécrire le message ?",
|
||||
"confirmations.reply.confirm": "Répondre",
|
||||
"confirmations.reply.message": "Répondre maintenant écrasera votre message en cours de rédaction. Voulez-vous vraiment continuer ?",
|
||||
|
@ -214,8 +222,8 @@
|
|||
"confirmations.unfollow.message": "Voulez-vous vraiment vous désabonner de {name} ?",
|
||||
"confirmations.unfollow.title": "Se désabonner de l'utilisateur·rice ?",
|
||||
"content_warning.hide": "Masquer le message",
|
||||
"content_warning.show": "Afficher quand même",
|
||||
"content_warning.show_more": "Déplier",
|
||||
"content_warning.show": "Montrer quand même",
|
||||
"content_warning.show_more": "Montrer plus",
|
||||
"conversation.delete": "Supprimer la conversation",
|
||||
"conversation.mark_as_read": "Marquer comme lu",
|
||||
"conversation.open": "Afficher la conversation",
|
||||
|
@ -231,15 +239,19 @@
|
|||
"disabled_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé.",
|
||||
"dismissable_banner.community_timeline": "Voici les messages publics les plus récents des comptes hébergés par {domain}.",
|
||||
"dismissable_banner.dismiss": "Rejeter",
|
||||
"dismissable_banner.explore_links": "Ces nouvelles sont les plus partagées sur le fediverse aujourd'hui. Les nouvelles plus récentes postées par un plus grand nombre de personnes sont mieux classées.",
|
||||
"dismissable_banner.explore_statuses": "Ces messages provenant de l'ensemble du fediverse gagnent en popularité aujourd'hui. Les messages les plus récents qui ont reçu le plus d'encouragements et de favoris sont mieux classés.",
|
||||
"dismissable_banner.explore_tags": "Ces hashtags gagnent du terrain sur le fediverse aujourd'hui. Les hashtags qui sont utilisés par un plus grand nombre de personnes différentes sont mieux classés.",
|
||||
"dismissable_banner.public_timeline": "Il s'agit des messages publics les plus récents publiés par des personnes sur le fediverse que les personnes sur {domain} suivent.",
|
||||
"domain_block_modal.block": "Bloquer le serveur",
|
||||
"domain_block_modal.block_account_instead": "Bloquer @{name} à la place",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Les personnes de ce serveur peuvent interagir avec vos anciennes publications.",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "Les personnes de ce serveur peuvent interagir avec vos anciens messages.",
|
||||
"domain_block_modal.they_cant_follow": "Personne de ce serveur ne peut vous suivre.",
|
||||
"domain_block_modal.they_wont_know": "Il ne saura pas qu'il a été bloqué.",
|
||||
"domain_block_modal.title": "Bloquer le domaine ?",
|
||||
"domain_block_modal.you_will_lose_num_followers": "Vous allez perdre {followersCount, plural, one {{followersCountDisplay} abonné·e} other {{followersCountDisplay} abonné·e·s}} et {followingCount, plural, one {{followingCountDisplay} personne que vous suivez} other {{followingCountDisplay} personnes que vous suivez}}.",
|
||||
"domain_block_modal.you_will_lose_relationships": "Vous allez perdre tous les abonné·e·s et les personnes que vous suivez sur ce serveur.",
|
||||
"domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les publications ou les notifications des utilisateur·rice·s de ce serveur.",
|
||||
"domain_block_modal.you_wont_see_posts": "Vous ne verrez plus les messages ou les notifications des utilisateur·rice·s de ce serveur.",
|
||||
"domain_pill.activitypub_lets_connect": "Cela vous permet de vous connecter et d'interagir avec les autres non seulement sur Mastodon, mais également sur d'autres applications de réseaux sociaux.",
|
||||
"domain_pill.activitypub_like_language": "ActivityPub est comme une langue que Mastodon utilise pour communiquer avec les autres réseaux sociaux.",
|
||||
"domain_pill.server": "Serveur",
|
||||
|
@ -297,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Essayez de les désactiver et de rafraîchir la page. Si cela ne vous aide pas, vous pouvez toujours utiliser Mastodon via un autre navigateur ou une application native.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copier la trace d'appels dans le presse-papier",
|
||||
"errors.unexpected_crash.report_issue": "Signaler le problème",
|
||||
"explore.search_results": "Résultats de la recherche",
|
||||
"explore.suggested_follows": "Personnes",
|
||||
"explore.title": "Explorer",
|
||||
"explore.trending_links": "Nouvelles",
|
||||
|
@ -347,13 +358,14 @@
|
|||
"footer.about": "À propos",
|
||||
"footer.directory": "Annuaire des profils",
|
||||
"footer.get_app": "Télécharger l’application",
|
||||
"footer.invite": "Inviter des personnes",
|
||||
"footer.keyboard_shortcuts": "Raccourcis clavier",
|
||||
"footer.privacy_policy": "Politique de confidentialité",
|
||||
"footer.source_code": "Voir le code source",
|
||||
"footer.status": "État",
|
||||
"footer.terms_of_service": "Conditions d’utilisation",
|
||||
"generic.saved": "Sauvegardé",
|
||||
"getting_started.heading": "Pour commencer",
|
||||
"hashtag.admin_moderation": "Ouvrir l'interface de modération pour #{name}",
|
||||
"hashtag.column_header.tag_mode.all": "et {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "ou {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "sans {additional}",
|
||||
|
@ -472,6 +484,7 @@
|
|||
"lists.exclusive_hint": "Si quelqu'un est dans cette liste, les cacher dans votre fil pour éviter de voir leurs messages deux fois.",
|
||||
"lists.find_users_to_add": "Trouver des utilisateurs à ajouter",
|
||||
"lists.list_members": "Lister les membres",
|
||||
"lists.list_members_count": "{count, plural, one {# member} other {# members}}",
|
||||
"lists.list_name": "Nom de la liste",
|
||||
"lists.new_list_name": "Nom de la nouvelle liste",
|
||||
"lists.no_lists_yet": "Aucune liste pour l'instant.",
|
||||
|
@ -482,6 +495,8 @@
|
|||
"lists.replies_policy.list": "Membres de la liste",
|
||||
"lists.replies_policy.none": "Personne",
|
||||
"lists.save": "Enregistrer",
|
||||
"lists.search": "Recherche",
|
||||
"lists.show_replies_to": "Inclure les réponses des membres de la liste à",
|
||||
"load_pending": "{count, plural, one {# nouvel élément} other {# nouveaux éléments}}",
|
||||
"loading_indicator.label": "Chargement…",
|
||||
"media_gallery.hide": "Masquer",
|
||||
|
@ -530,9 +545,12 @@
|
|||
"notification.admin.report_statuses_other": "{name} a signalé {target}",
|
||||
"notification.admin.sign_up": "{name} s'est inscrit",
|
||||
"notification.admin.sign_up.name_and_others": "{name} et {count, plural, one {# autre} other {# autres}} se sont inscrit",
|
||||
"notification.annual_report.message": "Votre {year} #Wrapstodon attend ! Dévoilez les moments forts et mémorables de votre année sur Mastodon !",
|
||||
"notification.annual_report.view": "Voir #Wrapstodon",
|
||||
"notification.favourite": "{name} a ajouté votre message à ses favoris",
|
||||
"notification.favourite.name_and_others_with_link": "{name} et <a>{count, plural, one {# autre} other {# autres}}</a> ont mis votre message en favori",
|
||||
"notification.favourite_pm": "{name} a mis votre mention privée en favori",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} et <a>{count, plural, one {# autre} other {# autres}}</a> ont mis votre mention privée en favori",
|
||||
"notification.follow": "{name} vous suit",
|
||||
"notification.follow.name_and_others": "{name} et <a>{count, plural, one {# autre} other {# autres}}</a> vous suivent",
|
||||
"notification.follow_request": "{name} a demandé à vous suivre",
|
||||
|
@ -641,6 +659,7 @@
|
|||
"onboarding.follows.done": "Terminé",
|
||||
"onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer d'utiliser la recherche ou parcourir la page de découverte pour trouver des personnes à suivre, ou réessayez plus tard.",
|
||||
"onboarding.follows.search": "Recherche",
|
||||
"onboarding.follows.title": "Suivre des personnes pour commencer",
|
||||
"onboarding.profile.discoverable": "Rendre mon profil découvrable",
|
||||
"onboarding.profile.discoverable_hint": "Lorsque vous acceptez d'être découvert sur Mastodon, vos messages peuvent apparaître dans les résultats de recherche et les tendances, et votre profil peut être suggéré à des personnes ayant des intérêts similaires aux vôtres.",
|
||||
"onboarding.profile.display_name": "Nom affiché",
|
||||
|
@ -733,7 +752,7 @@
|
|||
"report.thanks.title": "Vous ne voulez pas voir cela ?",
|
||||
"report.thanks.title_actionable": "Merci pour votre signalement, nous allons investiguer.",
|
||||
"report.unfollow": "Ne plus suivre @{name}",
|
||||
"report.unfollow_explanation": "Vous êtes abonné à ce compte. Pour ne plus voir ses publications dans votre fil principal, retirez-le de votre liste d'abonnements.",
|
||||
"report.unfollow_explanation": "Vous êtes abonné à ce compte. Pour ne plus voir ses messages dans votre fil principal, retirez-le de votre liste d'abonnements.",
|
||||
"report_notification.attached_statuses": "{count, plural, one {{count} message lié} other {{count} messages liés}}",
|
||||
"report_notification.categories.legal": "Légal",
|
||||
"report_notification.categories.legal_sentence": "contenu illégal",
|
||||
|
@ -750,7 +769,7 @@
|
|||
"search.quick_action.go_to_account": "Aller au profil {x}",
|
||||
"search.quick_action.go_to_hashtag": "Aller au hashtag {x}",
|
||||
"search.quick_action.open_url": "Ouvrir l'URL dans Mastodon",
|
||||
"search.quick_action.status_search": "Publications correspondant à {x}",
|
||||
"search.quick_action.status_search": "Messages correspondant à {x}",
|
||||
"search.search_or_paste": "Rechercher ou saisir une URL",
|
||||
"search_popout.full_text_search_disabled_message": "Non disponible sur {domain}.",
|
||||
"search_popout.full_text_search_logged_out_message": "Disponible uniquement lorsque vous êtes connecté.",
|
||||
|
@ -763,10 +782,11 @@
|
|||
"search_results.accounts": "Profils",
|
||||
"search_results.all": "Tous les résultats",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Aucun résultat avec ces mots-clefs",
|
||||
"search_results.no_results": "Aucun résultat.",
|
||||
"search_results.no_search_yet": "Essayez de rechercher des messages, des profils ou des hashtags.",
|
||||
"search_results.see_all": "Afficher tout",
|
||||
"search_results.statuses": "Messages",
|
||||
"search_results.title": "Rechercher {q}",
|
||||
"search_results.title": "Résultat de Recherche pour \"{q}\"",
|
||||
"server_banner.about_active_users": "Personnes utilisant ce serveur au cours des 30 derniers jours (Comptes actifs mensuellement)",
|
||||
"server_banner.active_users": "comptes actifs",
|
||||
"server_banner.administered_by": "Administré par :",
|
||||
|
@ -839,6 +859,7 @@
|
|||
"subscribed_languages.target": "Changer les langues abonnées pour {target}",
|
||||
"tabs_bar.home": "Accueil",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"terms_of_service.title": "Conditions d'utilisation",
|
||||
"time_remaining.days": "{number, plural, one {# jour restant} other {# jours restants}}",
|
||||
"time_remaining.hours": "{number, plural, one {# heure restante} other {# heures restantes}}",
|
||||
"time_remaining.minutes": "{number, plural, one {# minute restante} other {# minutes restantes}}",
|
||||
|
@ -860,6 +881,7 @@
|
|||
"upload_form.drag_and_drop.on_drag_cancel": "Le glissement a été annulé. La pièce jointe {item} n'a pas été ajoutée.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "La pièce jointe du média {item} a été déplacée.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "La pièce jointe du média {item} a été déplacée.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "A récupéré la pièce jointe {item}.",
|
||||
"upload_form.edit": "Modifier",
|
||||
"upload_form.thumbnail": "Changer la vignette",
|
||||
"upload_form.video_description": "Décrire pour les personnes ayant des problèmes de vue ou d'audition",
|
||||
|
|
|
@ -87,6 +87,13 @@
|
|||
"alert.unexpected.title": "Oepsy!",
|
||||
"alt_text_badge.title": "Alternative tekst",
|
||||
"announcement.announcement": "Oankundiging",
|
||||
"annual_report.summary.archetype.booster": "De cool-hunter",
|
||||
"annual_report.summary.archetype.oracle": "It orakel",
|
||||
"annual_report.summary.archetype.pollster": "De opinypeiler",
|
||||
"annual_report.summary.archetype.replier": "De sosjale flinter",
|
||||
"annual_report.summary.followers.followers": "folgers",
|
||||
"annual_report.summary.followers.total": "totaal {count}",
|
||||
"annual_report.summary.here_it_is": "Jo jieroersjoch foar {year}:",
|
||||
"attachments_list.unprocessed": "(net ferwurke)",
|
||||
"audio.hide": "Audio ferstopje",
|
||||
"block_modal.remote_users_caveat": "Wy freegje de server {domain} om jo beslút te respektearjen. It neilibben hjirfan is echter net garandearre, omdat guon servers blokkaden oars ynterpretearje kinne. Iepenbiere berjochten binne mooglik noch hieltyd sichtber foar net-oanmelde brûkers.",
|
||||
|
@ -278,7 +285,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Probearje dizze út te skeakeljen en de side te fernijen. Wannear’t dit net helpt is it noch hieltyd mooglik om Mastodon yn in oare browser of mobile app te brûken.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Stacktrace nei klamboerd kopiearje",
|
||||
"errors.unexpected_crash.report_issue": "Technysk probleem melde",
|
||||
"explore.search_results": "Sykresultaten",
|
||||
"explore.suggested_follows": "Minsken",
|
||||
"explore.title": "Ferkenne",
|
||||
"explore.trending_links": "Nijs",
|
||||
|
@ -328,7 +334,6 @@
|
|||
"footer.about": "Oer",
|
||||
"footer.directory": "Profylmap",
|
||||
"footer.get_app": "App downloade",
|
||||
"footer.invite": "Minsken útnûgje",
|
||||
"footer.keyboard_shortcuts": "Fluchtoetsen",
|
||||
"footer.privacy_policy": "Privacybelied",
|
||||
"footer.source_code": "Boarnekoade besjen",
|
||||
|
@ -722,10 +727,8 @@
|
|||
"search_results.accounts": "Profilen",
|
||||
"search_results.all": "Alles",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Dizze syktermen leverje gjin resultaat op",
|
||||
"search_results.see_all": "Alles besjen",
|
||||
"search_results.statuses": "Berjochten",
|
||||
"search_results.title": "Nei {q} sykje",
|
||||
"server_banner.about_active_users": "Oantal brûkers yn de ôfrûne 30 dagen (MAU)",
|
||||
"server_banner.active_users": "warbere brûkers",
|
||||
"server_banner.administered_by": "Beheard troch:",
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "hashtag is mó a úsáidtear",
|
||||
"annual_report.summary.most_used_hashtag.none": "Dada",
|
||||
"annual_report.summary.new_posts.new_posts": "postanna nua",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Cuireann sé sin i mbarr</topLabel><percentage></percentage><bottomLabel> úsáideoirí {domain}.</bottomLabel> thú",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Ní inseoidh muid do Bernie.",
|
||||
"annual_report.summary.thanks": "Go raibh maith agat as a bheith mar chuid de Mastodon!",
|
||||
"attachments_list.unprocessed": "(neamhphróiseáilte)",
|
||||
|
@ -204,6 +205,9 @@
|
|||
"confirmations.edit.confirm": "Eagar",
|
||||
"confirmations.edit.message": "Má dhéanann tú eagarthóireacht anois, déanfar an teachtaireacht atá á cumadh agat faoi láthair a fhorscríobh. An bhfuil tú cinnte gur mhaith leat leanúint ar aghaidh?",
|
||||
"confirmations.edit.title": "Forscríobh postáil?",
|
||||
"confirmations.follow_to_list.confirm": "Lean agus cuir leis an liosta",
|
||||
"confirmations.follow_to_list.message": "Ní mór duit {name} a leanúint chun iad a chur le liosta.",
|
||||
"confirmations.follow_to_list.title": "Lean an t-úsáideoir?",
|
||||
"confirmations.logout.confirm": "Logáil amach",
|
||||
"confirmations.logout.message": "An bhfuil tú cinnte gur mhaith leat logáil amach?",
|
||||
"confirmations.logout.title": "Logáil Amach?",
|
||||
|
@ -305,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Déan iarracht iad a dhíchumasú agus an leathanach a athnuachan. Mura gcabhraíonn sé sin, seans go mbeidh tú fós in ann Mastodon a úsáid trí bhrabhsálaí nó aip dhúchais eile.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Cóipeáil rian cruachta go dtí an ghearrthaisce",
|
||||
"errors.unexpected_crash.report_issue": "Tuairiscigh deacracht",
|
||||
"explore.search_results": "Torthaí cuardaigh",
|
||||
"explore.suggested_follows": "Daoine",
|
||||
"explore.title": "Féach thart",
|
||||
"explore.trending_links": "Nuacht",
|
||||
|
@ -355,7 +358,6 @@
|
|||
"footer.about": "Maidir le",
|
||||
"footer.directory": "Eolaire próifílí",
|
||||
"footer.get_app": "Faigh an aip",
|
||||
"footer.invite": "Tabhair cuireadh do dhaoine",
|
||||
"footer.keyboard_shortcuts": "Aicearraí méarchláir",
|
||||
"footer.privacy_policy": "Polasaí príobháideachais",
|
||||
"footer.source_code": "Féach ar an gcód foinseach",
|
||||
|
@ -492,6 +494,7 @@
|
|||
"lists.replies_policy.list": "Baill an liosta",
|
||||
"lists.replies_policy.none": "Duine ar bith",
|
||||
"lists.save": "Sábháil",
|
||||
"lists.search": "Cuardach",
|
||||
"lists.show_replies_to": "Cuir san áireamh freagraí ó bhaill an liosta go",
|
||||
"load_pending": "{count, plural, one {# mír nua} two {# mír nua} few {# mír nua} many {# mír nua} other {# mír nua}}",
|
||||
"loading_indicator.label": "Á lódáil…",
|
||||
|
@ -776,10 +779,8 @@
|
|||
"search_results.accounts": "Próifílí",
|
||||
"search_results.all": "Gach",
|
||||
"search_results.hashtags": "Haischlib",
|
||||
"search_results.nothing_found": "Níorbh fhéidir aon rud a aimsiú do na téarmaí cuardaigh seo",
|
||||
"search_results.see_all": "Gach rud a fheicáil",
|
||||
"search_results.statuses": "Postálacha",
|
||||
"search_results.title": "Cuardaigh ar thóir {q}",
|
||||
"server_banner.about_active_users": "Daoine a úsáideann an freastalaí seo le 30 lá anuas (Úsáideoirí Gníomhacha Míosúla)",
|
||||
"server_banner.active_users": "úsáideoirí gníomhacha",
|
||||
"server_banner.administered_by": "Arna riar ag:",
|
||||
|
|
|
@ -296,7 +296,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Feuch an cuir thu à comas iad ’s gun ath-nuadhaich thu an duilleag seo. Mura cuidich sin, dh’fhaoidte gur urrainn dhut Mastodon a chleachdadh fhathast le brabhsair eile no le aplacaid thùsail.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Cuir lethbhreac dhen stacktrace air an stòr-bhòrd",
|
||||
"errors.unexpected_crash.report_issue": "Dèan aithris air an duilgheadas",
|
||||
"explore.search_results": "Toraidhean an luirg",
|
||||
"explore.suggested_follows": "Daoine",
|
||||
"explore.title": "Rùraich",
|
||||
"explore.trending_links": "Naidheachdan",
|
||||
|
@ -346,7 +345,6 @@
|
|||
"footer.about": "Mu dhèidhinn",
|
||||
"footer.directory": "Eòlaire nam pròifil",
|
||||
"footer.get_app": "Faigh an aplacaid",
|
||||
"footer.invite": "Thoir cuireadh",
|
||||
"footer.keyboard_shortcuts": "Ath-ghoiridean a’ mheur-chlàir",
|
||||
"footer.privacy_policy": "Poileasaidh prìobhaideachd",
|
||||
"footer.source_code": "Seall am bun-tùs",
|
||||
|
@ -742,10 +740,8 @@
|
|||
"search_results.accounts": "Pròifilean",
|
||||
"search_results.all": "Na h-uile",
|
||||
"search_results.hashtags": "Tagaichean hais",
|
||||
"search_results.nothing_found": "Cha do lorg sinn dad dha na h-abairtean-luirg seo",
|
||||
"search_results.see_all": "Seall na h-uile",
|
||||
"search_results.statuses": "Postaichean",
|
||||
"search_results.title": "Lorg {q}",
|
||||
"server_banner.about_active_users": "Daoine a chleachd am frithealaiche seo rè an 30 latha mu dheireadh (Cleachdaichean gnìomhach gach mìos)",
|
||||
"server_banner.active_users": "cleachdaichean gnìomhach",
|
||||
"server_banner.administered_by": "Rianachd le:",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Intenta desactivalas e actualiza a páxina. Se isto non funciona, podes seguir usando Mastodon nun navegador diferente ou aplicación nativa.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copiar trazas (stacktrace) ó portapapeis",
|
||||
"errors.unexpected_crash.report_issue": "Informar sobre un problema",
|
||||
"explore.search_results": "Resultados da busca",
|
||||
"explore.suggested_follows": "Persoas",
|
||||
"explore.title": "Descubrir",
|
||||
"explore.trending_links": "Novas",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Sobre",
|
||||
"footer.directory": "Directorio de perfís",
|
||||
"footer.get_app": "Descarga a app",
|
||||
"footer.invite": "Convidar persoas",
|
||||
"footer.keyboard_shortcuts": "Atallos do teclado",
|
||||
"footer.privacy_policy": "Política de privacidade",
|
||||
"footer.source_code": "Ver código fonte",
|
||||
"footer.status": "Estado",
|
||||
"footer.terms_of_service": "Termos do servizo",
|
||||
"generic.saved": "Gardado",
|
||||
"getting_started.heading": "Primeiros pasos",
|
||||
"hashtag.admin_moderation": "Abrir interface de moderación para ##{name}",
|
||||
|
@ -413,7 +412,7 @@
|
|||
"interaction_modal.description.reblog": "Cunha conta en Mastodon, poderás promover esta publicación para compartila con quen te siga.",
|
||||
"interaction_modal.description.reply": "Cunha conta en Mastodon, poderás responder a esta publicación.",
|
||||
"interaction_modal.description.vote": "Podes votar nesta enquisa se tes unha conta en Mastodon.",
|
||||
"interaction_modal.login.action": "Lévame ao inicio",
|
||||
"interaction_modal.login.action": "Seguir desde alá",
|
||||
"interaction_modal.login.prompt": "Dominio do teu servidor de inicio, ex. mastodon.social",
|
||||
"interaction_modal.no_account_yet": "Aínda non tes unha conta?",
|
||||
"interaction_modal.on_another_server": "Nun servidor diferente",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "Ver #Wrapstodon",
|
||||
"notification.favourite": "{name} marcou como favorita a túa publicación",
|
||||
"notification.favourite.name_and_others_with_link": "{name} e <a>{count, plural, one {# máis} other {# máis}}</a> favoreceron a túa publicación",
|
||||
"notification.favourite_pm": "{name} favoreceu a túa mención privada",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} e <a>{count, plural, one {outra persoa} other {outras # persoas}}</a> favoreceron a túa mención privada",
|
||||
"notification.follow": "{name} comezou a seguirte",
|
||||
"notification.follow.name_and_others": "{name} e <a>{count, plural, one {# mais} other {# mais}}</a> seguíronte",
|
||||
"notification.follow_request": "{name} solicitou seguirte",
|
||||
|
@ -627,7 +628,7 @@
|
|||
"notifications.filter.follows": "Seguimentos",
|
||||
"notifications.filter.mentions": "Mencións",
|
||||
"notifications.filter.polls": "Resultados da enquisa",
|
||||
"notifications.filter.statuses": "Actualizacións de xente á que segues",
|
||||
"notifications.filter.statuses": "Actualizacións de persoas que segues",
|
||||
"notifications.grant_permission": "Conceder permiso.",
|
||||
"notifications.group": "{count} notificacións",
|
||||
"notifications.mark_as_read": "Marcar todas as notificacións como lidas",
|
||||
|
@ -684,7 +685,7 @@
|
|||
"poll_button.remove_poll": "Eliminar enquisa",
|
||||
"privacy.change": "Axustar privacidade",
|
||||
"privacy.direct.long": "Todas as mencionadas na publicación",
|
||||
"privacy.direct.short": "Persoas concretas",
|
||||
"privacy.direct.short": "Persoas mencionadas",
|
||||
"privacy.private.long": "Só para seguidoras",
|
||||
"privacy.private.short": "Seguidoras",
|
||||
"privacy.public.long": "Para todas dentro e fóra de Mastodon",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Perfís",
|
||||
"search_results.all": "Todo",
|
||||
"search_results.hashtags": "Cancelos",
|
||||
"search_results.nothing_found": "Non atopamos nada con estes termos de busca",
|
||||
"search_results.no_results": "Sen resultados.",
|
||||
"search_results.no_search_yet": "Intenta buscando publicacións, perfís ou cancelos.",
|
||||
"search_results.see_all": "Ver todo",
|
||||
"search_results.statuses": "Publicacións",
|
||||
"search_results.title": "Resultados para {q}",
|
||||
"search_results.title": "Resultados para «{q}»",
|
||||
"server_banner.about_active_users": "Persoas que usaron este servidor nos últimos 30 días (Usuarias Activas Mensuais)",
|
||||
"server_banner.active_users": "usuarias activas",
|
||||
"server_banner.administered_by": "Administrada por:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Cambiar a subscrición a idiomas para {target}",
|
||||
"tabs_bar.home": "Inicio",
|
||||
"tabs_bar.notifications": "Notificacións",
|
||||
"terms_of_service.title": "Termos do Servizo",
|
||||
"time_remaining.days": "Remata en {number, plural, one {# día} other {# días}}",
|
||||
"time_remaining.hours": "Remata en {number, plural, one {# hora} other {# horas}}",
|
||||
"time_remaining.minutes": "Remata en {number, plural, one {# minuto} other {# minutos}}",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "נסה/י להסיר אותם ולרענן את הדף. אם זה לא עוזר, אולי אפשר עדיין להשתמש במסטודון דרך דפדפן אחר או באמצעות אפליקציה ילידית.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "להעתיק את הקוד ללוח הכתיבה",
|
||||
"errors.unexpected_crash.report_issue": "דווח על בעיה",
|
||||
"explore.search_results": "תוצאות חיפוש",
|
||||
"explore.suggested_follows": "אנשים",
|
||||
"explore.title": "סיור",
|
||||
"explore.trending_links": "חדשות",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "אודות",
|
||||
"footer.directory": "ספריית פרופילים",
|
||||
"footer.get_app": "להתקנת היישומון",
|
||||
"footer.invite": "להזמין אנשים",
|
||||
"footer.keyboard_shortcuts": "קיצורי מקלדת",
|
||||
"footer.privacy_policy": "מדיניות פרטיות",
|
||||
"footer.source_code": "צפיה בקוד המקור",
|
||||
"footer.status": "מצב",
|
||||
"footer.terms_of_service": "תנאי השירות",
|
||||
"generic.saved": "נשמר",
|
||||
"getting_started.heading": "בואו נתחיל",
|
||||
"hashtag.admin_moderation": "פתיחת ממשק פיקוח דיון עבור #{name}",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "לצפייה ב- #סיכומודון",
|
||||
"notification.favourite": "הודעתך חובבה על ידי {name}",
|
||||
"notification.favourite.name_and_others_with_link": "{name} ועוד <a>{count, plural,one {אחד נוסף}other {# נוספים}}</a> חיבבו את הודעתך",
|
||||
"notification.favourite_pm": "{name} חיבב.ה איזכור שלך בהודעה פרטית",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} ועוד <a>{count, plural,one {אחד נוסף}other {# נוספים}}</a> חיבבו הודעתך הפרטית",
|
||||
"notification.follow": "{name} במעקב אחרייך",
|
||||
"notification.follow.name_and_others": "{name} ועוד <a>{count, plural,one {מישהו} other {# אחרים}}</a> החלו לעקוב אחריך",
|
||||
"notification.follow_request": "{name} ביקשו לעקוב אחריך",
|
||||
|
@ -781,10 +782,8 @@
|
|||
"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": "משתמשים פעילים בשרת ב־30 הימים האחרונים (משתמשים פעילים חודשיים)",
|
||||
"server_banner.active_users": "משתמשים פעילים",
|
||||
"server_banner.administered_by": "מנוהל ע\"י:",
|
||||
|
@ -857,6 +856,7 @@
|
|||
"subscribed_languages.target": "שינוי רישום שפה עבור {target}",
|
||||
"tabs_bar.home": "פיד הבית",
|
||||
"tabs_bar.notifications": "התראות",
|
||||
"terms_of_service.title": "תנאי השירות",
|
||||
"time_remaining.days": "נותרו {number, plural, one {# יום} other {# ימים}}",
|
||||
"time_remaining.hours": "נותרו {number, plural, one {# שעה} other {# שעות}}",
|
||||
"time_remaining.minutes": "נותרו {number, plural, one {# דקה} other {# דקות}}",
|
||||
|
|
|
@ -242,7 +242,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "उन्हें अक्षम करने और पृष्ठ को ताज़ा करने का प्रयास करें। यदि वह मदद नहीं करता है, तो आप अभी भी मास्टोडन का उपयोग किसी भिन्न ब्राउज़र या नेटिव ऐप के माध्यम से कर सकते हैं।",
|
||||
"errors.unexpected_crash.copy_stacktrace": "स्टैकट्रेस को क्लिपबोर्ड पर कॉपी करें",
|
||||
"errors.unexpected_crash.report_issue": "समस्या सूचित करें",
|
||||
"explore.search_results": "सर्च रिजल्ट्स",
|
||||
"explore.suggested_follows": "लोग",
|
||||
"explore.title": "एक्स्प्लोर",
|
||||
"explore.trending_links": "समाचार",
|
||||
|
@ -275,7 +274,6 @@
|
|||
"footer.about": "अबाउट",
|
||||
"footer.directory": "प्रोफाइल्स डायरेक्टरी",
|
||||
"footer.get_app": "अप्प प्राप्त करें",
|
||||
"footer.invite": "लोगों को आमंत्रित करें",
|
||||
"footer.keyboard_shortcuts": "कीबोर्ड शॉर्टकट",
|
||||
"footer.privacy_policy": "प्राइवेसी पालिसी",
|
||||
"footer.source_code": "सोर्स कोड देखें",
|
||||
|
|
|
@ -205,7 +205,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Pokušaj ih onemogućiti i osvježiti stranicu. Ako to ne pomogne, i dalje ćeš biti u mogućnosti koristiti Mastodon preko nekog drugog preglednika ili izvornog app-a.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopiraj stacktrace u međuspremnik",
|
||||
"errors.unexpected_crash.report_issue": "Prijavi problem",
|
||||
"explore.search_results": "Rezultati pretrage",
|
||||
"explore.suggested_follows": "Ljudi",
|
||||
"explore.title": "Pretraži",
|
||||
"explore.trending_links": "Novosti",
|
||||
|
@ -227,7 +226,6 @@
|
|||
"footer.about": "O aplikaciji",
|
||||
"footer.directory": "Direktorij profila",
|
||||
"footer.get_app": "Preuzmi aplikaciju",
|
||||
"footer.invite": "Pozovi ljude",
|
||||
"footer.keyboard_shortcuts": "Tipkovni prečaci",
|
||||
"footer.privacy_policy": "Pravila o zaštiti privatnosti",
|
||||
"footer.source_code": "Prikaz izvornog koda",
|
||||
|
@ -413,10 +411,8 @@
|
|||
"search_popout.user": "korisnik",
|
||||
"search_results.accounts": "Profili",
|
||||
"search_results.all": "Sve",
|
||||
"search_results.nothing_found": "Nije pronađeno ništa za te ključne riječi",
|
||||
"search_results.see_all": "Prikaži sve",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.title": "Traži {q}",
|
||||
"server_banner.about_active_users": "Popis aktivnih korisnika prošli mjesec",
|
||||
"server_banner.active_users": "aktivni korisnici",
|
||||
"server_banner.administered_by": "Administrator je:",
|
||||
|
|
|
@ -309,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Próbáld letiltani őket és frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Veremkiíratás vágólapra másolása",
|
||||
"errors.unexpected_crash.report_issue": "Probléma jelentése",
|
||||
"explore.search_results": "Keresési találatok",
|
||||
"explore.suggested_follows": "Emberek",
|
||||
"explore.title": "Felfedezés",
|
||||
"explore.trending_links": "Hírek",
|
||||
|
@ -359,11 +358,11 @@
|
|||
"footer.about": "Névjegy",
|
||||
"footer.directory": "Profiltár",
|
||||
"footer.get_app": "Alkalmazás beszerzése",
|
||||
"footer.invite": "Emberek meghívása",
|
||||
"footer.keyboard_shortcuts": "Gyorsbillentyűk",
|
||||
"footer.privacy_policy": "Adatvédelmi szabályzat",
|
||||
"footer.source_code": "Forráskód megtekintése",
|
||||
"footer.status": "Állapot",
|
||||
"footer.terms_of_service": "Felhasználási feltételek",
|
||||
"generic.saved": "Elmentve",
|
||||
"getting_started.heading": "Első lépések",
|
||||
"hashtag.admin_moderation": "Moderációs felület megnyitása a következőhöz: #{name}",
|
||||
|
@ -550,6 +549,8 @@
|
|||
"notification.annual_report.view": "#Wrapstodon Megtekintése",
|
||||
"notification.favourite": "{name} kedvencnek jelölte a bejegyzésedet",
|
||||
"notification.favourite.name_and_others_with_link": "{name} és <a>{count, plural, one {# másik} other {# másik}}</a> kedvencnek jelölte a bejegyzésedet",
|
||||
"notification.favourite_pm": "{name} kedvelte a privát említésedet",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} és <a>{count, plural, one {# másik} other {# másik}}</a> kedvencnek jelölte a privát említésedet",
|
||||
"notification.follow": "{name} követ téged",
|
||||
"notification.follow.name_and_others": "{name} és <a>{count, plural, one {# másik} other {# másik}}</a> követni kezdett",
|
||||
"notification.follow_request": "{name} követni szeretne téged",
|
||||
|
@ -781,10 +782,11 @@
|
|||
"search_results.accounts": "Profilok",
|
||||
"search_results.all": "Összes",
|
||||
"search_results.hashtags": "Hashtagek",
|
||||
"search_results.nothing_found": "Nincs találat ezekre a keresési kifejezésekre",
|
||||
"search_results.no_results": "Nincs találat.",
|
||||
"search_results.no_search_yet": "Próbálj meg bejegyzések, profilok vagy címkék után keresni.",
|
||||
"search_results.see_all": "Összes megtekintése",
|
||||
"search_results.statuses": "Bejegyzések",
|
||||
"search_results.title": "{q} keresése",
|
||||
"search_results.title": "\"{q}\" keresése",
|
||||
"server_banner.about_active_users": "Az elmúlt 30 napban ezt a kiszolgálót használók száma (Havi aktív felhasználók)",
|
||||
"server_banner.active_users": "aktív felhasználó",
|
||||
"server_banner.administered_by": "Adminisztrátor:",
|
||||
|
@ -857,6 +859,7 @@
|
|||
"subscribed_languages.target": "Feliratkozott nyelvek módosítása {target} esetében",
|
||||
"tabs_bar.home": "Kezdőlap",
|
||||
"tabs_bar.notifications": "Értesítések",
|
||||
"terms_of_service.title": "Felhasználási feltételek",
|
||||
"time_remaining.days": "{number, plural, one {# nap} other {# nap}} van hátra",
|
||||
"time_remaining.hours": "{number, plural, one {# óra} other {# óra}} van hátra",
|
||||
"time_remaining.minutes": "{number, plural, one {# perc} other {# perc}} van hátra",
|
||||
|
|
|
@ -180,7 +180,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Փորձիր անջատել յաւելուածները եւ թարմացնել էջը։ Եթե դա չօգնի, կարող ես օգտուել Մաստադոնից այլ դիտարկիչով կամ յաւելուածով։",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Պատճենել սթաքթրեյսը սեղմատախտակին",
|
||||
"errors.unexpected_crash.report_issue": "Զեկուցել խնդրի մասին",
|
||||
"explore.search_results": "Որոնման արդիւնքներ",
|
||||
"explore.suggested_follows": "Մարդիկ",
|
||||
"explore.title": "Բացայայտել",
|
||||
"explore.trending_links": "Նորութիւններ",
|
||||
|
@ -201,7 +200,6 @@
|
|||
"footer.about": "Մասին",
|
||||
"footer.directory": "Հաշիւների մատեան",
|
||||
"footer.get_app": "Ներբեռնիր յաւելուած",
|
||||
"footer.invite": "Հրաւիրել մարդկանց",
|
||||
"footer.keyboard_shortcuts": "Ստեղնաշարի կարճատներ",
|
||||
"footer.privacy_policy": "Գաղտնիութեան քաղաքականութիւն",
|
||||
"footer.source_code": "Նայել ելակոդը",
|
||||
|
@ -398,7 +396,6 @@
|
|||
"search_results.hashtags": "Պիտակներ",
|
||||
"search_results.see_all": "Տեսնել բոլորը",
|
||||
"search_results.statuses": "Գրառումներ",
|
||||
"search_results.title": "Որոնել {q}-ն",
|
||||
"server_banner.active_users": "ակտիւ մարդիկ",
|
||||
"server_banner.administered_by": "Կառաւարող",
|
||||
"server_banner.server_stats": "Սերուերի վիճակը",
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
"alert.rate_limited.title": "Excesso de requestas",
|
||||
"alert.unexpected.message": "Un error inexpectate ha occurrite.",
|
||||
"alert.unexpected.title": "Ups!",
|
||||
"alt_text_badge.title": "Texto alt",
|
||||
"alt_text_badge.title": "Texto alternative",
|
||||
"announcement.announcement": "Annuncio",
|
||||
"annual_report.summary.archetype.booster": "Le impulsator",
|
||||
"annual_report.summary.archetype.lurker": "Le lector",
|
||||
|
@ -103,6 +103,7 @@
|
|||
"annual_report.summary.most_used_hashtag.most_used_hashtag": "hashtag le plus usate",
|
||||
"annual_report.summary.most_used_hashtag.none": "Necun",
|
||||
"annual_report.summary.new_posts.new_posts": "nove messages",
|
||||
"annual_report.summary.percentile.text": "<topLabel>Isto te pone in le prime</topLabel><percentage></percentage><bottomLabel>usatores de {domain}.</bottomLabel>",
|
||||
"annual_report.summary.percentile.we_wont_tell_bernie": "Tu es un primo inter pares.",
|
||||
"annual_report.summary.thanks": "Gratias pro facer parte de Mastodon!",
|
||||
"attachments_list.unprocessed": "(non processate)",
|
||||
|
@ -143,10 +144,12 @@
|
|||
"column.direct": "Mentiones private",
|
||||
"column.directory": "Navigar profilos",
|
||||
"column.domain_blocks": "Dominios blocate",
|
||||
"column.edit_list": "Modificar lista",
|
||||
"column.favourites": "Favorites",
|
||||
"column.firehose": "Fluxos in directo",
|
||||
"column.follow_requests": "Requestas de sequimento",
|
||||
"column.home": "Initio",
|
||||
"column.list_members": "Gerer le membros del lista",
|
||||
"column.lists": "Listas",
|
||||
"column.mutes": "Usatores silentiate",
|
||||
"column.notifications": "Notificationes",
|
||||
|
@ -202,6 +205,9 @@
|
|||
"confirmations.edit.confirm": "Modificar",
|
||||
"confirmations.edit.message": "Si tu modifica isto ora, le message in curso de composition essera perdite. Es tu secur de voler continuar?",
|
||||
"confirmations.edit.title": "Superscriber le message?",
|
||||
"confirmations.follow_to_list.confirm": "Sequer e adder al lista",
|
||||
"confirmations.follow_to_list.message": "Tu debe sequer {name} pro poter adder le/la a un lista.",
|
||||
"confirmations.follow_to_list.title": "Sequer le usator?",
|
||||
"confirmations.logout.confirm": "Clauder session",
|
||||
"confirmations.logout.message": "Es tu secur que tu vole clauder le session?",
|
||||
"confirmations.logout.title": "Clauder session?",
|
||||
|
@ -233,6 +239,9 @@
|
|||
"disabled_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate.",
|
||||
"dismissable_banner.community_timeline": "Ecce le messages public le plus recente del personas con contos sur {domain}.",
|
||||
"dismissable_banner.dismiss": "Clauder",
|
||||
"dismissable_banner.explore_links": "Iste articulos de novas se condivide le plus sur le fediverso hodie. Le articulos de novas le plus recente, publicate per plus personas differente, se classifica plus in alto.",
|
||||
"dismissable_banner.explore_statuses": "Iste messages de tote le fediverso gania popularitate hodie. Le messages plus nove con plus impulsos e favorites se classifica plus in alto.",
|
||||
"dismissable_banner.explore_tags": "Iste hashtags gania popularitate sur le fediverso hodie. Le hashtags usate per plus personas differente se classifica plus in alto.",
|
||||
"dismissable_banner.public_timeline": "Istes es le messages public le plus recente del personas sur le fediverso que le gente sur {domain} seque.",
|
||||
"domain_block_modal.block": "Blocar le servitor",
|
||||
"domain_block_modal.block_account_instead": "Blocar @{name} in su loco",
|
||||
|
@ -240,8 +249,8 @@
|
|||
"domain_block_modal.they_cant_follow": "Necuno de iste servitor pote sequer te.",
|
||||
"domain_block_modal.they_wont_know": "Ille non sapera que ille ha essite blocate.",
|
||||
"domain_block_modal.title": "Blocar dominio?",
|
||||
"domain_block_modal.you_will_lose_num_followers": "Tu perdera {followersCount, plural, one {{followersCountDisplay} sequace} other {{followersCountDisplay} sequaces}} e {followingCount, plural, one {{followingCountDisplay} persona que tu seque} other {{followingCountDisplay} personas que tu seque}}.",
|
||||
"domain_block_modal.you_will_lose_relationships": "Tu perdera tote le sequaces e le personas que tu seque ab iste servitor.",
|
||||
"domain_block_modal.you_will_lose_num_followers": "Tu perdera {followersCount, plural, one {{followersCountDisplay} sequitor} other {{followersCountDisplay} sequitores}} e {followingCount, plural, one {{followingCountDisplay} persona que tu seque} other {{followingCountDisplay} personas que tu seque}}.",
|
||||
"domain_block_modal.you_will_lose_relationships": "Tu perdera tote le sequitores e personas que tu seque de iste servitor.",
|
||||
"domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes de usatores sur iste servitor.",
|
||||
"domain_pill.activitypub_lets_connect": "Illo te permitte connecter e interager con personas non solmente sur Mastodon, ma tamben sur altere applicationes social.",
|
||||
"domain_pill.activitypub_like_language": "ActivityPub es como le linguage commun que Mastodon parla con altere retes social.",
|
||||
|
@ -300,7 +309,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Tenta disactivar istes e refrescar le pagina. Si isto non remedia le problema, es possibile que tu pote totevia usar Mastodon per medio de un altere navigator o application native.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copiar le traciamento del pila al area de transferentia",
|
||||
"errors.unexpected_crash.report_issue": "Reportar problema",
|
||||
"explore.search_results": "Resultatos de recerca",
|
||||
"explore.suggested_follows": "Personas",
|
||||
"explore.title": "Explorar",
|
||||
"explore.trending_links": "Novas",
|
||||
|
@ -323,7 +331,7 @@
|
|||
"filter_modal.select_filter.title": "Filtrar iste message",
|
||||
"filter_modal.title.status": "Filtrar un message",
|
||||
"filter_warning.matches_filter": "Corresponde al filtro “<span>{title}</span>”",
|
||||
"filtered_notifications_banner.pending_requests": "De {count, plural, =0 {nemo} one {un persona} other {# personas}} que tu pote cognoscer",
|
||||
"filtered_notifications_banner.pending_requests": "De {count, plural, =0 {necuno} one {un persona} other {# personas}} que tu pote cognoscer",
|
||||
"filtered_notifications_banner.title": "Notificationes filtrate",
|
||||
"firehose.all": "Toto",
|
||||
"firehose.local": "Iste servitor",
|
||||
|
@ -350,13 +358,14 @@
|
|||
"footer.about": "A proposito",
|
||||
"footer.directory": "Directorio de profilos",
|
||||
"footer.get_app": "Obtener le application",
|
||||
"footer.invite": "Invitar personas",
|
||||
"footer.keyboard_shortcuts": "Accessos directe de claviero",
|
||||
"footer.privacy_policy": "Politica de confidentialitate",
|
||||
"footer.source_code": "Vider le codice fonte",
|
||||
"footer.status": "Stato",
|
||||
"footer.terms_of_service": "Conditiones de servicio",
|
||||
"generic.saved": "Salvate",
|
||||
"getting_started.heading": "Prime passos",
|
||||
"hashtag.admin_moderation": "Aperir le interfacie de moderation pro #{name}",
|
||||
"hashtag.column_header.tag_mode.all": "e {additional}",
|
||||
"hashtag.column_header.tag_mode.any": "o {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "sin {additional}",
|
||||
|
@ -372,9 +381,9 @@
|
|||
"hashtag.follow": "Sequer hashtag",
|
||||
"hashtag.unfollow": "Non sequer plus le hashtag",
|
||||
"hashtags.and_other": "…e {count, plural, one {}other {# plus}}",
|
||||
"hints.profiles.followers_may_be_missing": "Le sequaces pro iste profilo pote mancar.",
|
||||
"hints.profiles.follows_may_be_missing": "Sequites pro iste profilo pote mancar.",
|
||||
"hints.profiles.posts_may_be_missing": "Alcun messages ab iste profilo pote mancar.",
|
||||
"hints.profiles.followers_may_be_missing": "Le sequitores de iste profilo pote mancar.",
|
||||
"hints.profiles.follows_may_be_missing": "Le profilos sequite per iste profilo pote mancar.",
|
||||
"hints.profiles.posts_may_be_missing": "Alcun messages de iste profilo pote mancar.",
|
||||
"hints.profiles.see_more_followers": "Vider plus de sequitores sur {domain}",
|
||||
"hints.profiles.see_more_follows": "Vider plus de sequites sur {domain}",
|
||||
"hints.profiles.see_more_posts": "Vider plus de messages sur {domain}",
|
||||
|
@ -387,11 +396,11 @@
|
|||
"home.pending_critical_update.link": "Vider actualisationes",
|
||||
"home.pending_critical_update.title": "Actualisation de securitate critic disponibile!",
|
||||
"home.show_announcements": "Monstrar annuncios",
|
||||
"ignore_notifications_modal.disclaimer": "Mastodon non pote informar le usatores que tu ha ignorate lor avisos. Ignorar avisos non stoppara le messages mesme de esser inviate.",
|
||||
"ignore_notifications_modal.disclaimer": "Mastodon non pote informar al usatores que tu ha ignorate lor notificationes. Ignorar le notificationes non impedira le invio del messages.",
|
||||
"ignore_notifications_modal.filter_instead": "Filtrar in vice",
|
||||
"ignore_notifications_modal.filter_to_act_users": "Tu ancora potera acceptar, rejectar, o reportar usatores",
|
||||
"ignore_notifications_modal.filter_to_avoid_confusion": "Filtrar adjuta evitar confusion potential",
|
||||
"ignore_notifications_modal.filter_to_review_separately": "Tu pote revider avisos filtrate separatemente",
|
||||
"ignore_notifications_modal.filter_to_avoid_confusion": "Filtrar adjuta a evitar confusion potential",
|
||||
"ignore_notifications_modal.filter_to_review_separately": "Tu pote revider separatemente le notificationes filtrate",
|
||||
"ignore_notifications_modal.ignore": "Ignorar le notificationes",
|
||||
"ignore_notifications_modal.limited_accounts_title": "Ignorar le notificationes de contos moderate?",
|
||||
"ignore_notifications_modal.new_accounts_title": "Ignorar le notificationes de nove contos?",
|
||||
|
@ -455,8 +464,8 @@
|
|||
"lightbox.close": "Clauder",
|
||||
"lightbox.next": "Sequente",
|
||||
"lightbox.previous": "Precedente",
|
||||
"lightbox.zoom_in": "Aggrandir a dimension actual",
|
||||
"lightbox.zoom_out": "Aggrandir pro adaptar",
|
||||
"lightbox.zoom_in": "Aggrandir al dimension real",
|
||||
"lightbox.zoom_out": "Diminuer pro adaptar",
|
||||
"limited_account_hint.action": "Monstrar profilo in omne caso",
|
||||
"limited_account_hint.title": "Iste profilo ha essite celate per le moderatores de {domain}.",
|
||||
"link_preview.author": "Per {name}",
|
||||
|
@ -466,13 +475,28 @@
|
|||
"lists.add_to_list": "Adder al lista",
|
||||
"lists.add_to_lists": "Adder {name} al listas",
|
||||
"lists.create": "Crear",
|
||||
"lists.create_a_list_to_organize": "Crear un nove lista pro organisar tu fluxo de initio",
|
||||
"lists.create_list": "Crear lista",
|
||||
"lists.delete": "Deler lista",
|
||||
"lists.done": "Facite",
|
||||
"lists.edit": "Modificar lista",
|
||||
"lists.exclusive": "Celar memberos in Initio",
|
||||
"lists.exclusive_hint": "Si alcuno es sur iste lista, celar iste persona in tu fluxo de initio pro evitar de vider su messages duo vices.",
|
||||
"lists.find_users_to_add": "Trovar usatores a adder",
|
||||
"lists.list_members": "Membros del lista",
|
||||
"lists.list_members_count": "{count, plural, one {# membro} other {# membros}}",
|
||||
"lists.list_name": "Nomine del lista",
|
||||
"lists.new_list_name": "Nove nomine de lista",
|
||||
"lists.no_lists_yet": "Necun lista ancora.",
|
||||
"lists.no_members_yet": "Necun membro ancora.",
|
||||
"lists.no_results_found": "Necun resultato trovate.",
|
||||
"lists.remove_member": "Remover",
|
||||
"lists.replies_policy.followed": "Qualcunque usator sequite",
|
||||
"lists.replies_policy.list": "Membros del lista",
|
||||
"lists.replies_policy.none": "Nemo",
|
||||
"lists.save": "Salvar",
|
||||
"lists.search": "Cercar",
|
||||
"lists.show_replies_to": "Includer responsas de membros del lista a",
|
||||
"load_pending": "{count, plural, one {# nove entrata} other {# nove entratas}}",
|
||||
"loading_indicator.label": "Cargante…",
|
||||
"media_gallery.hide": "Celar",
|
||||
|
@ -520,11 +544,13 @@
|
|||
"notification.admin.report_statuses": "{name} ha reportate {target} pro {category}",
|
||||
"notification.admin.report_statuses_other": "{name} ha reportate {target}",
|
||||
"notification.admin.sign_up": "{name} se ha inscribite",
|
||||
"notification.admin.sign_up.name_and_others": "{name} e {count, plural, one {# altere usator} other {altere # usatores}} se inscribeva",
|
||||
"notification.admin.sign_up.name_and_others": "{name} e {count, plural, one {# altere persona} other {# altere personas}} se ha inscribite",
|
||||
"notification.annual_report.message": "Tu summario #Wrapstodon pro {year} attende! Revela le momentos saliente e memorabile de tu anno sur Mastodon!",
|
||||
"notification.annual_report.view": "Visitar summario #Wrapstodon",
|
||||
"notification.favourite": "{name} ha marcate tu message como favorite",
|
||||
"notification.favourite.name_and_others_with_link": "{name} e <a>{count, plural, one {# altere} other {# alteres}}</a> favoriva tu message",
|
||||
"notification.favourite.name_and_others_with_link": "{name} e <a>{count, plural, one {# altere} other {# alteres}}</a> ha marcate tu message como favorite",
|
||||
"notification.favourite_pm": "{name} ha marcate tu mention privte como favorite",
|
||||
"notification.favourite_pm.name_and_others_with_link": "{name} e <a>{count, plural, one {# altere} other {# alteres}}</a> ha marcate tu mention private como favorite",
|
||||
"notification.follow": "{name} te ha sequite",
|
||||
"notification.follow.name_and_others": "{name} e <a>{count, plural, one {# other} other {# alteres}}</a> te ha sequite",
|
||||
"notification.follow_request": "{name} ha requestate de sequer te",
|
||||
|
@ -534,7 +560,7 @@
|
|||
"notification.label.private_reply": "Responsa private",
|
||||
"notification.label.reply": "Responder",
|
||||
"notification.mention": "Mention",
|
||||
"notification.mentioned_you": "{name} te mentionava",
|
||||
"notification.mentioned_you": "{name} te ha mentionate",
|
||||
"notification.moderation-warning.learn_more": "Apprender plus",
|
||||
"notification.moderation_warning": "Tu ha recipite un advertimento de moderation",
|
||||
"notification.moderation_warning.action_delete_statuses": "Alcunes de tu messages ha essite removite.",
|
||||
|
@ -547,7 +573,7 @@
|
|||
"notification.own_poll": "Tu sondage ha finite",
|
||||
"notification.poll": "Un sondage in le qual tu ha votate ha finite",
|
||||
"notification.reblog": "{name} ha impulsate tu message",
|
||||
"notification.reblog.name_and_others_with_link": "{name} e <a>{count, plural, one {# altere} other {# alteres}}</a> promoveva tu message",
|
||||
"notification.reblog.name_and_others_with_link": "{name} e <a>{count, plural, one {# altere} other {# alteres}}</a> ha impulsate tu message",
|
||||
"notification.relationships_severance_event": "Connexiones perdite con {name}",
|
||||
"notification.relationships_severance_event.account_suspension": "Un administrator de {from} ha suspendiute {target}. Isto significa que tu non pote plus reciper actualisationes de iste persona o interager con ille.",
|
||||
"notification.relationships_severance_event.domain_block": "Un administrator de {from} ha blocate {target}, includente {followersCount} de tu sequitores e {followingCount, plural, one {# conto} other {# contos}} que tu seque.",
|
||||
|
@ -556,19 +582,19 @@
|
|||
"notification.status": "{name} ha justo ora publicate",
|
||||
"notification.update": "{name} ha modificate un message",
|
||||
"notification_requests.accept": "Acceptar",
|
||||
"notification_requests.accept_multiple": "{count, plural, one {Accepta # requesta…} other {Accepta # requestas…}}",
|
||||
"notification_requests.confirm_accept_multiple.button": "{count, plural, one {Accepta requesta} other {Accepta requestas}}",
|
||||
"notification_requests.confirm_accept_multiple.message": "Tu acceptara {count, plural, one {un requesta de aviso} other {# requestas de aviso}}. Desira tu vermente continuar?",
|
||||
"notification_requests.accept_multiple": "{count, plural, one {Acceptar # requesta…} other {Acceptar # requestas…}}",
|
||||
"notification_requests.confirm_accept_multiple.button": "{count, plural, one {Acceptar le requesta} other {Acceptar le requestas}}",
|
||||
"notification_requests.confirm_accept_multiple.message": "Tu es sur le puncto de acceptar {count, plural, one {un requesta de notification} other {# requestas de notification}}. Es tu secur de voler continuar?",
|
||||
"notification_requests.confirm_accept_multiple.title": "Acceptar petitiones de notification?",
|
||||
"notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Rejectar requesta} other {Rejectar requestas}}",
|
||||
"notification_requests.confirm_dismiss_multiple.message": "Tu rejectara {count, plural, one {un requesta de aviso} other {# requestas de aviso}}. Tu non potera facilemente acceder {count, plural, one {lo} other {los}} ancora. Desira tu vermente continuar?",
|
||||
"notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Rejectar le requesta} other {Rejectar le requestas}}",
|
||||
"notification_requests.confirm_dismiss_multiple.message": "Tu es sur le puncto de rejectar {count, plural, one {un requesta} other {# requestas}} de notification. Tu non potera facilemente acceder a {count, plural, one {illo} other {illos}} plus tarde. Es tu secur de voler continuar?",
|
||||
"notification_requests.confirm_dismiss_multiple.title": "Dimitter petitiones de notification?",
|
||||
"notification_requests.dismiss": "Clauder",
|
||||
"notification_requests.dismiss_multiple": "{count, plural, one {Rejectar # requesta…} other {Rejectar # requestas…}}",
|
||||
"notification_requests.edit_selection": "Modificar",
|
||||
"notification_requests.exit_selection": "Facite",
|
||||
"notification_requests.explainer_for_limited_account": "Le avisos ab iste conto ha essite filtrate perque le conto ha essite limitate per un moderator.",
|
||||
"notification_requests.explainer_for_limited_remote_account": "Le avisos ab iste conto ha essite filtrate perque le conto o su servitor ha essite limitate per un moderator.",
|
||||
"notification_requests.explainer_for_limited_account": "Le notificationes de iste conto ha essite filtrate perque le conto ha essite limitate per un moderator.",
|
||||
"notification_requests.explainer_for_limited_remote_account": "Le notificationes de iste conto ha essite filtrate perque le conto o su servitor ha essite limitate per un moderator.",
|
||||
"notification_requests.maximize": "Maximisar",
|
||||
"notification_requests.minimize_banner": "Minimisar le bandiera del avisos filtrate",
|
||||
"notification_requests.notifications_from": "Notificationes de {name}",
|
||||
|
@ -629,6 +655,8 @@
|
|||
"notifications_permission_banner.enable": "Activar notificationes de scriptorio",
|
||||
"notifications_permission_banner.how_to_control": "Pro reciper notificationes quando Mastodon non es aperte, activa le notificationes de scriptorio. Post lor activation, es possibile controlar precisemente qual typos de interaction genera notificationes de scriptorio per medio del button {icon} hic supra.",
|
||||
"notifications_permission_banner.title": "Non mancar jammais a un cosa",
|
||||
"onboarding.follows.back": "Retro",
|
||||
"onboarding.follows.done": "Facite",
|
||||
"onboarding.follows.empty": "Regrettabilemente, non es possibile monstrar resultatos al momento. Tu pote tentar usar le recerca o percurrer le pagina de exploration pro cercar personas a sequer, o tentar lo de novo plus tarde.",
|
||||
"onboarding.follows.search": "Cercar",
|
||||
"onboarding.follows.title": "Seque personas pro comenciar",
|
||||
|
@ -754,10 +782,8 @@
|
|||
"search_results.accounts": "Profilos",
|
||||
"search_results.all": "Toto",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Nihil trovate pro iste terminos de recerca",
|
||||
"search_results.see_all": "Vider toto",
|
||||
"search_results.statuses": "Messages",
|
||||
"search_results.title": "Cercar {q}",
|
||||
"server_banner.about_active_users": "Personas que ha usate iste servitor in le ultime 30 dies (usatores active per mense)",
|
||||
"server_banner.active_users": "usatores active",
|
||||
"server_banner.administered_by": "Administrate per:",
|
||||
|
@ -775,7 +801,7 @@
|
|||
"status.bookmark": "Adder al marcapaginas",
|
||||
"status.cancel_reblog_private": "Disfacer impulso",
|
||||
"status.cannot_reblog": "Iste message non pote esser impulsate",
|
||||
"status.continued_thread": "Argumento continuitate",
|
||||
"status.continued_thread": "Continuation del discussion",
|
||||
"status.copy": "Copiar ligamine a message",
|
||||
"status.delete": "Deler",
|
||||
"status.detailed_status": "Vista detaliate del conversation",
|
||||
|
@ -830,6 +856,7 @@
|
|||
"subscribed_languages.target": "Cambiar le linguas subscribite pro {target}",
|
||||
"tabs_bar.home": "Initio",
|
||||
"tabs_bar.notifications": "Notificationes",
|
||||
"terms_of_service.title": "Conditiones de servicio",
|
||||
"time_remaining.days": "{number, plural, one {# die} other {# dies}} restante",
|
||||
"time_remaining.hours": "{number, plural, one {# hora} other {# horas}} restante",
|
||||
"time_remaining.minutes": "{number, plural, one {# minuta} other {# minutas}} restante",
|
||||
|
@ -847,11 +874,11 @@
|
|||
"upload_error.poll": "Incargamento de files non permittite con sondages.",
|
||||
"upload_form.audio_description": "Describe lo pro le gente con difficultates auditive",
|
||||
"upload_form.description": "Describe lo pro le gente con difficultates visual",
|
||||
"upload_form.drag_and_drop.instructions": "Pro colliger un annexo de medios, pressar Spatio o Inviar. Trahente lo, usar le claves flecha pro mover le annexo de medios in ulle direction date. De novo pressar Spatio o Inviar pro deponer le annexo de medios in su nove position, o pressar Escappar pro cancellar.",
|
||||
"upload_form.drag_and_drop.on_drag_cancel": "Le extraction era cancellate. Le annexo de medios {item} era deponite.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "Le annexo de medios {item} era deponite.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "Le annexo de medios {item} era movite.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "Annexo de medios {item} colligite.",
|
||||
"upload_form.drag_and_drop.instructions": "Pro prender un annexo multimedial, preme sur le barra de spatios o Enter. Trahente lo, usa le claves de flecha pro displaciar le annexo multimedial in un certe direction. Preme le barra de spatios o Enter de novo pro deponer le annexo multimedial in su nove position, o preme sur Escape pro cancellar.",
|
||||
"upload_form.drag_and_drop.on_drag_cancel": "Le displaciamento ha essite cancellate. Le annexo multimedial {item} ha essite deponite.",
|
||||
"upload_form.drag_and_drop.on_drag_end": "Le annexo de medios {item} ha essite deponite.",
|
||||
"upload_form.drag_and_drop.on_drag_over": "Le annexo multimedial {item} ha essite displaciate.",
|
||||
"upload_form.drag_and_drop.on_drag_start": "Le annexo multimedial {item} ha essite prendite.",
|
||||
"upload_form.edit": "Modificar",
|
||||
"upload_form.thumbnail": "Cambiar le miniatura",
|
||||
"upload_form.video_description": "Describe lo pro le gente con difficultates auditive o visual",
|
||||
|
|
|
@ -271,7 +271,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Coba nonaktifkan mereka lalu segarkan halaman. Jika itu tidak membantu, Anda masih bisa memakai Mastodon dengan peramban berbeda atau aplikasi asli.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Salin stacktrace ke papan klip",
|
||||
"errors.unexpected_crash.report_issue": "Laporkan masalah",
|
||||
"explore.search_results": "Hasil pencarian",
|
||||
"explore.suggested_follows": "Orang",
|
||||
"explore.title": "Jelajahi",
|
||||
"explore.trending_links": "Berita",
|
||||
|
@ -315,7 +314,6 @@
|
|||
"footer.about": "Tentang",
|
||||
"footer.directory": "Direktori profil",
|
||||
"footer.get_app": "Dapatkan aplikasi",
|
||||
"footer.invite": "Undang orang",
|
||||
"footer.keyboard_shortcuts": "Pintasan papan ketik",
|
||||
"footer.privacy_policy": "Kebijakan privasi",
|
||||
"footer.source_code": "Lihat kode sumber",
|
||||
|
@ -536,9 +534,7 @@
|
|||
"search.search_or_paste": "Cari atau ketik URL",
|
||||
"search_results.all": "Semua",
|
||||
"search_results.hashtags": "Tagar",
|
||||
"search_results.nothing_found": "Tidak dapat menemukan apa pun untuk istilah-istilah pencarian ini",
|
||||
"search_results.statuses": "Kiriman",
|
||||
"search_results.title": "Cari {q}",
|
||||
"server_banner.about_active_users": "Orang menggunakan server ini selama 30 hari terakhir (Pengguna Aktif Bulanan)",
|
||||
"server_banner.active_users": "pengguna aktif",
|
||||
"server_banner.administered_by": "Dikelola oleh:",
|
||||
|
|
|
@ -258,7 +258,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Prova desactivisar les e recargar li págine. Si to ne auxilia, tu fórsan posse usar Mastodon per un diferent navigator o aplication.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Copiar cumul-tracie a paperiere",
|
||||
"errors.unexpected_crash.report_issue": "Raportar un problema",
|
||||
"explore.search_results": "Resultates de sercha",
|
||||
"explore.suggested_follows": "Gente",
|
||||
"explore.title": "Explorar",
|
||||
"explore.trending_links": "Novas",
|
||||
|
@ -306,7 +305,6 @@
|
|||
"footer.about": "Information",
|
||||
"footer.directory": "Profilarium",
|
||||
"footer.get_app": "Obtener li aplication",
|
||||
"footer.invite": "Invitar gente",
|
||||
"footer.keyboard_shortcuts": "Rapid-tastes",
|
||||
"footer.privacy_policy": "Politica pri privatie",
|
||||
"footer.source_code": "Vider li fonte-code",
|
||||
|
@ -626,10 +624,8 @@
|
|||
"search_results.accounts": "Profiles",
|
||||
"search_results.all": "Omni",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.nothing_found": "Trovat se nullcos por ti término de sercha",
|
||||
"search_results.see_all": "Vider omni",
|
||||
"search_results.statuses": "Postas",
|
||||
"search_results.title": "Sercha por {q}",
|
||||
"server_banner.about_active_users": "Gente usant ti-ci servitor durant li ultim 30 dies (Mensual Activ Usatores)",
|
||||
"server_banner.active_users": "activ usatores",
|
||||
"server_banner.administered_by": "Administrat de:",
|
||||
|
|
|
@ -276,7 +276,6 @@
|
|||
"error.unexpected_crash.next_steps_addons": "Probez desaktivigar e rifreshar pagino. Se to ne helpas, vu forsan ankore povas uzar Mastodon per diferenta vidilo o provizita softwaro.",
|
||||
"errors.unexpected_crash.copy_stacktrace": "Kopiez amastraso a klipplanko",
|
||||
"errors.unexpected_crash.report_issue": "Reportigez problemo",
|
||||
"explore.search_results": "Trovuri",
|
||||
"explore.suggested_follows": "Personi",
|
||||
"explore.title": "Explorez",
|
||||
"explore.trending_links": "Novaji",
|
||||
|
@ -325,7 +324,6 @@
|
|||
"footer.about": "Pri co",
|
||||
"footer.directory": "Profilcheflisto",
|
||||
"footer.get_app": "Obtenez la softwaro",
|
||||
"footer.invite": "Invitez personi",
|
||||
"footer.keyboard_shortcuts": "Kombini di klavi",
|
||||
"footer.privacy_policy": "Guidilo pri privateso",
|
||||
"footer.source_code": "Vidar la fontokodexo",
|
||||
|
@ -714,10 +712,8 @@
|
|||
"search_results.accounts": "Profili",
|
||||
"search_results.all": "Omna",
|
||||
"search_results.hashtags": "Hashtagi",
|
||||
"search_results.nothing_found": "Ne povas ganar irgo per ca trovvorti",
|
||||
"search_results.see_all": "Videz omni",
|
||||
"search_results.statuses": "Posti",
|
||||
"search_results.title": "Trovez {q}",
|
||||
"server_banner.about_active_users": "Personi quo uzas ca servilo dum antea 30 dii (monate aktiva uzanti)",
|
||||
"server_banner.active_users": "aktiva uzanti",
|
||||
"server_banner.administered_by": "Administresis da:",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue