diff --git a/app/javascript/mastodon/features/ui/util/react_router_helpers.js b/app/javascript/mastodon/features/ui/util/react_router_helpers.js
index 43007ddc3..32dfe320b 100644
--- a/app/javascript/mastodon/features/ui/util/react_router_helpers.js
+++ b/app/javascript/mastodon/features/ui/util/react_router_helpers.js
@@ -35,14 +35,19 @@ export class WrappedRoute extends React.Component {
component: PropTypes.func.isRequired,
content: PropTypes.node,
multiColumn: PropTypes.bool,
- }
+ componentParams: PropTypes.object,
+ };
+
+ static defaultProps = {
+ componentParams: {},
+ };
renderComponent = ({ match }) => {
- const { component, content, multiColumn } = this.props;
+ const { component, content, multiColumn, componentParams } = this.props;
return (
);
}
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 0ee8bb6c8..98ebcb6f9 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -4,6 +4,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { throttle } from 'lodash';
import classNames from 'classnames';
import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
+import { displaySensitiveMedia } from '../../initial_state';
const messages = defineMessages({
play: { id: 'video.play', defaultMessage: 'Play' },
@@ -29,7 +30,7 @@ const formatTime = secondsNum => {
return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
};
-const findElementPosition = el => {
+export const findElementPosition = el => {
let box;
if (el.getBoundingClientRect && el.parentNode) {
@@ -60,7 +61,7 @@ const findElementPosition = el => {
};
};
-const getPointerPosition = (el, event) => {
+export const getPointerPosition = (el, event) => {
const position = {};
const box = findElementPosition(el);
const boxW = el.offsetWidth;
@@ -76,7 +77,7 @@ const getPointerPosition = (el, event) => {
pageY = event.changedTouches[0].pageY;
}
- position.y = Math.max(0, Math.min(1, ((boxY - pageY) + boxH) / boxH));
+ position.y = Math.max(0, Math.min(1, (pageY - boxY) / boxH));
position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
return position;
@@ -96,6 +97,7 @@ export default class Video extends React.PureComponent {
onOpenVideo: PropTypes.func,
onCloseVideo: PropTypes.func,
detailed: PropTypes.bool,
+ inline: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
@@ -104,14 +106,21 @@ export default class Video extends React.PureComponent {
duration: 0,
paused: true,
dragging: false,
+ containerWidth: false,
fullscreen: false,
hovered: false,
muted: false,
- revealed: !this.props.sensitive,
+ revealed: !this.props.sensitive || displaySensitiveMedia,
};
setPlayerRef = c => {
this.player = c;
+
+ if (c) {
+ this.setState({
+ containerWidth: c.offsetWidth,
+ });
+ }
}
setVideoRef = c => {
@@ -245,12 +254,23 @@ export default class Video extends React.PureComponent {
}
render () {
- const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed } = this.props;
- const { currentTime, duration, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
+ const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed } = this.props;
+ const { containerWidth, currentTime, duration, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
const progress = (currentTime / duration) * 100;
+ const playerStyle = {};
+
+ let { width, height } = this.props;
+
+ if (inline && containerWidth) {
+ width = containerWidth;
+ height = containerWidth / (16/9);
+
+ playerStyle.width = width;
+ playerStyle.height = height;
+ }
return (
-
+
-
+
@@ -289,10 +309,10 @@ export default class Video extends React.PureComponent {
-
-
+
+
- {!onCloseVideo && }
+ {!onCloseVideo && }
{(detailed || fullscreen) &&
@@ -304,9 +324,9 @@ export default class Video extends React.PureComponent {
- {(!fullscreen && onOpenVideo) && }
- {onCloseVideo && }
-
+ {(!fullscreen && onOpenVideo) && }
+ {onCloseVideo && }
+
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index 3fc45077d..df310e7e1 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -5,9 +5,11 @@ const getMeta = (prop) => initialState && initialState.meta && initialState.meta
export const reduceMotion = getMeta('reduce_motion');
export const autoPlayGif = getMeta('auto_play_gif');
+export const displaySensitiveMedia = getMeta('display_sensitive_media');
export const unfollowModal = getMeta('unfollow_modal');
export const boostModal = getMeta('boost_modal');
export const deleteModal = getMeta('delete_modal');
export const me = getMeta('me');
+export const searchEnabled = getMeta('search_enabled');
export default initialState;
diff --git a/app/javascript/mastodon/load_polyfills.js b/app/javascript/mastodon/load_polyfills.js
index 8927b7358..815e1905b 100644
--- a/app/javascript/mastodon/load_polyfills.js
+++ b/app/javascript/mastodon/load_polyfills.js
@@ -14,6 +14,7 @@ function loadPolyfills() {
const needsBasePolyfills = !(
window.Intl &&
Object.assign &&
+ Object.values &&
Number.isNaN &&
window.Symbol &&
Array.prototype.includes
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 310c7aa55..73680a1a1 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -1,7 +1,9 @@
{
"account.block": "حظر @{name}",
"account.block_domain": "إخفاء كل شيئ قادم من إسم النطاق {domain}",
+ "account.blocked": "محظور",
"account.disclaimer_full": "قد لا تعكس المعلومات أدناه الملف الشخصي الكامل للمستخدم.",
+ "account.domain_blocked": "النطاق مخفي",
"account.edit_profile": "تعديل الملف الشخصي",
"account.follow": "تابِع",
"account.followers": "المتابعون",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} إنتقل إلى :",
"account.mute": "أكتم @{name}",
"account.mute_notifications": "كتم إخطارات @{name}",
- "account.posts": "المشاركات",
+ "account.muted": "مكتوم",
+ "account.posts": "التبويقات",
+ "account.posts_with_replies": "تبويقات تحتوي على رُدود",
"account.report": "أبلغ عن @{name}",
"account.requested": "في انتظار الموافقة",
"account.share": "مشاركة @{name}'s profile",
@@ -50,14 +54,16 @@
"column_header.unpin": "فك التدبيس",
"column_subheading.navigation": "التصفح",
"column_subheading.settings": "الإعدادات",
- "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+ "compose_form.hashtag_warning": "هذا التبويق لن يُدرَج تحت أي وسم كان بما أنه غير مُدرَج. لا يُسمح بالبحث إلّا عن التبويقات العمومية عن طريق الوسوم.",
"compose_form.lock_disclaimer": "حسابك ليس {locked}. يمكن لأي شخص متابعتك و عرض المنشورات.",
"compose_form.lock_disclaimer.lock": "مقفل",
"compose_form.placeholder": "فيمَ تفكّر؟",
"compose_form.publish": "بوّق",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "ضع علامة على الوسيط باعتباره حسّاس",
- "compose_form.spoiler": "أخفِ النص واعرض تحذيرا",
+ "compose_form.sensitive.marked": "لقد تم تحديد هذه الصورة كحساسة",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "إنّ النص مخفي وراء تحذير",
+ "compose_form.spoiler.unmarked": "النص غير مخفي",
"compose_form.spoiler_placeholder": "تنبيه عن المحتوى",
"confirmation_modal.cancel": "إلغاء",
"confirmations.block.confirm": "حجب",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "إلغاء",
+ "report.forward": "التحويل إلى {target}",
+ "report.forward_hint": "هذا الحساب ينتمي إلى خادوم آخَر. هل تودّ إرسال نسخة مجهولة مِن التقرير إلى هنالك أيضًا ؟",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "تعليقات إضافية",
"report.submit": "إرسال",
"report.target": "إبلاغ",
"search.placeholder": "ابحث",
"search_popout.search_format": "نمط البحث المتقدم",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "وسم",
"search_popout.tips.status": "حالة",
"search_popout.tips.text": "جملة قصيرة تُمكّنُك من عرض أسماء و حسابات و كلمات رمزية",
"search_popout.tips.user": "مستخدِم",
+ "search_results.accounts": "أشخاص",
+ "search_results.hashtags": "الوُسوم",
+ "search_results.statuses": "التبويقات",
"search_results.total": "{count, number} {count, plural, one {result} و {results}}",
"standalone.public_title": "نظرة على ...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "كتم المحادثة",
"status.open": "وسع هذه المشاركة",
"status.pin": "تدبيس على الملف الشخصي",
+ "status.pinned": "تبويق مثبَّت",
"status.reblog": "رَقِّي",
"status.reblogged_by": "{name} رقى",
"status.reply": "ردّ",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "محتوى حساس",
"status.share": "مشاركة",
"status.show_less": "إعرض أقلّ",
+ "status.show_less_all": "طي الكل",
"status.show_more": "أظهر المزيد",
+ "status.show_more_all": "توسيع الكل",
"status.unmute_conversation": "فك الكتم عن المحادثة",
"status.unpin": "فك التدبيس من الملف الشخصي",
- "tabs_bar.compose": "تحرير",
"tabs_bar.federated_timeline": "الموحَّد",
"tabs_bar.home": "الرئيسية",
"tabs_bar.local_timeline": "المحلي",
@@ -252,6 +267,7 @@
"upload_area.title": "إسحب ثم أفلت للرفع",
"upload_button.label": "إضافة وسائط",
"upload_form.description": "وصف للمعاقين بصريا",
+ "upload_form.focus": "قص",
"upload_form.undo": "إلغاء",
"upload_progress.label": "يرفع...",
"video.close": "إغلاق الفيديو",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 8188ae2c0..1dee16748 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -1,7 +1,9 @@
{
"account.block": "Блокирай",
"account.block_domain": "Hide everything from {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Редактирай профила си",
"account.follow": "Последвай",
"account.followers": "Последователи",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Mute @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "Публикации",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Report @{name}",
"account.requested": "В очакване на одобрение",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Какво си мислиш?",
"compose_form.publish": "Раздумай",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Отбележи съдържанието като деликатно",
- "compose_form.spoiler": "Скрий текста зад предупреждение",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Content warning",
"confirmation_modal.cancel": "Cancel",
"confirmations.block.confirm": "Block",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Отказ",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Additional comments",
"report.submit": "Submit",
"report.target": "Reporting",
"search.placeholder": "Търсене",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Споделяне",
"status.reblogged_by": "{name} сподели",
"status.reply": "Отговор",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Деликатно съдържание",
"status.share": "Share",
"status.show_less": "Show less",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Show more",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Съставяне",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Начало",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Drag & drop to upload",
"upload_button.label": "Добави медия",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Отмяна",
"upload_progress.label": "Uploading...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index ecaae0847..4923c1032 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -1,29 +1,33 @@
{
- "account.block": "Bloquejar @{name}",
- "account.block_domain": "Amagar tot de {domain}",
+ "account.block": "Bloca @{name}",
+ "account.block_domain": "Amaga-ho tot de {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "La informació següent pot reflectir incompleta el perfil de l'usuari.",
- "account.edit_profile": "Editar perfil",
- "account.follow": "Seguir",
+ "account.domain_blocked": "Domain hidden",
+ "account.edit_profile": "Edita el perfil",
+ "account.follow": "Segueix",
"account.followers": "Seguidors",
"account.follows": "Seguint",
- "account.follows_you": "et segueix",
+ "account.follows_you": "Et segueix",
"account.hide_reblogs": "Amaga els impulsos de @{name}",
"account.media": "Media",
"account.mention": "Esmentar @{name}",
"account.moved_to": "{name} s'ha mogut a:",
- "account.mute": "Silenciar @{name}",
+ "account.mute": "Silencia @{name}",
"account.mute_notifications": "Notificacions desactivades de @{name}",
- "account.posts": "Publicacions",
+ "account.muted": "Muted",
+ "account.posts": "Toots",
+ "account.posts_with_replies": "Toots amb respostes",
"account.report": "Informe @{name}",
"account.requested": "Esperant aprovació. Clic per a cancel·lar la petició de seguiment",
- "account.share": "Compartir el perfil de @{name}",
+ "account.share": "Comparteix el perfil de @{name}",
"account.show_reblogs": "Mostra els impulsos de @{name}",
- "account.unblock": "Desbloquejar @{name}",
+ "account.unblock": "Desbloca @{name}",
"account.unblock_domain": "Mostra {domain}",
- "account.unfollow": "Deixar de seguir",
+ "account.unfollow": "Deixa de seguir",
"account.unmute": "Treure silenci de @{name}",
"account.unmute_notifications": "Activar notificacions de @{name}",
- "account.view_full_profile": "Veure el perfil complet",
+ "account.view_full_profile": "Mostra el perfil complet",
"boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop",
"bundle_column_error.body": "S'ha produït un error en carregar aquest component.",
"bundle_column_error.retry": "Torna-ho a provar",
@@ -31,7 +35,7 @@
"bundle_modal_error.close": "Tanca",
"bundle_modal_error.message": "S'ha produït un error en carregar aquest component.",
"bundle_modal_error.retry": "Torna-ho a provar",
- "column.blocks": "Usuaris bloquejats",
+ "column.blocks": "Usuaris blocats",
"column.community": "Línia de temps local",
"column.favourites": "Favorits",
"column.follow_requests": "Peticions per seguir-te",
@@ -45,46 +49,48 @@
"column_header.hide_settings": "Amaga la configuració",
"column_header.moveLeft_settings": "Mou la columna cap a l'esquerra",
"column_header.moveRight_settings": "Mou la columna cap a la dreta",
- "column_header.pin": "Fixar",
+ "column_header.pin": "Fixa",
"column_header.show_settings": "Mostra la configuració",
- "column_header.unpin": "Deslligar",
+ "column_header.unpin": "No fixis",
"column_subheading.navigation": "Navegació",
"column_subheading.settings": "Configuració",
"compose_form.hashtag_warning": "Aquest toot no es mostrarà en cap etiqueta ja que no està llistat. Només els toots públics poden ser cercats per etiqueta.",
"compose_form.lock_disclaimer": "El teu compte no està bloquejat {locked}. Tothom pot seguir-te i veure els teus missatges a seguidors.",
- "compose_form.lock_disclaimer.lock": "bloquejat",
+ "compose_form.lock_disclaimer.lock": "blocat",
"compose_form.placeholder": "En què estàs pensant?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Marcar multimèdia com a sensible",
- "compose_form.spoiler": "Amagar text darrera l'advertència",
- "compose_form.spoiler_placeholder": "Escriu l'advertència aquí",
- "confirmation_modal.cancel": "Cancel·lar",
- "confirmations.block.confirm": "Bloquejar",
- "confirmations.block.message": "Estàs segur que vols bloquejar {name}?",
- "confirmations.delete.confirm": "Esborrar",
- "confirmations.delete.message": "Estàs segur que vols esborrar aquest estat?",
- "confirmations.delete_list.confirm": "Delete",
- "confirmations.delete_list.message": "Estàs segur que vols esborrar permanenment aquesta llista?",
- "confirmations.domain_block.confirm": "Amagar tot el domini",
- "confirmations.domain_block.message": "Estàs realment, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar és suficient i preferible.",
- "confirmations.mute.confirm": "Silenciar",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
+ "compose_form.spoiler_placeholder": "Escriu l'avís aquí",
+ "confirmation_modal.cancel": "Cancel·la",
+ "confirmations.block.confirm": "Bloca",
+ "confirmations.block.message": "Estàs segur que vols blocar {name}?",
+ "confirmations.delete.confirm": "Suprimeix",
+ "confirmations.delete.message": "Estàs segur que vols suprimir aquest estat?",
+ "confirmations.delete_list.confirm": "Suprimeix",
+ "confirmations.delete_list.message": "Estàs segur que vols suprimir permanentment aquesta llista?",
+ "confirmations.domain_block.confirm": "Amaga tot el domini",
+ "confirmations.domain_block.message": "Estàs realment, realment segur que vols blocar totalment {domain}? En la majoria dels casos blocar o silenciar uns pocs objectius és suficient i preferible.",
+ "confirmations.mute.confirm": "Silencia",
"confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
- "confirmations.unfollow.confirm": "Deixar de seguir",
+ "confirmations.unfollow.confirm": "Deixa de seguir",
"confirmations.unfollow.message": "Estàs segur que vols deixar de seguir {name}?",
"embed.instructions": "Incrusta aquest estat al lloc web copiant el codi a continuació.",
"embed.preview": "Aquí tenim quin aspecte tindrá:",
"emoji_button.activity": "Activitat",
"emoji_button.custom": "Personalitzat",
- "emoji_button.flags": "Marques",
- "emoji_button.food": "Menjar i Beure",
- "emoji_button.label": "Inserir emoji",
+ "emoji_button.flags": "Banderes",
+ "emoji_button.food": "Menjar i beure",
+ "emoji_button.label": "Insereix un emoji",
"emoji_button.nature": "Natura",
"emoji_button.not_found": "Emojos no!! (╯°□°)╯︵ ┻━┻",
"emoji_button.objects": "Objectes",
"emoji_button.people": "Gent",
- "emoji_button.recent": "Freqüentment utilitzat",
- "emoji_button.search": "Cercar...",
+ "emoji_button.recent": "Usats freqüentment",
+ "emoji_button.search": "Cerca...",
"emoji_button.search_results": "Resultats de la cerca",
"emoji_button.symbols": "Símbols",
"emoji_button.travel": "Viatges i Llocs",
@@ -207,15 +213,22 @@
"relative_time.minutes": "fa {number} minuts",
"relative_time.seconds": "fa {number} segons",
"reply_indicator.cancel": "Cancel·lar",
+ "report.forward": "Reenvia a {target}",
+ "report.forward_hint": "Aquest compte és d'un altre servidor. Enviar-hi també una copia anònima del informe?",
+ "report.hint": "El informe s'enviarà als moderadors de la teva instància. Pots explicar perquè vols informar d'aquest compte aquí:",
"report.placeholder": "Comentaris addicionals",
"report.submit": "Enviar",
"report.target": "Informes",
"search.placeholder": "Cercar",
"search_popout.search_format": "Format de cerca avançada",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "etiqueta",
"search_popout.tips.status": "status",
"search_popout.tips.text": "El text simple retorna coincidències amb els noms de visualització, els noms d'usuari i els hashtags",
"search_popout.tips.user": "usuari",
+ "search_results.accounts": "Gent",
+ "search_results.hashtags": "Etiquetes",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, un {result} altres {results}}",
"standalone.public_title": "Una mirada a l'interior ...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Silenciar conversació",
"status.open": "Ampliar aquest estat",
"status.pin": "Fixat en el perfil",
+ "status.pinned": "Pinned toot",
"status.reblog": "Impuls",
"status.reblogged_by": "{name} ha retootejat",
"status.reply": "Respondre",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Contingut sensible",
"status.share": "Compartir",
"status.show_less": "Mostra menys",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Mostra més",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Activar conversació",
"status.unpin": "Deslliga del perfil",
- "tabs_bar.compose": "Compondre",
"tabs_bar.federated_timeline": "Federada",
"tabs_bar.home": "Inici",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Arrossega i deixa anar per carregar",
"upload_button.label": "Afegir multimèdia",
"upload_form.description": "Descriure els problemes visuals",
+ "upload_form.focus": "Retallar",
"upload_form.undo": "Desfer",
"upload_progress.label": "Pujant...",
"video.close": "Tancar el vídeo",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index aadcfac97..e0fc0ee85 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -1,7 +1,9 @@
{
"account.block": "@{name} blocken",
"account.block_domain": "Alles von {domain} verstecken",
+ "account.blocked": "Blockiert",
"account.disclaimer_full": "Das Profil wird möglicherweise unvollständig wiedergegeben.",
+ "account.domain_blocked": "Domain versteckt",
"account.edit_profile": "Profil bearbeiten",
"account.follow": "Folgen",
"account.followers": "Folgende",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} ist umgezogen auf:",
"account.mute": "@{name} stummschalten",
"account.mute_notifications": "Benachrichtigungen von @{name} verbergen",
+ "account.muted": "Stummgeschaltet",
"account.posts": "Beiträge",
+ "account.posts_with_replies": "Beiträge mit Antworten",
"account.report": "@{name} melden",
"account.requested": "Warte auf Erlaubnis. Klicke zum Abbrechen",
"account.share": "Profil von @{name} teilen",
@@ -36,7 +40,7 @@
"column.favourites": "Favoriten",
"column.follow_requests": "Folgeanfragen",
"column.home": "Startseite",
- "column.lists": "Lists",
+ "column.lists": "Listen",
"column.mutes": "Stummgeschaltete Profile",
"column.notifications": "Mitteilungen",
"column.pins": "Angeheftete Beiträge",
@@ -50,14 +54,16 @@
"column_header.unpin": "Lösen",
"column_subheading.navigation": "Navigation",
"column_subheading.settings": "Einstellungen",
- "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+ "compose_form.hashtag_warning": "Dieser Beitrag wird nicht unter einen dieser Hashtags sichtbar sein, solange er ungelistet ist. Bei einer Suche kann er nicht gefunden werden.",
"compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Wer dir folgen will, kann das jederzeit tun und dann auch deine privaten Beiträge sehen.",
"compose_form.lock_disclaimer.lock": "gesperrt",
"compose_form.placeholder": "Worüber möchtest du schreiben?",
"compose_form.publish": "Tröt",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Medien als heikel markieren",
- "compose_form.spoiler": "Text hinter Warnung verbergen",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Inhaltswarnung",
"confirmation_modal.cancel": "Abbrechen",
"confirmations.block.confirm": "Blockieren",
@@ -65,7 +71,7 @@
"confirmations.delete.confirm": "Löschen",
"confirmations.delete.message": "Bist du dir sicher, dass du diesen Beitrag löschen möchtest?",
"confirmations.delete_list.confirm": "Delete",
- "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+ "confirmations.delete_list.message": "Bist du dir sicher, dass du diese Liste permanent löschen möchtest?",
"confirmations.domain_block.confirm": "Die ganze Domain verbergen",
"confirmations.domain_block.message": "Bist du dir wirklich sicher, dass du die ganze Domain {domain} verbergen willst? In den meisten Fällen reichen ein paar gezielte Blocks aus.",
"confirmations.mute.confirm": "Stummschalten",
@@ -111,35 +117,35 @@
"keyboard_shortcuts.back": "zurück navigieren",
"keyboard_shortcuts.boost": "boosten",
"keyboard_shortcuts.column": "einen Status in einer der Spalten fokussieren",
- "keyboard_shortcuts.compose": "to focus the compose textarea",
- "keyboard_shortcuts.description": "Description",
+ "keyboard_shortcuts.compose": "um das Textfeld zu fokussieren",
+ "keyboard_shortcuts.description": "Beschreibung",
"keyboard_shortcuts.down": "sich in der Liste hinunter bewegen",
- "keyboard_shortcuts.enter": "to open status",
- "keyboard_shortcuts.favourite": "favorisieren",
- "keyboard_shortcuts.heading": "Keyboard Shortcuts",
+ "keyboard_shortcuts.enter": "um den Status zu öffnen",
+ "keyboard_shortcuts.favourite": "um zu favorisieren",
+ "keyboard_shortcuts.heading": "Tastenkombinationen",
"keyboard_shortcuts.hotkey": "Hotkey",
- "keyboard_shortcuts.legend": "diese Übersicht anzeigen",
- "keyboard_shortcuts.mention": "Autor_in erwähnen",
- "keyboard_shortcuts.reply": "antworten",
- "keyboard_shortcuts.search": "die Suche fokussieren",
- "keyboard_shortcuts.toot": "einen neuen Toot beginnen",
- "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+ "keyboard_shortcuts.legend": "um diese Übersicht anzuzeigen",
+ "keyboard_shortcuts.mention": "um Autor_in zu erwähnen",
+ "keyboard_shortcuts.reply": "um zu antworten",
+ "keyboard_shortcuts.search": "um die Suche zu fokussieren",
+ "keyboard_shortcuts.toot": "um einen neuen Toot zu beginnen",
+ "keyboard_shortcuts.unfocus": "um das Textfeld/die Suche nicht mehr zu fokussieren",
"keyboard_shortcuts.up": "sich in der Liste hinauf bewegen",
"lightbox.close": "Schließen",
"lightbox.next": "Weiter",
"lightbox.previous": "Zurück",
- "lists.account.add": "Add to list",
- "lists.account.remove": "Remove from list",
+ "lists.account.add": "Zur Liste hinzufügen",
+ "lists.account.remove": "Von der Liste entfernen",
"lists.delete": "Delete list",
- "lists.edit": "Edit list",
- "lists.new.create": "Add list",
- "lists.new.title_placeholder": "New list title",
- "lists.search": "Search among people you follow",
- "lists.subheading": "Your lists",
+ "lists.edit": "Liste bearbeiten",
+ "lists.new.create": "Liste hinzufügen",
+ "lists.new.title_placeholder": "Neuer Titel der Liste",
+ "lists.search": "Suche nach Leuten denen du folgst",
+ "lists.subheading": "Deine Listen",
"loading_indicator.label": "Wird geladen …",
"media_gallery.toggle_visible": "Sichtbarkeit umschalten",
"missing_indicator.label": "Nicht gefunden",
- "missing_indicator.sublabel": "This resource could not be found",
+ "missing_indicator.sublabel": "Die Ressource konnte nicht gefunden werden",
"mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?",
"navigation_bar.blocks": "Blockierte Profile",
"navigation_bar.community_timeline": "Lokale Zeitleiste",
@@ -148,7 +154,7 @@
"navigation_bar.follow_requests": "Folgeanfragen",
"navigation_bar.info": "Über diese Instanz",
"navigation_bar.keyboard_shortcuts": "Tastenkombinationen",
- "navigation_bar.lists": "Lists",
+ "navigation_bar.lists": "Listen",
"navigation_bar.logout": "Abmelden",
"navigation_bar.mutes": "Stummgeschaltete Profile",
"navigation_bar.pins": "Angeheftete Beiträge",
@@ -171,7 +177,7 @@
"notifications.column_settings.sound": "Ton abspielen",
"onboarding.done": "Fertig",
"onboarding.next": "Weiter",
- "onboarding.page_five.public_timelines": "Die lokale Zeitleiste zeigt alle Beiträge von Leuten, die auch auf {domain} sind. Das gesamte bekannte Netz zeigt Beiträge von allen, denen von Leuten auf {domain} gefolgt wird. Zusammen sind sie die öffentlichen Zeitleisten. In ihnen kannst du viel Neues entdecken!",
+ "onboarding.page_five.public_timelines": "Die lokale Zeitleiste zeigt alle Beiträge von Leuten, die auch auf {domain} sind. Das gesamte bekannte Netz zeigt Beiträge von allen, denen von Leuten auf {domain} gefolgt wird. Zusammen sind sie die öffentlichen Zeitleisten, ein guter Weg, um neue Leute zu finden.",
"onboarding.page_four.home": "Die Startseite zeigt dir Beiträge von Leuten, denen du folgst.",
"onboarding.page_four.notifications": "Wenn jemand mit dir interagiert, bekommst du eine Mitteilung.",
"onboarding.page_one.federation": "Mastodon ist ein soziales Netzwerk, das aus unabhängigen Servern besteht. Diese Server nennen wir auch Instanzen.",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Abbrechen",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Zusätzliche Kommentare",
"report.submit": "Absenden",
"report.target": "{target} melden",
"search.placeholder": "Suche",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}",
"standalone.public_title": "Ein kleiner Einblick …",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Thread stummschalten",
"status.open": "Diesen Beitrag öffnen",
"status.pin": "Im Profil anheften",
+ "status.pinned": "Pinned toot",
"status.reblog": "Teilen",
"status.reblogged_by": "{name} teilte",
"status.reply": "Antworten",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Heikle Inhalte",
"status.share": "Teilen",
"status.show_less": "Weniger anzeigen",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Mehr anzeigen",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Stummschaltung von Thread aufheben",
"status.unpin": "Vom Profil lösen",
- "tabs_bar.compose": "Schreiben",
"tabs_bar.federated_timeline": "Föderation",
"tabs_bar.home": "Startseite",
"tabs_bar.local_timeline": "Lokal",
@@ -252,6 +267,7 @@
"upload_area.title": "Zum Hochladen hereinziehen",
"upload_button.label": "Mediendatei hinzufügen",
"upload_form.description": "Für Menschen mit Sehbehinderung beschreiben",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Entfernen",
"upload_progress.label": "Wird hochgeladen …",
"video.close": "Video schließen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 9a46927c1..d5b9c09cb 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -274,6 +274,10 @@
},
{
"descriptors": [
+ {
+ "defaultMessage": "Pinned toot",
+ "id": "status.pinned"
+ },
{
"defaultMessage": "{name} boosted",
"id": "status.reblogged_by"
@@ -317,12 +321,20 @@
},
{
"descriptors": [
+ {
+ "defaultMessage": "Toots",
+ "id": "account.posts"
+ },
+ {
+ "defaultMessage": "Toots with replies",
+ "id": "account.posts_with_replies"
+ },
{
"defaultMessage": "Media",
"id": "account.media"
}
],
- "path": "app/javascript/mastodon/features/account_gallery/index.json"
+ "path": "app/javascript/mastodon/features/account_timeline/components/header.json"
},
{
"descriptors": [
@@ -433,7 +445,7 @@
"id": "account.view_full_profile"
},
{
- "defaultMessage": "Posts",
+ "defaultMessage": "Toots",
"id": "account.posts"
},
{
@@ -461,9 +473,25 @@
"defaultMessage": "Awaiting approval. Click to cancel follow request",
"id": "account.requested"
},
+ {
+ "defaultMessage": "Unblock @{name}",
+ "id": "account.unblock"
+ },
{
"defaultMessage": "Follows you",
"id": "account.follows_you"
+ },
+ {
+ "defaultMessage": "Blocked",
+ "id": "account.blocked"
+ },
+ {
+ "defaultMessage": "Muted",
+ "id": "account.muted"
+ },
+ {
+ "defaultMessage": "Domain hidden",
+ "id": "account.domain_blocked"
}
],
"path": "app/javascript/mastodon/features/account/components/header.json"
@@ -650,6 +678,18 @@
},
{
"descriptors": [
+ {
+ "defaultMessage": "People",
+ "id": "search_results.accounts"
+ },
+ {
+ "defaultMessage": "Toots",
+ "id": "search_results.statuses"
+ },
+ {
+ "defaultMessage": "Hashtags",
+ "id": "search_results.hashtags"
+ },
{
"defaultMessage": "{count, number} {count, plural, one {result} other {results}}",
"id": "search_results.total"
@@ -663,6 +703,14 @@
"defaultMessage": "Search",
"id": "search.placeholder"
},
+ {
+ "defaultMessage": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+ "id": "search_popout.tips.full_text"
+ },
+ {
+ "defaultMessage": "Simple text returns matching display names, usernames and hashtags",
+ "id": "search_popout.tips.text"
+ },
{
"defaultMessage": "Advanced search format",
"id": "search_popout.search_format"
@@ -678,10 +726,6 @@
{
"defaultMessage": "status",
"id": "search_popout.tips.status"
- },
- {
- "defaultMessage": "Simple text returns matching display names, usernames and hashtags",
- "id": "search_popout.tips.text"
}
],
"path": "app/javascript/mastodon/features/compose/components/search.json"
@@ -706,13 +750,17 @@
},
{
"descriptors": [
+ {
+ "defaultMessage": "Describe for the visually impaired",
+ "id": "upload_form.description"
+ },
{
"defaultMessage": "Undo",
"id": "upload_form.undo"
},
{
- "defaultMessage": "Describe for the visually impaired",
- "id": "upload_form.description"
+ "defaultMessage": "Crop",
+ "id": "upload_form.focus"
}
],
"path": "app/javascript/mastodon/features/compose/components/upload.json"
@@ -720,8 +768,12 @@
{
"descriptors": [
{
- "defaultMessage": "Mark media as sensitive",
- "id": "compose_form.sensitive"
+ "defaultMessage": "Media is marked as sensitive",
+ "id": "compose_form.sensitive.marked"
+ },
+ {
+ "defaultMessage": "Media is not marked as sensitive",
+ "id": "compose_form.sensitive.unmarked"
}
],
"path": "app/javascript/mastodon/features/compose/containers/sensitive_button_container.json"
@@ -729,8 +781,12 @@
{
"descriptors": [
{
- "defaultMessage": "Hide text behind warning",
- "id": "compose_form.spoiler"
+ "defaultMessage": "Text is hidden behind warning",
+ "id": "compose_form.spoiler.marked"
+ },
+ {
+ "defaultMessage": "Text is not hidden",
+ "id": "compose_form.spoiler.unmarked"
}
],
"path": "app/javascript/mastodon/features/compose/containers/spoiler_button_container.json"
@@ -1230,6 +1286,15 @@
],
"path": "app/javascript/mastodon/features/public_timeline/index.json"
},
+ {
+ "descriptors": [
+ {
+ "defaultMessage": "A look inside...",
+ "id": "standalone.public_title"
+ }
+ ],
+ "path": "app/javascript/mastodon/features/standalone/community_timeline/index.json"
+ },
{
"descriptors": [
{
@@ -1318,6 +1383,14 @@
"defaultMessage": "Block",
"id": "confirmations.block.confirm"
},
+ {
+ "defaultMessage": "Show more for all",
+ "id": "status.show_more_all"
+ },
+ {
+ "defaultMessage": "Show less for all",
+ "id": "status.show_less_all"
+ },
{
"defaultMessage": "Are you sure you want to block {name}?",
"id": "confirmations.block.message"
@@ -1543,6 +1616,10 @@
},
{
"descriptors": [
+ {
+ "defaultMessage": "Close",
+ "id": "lightbox.close"
+ },
{
"defaultMessage": "Additional comments",
"id": "report.placeholder"
@@ -1554,16 +1631,24 @@
{
"defaultMessage": "Report {target}",
"id": "report.target"
+ },
+ {
+ "defaultMessage": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
+ "id": "report.hint"
+ },
+ {
+ "defaultMessage": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "id": "report.forward_hint"
+ },
+ {
+ "defaultMessage": "Forward to {target}",
+ "id": "report.forward"
}
],
"path": "app/javascript/mastodon/features/ui/components/report_modal.json"
},
{
"descriptors": [
- {
- "defaultMessage": "Compose",
- "id": "tabs_bar.compose"
- },
{
"defaultMessage": "Home",
"id": "tabs_bar.home"
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 1e952b7b7..d0d863f79 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -1,7 +1,9 @@
{
"account.block": "Block @{name}",
"account.block_domain": "Hide everything from {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Edit profile",
"account.follow": "Follow",
"account.followers": "Followers",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Mute @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
- "account.posts": "Posts",
+ "account.muted": "Muted",
+ "account.posts": "Toots",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Report @{name}",
"account.requested": "Awaiting approval. Click to cancel follow request",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "What is on your mind?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Mark media as sensitive",
- "compose_form.spoiler": "Hide text behind warning",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Write your warning here",
"confirmation_modal.cancel": "Cancel",
"confirmations.block.confirm": "Block",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Cancel",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Additional comments",
"report.submit": "Submit",
"report.target": "Reporting {target}",
"search.placeholder": "Search",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Boost",
"status.reblogged_by": "{name} boosted",
"status.reply": "Reply",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Sensitive content",
"status.share": "Share",
"status.show_less": "Show less",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Show more",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Compose",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Home",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Undo",
"upload_progress.label": "Uploading...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index cf0b4f2ec..fd687e8b1 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -1,236 +1,250 @@
{
"account.block": "Bloki @{name}",
- "account.block_domain": "Kaŝi ĉion el {domain}",
- "account.disclaimer_full": "La ĉi-subaj informoj povas ne plene reflekti la profilon de la uzanto.",
- "account.edit_profile": "Redakti la profilon",
+ "account.block_domain": "Kaŝi ĉion de {domain}",
+ "account.blocked": "Blokita",
+ "account.disclaimer_full": "Subaj informoj povas reflekti la profilon de la uzanto nekomplete.",
+ "account.domain_blocked": "Domajno kaŝita",
+ "account.edit_profile": "Redakti profilon",
"account.follow": "Sekvi",
"account.followers": "Sekvantoj",
"account.follows": "Sekvatoj",
"account.follows_you": "Sekvas vin",
- "account.hide_reblogs": "Maski diskonigitaĵojn de @{name}",
- "account.media": "Sonbildaĵoj",
+ "account.hide_reblogs": "Kaŝi diskonigojn de @{name}",
+ "account.media": "Aŭdovidaĵoj",
"account.mention": "Mencii @{name}",
- "account.moved_to": "{name} movis al:",
+ "account.moved_to": "{name} moviĝis al:",
"account.mute": "Silentigi @{name}",
"account.mute_notifications": "Silentigi sciigojn el @{name}",
+ "account.muted": "Silentigita",
"account.posts": "Mesaĝoj",
+ "account.posts_with_replies": "Mesaĝoj kun respondoj",
"account.report": "Signali @{name}",
- "account.requested": "Atendas aprobon",
+ "account.requested": "Atendo de aprobo. Alklaku por nuligi peton de sekvado",
"account.share": "Diskonigi la profilon de @{name}",
- "account.show_reblogs": "Montri diskonigaĵojn de @{name}",
+ "account.show_reblogs": "Montri diskonigojn de @{name}",
"account.unblock": "Malbloki @{name}",
"account.unblock_domain": "Malkaŝi {domain}",
- "account.unfollow": "Ne plus sekvi",
+ "account.unfollow": "Ne plu sekvi",
"account.unmute": "Malsilentigi @{name}",
"account.unmute_notifications": "Malsilentigi sciigojn de @{name}",
"account.view_full_profile": "Vidi plenan profilon",
- "boost_modal.combo": "La proksiman fojon, premu {combo} por pasigi",
- "bundle_column_error.body": "Io malfunkciis ŝargante tiun ĉi komponanton.",
+ "boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje",
+ "bundle_column_error.body": "Io misfunkciis en la ŝargado de ĉi tiu elemento.",
"bundle_column_error.retry": "Bonvolu reprovi",
"bundle_column_error.title": "Reta eraro",
"bundle_modal_error.close": "Fermi",
- "bundle_modal_error.message": "Io malfunkciis ŝargante tiun ĉi komponanton.",
+ "bundle_modal_error.message": "Io misfunkciis en la ŝargado de ĉi tiu elemento.",
"bundle_modal_error.retry": "Bonvolu reprovi",
"column.blocks": "Blokitaj uzantoj",
"column.community": "Loka tempolinio",
- "column.favourites": "Favoritoj",
- "column.follow_requests": "Abonpetoj",
+ "column.favourites": "Stelumoj",
+ "column.follow_requests": "Petoj de sekvado",
"column.home": "Hejmo",
"column.lists": "Listoj",
"column.mutes": "Silentigitaj uzantoj",
"column.notifications": "Sciigoj",
- "column.pins": "Alpinglitaj pepoj",
+ "column.pins": "Alpinglitaj mesaĝoj",
"column.public": "Fratara tempolinio",
"column_back_button.label": "Reveni",
"column_header.hide_settings": "Kaŝi agordojn",
"column_header.moveLeft_settings": "Movi kolumnon maldekstren",
"column_header.moveRight_settings": "Movi kolumnon dekstren",
"column_header.pin": "Alpingli",
- "column_header.show_settings": "Malkaŝi agordojn",
+ "column_header.show_settings": "Montri agordojn",
"column_header.unpin": "Depingli",
"column_subheading.navigation": "Navigado",
- "column_subheading.settings": "Agordoj",
- "compose_form.hashtag_warning": "Ĉi tiu pepo ne estos listigita en iu ajn kradvorta listo pro ĝia videbleco estas “eksterlista”. Nur publikaj pepoj povas esti kradvorte trovitaj.",
- "compose_form.lock_disclaimer": "Via konta ne estas ŝlosita. Iu ajn povas sekvi vin por vidi viajn privatajn pepojn.",
+ "column_subheading.settings": "Agordado",
+ "compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.",
+ "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn nur por sekvantoj.",
"compose_form.lock_disclaimer.lock": "ŝlosita",
"compose_form.placeholder": "Pri kio vi pensas?",
"compose_form.publish": "Hup",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Marki ke la enhavo estas tikla",
- "compose_form.spoiler": "Kaŝi la tekston malantaŭ averto",
- "compose_form.spoiler_placeholder": "Skribu tie vian averton",
- "confirmation_modal.cancel": "Malfari",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
+ "compose_form.spoiler_placeholder": "Skribu vian averton ĉi tie",
+ "confirmation_modal.cancel": "Nuligi",
"confirmations.block.confirm": "Bloki",
- "confirmations.block.message": "Ĉu vi konfirmas la blokadon de {name}?",
- "confirmations.delete.confirm": "Malaperigi",
- "confirmations.delete.message": "Ĉu vi konfirmas la malaperigon de tiun pepon?",
- "confirmations.delete_list.confirm": "Delete",
- "confirmations.delete_list.message": "Ĉu vi certas forviŝi ĉi tiun liston por ĉiam?",
- "confirmations.domain_block.confirm": "Kaŝi la tutan reton",
- "confirmations.domain_block.message": "Ĉu vi vere, vere certas, ke vi volas bloki {domain} tute? Plej ofte, kelkaj celitaj blokadoj aŭ silentigoj estas sufiĉaj kaj preferindaj.",
+ "confirmations.block.message": "Ĉu vi certas, ke vi volas bloki {name}?",
+ "confirmations.delete.confirm": "Forigi",
+ "confirmations.delete.message": "Ĉu vi certas, ke vi volas forigi ĉi tiun mesaĝon?",
+ "confirmations.delete_list.confirm": "Forigi",
+ "confirmations.delete_list.message": "Ĉu vi certas, ke vi volas porĉiame forigi ĉi tiun liston?",
+ "confirmations.domain_block.confirm": "Kaŝi la tutan domajnon",
+ "confirmations.domain_block.message": "Ĉu vi vere, vere certas, ke vi volas tute bloki {domain}? Plej ofte, trafa blokado kaj silentigado sufiĉas kaj preferindas.",
"confirmations.mute.confirm": "Silentigi",
- "confirmations.mute.message": "Ĉu vi konfirmas la silentigon de {name}?",
+ "confirmations.mute.message": "Ĉu vi certas, ke vi volas silentigi {name}?",
"confirmations.unfollow.confirm": "Ne plu sekvi",
- "confirmations.unfollow.message": "Ĉu vi volas ĉesi sekvi {name}?",
- "embed.instructions": "Enmetu tiun statkonigon ĉe vian retejon kopiante la ĉi-suban kodon.",
+ "confirmations.unfollow.message": "Ĉu vi certas, ke vi volas ĉesi sekvi {name}?",
+ "embed.instructions": "Enkorpigu ĉi tiun mesaĝon en vian retejon per kopio de la suba kodo.",
"embed.preview": "Ĝi aperos tiel:",
- "emoji_button.activity": "Aktivecoj",
- "emoji_button.custom": "Personaj",
+ "emoji_button.activity": "Agadoj",
+ "emoji_button.custom": "Propraj",
"emoji_button.flags": "Flagoj",
"emoji_button.food": "Manĝi kaj trinki",
- "emoji_button.label": "Enmeti mieneton",
+ "emoji_button.label": "Enmeti emoĝion",
"emoji_button.nature": "Naturo",
- "emoji_button.not_found": "Neniuj mienetoj!! (╯°□°)╯︵ ┻━┻",
- "emoji_button.objects": "Objektoj",
+ "emoji_button.not_found": "Neniu emoĝio!! (╯°□°)╯︵ ┻━┻",
+ "emoji_button.objects": "Aĵoj",
"emoji_button.people": "Homoj",
"emoji_button.recent": "Ofte uzataj",
"emoji_button.search": "Serĉo…",
- "emoji_button.search_results": "Rezultatoj de serĉo",
+ "emoji_button.search_results": "Serĉaj rezultoj",
"emoji_button.symbols": "Simboloj",
- "emoji_button.travel": "Vojaĝoj & lokoj",
+ "emoji_button.travel": "Vojaĝoj kaj lokoj",
"empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!",
- "empty_column.hashtag": "Ĝise, neniu enhavo estas asociita kun tiu kradvorto.",
+ "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
"empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.",
"empty_column.home.public_timeline": "la publika tempolinio",
- "empty_column.list": "Estas ankoraŭ nenio en ĉi tiu listo. Tuj kiam anoj de ĉi tiu listo publikigos, ties pepoj aperos ĉi tie.",
- "empty_column.notifications": "Vi dume ne havas sciigojn. Interagi kun aliajn uzantojn por komenci la konversacion.",
- "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj instancoj por plenigi la publikan tempolinion",
- "follow_request.authorize": "Akcepti",
+ "empty_column.list": "Ankoraŭ estas nenio en ĉi tiu listo. Kiam membroj de ĉi tiu listo afiŝos novajn mesaĝojn, ili aperos ĉi tie.",
+ "empty_column.notifications": "Vi ankoraŭ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.",
+ "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj nodoj por plenigi la publikan tempolinion",
+ "follow_request.authorize": "Rajtigi",
"follow_request.reject": "Rifuzi",
"getting_started.appsshort": "Aplikaĵoj",
"getting_started.faq": "Oftaj demandoj",
"getting_started.heading": "Por komenci",
- "getting_started.open_source_notice": "Mastodono estas malfermkoda programo. Vi povas kontribui aŭ raporti problemojn en GitHub je {github}.",
+ "getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en GitHub je {github}.",
"getting_started.userguide": "Gvidilo de uzo",
"home.column_settings.advanced": "Precizaj agordoj",
"home.column_settings.basic": "Bazaj agordoj",
- "home.column_settings.filter_regex": "Forfiltri per regulesprimo",
+ "home.column_settings.filter_regex": "Filtri per regulesprimoj",
"home.column_settings.show_reblogs": "Montri diskonigojn",
"home.column_settings.show_replies": "Montri respondojn",
- "home.settings": "Agordoj de la kolumno",
- "keyboard_shortcuts.back": "reeniri",
- "keyboard_shortcuts.boost": "diskonigi",
- "keyboard_shortcuts.column": "fokusigi statuson en unu el la columnoj",
- "keyboard_shortcuts.compose": "por fokusigi la redaktujon",
- "keyboard_shortcuts.description": "Description",
- "keyboard_shortcuts.down": "subenmovi en la listo",
- "keyboard_shortcuts.enter": "to open status",
- "keyboard_shortcuts.favourite": "ŝatitaren",
- "keyboard_shortcuts.heading": "Keyboard Shortcuts",
+ "home.settings": "Kolumnaj agordoj",
+ "keyboard_shortcuts.back": "por reveni",
+ "keyboard_shortcuts.boost": "por diskonigi",
+ "keyboard_shortcuts.column": "por fokusigi mesaĝon en unu el la kolumnoj",
+ "keyboard_shortcuts.compose": "por fokusigi la tekstujon",
+ "keyboard_shortcuts.description": "Priskribo",
+ "keyboard_shortcuts.down": "por iri suben en la listo",
+ "keyboard_shortcuts.enter": "por malfermi mesaĝon",
+ "keyboard_shortcuts.favourite": "por stelumi",
+ "keyboard_shortcuts.heading": "Klavaraj mallongigoj",
"keyboard_shortcuts.hotkey": "Rapidklavo",
- "keyboard_shortcuts.legend": "por montri ĉi tiun legendon",
- "keyboard_shortcuts.mention": "por sciigi ties aŭtoron",
+ "keyboard_shortcuts.legend": "por montri ĉi tiun noton",
+ "keyboard_shortcuts.mention": "por mencii la aŭtoron",
"keyboard_shortcuts.reply": "por respondi",
- "keyboard_shortcuts.search": "por fokusigi la serĉadon",
- "keyboard_shortcuts.toot": "por ekredakti tute novan pepon",
- "keyboard_shortcuts.unfocus": "por malfokusigi la redaktujon aŭ la serĉilon",
- "keyboard_shortcuts.up": "por suprenmovi en la listo",
+ "keyboard_shortcuts.search": "por fokusigi la serĉilon",
+ "keyboard_shortcuts.toot": "por komenci tute novan mesaĝon",
+ "keyboard_shortcuts.unfocus": "por malfokusigi la tekstujon aŭ la serĉilon",
+ "keyboard_shortcuts.up": "por iri supren en la listo",
"lightbox.close": "Fermi",
- "lightbox.next": "Malantaŭa",
+ "lightbox.next": "Sekva",
"lightbox.previous": "Antaŭa",
"lists.account.add": "Aldoni al la listo",
- "lists.account.remove": "Forviŝi de la listo",
- "lists.delete": "Delete list",
+ "lists.account.remove": "Forigi de la listo",
+ "lists.delete": "Forigi la liston",
"lists.edit": "Redakti la liston",
"lists.new.create": "Aldoni liston",
- "lists.new.title_placeholder": "Titulo de la nova listo",
- "lists.search": "Serĉi el la homoj kiujn vi sekvas",
+ "lists.new.title_placeholder": "Titolo de la nova listo",
+ "lists.search": "Serĉi inter la homoj, kiujn vi sekvas",
"lists.subheading": "Viaj listoj",
- "loading_indicator.label": "Ŝarganta…",
- "media_gallery.toggle_visible": "Baskuli videblecon",
+ "loading_indicator.label": "Ŝargado…",
+ "media_gallery.toggle_visible": "Baskuligi videblecon",
"missing_indicator.label": "Ne trovita",
- "missing_indicator.sublabel": "Ĉi tiu rimedo ne troviĝis",
- "mute_modal.hide_notifications": "Ĉu kaŝi sciigojn el tiu ĉi uzanto?",
+ "missing_indicator.sublabel": "Ĉi tiu rimedo ne estis trovita",
+ "mute_modal.hide_notifications": "Ĉu kaŝi sciigojn el ĉi tiu uzanto?",
"navigation_bar.blocks": "Blokitaj uzantoj",
"navigation_bar.community_timeline": "Loka tempolinio",
- "navigation_bar.edit_profile": "Redakti la profilon",
- "navigation_bar.favourites": "Favoritaj",
- "navigation_bar.follow_requests": "Abonpetoj",
- "navigation_bar.info": "Plia informo",
- "navigation_bar.keyboard_shortcuts": "Klavmallongigo",
+ "navigation_bar.edit_profile": "Redakti profilon",
+ "navigation_bar.favourites": "Stelumoj",
+ "navigation_bar.follow_requests": "Petoj de sekvado",
+ "navigation_bar.info": "Pri ĉi tiu nodo",
+ "navigation_bar.keyboard_shortcuts": "Klavaraj mallongigoj",
"navigation_bar.lists": "Listoj",
"navigation_bar.logout": "Elsaluti",
"navigation_bar.mutes": "Silentigitaj uzantoj",
- "navigation_bar.pins": "Alpinglitaj pepoj",
+ "navigation_bar.pins": "Alpinglitaj mesaĝoj",
"navigation_bar.preferences": "Preferoj",
"navigation_bar.public_timeline": "Fratara tempolinio",
- "notification.favourite": "{name} favoris vian mesaĝon",
- "notification.follow": "{name} sekvis vin",
+ "notification.favourite": "{name} stelumis vian mesaĝon",
+ "notification.follow": "{name} eksekvis vin",
"notification.mention": "{name} menciis vin",
"notification.reblog": "{name} diskonigis vian mesaĝon",
- "notifications.clear": "Forviŝi la sciigojn",
- "notifications.clear_confirmation": "Ĉu vi certe volas malaperigi ĉiujn viajn sciigojn?",
- "notifications.column_settings.alert": "Retumilaj atentigoj",
- "notifications.column_settings.favourite": "Favoritoj:",
+ "notifications.clear": "Forviŝi sciigojn",
+ "notifications.clear_confirmation": "Ĉu vi certas, ke vi volas porĉiame forviŝi ĉiujn viajn sciigojn?",
+ "notifications.column_settings.alert": "Retumilaj sciigoj",
+ "notifications.column_settings.favourite": "Stelumoj:",
"notifications.column_settings.follow": "Novaj sekvantoj:",
"notifications.column_settings.mention": "Mencioj:",
"notifications.column_settings.push": "Puŝsciigoj",
- "notifications.column_settings.push_meta": "Tiu ĉi aparato",
+ "notifications.column_settings.push_meta": "Ĉi tiu aparato",
"notifications.column_settings.reblog": "Diskonigoj:",
- "notifications.column_settings.show": "Montri en kolono",
+ "notifications.column_settings.show": "Montri en kolumno",
"notifications.column_settings.sound": "Eligi sonon",
"onboarding.done": "Farita",
- "onboarding.next": "Malantaŭa",
- "onboarding.page_five.public_timelines": "La loka tempolinio enhavas mesaĝojn de ĉiuj ĉe {domain}. La federacia tempolinio enhavas ĉiujn mesaĝojn de uzantoj, kiujn iu ĉe {domain} sekvas. Ambaŭ tre utilas por trovi novajn kunparolantojn.",
- "onboarding.page_four.home": "La hejma tempolinio enhavas la mesaĝojn de ĉiuj uzantoj, kiuj vi sekvas.",
- "onboarding.page_four.notifications": "La sciiga kolumno informas vin kiam iu interagas kun vi.",
- "onboarding.page_one.federation": "Mastodono estas reto de nedependaj serviloj, unuiĝintaj por krei pligrandan socian retejon. Ni nomas tiujn servilojn instancoj.",
- "onboarding.page_one.full_handle": "Via tuta uzantnomo",
- "onboarding.page_one.handle_hint": "Jen kion vi dirintus al viaj amikoj por serĉi.",
- "onboarding.page_one.welcome": "Bonvenon al Mastodono!",
- "onboarding.page_six.admin": "Via instancestro estas {admin}.",
- "onboarding.page_six.almost_done": "Estas preskaŭ finita…",
- "onboarding.page_six.appetoot": "Bonan a‘pepi’ton!",
- "onboarding.page_six.apps_available": "{apps} estas elŝuteblaj por iOS, Androido kaj alioj.",
- "onboarding.page_six.github": "Mastodono estas libera, senpaga kaj malfermkoda programaro. Vi povas signali cimojn, proponi funkciojn aŭ kontribui al gîa kreskado ĉe {github}.",
- "onboarding.page_six.guidelines": "komunreguloj",
- "onboarding.page_six.read_guidelines": "Ni petas vin: ne forgesu legi la {guidelines}n de {domain}!",
- "onboarding.page_six.various_app": "telefon-aplikaĵoj",
- "onboarding.page_three.profile": "Redaktu vian profilon por ŝanĝi vian avataron, priskribon kaj vian nomon. Vi tie trovos ankoraŭ aliajn agordojn.",
- "onboarding.page_three.search": "Uzu la serĉokampo por trovi uzantojn kaj esplori kradvortojn tiel ke {illustration} kaj {introductions}. Por trovi iun, kiu ne estas ĉe ĉi tiu instanco, uzu ĝian kompletan uznomon.",
- "onboarding.page_two.compose": "Skribu pepojn en la verkkolumno. Vi povas aldoni bildojn, ŝanĝi la agordojn de privateco kaj aldoni tiklavertojn (« content warning ») dank' al la piktogramoj malsupre.",
- "onboarding.skip": "Pasigi",
- "privacy.change": "Alĝustigi la privateco de la mesaĝo",
- "privacy.direct.long": "Vidigi nur al la menciitaj personoj",
+ "onboarding.next": "Sekva",
+ "onboarding.page_five.public_timelines": "La loka tempolinio montras publikajn mesaĝojn de ĉiuj en {domain}. La fratara tempolinio montras publikajn mesaĝojn de ĉiuj, kiuj estas sekvataj de homoj en {domain}. Tio estas la publikaj tempolinioj, kio estas bona maniero por malkovri novajn homojn.",
+ "onboarding.page_four.home": "La hejma tempolinio montras mesaĝojn de ĉiuj uzantoj, kiujn vi sekvas.",
+ "onboarding.page_four.notifications": "La sciiga kolumno montras kiam iu interagas kun vi.",
+ "onboarding.page_one.federation": "Mastodon estas reto de sendependaj serviloj, unuiĝintaj por krei pligrandan socian reton. Ni nomas tiujn servilojn nodoj.",
+ "onboarding.page_one.full_handle": "Via kompleta uzantnomo",
+ "onboarding.page_one.handle_hint": "Jen kion vi petus al viaj amikoj serĉi.",
+ "onboarding.page_one.welcome": "Bonvenon en Mastodon!",
+ "onboarding.page_six.admin": "Via noda administranto estas {admin}.",
+ "onboarding.page_six.almost_done": "Preskaŭ finita…",
+ "onboarding.page_six.appetoot": "Saĝan mesaĝadon!",
+ "onboarding.page_six.apps_available": "{apps} estas disponeblaj por iOS, Android kaj aliaj platformoj.",
+ "onboarding.page_six.github": "Mastodon estas libera, senpaga kaj malfermitkoda programo. Vi povas raporti cimojn, proponi funkciojn aŭ kontribui al la kodo en {github}.",
+ "onboarding.page_six.guidelines": "komunumaj gvidlinioj",
+ "onboarding.page_six.read_guidelines": "Bonvolu atenti pri la {guidelines} de {domain}!",
+ "onboarding.page_six.various_app": "telefonaj aplikaĵoj",
+ "onboarding.page_three.profile": "Redaktu vian profilon por ŝanĝi vian profilbildon, priskribon kaj nomon. Vi ankaŭ trovos tie aliajn agordojn.",
+ "onboarding.page_three.search": "Uzu la serĉilon por trovi uzantojn kaj esplori kradvortojn, tiel {illustration} kaj {introductions}. Por trovi iun, kiu ne estas en ĉi tiu nodo, uzu ties kompletan uzantnomon.",
+ "onboarding.page_two.compose": "Skribu mesaĝojn en la skriba kolumno. Vi povas alŝuti bildojn, ŝanĝi privatecajn agordojn, kaj aldoni avertojn pri la enhavo per la subaj bildetoj.",
+ "onboarding.skip": "Preterpasi",
+ "privacy.change": "Agordi mesaĝan privatecon",
+ "privacy.direct.long": "Afiŝi nur al menciitaj uzantoj",
"privacy.direct.short": "Rekta",
- "privacy.private.long": "Vidigi nur al viaj sekvantoj",
- "privacy.private.short": "Nursekvanta",
- "privacy.public.long": "Vidigi en publikaj tempolinioj",
+ "privacy.private.long": "Afiŝi nur al sekvantoj",
+ "privacy.private.short": "Nur por sekvantoj",
+ "privacy.public.long": "Afiŝi en publikaj tempolinioj",
"privacy.public.short": "Publika",
- "privacy.unlisted.long": "Ne vidigi en publikaj tempolinioj",
+ "privacy.unlisted.long": "Ne afiŝi en publikaj tempolinioj",
"privacy.unlisted.short": "Nelistigita",
- "regeneration_indicator.label": "Elŝultanta…",
- "regeneration_indicator.sublabel": "Via ĉefpaĝo estas preparanta!",
+ "regeneration_indicator.label": "Ŝargado…",
+ "regeneration_indicator.sublabel": "Via hejma fluo pretiĝas!",
"relative_time.days": "{number}t",
"relative_time.hours": "{number}h",
"relative_time.just_now": "nun",
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
- "reply_indicator.cancel": "Malfari",
+ "reply_indicator.cancel": "Nuligi",
+ "report.forward": "Plusendi al {target}",
+ "report.forward_hint": "La konto estas en alia servilo. Ĉu sendi sennomigitan kopion de la signalo ankaŭ tien?",
+ "report.hint": "La signalo estos sendita al la kontrolantoj de via nodo. Vi povas doni klarigon pri kial vi signalas ĉi tiun konton sube:",
"report.placeholder": "Pliaj komentoj",
"report.submit": "Sendi",
- "report.target": "Signalaĵo",
+ "report.target": "Signali {target}",
"search.placeholder": "Serĉi",
"search_popout.search_format": "Detala serĉo",
+ "search_popout.tips.full_text": "Simplaj tekstoj montras la mesaĝojn, kiujn vi skribis, stelumis, diskonigis, aŭ en kiuj vi estis menciita, sed ankaŭ kongruajn uzantnomojn, montratajn nomojn, kaj kradvortojn.",
"search_popout.tips.hashtag": "kradvorto",
- "search_popout.tips.status": "statkonigo",
- "search_popout.tips.text": "Simpla teksto eligas la kongruajn afiŝnomojn, uznomojn kaj kradvortojn",
+ "search_popout.tips.status": "mesaĝoj",
+ "search_popout.tips.text": "Simpla teksto montras la kongruajn afiŝitajn nomojn, uzantnomojn kaj kradvortojn",
"search_popout.tips.user": "uzanto",
- "search_results.total": "{count, number} {count, plural, one {rezultato} other {rezultatoj}}",
- "standalone.public_title": "Rigardeti…",
- "status.block": "Block @{name}",
- "status.cannot_reblog": "Tiun publikaĵon oni ne povas diskonigi",
+ "search_results.accounts": "Homoj",
+ "search_results.hashtags": "Kradvortoj",
+ "search_results.statuses": "Mesaĝoj",
+ "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}",
+ "standalone.public_title": "Enrigardo…",
+ "status.block": "Bloki @{name}",
+ "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas",
"status.delete": "Forigi",
- "status.embed": "Enmeti",
- "status.favourite": "Favori",
- "status.load_more": "Ŝargi plie",
- "status.media_hidden": "Sonbildaĵo kaŝita",
+ "status.embed": "Enkorpigi",
+ "status.favourite": "Stelumi",
+ "status.load_more": "Ŝargi pli",
+ "status.media_hidden": "Aŭdovidaĵo kaŝita",
"status.mention": "Mencii @{name}",
"status.more": "Pli",
"status.mute": "Silentigi @{name}",
"status.mute_conversation": "Silentigi konversacion",
- "status.open": "Disfaldi statkonigon",
- "status.pin": "Pingli al la profilo",
+ "status.open": "Grandigi ĉi tiun mesaĝon",
+ "status.pin": "Alpingli en la profilo",
+ "status.pinned": "Alpinglita mesaĝo",
"status.reblog": "Diskonigi",
"status.reblogged_by": "{name} diskonigis",
"status.reply": "Respondi",
@@ -239,28 +253,30 @@
"status.sensitive_toggle": "Alklaki por vidi",
"status.sensitive_warning": "Tikla enhavo",
"status.share": "Diskonigi",
- "status.show_less": "Refaldi",
- "status.show_more": "Disfaldi",
+ "status.show_less": "Malgrandigi",
+ "status.show_less_all": "Show less for all",
+ "status.show_more": "Grandigi",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Malsilentigi konversacion",
"status.unpin": "Depingli de profilo",
- "tabs_bar.compose": "Ekskribi",
- "tabs_bar.federated_timeline": "Federacia tempolinio",
+ "tabs_bar.federated_timeline": "Fratara tempolinio",
"tabs_bar.home": "Hejmo",
"tabs_bar.local_timeline": "Loka tempolinio",
"tabs_bar.notifications": "Sciigoj",
"ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.",
- "upload_area.title": "Algliti por alŝuti",
- "upload_button.label": "Aldoni sonbildaĵon",
- "upload_form.description": "Priskribi por la misvidantaj",
+ "upload_area.title": "Altreni kaj lasi por alŝuti",
+ "upload_button.label": "Aldoni aŭdovidaĵon",
+ "upload_form.description": "Priskribi por misvidantaj homoj",
+ "upload_form.focus": "Stuci",
"upload_form.undo": "Malfari",
- "upload_progress.label": "Alŝutanta…",
+ "upload_progress.label": "Alŝutado…",
"video.close": "Fermi videon",
- "video.exit_fullscreen": "Eliri el plenekrano",
- "video.expand": "Vastigi videon",
- "video.fullscreen": "Igi plenekrane",
+ "video.exit_fullscreen": "Eksigi plenekrana",
+ "video.expand": "Grandigi videon",
+ "video.fullscreen": "Igi plenekrana",
"video.hide": "Kaŝi videon",
"video.mute": "Silentigi",
"video.pause": "Paŭzi",
- "video.play": "Legi",
+ "video.play": "Ekigi",
"video.unmute": "Malsilentigi"
}
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 4bb15396c..2107a1525 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -1,7 +1,9 @@
{
"account.block": "Bloquear",
"account.block_domain": "Ocultar todo de {domain}",
+ "account.blocked": "Bloqueado",
"account.disclaimer_full": "La siguiente información del usuario puede estar incompleta.",
+ "account.domain_blocked": "Dominio oculto",
"account.edit_profile": "Editar perfil",
"account.follow": "Seguir",
"account.followers": "Seguidores",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} se ha mudado a:",
"account.mute": "Silenciar a @{name}",
"account.mute_notifications": "Silenciar notificaciones de @{name}",
- "account.posts": "Publicaciones",
+ "account.muted": "Silenciado",
+ "account.posts": "Toots",
+ "account.posts_with_replies": "Toots con respuestas",
"account.report": "Reportar a @{name}",
"account.requested": "Esperando aprobación",
"account.share": "Compartir el perfil de @{name}",
@@ -50,14 +54,16 @@
"column_header.unpin": "Dejar de fijar",
"column_subheading.navigation": "Navegación",
"column_subheading.settings": "Ajustes",
- "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+ "compose_form.hashtag_warning": "Este toot no se mostrará bajo hashtags porque no es público. Sólo los toots públicos se pueden buscar por hashtag.",
"compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.",
"compose_form.lock_disclaimer.lock": "bloqueado",
"compose_form.placeholder": "¿En qué estás pensando?",
"compose_form.publish": "Tootear",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Marcar contenido como sensible",
- "compose_form.spoiler": "Ocultar texto tras una advertencia",
+ "compose_form.sensitive.marked": "Material marcado como sensible",
+ "compose_form.sensitive.unmarked": "Material no marcado como sensible",
+ "compose_form.spoiler.marked": "Texto oculto tras la advertencia",
+ "compose_form.spoiler.unmarked": "Texto no oculto",
"compose_form.spoiler_placeholder": "Advertencia de contenido",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.confirm": "Bloquear",
@@ -65,7 +71,7 @@
"confirmations.delete.confirm": "Eliminar",
"confirmations.delete.message": "¿Estás seguro de que quieres borrar este toot?",
"confirmations.delete_list.confirm": "Delete",
- "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+ "confirmations.delete_list.message": "¿Seguro que quieres borrar esta lista permanentemente?",
"confirmations.domain_block.confirm": "Ocultar dominio entero",
"confirmations.domain_block.message": "¿Seguro de que quieres bloquear al dominio entero? En algunos casos es preferible bloquear o silenciar objetivos determinados.",
"confirmations.mute.confirm": "Silenciar",
@@ -128,18 +134,18 @@
"lightbox.close": "Cerrar",
"lightbox.next": "Siguiente",
"lightbox.previous": "Anterior",
- "lists.account.add": "Add to list",
- "lists.account.remove": "Remove from list",
+ "lists.account.add": "Añadir a lista",
+ "lists.account.remove": "Quitar de lista",
"lists.delete": "Delete list",
- "lists.edit": "Edit list",
- "lists.new.create": "Add list",
- "lists.new.title_placeholder": "New list title",
- "lists.search": "Search among people you follow",
- "lists.subheading": "Your lists",
+ "lists.edit": "Editar lista",
+ "lists.new.create": "Añadir lista",
+ "lists.new.title_placeholder": "Título de la nueva lista",
+ "lists.search": "Buscar entre la gente a la que sigues",
+ "lists.subheading": "Tus listas",
"loading_indicator.label": "Cargando…",
"media_gallery.toggle_visible": "Cambiar visibilidad",
"missing_indicator.label": "No encontrado",
- "missing_indicator.sublabel": "This resource could not be found",
+ "missing_indicator.sublabel": "No se encontró este recurso",
"mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?",
"navigation_bar.blocks": "Usuarios bloqueados",
"navigation_bar.community_timeline": "Historia local",
@@ -148,7 +154,7 @@
"navigation_bar.follow_requests": "Solicitudes para seguirte",
"navigation_bar.info": "Información adicional",
"navigation_bar.keyboard_shortcuts": "Atajos de teclado",
- "navigation_bar.lists": "Lists",
+ "navigation_bar.lists": "Listas",
"navigation_bar.logout": "Cerrar sesión",
"navigation_bar.mutes": "Usuarios silenciados",
"navigation_bar.pins": "Toots fijados",
@@ -175,8 +181,8 @@
"onboarding.page_four.home": "La línea de tiempo principal muestra toots de gente que sigues.",
"onboarding.page_four.notifications": "Las notificaciones se muestran cuando alguien interactúa contigo.",
"onboarding.page_one.federation": "Mastodon es una red de servidores federados que conforman una red social aún más grande. Llamamos a estos servidores instancias.",
- "onboarding.page_one.full_handle": "Your full handle",
- "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
+ "onboarding.page_one.full_handle": "Tu sobrenombre completo",
+ "onboarding.page_one.handle_hint": "Esto es lo que dirías a tus amistades que buscaran.",
"onboarding.page_one.welcome": "¡Bienvenido a Mastodon!",
"onboarding.page_six.admin": "El administrador de tu instancia es {admin}.",
"onboarding.page_six.almost_done": "Ya casi…",
@@ -199,23 +205,30 @@
"privacy.public.short": "Público",
"privacy.unlisted.long": "No mostrar en la historia federada",
"privacy.unlisted.short": "Sin federar",
- "regeneration_indicator.label": "Loading…",
- "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+ "regeneration_indicator.label": "Cargando…",
+ "regeneration_indicator.sublabel": "¡Tu historia de inicio se está preparando!",
"relative_time.days": "{number}d",
"relative_time.hours": "{number}h",
"relative_time.just_now": "ahora",
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Cancelar",
+ "report.forward": "Reenviar a {target}",
+ "report.forward_hint": "Esta cuenta es de otro servidor. ¿Enviar una copia anonimizada del informe allí también?",
+ "report.hint": "El informe se enviará a los moderadores de tu instancia. Puedes proporcionar una explicación de por qué informas sobre esta cuenta a continuación:",
"report.placeholder": "Comentarios adicionales",
"report.submit": "Publicar",
"report.target": "Reportando",
"search.placeholder": "Buscar",
"search_popout.search_format": "Formato de búsqueda avanzada",
+ "search_popout.tips.full_text": "Búsquedas de texto recuperan posts que has escrito, marcado como favoritos, retooteado o en los que has sido mencionado, así como usuarios, nombres y hashtags.",
"search_popout.tips.hashtag": "etiqueta",
"search_popout.tips.status": "status",
"search_popout.tips.text": "El texto simple devuelve correspondencias de nombre, usuario y hashtag",
"search_popout.tips.user": "usuario",
+ "search_results.accounts": "Gente",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
"standalone.public_title": "Un pequeño vistazo...",
"status.block": "Block @{name}",
@@ -227,10 +240,11 @@
"status.media_hidden": "Contenido multimedia oculto",
"status.mention": "Mencionar",
"status.more": "Más",
- "status.mute": "Mute @{name}",
+ "status.mute": "Silenciar @{name}",
"status.mute_conversation": "Silenciar conversación",
"status.open": "Expandir estado",
"status.pin": "Fijar",
+ "status.pinned": "Toot fijado",
"status.reblog": "Retootear",
"status.reblogged_by": "Retooteado por {name}",
"status.reply": "Responder",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Contenido sensible",
"status.share": "Compartir",
"status.show_less": "Mostrar menos",
+ "status.show_less_all": "Mostrar menos para todo",
"status.show_more": "Mostrar más",
+ "status.show_more_all": "Mostrar más para todo",
"status.unmute_conversation": "Dejar de silenciar conversación",
"status.unpin": "Dejar de fijar",
- "tabs_bar.compose": "Redactar",
"tabs_bar.federated_timeline": "Federado",
"tabs_bar.home": "Inicio",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Arrastra y suelta para subir",
"upload_button.label": "Subir multimedia",
"upload_form.description": "Describir para los usuarios con dificultad visual",
+ "upload_form.focus": "Recortar",
"upload_form.undo": "Deshacer",
"upload_progress.label": "Subiendo…",
"video.close": "Cerrar video",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 6846da66d..455dc5d9f 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -1,7 +1,9 @@
{
"account.block": "مسدودسازی @{name}",
"account.block_domain": "پنهانسازی همه چیز از سرور {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "اطلاعات زیر ممکن است نمایهٔ این کاربر را به تمامی نشان ندهد.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "ویرایش نمایه",
"account.follow": "پی بگیرید",
"account.followers": "پیگیران",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} منتقل شده است به:",
"account.mute": "بیصدا کردن @{name}",
"account.mute_notifications": "بیصداکردن اعلانها از طرف @{name}",
+ "account.muted": "Muted",
"account.posts": "نوشتهها",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "گزارش @{name}",
"account.requested": "در انتظار پذیرش",
"account.share": "همرسانی نمایهٔ @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "تازه چه خبر؟",
"compose_form.publish": "بوق",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "تصاویر حساس هستند",
- "compose_form.spoiler": "نوشته را پشت هشدار پنهان کنید",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "هشدار محتوا",
"confirmation_modal.cancel": "بیخیال",
"confirmations.block.confirm": "مسدود کن",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "لغو",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "توضیح اضافه",
"report.submit": "بفرست",
"report.target": "گزارشدادن",
"search.placeholder": "جستجو",
"search_popout.search_format": "راهنمای جستجوی پیشرفته",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "هشتگ",
"search_popout.tips.status": "نوشته",
"search_popout.tips.text": "جستجوی متنی ساده برای نامها، نامهای کاربری، و هشتگها",
"search_popout.tips.user": "کاربر",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
"standalone.public_title": "نگاهی به کاربران این سرور...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "بیصداکردن گفتگو",
"status.open": "این نوشته را باز کن",
"status.pin": "نوشتهٔ ثابت نمایه",
+ "status.pinned": "Pinned toot",
"status.reblog": "بازبوقیدن",
"status.reblogged_by": "{name} بازبوقید",
"status.reply": "پاسخ",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "محتوای حساس",
"status.share": "همرسانی",
"status.show_less": "نهفتن",
+ "status.show_less_all": "Show less for all",
"status.show_more": "نمایش",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "باصداکردن گفتگو",
"status.unpin": "برداشتن نوشتهٔ ثابت نمایه",
- "tabs_bar.compose": "بنویسید",
"tabs_bar.federated_timeline": "همگانی",
"tabs_bar.home": "خانه",
"tabs_bar.local_timeline": "محلی",
@@ -252,6 +267,7 @@
"upload_area.title": "برای بارگذاری به اینجا بکشید",
"upload_button.label": "افزودن تصویر",
"upload_form.description": "نوشتهٔ توضیحی برای کمبینایان و نابینایان",
+ "upload_form.focus": "Crop",
"upload_form.undo": "واگردانی",
"upload_progress.label": "بارگذاری...",
"video.close": "بستن ویدیو",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index eb81e7eb4..1741445ed 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -1,266 +1,282 @@
{
"account.block": "Estä @{name}",
- "account.block_domain": "Hide everything from {domain}",
- "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}",
+ "account.blocked": "Estetty",
+ "account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.",
+ "account.domain_blocked": "Verkko-osoite piilotettu",
"account.edit_profile": "Muokkaa",
"account.follow": "Seuraa",
"account.followers": "Seuraajia",
"account.follows": "Seuraa",
"account.follows_you": "Seuraa sinua",
- "account.hide_reblogs": "Hide boosts from @{name}",
+ "account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}",
"account.media": "Media",
"account.mention": "Mainitse @{name}",
- "account.moved_to": "{name} has moved to:",
- "account.mute": "Mute @{name}",
- "account.mute_notifications": "Mute notifications from @{name}",
- "account.posts": "Postit",
+ "account.moved_to": "{name} on muuttanut instanssiin:",
+ "account.mute": "Mykistä @{name}",
+ "account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}",
+ "account.muted": "Mykistetty",
+ "account.posts": "Töötit",
+ "account.posts_with_replies": "Töötit ja vastaukset",
"account.report": "Report @{name}",
- "account.requested": "Odottaa hyväksyntää",
- "account.share": "Share @{name}'s profile",
- "account.show_reblogs": "Show boosts from @{name}",
+ "account.requested": "Odottaa hyväksyntää. Klikkaa peruuttaaksesi seurauspyynnön",
+ "account.share": "Jaa käyttäjän @{name} profiili",
+ "account.show_reblogs": "Näytä boostaukset käyttäjältä @{name}",
"account.unblock": "Salli @{name}",
- "account.unblock_domain": "Unhide {domain}",
- "account.unfollow": "Lopeta seuraaminen",
- "account.unmute": "Unmute @{name}",
- "account.unmute_notifications": "Unmute notifications from @{name}",
- "account.view_full_profile": "View full profile",
- "boost_modal.combo": "You can press {combo} to skip this next time",
- "bundle_column_error.body": "Something went wrong while loading this component.",
- "bundle_column_error.retry": "Try again",
+ "account.unblock_domain": "Näytä {domain}",
+ "account.unfollow": "Lakkaa seuraamasta",
+ "account.unmute": "Poista mykistys käyttäjältä @{name}",
+ "account.unmute_notifications": "Poista mykistys käyttäjän @{name} ilmoituksilta",
+ "account.view_full_profile": "Näytä koko profiili",
+ "boost_modal.combo": "Voit painaa näppäimiä {combo} ohittaaksesi tämän ensi kerralla",
+ "bundle_column_error.body": "Jokin meni vikaan tätä komponenttia ladatessa.",
+ "bundle_column_error.retry": "Yritä uudestaan",
"bundle_column_error.title": "Network error",
- "bundle_modal_error.close": "Close",
- "bundle_modal_error.message": "Something went wrong while loading this component.",
- "bundle_modal_error.retry": "Try again",
- "column.blocks": "Blocked users",
+ "bundle_modal_error.close": "Sulje",
+ "bundle_modal_error.message": "Jokin meni vikaan tätä komponenttia ladatessa.",
+ "bundle_modal_error.retry": "Yritä uudestaan",
+ "column.blocks": "Estetyt käyttäjät",
"column.community": "Paikallinen aikajana",
- "column.favourites": "Favourites",
- "column.follow_requests": "Follow requests",
+ "column.favourites": "Suosikit",
+ "column.follow_requests": "Seurauspyynnöt",
"column.home": "Koti",
- "column.lists": "Lists",
- "column.mutes": "Muted users",
+ "column.lists": "Listat",
+ "column.mutes": "Mykistetyt käyttäjät",
"column.notifications": "Ilmoitukset",
"column.pins": "Pinned toot",
"column.public": "Yleinen aikajana",
"column_back_button.label": "Takaisin",
- "column_header.hide_settings": "Hide settings",
- "column_header.moveLeft_settings": "Move column to the left",
- "column_header.moveRight_settings": "Move column to the right",
- "column_header.pin": "Pin",
- "column_header.show_settings": "Show settings",
- "column_header.unpin": "Unpin",
- "column_subheading.navigation": "Navigation",
- "column_subheading.settings": "Settings",
- "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
- "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
- "compose_form.lock_disclaimer.lock": "locked",
+ "column_header.hide_settings": "Piilota asetukset",
+ "column_header.moveLeft_settings": "Siirrä saraketta vasemmalle",
+ "column_header.moveRight_settings": "Siirrä saraketta oikealle",
+ "column_header.pin": "Kiinnitä",
+ "column_header.show_settings": "Näytä asetukset",
+ "column_header.unpin": "Poista kiinnitys",
+ "column_subheading.navigation": "Navigaatio",
+ "column_subheading.settings": "Asetukset",
+ "compose_form.hashtag_warning": "Tämä töötti ei tule näkymään hashtag-hauissa, koska se ei näy julkisilla aikajanoilla. Vain julkisia tööttejä voi hakea hashtageilla.",
+ "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille -postauksesi.",
+ "compose_form.lock_disclaimer.lock": "lukittu",
"compose_form.placeholder": "Mitä sinulla on mielessä?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Merkitse media herkäksi",
- "compose_form.spoiler": "Piiloita teksti varoituksen taakse",
+ "compose_form.sensitive.marked": "Media on merkitty arkaluontoiseksi",
+ "compose_form.sensitive.unmarked": "Mediaa ei ole merkitty arkaluontoiseksi",
+ "compose_form.spoiler.marked": "Teksti on piilotettu varoituksen taakse",
+ "compose_form.spoiler.unmarked": "Teksti ei ole piilotettu",
"compose_form.spoiler_placeholder": "Content warning",
- "confirmation_modal.cancel": "Cancel",
- "confirmations.block.confirm": "Block",
- "confirmations.block.message": "Are you sure you want to block {name}?",
+ "confirmation_modal.cancel": "Peruuta",
+ "confirmations.block.confirm": "Estä",
+ "confirmations.block.message": "Oletko varma, että haluat estää käyttäjän {name}?",
"confirmations.delete.confirm": "Delete",
- "confirmations.delete.message": "Are you sure you want to delete this status?",
+ "confirmations.delete.message": "Oletko varma, että haluat poistaa tämän statuspäivityksen?",
"confirmations.delete_list.confirm": "Delete",
- "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
- "confirmations.domain_block.confirm": "Hide entire domain",
- "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
- "confirmations.mute.confirm": "Mute",
- "confirmations.mute.message": "Are you sure you want to mute {name}?",
- "confirmations.unfollow.confirm": "Unfollow",
- "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
- "embed.instructions": "Embed this status on your website by copying the code below.",
- "embed.preview": "Here is what it will look like:",
- "emoji_button.activity": "Activity",
- "emoji_button.custom": "Custom",
- "emoji_button.flags": "Flags",
- "emoji_button.food": "Food & Drink",
- "emoji_button.label": "Insert emoji",
- "emoji_button.nature": "Nature",
- "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
- "emoji_button.objects": "Objects",
- "emoji_button.people": "People",
- "emoji_button.recent": "Frequently used",
- "emoji_button.search": "Search...",
- "emoji_button.search_results": "Search results",
- "emoji_button.symbols": "Symbols",
- "emoji_button.travel": "Travel & Places",
- "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
- "empty_column.hashtag": "There is nothing in this hashtag yet.",
- "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
- "empty_column.home.public_timeline": "the public timeline",
- "empty_column.list": "There is nothing in this list yet.",
- "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
- "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
- "follow_request.authorize": "Authorize",
- "follow_request.reject": "Reject",
- "getting_started.appsshort": "Apps",
+ "confirmations.delete_list.message": "Oletko varma, että haluat poistaa tämän listan pysyvästi?",
+ "confirmations.domain_block.confirm": "Piilota koko verkko-osoite",
+ "confirmations.domain_block.message": "Oletko aivan oikeasti varma että haluat estää koko verkko-osoitteen {domain}? Useimmissa tapauksissa muutamat kohdistetut estot ja mykistykset ovat riittäviä ja suositeltavampia.",
+ "confirmations.mute.confirm": "Mykistä",
+ "confirmations.mute.message": "Oletko varma että haluat mykistää käyttäjän {name}?",
+ "confirmations.unfollow.confirm": "Lakkaa seuraamasta",
+ "confirmations.unfollow.message": "Oletko varma, että haluat lakata seuraamasta käyttäjää {name}?",
+ "embed.instructions": "Upota tämä statuspäivitys sivullesi kopioimalla alla oleva koodi.",
+ "embed.preview": "Tältä se tulee näyttämään:",
+ "emoji_button.activity": "Aktiviteetit",
+ "emoji_button.custom": "Mukautetut",
+ "emoji_button.flags": "Liput",
+ "emoji_button.food": "Ruoka ja juoma",
+ "emoji_button.label": "Lisää emoji",
+ "emoji_button.nature": "Luonto",
+ "emoji_button.not_found": "Ei emojeja!! (╯°□°)╯︵ ┻━┻",
+ "emoji_button.objects": "Objektit",
+ "emoji_button.people": "Ihmiset",
+ "emoji_button.recent": "Usein käytetyt",
+ "emoji_button.search": "Etsi...",
+ "emoji_button.search_results": "Hakutulokset",
+ "emoji_button.symbols": "Symbolit",
+ "emoji_button.travel": "Matkailu",
+ "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista saadaksesi pyörät pyörimään!",
+ "empty_column.hashtag": "Tässä hashtagissa ei ole vielä mitään.",
+ "empty_column.home": "Kotiaikajanasi on tyhjä! Käy vierailemassa {public}ssa tai käytä hakutoimintoa aloittaaksesi ja tavataksesi muita käyttäjiä.",
+ "empty_column.home.public_timeline": "yleinen aikajana",
+ "empty_column.list": "Tämä lista on vielä tyhjä. Kun listan jäsenet julkaisevat statuspäivityksiä, ne näkyvät tässä.",
+ "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Juttele muille aloittaaksesi keskustelun.",
+ "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti, tai käy manuaalisesti seuraamassa käyttäjiä muista instansseista saadaksesi sisältöä",
+ "follow_request.authorize": "Valtuuta",
+ "follow_request.reject": "Hylkää",
+ "getting_started.appsshort": "Sovellukset",
"getting_started.faq": "FAQ",
"getting_started.heading": "Aloitus",
- "getting_started.open_source_notice": "Mastodon Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}.",
- "getting_started.userguide": "User Guide",
- "home.column_settings.advanced": "Advanced",
- "home.column_settings.basic": "Basic",
- "home.column_settings.filter_regex": "Filter out by regular expressions",
- "home.column_settings.show_reblogs": "Show boosts",
- "home.column_settings.show_replies": "Show replies",
- "home.settings": "Column settings",
- "keyboard_shortcuts.back": "to navigate back",
- "keyboard_shortcuts.boost": "to boost",
- "keyboard_shortcuts.column": "to focus a status in one of the columns",
- "keyboard_shortcuts.compose": "to focus the compose textarea",
+ "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}.",
+ "getting_started.userguide": "Käyttöopas",
+ "home.column_settings.advanced": "Tarkemmat asetukset",
+ "home.column_settings.basic": "Perusasetukset",
+ "home.column_settings.filter_regex": "Suodata säännöllisten lauseiden avulla",
+ "home.column_settings.show_reblogs": "Näytä buustaukset",
+ "home.column_settings.show_replies": "Näytä vastaukset",
+ "home.settings": "Sarakeasetukset",
+ "keyboard_shortcuts.back": "liikkuaksesi taaksepäin",
+ "keyboard_shortcuts.boost": "buustataksesi",
+ "keyboard_shortcuts.column": "keskittääksesi statuspäivitykseen yhdessä sarakkeista",
+ "keyboard_shortcuts.compose": "aktivoidaksesi tekstinkirjoitusalueen",
"keyboard_shortcuts.description": "Description",
- "keyboard_shortcuts.down": "to move down in the list",
+ "keyboard_shortcuts.down": "liikkuaksesi listassa alaspäin",
"keyboard_shortcuts.enter": "to open status",
- "keyboard_shortcuts.favourite": "to favourite",
- "keyboard_shortcuts.heading": "Keyboard Shortcuts",
- "keyboard_shortcuts.hotkey": "Hotkey",
- "keyboard_shortcuts.legend": "to display this legend",
- "keyboard_shortcuts.mention": "to mention author",
- "keyboard_shortcuts.reply": "to reply",
- "keyboard_shortcuts.search": "to focus search",
- "keyboard_shortcuts.toot": "to start a brand new toot",
- "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
- "keyboard_shortcuts.up": "to move up in the list",
+ "keyboard_shortcuts.favourite": "tykätäksesi",
+ "keyboard_shortcuts.heading": "Näppäinoikotiet",
+ "keyboard_shortcuts.hotkey": "Pikanäppäin",
+ "keyboard_shortcuts.legend": "näyttääksesi tämän selitteen",
+ "keyboard_shortcuts.mention": "mainitaksesi julkaisijan",
+ "keyboard_shortcuts.reply": "vastataksesi",
+ "keyboard_shortcuts.search": "aktivoidaksesi hakukentän",
+ "keyboard_shortcuts.toot": "aloittaaksesi uuden töötin kirjoittamisen",
+ "keyboard_shortcuts.unfocus": "poistaaksesi aktivoinnin tekstikentästä/hakukentästä",
+ "keyboard_shortcuts.up": "liikkuaksesi listassa ylöspäin",
"lightbox.close": "Sulje",
- "lightbox.next": "Next",
- "lightbox.previous": "Previous",
- "lists.account.add": "Add to list",
- "lists.account.remove": "Remove from list",
+ "lightbox.next": "Seuraava",
+ "lightbox.previous": "Edellinen",
+ "lists.account.add": "Lisää listaan",
+ "lists.account.remove": "Poista listalta",
"lists.delete": "Delete list",
- "lists.edit": "Edit list",
- "lists.new.create": "Add list",
- "lists.new.title_placeholder": "New list title",
- "lists.search": "Search among people you follow",
- "lists.subheading": "Your lists",
+ "lists.edit": "Muokkaa listaa",
+ "lists.new.create": "Lisää lista",
+ "lists.new.title_placeholder": "Uuden listan otsikko",
+ "lists.search": "Etsi seuraamiesi henkilöiden joukosta",
+ "lists.subheading": "Omat listat",
"loading_indicator.label": "Ladataan...",
- "media_gallery.toggle_visible": "Toggle visibility",
- "missing_indicator.label": "Not found",
- "missing_indicator.sublabel": "This resource could not be found",
- "mute_modal.hide_notifications": "Hide notifications from this user?",
- "navigation_bar.blocks": "Blocked users",
+ "media_gallery.toggle_visible": "Säädä näkyvyyttä",
+ "missing_indicator.label": "Ei löydetty",
+ "missing_indicator.sublabel": "Tätä resurssia ei löytynyt",
+ "mute_modal.hide_notifications": "Piilota ilmoitukset tältä käyttäjältä?",
+ "navigation_bar.blocks": "Estetyt käyttäjät",
"navigation_bar.community_timeline": "Paikallinen aikajana",
"navigation_bar.edit_profile": "Muokkaa profiilia",
- "navigation_bar.favourites": "Favourites",
- "navigation_bar.follow_requests": "Follow requests",
- "navigation_bar.info": "Extended information",
- "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
- "navigation_bar.lists": "Lists",
+ "navigation_bar.favourites": "Suosikit",
+ "navigation_bar.follow_requests": "Seurauspyynnöt",
+ "navigation_bar.info": "Tietoa tästä instanssista",
+ "navigation_bar.keyboard_shortcuts": "Näppäinoikotiet",
+ "navigation_bar.lists": "Listat",
"navigation_bar.logout": "Kirjaudu ulos",
- "navigation_bar.mutes": "Muted users",
- "navigation_bar.pins": "Pinned toots",
+ "navigation_bar.mutes": "Mykistetyt käyttäjät",
+ "navigation_bar.pins": "Kiinnitetyt töötit",
"navigation_bar.preferences": "Ominaisuudet",
"navigation_bar.public_timeline": "Yleinen aikajana",
"notification.favourite": "{name} tykkäsi statuksestasi",
"notification.follow": "{name} seurasi sinua",
"notification.mention": "{name} mainitsi sinut",
"notification.reblog": "{name} buustasi statustasi",
- "notifications.clear": "Clear notifications",
- "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+ "notifications.clear": "Tyhjennä ilmoitukset",
+ "notifications.clear_confirmation": "Oletko varma, että haluat lopullisesti tyhjentää kaikki ilmoituksesi?",
"notifications.column_settings.alert": "Työpöytä ilmoitukset",
"notifications.column_settings.favourite": "Tykkäyksiä:",
"notifications.column_settings.follow": "Uusia seuraajia:",
"notifications.column_settings.mention": "Mainintoja:",
- "notifications.column_settings.push": "Push notifications",
- "notifications.column_settings.push_meta": "This device",
+ "notifications.column_settings.push": "Push-ilmoitukset",
+ "notifications.column_settings.push_meta": "Tämä laite",
"notifications.column_settings.reblog": "Buusteja:",
"notifications.column_settings.show": "Näytä sarakkeessa",
- "notifications.column_settings.sound": "Play sound",
- "onboarding.done": "Done",
- "onboarding.next": "Next",
- "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
- "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
- "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
- "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
- "onboarding.page_one.full_handle": "Your full handle",
- "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
- "onboarding.page_one.welcome": "Welcome to Mastodon!",
- "onboarding.page_six.admin": "Your instance's admin is {admin}.",
- "onboarding.page_six.almost_done": "Almost done...",
- "onboarding.page_six.appetoot": "Bon Appetoot!",
- "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
- "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
- "onboarding.page_six.guidelines": "community guidelines",
- "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
- "onboarding.page_six.various_app": "mobile apps",
- "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
- "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
- "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
- "onboarding.skip": "Skip",
- "privacy.change": "Adjust status privacy",
- "privacy.direct.long": "Post to mentioned users only",
- "privacy.direct.short": "Direct",
- "privacy.private.long": "Post to followers only",
- "privacy.private.short": "Followers-only",
- "privacy.public.long": "Post to public timelines",
- "privacy.public.short": "Public",
- "privacy.unlisted.long": "Do not show in public timelines",
- "privacy.unlisted.short": "Unlisted",
- "regeneration_indicator.label": "Loading…",
- "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+ "notifications.column_settings.sound": "Soita ääni",
+ "onboarding.done": "Valmis",
+ "onboarding.next": "Seuraava",
+ "onboarding.page_five.public_timelines": "Paikallinen aikajana näyttää kaikki julkiset julkaisut kaikilta, jotka ovat verkko-osoitteessa {domain}. Yleinen aikajana näyttää julkiset julkaisut kaikilta niiltä, joita käyttäjät verkko-osoitteessa {domain} seuraavat. Nämä ovat julkiset aikajanat, ja ne ovat hyviä tapoja löytää uusia ihmisiä.",
+ "onboarding.page_four.home": "Kotiaikajana näyttää julkaisut ihmisiltä joita seuraat.",
+ "onboarding.page_four.notifications": "Ilmoitukset-sarake näyttää sinulle, kun joku on viestii kanssasi.",
+ "onboarding.page_one.federation": "Mastodon on yhteisöpalvelu, joka toimii monen itsenäisen palvelimen muodostamassa verkossa. Me kutsumme näitä palvelimia instansseiksi.",
+ "onboarding.page_one.full_handle": "Koko käyttäjänimesi",
+ "onboarding.page_one.handle_hint": "Tämä on se, mitä voisit ehdottaa ystäviäsi etsimään.",
+ "onboarding.page_one.welcome": "Tervetuloa Mastodoniin!",
+ "onboarding.page_six.admin": "Instanssisi ylläpitäjä on {admin}.",
+ "onboarding.page_six.almost_done": "Melkein valmista...",
+ "onboarding.page_six.appetoot": "Bon Appetööt!",
+ "onboarding.page_six.apps_available": "{apps} on saatavilla iOS:lle, Androidille ja muille alustoille.",
+ "onboarding.page_six.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, pyytää ominaisuuksia tai osallistua kehittämiseen GitHub-palvelussa: {github}.",
+ "onboarding.page_six.guidelines": "yhteisön säännöt",
+ "onboarding.page_six.read_guidelines": "Ole hyvä ja lue {domain}:n {guidelines}!",
+ "onboarding.page_six.various_app": "mobiilisovellukset",
+ "onboarding.page_three.profile": "Muokkaa profiiliasi muuttaaksesi kuvakettasi, esittelyäsi ja nimimerkkiäsi. Löydät sieltä myös muita henkilökohtaisia asetuksia.",
+ "onboarding.page_three.search": "Käytä hakukenttää löytääksesi ihmisiä ja etsiäksesi hashtageja, kuten {illustration} tai {introductions}. Hakeaksesi henkilöä joka on toisessa instanssissa, käytä hänen käyttäjänimeään kokonaisuudessaan.",
+ "onboarding.page_two.compose": "Kirjoita postauksia kirjoita-sarakkeessa. Voit ladata kuvia, vaihtaa yksityisyysasetuksia ja lisätä sisältövaroituksia alla olevista painikkeista.",
+ "onboarding.skip": "Ohita",
+ "privacy.change": "Säädä töötin yksityisyysasetuksia",
+ "privacy.direct.long": "Julkaise vain mainituille käyttäjille",
+ "privacy.direct.short": "Yksityisviesti",
+ "privacy.private.long": "Julkaise vain seuraajille",
+ "privacy.private.short": "Vain seuraajat",
+ "privacy.public.long": "Julkaise julkisille aikajanoille",
+ "privacy.public.short": "Julkinen",
+ "privacy.unlisted.long": "Älä julkaise yleisillä aikajanoilla",
+ "privacy.unlisted.short": "Julkinen, mutta älä näytä julkisella aikajanalla",
+ "regeneration_indicator.label": "Ladataan…",
+ "regeneration_indicator.sublabel": "Kotinäkymääsi valmistellaan!",
"relative_time.days": "{number}d",
"relative_time.hours": "{number}h",
- "relative_time.just_now": "now",
+ "relative_time.just_now": "nyt",
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Peruuta",
- "report.placeholder": "Additional comments",
+ "report.forward": "Uudelleenohjaa kohteeseen {target}",
+ "report.forward_hint": "Tämä tili on toiselta serveriltä. Haluatko, että myös sinne lähetetään anonymisoitu kopio ilmiantoraportista?",
+ "report.hint": "Ilmianto lähetetään instanssisi moderaattoreille. Voit antaa kuvauksen käyttäjän ilmiantamisen syystä alle:",
+ "report.placeholder": "Lisäkommentit",
"report.submit": "Submit",
"report.target": "Reporting",
"search.placeholder": "Hae",
- "search_popout.search_format": "Advanced search format",
- "search_popout.tips.hashtag": "hashtag",
+ "search_popout.search_format": "Tarkennettu haku",
+ "search_popout.tips.full_text": "Tekstihaku palauttaa statuspäivitykset jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä käyttäjänimet, nimimerkit ja hastagit jotka sisältävät tekstin.",
+ "search_popout.tips.hashtag": "hashtagi",
"search_popout.tips.status": "status",
- "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
- "search_popout.tips.user": "user",
+ "search_popout.tips.text": "Pelkkä tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit",
+ "search_popout.tips.user": "käyttäjä",
+ "search_results.accounts": "Ihmiset",
+ "search_results.hashtags": "Hashtagit",
+ "search_results.statuses": "Töötit",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
- "standalone.public_title": "A look inside...",
+ "standalone.public_title": "Kurkistus sisälle...",
"status.block": "Block @{name}",
- "status.cannot_reblog": "This post cannot be boosted",
+ "status.cannot_reblog": "Tätä postausta ei voi buustata",
"status.delete": "Poista",
- "status.embed": "Embed",
+ "status.embed": "Upota",
"status.favourite": "Tykkää",
- "status.load_more": "Load more",
- "status.media_hidden": "Media hidden",
+ "status.load_more": "Lataa lisää",
+ "status.media_hidden": "Media piilotettu",
"status.mention": "Mainitse @{name}",
- "status.more": "More",
- "status.mute": "Mute @{name}",
- "status.mute_conversation": "Mute conversation",
- "status.open": "Expand this status",
- "status.pin": "Pin on profile",
+ "status.more": "Lisää",
+ "status.mute": "Mykistä @{name}",
+ "status.mute_conversation": "Mykistä keskustelu",
+ "status.open": "Laajenna statuspäivitys",
+ "status.pin": "Kiinnitä profiiliin",
+ "status.pinned": "Kiinnitetty töötti",
"status.reblog": "Buustaa",
"status.reblogged_by": "{name} buustasi",
"status.reply": "Vastaa",
- "status.replyAll": "Reply to thread",
+ "status.replyAll": "Vastaa ketjuun",
"status.report": "Report @{name}",
"status.sensitive_toggle": "Klikkaa nähdäksesi",
"status.sensitive_warning": "Arkaluontoista sisältöä",
- "status.share": "Share",
- "status.show_less": "Show less",
- "status.show_more": "Show more",
- "status.unmute_conversation": "Unmute conversation",
- "status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Luo",
- "tabs_bar.federated_timeline": "Federated",
+ "status.share": "Jaa",
+ "status.show_less": "Näytä vähemmän",
+ "status.show_less_all": "Näytä vähemmän kaikista",
+ "status.show_more": "Näytä lisää",
+ "status.show_more_all": "Näytä enemmän kaikista",
+ "status.unmute_conversation": "Poista mykistys keskustelulta",
+ "status.unpin": "Irrota profiilista",
+ "tabs_bar.federated_timeline": "Yleinen",
"tabs_bar.home": "Koti",
- "tabs_bar.local_timeline": "Local",
+ "tabs_bar.local_timeline": "Paikallinen",
"tabs_bar.notifications": "Ilmoitukset",
- "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
- "upload_area.title": "Drag & drop to upload",
+ "ui.beforeunload": "Luonnoksesi menetetään, jos poistut Mastodonista.",
+ "upload_area.title": "Raahaa ja pudota tähän ladataksesi",
"upload_button.label": "Lisää mediaa",
- "upload_form.description": "Describe for the visually impaired",
+ "upload_form.description": "Anna kuvaus näkörajoitteisia varten",
+ "upload_form.focus": "Rajaa",
"upload_form.undo": "Peru",
- "upload_progress.label": "Uploading...",
- "video.close": "Close video",
- "video.exit_fullscreen": "Exit full screen",
- "video.expand": "Expand video",
+ "upload_progress.label": "Ladataan...",
+ "video.close": "Sulje video",
+ "video.exit_fullscreen": "Poistu koko näytön tilasta",
+ "video.expand": "Laajenna video",
"video.fullscreen": "Full screen",
- "video.hide": "Hide video",
- "video.mute": "Mute sound",
- "video.pause": "Pause",
- "video.play": "Play",
- "video.unmute": "Unmute sound"
+ "video.hide": "Piilota video",
+ "video.mute": "Mykistä ääni",
+ "video.pause": "Keskeytä",
+ "video.play": "Toista",
+ "video.unmute": "Poista mykistys ääneltä"
}
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 17075f6de..40fd6163e 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -1,7 +1,9 @@
{
"account.block": "Bloquer @{name}",
"account.block_domain": "Tout masquer venant de {domain}",
+ "account.blocked": "Bloqué",
"account.disclaimer_full": "Les données ci-dessous peuvent ne pas refléter ce profil dans sa totalité.",
+ "account.domain_blocked": "Domaine caché",
"account.edit_profile": "Modifier le profil",
"account.follow": "Suivre",
"account.followers": "Abonné⋅e⋅s",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} a déménagé vers :",
"account.mute": "Masquer @{name}",
"account.mute_notifications": "Ignorer les notifications de @{name}",
- "account.posts": "Statuts",
+ "account.muted": "Silencé",
+ "account.posts": "Pouets",
+ "account.posts_with_replies": "Pouets avec réponses",
"account.report": "Signaler",
"account.requested": "Invitation envoyée",
"account.share": "Partager le profil de @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Qu’avez-vous en tête ?",
"compose_form.publish": "Pouet",
"compose_form.publish_loud": "{publish} !",
- "compose_form.sensitive": "Marquer le média comme sensible",
- "compose_form.spoiler": "Masquer le texte derrière un avertissement",
+ "compose_form.sensitive.marked": "Média marqué comme sensible",
+ "compose_form.sensitive.unmarked": "Média non marqué comme sensible",
+ "compose_form.spoiler.marked": "Le texte est caché derrière un avertissement",
+ "compose_form.spoiler.unmarked": "Le texte n'est pas caché",
"compose_form.spoiler_placeholder": "Écrivez ici votre avertissement",
"confirmation_modal.cancel": "Annuler",
"confirmations.block.confirm": "Bloquer",
@@ -163,7 +169,7 @@
"notifications.column_settings.alert": "Notifications locales",
"notifications.column_settings.favourite": "Favoris :",
"notifications.column_settings.follow": "Nouveaux⋅elles abonné⋅e·s :",
- "notifications.column_settings.mention": "Mentions :",
+ "notifications.column_settings.mention": "Mentions :",
"notifications.column_settings.push": "Notifications push",
"notifications.column_settings.push_meta": "Cet appareil",
"notifications.column_settings.reblog": "Partages :",
@@ -200,22 +206,29 @@
"privacy.unlisted.long": "Ne pas afficher dans les fils publics",
"privacy.unlisted.short": "Non-listé",
"regeneration_indicator.label": "Chargement…",
- "regeneration_indicator.sublabel": "Votre page principale est en cours de préparation!",
+ "regeneration_indicator.sublabel": "Le flux de votre page principale est en cours de préparation !",
"relative_time.days": "{number} j",
"relative_time.hours": "{number} h",
"relative_time.just_now": "à l’instant",
"relative_time.minutes": "{number} min",
"relative_time.seconds": "{number} s",
"reply_indicator.cancel": "Annuler",
+ "report.forward": "Transférer à {target}",
+ "report.forward_hint": "Le compte provient d'un autre serveur. Envoyez également une copie anonyme du rapport ?",
+ "report.hint": "Le rapport sera envoyé aux modérateurs de votre instance. Vous pouvez expliquer pourquoi vous signalez ce compte ci-dessous :",
"report.placeholder": "Commentaires additionnels",
"report.submit": "Envoyer",
"report.target": "Signalement",
"search.placeholder": "Rechercher",
"search_popout.search_format": "Recherche avancée",
+ "search_popout.tips.full_text": "Les textes simples retournent les pouets que vous avez écris, mis en favori, épinglés, ou ayant été mentionnés, ainsi que les noms d'utilisateurs, les noms affichés, et les hashtags correspondant.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "statuts",
"search_popout.tips.text": "Un texte simple renvoie les noms affichés, les noms d’utilisateur⋅ice et les hashtags correspondants",
"search_popout.tips.user": "utilisateur⋅ice",
+ "search_results.accounts": "Personnes",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Pouets",
"search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}",
"standalone.public_title": "Jeter un coup d’œil…",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Masquer la conversation",
"status.open": "Déplier ce statut",
"status.pin": "Épingler sur le profil",
+ "status.pinned": "Pouet épinglé",
"status.reblog": "Partager",
"status.reblogged_by": "{name} a partagé :",
"status.reply": "Répondre",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Contenu sensible",
"status.share": "Partager",
"status.show_less": "Replier",
+ "status.show_less_all": "Tout replier",
"status.show_more": "Déplier",
+ "status.show_more_all": "Tout déplier",
"status.unmute_conversation": "Ne plus masquer la conversation",
"status.unpin": "Retirer du profil",
- "tabs_bar.compose": "Composer",
"tabs_bar.federated_timeline": "Fil public global",
"tabs_bar.home": "Accueil",
"tabs_bar.local_timeline": "Fil public local",
@@ -252,6 +267,7 @@
"upload_area.title": "Glissez et déposez pour envoyer",
"upload_button.label": "Joindre un média",
"upload_form.description": "Décrire pour les malvoyants",
+ "upload_form.focus": "Recadrer",
"upload_form.undo": "Annuler",
"upload_progress.label": "Envoi en cours…",
"video.close": "Fermer la vidéo",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 9e8352ba4..edfb9cfcb 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -1,7 +1,9 @@
{
"account.block": "Bloquear @{name}",
"account.block_domain": "Ocultar calquer contido de {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "A información inferior podería mostrar un perfil incompleto da usuaria.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Editar perfil",
"account.follow": "Seguir",
"account.followers": "Seguidoras",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} marchou a:",
"account.mute": "Acalar @{name}",
"account.mute_notifications": "Acalar as notificacións de @{name}",
- "account.posts": "Publicacións",
+ "account.muted": "Muted",
+ "account.posts": "Toots",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Informar sobre @{name}",
"account.requested": "Agardando aceptación. Pulse para cancelar a solicitude de seguimento",
"account.share": "Compartir o perfil de @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "A qué andas?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Marcar medios como sensibles",
- "compose_form.spoiler": "Agochar texto detrás de un aviso",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Escriba o aviso aquí",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.confirm": "Bloquear",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Cancelar",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Comentarios adicionais",
"report.submit": "Enviar",
"report.target": "Informar {target}",
"search.placeholder": "Buscar",
"search_popout.search_format": "Formato de busca avanzada",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "etiqueta",
"search_popout.tips.status": "estado",
"search_popout.tips.text": "Texto simple devolve coincidencias con nomes públicos, nomes de usuaria e etiquetas",
"search_popout.tips.user": "usuaria",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count,plural,one {result} outros {results}}",
"standalone.public_title": "Ollada dentro...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Acalar conversa",
"status.open": "Expandir este estado",
"status.pin": "Fixar no perfil",
+ "status.pinned": "Pinned toot",
"status.reblog": "Promover",
"status.reblogged_by": "{name} promoveu",
"status.reply": "Resposta",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Contido sensible",
"status.share": "Compartir",
"status.show_less": "Mostrar menos",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Mostrar máis",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Non acalar a conversa",
"status.unpin": "Despegar do perfil",
- "tabs_bar.compose": "Compoñer",
"tabs_bar.federated_timeline": "Federado",
"tabs_bar.home": "Inicio",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Arrastre e solte para subir",
"upload_button.label": "Engadir medios",
"upload_form.description": "Describa para deficientes visuais",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Desfacer",
"upload_progress.label": "Subindo...",
"video.close": "Pechar video",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index d6665295f..b637ae414 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -1,7 +1,9 @@
{
"account.block": "חסימת @{name}",
"account.block_domain": "להסתיר הכל מהקהילה {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "המידע להלן עשוי להיות לא עדכני או לא שלם.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "עריכת פרופיל",
"account.follow": "מעקב",
"account.followers": "עוקבים",
@@ -13,7 +15,9 @@
"account.moved_to": "החשבון {name} הועבר אל:",
"account.mute": "להשתיק את @{name}",
"account.mute_notifications": "להסתיר התראות מאת @{name}",
+ "account.muted": "Muted",
"account.posts": "הודעות",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "לדווח על @{name}",
"account.requested": "בהמתנה לאישור",
"account.share": "לשתף את אודות @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "מה עובר לך בראש?",
"compose_form.publish": "ללחוש",
"compose_form.publish_loud": "לחצרץ!",
- "compose_form.sensitive": "סימון תוכן כרגיש",
- "compose_form.spoiler": "הסתרה מאחורי אזהרת תוכן",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "אזהרת תוכן",
"confirmation_modal.cancel": "ביטול",
"confirmations.block.confirm": "לחסום",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "ביטול",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "הערות נוספות",
"report.submit": "שליחה",
"report.target": "דיווח",
"search.placeholder": "חיפוש",
"search_popout.search_format": "מבנה חיפוש מתקדם",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "האשתג",
"search_popout.tips.status": "status",
"search_popout.tips.text": "טקסט פשוט מחזיר כינויים, שמות משתמש והאשתגים",
"search_popout.tips.user": "משתמש(ת)",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {תוצאה} other {תוצאות}}",
"standalone.public_title": "הצצה פנימה...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "השתקת שיחה",
"status.open": "הרחבת הודעה",
"status.pin": "לקבע באודות",
+ "status.pinned": "Pinned toot",
"status.reblog": "הדהוד",
"status.reblogged_by": "הודהד על ידי {name}",
"status.reply": "תגובה",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "תוכן רגיש",
"status.share": "שיתוף",
"status.show_less": "הראה פחות",
+ "status.show_less_all": "Show less for all",
"status.show_more": "הראה יותר",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "הסרת השתקת שיחה",
"status.unpin": "לשחרר מקיבוע באודות",
- "tabs_bar.compose": "חיבור",
"tabs_bar.federated_timeline": "ציר זמן בין-קהילתי",
"tabs_bar.home": "בבית",
"tabs_bar.local_timeline": "ציר זמן מקומי",
@@ -252,6 +267,7 @@
"upload_area.title": "ניתן להעלות על ידי Drag & drop",
"upload_button.label": "הוספת מדיה",
"upload_form.description": "תיאור לכבדי ראיה",
+ "upload_form.focus": "Crop",
"upload_form.undo": "ביטול",
"upload_progress.label": "עולה...",
"video.close": "סגירת וידאו",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index c49ae160f..4b64d796d 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -1,7 +1,9 @@
{
"account.block": "Blokiraj @{name}",
"account.block_domain": "Sakrij sve sa {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Ovaj korisnik je sa druge instance. Ovaj broj bi mogao biti veći.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Uredi profil",
"account.follow": "Slijedi",
"account.followers": "Sljedbenici",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Utišaj @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "Postovi",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Prijavi @{name}",
"account.requested": "Čeka pristanak",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Što ti je na umu?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Označi media sadržaj kao osjetljiv",
- "compose_form.spoiler": "Sakrij text iza upozorenja",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Upozorenje o sadržaju",
"confirmation_modal.cancel": "Otkaži",
"confirmations.block.confirm": "Blokiraj",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Otkaži",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Dodatni komentari",
"report.submit": "Pošalji",
"report.target": "Prijavljivanje",
"search.placeholder": "Traži",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Utišaj razgovor",
"status.open": "Proširi ovaj status",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Podigni",
"status.reblogged_by": "{name} je podigao",
"status.reply": "Odgovori",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Osjetljiv sadržaj",
"status.share": "Share",
"status.show_less": "Pokaži manje",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Pokaži više",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Poništi utišavanje razgovora",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Sastavi",
"tabs_bar.federated_timeline": "Federalni",
"tabs_bar.home": "Dom",
"tabs_bar.local_timeline": "Lokalno",
@@ -252,6 +267,7 @@
"upload_area.title": "Povuci i spusti kako bi uploadao",
"upload_button.label": "Dodaj media",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Poništi",
"upload_progress.label": "Uploadam...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 316687129..79888e41e 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -1,19 +1,23 @@
{
"account.block": "@{name} letiltása",
"account.block_domain": "Minden elrejtése innen: {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Az alul található információk hiányosan mutathatják be a felhasználót.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Profil szerkesztése",
"account.follow": "Követés",
"account.followers": "Követők",
"account.follows": "Követve",
"account.follows_you": "Követnek téged",
- "account.hide_reblogs": "@{name} kedvenceinek elrejtése",
+ "account.hide_reblogs": "Rejtsd el a tülkölést @{name}-tól/től",
"account.media": "Média",
"account.mention": "@{name} említése",
"account.moved_to": "{name} átköltözött:",
"account.mute": "@{name} némítása",
"account.mute_notifications": "@{name} értesítések némítása",
+ "account.muted": "Muted",
"account.posts": "Státuszok",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "@{name} jelentése",
"account.requested": "Engedélyre vár. Kattintson a követési kérés visszavonására",
"account.share": "@{name} profiljának megosztása",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Mire gondolsz?",
"compose_form.publish": "Tülk",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Tartalom érzékenynek jelölése",
- "compose_form.spoiler": "Szöveg figyelmeztetés mögé rejtése",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Figyelmeztetését írja ide",
"confirmation_modal.cancel": "Bezár",
"confirmations.block.confirm": "Letilt",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Mégsem",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "További kommentek",
"report.submit": "Submit",
"report.target": "Reporting",
"search.placeholder": "Keresés",
"search_popout.search_format": "Fejlett keresés",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "felhasználó",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"standalone.public_title": "Betekintés...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Beszélgetés némítása",
"status.open": "Státusz kinagyítása",
"status.pin": "Kitűzés a profilra",
+ "status.pinned": "Pinned toot",
"status.reblog": "Reblog",
"status.reblogged_by": "{name} reblogolta",
"status.reply": "Válasz",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Érzékeny tartalom",
"status.share": "Megosztás",
"status.show_less": "Kevesebb",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Többet",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Beszélgetés némításának elvonása",
"status.unpin": "Kitűzés eltávolítása a profilról",
- "tabs_bar.compose": "Összeállítás",
"tabs_bar.federated_timeline": "Federált",
"tabs_bar.home": "Kezdőlap",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Húzza ide a feltöltéshez",
"upload_button.label": "Média hozzáadása",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Mégsem",
"upload_progress.label": "Uploading...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index f46db5b00..932ff1565 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -1,7 +1,9 @@
{
"account.block": "Արգելափակել @{name}֊ին",
"account.block_domain": "Թաքցնել ամենը հետեւյալ տիրույթից՝ {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Ներքոհիշյալը կարող է ոչ ամբողջությամբ արտացոլել օգտատիրոջ էջի տվյալները։",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Խմբագրել անձնական էջը",
"account.follow": "Հետեւել",
"account.followers": "Հետեւվողներ",
@@ -13,7 +15,9 @@
"account.moved_to": "{name}֊ը տեղափոխվել է՝",
"account.mute": "Լռեցնել @{name}֊ին",
"account.mute_notifications": "Անջատել ծանուցումները @{name}֊ից",
+ "account.muted": "Muted",
"account.posts": "Գրառումներ",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Բողոքել @{name}֊ից",
"account.requested": "Հաստատման կարիք ունի։ Սեղմիր՝ հետեւելու հայցը չեղարկելու համար։",
"account.share": "Կիսվել @{name}֊ի էջով",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Ի՞նչ կա մտքիդ",
"compose_form.publish": "Թթել",
"compose_form.publish_loud": "Թթե՜լ",
- "compose_form.sensitive": "Նշել բովանդակությունը որպես կասկածելի",
- "compose_form.spoiler": "Թաքցնել տեքստը նախազգուշացման ետեւում",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Գրիր նախազգուշացումդ այստեղ",
"confirmation_modal.cancel": "Չեղարկել",
"confirmations.block.confirm": "Արգելափակել",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}ր",
"relative_time.seconds": "{number}վ",
"reply_indicator.cancel": "Չեղարկել",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Լրացուցիչ մեկնաբանություններ",
"report.submit": "Ուղարկել",
"report.target": "Բողոքել {target}֊ի մասին",
"search.placeholder": "Փնտրել",
"search_popout.search_format": "Փնտրելու առաջադեմ ձեւ",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "պիտակ",
"search_popout.tips.status": "թութ",
"search_popout.tips.text": "Հասարակ տեքստը կվերադարձնի համընկնող անուններ, օգտանուններ ու պիտակներ",
"search_popout.tips.user": "օգտատեր",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"standalone.public_title": "Այս պահին…",
"status.block": "Արգելափակել @{name}֊ին",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Լռեցնել խոսակցությունը",
"status.open": "Ընդարձակել այս թութը",
"status.pin": "Ամրացնել անձնական էջում",
+ "status.pinned": "Pinned toot",
"status.reblog": "Տարածել",
"status.reblogged_by": "{name} տարածել է",
"status.reply": "Պատասխանել",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Կասկածելի բովանդակություն",
"status.share": "Կիսվել",
"status.show_less": "Պակաս",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Ավելին",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Ապալռեցնել խոսակցությունը",
"status.unpin": "Հանել անձնական էջից",
- "tabs_bar.compose": "Շարադրել",
"tabs_bar.federated_timeline": "Դաշնային",
"tabs_bar.home": "Հիմնական",
"tabs_bar.local_timeline": "Տեղական",
@@ -252,6 +267,7 @@
"upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար",
"upload_button.label": "Ավելացնել մեդիա",
"upload_form.description": "Նկարագրություն ավելացրու տեսողական խնդիրներ ունեցողների համար",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Հետարկել",
"upload_progress.label": "Վերբեռնվում է…",
"video.close": "Փակել տեսագրությունը",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 6edf855d3..bc4294679 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -1,127 +1,133 @@
{
"account.block": "Blokir @{name}",
- "account.block_domain": "Hide everything from {domain}",
- "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.block_domain": "Sembunyikan segalanya dari {domain}",
+ "account.blocked": "Terblokir",
+ "account.disclaimer_full": "Informasi di bawah mungkin tidak mencerminkan profil user secara lengkap.",
+ "account.domain_blocked": "Domain disembunyikan",
"account.edit_profile": "Ubah profil",
"account.follow": "Ikuti",
"account.followers": "Pengikut",
"account.follows": "Mengikuti",
"account.follows_you": "Mengikuti anda",
- "account.hide_reblogs": "Hide boosts from @{name}",
+ "account.hide_reblogs": "Sembunyikan boosts dari @{name}",
"account.media": "Media",
"account.mention": "Balasan @{name}",
- "account.moved_to": "{name} has moved to:",
+ "account.moved_to": "{name} telah pindah ke:",
"account.mute": "Bisukan @{name}",
- "account.mute_notifications": "Mute notifications from @{name}",
- "account.posts": "Postingan",
+ "account.mute_notifications": "Sembunyikan notifikasi dari @{name}",
+ "account.muted": "Dibisukan",
+ "account.posts": "Toots",
+ "account.posts_with_replies": "Postingan dengan balasan",
"account.report": "Laporkan @{name}",
- "account.requested": "Menunggu persetujuan",
- "account.share": "Share @{name}'s profile",
- "account.show_reblogs": "Show boosts from @{name}",
+ "account.requested": "Menunggu persetujuan. Klik untuk membatalkan permintaan",
+ "account.share": "Bagikan profil @{name}",
+ "account.show_reblogs": "Tampilkan boost dari @{name}",
"account.unblock": "Hapus blokir @{name}",
- "account.unblock_domain": "Unhide {domain}",
+ "account.unblock_domain": "Tampilkan {domain}",
"account.unfollow": "Berhenti mengikuti",
"account.unmute": "Berhenti membisukan @{name}",
- "account.unmute_notifications": "Unmute notifications from @{name}",
- "account.view_full_profile": "View full profile",
+ "account.unmute_notifications": "Munculkan notifikasi dari @{name}",
+ "account.view_full_profile": "Lihat profil lengkap",
"boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini",
- "bundle_column_error.body": "Something went wrong while loading this component.",
- "bundle_column_error.retry": "Try again",
+ "bundle_column_error.body": "Kesalahan terjadi saat memuat komponen ini.",
+ "bundle_column_error.retry": "Coba lagi",
"bundle_column_error.title": "Network error",
- "bundle_modal_error.close": "Close",
- "bundle_modal_error.message": "Something went wrong while loading this component.",
- "bundle_modal_error.retry": "Try again",
+ "bundle_modal_error.close": "Tutup",
+ "bundle_modal_error.message": "Kesalahan terjadi saat memuat komponen ini.",
+ "bundle_modal_error.retry": "Coba lagi",
"column.blocks": "Pengguna diblokir",
"column.community": "Linimasa Lokal",
"column.favourites": "Favorit",
"column.follow_requests": "Permintaan mengikuti",
"column.home": "Beranda",
- "column.lists": "Lists",
- "column.mutes": "Pengguna dibisukan",
+ "column.lists": "List",
+ "column.mutes": "Pengguna yang dibisukan",
"column.notifications": "Notifikasi",
"column.pins": "Pinned toot",
- "column.public": "Linimasa gabunggan",
+ "column.public": "Linimasa gabungan",
"column_back_button.label": "Kembali",
- "column_header.hide_settings": "Hide settings",
- "column_header.moveLeft_settings": "Move column to the left",
- "column_header.moveRight_settings": "Move column to the right",
- "column_header.pin": "Pin",
- "column_header.show_settings": "Show settings",
- "column_header.unpin": "Unpin",
+ "column_header.hide_settings": "Sembunyikan pengaturan",
+ "column_header.moveLeft_settings": "Pindahkan kolom ke kiri",
+ "column_header.moveRight_settings": "Pindahkan kolom ke kanan",
+ "column_header.pin": "Sematkan",
+ "column_header.show_settings": "Tampilkan pengaturan",
+ "column_header.unpin": "Lepaskan",
"column_subheading.navigation": "Navigasi",
"column_subheading.settings": "Pengaturan",
- "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+ "compose_form.hashtag_warning": "Toot ini tidak akan ada dalam daftar tagar manapun karena telah di set sebagai tidak terdaftar. Hanya postingan publik yang bisa dicari dengan tagar.",
"compose_form.lock_disclaimer": "Akun anda tidak {locked}. Semua orang dapat mengikuti anda untuk melihat postingan khusus untuk pengikut anda.",
- "compose_form.lock_disclaimer.lock": "dikunci",
+ "compose_form.lock_disclaimer.lock": "terkunci",
"compose_form.placeholder": "Apa yang ada di pikiran anda?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Tandai media sensitif",
- "compose_form.spoiler": "Sembunyikan teks dibalik peringatan",
+ "compose_form.sensitive.marked": "Sumber ini telah ditandai sebagai sumber sensitif.",
+ "compose_form.sensitive.unmarked": "Sumber ini tidak ditandai sebagai sumber sensitif",
+ "compose_form.spoiler.marked": "Teks disembunyikan dibalik peringatan",
+ "compose_form.spoiler.unmarked": "Teks tidak tersembunyi",
"compose_form.spoiler_placeholder": "Peringatan konten",
"confirmation_modal.cancel": "Batal",
"confirmations.block.confirm": "Blokir",
"confirmations.block.message": "Apa anda yakin ingin memblokir {name}?",
"confirmations.delete.confirm": "Hapus",
- "confirmations.delete.message": "Apa anda yakin akan menghapus status ini?",
+ "confirmations.delete.message": "Apa anda yakin untuk menghapus status ini?",
"confirmations.delete_list.confirm": "Delete",
- "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
- "confirmations.domain_block.confirm": "Hide entire domain",
- "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
+ "confirmations.delete_list.message": "Apakah anda yakin untuk menghapus daftar ini secara permanen?",
+ "confirmations.domain_block.confirm": "Sembunyikan keseluruhan domain",
+ "confirmations.domain_block.message": "Apakah anda benar benar yakin untuk memblokir keseluruhan {domain}? Dalam kasus tertentu beberapa pemblokiran atau penyembunyian lebih baik.",
"confirmations.mute.confirm": "Bisukan",
"confirmations.mute.message": "Apa anda yakin ingin membisukan {name}?",
- "confirmations.unfollow.confirm": "Unfollow",
- "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
- "embed.instructions": "Embed this status on your website by copying the code below.",
- "embed.preview": "Here is what it will look like:",
+ "confirmations.unfollow.confirm": "Berhenti mengikuti",
+ "confirmations.unfollow.message": "Apakah anda ingin berhenti mengikuti {name}?",
+ "embed.instructions": "Sematkan status ini di website anda dengan menyalin kode di bawah ini.",
+ "embed.preview": "Seperti ini nantinya:",
"emoji_button.activity": "Aktivitas",
- "emoji_button.custom": "Custom",
+ "emoji_button.custom": "Kustom",
"emoji_button.flags": "Bendera",
"emoji_button.food": "Makanan & Minuman",
"emoji_button.label": "Tambahkan emoji",
"emoji_button.nature": "Alam",
- "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+ "emoji_button.not_found": "Katakan tidak pada emoji!! (╯°□°)╯︵ ┻━┻",
"emoji_button.objects": "Benda-benda",
"emoji_button.people": "Orang",
- "emoji_button.recent": "Frequently used",
+ "emoji_button.recent": "Yang sering digunakan",
"emoji_button.search": "Cari...",
- "emoji_button.search_results": "Search results",
+ "emoji_button.search_results": "Hasil pencarian",
"emoji_button.symbols": "Simbol",
"emoji_button.travel": "Tempat Wisata",
"empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!",
"empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.",
- "empty_column.home": "Anda sedang tidak mengikuti siapapun. Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
+ "empty_column.home": "Linimasa anda kosong! Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
"empty_column.home.public_timeline": "linimasa publik",
- "empty_column.list": "There is nothing in this list yet.",
+ "empty_column.list": "Tidak ada postingan di list ini. Ketika anggota dari list ini memposting status baru, status tersebut akan tampil disini.",
"empty_column.notifications": "Anda tidak memiliki notifikasi apapun. Berinteraksi dengan orang lain untuk memulai percakapan.",
- "empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisinya secara manual",
+ "empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisi ini",
"follow_request.authorize": "Izinkan",
"follow_request.reject": "Tolak",
- "getting_started.appsshort": "Apps",
+ "getting_started.appsshort": "Aplikasi",
"getting_started.faq": "FAQ",
"getting_started.heading": "Mulai",
- "getting_started.open_source_notice": "Mastodon adalah perangkat lunak yang bersifat open source. Anda dapat berkontribusi atau melaporkan permasalahan/bug di Github {github}.",
- "getting_started.userguide": "User Guide",
+ "getting_started.open_source_notice": "Mastodon adalah perangkat lunak yang bersifat terbuka. Anda dapat berkontribusi atau melaporkan permasalahan/bug di Github {github}.",
+ "getting_started.userguide": "Panduan Pengguna",
"home.column_settings.advanced": "Tingkat Lanjut",
"home.column_settings.basic": "Dasar",
- "home.column_settings.filter_regex": "Penyaringan dengan Regular Expression",
- "home.column_settings.show_reblogs": "Tampilkan Boost",
+ "home.column_settings.filter_regex": "Saring dengan regular expressions",
+ "home.column_settings.show_reblogs": "Tampilkan boost",
"home.column_settings.show_replies": "Tampilkan balasan",
"home.settings": "Pengaturan kolom",
- "keyboard_shortcuts.back": "to navigate back",
- "keyboard_shortcuts.boost": "to boost",
- "keyboard_shortcuts.column": "to focus a status in one of the columns",
- "keyboard_shortcuts.compose": "to focus the compose textarea",
- "keyboard_shortcuts.description": "Description",
- "keyboard_shortcuts.down": "to move down in the list",
- "keyboard_shortcuts.enter": "to open status",
- "keyboard_shortcuts.favourite": "to favourite",
- "keyboard_shortcuts.heading": "Keyboard Shortcuts",
+ "keyboard_shortcuts.back": "untuk kembali",
+ "keyboard_shortcuts.boost": "untuk menyebarkan",
+ "keyboard_shortcuts.column": "untuk fokus kepada sebuah status di sebuah kolom",
+ "keyboard_shortcuts.compose": "untuk fokus ke area penulisan",
+ "keyboard_shortcuts.description": "Deskripsi",
+ "keyboard_shortcuts.down": "untuk pindah ke bawah dalam sebuah daftar",
+ "keyboard_shortcuts.enter": "untuk membuka status",
+ "keyboard_shortcuts.favourite": "untuk memfavoritkan",
+ "keyboard_shortcuts.heading": "Pintasan keyboard",
"keyboard_shortcuts.hotkey": "Hotkey",
"keyboard_shortcuts.legend": "to display this legend",
"keyboard_shortcuts.mention": "to mention author",
"keyboard_shortcuts.reply": "to reply",
- "keyboard_shortcuts.search": "to focus search",
+ "keyboard_shortcuts.search": "untuk fokus mencari",
"keyboard_shortcuts.toot": "to start a brand new toot",
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
"keyboard_shortcuts.up": "to move up in the list",
@@ -193,30 +199,37 @@
"privacy.change": "Tentukan privasi status",
"privacy.direct.long": "Kirim hanya ke pengguna yang disebut",
"privacy.direct.short": "Langsung",
- "privacy.private.long": "Kirim hanya ke pengikut",
+ "privacy.private.long": "Kirim postingan hanya kepada pengikut",
"privacy.private.short": "Pribadi",
"privacy.public.long": "Kirim ke linimasa publik",
"privacy.public.short": "Publik",
"privacy.unlisted.long": "Tidak ditampilkan di linimasa publik",
"privacy.unlisted.short": "Tak Terdaftar",
"regeneration_indicator.label": "Loading…",
- "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+ "regeneration_indicator.sublabel": "Linimasa anda sedang disiapkan!",
"relative_time.days": "{number}d",
"relative_time.hours": "{number}h",
"relative_time.just_now": "now",
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Batal",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Komentar tambahan",
"report.submit": "Kirim",
"report.target": "Melaporkan",
"search.placeholder": "Pencarian",
"search_popout.search_format": "Advanced search format",
- "search_popout.tips.hashtag": "hashtag",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+ "search_popout.tips.hashtag": "tagar",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
- "search_results.total": "{count} {count, plural, one {hasil} other {hasil}}",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
+ "search_results.total": "{count, number} {count, plural, one {hasil} other {hasil}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
"status.cannot_reblog": "This post cannot be boosted",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Tampilkan status ini",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Boost",
"status.reblogged_by": "di-boost {name}",
"status.reply": "Balas",
@@ -240,23 +254,25 @@
"status.sensitive_warning": "Konten sensitif",
"status.share": "Share",
"status.show_less": "Tampilkan lebih sedikit",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Tampilkan semua",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Tulis",
"tabs_bar.federated_timeline": "Gabungan",
"tabs_bar.home": "Beranda",
"tabs_bar.local_timeline": "Lokal",
"tabs_bar.notifications": "Notifikasi",
- "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+ "ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.",
"upload_area.title": "Seret & lepaskan untuk mengunggah",
"upload_button.label": "Tambahkan media",
- "upload_form.description": "Describe for the visually impaired",
+ "upload_form.description": "Deskripsikan untuk mereka yang tidak bisa melihat dengan jelas",
+ "upload_form.focus": "Potong",
"upload_form.undo": "Undo",
"upload_progress.label": "Mengunggah...",
"video.close": "Close video",
- "video.exit_fullscreen": "Exit full screen",
- "video.expand": "Expand video",
+ "video.exit_fullscreen": "Keluar dari layar penuh",
+ "video.expand": "Perbesar video",
"video.fullscreen": "Full screen",
"video.hide": "Hide video",
"video.mute": "Mute sound",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 7aa7cb144..5ea982f46 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -1,7 +1,9 @@
{
"account.block": "Blokusar @{name}",
"account.block_domain": "Hide everything from {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Modifikar profilo",
"account.follow": "Sequar",
"account.followers": "Sequanti",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Celar @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "Mesaji",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Denuncar @{name}",
"account.requested": "Vartante aprobo",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Quo esas en tua spirito?",
"compose_form.publish": "Siflar",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Markizar kontenajo kom trubliva",
- "compose_form.spoiler": "Celar texto dop averto",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Averto di kontenajo",
"confirmation_modal.cancel": "Cancel",
"confirmations.block.confirm": "Block",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Nihiligar",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Plusa komenti",
"report.submit": "Sendar",
"report.target": "Denuncante",
"search.placeholder": "Serchez",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Detaligar ca mesajo",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Repetar",
"status.reblogged_by": "{name} repetita",
"status.reply": "Respondar",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Trubliva kontenajo",
"status.share": "Share",
"status.show_less": "Montrar mine",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Montrar plue",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Kompozar",
"tabs_bar.federated_timeline": "Federata",
"tabs_bar.home": "Hemo",
"tabs_bar.local_timeline": "Lokala",
@@ -252,6 +267,7 @@
"upload_area.title": "Tranar faligar por kargar",
"upload_button.label": "Adjuntar kontenajo",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Desfacar",
"upload_progress.label": "Kargante...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 61467df16..068598de2 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -1,7 +1,9 @@
{
"account.block": "Blocca @{name}",
"account.block_domain": "Hide everything from {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Modifica profilo",
"account.follow": "Segui",
"account.followers": "Seguaci",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Silenzia @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "Posts",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Segnala @{name}",
"account.requested": "In attesa di approvazione",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "A cosa stai pensando?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Segnala file come sensibile",
- "compose_form.spoiler": "Nascondi testo con avvertimento",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Content warning",
"confirmation_modal.cancel": "Cancel",
"confirmations.block.confirm": "Block",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Annulla",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Commenti aggiuntivi",
"report.submit": "Invia",
"report.target": "Invio la segnalazione",
"search.placeholder": "Cerca",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count} {count, plural, one {risultato} other {risultati}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Espandi questo post",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Condividi",
"status.reblogged_by": "{name} ha condiviso",
"status.reply": "Rispondi",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Materiale sensibile",
"status.share": "Share",
"status.show_less": "Mostra meno",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Mostra di più",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Scrivi",
"tabs_bar.federated_timeline": "Federazione",
"tabs_bar.home": "Home",
"tabs_bar.local_timeline": "Locale",
@@ -252,6 +267,7 @@
"upload_area.title": "Trascina per caricare",
"upload_button.label": "Aggiungi file multimediale",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Annulla",
"upload_progress.label": "Sto caricando...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index b239d5102..0b88ac2df 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -1,7 +1,9 @@
{
"account.block": "@{name}さんをブロック",
"account.block_domain": "{domain}全体を非表示",
+ "account.blocked": "ブロック済み",
"account.disclaimer_full": "以下の情報は不正確な可能性があります。",
+ "account.domain_blocked": "ドメイン非表示中",
"account.edit_profile": "プロフィールを編集",
"account.follow": "フォロー",
"account.followers": "フォロワー",
@@ -13,12 +15,14 @@
"account.moved_to": "{name}さんは引っ越しました:",
"account.mute": "@{name}さんをミュート",
"account.mute_notifications": "@{name}さんからの通知を受け取らない",
+ "account.muted": "ミュート済み",
"account.posts": "投稿",
+ "account.posts_with_replies": "投稿と返信",
"account.report": "@{name}さんを通報",
"account.requested": "フォロー承認待ちです。クリックしてキャンセル",
"account.share": "@{name}さんのプロフィールを共有する",
"account.show_reblogs": "@{name}さんからのブーストを表示",
- "account.unblock": "@{name}さんのブロック解除",
+ "account.unblock": "@{name}さんのブロックを解除",
"account.unblock_domain": "{domain}を表示",
"account.unfollow": "フォロー解除",
"account.unmute": "@{name}さんのミュートを解除",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "今なにしてる?",
"compose_form.publish": "トゥート",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "メディアを閲覧注意としてマークする",
- "compose_form.spoiler": "テキストを隠す",
+ "compose_form.sensitive.marked": "メディアに閲覧注意が設定されています",
+ "compose_form.sensitive.unmarked": "メディアに閲覧注意が設定されていません",
+ "compose_form.spoiler.marked": "閲覧注意が設定されています",
+ "compose_form.spoiler.unmarked": "閲覧注意が設定されていません",
"compose_form.spoiler_placeholder": "ここに警告を書いてください",
"confirmation_modal.cancel": "キャンセル",
"confirmations.block.confirm": "ブロック",
@@ -67,7 +73,7 @@
"confirmations.delete_list.confirm": "削除",
"confirmations.delete_list.message": "本当にこのリストを完全に削除しますか?",
"confirmations.domain_block.confirm": "ドメイン全体を非表示",
- "confirmations.domain_block.message": "本当に{domain}全体を非表示にしますか? 多くの場合は個別にブロックやミュートするだけで充分であり、また好ましいです。",
+ "confirmations.domain_block.message": "本当に{domain}全体を非表示にしますか? 多くの場合は個別にブロックやミュートするだけで充分であり、また好ましいです。",
"confirmations.mute.confirm": "ミュート",
"confirmations.mute.message": "本当に{name}さんをミュートしますか?",
"confirmations.unfollow.confirm": "フォロー解除",
@@ -200,22 +206,29 @@
"privacy.unlisted.long": "公開TLで表示しない",
"privacy.unlisted.short": "未収載",
"regeneration_indicator.label": "読み込み中…",
- "regeneration_indicator.sublabel": "ホームタイムラインは準備中です!",
+ "regeneration_indicator.sublabel": "ホームタイムラインは準備中です!",
"relative_time.days": "{number}日前",
"relative_time.hours": "{number}時間前",
"relative_time.just_now": "今",
"relative_time.minutes": "{number}分前",
"relative_time.seconds": "{number}秒前",
"reply_indicator.cancel": "キャンセル",
+ "report.forward": "{target} に転送する",
+ "report.forward_hint": "このアカウントは別のインスタンスに所属しています。通報内容を匿名で転送しますか?",
+ "report.hint": "通報内容はあなたのインスタンスのモデレーターへ送信されます。通報理由を入力してください。:",
"report.placeholder": "追加コメント",
"report.submit": "通報する",
"report.target": "{target}さんを通報する",
"search.placeholder": "検索",
"search_popout.search_format": "高度な検索フォーマット",
+ "search_popout.tips.full_text": "表示名やユーザー名、ハッシュタグのほか、あなたのトゥートやお気に入り、ブーストしたトゥート、返信に一致する単純なテキスト。",
"search_popout.tips.hashtag": "ハッシュタグ",
"search_popout.tips.status": "トゥート",
"search_popout.tips.text": "表示名やユーザー名、ハッシュタグに一致する単純なテキスト",
"search_popout.tips.user": "ユーザー",
+ "search_results.accounts": "人々",
+ "search_results.hashtags": "ハッシュタグ",
+ "search_results.statuses": "トゥート",
"search_results.total": "{count, number}件の結果",
"standalone.public_title": "今こんな話をしています...",
"status.block": "@{name}さんをブロック",
@@ -231,8 +244,9 @@
"status.mute_conversation": "会話をミュート",
"status.open": "詳細を表示",
"status.pin": "プロフィールに固定表示",
+ "status.pinned": "固定されたトゥート",
"status.reblog": "ブースト",
- "status.reblogged_by": "{name}さんにブーストされました",
+ "status.reblogged_by": "{name}さんがブースト",
"status.reply": "返信",
"status.replyAll": "全員に返信",
"status.report": "@{name}さんを通報",
@@ -240,18 +254,20 @@
"status.sensitive_warning": "閲覧注意",
"status.share": "共有",
"status.show_less": "隠す",
+ "status.show_less_all": "全て隠す",
"status.show_more": "もっと見る",
+ "status.show_more_all": "全て見る",
"status.unmute_conversation": "会話のミュートを解除",
"status.unpin": "プロフィールの固定表示を解除",
- "tabs_bar.compose": "投稿",
"tabs_bar.federated_timeline": "連合",
"tabs_bar.home": "ホーム",
"tabs_bar.local_timeline": "ローカル",
"tabs_bar.notifications": "通知",
- "ui.beforeunload": "Mastodonから離れるとあなたのドラフトは失われます。",
+ "ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
"upload_area.title": "ドラッグ&ドロップでアップロード",
"upload_button.label": "メディアを追加",
"upload_form.description": "視覚障害者のための説明",
+ "upload_form.focus": "焦点",
"upload_form.undo": "やり直す",
"upload_progress.label": "アップロード中...",
"video.close": "動画を閉じる",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 02b4b18e2..532c1f04d 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -1,7 +1,9 @@
{
"account.block": "@{name}을 차단",
"account.block_domain": "{domain} 전체를 숨김",
+ "account.blocked": "차단 됨",
"account.disclaimer_full": "여기 있는 정보는 유저의 프로파일을 정확히 반영하지 못 할 수도 있습니다.",
+ "account.domain_blocked": "도메인 숨겨짐",
"account.edit_profile": "프로필 편집",
"account.follow": "팔로우",
"account.followers": "팔로워",
@@ -13,7 +15,9 @@
"account.moved_to": "{name}는 계정을 이동했습니다:",
"account.mute": "@{name} 뮤트",
"account.mute_notifications": "@{name}의 알림을 뮤트",
+ "account.muted": "뮤트 됨",
"account.posts": "게시물",
+ "account.posts_with_replies": "툿과 답장",
"account.report": "@{name} 신고",
"account.requested": "승인 대기 중. 클릭해서 취소하기",
"account.share": "@{name}의 프로파일 공유",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "지금 무엇을 하고 있나요?",
"compose_form.publish": "툿",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "이 미디어를 민감한 미디어로 취급",
- "compose_form.spoiler": "텍스트 숨기기",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "경고",
"confirmation_modal.cancel": "취소",
"confirmations.block.confirm": "차단",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}분 전",
"relative_time.seconds": "{number}초 전",
"reply_indicator.cancel": "취소",
+ "report.forward": "{target}에 포워드 됨",
+ "report.forward_hint": "이 계정은 다른 서버에 있습니다. 익명화 된 사본을 해당 서버에도 전송할까요?",
+ "report.hint": "신고는 당신의 서버 스태프에게 전송 됩니다. 왜 이 계정을 신고하는 지에 대한 설명을 아래에 작성할 수 있습니다:",
"report.placeholder": "코멘트",
"report.submit": "신고하기",
"report.target": "문제가 된 사용자",
"search.placeholder": "검색",
"search_popout.search_format": "고급 검색 방법",
+ "search_popout.tips.full_text": "단순한 텍스트 검색은 당신이 작성했거나, 관심글로 지정했거나, 부스트했거나, 멘션을 받은 게시글, 그리고 유저네임, 디스플레이네임, 해시태그를 반환합니다.",
"search_popout.tips.hashtag": "해시태그",
"search_popout.tips.status": "툿",
"search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다",
"search_popout.tips.user": "유저",
+ "search_results.accounts": "사람",
+ "search_results.hashtags": "해시태그",
+ "search_results.statuses": "툿",
"search_results.total": "{count, number}건의 결과",
"standalone.public_title": "지금 이런 이야기를 하고 있습니다…",
"status.block": "@{name} 차단",
@@ -231,6 +244,7 @@
"status.mute_conversation": "이 대화를 뮤트",
"status.open": "상세 정보 표시",
"status.pin": "고정",
+ "status.pinned": "고정 된 툿",
"status.reblog": "부스트",
"status.reblogged_by": "{name}님이 부스트 했습니다",
"status.reply": "답장",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "민감한 미디어",
"status.share": "공유",
"status.show_less": "숨기기",
+ "status.show_less_all": "Show less for all",
"status.show_more": "더 보기",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "이 대화의 뮤트 해제하기",
"status.unpin": "고정 해제",
- "tabs_bar.compose": "포스트",
"tabs_bar.federated_timeline": "연합",
"tabs_bar.home": "홈",
"tabs_bar.local_timeline": "로컬",
@@ -252,6 +267,7 @@
"upload_area.title": "드래그 & 드롭으로 업로드",
"upload_button.label": "미디어 추가",
"upload_form.description": "시각장애인을 위한 설명",
+ "upload_form.focus": "크롭",
"upload_form.undo": "재시도",
"upload_progress.label": "업로드 중...",
"video.close": "동영상 닫기",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 11a012de5..a83971f00 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -1,7 +1,9 @@
{
"account.block": "Blokkeer @{name}",
"account.block_domain": "Negeer alles van {domain}",
+ "account.blocked": "Geblokkeerd",
"account.disclaimer_full": "De informatie hieronder kan mogelijk een incompleet beeld geven van dit gebruikersprofiel.",
+ "account.domain_blocked": "Domein verborgen",
"account.edit_profile": "Profiel bewerken",
"account.follow": "Volgen",
"account.followers": "Volgers",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} is verhuisd naar:",
"account.mute": "Negeer @{name}",
"account.mute_notifications": "Negeer meldingen van @{name}",
+ "account.muted": "Genegeerd",
"account.posts": "Toots",
+ "account.posts_with_replies": "Toots met reacties",
"account.report": "Rapporteer @{name}",
"account.requested": "Wacht op goedkeuring. Klik om het volgverzoek te annuleren",
"account.share": "Profiel van @{name} delen",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Wat wil je kwijt?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Media als gevoelig markeren (nsfw)",
- "compose_form.spoiler": "Tekst achter waarschuwing verbergen",
+ "compose_form.sensitive.marked": "Media is als gevoelig gemarkeerd",
+ "compose_form.sensitive.unmarked": "Media is niet als gevoelig gemarkeerd",
+ "compose_form.spoiler.marked": "Tekst is achter een waarschuwing verborgen",
+ "compose_form.spoiler.unmarked": "Tekst is niet verborgen",
"compose_form.spoiler_placeholder": "Waarschuwingstekst",
"confirmation_modal.cancel": "Annuleren",
"confirmations.block.confirm": "Blokkeren",
@@ -207,18 +213,25 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Annuleren",
+ "report.forward": "Doorsturen naar {target}",
+ "report.forward_hint": "Het account bevindt zich op een andere server. Stuur daar eveneens een geanonimiseerde kopie van de gerapporteerde toot(s) naartoe?",
+ "report.hint": "De gerapporteerde toot(s) worden naar de moderatoren van jouw server gestuurd. Je kunt hieronder een uitleg geven waarom je dit account rapporteert:",
"report.placeholder": "Extra opmerkingen",
"report.submit": "Verzenden",
"report.target": "Rapporteer {target}",
"search.placeholder": "Zoeken",
"search_popout.search_format": "Geavanceerd zoeken",
+ "search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken naar jouw toots, gebooste toots, favorieten en naar toots waarin jij bent vermeldt, en naar gebruikersnamen, weergavenamen en hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "toot",
"search_popout.tips.text": "Gebruik gewone tekst om te zoeken op weergavenamen, gebruikersnamen en hashtags",
"search_popout.tips.user": "gebruiker",
+ "search_results.accounts": "Gebruikers",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}",
"standalone.public_title": "Een kijkje binnenin...",
- "status.block": "Block @{name}",
+ "status.block": "Blokkeer @{name}",
"status.cannot_reblog": "Deze toot kan niet geboost worden",
"status.delete": "Verwijderen",
"status.embed": "Embed",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Negeer conversatie",
"status.open": "Toot volledig tonen",
"status.pin": "Aan profielpagina vastmaken",
+ "status.pinned": "Vastgemaakte toot",
"status.reblog": "Boost",
"status.reblogged_by": "{name} boostte",
"status.reply": "Reageren",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Gevoelige inhoud",
"status.share": "Delen",
"status.show_less": "Minder tonen",
+ "status.show_less_all": "Alles minder tonen",
"status.show_more": "Meer tonen",
+ "status.show_more_all": "Alles meer tonen",
"status.unmute_conversation": "Conversatie niet meer negeren",
"status.unpin": "Van profielpagina losmaken",
- "tabs_bar.compose": "Schrijven",
"tabs_bar.federated_timeline": "Globaal",
"tabs_bar.home": "Start",
"tabs_bar.local_timeline": "Lokaal",
@@ -252,6 +267,7 @@
"upload_area.title": "Hierin slepen om te uploaden",
"upload_button.label": "Media toevoegen",
"upload_form.description": "Omschrijf dit voor mensen met een visuele beperking",
+ "upload_form.focus": "Bijsnijden",
"upload_form.undo": "Ongedaan maken",
"upload_progress.label": "Uploaden...",
"video.close": "Video sluiten",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 21fd50183..aaad033e2 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -1,7 +1,9 @@
{
"account.block": "Blokkér @{name}",
"account.block_domain": "Skjul alt fra {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Informasjonen nedenfor kan gi et ufullstendig bilde av brukerens profil.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Rediger profil",
"account.follow": "Følg",
"account.followers": "Følgere",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} har flyttet til:",
"account.mute": "Demp @{name}",
"account.mute_notifications": "Ignorer varsler fra @{name}",
+ "account.muted": "Muted",
"account.posts": "Innlegg",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Rapportér @{name}",
"account.requested": "Venter på godkjennelse",
"account.share": "Del @{name}s profil",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Hva har du på hjertet?",
"compose_form.publish": "Tut",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Merk media som følsomt",
- "compose_form.spoiler": "Skjul tekst bak advarsel",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Innholdsadvarsel",
"confirmation_modal.cancel": "Avbryt",
"confirmations.block.confirm": "Blokkèr",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Avbryt",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Tilleggskommentarer",
"report.submit": "Send inn",
"report.target": "Rapporterer",
"search.placeholder": "Søk",
"search_popout.search_format": "Avansert søkeformat",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "emneknagg",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Enkel tekst returnerer matchende visningsnavn, brukernavn og emneknagger",
"search_popout.tips.user": "bruker",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}",
"standalone.public_title": "En titt inni...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Demp samtale",
"status.open": "Utvid denne statusen",
"status.pin": "Fest på profilen",
+ "status.pinned": "Pinned toot",
"status.reblog": "Fremhev",
"status.reblogged_by": "Fremhevd av {name}",
"status.reply": "Svar",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Følsomt innhold",
"status.share": "Del",
"status.show_less": "Vis mindre",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Vis mer",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Ikke demp samtale",
"status.unpin": "Angre festing på profilen",
- "tabs_bar.compose": "Komponer",
"tabs_bar.federated_timeline": "Felles",
"tabs_bar.home": "Hjem",
"tabs_bar.local_timeline": "Lokal",
@@ -252,6 +267,7 @@
"upload_area.title": "Dra og slipp for å laste opp",
"upload_button.label": "Legg til media",
"upload_form.description": "Beskriv for synshemmede",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Angre",
"upload_progress.label": "Laster opp...",
"video.close": "Lukk video",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 3cf99028a..c7a26c1c6 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -1,7 +1,9 @@
{
"account.block": "Blocar @{name}",
"account.block_domain": "Tot amagar del domeni {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Aquelas informacions de perfil pòdon èsser incomplètas.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Modificar lo perfil",
"account.follow": "Sègre",
"account.followers": "Seguidors",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} a mudat los catons a :",
"account.mute": "Rescondre @{name}",
"account.mute_notifications": "Rescondre las notificacions de @{name}",
+ "account.muted": "Muted",
"account.posts": "Estatuts",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Senhalar @{name}",
"account.requested": "Invitacion mandada. Clicatz per anullar",
"account.share": "Partejar lo perfil a @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "A de qué pensatz ?",
"compose_form.publish": "Tut",
"compose_form.publish_loud": "{publish} !",
- "compose_form.sensitive": "Marcar lo mèdia coma sensible",
- "compose_form.spoiler": "Rescondre lo tèxte darrièr un avertiment",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Escrivètz l’avertiment aquí",
"confirmation_modal.cancel": "Anullar",
"confirmations.block.confirm": "Blocar",
@@ -207,15 +213,22 @@
"relative_time.minutes": "fa {number}min",
"relative_time.seconds": "fa {number}s",
"reply_indicator.cancel": "Anullar",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Comentaris addicionals",
"report.submit": "Mandar",
"report.target": "Senhalar {target}",
"search.placeholder": "Recercar",
"search_popout.search_format": "Format recèrca avançada",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "etiqueta",
"search_popout.tips.status": "estatut",
"search_popout.tips.text": "Lo tèxt brut tòrna escais, noms d’utilizaire e etiquetas correspondents",
"search_popout.tips.user": "utilizaire",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
"standalone.public_title": "Una ulhada dedins…",
"status.block": "Blocar @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Rescondre la conversacion",
"status.open": "Desplegar aqueste estatut",
"status.pin": "Penjar al perfil",
+ "status.pinned": "Pinned toot",
"status.reblog": "Partejar",
"status.reblogged_by": "{name} a partejat",
"status.reply": "Respondre",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Contengut sensible",
"status.share": "Partejar",
"status.show_less": "Tornar plegar",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Desplegar",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Tornar mostrar la conversacion",
"status.unpin": "Tirar del perfil",
- "tabs_bar.compose": "Compausar",
"tabs_bar.federated_timeline": "Flux public global",
"tabs_bar.home": "Acuèlh",
"tabs_bar.local_timeline": "Flux public local",
@@ -252,6 +267,7 @@
"upload_area.title": "Lisatz e depausatz per mandar",
"upload_button.label": "Ajustar un mèdia",
"upload_form.description": "Descripcion pels mal vesents",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Anullar",
"upload_progress.label": "Mandadís…",
"video.close": "Tampar la vidèo",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 81ec79776..8496495f5 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -1,19 +1,23 @@
{
"account.block": "Blokuj @{name}",
"account.block_domain": "Blokuj wszystko z {domain}",
+ "account.blocked": "Zablokowany",
"account.disclaimer_full": "Poniższe informacje mogą nie odwzorowywać bezbłędnie profilu użytkownika.",
+ "account.domain_blocked": "Ukryto domenę",
"account.edit_profile": "Edytuj profil",
"account.follow": "Śledź",
"account.followers": "Śledzący",
"account.follows": "Śledzeni",
"account.follows_you": "Śledzi Cię",
"account.hide_reblogs": "Ukryj podbicia od @{name}",
- "account.media": "Media",
+ "account.media": "Zawartość multimedialna",
"account.mention": "Wspomnij o @{name}",
"account.moved_to": "{name} przeniósł się do:",
"account.mute": "Wycisz @{name}",
"account.mute_notifications": "Wycisz powiadomienia o @{name}",
+ "account.muted": "Wyciszony",
"account.posts": "Wpisy",
+ "account.posts_with_replies": "Wpisy z odpowiedziami",
"account.report": "Zgłoś @{name}",
"account.requested": "Oczekująca prośba, kliknij aby anulować",
"account.share": "Udostępnij profil @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Co Ci chodzi po głowie?",
"compose_form.publish": "Wyślij",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Oznacz treści jako wrażliwe",
- "compose_form.spoiler": "Ukryj tekst za ostrzeżeniem",
+ "compose_form.sensitive.marked": "Zawartość multimedia jest oznaczona jako wrażliwa",
+ "compose_form.sensitive.unmarked": "Zawartość multimedialna nie jest oznaczona jako wrażliwa",
+ "compose_form.spoiler.marked": "Tekst jest ukryty za ostrzeżeniem",
+ "compose_form.spoiler.unmarked": "Tekst nie jest ukryty",
"compose_form.spoiler_placeholder": "Wprowadź swoje ostrzeżenie o zawartości",
"confirmation_modal.cancel": "Anuluj",
"confirmations.block.confirm": "Zablokuj",
@@ -94,7 +100,7 @@
"empty_column.home.public_timeline": "publiczna oś czasu",
"empty_column.list": "Nie ma nic na tej liście. Kiedy członkowie listy dodadzą nowe wpisy, pojawia się one tutaj.",
"empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.",
- "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.",
+ "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić",
"follow_request.authorize": "Autoryzuj",
"follow_request.reject": "Odrzuć",
"getting_started.appsshort": "Aplikacje",
@@ -129,17 +135,17 @@
"lightbox.next": "Następne",
"lightbox.previous": "Poprzednie",
"lists.account.add": "Dodaj do listy",
- "lists.account.remove": "Remove from list",
+ "lists.account.remove": "Usunąć z listy",
"lists.delete": "Usuń listę",
"lists.edit": "Edytuj listę",
"lists.new.create": "Utwórz listę",
- "lists.new.title_placeholder": "Wprowadź tytuł listy…",
+ "lists.new.title_placeholder": "Wprowadź tytuł listy",
"lists.search": "Szukaj wśród osób które śledzisz",
"lists.subheading": "Twoje listy",
"loading_indicator.label": "Ładowanie…",
"media_gallery.toggle_visible": "Przełącz widoczność",
"missing_indicator.label": "Nie znaleziono",
- "missing_indicator.sublabel": "This resource could not be found",
+ "missing_indicator.sublabel": "Nie można odnaleźć tego zasobu",
"mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?",
"navigation_bar.blocks": "Zablokowani użytkownicy",
"navigation_bar.community_timeline": "Lokalna oś czasu",
@@ -199,23 +205,30 @@
"privacy.public.short": "Publiczny",
"privacy.unlisted.long": "Niewidoczny na publicznych osiach czasu",
"privacy.unlisted.short": "Niewidoczny",
- "regeneration_indicator.label": "Loading…",
- "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+ "regeneration_indicator.label": "Ładuję…",
+ "regeneration_indicator.sublabel": "Twoja oś czasu jest przygotowywana!",
"relative_time.days": "{number} dni",
"relative_time.hours": "{number} godz.",
"relative_time.just_now": "teraz",
"relative_time.minutes": "{number} min.",
"relative_time.seconds": "{number} s.",
"reply_indicator.cancel": "Anuluj",
+ "report.forward": "Przekaż na {target}",
+ "report.forward_hint": "To konto znajduje się na innej instancji. Czy chcesz wysłać anonimową kopię zgłoszenia rnież na nią?",
+ "report.hint": "Zgłoszenie zostanie wysłane moderatorom Twojej instancji. Poniżej możesz też umieścić wyjaśnieni dlaczego zgłaszasz to konto:",
"report.placeholder": "Dodatkowe komentarze",
"report.submit": "Wyślij",
"report.target": "Zgłaszanie {target}",
"search.placeholder": "Szukaj",
"search_popout.search_format": "Zaawansowane wyszukiwanie",
+ "search_popout.tips.full_text": "Pozwala na wyszukiwanie wpisów które napisałeś, dodałeś do ulubionych, podbiłeś w których o Tobie wspomniano, oraz pasujące nazwy użytkowników, pełne nazwy i hashtagi.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "wpis",
"search_popout.tips.text": "Proste wyszukiwanie pasujących pseudonimów, nazw użytkowników i hashtagów",
"search_popout.tips.user": "użytkownik",
+ "search_results.accounts": "Ludzie",
+ "search_results.hashtags": "Hashtagi",
+ "search_results.statuses": "Wpisy",
"search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}",
"standalone.public_title": "Spojrzenie w głąb…",
"status.block": "Zablokuj @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Wycisz konwersację",
"status.open": "Rozszerz ten wpis",
"status.pin": "Przypnij do profilu",
+ "status.pinned": "Przypięty wpis",
"status.reblog": "Podbij",
"status.reblogged_by": "{name} podbił",
"status.reply": "Odpowiedz",
@@ -239,11 +253,12 @@
"status.sensitive_toggle": "Naciśnij aby wyświetlić",
"status.sensitive_warning": "Wrażliwa zawartość",
"status.share": "Udostępnij",
- "status.show_less": "Pokaż mniej",
- "status.show_more": "Pokaż więcej",
+ "status.show_less": "Zwiń",
+ "status.show_less_all": "Zwiń wszystkie",
+ "status.show_more": "Rozwiń",
+ "status.show_more_all": "Rozwiń wszystkie",
"status.unmute_conversation": "Cofnij wyciszenie konwersacji",
"status.unpin": "Odepnij z profilu",
- "tabs_bar.compose": "Napisz",
"tabs_bar.federated_timeline": "Globalne",
"tabs_bar.home": "Strona główna",
"tabs_bar.local_timeline": "Lokalne",
@@ -252,6 +267,7 @@
"upload_area.title": "Przeciągnij i upuść aby wysłać",
"upload_button.label": "Dodaj zawartość multimedialną",
"upload_form.description": "Wprowadź opis dla niewidomych i niedowidzących",
+ "upload_form.focus": "Przytnij",
"upload_form.undo": "Cofnij",
"upload_progress.label": "Wysyłanie",
"video.close": "Zamknij film",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 3d63da850..c90fb37a0 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -1,7 +1,9 @@
{
"account.block": "Bloquear @{name}",
"account.block_domain": "Esconder tudo de {domain}",
+ "account.blocked": "Bloqueado",
"account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de maneira incompleta.",
+ "account.domain_blocked": "Domínio escondido",
"account.edit_profile": "Editar perfil",
"account.follow": "Seguir",
"account.followers": "Seguidores",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} se mudou para:",
"account.mute": "Silenciar @{name}",
"account.mute_notifications": "Silenciar notificações de @{name}",
- "account.posts": "Posts",
+ "account.muted": "Silenciado",
+ "account.posts": "Toots",
+ "account.posts_with_replies": "Toots e respostas",
"account.report": "Denunciar @{name}",
"account.requested": "Aguardando aprovação. Clique para cancelar a solicitação",
"account.share": "Compartilhar perfil de @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "No que você está pensando?",
"compose_form.publish": "Publicar",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Marcar mídia como conteúdo sensível",
- "compose_form.spoiler": "Esconder texto com aviso de conteúdo",
+ "compose_form.sensitive.marked": "Mídia está marcada como sensível",
+ "compose_form.sensitive.unmarked": "Mídia não está marcada como sensível",
+ "compose_form.spoiler.marked": "O texto está escondido por um aviso de conteúdo",
+ "compose_form.spoiler.unmarked": "O texto não está escondido",
"compose_form.spoiler_placeholder": "Aviso de conteúdo",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.confirm": "Bloquear",
@@ -90,7 +96,7 @@
"emoji_button.travel": "Viagens & Lugares",
"empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!",
"empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.",
- "empty_column.home": "Você ainda não segue usuário algo. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.",
+ "empty_column.home": "Você ainda não segue usuário algum. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.",
"empty_column.home.public_timeline": "global",
"empty_column.list": "Ainda não há nada nesta lista. Quando membros dessa lista fizerem novas postagens, elas aparecerão aqui.",
"empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar.",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Cancelar",
+ "report.forward": "Encaminhar para {target}",
+ "report.forward_hint": "Essa conta pertence à um outro servidor. Encaminhar uma cópia da denúncia com seus dados tornados anônimos para esse servidor?",
+ "report.hint": "A sua denúncia será enviada aos moderadores da instância. Você pode adicionar uma explicação de porque você está denunciando essa conta abaixo:",
"report.placeholder": "Comentários adicionais",
"report.submit": "Enviar",
"report.target": "Denunciar",
"search.placeholder": "Pesquisar",
"search_popout.search_format": "Formato de busca avançado",
+ "search_popout.tips.full_text": "Texto simples retorna status que você escreveu, favoritou, compartilhou ou em que tenha sido mencionado; também retorna nomes de exibição, usuários e hashtags correspondentes.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Texto simples retorna nomes de exibição, usuários e hashtags correspondentes",
"search_popout.tips.user": "usuário",
+ "search_results.accounts": "Pessoas",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
"standalone.public_title": "Dê uma espiada...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Silenciar conversa",
"status.open": "Expandir",
"status.pin": "Fixar no perfil",
+ "status.pinned": "Toot fixado",
"status.reblog": "Compartilhar",
"status.reblogged_by": "{name} compartilhou",
"status.reply": "Responder",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Conteúdo sensível",
"status.share": "Compartilhar",
"status.show_less": "Mostrar menos",
+ "status.show_less_all": "Mostrar menos para todas as mensagens",
"status.show_more": "Mostrar mais",
+ "status.show_more_all": "Mostrar mais para todas as mensagens",
"status.unmute_conversation": "Desativar silêncio desta conversa",
"status.unpin": "Desafixar do perfil",
- "tabs_bar.compose": "Criar",
"tabs_bar.federated_timeline": "Global",
"tabs_bar.home": "Página inicial",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Arraste e solte para enviar",
"upload_button.label": "Adicionar mídia",
"upload_form.description": "Descreva a imagem para deficientes visuais",
+ "upload_form.focus": "Recortar",
"upload_form.undo": "Desfazer",
"upload_progress.label": "Salvando...",
"video.close": "Fechar vídeo",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 2fd13db15..3b20cf4e6 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -1,7 +1,9 @@
{
"account.block": "Bloquear @{name}",
"account.block_domain": "Esconder tudo do domínio {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de forma incompleta.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Editar perfil",
"account.follow": "Seguir",
"account.followers": "Seguidores",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} mudou a sua conta para:",
"account.mute": "Silenciar @{name}",
"account.mute_notifications": "Silenciar notificações de @{name}",
+ "account.muted": "Muted",
"account.posts": "Posts",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Denunciar @{name}",
"account.requested": "A aguardar aprovação",
"account.share": "Partilhar o perfil @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Em que estás a pensar?",
"compose_form.publish": "Publicar",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Marcar media como conteúdo sensível",
- "compose_form.spoiler": "Esconder texto com aviso",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Aviso de conteúdo",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.confirm": "Block",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Cancelar",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Comentários adicionais",
"report.submit": "Enviar",
"report.target": "Denunciar",
"search.placeholder": "Pesquisar",
"search_popout.search_format": "Formato avançado de pesquisa",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "O texto simples retorna a correspondência de nomes, utilizadores e hashtags",
"search_popout.tips.user": "utilizador",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
"standalone.public_title": "Espreitar lá dentro...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Silenciar conversa",
"status.open": "Expandir",
"status.pin": "Fixar no perfil",
+ "status.pinned": "Pinned toot",
"status.reblog": "Partilhar",
"status.reblogged_by": "{name} partilhou",
"status.reply": "Responder",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Conteúdo sensível",
"status.share": "Compartilhar",
"status.show_less": "Mostrar menos",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Mostrar mais",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Deixar de silenciar esta conversa",
"status.unpin": "Não fixar no perfil",
- "tabs_bar.compose": "Criar",
"tabs_bar.federated_timeline": "Global",
"tabs_bar.home": "Home",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Arraste e solte para enviar",
"upload_button.label": "Adicionar media",
"upload_form.description": "Descrição da imagem para pessoas com dificuldades visuais",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Anular",
"upload_progress.label": "A gravar...",
"video.close": "Fechar vídeo",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 29dbe84c5..ec21b5d55 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -1,28 +1,32 @@
{
"account.block": "Блокировать",
"account.block_domain": "Блокировать все с {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Нижеуказанная информация может не полностью отражать профиль пользователя.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Изменить профиль",
"account.follow": "Подписаться",
"account.followers": "Подписаны",
"account.follows": "Подписки",
"account.follows_you": "Подписан(а) на Вас",
- "account.hide_reblogs": "Hide boosts from @{name}",
+ "account.hide_reblogs": "Скрыть продвижения от @{name}",
"account.media": "Медиаконтент",
"account.mention": "Упомянуть",
- "account.moved_to": "{name} has moved to:",
+ "account.moved_to": "Ищите {name} здесь:",
"account.mute": "Заглушить",
- "account.mute_notifications": "Mute notifications from @{name}",
+ "account.mute_notifications": "Скрыть уведомления от @{name}",
+ "account.muted": "Muted",
"account.posts": "Посты",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Пожаловаться",
"account.requested": "Ожидает подтверждения",
"account.share": "Поделиться профилем @{name}",
- "account.show_reblogs": "Show boosts from @{name}",
+ "account.show_reblogs": "Показывать продвижения от @{name}",
"account.unblock": "Разблокировать",
"account.unblock_domain": "Разблокировать {domain}",
"account.unfollow": "Отписаться",
"account.unmute": "Снять глушение",
- "account.unmute_notifications": "Unmute notifications from @{name}",
+ "account.unmute_notifications": "Показывать уведомления от @{name}",
"account.view_full_profile": "Показать полный профиль",
"boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз",
"bundle_column_error.body": "Что-то пошло не так при загрузке этого компонента.",
@@ -36,10 +40,10 @@
"column.favourites": "Понравившееся",
"column.follow_requests": "Запросы на подписку",
"column.home": "Главная",
- "column.lists": "Lists",
+ "column.lists": "Списки",
"column.mutes": "Список глушения",
"column.notifications": "Уведомления",
- "column.pins": "Pinned toot",
+ "column.pins": "Закреплённый пост",
"column.public": "Глобальная лента",
"column_back_button.label": "Назад",
"column_header.hide_settings": "Скрыть настройки",
@@ -50,22 +54,24 @@
"column_header.unpin": "Открепить",
"column_subheading.navigation": "Навигация",
"column_subheading.settings": "Настройки",
- "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+ "compose_form.hashtag_warning": "Этот пост не будет показывается в поиске по хэштегу, т.к. он непубличный. Только публичные посты можно найти в поиске по хэштегу.",
"compose_form.lock_disclaimer": "Ваш аккаунт не {locked}. Любой человек может подписаться на Вас и просматривать посты для подписчиков.",
"compose_form.lock_disclaimer.lock": "закрыт",
"compose_form.placeholder": "О чем Вы думаете?",
"compose_form.publish": "Трубить",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Отметить как чувствительный контент",
- "compose_form.spoiler": "Скрыть текст за предупреждением",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Напишите свое предупреждение здесь",
"confirmation_modal.cancel": "Отмена",
"confirmations.block.confirm": "Заблокировать",
"confirmations.block.message": "Вы уверены, что хотите заблокировать {name}?",
"confirmations.delete.confirm": "Удалить",
"confirmations.delete.message": "Вы уверены, что хотите удалить этот статус?",
- "confirmations.delete_list.confirm": "Delete",
- "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+ "confirmations.delete_list.confirm": "Удалить",
+ "confirmations.delete_list.message": "Вы действительно хотите навсегда удалить этот список?",
"confirmations.domain_block.confirm": "Блокировать весь домен",
"confirmations.domain_block.message": "Вы на самом деле уверены, что хотите блокировать весь {domain}? В большинстве случаев нескольких отдельных блокировок или глушений достаточно.",
"confirmations.mute.confirm": "Заглушить",
@@ -92,7 +98,7 @@
"empty_column.hashtag": "Статусов с таким хэштегом еще не существует.",
"empty_column.home": "Пока Вы ни на кого не подписаны. Полистайте {public} или используйте поиск, чтобы освоиться и завести новые знакомства.",
"empty_column.home.public_timeline": "публичные ленты",
- "empty_column.list": "There is nothing in this list yet.",
+ "empty_column.list": "В этом списке пока ничего нет.",
"empty_column.notifications": "У Вас еще нет уведомлений. Заведите знакомство с другими пользователями, чтобы начать разговор.",
"empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других узлов, чтобы заполнить ленту.",
"follow_request.authorize": "Авторизовать",
@@ -108,50 +114,50 @@
"home.column_settings.show_reblogs": "Показывать продвижения",
"home.column_settings.show_replies": "Показывать ответы",
"home.settings": "Настройки колонки",
- "keyboard_shortcuts.back": "to navigate back",
- "keyboard_shortcuts.boost": "to boost",
- "keyboard_shortcuts.column": "to focus a status in one of the columns",
- "keyboard_shortcuts.compose": "to focus the compose textarea",
- "keyboard_shortcuts.description": "Description",
- "keyboard_shortcuts.down": "to move down in the list",
- "keyboard_shortcuts.enter": "to open status",
- "keyboard_shortcuts.favourite": "to favourite",
- "keyboard_shortcuts.heading": "Keyboard Shortcuts",
- "keyboard_shortcuts.hotkey": "Hotkey",
- "keyboard_shortcuts.legend": "to display this legend",
- "keyboard_shortcuts.mention": "to mention author",
- "keyboard_shortcuts.reply": "to reply",
- "keyboard_shortcuts.search": "to focus search",
- "keyboard_shortcuts.toot": "to start a brand new toot",
- "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
- "keyboard_shortcuts.up": "to move up in the list",
+ "keyboard_shortcuts.back": "перейти назад",
+ "keyboard_shortcuts.boost": "продвинуть пост",
+ "keyboard_shortcuts.column": "фокус на одном из столбцов",
+ "keyboard_shortcuts.compose": "фокус на поле ввода",
+ "keyboard_shortcuts.description": "Описание",
+ "keyboard_shortcuts.down": "вниз по списку",
+ "keyboard_shortcuts.enter": "развернуть пост",
+ "keyboard_shortcuts.favourite": "в избранное",
+ "keyboard_shortcuts.heading": "Сочетания клавиш",
+ "keyboard_shortcuts.hotkey": "Гор. клавиша",
+ "keyboard_shortcuts.legend": "показать это окно",
+ "keyboard_shortcuts.mention": "упомянуть автора поста",
+ "keyboard_shortcuts.reply": "ответить",
+ "keyboard_shortcuts.search": "перейти к поиску",
+ "keyboard_shortcuts.toot": "начать писать новый пост",
+ "keyboard_shortcuts.unfocus": "убрать фокус с поля ввода/поиска",
+ "keyboard_shortcuts.up": "вверх по списку",
"lightbox.close": "Закрыть",
"lightbox.next": "Далее",
"lightbox.previous": "Назад",
- "lists.account.add": "Add to list",
- "lists.account.remove": "Remove from list",
- "lists.delete": "Delete list",
- "lists.edit": "Edit list",
- "lists.new.create": "Add list",
- "lists.new.title_placeholder": "New list title",
- "lists.search": "Search among people you follow",
- "lists.subheading": "Your lists",
+ "lists.account.add": "Добавить в список",
+ "lists.account.remove": "Убрать из списка",
+ "lists.delete": "Удалить список",
+ "lists.edit": "Изменить список",
+ "lists.new.create": "Новый список",
+ "lists.new.title_placeholder": "Заголовок списка",
+ "lists.search": "Искать из ваших подписок",
+ "lists.subheading": "Ваши списки",
"loading_indicator.label": "Загрузка...",
"media_gallery.toggle_visible": "Показать/скрыть",
"missing_indicator.label": "Не найдено",
- "missing_indicator.sublabel": "This resource could not be found",
- "mute_modal.hide_notifications": "Hide notifications from this user?",
+ "missing_indicator.sublabel": "Запрашиваемый ресурс не найден",
+ "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?",
"navigation_bar.blocks": "Список блокировки",
"navigation_bar.community_timeline": "Локальная лента",
"navigation_bar.edit_profile": "Изменить профиль",
"navigation_bar.favourites": "Понравившееся",
"navigation_bar.follow_requests": "Запросы на подписку",
"navigation_bar.info": "Об узле",
- "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
- "navigation_bar.lists": "Lists",
+ "navigation_bar.keyboard_shortcuts": "Сочетания клавиш",
+ "navigation_bar.lists": "Списки",
"navigation_bar.logout": "Выйти",
"navigation_bar.mutes": "Список глушения",
- "navigation_bar.pins": "Pinned toots",
+ "navigation_bar.pins": "Закреплённые посты",
"navigation_bar.preferences": "Опции",
"navigation_bar.public_timeline": "Глобальная лента",
"notification.favourite": "{name} понравился Ваш статус",
@@ -175,8 +181,8 @@
"onboarding.page_four.home": "Домашняя лента показывает посты от тех, на кого Вы подписаны.",
"onboarding.page_four.notifications": "Колонка уведомлений сообщает о взаимодействии с Вами других людей.",
"onboarding.page_one.federation": "Mastodon - это сеть независимых серверов, которые вместе образуют единую социальную сеть. Мы называем эти сервера узлами.",
- "onboarding.page_one.full_handle": "Your full handle",
- "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
+ "onboarding.page_one.full_handle": "Всё в ваших руках",
+ "onboarding.page_one.handle_hint": "Это то, что вы посоветуете искать своим друзьям.",
"onboarding.page_one.welcome": "Добро пожаловать в Mastodon!",
"onboarding.page_six.admin": "Админ Вашего узла - {admin}.",
"onboarding.page_six.almost_done": "Почти готово...",
@@ -199,26 +205,33 @@
"privacy.public.short": "Публичный",
"privacy.unlisted.long": "Не показывать в лентах",
"privacy.unlisted.short": "Скрытый",
- "regeneration_indicator.label": "Loading…",
- "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+ "regeneration_indicator.label": "Загрузка…",
+ "regeneration_indicator.sublabel": "Ваша домашняя лента готовится!",
"relative_time.days": "{number}д",
"relative_time.hours": "{number}ч",
"relative_time.just_now": "только что",
"relative_time.minutes": "{number}м",
"relative_time.seconds": "{number}с",
"reply_indicator.cancel": "Отмена",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Комментарий",
"report.submit": "Отправить",
"report.target": "Жалуемся на",
"search.placeholder": "Поиск",
"search_popout.search_format": "Продвинутый формат поиска",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "хэштег",
"search_popout.tips.status": "статус",
"search_popout.tips.text": "Простой ввод текста покажет совпадающие имена пользователей, отображаемые имена и хэштеги",
"search_popout.tips.user": "пользователь",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}",
"standalone.public_title": "Прямо сейчас",
- "status.block": "Block @{name}",
+ "status.block": "Заблокировать @{name}",
"status.cannot_reblog": "Этот статус не может быть продвинут",
"status.delete": "Удалить",
"status.embed": "Встроить",
@@ -227,10 +240,11 @@
"status.media_hidden": "Медиаконтент скрыт",
"status.mention": "Упомянуть @{name}",
"status.more": "Больше",
- "status.mute": "Mute @{name}",
+ "status.mute": "Заглушить @{name}",
"status.mute_conversation": "Заглушить тред",
"status.open": "Развернуть статус",
"status.pin": "Закрепить в профиле",
+ "status.pinned": "Pinned toot",
"status.reblog": "Продвинуть",
"status.reblogged_by": "{name} продвинул(а)",
"status.reply": "Ответить",
@@ -240,18 +254,20 @@
"status.sensitive_warning": "Чувствительный контент",
"status.share": "Поделиться",
"status.show_less": "Свернуть",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Развернуть",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Снять глушение с треда",
"status.unpin": "Открепить от профиля",
- "tabs_bar.compose": "Написать",
"tabs_bar.federated_timeline": "Глобальная",
"tabs_bar.home": "Главная",
"tabs_bar.local_timeline": "Локальная",
"tabs_bar.notifications": "Уведомления",
- "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+ "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.",
"upload_area.title": "Перетащите сюда, чтобы загрузить",
"upload_button.label": "Добавить медиаконтент",
"upload_form.description": "Описать для людей с нарушениями зрения",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Отменить",
"upload_progress.label": "Загрузка...",
"video.close": "Закрыть видео",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 1a0629708..683f2aadb 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -1,20 +1,24 @@
{
"account.block": "Blokovať @{name}",
"account.block_domain": "Ukryť všetko z {domain}",
+ "account.blocked": "Blokovaný/á",
"account.disclaimer_full": "Inofrmácie nižšie nemusia byť úplným odrazom uživateľovho účtu.",
+ "account.domain_blocked": "Doména ukrytá",
"account.edit_profile": "Upraviť profil",
"account.follow": "Následovať",
- "account.followers": "Následovaťelia",
+ "account.followers": "Sledujúci",
"account.follows": "Sledujete",
- "account.follows_you": "Následuje vás",
+ "account.follows_you": "Následuje ťa",
"account.hide_reblogs": "Skryť povýšenia od @{name}",
"account.media": "Médiá",
"account.mention": "Spomeňte @{name}",
"account.moved_to": "{name} sa presunul/a na:",
"account.mute": "Ignorovať @{name}",
"account.mute_notifications": "Stĺmiť notifikácie od @{name}",
- "account.posts": "Správy",
- "account.report": "Nahlásiť @{name}",
+ "account.muted": "Utíšený/á",
+ "account.posts": "Hlášky",
+ "account.posts_with_replies": "Príspevky s odpoveďami",
+ "account.report": "Nahlás @{name}",
"account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti",
"account.share": "Zdieľať @{name} profil",
"account.show_reblogs": "Zobraziť povýšenia od @{name}",
@@ -31,13 +35,13 @@
"bundle_modal_error.close": "Zatvoriť",
"bundle_modal_error.message": "Nastala chyba pri načítaní tohto komponentu.",
"bundle_modal_error.retry": "Skúsiť znova",
- "column.blocks": "Blokovaní používatelia",
+ "column.blocks": "Blokovaní užívatelia",
"column.community": "Lokálna časová os",
"column.favourites": "Obľúbené",
"column.follow_requests": "Žiadosti o sledovaní",
"column.home": "Domov",
"column.lists": "Zoznamy",
- "column.mutes": "Ignorovaní používatelia",
+ "column.mutes": "Ignorovaní užívatelia",
"column.notifications": "Notifikácie",
"column.pins": "Pripnuté toots",
"column.public": "Federovaná časová os",
@@ -46,18 +50,20 @@
"column_header.moveLeft_settings": "Presunúť stĺpec doľava",
"column_header.moveRight_settings": "Presunúť stĺpec doprava",
"column_header.pin": "Pripnúť",
- "column_header.show_settings": "Ukázať nastavenia",
+ "column_header.show_settings": "Ukáž nastavenia",
"column_header.unpin": "Odopnúť",
"column_subheading.navigation": "Navigácia",
"column_subheading.settings": "Nastavenia",
- "compose_form.hashtag_warning": "Tento toot nebude zobrazený pod žiadným haštagom lebo nieje listovaný. Iba verejné toots môžu byť nájdené podľa haštagu.",
+ "compose_form.hashtag_warning": "Tento toot nebude zobrazený pod žiadným haštagom lebo nieje listovaný. Iba verejné tooty môžu byť nájdené podľa haštagu.",
"compose_form.lock_disclaimer": "Váš účet nie je zamknutý. Ktokoľvek ťa môže nasledovať a vidieť tvoje správy pre sledujúcich.",
"compose_form.lock_disclaimer.lock": "zamknutý",
"compose_form.placeholder": "Na čo myslíš?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Označ médiá ako chúlostivé",
- "compose_form.spoiler": "Skryť text za varovanie",
+ "compose_form.sensitive.marked": "Médiálny obsah je označený ako chúlostivý",
+ "compose_form.sensitive.unmarked": "Médiálny obsah nieje označený ako chúlostivý",
+ "compose_form.spoiler.marked": "Text je ukrytý za varovaním",
+ "compose_form.spoiler.unmarked": "Text nieje ukrytý",
"compose_form.spoiler_placeholder": "Sem napíšte vaše varovanie",
"confirmation_modal.cancel": "Zrušiť",
"confirmations.block.confirm": "Blokovať",
@@ -72,7 +78,7 @@
"confirmations.mute.message": "Naozaj chcete ignorovať {name}?",
"confirmations.unfollow.confirm": "Nesledovať",
"confirmations.unfollow.message": "Naozaj chcete prestať sledovať {name}?",
- "embed.instructions": "Skopírujte kód uvedený nižšie pre pridanie tohto statusu na vašu web stránku.",
+ "embed.instructions": "Umiestnite kód uvedený nižšie pre pridanie tohto statusu na vašu web stránku.",
"embed.preview": "Tu je ako to bude vyzerať:",
"emoji_button.activity": "Aktivita",
"emoji_button.custom": "Vlastné",
@@ -95,14 +101,14 @@
"empty_column.list": "Tento zoznam je ešte prázdny. Keď ale členovia tohoto zoznamu napíšu nové správy, tak tie sa objavia priamo tu.",
"empty_column.notifications": "Nemáte ešte žiadne notifikácie. Napíšte niekomu, následujte niekoho a komunikujte s ostatnými aby diskusia mohla začať.",
"empty_column.public": "Ešte tu nič nie je. Napíšte niečo verejne alebo začnite sledovať používateľov z iných Mastodon serverov aby tu niečo pribudlo",
- "follow_request.authorize": "Povoliť prístup",
- "follow_request.reject": "Odmietnúť",
+ "follow_request.authorize": "Povoľ prístup",
+ "follow_request.reject": "Odmietni",
"getting_started.appsshort": "Aplikácie",
- "getting_started.faq": "FAQ",
- "getting_started.heading": "Začíname",
- "getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispievať vlastným kódom môžeš na GitHube v {github}.",
+ "getting_started.faq": "Časté otázky",
+ "getting_started.heading": "Začni tu",
+ "getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispievať vlastným kódom môžete na GitHube v {github}.",
"getting_started.userguide": "Používateľská príručka",
- "home.column_settings.advanced": "Rozšírené",
+ "home.column_settings.advanced": "Pokročilé",
"home.column_settings.basic": "Základné",
"home.column_settings.filter_regex": "Filtrovať použitím regulárnych výrazov",
"home.column_settings.show_reblogs": "Zobraziť povýšené",
@@ -122,11 +128,11 @@
"keyboard_shortcuts.mention": "spomenúť autora",
"keyboard_shortcuts.reply": "odpovedať",
"keyboard_shortcuts.search": "zamerať sa na vyhľadávanie",
- "keyboard_shortcuts.toot": "začať úplne nový toot",
- "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+ "keyboard_shortcuts.toot": "začať úplne novú hlášku",
+ "keyboard_shortcuts.unfocus": "nesústrediť sa na písaciu plochu, alebo hľadanie",
"keyboard_shortcuts.up": "posunúť sa vyššie v zozname",
"lightbox.close": "Zatvoriť",
- "lightbox.next": "Ďalší",
+ "lightbox.next": "Ďalšie",
"lightbox.previous": "Predchádzajúci",
"lists.account.add": "Pridať do zoznamu",
"lists.account.remove": "Odobrať zo zoznamu",
@@ -141,7 +147,7 @@
"missing_indicator.label": "Nenájdené",
"missing_indicator.sublabel": "Tento zdroj sa nepodarilo nájsť",
"mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?",
- "navigation_bar.blocks": "Blokovaní používatelia",
+ "navigation_bar.blocks": "Blokovaní užívatelia",
"navigation_bar.community_timeline": "Lokálna časová os",
"navigation_bar.edit_profile": "Upraviť profil",
"navigation_bar.favourites": "Obľúbené",
@@ -150,19 +156,19 @@
"navigation_bar.keyboard_shortcuts": "Klávesové skratky",
"navigation_bar.lists": "Zoznamy",
"navigation_bar.logout": "Odhlásiť",
- "navigation_bar.mutes": "Ignorovaní používatelia",
+ "navigation_bar.mutes": "Ignorovaní užívatelia",
"navigation_bar.pins": "Pripnuté toots",
- "navigation_bar.preferences": "Možnosti",
+ "navigation_bar.preferences": "Voľby",
"navigation_bar.public_timeline": "Federovaná časová os",
"notification.favourite": "{name} sa páči tvoj status",
- "notification.follow": "{name} vás začal(a) sledovať",
- "notification.mention": "{name} vás spomenul",
+ "notification.follow": "{name} ťa začal/a následovať",
+ "notification.mention": "{name} ťa spomenul/a",
"notification.reblog": "{name} re-tootol tvoj status",
"notifications.clear": "Vyčistiť zoznam notifikácii",
- "notifications.clear_confirmation": "Naozaj chcete nenávratne vymazať všetky vaše notifikácie?",
+ "notifications.clear_confirmation": "Naozaj chcete nenávratne prečistiť všetky vaše notifikácie?",
"notifications.column_settings.alert": "Notifikácie na ploche",
"notifications.column_settings.favourite": "Obľúbené:",
- "notifications.column_settings.follow": "Nový nasledujúci:",
+ "notifications.column_settings.follow": "Noví následujúci:",
"notifications.column_settings.mention": "Zmienenia:",
"notifications.column_settings.push": "Push notifikácie",
"notifications.column_settings.push_meta": "Toto zariadenie",
@@ -175,7 +181,7 @@
"onboarding.page_four.home": "Domovská časová os zobrazí správy od ľudí ktorých sledujete.",
"onboarding.page_four.notifications": "Stĺpec s notifikáciami zobrazí keď budete s niekým komunikovať.",
"onboarding.page_one.federation": "Mastodon je sieť nezávislých serverov, spojením ktorých vzniká jedna veľká federovaná sociálna sieť.",
- "onboarding.page_one.full_handle": "Your full handle",
+ "onboarding.page_one.full_handle": "Vaša celá prezývka aj s adresou",
"onboarding.page_one.handle_hint": "Toto je čo by ste povedali vaším priateľom že majú hľadať.",
"onboarding.page_one.welcome": "Vitajte na Mastodone!",
"onboarding.page_six.admin": "Správca tohto servera je {admin}.",
@@ -193,7 +199,7 @@
"privacy.change": "Zmeňiť viditeľnosť statusu",
"privacy.direct.long": "Poslať priamo iba spomenutým používateľom",
"privacy.direct.short": "Súkromne",
- "privacy.private.long": "Poslať iba sledujúcim",
+ "privacy.private.long": "Poslať iba následovateľom",
"privacy.private.short": "Iba pre sledujúcich",
"privacy.public.long": "Poslať všetkým verejne",
"privacy.public.short": "Verejné",
@@ -207,23 +213,30 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Zrušiť",
+ "report.forward": "Posuň ku {target}",
+ "report.forward_hint": "Tento účet je z iného serveru. Chceš poslať anonymnú kópiu reportu aj tam?",
+ "report.hint": "Toto nahlásenie bude zaslané správcom servera. Môžeš napísať odvôvodnenie prečo si nahlásil/a tento účet:",
"report.placeholder": "Ďalšie komentáre",
"report.submit": "Poslať",
"report.target": "Nahlásenie {target}",
- "search.placeholder": "Hľadať",
- "search_popout.search_format": "Pokročilý tvar vyhľadávania",
- "search_popout.tips.hashtag": "hashtag",
+ "search.placeholder": "Hľadaj",
+ "search_popout.search_format": "Pokročilé vyhľadávanie",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+ "search_popout.tips.hashtag": "haštag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Jednoduchý text vráti zhodujúce sa mená, prezývky a hashtagy",
"search_popout.tips.user": "používateľ",
+ "search_results.accounts": "Ľudia",
+ "search_results.hashtags": "Haštagy",
+ "search_results.statuses": "Príspevky",
"search_results.total": "{count, number} {count, plural, one {result} ostatné {results}}",
"standalone.public_title": "Pohľad dovnútra...",
"status.block": "Blokovať @{name}",
"status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý",
"status.delete": "Zmazať",
- "status.embed": "Embed",
+ "status.embed": "Vložiť",
"status.favourite": "Páči sa mi",
- "status.load_more": "Zobraziť viac",
+ "status.load_more": "Zobraz viac",
"status.media_hidden": "Skryté médiá",
"status.mention": "Napísať @{name}",
"status.more": "Viac",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Ignorovať konverzáciu",
"status.open": "Otvoriť tento status",
"status.pin": "Pripnúť na profil",
+ "status.pinned": "Pripnutý príspevok",
"status.reblog": "Povýšiť",
"status.reblogged_by": "{name} povýšil",
"status.reply": "Odpovedať",
@@ -239,11 +253,12 @@
"status.sensitive_toggle": "Kliknite pre zobrazenie",
"status.sensitive_warning": "Chúlostivý obsah",
"status.share": "Zdieľať",
- "status.show_less": "Zobraziť menej",
- "status.show_more": "Zobraziť viac",
+ "status.show_less": "Zobraz menej",
+ "status.show_less_all": "Všetkým ukáž menej",
+ "status.show_more": "Zobraz viac",
+ "status.show_more_all": "Všetkým ukáž viac",
"status.unmute_conversation": "Prestať ignorovať konverzáciu",
"status.unpin": "Odopnúť z profilu",
- "tabs_bar.compose": "Napísať",
"tabs_bar.federated_timeline": "Federovaná",
"tabs_bar.home": "Domov",
"tabs_bar.local_timeline": "Lokálna",
@@ -252,6 +267,7 @@
"upload_area.title": "Ťahaj a pusti pre nahratie",
"upload_button.label": "Pridať médiá",
"upload_form.description": "Opis pre slabo vidiacich",
+ "upload_form.focus": "Vystrihni",
"upload_form.undo": "Navrátiť",
"upload_progress.label": "Nahráva sa...",
"video.close": "Zavrieť video",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index cd48967cb..c6512cda4 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -1,7 +1,9 @@
{
"account.block": "Blokiraj korisnika @{name}",
"account.block_domain": "Sakrij sve sa domena {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Navedene informacije možda ne odslikavaju korisnički profil u potpunosti.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Izmeni profil",
"account.follow": "Zaprati",
"account.followers": "Pratioca",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} se pomerio na:",
"account.mute": "Ućutkaj korisnika @{name}",
"account.mute_notifications": "Isključi obaveštenja od korisnika @{name}",
+ "account.muted": "Muted",
"account.posts": "Statusa",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Prijavi @{name}",
"account.requested": "Čekam odobrenje. Kliknite da poništite zahtev za praćenje",
"account.share": "Podeli profil korisnika @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Šta Vam je na umu?",
"compose_form.publish": "Tutni",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Obeleži multimediju kao osetljivu",
- "compose_form.spoiler": "Sakrij tekst ispod upozorenja",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Ovde upišite upozorenje",
"confirmation_modal.cancel": "Poništi",
"confirmations.block.confirm": "Blokiraj",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Poništi",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Dodatni komentari",
"report.submit": "Pošalji",
"report.target": "Prijavljujem {target}",
"search.placeholder": "Pretraga",
"search_popout.search_format": "Napredni format pretrage",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hešteg",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Traženjem običnog teksta ćete dobiti sva pronađena imena, sva korisnička imena i sve nađene heštegove",
"search_popout.tips.user": "korisnik",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}",
"standalone.public_title": "Pogled iznutra...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Ućutkaj prepisku",
"status.open": "Proširi ovaj status",
"status.pin": "Prikači na profil",
+ "status.pinned": "Pinned toot",
"status.reblog": "Podrži",
"status.reblogged_by": "{name} podržao(la)",
"status.reply": "Odgovori",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Osetljiv sadržaj",
"status.share": "Podeli",
"status.show_less": "Prikaži manje",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Prikaži više",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Uključi prepisku",
"status.unpin": "Otkači sa profila",
- "tabs_bar.compose": "Napiši",
"tabs_bar.federated_timeline": "Federisano",
"tabs_bar.home": "Početna",
"tabs_bar.local_timeline": "Lokalno",
@@ -252,6 +267,7 @@
"upload_area.title": "Prevucite ovde da otpremite",
"upload_button.label": "Dodaj multimediju",
"upload_form.description": "Opiši za slabovide osobe",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Opozovi",
"upload_progress.label": "Otpremam...",
"video.close": "Zatvori video",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 595a70ea6..93fbe5960 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -1,7 +1,9 @@
{
"account.block": "Блокирај корисника @{name}",
"account.block_domain": "Сакриј све са домена {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Наведене информације можда не одсликавају кориснички профил у потпуности.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Измени профил",
"account.follow": "Запрати",
"account.followers": "Пратиоца",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} се померио на:",
"account.mute": "Ућуткај корисника @{name}",
"account.mute_notifications": "Искључи обавештења од корисника @{name}",
+ "account.muted": "Muted",
"account.posts": "Статуса",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Пријави @{name}",
"account.requested": "Чекам одобрење. Кликните да поништите захтев за праћење",
"account.share": "Подели профил корисника @{name}",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Шта Вам је на уму?",
"compose_form.publish": "Тутни",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Обележи мултимедију као осетљиву",
- "compose_form.spoiler": "Сакриј текст испод упозорења",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Овде упишите упозорење",
"confirmation_modal.cancel": "Поништи",
"confirmations.block.confirm": "Блокирај",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Поништи",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Додатни коментари",
"report.submit": "Пошаљи",
"report.target": "Пријављујем {target}",
"search.placeholder": "Претрага",
"search_popout.search_format": "Напредни формат претраге",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "хештег",
"search_popout.tips.status": "статус",
"search_popout.tips.text": "Тражењем обичног текста ћете добити сва пронађена имена, сва корисничка имена и све нађене хештегове",
"search_popout.tips.user": "корисник",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}",
"standalone.public_title": "Поглед изнутра...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Ућуткај преписку",
"status.open": "Прошири овај статус",
"status.pin": "Прикачи на профил",
+ "status.pinned": "Pinned toot",
"status.reblog": "Подржи",
"status.reblogged_by": "{name} подржао(ла)",
"status.reply": "Одговори",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Осетљив садржај",
"status.share": "Подели",
"status.show_less": "Прикажи мање",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Прикажи више",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Укључи преписку",
"status.unpin": "Откачи са профила",
- "tabs_bar.compose": "Напиши",
"tabs_bar.federated_timeline": "Федерисано",
"tabs_bar.home": "Почетна",
"tabs_bar.local_timeline": "Локално",
@@ -252,6 +267,7 @@
"upload_area.title": "Превуците овде да отпремите",
"upload_button.label": "Додај мултимедију",
"upload_form.description": "Опиши за слабовиде особе",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Опозови",
"upload_progress.label": "Отпремам...",
"video.close": "Затвори видео",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 3f25648c2..4fa129173 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -1,7 +1,9 @@
{
"account.block": "Blockera @{name}",
"account.block_domain": "Dölj allt från {domain}",
+ "account.blocked": "Blockerad",
"account.disclaimer_full": "Informationen nedan kan spegla användarens profil ofullständigt.",
+ "account.domain_blocked": "Domän gömd",
"account.edit_profile": "Redigera profil",
"account.follow": "Följ",
"account.followers": "Följare",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} har flyttat till:",
"account.mute": "Tysta @{name}",
"account.mute_notifications": "Stäng av notifieringar från @{name}",
+ "account.muted": "Nertystad",
"account.posts": "Inlägg",
+ "account.posts_with_replies": "Toots med svar",
"account.report": "Rapportera @{name}",
"account.requested": "Inväntar godkännande. Klicka för att avbryta följförfrågan",
"account.share": "Dela @{name}'s profil",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Vad funderar du på?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Markera media som känslig",
- "compose_form.spoiler": "Dölj text bakom varning",
+ "compose_form.sensitive.marked": "Media har markerats som känsligt",
+ "compose_form.sensitive.unmarked": "Media har inte markerats som känsligt",
+ "compose_form.spoiler.marked": "Texten har dolts bakom en varning",
+ "compose_form.spoiler.unmarked": "Texten är inte dold",
"compose_form.spoiler_placeholder": "Skriv din varning här",
"confirmation_modal.cancel": "Ångra",
"confirmations.block.confirm": "Blockera",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Ångra",
+ "report.forward": "Vidarebefordra till {target}",
+ "report.forward_hint": "Kontot är från en annan server. Skicka även en anonymiserad kopia av anmälan dit?",
+ "report.hint": "Anmälan skickas till din instans moderatorer. Du kan ge en förklaring till varför du har anmält detta konto nedan:",
"report.placeholder": "Ytterligare kommentarer",
"report.submit": "Skicka",
"report.target": "Rapporterar {target}",
"search.placeholder": "Sök",
"search_popout.search_format": "Avancerat sökformat",
+ "search_popout.tips.full_text": "Enkel text returnerar statusar där du har skrivit, favoriserat, knuffat eller nämnts samt med matchande användarnamn, visningsnamn och hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Enkel text returnerar matchande visningsnamn, användarnamn och hashtags",
"search_popout.tips.user": "användare",
+ "search_results.accounts": "Människor",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, ett {result} andra {results}}",
"standalone.public_title": "En titt inuti...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Tysta konversation",
"status.open": "Utvidga denna status",
"status.pin": "Fäst i profil",
+ "status.pinned": "Fäst toot",
"status.reblog": "Knuff",
"status.reblogged_by": "{name} knuffade",
"status.reply": "Svara",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Känsligt innehåll",
"status.share": "Dela",
"status.show_less": "Visa mindre",
+ "status.show_less_all": "Visa mindre för alla",
"status.show_more": "Visa mer",
+ "status.show_more_all": "Visa mer för alla",
"status.unmute_conversation": "Öppna konversation",
"status.unpin": "Ångra fäst i profil",
- "tabs_bar.compose": "Skriv",
"tabs_bar.federated_timeline": "Förenad",
"tabs_bar.home": "Hem",
"tabs_bar.local_timeline": "Lokal",
@@ -252,6 +267,7 @@
"upload_area.title": "Dra & släpp för att ladda upp",
"upload_button.label": "Lägg till media",
"upload_form.description": "Beskriv för synskadade",
+ "upload_form.focus": "Beskär",
"upload_form.undo": "Ångra",
"upload_progress.label": "Laddar upp...",
"video.close": "Stäng video",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 740fb80e7..95a933b40 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -1,7 +1,9 @@
{
"account.block": "Block @{name}",
"account.block_domain": "Hide everything from {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Edit profile",
"account.follow": "Follow",
"account.followers": "Followers",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Mute @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "Posts",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Report @{name}",
"account.requested": "Awaiting approval",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "What is on your mind?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Mark media as sensitive",
- "compose_form.spoiler": "Hide text behind warning",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Content warning",
"confirmation_modal.cancel": "Cancel",
"confirmations.block.confirm": "Block",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Cancel",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Additional comments",
"report.submit": "Submit",
"report.target": "Reporting",
"search.placeholder": "Search",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Boost",
"status.reblogged_by": "{name} boosted",
"status.reply": "Reply",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Sensitive content",
"status.share": "Share",
"status.show_less": "Show less",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Show more",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Compose",
"tabs_bar.federated_timeline": "Federated",
"tabs_bar.home": "Home",
"tabs_bar.local_timeline": "Local",
@@ -252,6 +267,7 @@
"upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Undo",
"upload_progress.label": "Uploading...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 8805e52f4..baaa5c97a 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -1,7 +1,9 @@
{
"account.block": "Engelle @{name}",
"account.block_domain": "Hide everything from {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Profili düzenle",
"account.follow": "Takip et",
"account.followers": "Takipçiler",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Sustur @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "Gönderiler",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Rapor et @{name}",
"account.requested": "Onay bekleniyor",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Ne düşünüyorsun?",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Görseli hassas olarak işaretle",
- "compose_form.spoiler": "Metni uyarı arkasına gizle",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "İçerik uyarısı",
"confirmation_modal.cancel": "İptal",
"confirmations.block.confirm": "Engelle",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "İptal",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Ek yorumlar",
"report.submit": "Gönder",
"report.target": "Raporlama",
"search.placeholder": "Ara",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {sonuç} other {sonuçlar}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Bu gönderiyi genişlet",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Boost'la",
"status.reblogged_by": "{name} boost etti",
"status.reply": "Cevapla",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Hassas içerik",
"status.share": "Share",
"status.show_less": "Daha azı",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Daha fazlası",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Oluştur",
"tabs_bar.federated_timeline": "Federe",
"tabs_bar.home": "Ana sayfa",
"tabs_bar.local_timeline": "Yerel",
@@ -252,6 +267,7 @@
"upload_area.title": "Upload için sürükle bırak yapınız",
"upload_button.label": "Görsel ekle",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Geri al",
"upload_progress.label": "Yükleniyor...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 2cdaba0ac..1755c55b4 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -1,7 +1,9 @@
{
"account.block": "Заблокувати",
"account.block_domain": "Заглушити {domain}",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "Налаштування профілю",
"account.follow": "Підписатися",
"account.followers": "Підписники",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "Заглушити",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "Пости",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "Поскаржитися",
"account.requested": "Очікує підтвердження",
"account.share": "Share @{name}'s profile",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "Що у Вас на думці?",
"compose_form.publish": "Дмухнути",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "Відмітити як непристойний зміст",
- "compose_form.spoiler": "Приховати текст за попередженням",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Попередження щодо прихованого тексту",
"confirmation_modal.cancel": "Відмінити",
"confirmations.block.confirm": "Заблокувати",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Відмінити",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Додаткові коментарі",
"report.submit": "Відправити",
"report.target": "Скаржимося на",
"search.placeholder": "Пошук",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {результат} few {результати} many {результатів} other {результатів}}",
"standalone.public_title": "A look inside...",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "Заглушити діалог",
"status.open": "Розгорнути допис",
"status.pin": "Pin on profile",
+ "status.pinned": "Pinned toot",
"status.reblog": "Передмухнути",
"status.reblogged_by": "{name} передмухнув(-ла)",
"status.reply": "Відповісти",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "Непристойний зміст",
"status.share": "Share",
"status.show_less": "Згорнути",
+ "status.show_less_all": "Show less for all",
"status.show_more": "Розгорнути",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "Зняти глушення з діалогу",
"status.unpin": "Unpin from profile",
- "tabs_bar.compose": "Написати",
"tabs_bar.federated_timeline": "Глобальна",
"tabs_bar.home": "Головна",
"tabs_bar.local_timeline": "Локальна",
@@ -252,6 +267,7 @@
"upload_area.title": "Перетягніть сюди, щоб завантажити",
"upload_button.label": "Додати медіаконтент",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "Відмінити",
"upload_progress.label": "Завантаження...",
"video.close": "Close video",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index a02211b8a..d031c85f3 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -1,7 +1,9 @@
{
"account.block": "屏蔽 @{name}",
"account.block_domain": "隐藏来自 {domain} 的内容",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "此处显示的信息可能不是全部内容。",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "修改个人资料",
"account.follow": "关注",
"account.followers": "关注者",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} 已经迁移到:",
"account.mute": "隐藏 @{name}",
"account.mute_notifications": "隐藏来自 @{name} 的通知",
+ "account.muted": "Muted",
"account.posts": "嘟文",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "举报 @{name}",
"account.requested": "正在等待对方同意。点击以取消发送关注请求",
"account.share": "分享 @{name} 的个人资料",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "在想啥?",
"compose_form.publish": "嘟嘟",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "将媒体文件标记为敏感内容",
- "compose_form.spoiler": "折叠嘟文内容",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "折叠部分的警告消息",
"confirmation_modal.cancel": "取消",
"confirmations.block.confirm": "屏蔽",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}分",
"relative_time.seconds": "{number}秒",
"reply_indicator.cancel": "取消",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "附言",
"report.submit": "提交",
"report.target": "举报 {target}",
"search.placeholder": "搜索",
"search_popout.search_format": "高级搜索格式",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "话题标签",
"search_popout.tips.status": "嘟文",
"search_popout.tips.text": "使用普通字符进行搜索将会返回昵称、用户名和话题标签",
"search_popout.tips.user": "用户",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "共 {count, number} 个结果",
"standalone.public_title": "大家都在干啥?",
"status.block": "屏蔽 @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "隐藏此对话",
"status.open": "展开嘟文",
"status.pin": "在个人资料页面置顶",
+ "status.pinned": "Pinned toot",
"status.reblog": "转嘟",
"status.reblogged_by": "{name} 转嘟了",
"status.reply": "回复",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "敏感内容",
"status.share": "分享",
"status.show_less": "隐藏内容",
+ "status.show_less_all": "Show less for all",
"status.show_more": "显示内容",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "不再隐藏此对话",
"status.unpin": "在个人资料页面取消置顶",
- "tabs_bar.compose": "撰写",
"tabs_bar.federated_timeline": "跨站",
"tabs_bar.home": "主页",
"tabs_bar.local_timeline": "本站",
@@ -252,6 +267,7 @@
"upload_area.title": "将文件拖放到此处开始上传",
"upload_button.label": "上传媒体文件",
"upload_form.description": "为视觉障碍人士添加文字说明",
+ "upload_form.focus": "Crop",
"upload_form.undo": "取消上传",
"upload_progress.label": "上传中…",
"video.close": "关闭视频",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 0a3ae423d..d3ad238ad 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -1,7 +1,9 @@
{
"account.block": "封鎖 @{name}",
"account.block_domain": "隱藏來自 {domain} 的一切文章",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "下列資料不一定完整。",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "修改個人資料",
"account.follow": "關注",
"account.followers": "關注的人",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "將 @{name} 靜音",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "文章",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "舉報 @{name}",
"account.requested": "等候審批",
"account.share": "分享 @{name} 的個人資料",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "你在想甚麼?",
"compose_form.publish": "發文",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "將媒體檔案標示為「敏感內容」",
- "compose_form.spoiler": "將部份文字藏於警告訊息之後",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "敏感警告訊息",
"confirmation_modal.cancel": "取消",
"confirmations.block.confirm": "封鎖",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "取消",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "額外訊息",
"report.submit": "提交",
"report.target": "舉報",
"search.placeholder": "搜尋",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} 項結果",
"standalone.public_title": "站點一瞥…",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "靜音對話",
"status.open": "展開文章",
"status.pin": "置頂到資料頁",
+ "status.pinned": "Pinned toot",
"status.reblog": "轉推",
"status.reblogged_by": "{name} 轉推",
"status.reply": "回應",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "敏感內容",
"status.share": "Share",
"status.show_less": "減少顯示",
+ "status.show_less_all": "Show less for all",
"status.show_more": "顯示更多",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "解禁對話",
"status.unpin": "解除置頂",
- "tabs_bar.compose": "撰寫",
"tabs_bar.federated_timeline": "跨站",
"tabs_bar.home": "主頁",
"tabs_bar.local_timeline": "本站",
@@ -252,6 +267,7 @@
"upload_area.title": "將檔案拖放至此上載",
"upload_button.label": "上載媒體檔案",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "還原",
"upload_progress.label": "上載中……",
"video.close": "關閉影片",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 1201fe3c7..3a5eade41 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -1,7 +1,9 @@
{
"account.block": "封鎖 @{name}",
"account.block_domain": "隱藏來自 {domain} 的一切貼文",
+ "account.blocked": "Blocked",
"account.disclaimer_full": "下列資料不一定完整。",
+ "account.domain_blocked": "Domain hidden",
"account.edit_profile": "編輯用者資訊",
"account.follow": "關注",
"account.followers": "專注者",
@@ -13,7 +15,9 @@
"account.moved_to": "{name} has moved to:",
"account.mute": "消音 @{name}",
"account.mute_notifications": "Mute notifications from @{name}",
+ "account.muted": "Muted",
"account.posts": "貼文",
+ "account.posts_with_replies": "Toots with replies",
"account.report": "檢舉 @{name}",
"account.requested": "正在等待許可",
"account.share": "分享 @{name} 的用者資訊",
@@ -56,8 +60,10 @@
"compose_form.placeholder": "在想些什麼?",
"compose_form.publish": "貼掉",
"compose_form.publish_loud": "{publish}!",
- "compose_form.sensitive": "將此媒體標為敏感",
- "compose_form.spoiler": "將訊息隱藏在警告訊息之後",
+ "compose_form.sensitive.marked": "Media is marked as sensitive",
+ "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
+ "compose_form.spoiler.marked": "Text is hidden behind warning",
+ "compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "內容警告",
"confirmation_modal.cancel": "取消",
"confirmations.block.confirm": "封鎖",
@@ -207,15 +213,22 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "取消",
+ "report.forward": "Forward to {target}",
+ "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "更多訊息",
"report.submit": "送出",
"report.target": "通報中",
"search.placeholder": "搜尋",
"search_popout.search_format": "Advanced search format",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
+ "search_results.accounts": "People",
+ "search_results.hashtags": "Hashtags",
+ "search_results.statuses": "Toots",
"search_results.total": "{count, number} 項結果",
"standalone.public_title": "站點一瞥…",
"status.block": "Block @{name}",
@@ -231,6 +244,7 @@
"status.mute_conversation": "消音對話",
"status.open": "展開這個狀態",
"status.pin": "置頂到個人資訊頁",
+ "status.pinned": "Pinned toot",
"status.reblog": "轉推",
"status.reblogged_by": "{name} 轉推了",
"status.reply": "回應",
@@ -240,10 +254,11 @@
"status.sensitive_warning": "敏感內容",
"status.share": "Share",
"status.show_less": "看少點",
+ "status.show_less_all": "Show less for all",
"status.show_more": "看更多",
+ "status.show_more_all": "Show more for all",
"status.unmute_conversation": "不消音對話",
"status.unpin": "解除置頂",
- "tabs_bar.compose": "編輯",
"tabs_bar.federated_timeline": "聯盟",
"tabs_bar.home": "家",
"tabs_bar.local_timeline": "本地",
@@ -252,6 +267,7 @@
"upload_area.title": "拖放來上傳",
"upload_button.label": "增加媒體",
"upload_form.description": "Describe for the visually impaired",
+ "upload_form.focus": "Crop",
"upload_form.undo": "復原",
"upload_progress.label": "上傳中...",
"video.close": "關閉影片",
diff --git a/app/javascript/mastodon/reducers/accounts.js b/app/javascript/mastodon/reducers/accounts.js
index f77061dfa..47e6d2330 100644
--- a/app/javascript/mastodon/reducers/accounts.js
+++ b/app/javascript/mastodon/reducers/accounts.js
@@ -102,7 +102,7 @@ const initialState = ImmutableMap();
export default function accounts(state = initialState, action) {
switch(action.type) {
case STORE_HYDRATE:
- return state.merge(action.state.get('accounts'));
+ return normalizeAccounts(state, Object.values(action.state.get('accounts').toJS()));
case ACCOUNT_FETCH_SUCCESS:
case NOTIFICATIONS_UPDATE:
return normalizeAccount(state, action.account);
diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js
index c709fb88c..5eadebb81 100644
--- a/app/javascript/mastodon/reducers/compose.js
+++ b/app/javascript/mastodon/reducers/compose.js
@@ -16,6 +16,8 @@ import {
COMPOSE_SUGGESTIONS_CLEAR,
COMPOSE_SUGGESTIONS_READY,
COMPOSE_SUGGESTION_SELECT,
+ COMPOSE_SUGGESTION_TAGS_UPDATE,
+ COMPOSE_TAG_HISTORY_UPDATE,
COMPOSE_SENSITIVITY_CHANGE,
COMPOSE_SPOILERNESS_CHANGE,
COMPOSE_SPOILER_TEXT_CHANGE,
@@ -33,8 +35,10 @@ import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrde
import uuid from '../uuid';
import { me } from '../initial_state';
+const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
+
const initialState = ImmutableMap({
- mounted: false,
+ mounted: 0,
sensitive: false,
spoiler: false,
spoiler_text: '',
@@ -54,6 +58,7 @@ const initialState = ImmutableMap({
default_sensitive: false,
resetFileKey: Math.floor((Math.random() * 0x10000)),
idempotencyKey: null,
+ tagHistory: ImmutableList(),
});
function statusToTextMentions(state, status) {
@@ -87,7 +92,6 @@ function appendMedia(state, media) {
map.update('media_attachments', list => list.push(media));
map.set('is_uploading', false);
map.set('resetFileKey', Math.floor((Math.random() * 0x10000)));
- map.update('text', oldText => `${oldText.trim()} ${media.get('text_url')}`);
map.set('focusDate', new Date());
map.set('idempotencyKey', uuid());
@@ -98,12 +102,10 @@ function appendMedia(state, media) {
};
function removeMedia(state, mediaId) {
- const media = state.get('media_attachments').find(item => item.get('id') === mediaId);
const prevSize = state.get('media_attachments').size;
return state.withMutations(map => {
map.update('media_attachments', list => list.filterNot(item => item.get('id') === mediaId));
- map.update('text', text => text.replace(media.get('text_url'), '').trim());
map.set('idempotencyKey', uuid());
if (prevSize === 1) {
@@ -122,13 +124,27 @@ const insertSuggestion = (state, position, token, completion) => {
});
};
-const insertEmoji = (state, position, emojiData) => {
- const emoji = emojiData.native;
+const updateSuggestionTags = (state, token) => {
+ const prefix = token.slice(1);
- return state.withMutations(map => {
- map.update('text', oldText => `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`);
- map.set('focusDate', new Date());
- map.set('idempotencyKey', uuid());
+ return state.merge({
+ suggestions: state.get('tagHistory')
+ .filter(tag => tag.startsWith(prefix))
+ .slice(0, 4)
+ .map(tag => '#' + tag),
+ suggestion_token: token,
+ });
+};
+
+const insertEmoji = (state, position, emojiData) => {
+ const oldText = state.get('text');
+ const needsSpace = emojiData.custom && position > 0 && !allowedAroundShortCode.includes(oldText[position - 1]);
+ const emoji = needsSpace ? ' ' + emojiData.native : emojiData.native;
+
+ return state.merge({
+ text: `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`,
+ focusDate: new Date(),
+ idempotencyKey: uuid(),
});
};
@@ -159,10 +175,10 @@ export default function compose(state = initialState, action) {
case STORE_HYDRATE:
return hydrate(state, action.state.get('compose'));
case COMPOSE_MOUNT:
- return state.set('mounted', true);
+ return state.set('mounted', state.get('mounted') + 1);
case COMPOSE_UNMOUNT:
return state
- .set('mounted', false)
+ .set('mounted', Math.max(state.get('mounted') - 1, 0))
.set('is_composing', false);
case COMPOSE_SENSITIVITY_CHANGE:
return state.withMutations(map => {
@@ -252,6 +268,10 @@ export default function compose(state = initialState, action) {
return state.set('suggestions', ImmutableList(action.accounts ? action.accounts.map(item => item.id) : action.emojis)).set('suggestion_token', action.token);
case COMPOSE_SUGGESTION_SELECT:
return insertSuggestion(state, action.position, action.token, action.completion);
+ case COMPOSE_SUGGESTION_TAGS_UPDATE:
+ return updateSuggestionTags(state, action.token);
+ case COMPOSE_TAG_HISTORY_UPDATE:
+ return state.set('tagHistory', fromJS(action.tags));
case TIMELINE_DELETE:
if (action.id === state.get('in_reply_to')) {
return state.set('in_reply_to', null);
@@ -265,7 +285,7 @@ export default function compose(state = initialState, action) {
.set('is_submitting', false)
.update('media_attachments', list => list.map(item => {
if (item.get('id') === action.media.id) {
- return item.set('description', action.media.description);
+ return fromJS(action.media);
}
return item;
diff --git a/app/javascript/mastodon/reducers/dropdown_menu.js b/app/javascript/mastodon/reducers/dropdown_menu.js
new file mode 100644
index 000000000..5449884cc
--- /dev/null
+++ b/app/javascript/mastodon/reducers/dropdown_menu.js
@@ -0,0 +1,18 @@
+import Immutable from 'immutable';
+import {
+ DROPDOWN_MENU_OPEN,
+ DROPDOWN_MENU_CLOSE,
+} from '../actions/dropdown_menu';
+
+const initialState = Immutable.Map({ openId: null, placement: null });
+
+export default function dropdownMenu(state = initialState, action) {
+ switch (action.type) {
+ case DROPDOWN_MENU_OPEN:
+ return state.merge({ openId: action.id, placement: action.placement });
+ case DROPDOWN_MENU_CLOSE:
+ return state.get('openId') === action.id ? state.set('openId', null) : state;
+ default:
+ return state;
+ }
+}
diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js
index a028e989c..b84b2d18a 100644
--- a/app/javascript/mastodon/reducers/index.js
+++ b/app/javascript/mastodon/reducers/index.js
@@ -1,4 +1,5 @@
import { combineReducers } from 'redux-immutable';
+import dropdown_menu from './dropdown_menu';
import timelines from './timelines';
import meta from './meta';
import alerts from './alerts';
@@ -26,6 +27,7 @@ import lists from './lists';
import listEditor from './list_editor';
const reducers = {
+ dropdown_menu,
timelines,
meta,
alerts,
diff --git a/app/javascript/mastodon/reducers/reports.js b/app/javascript/mastodon/reducers/reports.js
index a08bbec38..21ae6f93f 100644
--- a/app/javascript/mastodon/reducers/reports.js
+++ b/app/javascript/mastodon/reducers/reports.js
@@ -6,6 +6,7 @@ import {
REPORT_CANCEL,
REPORT_STATUS_TOGGLE,
REPORT_COMMENT_CHANGE,
+ REPORT_FORWARD_CHANGE,
} from '../actions/reports';
import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable';
@@ -15,6 +16,7 @@ const initialState = ImmutableMap({
account_id: null,
status_ids: ImmutableSet(),
comment: '',
+ forward: false,
}),
});
@@ -42,6 +44,8 @@ export default function reports(state = initialState, action) {
});
case REPORT_COMMENT_CHANGE:
return state.setIn(['new', 'comment'], action.comment);
+ case REPORT_FORWARD_CHANGE:
+ return state.setIn(['new', 'forward'], action.forward);
case REPORT_SUBMIT_REQUEST:
return state.setIn(['new', 'isSubmitting'], true);
case REPORT_SUBMIT_FAIL:
diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js
index cc0d4dacd..7b3141623 100644
--- a/app/javascript/mastodon/reducers/statuses.js
+++ b/app/javascript/mastodon/reducers/statuses.js
@@ -15,6 +15,8 @@ import {
CONTEXT_FETCH_SUCCESS,
STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS,
+ STATUS_REVEAL,
+ STATUS_HIDE,
} from '../actions/statuses';
import {
TIMELINE_REFRESH_SUCCESS,
@@ -54,16 +56,21 @@ const normalizeStatus = (state, status) => {
normalStatus.reblog = status.reblog.id;
}
- const searchContent = [status.spoiler_text, status.content].join('\n\n').replace(/
/g, '\n').replace(/<\/p>
/g, '\n\n');
+ // Only calculate these values when status first encountered
+ // Otherwise keep the ones already in the reducer
+ if (!state.has(status.id)) {
+ const searchContent = [status.spoiler_text, status.content].join('\n\n').replace(/ /g, '\n').replace(/<\/p>
/g, '\n\n');
- const emojiMap = normalStatus.emojis.reduce((obj, emoji) => {
- obj[`:${emoji.shortcode}:`] = emoji;
- return obj;
- }, {});
+ const emojiMap = normalStatus.emojis.reduce((obj, emoji) => {
+ obj[`:${emoji.shortcode}:`] = emoji;
+ return obj;
+ }, {});
- normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
- normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
- normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(normalStatus.spoiler_text || ''), emojiMap);
+ normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
+ normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
+ normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(normalStatus.spoiler_text || ''), emojiMap);
+ normalStatus.hidden = normalStatus.sensitive;
+ }
return state.update(status.id, ImmutableMap(), map => map.mergeDeep(fromJS(normalStatus)));
};
@@ -111,6 +118,14 @@ export default function statuses(state = initialState, action) {
return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS:
return state.setIn([action.id, 'muted'], false);
+ case STATUS_REVEAL:
+ return state.withMutations(map => {
+ action.ids.forEach(id => map.setIn([id, 'hidden'], false));
+ });
+ case STATUS_HIDE:
+ return state.withMutations(map => {
+ action.ids.forEach(id => map.setIn([id, 'hidden'], true));
+ });
case TIMELINE_REFRESH_SUCCESS:
case TIMELINE_EXPAND_SUCCESS:
case CONTEXT_FETCH_SUCCESS:
diff --git a/app/javascript/mastodon/service_worker/entry.js b/app/javascript/mastodon/service_worker/entry.js
index eea4cfc3c..8b65f27a3 100644
--- a/app/javascript/mastodon/service_worker/entry.js
+++ b/app/javascript/mastodon/service_worker/entry.js
@@ -1,10 +1,48 @@
import './web_push_notifications';
+function openCache() {
+ return caches.open('mastodon-web');
+}
+
+function fetchRoot() {
+ return fetch('/', { credentials: 'include' });
+}
+
// Cause a new version of a registered Service Worker to replace an existing one
// that is already installed, and replace the currently active worker on open pages.
self.addEventListener('install', function(event) {
- event.waitUntil(self.skipWaiting());
+ event.waitUntil(Promise.all([openCache(), fetchRoot()]).then(([cache, root]) => cache.put('/', root)));
});
self.addEventListener('activate', function(event) {
event.waitUntil(self.clients.claim());
});
+self.addEventListener('fetch', function(event) {
+ const url = new URL(event.request.url);
+
+ if (url.pathname.startsWith('/web/')) {
+ const asyncResponse = fetchRoot();
+ const asyncCache = openCache();
+
+ event.respondWith(asyncResponse.then(async response => {
+ if (response.ok) {
+ const cache = await asyncCache;
+ await cache.put('/', response);
+ return response.clone();
+ }
+
+ throw null;
+ }).catch(() => caches.match('/')));
+ } else if (url.pathname === '/auth/sign_out') {
+ const asyncResponse = fetch(event.request);
+ const asyncCache = openCache();
+
+ event.respondWith(asyncResponse.then(async response => {
+ if (response.ok || response.type === 'opaqueredirect') {
+ const cache = await asyncCache;
+ await cache.delete('/');
+ }
+
+ return response;
+ }));
+ }
+});
diff --git a/app/javascript/mastodon/settings.js b/app/javascript/mastodon/settings.js
index dbd969cb1..7643a508e 100644
--- a/app/javascript/mastodon/settings.js
+++ b/app/javascript/mastodon/settings.js
@@ -44,3 +44,4 @@ export default class Settings {
}
export const pushNotificationsSetting = new Settings('mastodon_push_notification_data');
+export const tagHistory = new Settings('mastodon_tag_history');
diff --git a/app/javascript/packs/about.js b/app/javascript/packs/about.js
index 50c81198e..63e12da42 100644
--- a/app/javascript/packs/about.js
+++ b/app/javascript/packs/about.js
@@ -1,7 +1,5 @@
import loadPolyfills from '../mastodon/load_polyfills';
-require.context('../images/', true);
-
function loaded() {
const TimelineContainer = require('../mastodon/containers/timeline_container').default;
const React = require('react');
diff --git a/app/javascript/packs/share.js b/app/javascript/packs/share.js
index 51e4ae38b..e9580f648 100644
--- a/app/javascript/packs/share.js
+++ b/app/javascript/packs/share.js
@@ -1,7 +1,5 @@
import loadPolyfills from '../mastodon/load_polyfills';
-require.context('../images/', true);
-
function loaded() {
const ComposeContainer = require('../mastodon/containers/compose_container').default;
const React = require('react');
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index 0806171be..c484f074b 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -1,4 +1,183 @@
+$maximum-width: 1235px;
+$fluid-breakpoint: $maximum-width + 20px;
+$column-breakpoint: 700px;
+$small-breakpoint: 960px;
+
+.container {
+ box-sizing: border-box;
+ max-width: $maximum-width;
+ margin: 0 auto;
+ position: relative;
+
+ @media screen and (max-width: $fluid-breakpoint) {
+ width: 100%;
+ padding: 0 10px;
+ }
+}
+
.landing-page {
+ .grid {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: 1fr 2fr;
+ grid-auto-columns: 25%;
+ grid-auto-rows: max-content;
+
+ .column-0 {
+ display: none;
+ }
+
+ .column-1 {
+ grid-column: 1;
+ grid-row: 1;
+ }
+
+ .column-2 {
+ grid-column: 2;
+ grid-row: 1;
+ }
+
+ .column-3 {
+ grid-column: 3;
+ grid-row: 1 / 3;
+ }
+
+ .column-4 {
+ grid-column: 1 / 3;
+ grid-row: 2;
+ }
+ }
+
+ @media screen and (max-width: $small-breakpoint) {
+ .grid {
+ grid-template-columns: 40% 60%;
+
+ .column-0 {
+ display: none;
+ }
+
+ .column-1 {
+ grid-column: 1;
+ grid-row: 1;
+
+ &.non-preview .landing-page__forms {
+ height: 100%;
+ }
+ }
+
+ .column-2 {
+ grid-column: 2;
+ grid-row: 1 / 3;
+
+ &.non-preview {
+ grid-column: 2;
+ grid-row: 1;
+ }
+ }
+
+ .column-3 {
+ grid-column: 1;
+ grid-row: 2 / 4;
+ }
+
+ .column-4 {
+ grid-column: 2;
+ grid-row: 3;
+
+ &.non-preview {
+ grid-column: 1 / 3;
+ grid-row: 2;
+ }
+ }
+ }
+ }
+
+ @media screen and (max-width: $column-breakpoint) {
+ .grid {
+ grid-template-columns: auto;
+
+ .column-0 {
+ display: block;
+ grid-column: 1;
+ grid-row: 1;
+ }
+
+ .column-1 {
+ grid-column: 1;
+ grid-row: 3;
+
+ .brand {
+ display: none;
+ }
+ }
+
+ .column-2 {
+ grid-column: 1;
+ grid-row: 2;
+
+ .landing-page__logo,
+ .landing-page__call-to-action {
+ display: none;
+ }
+
+ &.non-preview {
+ grid-column: 1;
+ grid-row: 2;
+ }
+ }
+
+ .column-3 {
+ grid-column: 1;
+ grid-row: 5;
+ }
+
+ .column-4 {
+ grid-column: 1;
+ grid-row: 4;
+
+ &.non-preview {
+ grid-column: 1;
+ grid-row: 4;
+ }
+ }
+ }
+ }
+
+ .column-flex {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .separator-or {
+ position: relative;
+ margin: 40px 0;
+ text-align: center;
+
+ &::before {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 0;
+ border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
+ position: absolute;
+ top: 50%;
+ left: 0;
+ }
+
+ span {
+ display: inline-block;
+ background: $ui-base-color;
+ font-size: 12px;
+ font-weight: 500;
+ color: $ui-primary-color;
+ text-transform: uppercase;
+ position: relative;
+ z-index: 1;
+ padding: 0 8px;
+ cursor: default;
+ }
+ }
+
p,
li {
font-family: 'mastodon-font-sans-serif', sans-serif;
@@ -15,6 +194,28 @@
}
}
+ .closed-registrations-message {
+ margin-top: 20px;
+
+ &,
+ p {
+ text-align: center;
+ font-size: 12px;
+ line-height: 18px;
+ color: $ui-primary-color;
+ margin-bottom: 0;
+
+ a {
+ color: $ui-highlight-color;
+ text-decoration: underline;
+ }
+ }
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+ }
+
em {
display: inline;
margin: 0;
@@ -116,10 +317,14 @@
}
hr {
- border-color: rgba($ui-base-lighter-color, .6);
+ width: 100%;
+ height: 0;
+ border: 0;
+ border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
+ margin: 20px 0;
}
- .container {
+ .container-alt {
width: 100%;
box-sizing: border-box;
max-width: 800px;
@@ -152,24 +357,20 @@
}
}
}
+ }
- .mascot-container {
- max-width: 800px;
- margin: 0 auto;
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- height: 100%;
+ .brand {
+ a {
+ padding-left: 0;
+ padding-right: 0;
+ color: $white;
}
- .mascot {
- position: absolute;
- bottom: -14px;
- width: auto;
- height: auto;
- left: 60px;
- z-index: 3;
+ img {
+ height: 32px;
+ position: relative;
+ top: 4px;
+ left: -10px;
}
}
@@ -177,7 +378,7 @@
line-height: 30px;
overflow: hidden;
- .container {
+ .container-alt {
display: flex;
justify-content: space-between;
}
@@ -203,21 +404,6 @@
}
}
- .brand {
- a {
- padding-left: 0;
- padding-right: 0;
- color: $white;
- }
-
- img {
- height: 32px;
- position: relative;
- top: 4px;
- left: -10px;
- }
- }
-
ul {
list-style: none;
margin: 0;
@@ -243,53 +429,6 @@
align-items: center;
position: relative;
- .floats {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
-
- div {
- position: absolute;
- transition: all 0.1s linear;
- animation-name: floating;
- animation-iteration-count: infinite;
- animation-direction: alternate;
- animation-timing-function: ease-in-out;
- z-index: 2;
- }
-
- .float-1 {
- width: 324px;
- height: 170px;
- right: -120px;
- bottom: 0;
- animation-duration: 3s;
- background-image: url('data:image/svg+xml;utf8, ');
- }
-
- .float-2 {
- width: 241px;
- height: 100px;
- right: 210px;
- bottom: 0;
- animation-duration: 3.5s;
- animation-delay: 0.2s;
- background-image: url('data:image/svg+xml;utf8, ');
- }
-
- .float-3 {
- width: 267px;
- height: 140px;
- right: 110px;
- top: -30px;
- animation-duration: 4s;
- animation-delay: 0.5s;
- background-image: url('data:image/svg+xml;utf8, ');
- }
- }
-
.heading {
position: relative;
z-index: 4;
@@ -346,18 +485,18 @@
background: darken($ui-base-color, 4%);
padding: 20px 0;
- .container {
+ .container-alt {
position: relative;
padding-right: 280px + 15px;
}
- .information-board-sections {
+ &__sections {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
- .section {
+ &__section {
flex: 1 0 0;
font-family: 'mastodon-font-sans-serif', sans-serif;
font-size: 16px;
@@ -382,6 +521,10 @@
font-size: 32px;
line-height: 48px;
}
+
+ @media screen and (max-width: $column-breakpoint) {
+ text-align: center;
+ }
}
.panel {
@@ -460,113 +603,309 @@
}
}
- .features {
- padding: 50px 0;
+ &.alternative {
+ padding: 10px 0;
- .container {
+ .brand {
+ text-align: center;
+ padding: 30px 0;
+ margin-bottom: 10px;
+
+ img {
+ position: static;
+ padding: 10px 0;
+ }
+
+ @media screen and (max-width: $small-breakpoint) {
+ padding: 15px 0;
+ }
+
+ @media screen and (max-width: $column-breakpoint) {
+ padding: 0;
+ margin-bottom: -10px;
+ }
+ }
+ }
+
+ &__information,
+ &__forms {
+ padding: 20px;
+ }
+
+ &__call-to-action {
+ background: darken($ui-base-color, 4%);
+ border-radius: 4px;
+ padding: 25px 40px;
+ overflow: hidden;
+
+ .row {
display: flex;
+ flex-direction: row-reverse;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: center;
}
- #mastodon-timeline {
+ .row__information-board {
display: flex;
- -webkit-overflow-scrolling: touch;
- -ms-overflow-style: -ms-autohiding-scrollbar;
- font-family: 'mastodon-font-sans-serif', sans-serif;
- font-size: 13px;
- line-height: 18px;
- font-weight: 400;
- color: $primary-text-color;
- width: 330px;
- margin-right: 30px;
- flex: 0 0 auto;
- background: $ui-base-color;
- overflow: hidden;
- border-radius: 4px;
- box-shadow: 0 0 6px rgba($black, 0.1);
+ justify-content: flex-end;
+ align-items: flex-end;
- .column-header {
- color: inherit;
- font-family: inherit;
- font-size: 16px;
- line-height: inherit;
- font-weight: inherit;
- margin: 0;
- padding: 0;
+ .information-board__section {
+ flex: 1 0 auto;
+ padding: 0 10px;
}
+ }
- .column {
- padding: 0;
- border-radius: 4px;
- overflow: hidden;
- }
+ .row__mascot {
+ flex: 1;
+ margin: 10px -50px 0 0;
+ }
+ }
- .scrollable {
- height: 400px;
- }
+ &__logo {
+ margin-right: 20px;
- p {
- font-size: inherit;
- line-height: inherit;
- font-weight: inherit;
- color: $primary-text-color;
+ img {
+ height: 50px;
+ width: auto;
+ mix-blend-mode: lighten;
+ }
+ }
+
+ &__information {
+ padding: 45px 40px;
+ margin-bottom: 10px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ @media screen and (max-width: $column-breakpoint) {
+ padding: 25px 20px;
+ }
+ }
+
+ &__information,
+ &__forms,
+ #mastodon-timeline {
+ box-sizing: border-box;
+ background: $ui-base-color;
+ border-radius: 4px;
+ box-shadow: 0 0 6px rgba($black, 0.1);
+ }
+
+ &__mascot {
+ height: 104px;
+ position: relative;
+ left: -40px;
+ bottom: 25px;
+
+ img {
+ height: 190px;
+ width: auto;
+ }
+ }
+
+ &__short-description {
+ .row {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ margin-bottom: 40px;
+ }
+
+ @media screen and (max-width: $column-breakpoint) {
+ .row {
margin-bottom: 20px;
+ }
+ }
- &:last-child {
+ p a {
+ color: $ui-secondary-color;
+ }
+
+ h1 {
+ font-weight: 500;
+ color: $primary-text-color;
+ margin-bottom: 0;
+
+ small {
+ color: $ui-primary-color;
+
+ span {
+ color: $ui-secondary-color;
+ }
+ }
+ }
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ &__hero {
+ margin-bottom: 10px;
+
+ img {
+ display: block;
+ margin: 0;
+ max-width: 100%;
+ height: auto;
+ border-radius: 4px;
+ }
+ }
+
+ &__forms {
+ height: 100%;
+
+ @media screen and (max-width: $small-breakpoint) {
+ height: auto;
+ }
+
+ @media screen and (max-width: $column-breakpoint) {
+ background: transparent;
+ box-shadow: none;
+ padding: 0 20px;
+ margin-top: 30px;
+ margin-bottom: 40px;
+
+ .separator-or {
+ span {
+ background: darken($ui-base-color, 8%);
+ }
+ }
+ }
+
+ hr {
+ margin: 40px 0;
+ }
+
+ .button {
+ display: block;
+ }
+
+ .subtle-hint a {
+ text-decoration: none;
+
+ &:hover,
+ &:focus,
+ &:active {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ #mastodon-timeline {
+ display: flex;
+ -webkit-overflow-scrolling: touch;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+ font-family: 'mastodon-font-sans-serif', sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+ font-weight: 400;
+ color: $primary-text-color;
+ width: 100%;
+ flex: 1 1 auto;
+ overflow: hidden;
+ height: 100%;
+
+ .column-header {
+ color: inherit;
+ font-family: inherit;
+ font-size: 16px;
+ line-height: inherit;
+ font-weight: inherit;
+ margin: 0;
+ padding: 0;
+ }
+
+ .column {
+ padding: 0;
+ border-radius: 4px;
+ overflow: hidden;
+ width: 100%;
+ }
+
+ .scrollable {
+ height: 400px;
+ }
+
+ p {
+ font-size: inherit;
+ line-height: inherit;
+ font-weight: inherit;
+ color: $primary-text-color;
+ margin-bottom: 20px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ a {
+ color: $ui-secondary-color;
+ text-decoration: none;
+ }
+ }
+
+ @media screen and (max-width: $column-breakpoint) {
+ height: 90vh;
+ }
+ }
+
+ &__features {
+ & > p {
+ padding-right: 60px;
+ }
+
+ .features-list {
+ margin: 40px 0;
+ margin-top: 30px;
+ }
+
+ &__action {
+ text-align: center;
+ }
+ }
+
+ .features-list {
+ .features-list__row {
+ display: flex;
+ padding: 10px 0;
+ justify-content: space-between;
+
+ .visual {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ margin-left: 15px;
+
+ .fa {
+ display: block;
+ color: $ui-primary-color;
+ font-size: 48px;
+ }
+ }
+
+ .text {
+ font-size: 16px;
+ line-height: 30px;
+ color: $ui-primary-color;
+
+ h6 {
+ font-size: inherit;
+ line-height: inherit;
margin-bottom: 0;
}
-
- a {
- color: $ui-secondary-color;
- text-decoration: none;
- }
}
}
- .about-mastodon {
- max-width: 675px;
-
- p {
- margin-bottom: 20px;
- }
-
- .features-list {
- margin-top: 20px;
-
- .features-list__row {
- display: flex;
- padding: 10px 0;
- justify-content: space-between;
-
- &:first-child {
- padding-top: 0;
- }
-
- .visual {
- flex: 0 0 auto;
- display: flex;
- align-items: center;
- margin-left: 15px;
-
- .fa {
- display: block;
- color: $ui-primary-color;
- font-size: 48px;
- }
- }
-
- .text {
- font-size: 16px;
- line-height: 30px;
- color: $ui-primary-color;
-
- h6 {
- font-size: inherit;
- line-height: inherit;
- margin-bottom: 0;
- }
- }
- }
- }
+ @media screen and (min-width: $small-breakpoint) {
+ display: grid;
+ grid-gap: 30px;
+ grid-template-columns: 1fr 1fr;
+ grid-auto-columns: 50%;
+ grid-auto-rows: max-content;
}
}
@@ -600,21 +939,31 @@
}
}
+ &__footer {
+ margin-top: 10px;
+ text-align: center;
+ color: $ui-base-lighter-color;
+
+ p {
+ font-size: 14px;
+
+ a {
+ color: inherit;
+ text-decoration: underline;
+ }
+ }
+ }
+
@media screen and (max-width: 840px) {
- .container {
+ .container-alt {
padding: 0 20px;
}
.information-board {
-
- .container {
+ .container-alt {
padding-right: 20px;
}
- .section {
- text-align: center;
- }
-
.panel {
position: static;
margin-top: 20px;
@@ -626,16 +975,6 @@
}
}
}
-
- .header-wrapper .mascot {
- left: 20px;
- }
- }
-
- @media screen and (max-width: 689px) {
- .header-wrapper .mascot {
- display: none;
- }
}
@media screen and (max-width: 675px) {
@@ -651,13 +990,12 @@
}
}
- .header .container,
- .features .container {
+ .header .container-alt,
+ .features .container-alt {
display: block;
}
.header {
-
.links {
padding-top: 15px;
background: darken($ui-base-color, 4%);
@@ -682,10 +1020,6 @@
margin-top: 30px;
padding: 0;
- .floats {
- display: none;
- }
-
.heading {
padding: 30px 20px;
text-align: center;
@@ -700,16 +1034,6 @@
}
}
}
-
- .features #mastodon-timeline {
- height: 70vh;
- width: 100%;
- margin-bottom: 50px;
-
- .column {
- width: 100%;
- }
- }
}
.cta {
@@ -717,108 +1041,55 @@
}
&.tag-page {
- .features {
- padding: 30px 0;
+ .grid {
+ @media screen and (min-width: $small-breakpoint) {
+ grid-template-columns: 33% 67%;
+ }
- .container {
- max-width: 820px;
-
- #mastodon-timeline {
- margin-right: 0;
- border-top-right-radius: 0;
- }
-
- .about-mastodon {
- .about-hashtag {
- background: darken($ui-base-color, 4%);
- padding: 0 20px 20px 30px;
- border-radius: 0 5px 5px 0;
-
- .brand {
- padding-top: 20px;
- margin-bottom: 20px;
-
- img {
- height: 48px;
- width: auto;
- }
- }
-
- p {
- strong {
- color: $ui-secondary-color;
- font-weight: 700;
- }
- }
-
- .cta {
- margin: 0;
-
- .button {
- margin-right: 4px;
- }
- }
- }
-
- .features-list {
- margin-left: 30px;
- margin-right: 10px;
- }
- }
+ .column-2 {
+ grid-column: 2;
+ grid-row: 1;
}
}
- @media screen and (max-width: 675px) {
- .features {
- padding: 10px 0;
+ .brand {
+ text-align: unset;
+ padding: 0;
- .container {
- display: flex;
- flex-direction: column;
+ img {
+ height: 48px;
+ width: auto;
+ }
+ }
- #mastodon-timeline {
- order: 2;
- flex: 0 0 auto;
- height: 60vh;
- margin-bottom: 20px;
- border-top-right-radius: 4px;
- }
+ .cta {
+ margin: 0;
- .about-mastodon {
- order: 1;
- flex: 0 0 auto;
- max-width: 100%;
+ .button {
+ margin-right: 4px;
+ }
+ }
- .about-hashtag {
- background: unset;
- padding: 0;
- border-radius: 0;
-
- .cta {
- margin: 20px 0;
- }
- }
-
- .features-list {
- display: none;
- }
- }
+ @media screen and (max-width: $column-breakpoint) {
+ .grid {
+ .column-1 {
+ grid-column: 1;
+ grid-row: 2;
}
+
+ .column-2 {
+ grid-column: 1;
+ grid-row: 1;
+ }
+ }
+
+ .brand {
+ margin: 0;
+ }
+
+ .landing-page__features {
+ display: none;
}
}
}
}
-
-@keyframes floating {
- from {
- transform: translate(0, 0);
- }
-
- 65% {
- transform: translate(0, 4px);
- }
-
- to {
- transform: translate(0, -0);
- }
-}
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index 9015d04cb..dd82ab375 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -97,32 +97,6 @@
}
}
- .controls {
- position: absolute;
- top: 15px;
- left: 15px;
- z-index: 2;
-
- .icon-button {
- color: rgba($white, 0.8);
- text-decoration: none;
- font-size: 13px;
- line-height: 13px;
- font-weight: 500;
-
- .fa {
- font-weight: 400;
- margin-right: 5px;
- }
-
- &:hover,
- &:active,
- &:focus {
- color: $white;
- }
- }
- }
-
.roles {
margin-bottom: 30px;
padding: 0 15px;
@@ -226,6 +200,40 @@
}
}
+.card,
+.account-grid-card {
+ .controls {
+ position: absolute;
+ top: 15px;
+ left: 15px;
+ z-index: 2;
+
+ .icon-button {
+ color: rgba($white, 0.8);
+ text-decoration: none;
+ font-size: 13px;
+ line-height: 13px;
+ font-weight: 500;
+
+ .fa {
+ font-weight: 400;
+ margin-right: 5px;
+ }
+
+ &:hover,
+ &:active,
+ &:focus {
+ color: $white;
+ }
+ }
+ }
+}
+
+.account-grid-card .controls {
+ left: auto;
+ right: 15px;
+}
+
.pagination {
padding: 30px 0;
text-align: center;
@@ -233,8 +241,8 @@
a,
.current,
- .next,
- .prev,
+ .newer,
+ .older,
.page,
.gap {
font-size: 14px;
@@ -257,13 +265,13 @@
cursor: default;
}
- .prev,
- .next {
+ .older,
+ .newer {
text-transform: uppercase;
color: $ui-secondary-color;
}
- .prev {
+ .older {
float: left;
padding-left: 0;
@@ -273,7 +281,7 @@
}
}
- .next {
+ .newer {
float: right;
padding-right: 0;
@@ -295,8 +303,8 @@
display: none;
}
- .next,
- .prev {
+ .newer,
+ .older {
display: inline-block;
}
}
@@ -411,13 +419,14 @@
font-weight: 400;
}
- .note {
+ .account__header__content {
padding: 10px 15px;
padding-top: 15px;
- box-sizing: border-box;
color: lighten($ui-base-color, 26%);
word-wrap: break-word;
- min-height: 80px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ height: 5.5em;
}
}
}
@@ -431,6 +440,7 @@
text-align: center;
padding: 60px 0;
padding-top: 55px;
+ margin: 0 auto;
cursor: default;
}
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 0c343e1df..e6bd0c717 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -105,6 +105,16 @@
margin-bottom: 30px;
}
+ h4 {
+ text-transform: uppercase;
+ font-size: 13px;
+ font-weight: 500;
+ color: $ui-primary-color;
+ padding-bottom: 8px;
+ margin-bottom: 8px;
+ border-bottom: 1px solid lighten($ui-base-color, 8%);
+ }
+
h6 {
font-size: 16px;
color: $ui-secondary-color;
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index b5d77ff63..bec0d4d91 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -47,6 +47,10 @@ body {
padding-bottom: 0;
}
+ &.player {
+ text-align: center;
+ }
+
&.embed {
background: transparent;
margin: 0;
@@ -118,5 +122,6 @@ button {
height: 100%;
align-items: center;
justify-content: center;
+ outline: 0 !important;
}
}
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index bfca34f4d..20e07a042 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -40,14 +40,20 @@
cursor: default;
}
- &.button-alternative {
+ &.button-primary,
+ &.button-alternative,
+ &.button-secondary,
+ &.button-alternative-2 {
font-size: 16px;
line-height: 36px;
height: auto;
- color: $ui-base-color;
- background: $ui-primary-color;
text-transform: none;
padding: 4px 16px;
+ }
+
+ &.button-alternative {
+ color: $ui-base-color;
+ background: $ui-primary-color;
&:active,
&:focus,
@@ -56,15 +62,20 @@
}
}
+ &.button-alternative-2 {
+ background: $ui-base-lighter-color;
+
+ &:active,
+ &:focus,
+ &:hover {
+ background-color: lighten($ui-base-lighter-color, 4%);
+ }
+ }
+
&.button-secondary {
- font-size: 16px;
- line-height: 36px;
- height: auto;
color: $ui-primary-color;
- text-transform: none;
background: transparent;
padding: 3px 15px;
- border-radius: 4px;
border: 1px solid $ui-primary-color;
&:active,
@@ -433,6 +444,34 @@
min-width: 40%;
margin: 5px;
+ &__actions {
+ background: linear-gradient(180deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ opacity: 0;
+ transition: opacity .1s ease;
+
+ .icon-button {
+ flex: 0 1 auto;
+ color: $ui-secondary-color;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 10px;
+ font-family: inherit;
+
+ &:hover,
+ &:focus,
+ &:active {
+ color: lighten($ui-secondary-color, 4%);
+ }
+ }
+
+ &.active {
+ opacity: 1;
+ }
+ }
+
&-description {
position: absolute;
z-index: 2;
@@ -470,10 +509,6 @@
opacity: 1;
}
}
-
- .icon-button {
- mix-blend-mode: difference;
- }
}
.compose-form__upload-thumbnail {
@@ -481,8 +516,9 @@
background-position: center;
background-size: cover;
background-repeat: no-repeat;
- height: 100px;
+ height: 140px;
width: 100%;
+ overflow: hidden;
}
}
@@ -686,12 +722,13 @@
background: transparent;
border: 0;
color: lighten($ui-base-color, 8%);
- font-weight: 500;
+ font-weight: 700;
font-size: 11px;
padding: 0 6px;
text-transform: uppercase;
- line-height: inherit;
+ line-height: 20px;
cursor: pointer;
+ vertical-align: middle;
}
.status__prepend-icon-wrapper {
@@ -825,12 +862,27 @@
border-bottom: 1px solid $ui-secondary-color;
display: flex;
- .status__content {
- flex: 1 1 auto;
- padding: 10px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
+ .status-check-box__status {
+ margin: 10px 0 10px 10px;
+ flex: 1;
+
+ .media-gallery {
+ max-width: 250px;
+ }
+
+ .status__content {
+ padding: 0;
+ white-space: normal;
+ }
+
+ .video-player {
+ margin-top: 8px;
+ max-width: 250px;
+ }
+
+ .media-gallery__item-thumbnail {
+ cursor: default;
+ }
}
}
@@ -899,6 +951,11 @@
height: 24px;
margin: -1px 0 0;
}
+
+ .status__content__spoiler-link {
+ line-height: 24px;
+ margin: -1px 0 0;
+ }
}
.video-player {
@@ -1376,36 +1433,29 @@
.image-loader {
position: relative;
+ width: 100%;
+ height: 100%;
&.image-loader--loading {
+ display: flex;
+ align-content: center;
+
.image-loader__preview-canvas {
filter: blur(2px);
}
}
- .image-loader__img {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- max-width: 100%;
- max-height: 100%;
- background-image: none;
+ &.image-loader--amorphous .image-loader__preview-canvas {
+ display: none;
}
+}
- &.image-loader--amorphous {
- position: static;
-
- .image-loader__preview-canvas {
- display: none;
- }
-
- .image-loader__img {
- position: static;
- width: auto;
- height: auto;
- }
- }
+.zoomable-image {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-content: center;
}
.navigation-bar {
@@ -1766,7 +1816,7 @@
position: absolute;
top: 0;
left: 0;
- background: lighten($ui-base-color, 13%) url('data:image/svg+xml;utf8, ') no-repeat bottom / 100% auto;
+ background: lighten($ui-base-color, 13%);
box-sizing: border-box;
padding: 0;
display: flex;
@@ -1779,10 +1829,22 @@
&.darker {
background: $ui-base-color;
}
+}
- > .mastodon {
- background: url('../images/elephant_ui_plane.svg') no-repeat left bottom / contain;
- flex: 1;
+.drawer__inner__mastodon {
+ background: lighten($ui-base-color, 13%) url('data:image/svg+xml;utf8, ') no-repeat bottom / 100% auto;
+ flex: 1;
+ min-height: 47px;
+
+ > img {
+ display: block;
+ object-fit: contain;
+ object-position: bottom left;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ user-drag: none;
+ user-select: none;
}
}
@@ -1827,7 +1889,7 @@
font-size: 14px;
font-weight: 500;
border-bottom: 2px solid lighten($ui-base-color, 8%);
- transition: all 200ms linear;
+ transition: all 50ms linear;
.fa {
font-weight: 400;
@@ -1844,7 +1906,6 @@
&:active {
@media screen and (min-width: 631px) {
background: lighten($ui-base-color, 14%);
- transition: all 100ms linear;
}
}
@@ -1913,7 +1974,7 @@
font-family: inherit;
color: $ui-highlight-color;
cursor: pointer;
- flex: 0 0 auto;
+ white-space: nowrap;
font-size: 16px;
padding: 0 5px 0 0;
z-index: 3;
@@ -2199,7 +2260,6 @@
.status-card {
display: flex;
- cursor: pointer;
font-size: 14px;
border: 1px solid lighten($ui-base-color, 8%);
border-radius: 4px;
@@ -2208,20 +2268,58 @@
text-decoration: none;
overflow: hidden;
- &:hover {
- background: lighten($ui-base-color, 8%);
+ &__actions {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ & > div {
+ background: rgba($base-shadow-color, 0.6);
+ border-radius: 4px;
+ padding: 12px 9px;
+ flex: 0 0 auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ button,
+ a {
+ display: inline;
+ color: $primary-text-color;
+ background: transparent;
+ border: 0;
+ padding: 0 5px;
+ text-decoration: none;
+ opacity: 0.6;
+ font-size: 18px;
+ line-height: 18px;
+
+ &:hover,
+ &:active,
+ &:focus {
+ opacity: 1;
+ }
+ }
+
+ a {
+ font-size: 19px;
+ position: relative;
+ bottom: -1px;
+ }
}
}
-.status-card-video,
-.status-card-rich,
-.status-card-photo {
- margin-top: 14px;
- overflow: hidden;
+a.status-card {
+ cursor: pointer;
- iframe {
- width: 100%;
- height: auto;
+ &:hover {
+ background: lighten($ui-base-color, 8%);
}
}
@@ -2249,6 +2347,7 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+ text-decoration: none;
}
.status-card__content {
@@ -2270,6 +2369,7 @@
.status-card__image {
flex: 0 0 100px;
background: lighten($ui-base-color, 8%);
+ position: relative;
}
.status-card.horizontal {
@@ -2295,6 +2395,8 @@
width: 100%;
height: 100%;
object-fit: cover;
+ background-size: cover;
+ background-position: center center;
}
.load-more {
@@ -2403,15 +2505,21 @@
overflow: hidden;
& > button {
- display: flex;
- flex: auto;
margin: 0;
border: none;
- padding: 15px;
+ padding: 15px 0 15px 15px;
color: inherit;
background: transparent;
font: inherit;
text-align: left;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ flex: 1;
+ }
+
+ & > .column-header__back-button {
+ color: $ui-highlight-color;
}
&.active {
@@ -2432,7 +2540,6 @@
.column-header__buttons {
height: 48px;
display: flex;
- margin-left: 0;
}
.column-header__links .text-btn {
@@ -2512,14 +2619,6 @@
}
}
-.column-header__title {
- display: inline-block;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
- flex: 1;
-}
-
.text-btn {
display: inline-block;
padding: 0;
@@ -2624,12 +2723,16 @@
background: $base-overlay-background;
color: $ui-primary-color;
border: 0;
+ padding: 0;
width: 100%;
height: 100%;
+ border-radius: 4px;
+ appearance: none;
&:hover,
&:active,
&:focus {
+ padding: 0;
color: lighten($ui-primary-color, 8%);
}
}
@@ -2642,7 +2745,7 @@
.media-spoiler__trigger {
display: block;
font-size: 11px;
- font-weight: 500;
+ font-weight: 700;
}
.spoiler-button {
@@ -2696,29 +2799,6 @@
}
}
-.modal-container__nav {
- align-items: center;
- background: rgba($base-overlay-background, 0.5);
- box-sizing: border-box;
- border: 0;
- color: $primary-text-color;
- cursor: pointer;
- display: flex;
- font-size: 24px;
- height: 100%;
- padding: 30px 15px;
- position: absolute;
- top: 0;
-}
-
-.modal-container__nav--left {
- left: -61px;
-}
-
-.modal-container__nav--right {
- right: -61px;
-}
-
.account--follows-info {
color: $primary-text-color;
position: absolute;
@@ -2735,6 +2815,22 @@
border-radius: 4px;
}
+.account--muting-info {
+ color: $primary-text-color;
+ position: absolute;
+ top: 40px;
+ left: 10px;
+ opacity: 0.7;
+ display: inline-block;
+ vertical-align: top;
+ background-color: rgba($base-overlay-background, 0.4);
+ text-transform: uppercase;
+ font-size: 11px;
+ font-weight: 500;
+ padding: 4px;
+ border-radius: 4px;
+}
+
.account--action-button {
position: absolute;
top: 10px;
@@ -3227,6 +3323,43 @@
font-weight: 500;
}
+.search-results__section {
+ margin-bottom: 20px;
+
+ h5 {
+ position: relative;
+
+ &::before {
+ content: "";
+ display: block;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 50%;
+ width: 100%;
+ height: 0;
+ border-top: 1px solid lighten($ui-base-color, 8%);
+ }
+
+ span {
+ display: inline-block;
+ background: $ui-base-color;
+ color: $ui-primary-color;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 10px;
+ position: relative;
+ z-index: 1;
+ cursor: default;
+ }
+ }
+
+ .account:last-child,
+ & > div:last-child .status {
+ border-bottom: 0;
+ }
+}
+
.search-results__hashtag {
display: block;
padding: 10px;
@@ -3278,43 +3411,93 @@
z-index: 9999;
}
+.video-modal {
+ max-width: 100vw;
+ max-height: 100vh;
+ position: relative;
+}
+
.media-modal {
- max-width: 80vw;
- max-height: 80vh;
+ width: 100%;
+ height: 100%;
position: relative;
- .extended-video-player,
img,
canvas,
video {
- max-width: 80vw;
- max-height: 80vh;
+ max-width: 100%;
+ /*
+ put margins on top and bottom of image to avoid the screen coverd by
+ image.
+ */
+ max-height: 80%;
width: auto;
height: auto;
margin: auto;
}
- .extended-video-player,
- video {
- display: flex;
- width: 80vw;
- height: 80vh;
- }
-
img,
canvas {
display: block;
background: url('../images/void.png') repeat;
object-fit: contain;
}
+}
- .react-swipeable-view-container {
- max-width: 80vw;
+.media-modal__closer {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.media-modal__navigation {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ pointer-events: none;
+ transition: opacity 0.3s linear;
+ will-change: opacity;
+
+ * {
+ pointer-events: auto;
+ }
+
+ &.media-modal__navigation--hidden {
+ opacity: 0;
+
+ * {
+ pointer-events: none;
+ }
}
}
-.media-modal__content {
- background: $base-overlay-background;
+.media-modal__nav {
+ background: rgba($base-overlay-background, 0.5);
+ box-sizing: border-box;
+ border: 0;
+ color: $primary-text-color;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ font-size: 24px;
+ height: 20vmax;
+ margin: auto 0;
+ padding: 30px 15px;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+}
+
+.media-modal__nav--left {
+ left: 0;
+}
+
+.media-modal__nav--right {
+ right: 0;
}
.media-modal__pagination {
@@ -3322,7 +3505,8 @@
text-align: center;
position: absolute;
left: 0;
- bottom: -40px;
+ bottom: 20px;
+ pointer-events: none;
}
.media-modal__page-dot {
@@ -3346,8 +3530,8 @@
.media-modal__close {
position: absolute;
- right: 4px;
- top: 4px;
+ right: 8px;
+ top: 8px;
z-index: 100;
}
@@ -3765,8 +3949,7 @@
.boost-modal__action-bar,
.confirmation-modal__action-bar,
-.mute-modal__action-bar,
-.report-modal__action-bar {
+.mute-modal__action-bar {
display: flex;
justify-content: space-between;
background: $ui-secondary-color;
@@ -3810,21 +3993,94 @@
vertical-align: middle;
}
+.report-modal {
+ width: 90vw;
+ max-width: 700px;
+}
+
+.report-modal__container {
+ display: flex;
+ border-top: 1px solid $ui-secondary-color;
+
+ @media screen and (max-width: 480px) {
+ flex-wrap: wrap;
+ overflow-y: auto;
+ }
+}
+
.report-modal__statuses,
.report-modal__comment {
- padding: 10px;
+ box-sizing: border-box;
+ width: 50%;
+
+ @media screen and (max-width: 480px) {
+ width: 100%;
+ }
}
.report-modal__statuses {
+ flex: 1 1 auto;
min-height: 20vh;
max-height: 40vh;
overflow-y: auto;
overflow-x: hidden;
+
+ @media screen and (max-width: 480px) {
+ max-height: 10vh;
+ }
}
.report-modal__comment {
+ padding: 20px;
+ border-right: 1px solid $ui-secondary-color;
+ max-width: 320px;
+
+ p {
+ font-size: 14px;
+ line-height: 20px;
+ margin-bottom: 20px;
+ }
+
.setting-text {
- margin-top: 10px;
+ display: block;
+ box-sizing: border-box;
+ width: 100%;
+ margin: 0;
+ color: $ui-base-color;
+ background: $white;
+ padding: 10px;
+ font-family: inherit;
+ font-size: 14px;
+ resize: vertical;
+ border: 0;
+ outline: 0;
+ border-radius: 4px;
+ border: 1px solid $ui-secondary-color;
+ margin-bottom: 20px;
+
+ &:focus {
+ border: 1px solid darken($ui-secondary-color, 8%);
+ }
+ }
+
+ .setting-toggle {
+ margin-top: 20px;
+ margin-bottom: 24px;
+
+ &__label {
+ color: $ui-base-color;
+ font-size: 14px;
+ }
+ }
+
+ @media screen and (max-width: 480px) {
+ padding: 10px;
+ max-width: 100%;
+ order: 2;
+
+ .setting-toggle {
+ margin-bottom: 4px;
+ }
}
}
@@ -3917,6 +4173,15 @@
}
}
+.report-modal__target {
+ padding: 20px;
+
+ .media-modal__close {
+ top: 19px;
+ right: 15px;
+ }
+}
+
.loading-bar {
background-color: $ui-highlight-color;
height: 3px;
@@ -3963,45 +4228,59 @@
border-radius: 4px;
margin-top: 14px;
overflow: hidden;
-}
-.attachment-list__icon {
- flex: 0 0 auto;
- color: $ui-base-lighter-color;
- padding: 8px 18px;
- cursor: default;
- border-right: 1px solid lighten($ui-base-color, 8%);
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: 26px;
-
- .fa {
- display: block;
- }
-}
-
-.attachment-list__list {
- list-style: none;
- padding: 4px 0;
- padding-left: 8px;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- li {
- display: block;
- padding: 4px 0;
- }
-
- a {
- text-decoration: none;
+ &__icon {
+ flex: 0 0 auto;
color: $ui-base-lighter-color;
- font-weight: 500;
+ padding: 8px 18px;
+ cursor: default;
+ border-right: 1px solid lighten($ui-base-color, 8%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: 26px;
- &:hover {
- text-decoration: underline;
+ .fa {
+ display: block;
+ }
+ }
+
+ &__list {
+ list-style: none;
+ padding: 4px 0;
+ padding-left: 8px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+
+ li {
+ display: block;
+ padding: 4px 0;
+ }
+
+ a {
+ text-decoration: none;
+ color: $ui-base-lighter-color;
+ font-weight: 500;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ &.compact {
+ border: 0;
+ margin-top: 4px;
+
+ .attachment-list__list {
+ padding: 0;
+ display: block;
+ }
+
+ .fa {
+ color: $ui-base-lighter-color;
}
}
}
@@ -4011,6 +4290,7 @@
box-sizing: border-box;
margin-top: 8px;
overflow: hidden;
+ border-radius: 4px;
position: relative;
width: 100%;
}
@@ -4021,10 +4301,13 @@
display: block;
float: left;
position: relative;
+ border-radius: 4px;
+ overflow: hidden;
&.standalone {
.media-gallery__item-gifv-thumbnail {
transform: none;
+ top: 0;
}
}
}
@@ -4033,13 +4316,16 @@
cursor: zoom-in;
display: block;
text-decoration: none;
- height: 100%;
+ color: $ui-secondary-color;
line-height: 0;
&,
img {
- width: 100%;
height: 100%;
+ width: 100%;
+ }
+
+ img {
object-fit: cover;
}
}
@@ -4149,7 +4435,7 @@
&.inline {
video {
- object-fit: cover;
+ object-fit: contain;
position: relative;
top: 50%;
transform: translateY(-50%);
@@ -4369,64 +4655,105 @@
/* End Video Player */
.account-gallery__container {
- margin: -2px;
- padding: 4px;
display: flex;
+ justify-content: center;
flex-wrap: wrap;
+ padding: 2px;
}
.account-gallery__item {
- flex: 1 1 auto;
- width: calc(100% / 3 - 4px);
- height: 95px;
- margin: 2px;
+ flex-grow: 1;
+ width: 50%;
+ overflow: hidden;
+ position: relative;
+
+ &::before {
+ content: "";
+ display: block;
+ padding-top: 100%;
+ }
a {
display: block;
- width: 100%;
- height: 100%;
+ width: calc(100% - 4px);
+ height: calc(100% - 4px);
+ margin: 2px;
+ top: 0;
+ left: 0;
background-color: $base-overlay-background;
background-size: cover;
background-position: center;
- position: relative;
- color: inherit;
+ position: absolute;
+ color: $ui-primary-color;
text-decoration: none;
+ border-radius: 4px;
&:hover,
&:active,
&:focus {
outline: 0;
+ color: $ui-secondary-color;
+
+ &::before {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 100%;
+ background: rgba($base-overlay-background, 0.3);
+ border-radius: 4px;
+ }
}
}
+
+ &__icons {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 24px;
+ }
}
-.account-section-headline {
- color: $ui-base-lighter-color;
- background: lighten($ui-base-color, 2%);
- border-bottom: 1px solid lighten($ui-base-color, 4%);
- padding: 15px 10px;
- font-size: 14px;
- font-weight: 500;
- position: relative;
+.account__section-headline {
+ background: darken($ui-base-color, 4%);
+ border-bottom: 1px solid lighten($ui-base-color, 8%);
cursor: default;
+ display: flex;
- &::before,
- &::after {
+ a {
display: block;
- content: "";
- position: absolute;
- bottom: 0;
- left: 18px;
- width: 0;
- height: 0;
- border-style: solid;
- border-width: 0 10px 10px;
- border-color: transparent transparent lighten($ui-base-color, 4%);
- }
+ flex: 1 1 auto;
+ color: $ui-primary-color;
+ padding: 15px 0;
+ font-size: 14px;
+ font-weight: 500;
+ text-align: center;
+ text-decoration: none;
+ position: relative;
- &::after {
- bottom: -1px;
- border-color: transparent transparent $ui-base-color;
+ &.active {
+ color: $ui-secondary-color;
+
+ &::before,
+ &::after {
+ display: block;
+ content: "";
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ width: 0;
+ height: 0;
+ transform: translateX(-50%);
+ border-style: solid;
+ border-width: 0 10px 10px;
+ border-color: transparent transparent lighten($ui-base-color, 8%);
+ }
+
+ &::after {
+ bottom: -1px;
+ border-color: transparent transparent $ui-base-color;
+ }
+ }
}
}
@@ -4748,3 +5075,69 @@ noscript {
margin-bottom: 0;
}
}
+
+.focal-point-modal {
+ max-width: 80vw;
+ max-height: 80vh;
+ position: relative;
+}
+
+.focal-point {
+ position: relative;
+ cursor: pointer;
+ overflow: hidden;
+
+ &.dragging {
+ cursor: move;
+ }
+
+ img {
+ max-width: 80vw;
+ max-height: 80vh;
+ width: auto;
+ height: auto;
+ margin: auto;
+ }
+
+ &__reticle {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ transform: translate(-50%, -50%);
+ background: url('../images/reticle.png') no-repeat 0 0;
+ border-radius: 50%;
+ box-shadow: 0 0 0 9999em rgba($base-shadow-color, 0.35);
+ }
+
+ &__overlay {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ }
+}
+
+.floating-action-button {
+ position: fixed;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 3.9375rem;
+ height: 3.9375rem;
+ bottom: 1.3125rem;
+ right: 1.3125rem;
+ background: darken($ui-highlight-color, 3%);
+ color: $white;
+ border-radius: 50%;
+ font-size: 21px;
+ line-height: 21px;
+ text-decoration: none;
+ box-shadow: 2px 3px 9px rgba($base-shadow-color, 0.4);
+
+ &:hover,
+ &:focus,
+ &:active {
+ background: lighten($ui-highlight-color, 7%);
+ }
+}
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index af2589e23..6fa1fa38f 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -1,4 +1,4 @@
-.container {
+.container-alt {
width: 700px;
margin: 0 auto;
margin-top: 40px;
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index 2bef53cff..d74c5a4fd 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -22,16 +22,6 @@ code {
margin-top: 4px;
}
- h4 {
- text-transform: uppercase;
- font-size: 13px;
- font-weight: 500;
- color: $ui-primary-color;
- padding-bottom: 8px;
- margin-bottom: 8px;
- border-bottom: 1px solid lighten($ui-base-color, 8%);
- }
-
p.hint {
margin-bottom: 15px;
color: $ui-primary-color;
@@ -278,6 +268,11 @@ code {
.actions {
margin-top: 30px;
display: flex;
+
+ &.actions--top {
+ margin-top: 0;
+ margin-bottom: 30px;
+ }
}
button,
@@ -568,3 +563,21 @@ code {
margin-bottom: 4px;
}
}
+
+.alternative-login {
+ margin-top: 20px;
+ margin-bottom: 20px;
+
+ h4 {
+ font-size: 16px;
+ color: $ui-base-lighter-color;
+ text-align: center;
+ margin-bottom: 20px;
+ border: 0;
+ padding: 0;
+ }
+
+ .button {
+ display: block;
+ }
+}
diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss
index 77420c84b..e9099a9e9 100644
--- a/app/javascript/styles/mastodon/rtl.scss
+++ b/app/javascript/styles/mastodon/rtl.scss
@@ -1,6 +1,22 @@
body.rtl {
direction: rtl;
+ .column-header > button {
+ text-align: right;
+ padding-left: 0;
+ padding-right: 15px;
+ }
+
+ .landing-page__logo {
+ margin-right: 0;
+ margin-left: 20px;
+ }
+
+ .landing-page .features-list .features-list__row .visual {
+ margin-left: 0;
+ margin-right: 15px;
+ }
+
.column-link__icon,
.column-header__icon {
margin-right: 0;
diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss
index a35bbee79..442b143a0 100644
--- a/app/javascript/styles/mastodon/stream_entries.scss
+++ b/app/javascript/styles/mastodon/stream_entries.scss
@@ -65,6 +65,10 @@
}
}
+ .media-gallery__gifv__label {
+ bottom: 9px;
+ }
+
.status.light {
padding: 14px 14px 14px (48px + 14px * 2);
position: relative;
@@ -142,10 +146,10 @@
a.status__content__spoiler-link {
color: $primary-text-color;
- background: $ui-primary-color;
+ background: $ui-base-color;
&:hover {
- background: lighten($ui-primary-color, 8%);
+ background: lighten($ui-base-color, 8%);
}
}
}
@@ -210,10 +214,10 @@
a.status__content__spoiler-link {
color: $primary-text-color;
- background: $ui-primary-color;
+ background: $ui-base-color;
&:hover {
- background: lighten($ui-primary-color, 8%);
+ background: lighten($ui-base-color, 8%);
}
}
}
@@ -256,16 +260,8 @@
}
.media-spoiler {
- background: $ui-primary-color;
- color: $white;
- transition: all 100ms linear;
-
- &:hover,
- &:active,
- &:focus {
- background: darken($ui-primary-color, 5%);
- color: unset;
- }
+ background: $ui-base-color;
+ color: $ui-primary-color;
}
.pre-header {
diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb
index 0f9e4f263..9b00f0f52 100644
--- a/app/lib/activitypub/activity.rb
+++ b/app/lib/activitypub/activity.rb
@@ -44,6 +44,12 @@ class ActivityPub::Activity
ActivityPub::Activity::Accept
when 'Reject'
ActivityPub::Activity::Reject
+ when 'Flag'
+ ActivityPub::Activity::Flag
+ when 'Add'
+ ActivityPub::Activity::Add
+ when 'Remove'
+ ActivityPub::Activity::Remove
end
end
end
@@ -74,7 +80,7 @@ class ActivityPub::Activity
# Only continue if the status is supposed to have
# arrived in real-time
- return unless @options[:override_timestamps]
+ return unless @options[:override_timestamps] || status.within_realtime_window?
distribute_to_followers(status)
end
diff --git a/app/lib/activitypub/activity/add.rb b/app/lib/activitypub/activity/add.rb
new file mode 100644
index 000000000..ea94d2f98
--- /dev/null
+++ b/app/lib/activitypub/activity/add.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ActivityPub::Activity::Add < ActivityPub::Activity
+ def perform
+ return unless @json['target'].present? && value_or_id(@json['target']) == @account.featured_collection_url
+
+ status = status_from_uri(object_uri)
+
+ return unless status.account_id == @account.id && !@account.pinned?(status)
+
+ StatusPin.create!(account: @account, status: status)
+ end
+end
diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb
index abf2b9b80..c8a358195 100644
--- a/app/lib/activitypub/activity/announce.rb
+++ b/app/lib/activitypub/activity/announce.rb
@@ -15,7 +15,8 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
account: @account,
reblog: original_status,
uri: @json['id'],
- created_at: @options[:override_timestamps] ? nil : @json['published']
+ created_at: @options[:override_timestamps] ? nil : @json['published'],
+ visibility: original_status.visibility
)
distribute(status)
@@ -35,6 +36,6 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
end
def announceable?(status)
- status.public_visibility? || status.unlisted_visibility?
+ status.account_id == @account.id || status.public_visibility? || status.unlisted_visibility?
end
end
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index 64c429420..676e885c0 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -20,13 +20,12 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
private
def process_status
- media_attachments = process_attachments
+ status_params = process_status_params
ApplicationRecord.transaction do
@status = Status.create!(status_params)
process_tags(@status)
- attach_media(@status, media_attachments)
end
resolve_thread(@status)
@@ -40,7 +39,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
status
end
- def status_params
+ def process_status_params
{
uri: @object['id'],
url: object_url || @object['id'],
@@ -54,6 +53,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
visibility: visibility_from_audience,
thread: replied_to_status,
conversation: conversation_from_uri(@object['conversation']),
+ media_attachment_ids: process_attachments.take(4).map(&:id),
}
end
@@ -108,7 +108,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def process_attachments
- return if @object['attachment'].nil?
+ return [] if @object['attachment'].nil?
media_attachments = []
@@ -116,7 +116,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
next if unsupported_media_type?(attachment['mediaType']) || attachment['url'].blank?
href = Addressable::URI.parse(attachment['url']).normalize.to_s
- media_attachment = MediaAttachment.create(account: @account, remote_url: href, description: attachment['name'].presence)
+ media_attachment = MediaAttachment.create(account: @account, remote_url: href, description: attachment['name'].presence, focus: attachment['focalPoint'])
media_attachments << media_attachment
next if skip_download?
@@ -132,13 +132,6 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
media_attachments
end
- def attach_media(status, media_attachments)
- return if media_attachments.blank?
-
- media = MediaAttachment.where(status_id: nil, id: media_attachments.take(4).map(&:id))
- media.update(status_id: status.id)
- end
-
def resolve_thread(status)
return unless status.reply? && status.thread.nil?
ThreadResolveWorker.perform_async(status.id, in_reply_to_uri)
diff --git a/app/lib/activitypub/activity/flag.rb b/app/lib/activitypub/activity/flag.rb
new file mode 100644
index 000000000..36d3c5730
--- /dev/null
+++ b/app/lib/activitypub/activity/flag.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class ActivityPub::Activity::Flag < ActivityPub::Activity
+ def perform
+ target_accounts = object_uris.map { |uri| account_from_uri(uri) }.compact.select(&:local?)
+ target_statuses_by_account = object_uris.map { |uri| status_from_uri(uri) }.compact.select(&:local?).group_by(&:account_id)
+
+ target_accounts.each do |target_account|
+ next if Report.where(account: @account, target_account: target_account).exists?
+
+ target_statuses = target_statuses_by_account[target_account.id]
+
+ ReportService.new.call(
+ @account,
+ target_account,
+ status_ids: target_statuses.nil? ? [] : target_statuses.map(&:id),
+ comment: @json['content'] || ''
+ )
+ end
+ end
+
+ def object_uris
+ @object_uris ||= Array(@object.is_a?(Array) ? @object.map { |item| value_or_id(item) } : value_or_id(@object))
+ end
+end
diff --git a/app/lib/activitypub/activity/reject.rb b/app/lib/activitypub/activity/reject.rb
index d815feeb6..28d472883 100644
--- a/app/lib/activitypub/activity/reject.rb
+++ b/app/lib/activitypub/activity/reject.rb
@@ -17,6 +17,8 @@ class ActivityPub::Activity::Reject < ActivityPub::Activity
follow_request = FollowRequest.find_by(account: target_account, target_account: @account)
follow_request&.reject!
+
+ UnfollowService.new.call(target_account, @account) if target_account.following?(@account)
end
def target_uri
diff --git a/app/lib/activitypub/activity/remove.rb b/app/lib/activitypub/activity/remove.rb
new file mode 100644
index 000000000..62a1e3196
--- /dev/null
+++ b/app/lib/activitypub/activity/remove.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class ActivityPub::Activity::Remove < ActivityPub::Activity
+ def perform
+ return unless @json['target'].present? && value_or_id(@json['target']) == @account.featured_collection_url
+
+ status = status_from_uri(object_uri)
+
+ return unless status.account_id == @account.id
+
+ pin = StatusPin.find_by(account: @account, status: status)
+ pin&.destroy!
+ end
+end
diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb
index 90d589d90..f19b04ae6 100644
--- a/app/lib/activitypub/adapter.rb
+++ b/app/lib/activitypub/adapter.rb
@@ -17,6 +17,8 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
'conversation' => 'ostatus:conversation',
'toot' => 'http://joinmastodon.org/ns#',
'Emoji' => 'toot:Emoji',
+ 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' },
+ 'featured' => 'toot:featured',
},
],
}.freeze
diff --git a/app/lib/exceptions.rb b/app/lib/exceptions.rb
index b2489711d..95e3365c2 100644
--- a/app/lib/exceptions.rb
+++ b/app/lib/exceptions.rb
@@ -4,6 +4,7 @@ module Mastodon
class Error < StandardError; end
class NotPermittedError < Error; end
class ValidationError < Error; end
+ class HostValidationError < ValidationError; end
class RaceConditionError < Error; end
class UnexpectedResponseError < Error
diff --git a/app/lib/fast_geometry_parser.rb b/app/lib/fast_geometry_parser.rb
new file mode 100644
index 000000000..5209c2bc5
--- /dev/null
+++ b/app/lib/fast_geometry_parser.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class FastGeometryParser
+ def self.from_file(file)
+ width, height = FastImage.size(file.path)
+
+ raise Paperclip::Errors::NotIdentifiedByImageMagickError if width.nil?
+
+ Paperclip::Geometry.new(width, height)
+ end
+end
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 20b10e113..dd78e543f 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -220,6 +220,14 @@ class FeedManager
return false
end
else
+ # A reblog may reach earlier than the original status because of the
+ # delay of the worker deliverying the original status, the late addition
+ # by merging timelines, and other reasons.
+ # If such a reblog already exists, just do not re-insert it into the feed.
+ rank = redis.zrevrank(reblog_key, status.id)
+
+ return false unless rank.nil?
+
redis.zadd(timeline_key, status.id, status.id)
end
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 8c0f8cebc..1df4ff8d4 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -19,6 +19,8 @@ class Formatter
raw_content = status.text
+ return '' if raw_content.blank?
+
unless status.local?
html = reformat(raw_content)
html = encode_custom_emojis(html, status.emojis) if options[:custom_emojify]
diff --git a/app/lib/ostatus/activity/creation.rb b/app/lib/ostatus/activity/creation.rb
index b38407cd3..6235127b2 100644
--- a/app/lib/ostatus/activity/creation.rb
+++ b/app/lib/ostatus/activity/creation.rb
@@ -29,7 +29,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
# Skip if the reblogged status is not public
return if cached_reblog && !(cached_reblog.public_visibility? || cached_reblog.unlisted_visibility?)
- media_attachments = save_media
+ media_attachments = save_media.take(4)
ApplicationRecord.transaction do
status = Status.create!(
@@ -44,12 +44,12 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
language: content_language,
visibility: visibility_scope,
conversation: find_or_create_conversation,
- thread: thread? ? find_status(thread.first) || find_activitypub_status(thread.first, thread.second) : nil
+ thread: thread? ? find_status(thread.first) || find_activitypub_status(thread.first, thread.second) : nil,
+ media_attachment_ids: media_attachments.map(&:id)
)
save_mentions(status)
save_hashtags(status)
- attach_media(status, media_attachments)
save_emojis(status)
end
@@ -61,7 +61,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
Rails.logger.debug "Queuing remote status #{status.id} (#{id}) for distribution"
LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text?
- DistributionWorker.perform_async(status.id) if @options[:override_timestamps]
+ DistributionWorker.perform_async(status.id) if @options[:override_timestamps] || status.within_realtime_window?
status
end
@@ -159,13 +159,6 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
media_attachments
end
- def attach_media(parent, media_attachments)
- return if media_attachments.blank?
-
- media = MediaAttachment.where(status_id: nil, id: media_attachments.take(4).map(&:id))
- media.update(status_id: parent.id)
- end
-
def save_emojis(parent)
do_not_download = DomainBlock.find_by(domain: parent.account.domain)&.reject_media?
diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb
index 656e45822..46d0a8b37 100644
--- a/app/lib/ostatus/atom_serializer.rb
+++ b/app/lib/ostatus/atom_serializer.rb
@@ -351,7 +351,7 @@ class OStatus::AtomSerializer
append_element(entry, 'summary', status.spoiler_text, 'xml:lang': status.language) if status.spoiler_text?
append_element(entry, 'content', Formatter.instance.format(status).to_str, type: 'html', 'xml:lang': status.language)
- status.mentions.each do |mentioned|
+ status.mentions.order(:id).each do |mentioned|
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:person], href: OStatus::TagManager.instance.uri_for(mentioned.account))
end
diff --git a/app/lib/request.rb b/app/lib/request.rb
index 7671f4ffc..298fb9528 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+require 'ipaddr'
+require 'socket'
+
class Request
REQUEST_TARGET = '(request-target)'
@@ -8,7 +11,7 @@ class Request
def initialize(verb, url, **options)
@verb = verb
@url = Addressable::URI.parse(url).normalize
- @options = options
+ @options = options.merge(socket_class: Socket)
@headers = {}
set_common_headers!
@@ -87,4 +90,25 @@ class Request
def http_client
HTTP.timeout(:per_operation, timeout).follow(max_hops: 2)
end
+
+ class Socket < TCPSocket
+ class << self
+ def open(host, *args)
+ outer_e = nil
+ Addrinfo.foreach(host, nil, nil, :SOCK_STREAM) do |address|
+ begin
+ raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address.ip_address)
+ return super address.ip_address, *args
+ rescue => e
+ outer_e = e
+ end
+ end
+ raise outer_e if outer_e
+ end
+
+ alias new open
+ end
+ end
+
+ private_constant :Socket
end
diff --git a/app/lib/sidekiq_error_handler.rb b/app/lib/sidekiq_error_handler.rb
new file mode 100644
index 000000000..23785cf05
--- /dev/null
+++ b/app/lib/sidekiq_error_handler.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class SidekiqErrorHandler
+ def call(*)
+ yield
+ rescue Mastodon::HostValidationError => e
+ Rails.logger.error "#{e.class}: #{e.message}"
+ Rails.logger.error e.backtrace.join("\n")
+ # Do not retry
+ end
+end
diff --git a/app/lib/status_filter.rb b/app/lib/status_filter.rb
index a6a050ce1..41d4381e5 100644
--- a/app/lib/status_filter.rb
+++ b/app/lib/status_filter.rb
@@ -9,6 +9,7 @@ class StatusFilter
end
def filtered?
+ return false if !account.nil? && account.id == status.account_id
blocked_by_policy? || (account_present? && filtered_status?) || silenced_account?
end
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
index d48e1da65..4d6f19467 100644
--- a/app/lib/user_settings_decorator.rb
+++ b/app/lib/user_settings_decorator.rb
@@ -15,18 +15,19 @@ class UserSettingsDecorator
private
def process_update
- user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
- user.settings['interactions'] = merged_interactions if change?('interactions')
- user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy')
- user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive')
- user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal')
- user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal')
- user.settings['delete_modal'] = delete_modal_preference if change?('setting_delete_modal')
- user.settings['auto_play_gif'] = auto_play_gif_preference if change?('setting_auto_play_gif')
- user.settings['reduce_motion'] = reduce_motion_preference if change?('setting_reduce_motion')
- user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui')
- user.settings['noindex'] = noindex_preference if change?('setting_noindex')
- user.settings['theme'] = theme_preference if change?('setting_theme')
+ user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
+ user.settings['interactions'] = merged_interactions if change?('interactions')
+ user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy')
+ user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive')
+ user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal')
+ user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal')
+ user.settings['delete_modal'] = delete_modal_preference if change?('setting_delete_modal')
+ user.settings['auto_play_gif'] = auto_play_gif_preference if change?('setting_auto_play_gif')
+ user.settings['display_sensitive_media'] = display_sensitive_media_preference if change?('setting_display_sensitive_media')
+ user.settings['reduce_motion'] = reduce_motion_preference if change?('setting_reduce_motion')
+ user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui')
+ user.settings['noindex'] = noindex_preference if change?('setting_noindex')
+ user.settings['theme'] = theme_preference if change?('setting_theme')
end
def merged_notification_emails
@@ -65,6 +66,10 @@ class UserSettingsDecorator
boolean_cast_setting 'setting_auto_play_gif'
end
+ def display_sensitive_media_preference
+ boolean_cast_setting 'setting_display_sensitive_media'
+ end
+
def reduce_motion_preference
boolean_cast_setting 'setting_reduce_motion'
end
diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb
index 9fed4a636..b45844296 100644
--- a/app/mailers/notification_mailer.rb
+++ b/app/mailers/notification_mailer.rb
@@ -9,7 +9,7 @@ class NotificationMailer < ApplicationMailer
@me = recipient
@status = notification.target_status
- return if @me.user.disabled?
+ return if @me.user.disabled? || @status.nil?
locale_for_account(@me) do
thread_by_conversation(@status.conversation)
@@ -33,7 +33,7 @@ class NotificationMailer < ApplicationMailer
@account = notification.from_account
@status = notification.target_status
- return if @me.user.disabled?
+ return if @me.user.disabled? || @status.nil?
locale_for_account(@me) do
thread_by_conversation(@status.conversation)
@@ -46,7 +46,7 @@ class NotificationMailer < ApplicationMailer
@account = notification.from_account
@status = notification.target_status
- return if @me.user.disabled?
+ return if @me.user.disabled? || @status.nil?
locale_for_account(@me) do
thread_by_conversation(@status.conversation)
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index 2fc9caba3..9848c34a2 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -66,4 +66,16 @@ class UserMailer < Devise::Mailer
mail to: @resource.email, subject: I18n.t('user_mailer.welcome.subject')
end
end
+
+ def backup_ready(user, backup)
+ @resource = user
+ @instance = Rails.configuration.x.local_domain
+ @backup = backup
+
+ return if @resource.disabled?
+
+ I18n.with_locale(@resource.locale || I18n.default_locale) do
+ mail to: @resource.email, subject: I18n.t('user_mailer.backup_ready.subject')
+ end
+ end
end
diff --git a/app/models/account.rb b/app/models/account.rb
index 13692d0d7..9a83d979f 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -43,10 +43,12 @@
# protocol :integer default("ostatus"), not null
# memorial :boolean default(FALSE), not null
# moved_to_account_id :integer
+# featured_collection_url :string
#
class Account < ApplicationRecord
- MENTION_RE = /(?<=^|[^\/[:word:]])@(([a-z0-9_]+)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
+ USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.]+[a-z0-9_]+)?/i
+ MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
include AccountAvatar
include AccountFinderConcern
@@ -67,7 +69,8 @@ class Account < ApplicationRecord
validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? }
# Local user validations
- validates :username, format: { with: /\A[a-z0-9_]+\z/i }, uniqueness: { scope: :domain, case_sensitive: false }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? }
+ validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? }
+ validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? }
validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
validates :note, length: { maximum: 160 }, if: -> { local? && will_save_change_to_note? }
diff --git a/app/models/account_domain_block.rb b/app/models/account_domain_block.rb
index abcc923b3..bc00b4f32 100644
--- a/app/models/account_domain_block.rb
+++ b/app/models/account_domain_block.rb
@@ -16,12 +16,16 @@ class AccountDomainBlock < ApplicationRecord
belongs_to :account
validates :domain, presence: true, uniqueness: { scope: :account_id }
- after_create :remove_blocking_cache
- after_destroy :remove_blocking_cache
+ after_commit :remove_blocking_cache
+ after_commit :remove_relationship_cache
private
def remove_blocking_cache
Rails.cache.delete("exclude_domains_for:#{account_id}")
end
+
+ def remove_relationship_cache
+ Rails.cache.delete_matched("relationship:#{account_id}:*")
+ end
end
diff --git a/app/models/backup.rb b/app/models/backup.rb
new file mode 100644
index 000000000..5a7e6a14d
--- /dev/null
+++ b/app/models/backup.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: backups
+#
+# id :integer not null, primary key
+# user_id :integer
+# dump_file_name :string
+# dump_content_type :string
+# dump_file_size :integer
+# dump_updated_at :datetime
+# processed :boolean default(FALSE), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class Backup < ApplicationRecord
+ belongs_to :user, inverse_of: :backups
+
+ has_attached_file :dump
+ do_not_validate_attachment_file_type :dump
+end
diff --git a/app/models/block.rb b/app/models/block.rb
index 441e6bca3..d6ecabd3b 100644
--- a/app/models/block.rb
+++ b/app/models/block.rb
@@ -12,14 +12,14 @@
class Block < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
validates :account_id, uniqueness: { scope: :target_account_id }
- after_create :remove_blocking_cache
- after_destroy :remove_blocking_cache
+ after_commit :remove_blocking_cache
private
diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account_avatar.rb
index 8a5c9a22c..9e34a9461 100644
--- a/app/models/concerns/account_avatar.rb
+++ b/app/models/concerns/account_avatar.rb
@@ -7,8 +7,8 @@ module AccountAvatar
class_methods do
def avatar_styles(file)
- styles = { original: '120x120#' }
- styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
+ styles = { original: { geometry: '400x400#', file_geometry_parser: FastGeometryParser } }
+ styles[:static] = { geometry: '400x400#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
styles
end
@@ -17,7 +17,7 @@ module AccountAvatar
included do
# Avatar upload
- has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' }
+ has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-strip' }, processors: [:lazy_thumbnail]
validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
validates_attachment_size :avatar, less_than: 2.megabytes
end
diff --git a/app/models/concerns/account_header.rb b/app/models/concerns/account_header.rb
index aff2aa3f9..04c576b28 100644
--- a/app/models/concerns/account_header.rb
+++ b/app/models/concerns/account_header.rb
@@ -7,8 +7,8 @@ module AccountHeader
class_methods do
def header_styles(file)
- styles = { original: '700x335#' }
- styles[:static] = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
+ styles = { original: { geometry: '700x335#', file_geometry_parser: FastGeometryParser } }
+ styles[:static] = { geometry: '700x335#', format: 'png', convert_options: '-coalesce', file_geometry_parser: FastGeometryParser } if file.content_type == 'image/gif'
styles
end
@@ -17,7 +17,7 @@ module AccountHeader
included do
# Header upload
- has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-quality 80 -strip' }
+ has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-strip' }, processors: [:lazy_thumbnail]
validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
validates_attachment_size :header, less_than: 2.megabytes
end
diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb
new file mode 100644
index 000000000..50288e700
--- /dev/null
+++ b/app/models/concerns/omniauthable.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+module Omniauthable
+ extend ActiveSupport::Concern
+
+ TEMP_EMAIL_PREFIX = 'change@me'
+ TEMP_EMAIL_REGEX = /\Achange@me/
+
+ included do
+ def omniauth_providers
+ Devise.omniauth_configs.keys
+ end
+
+ def email_verified?
+ email && email !~ TEMP_EMAIL_REGEX
+ end
+ end
+
+ class_methods do
+ def find_for_oauth(auth, signed_in_resource = nil)
+ # EOLE-SSO Patch
+ auth.uid = (auth.uid[0][:uid] || auth.uid[0][:user]) if auth.uid.is_a? Hashie::Array
+ identity = Identity.find_for_oauth(auth)
+
+ # If a signed_in_resource is provided it always overrides the existing user
+ # to prevent the identity being locked with accidentally created accounts.
+ # Note that this may leave zombie accounts (with no associated identity) which
+ # can be cleaned up at a later date.
+ user = signed_in_resource ? signed_in_resource : identity.user
+ user = create_for_oauth(auth) if user.nil?
+
+ if identity.user.nil?
+ identity.user = user
+ identity.save!
+ end
+
+ user
+ end
+
+ def create_for_oauth(auth)
+ # Check if the user exists with provided email if the provider gives us a
+ # verified email. If no verified email was provided or the user already
+ # exists, we assign a temporary email and ask the user to verify it on
+ # the next step via Auth::ConfirmationsController.finish_signup
+
+ user = User.new(user_params_from_auth(auth))
+ user.account.avatar_remote_url = auth.info.image if auth.info.image =~ /\A#{URI.regexp(%w(http https))}\z/
+ user.skip_confirmation!
+ user.save!
+ user
+ end
+
+ private
+
+ def user_params_from_auth(auth)
+ strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
+ assume_verified = strategy.try(:security).try(:assume_email_is_verified)
+ email_is_verified = auth.info.verified || auth.info.verified_email || assume_verified
+ email = auth.info.verified_email || auth.info.email
+ email = email_is_verified && !User.exists?(email: auth.info.email) && email
+ display_name = auth.info.full_name || [auth.info.first_name, auth.info.last_name].join(' ')
+
+ {
+ email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
+ password: Devise.friendly_token[0, 20],
+ account_attributes: {
+ username: ensure_unique_username(auth.uid),
+ display_name: display_name,
+ },
+ }
+ end
+
+ def ensure_unique_username(starting_username)
+ username = starting_username
+ i = 0
+
+ while Account.exists?(username: username)
+ i += 1
+ username = "#{starting_username}_#{i}"
+ end
+
+ username
+ end
+ end
+end
diff --git a/app/models/concerns/paginable.rb b/app/models/concerns/paginable.rb
index 6061bf9bd..66695677e 100644
--- a/app/models/concerns/paginable.rb
+++ b/app/models/concerns/paginable.rb
@@ -10,5 +10,14 @@ module Paginable
query = query.where(arel_table[:id].gt(since_id)) if since_id.present?
query
}
+
+ # Differs from :paginate_by_max_id in that it gives the results immediately following min_id,
+ # whereas since_id gives the items with largest id, but with since_id as a cutoff.
+ # Results will be in ascending order by id.
+ scope :paginate_by_min_id, ->(limit, min_id = nil) {
+ query = reorder(arel_table[:id]).limit(limit)
+ query = query.where(arel_table[:id].gt(min_id)) if min_id.present?
+ query
+ }
end
end
diff --git a/app/models/concerns/relationship_cacheable.rb b/app/models/concerns/relationship_cacheable.rb
new file mode 100644
index 000000000..0d9359f7e
--- /dev/null
+++ b/app/models/concerns/relationship_cacheable.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module RelationshipCacheable
+ extend ActiveSupport::Concern
+
+ included do
+ after_commit :remove_relationship_cache
+ end
+
+ private
+
+ def remove_relationship_cache
+ Rails.cache.delete("relationship:#{account_id}:#{target_account_id}")
+ Rails.cache.delete("relationship:#{target_account_id}:#{account_id}")
+ end
+end
diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb
index 990035b34..69685ec83 100644
--- a/app/models/concerns/remotable.rb
+++ b/app/models/concerns/remotable.rb
@@ -28,13 +28,17 @@ module Remotable
matches = response.headers['content-disposition']&.match(/filename="([^"]*)"/)
filename = matches.nil? ? parsed_url.path.split('/').last : matches[1]
basename = SecureRandom.hex(8)
- extname = File.extname(filename)
+ extname = if filename.nil?
+ ''
+ else
+ File.extname(filename)
+ end
send("#{attachment_name}=", StringIO.new(response.to_s))
send("#{attachment_name}_file_name=", basename + extname)
self[attribute_name] = url if has_attribute?(attribute_name)
- rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError => e
+ rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError => e
Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
nil
end
diff --git a/app/models/favourite.rb b/app/models/favourite.rb
index 2b1271f31..fa1884b86 100644
--- a/app/models/favourite.rb
+++ b/app/models/favourite.rb
@@ -13,6 +13,8 @@
class Favourite < ApplicationRecord
include Paginable
+ update_index('statuses#status', :status) if Chewy.enabled?
+
belongs_to :account, inverse_of: :favourites
belongs_to :status, inverse_of: :favourites, counter_cache: true
diff --git a/app/models/follow.rb b/app/models/follow.rb
index f953b8e3e..8e6fe537a 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -13,6 +13,7 @@
class Follow < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account, counter_cache: :following_count
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
index bd6c4a0b9..cde26ceed 100644
--- a/app/models/follow_request.rb
+++ b/app/models/follow_request.rb
@@ -13,6 +13,7 @@
class FollowRequest < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index dd629279c..32922e7f1 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -34,6 +34,8 @@ class Form::AdminSettings
:activity_api_enabled=,
:peers_api_enabled,
:peers_api_enabled=,
+ :show_known_fediverse_at_about_page,
+ :show_known_fediverse_at_about_page=,
to: Setting
)
end
diff --git a/app/models/identity.rb b/app/models/identity.rb
new file mode 100644
index 000000000..a5e0c09ec
--- /dev/null
+++ b/app/models/identity.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: identities
+#
+# id :integer not null, primary key
+# user_id :integer
+# provider :string default(""), not null
+# uid :string default(""), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class Identity < ApplicationRecord
+ belongs_to :user, dependent: :destroy
+ validates :uid, presence: true, uniqueness: { scope: :provider }
+ validates :provider, presence: true
+
+ def self.find_for_oauth(auth)
+ find_or_create_by(uid: auth.uid, provider: auth.provider)
+ end
+end
diff --git a/app/models/import.rb b/app/models/import.rb
index ba88435bf..fdb4c6b80 100644
--- a/app/models/import.rb
+++ b/app/models/import.rb
@@ -26,7 +26,7 @@ class Import < ApplicationRecord
validates :type, presence: true
- has_attached_file :data, url: '/system/:hash.:extension', hash_secret: ENV['PAPERCLIP_SECRET']
+ has_attached_file :data
validates_attachment_content_type :data, content_type: FILE_TYPES
validates_attachment_presence :data
end
diff --git a/app/models/invite.rb b/app/models/invite.rb
index b87a3b722..4ba5432d2 100644
--- a/app/models/invite.rb
+++ b/app/models/invite.rb
@@ -4,7 +4,7 @@
# Table name: invites
#
# id :integer not null, primary key
-# user_id :integer
+# user_id :integer not null
# code :string default(""), not null
# expires_at :datetime
# max_uses :integer
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 4b84b95fa..a4d9cd9d1 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -32,7 +32,18 @@ class MediaAttachment < ApplicationRecord
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze
VIDEO_MIME_TYPES = ['video/webm', 'video/mp4'].freeze
- IMAGE_STYLES = { original: '1280x1280>', small: '400x400>' }.freeze
+ IMAGE_STYLES = {
+ original: {
+ geometry: '1280x1280>',
+ file_geometry_parser: FastGeometryParser,
+ },
+
+ small: {
+ geometry: '400x400>',
+ file_geometry_parser: FastGeometryParser,
+ },
+ }.freeze
+
VIDEO_STYLES = {
small: {
convert_options: {
@@ -80,6 +91,24 @@ class MediaAttachment < ApplicationRecord
shortcode
end
+ def focus=(point)
+ return if point.blank?
+
+ x, y = (point.is_a?(Enumerable) ? point : point.split(',')).map(&:to_f)
+
+ meta = file.instance_read(:meta) || {}
+ meta['focus'] = { 'x' => x, 'y' => y }
+
+ file.instance_write(:meta, meta)
+ end
+
+ def focus
+ x = file.meta['focus']['x']
+ y = file.meta['focus']['y']
+
+ "#{x},#{y}"
+ end
+
before_create :prepare_description, unless: :local?
before_create :set_shortcode
before_post_process :set_type_and_extension
@@ -157,26 +186,42 @@ class MediaAttachment < ApplicationRecord
end
def populate_meta
- meta = {}
+ meta = file.instance_read(:meta) || {}
file.queued_for_write.each do |style, file|
- begin
- geo = Paperclip::Geometry.from_file file
-
- meta[style] = {
- width: geo.width.to_i,
- height: geo.height.to_i,
- size: "#{geo.width.to_i}x#{geo.height.to_i}",
- aspect: geo.width.to_f / geo.height.to_f,
- }
- rescue Paperclip::Errors::NotIdentifiedByImageMagickError
- meta[style] = {}
- end
+ meta[style] = style == :small || image? ? image_geometry(file) : video_metadata(file)
end
meta
end
+ def image_geometry(file)
+ width, height = FastImage.size(file.path)
+
+ return {} if width.nil?
+
+ {
+ width: width,
+ height: height,
+ size: "#{width}x#{height}",
+ aspect: width.to_f / height.to_f,
+ }
+ end
+
+ def video_metadata(file)
+ movie = FFMPEG::Movie.new(file.path)
+
+ return {} unless movie.valid?
+
+ {
+ width: movie.width,
+ height: movie.height,
+ frame_rate: movie.frame_rate,
+ duration: movie.duration,
+ bitrate: movie.bitrate,
+ }
+ end
+
def appropriate_extension
mime_type = MIME::Types[file.content_type]
diff --git a/app/models/mute.rb b/app/models/mute.rb
index 948f22444..8efa27ac0 100644
--- a/app/models/mute.rb
+++ b/app/models/mute.rb
@@ -13,14 +13,14 @@
class Mute < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
validates :account_id, uniqueness: { scope: :target_account_id }
- after_create :remove_blocking_cache
- after_destroy :remove_blocking_cache
+ after_commit :remove_blocking_cache
private
diff --git a/app/models/notification.rb b/app/models/notification.rb
index 733f89cf7..7f8dae5ec 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -69,7 +69,7 @@ class Notification < ApplicationRecord
class << self
def reload_stale_associations!(cached_items)
- account_ids = cached_items.map(&:from_account_id).uniq
+ account_ids = (cached_items.map(&:from_account_id) + cached_items.map { |item| item.target_status&.account_id }.compact).uniq
return if account_ids.empty?
@@ -77,6 +77,7 @@ class Notification < ApplicationRecord
cached_items.each do |item|
item.from_account = accounts[item.from_account_id]
+ item.target_status.account = accounts[item.target_status.account_id] if item.target_status
end
end
diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index 716b82243..86eecdfe5 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -33,7 +33,7 @@ class PreviewCard < ApplicationRecord
has_and_belongs_to_many :statuses
- has_attached_file :image, styles: { original: '400x400>' }, convert_options: { all: '-quality 80 -strip' }
+ has_attached_file :image, styles: { original: { geometry: '400x400>', file_geometry_parser: FastGeometryParser } }, convert_options: { all: '-quality 80 -strip' }
include Attachmentable
include Remotable
@@ -58,10 +58,11 @@ class PreviewCard < ApplicationRecord
return if file.nil?
- geo = Paperclip::Geometry.from_file(file)
- self.width = geo.width.to_i
- self.height = geo.height.to_i
- rescue Paperclip::Errors::NotIdentifiedByImageMagickError
- nil
+ width, height = FastImage.size(file.path)
+
+ return nil if width.nil?
+
+ self.width = width
+ self.height = height
end
end
diff --git a/app/models/report.rb b/app/models/report.rb
index f55fb6d3e..dd123fc15 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -24,6 +24,10 @@ class Report < ApplicationRecord
validates :comment, length: { maximum: 1000 }
+ def object_type
+ :flag
+ end
+
def statuses
Status.where(id: status_ids).includes(:account, :media_attachments, :mentions)
end
diff --git a/app/models/site_upload.rb b/app/models/site_upload.rb
index 8ffdc8313..641128adf 100644
--- a/app/models/site_upload.rb
+++ b/app/models/site_upload.rb
@@ -34,8 +34,8 @@ class SiteUpload < ApplicationRecord
return if tempfile.nil?
- geometry = Paperclip::Geometry.from_file(tempfile)
- self.meta = { width: geometry.width.to_i, height: geometry.height.to_i }
+ width, height = FastImage.size(tempfile.path)
+ self.meta = { width: width, height: height }
end
def clear_cache
diff --git a/app/models/status.rb b/app/models/status.rb
index 26ff40bf7..60fa7a22e 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -31,6 +31,8 @@ class Status < ApplicationRecord
include Cacheable
include StatusThreadingConcern
+ update_index('statuses#status', :proper) if Chewy.enabled?
+
enum visibility: [:public, :unlisted, :private, :direct], _suffix: :visibility
belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
@@ -55,7 +57,7 @@ class Status < ApplicationRecord
has_one :stream_entry, as: :activity, inverse_of: :status
validates :uri, uniqueness: true, presence: true, unless: :local?
- validates :text, presence: true, unless: :reblog?
+ validates :text, presence: true, unless: -> { with_media? || reblog? }
validates_with StatusLengthValidator
validates :reblog, uniqueness: { scope: :account }, if: :reblog?
@@ -74,10 +76,28 @@ class Status < ApplicationRecord
scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) }
scope :not_domain_blocked_by_account, ->(account) { account.excluded_from_timeline_domains.blank? ? left_outer_joins(:account) : left_outer_joins(:account).where('accounts.domain IS NULL OR accounts.domain NOT IN (?)', account.excluded_from_timeline_domains) }
- cache_associated :account, :application, :media_attachments, :tags, :stream_entry, mentions: :account, reblog: [:account, :application, :stream_entry, :tags, :media_attachments, mentions: :account], thread: :account
+ cache_associated :account, :application, :media_attachments, :conversation, :tags, :stream_entry, mentions: :account, reblog: [:account, :application, :stream_entry, :tags, :media_attachments, :conversation, mentions: :account], thread: :account
delegate :domain, to: :account, prefix: true
+ REAL_TIME_WINDOW = 6.hours
+
+ def searchable_by(preloaded = nil)
+ ids = [account_id]
+
+ if preloaded.nil?
+ ids += mentions.pluck(:account_id)
+ ids += favourites.pluck(:account_id)
+ ids += reblogs.pluck(:account_id)
+ else
+ ids += preloaded.mentions[id] || []
+ ids += preloaded.favourites[id] || []
+ ids += preloaded.reblogs[id] || []
+ end
+
+ ids.uniq
+ end
+
def reply?
!in_reply_to_id.nil? || attributes['reply']
end
@@ -90,6 +110,10 @@ class Status < ApplicationRecord
!reblog_of_id.nil?
end
+ def within_realtime_window?
+ created_at >= REAL_TIME_WINDOW.ago
+ end
+
def verb
if destroyed?
:delete
@@ -126,8 +150,12 @@ class Status < ApplicationRecord
private_visibility? || direct_visibility?
end
+ def with_media?
+ media_attachments.any?
+ end
+
def non_sensitive_with_media?
- !sensitive? && media_attachments.any?
+ !sensitive? && with_media?
end
def emojis
diff --git a/app/models/tag.rb b/app/models/tag.rb
index dc2c8d129..9fa9405d7 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -12,7 +12,7 @@
class Tag < ApplicationRecord
has_and_belongs_to_many :statuses
- HASHTAG_NAME_RE = '[[:word:]_]*[[:alpha:]_][[:word:]_]*'
+ HASHTAG_NAME_RE = '[[:word:]_]*[[:alpha:]_·][[:word:]_]*'
HASHTAG_RE = /(?:^|[^\/\)\w])#(#{HASHTAG_NAME_RE})/i
validates :name, presence: true, uniqueness: true, format: { with: /\A#{HASHTAG_NAME_RE}\z/i }
diff --git a/app/models/user.rb b/app/models/user.rb
index 40c298b1a..2d5f145fa 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -34,15 +34,17 @@
# disabled :boolean default(FALSE), not null
# moderator :boolean default(FALSE), not null
# invite_id :integer
+# remember_token :string
#
class User < ApplicationRecord
include Settings::Extend
+ include Omniauthable
ACTIVE_DURATION = 14.days
devise :two_factor_authenticatable,
- otp_secret_encryption_key: ENV['OTP_SECRET']
+ otp_secret_encryption_key: Rails.configuration.x.otp_secret
devise :two_factor_backupable,
otp_number_of_backup_codes: 10
@@ -50,11 +52,16 @@ class User < ApplicationRecord
devise :registerable, :recoverable, :rememberable, :trackable, :validatable,
:confirmable
+ devise :pam_authenticatable if ENV['PAM_ENABLED'] == 'true'
+
+ devise :omniauthable
+
belongs_to :account, inverse_of: :user
belongs_to :invite, counter_cache: :uses, optional: true
accepts_nested_attributes_for :account
has_many :applications, class_name: 'Doorkeeper::Application', as: :owner
+ has_many :backups, inverse_of: :user
validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale?
validates_with BlacklistedEmailValidator, if: :email_changed?
@@ -79,11 +86,44 @@ class User < ApplicationRecord
has_many :session_activations, dependent: :destroy
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
- :reduce_motion, :system_font_ui, :noindex, :theme,
+ :reduce_motion, :system_font_ui, :noindex, :theme, :display_sensitive_media,
to: :settings, prefix: :setting, allow_nil: false
attr_accessor :invite_code
+ def pam_conflict(_)
+ # block pam login tries on traditional account
+ nil
+ end
+
+ def pam_conflict?
+ return false unless Devise.pam_authentication
+ encrypted_password.present? && pam_managed_user?
+ end
+
+ def pam_get_name
+ return account.username if account.present?
+ super
+ end
+
+ def pam_setup(_attributes)
+ acc = Account.new(username: pam_get_name)
+ acc.save!(validate: false)
+
+ self.email = "#{acc.username}@#{find_pam_suffix}" if email.nil? && find_pam_suffix
+ self.confirmed_at = Time.now.utc
+ self.admin = false
+ self.account = acc
+
+ acc.destroy! unless save
+ end
+
+ def ldap_setup(_attributes)
+ self.confirmed_at = Time.now.utc
+ self.admin = false
+ save!
+ end
+
def confirmed?
confirmed_at.present?
end
@@ -213,6 +253,56 @@ class User < ApplicationRecord
@invite_code = code
end
+ def password_required?
+ return false if Devise.pam_authentication || Devise.ldap_authentication
+ super
+ end
+
+ def send_reset_password_instructions
+ return false if encrypted_password.blank? && (Devise.pam_authentication || Devise.ldap_authentication)
+ super
+ end
+
+ def reset_password!(new_password, new_password_confirmation)
+ return false if encrypted_password.blank? && (Devise.pam_authentication || Devise.ldap_authentication)
+ super
+ end
+
+ def self.pam_get_user(attributes = {})
+ return nil unless attributes[:email]
+ resource =
+ if Devise.check_at_sign && !attributes[:email].index('@')
+ joins(:account).find_by(accounts: { username: attributes[:email] })
+ else
+ find_by(email: attributes[:email])
+ end
+
+ if resource.blank?
+ resource = new(email: attributes[:email])
+ if Devise.check_at_sign && !resource[:email].index('@')
+ resource[:email] = Rpam2.getenv(resource.find_pam_service, attributes[:email], attributes[:password], 'email', false)
+ resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" unless resource[:email]
+ end
+ end
+ resource
+ end
+
+ def self.ldap_get_user(attributes = {})
+ resource = joins(:account).find_by(accounts: { username: attributes[Devise.ldap_uid.to_sym].first })
+
+ if resource.blank?
+ resource = new(email: attributes[:mail].first, account_attributes: { username: attributes[Devise.ldap_uid.to_sym].first })
+ resource.ldap_setup(attributes)
+ end
+
+ resource
+ end
+
+ def self.authenticate_with_pam(attributes = {})
+ return nil unless Devise.pam_authentication
+ super
+ end
+
protected
def send_devise_notification(notification, *args)
diff --git a/app/models/web/setting.rb b/app/models/web/setting.rb
index 12b9d1226..0a5129d17 100644
--- a/app/models/web/setting.rb
+++ b/app/models/web/setting.rb
@@ -7,7 +7,7 @@
# data :json
# created_at :datetime not null
# updated_at :datetime not null
-# user_id :integer
+# user_id :integer not null
#
class Web::Setting < ApplicationRecord
diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb
index 3e617001f..d1de5e81a 100644
--- a/app/policies/application_policy.rb
+++ b/app/policies/application_policy.rb
@@ -15,4 +15,8 @@ class ApplicationPolicy
def current_user
current_account&.user
end
+
+ def user_signed_in?
+ !current_user.nil?
+ end
end
diff --git a/app/policies/backup_policy.rb b/app/policies/backup_policy.rb
new file mode 100644
index 000000000..0ef89a8d0
--- /dev/null
+++ b/app/policies/backup_policy.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class BackupPolicy < ApplicationPolicy
+ MIN_AGE = 1.week
+
+ def create?
+ user_signed_in? && current_user.backups.where('created_at >= ?', MIN_AGE.ago).count.zero?
+ end
+end
diff --git a/app/presenters/account_relationships_presenter.rb b/app/presenters/account_relationships_presenter.rb
index bf1ba3716..b1e99b31b 100644
--- a/app/presenters/account_relationships_presenter.rb
+++ b/app/presenters/account_relationships_presenter.rb
@@ -5,11 +5,67 @@ class AccountRelationshipsPresenter
:muting, :requested, :domain_blocking
def initialize(account_ids, current_account_id, **options)
- @following = Account.following_map(account_ids, current_account_id).merge(options[:following_map] || {})
- @followed_by = Account.followed_by_map(account_ids, current_account_id).merge(options[:followed_by_map] || {})
- @blocking = Account.blocking_map(account_ids, current_account_id).merge(options[:blocking_map] || {})
- @muting = Account.muting_map(account_ids, current_account_id).merge(options[:muting_map] || {})
- @requested = Account.requested_map(account_ids, current_account_id).merge(options[:requested_map] || {})
- @domain_blocking = Account.domain_blocking_map(account_ids, current_account_id).merge(options[:domain_blocking_map] || {})
+ @account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a }
+ @current_account_id = current_account_id
+
+ @following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
+ @followed_by = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
+ @blocking = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
+ @muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id))
+ @requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id))
+ @domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
+
+ cache_uncached!
+
+ @following.merge!(options[:following_map] || {})
+ @followed_by.merge!(options[:followed_by_map] || {})
+ @blocking.merge!(options[:blocking_map] || {})
+ @muting.merge!(options[:muting_map] || {})
+ @requested.merge!(options[:requested_map] || {})
+ @domain_blocking.merge!(options[:domain_blocking_map] || {})
+ end
+
+ private
+
+ def cached
+ return @cached if defined?(@cached)
+
+ @cached = {
+ following: {},
+ followed_by: {},
+ blocking: {},
+ muting: {},
+ requested: {},
+ domain_blocking: {},
+ }
+
+ @uncached_account_ids = []
+
+ @account_ids.each do |account_id|
+ maps_for_account = Rails.cache.read("relationship:#{@current_account_id}:#{account_id}")
+
+ if maps_for_account.is_a?(Hash)
+ @cached.deep_merge!(maps_for_account)
+ else
+ @uncached_account_ids << account_id
+ end
+ end
+
+ @cached
+ end
+
+ def cache_uncached!
+ @uncached_account_ids.each do |account_id|
+ maps_for_account = {
+ following: { account_id => following[account_id] },
+ followed_by: { account_id => followed_by[account_id] },
+ blocking: { account_id => blocking[account_id] },
+ muting: { account_id => muting[account_id] },
+ requested: { account_id => requested[account_id] },
+ domain_blocking: { account_id => domain_blocking[account_id] },
+ }
+
+ Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day)
+ end
end
end
diff --git a/app/presenters/instance_presenter.rb b/app/presenters/instance_presenter.rb
index 4c1124d59..e4972c962 100644
--- a/app/presenters/instance_presenter.rb
+++ b/app/presenters/instance_presenter.rb
@@ -39,4 +39,8 @@ class InstancePresenter
def thumbnail
@thumbnail ||= Rails.cache.fetch('site_uploads/thumbnail') { SiteUpload.find_by(var: 'thumbnail') }
end
+
+ def hero
+ @hero ||= Rails.cache.fetch('site_uploads/hero') { SiteUpload.find_by(var: 'hero') }
+ end
end
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index 622bdde0c..afcd37771 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -4,7 +4,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
include RoutingHelper
attributes :id, :type, :following, :followers,
- :inbox, :outbox,
+ :inbox, :outbox, :featured,
:preferred_username, :name, :summary,
:url, :manually_approves_followers
@@ -53,6 +53,10 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
account_outbox_url(object)
end
+ def featured
+ account_collection_url(object, :featured)
+ end
+
def endpoints
object
end
diff --git a/app/serializers/activitypub/add_serializer.rb b/app/serializers/activitypub/add_serializer.rb
new file mode 100644
index 000000000..c0906e8d0
--- /dev/null
+++ b/app/serializers/activitypub/add_serializer.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class ActivityPub::AddSerializer < ActiveModel::Serializer
+ include RoutingHelper
+
+ attributes :type, :actor, :target
+ attribute :proper_object, key: :object
+
+ def type
+ 'Add'
+ end
+
+ def actor
+ ActivityPub::TagManager.instance.uri_for(object.account)
+ end
+
+ def proper_object
+ ActivityPub::TagManager.instance.uri_for(object)
+ end
+
+ def target
+ account_collection_url(object.account, :featured)
+ end
+end
diff --git a/app/serializers/activitypub/collection_serializer.rb b/app/serializers/activitypub/collection_serializer.rb
index 9832133fc..1ae492945 100644
--- a/app/serializers/activitypub/collection_serializer.rb
+++ b/app/serializers/activitypub/collection_serializer.rb
@@ -2,7 +2,7 @@
class ActivityPub::CollectionSerializer < ActiveModel::Serializer
def self.serializer_for(model, options)
- return ActivityPub::ActivitySerializer if model.class.name == 'Status'
+ return ActivityPub::NoteSerializer if model.class.name == 'Status'
return ActivityPub::CollectionSerializer if model.class.name == 'ActivityPub::CollectionPresenter'
super
end
@@ -13,8 +13,8 @@ class ActivityPub::CollectionSerializer < ActiveModel::Serializer
attribute :part_of, if: -> { object.part_of.present? }
has_one :first, if: -> { object.first.present? }
- has_many :items, key: :items, if: -> { (object.items.present? || page?) && !ordered? }
- has_many :items, key: :ordered_items, if: -> { (object.items.present? || page?) && ordered? }
+ has_many :items, key: :items, if: -> { (!object.items.nil? || page?) && !ordered? }
+ has_many :items, key: :ordered_items, if: -> { (!object.items.nil? || page?) && ordered? }
def type
if page?
diff --git a/app/serializers/activitypub/flag_serializer.rb b/app/serializers/activitypub/flag_serializer.rb
new file mode 100644
index 000000000..53e8f726d
--- /dev/null
+++ b/app/serializers/activitypub/flag_serializer.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class ActivityPub::FlagSerializer < ActiveModel::Serializer
+ attributes :id, :type, :actor, :content
+ attribute :virtual_object, key: :object
+
+ def id
+ # This is nil for now
+ ActivityPub::TagManager.instance.uri_for(object)
+ end
+
+ def type
+ 'Flag'
+ end
+
+ def actor
+ ActivityPub::TagManager.instance.uri_for(instance_options[:account] || object.account)
+ end
+
+ def virtual_object
+ [ActivityPub::TagManager.instance.uri_for(object.target_account)] + object.statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) }
+ end
+
+ def content
+ object.comment
+ end
+end
diff --git a/app/serializers/activitypub/image_serializer.rb b/app/serializers/activitypub/image_serializer.rb
index a015c6b1b..3c08f77e8 100644
--- a/app/serializers/activitypub/image_serializer.rb
+++ b/app/serializers/activitypub/image_serializer.rb
@@ -4,6 +4,7 @@ class ActivityPub::ImageSerializer < ActiveModel::Serializer
include RoutingHelper
attributes :type, :media_type, :url
+ attribute :focal_point, if: :focal_point?
def type
'Image'
@@ -16,4 +17,12 @@ class ActivityPub::ImageSerializer < ActiveModel::Serializer
def media_type
object.content_type
end
+
+ def focal_point?
+ object.respond_to?(:meta) && object.meta.is_a?(Hash) && object.meta['focus'].is_a?(Hash)
+ end
+
+ def focal_point
+ [object.meta['focus']['x'], object.meta['focus']['y']]
+ end
end
diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb
index 24c39f3c9..ddafb540d 100644
--- a/app/serializers/activitypub/note_serializer.rb
+++ b/app/serializers/activitypub/note_serializer.rb
@@ -57,7 +57,7 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer
end
def virtual_tags
- object.mentions + object.tags + object.emojis
+ object.mentions.to_a.sort_by(&:id) + object.tags + object.emojis
end
def atom_uri
@@ -90,6 +90,7 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer
include RoutingHelper
attributes :type, :media_type, :url, :name
+ attribute :focal_point, if: :focal_point?
def type
'Document'
@@ -106,6 +107,14 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer
def url
object.local? ? full_asset_url(object.file.url(:original, false)) : object.remote_url
end
+
+ def focal_point?
+ object.file.meta.is_a?(Hash) && object.file.meta['focus'].is_a?(Hash)
+ end
+
+ def focal_point
+ [object.file.meta['focus']['x'], object.file.meta['focus']['y']]
+ end
end
class MentionSerializer < ActiveModel::Serializer
diff --git a/app/serializers/activitypub/outbox_serializer.rb b/app/serializers/activitypub/outbox_serializer.rb
new file mode 100644
index 000000000..48fbad0fd
--- /dev/null
+++ b/app/serializers/activitypub/outbox_serializer.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class ActivityPub::OutboxSerializer < ActivityPub::CollectionSerializer
+ def self.serializer_for(model, options)
+ return ActivityPub::ActivitySerializer if model.is_a?(Status)
+ super
+ end
+end
diff --git a/app/serializers/activitypub/remove_serializer.rb b/app/serializers/activitypub/remove_serializer.rb
new file mode 100644
index 000000000..c2a5ae1b3
--- /dev/null
+++ b/app/serializers/activitypub/remove_serializer.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class ActivityPub::RemoveSerializer < ActiveModel::Serializer
+ include RoutingHelper
+
+ attributes :type, :actor, :target
+ attribute :proper_object, key: :object
+
+ def type
+ 'Remove'
+ end
+
+ def actor
+ ActivityPub::TagManager.instance.uri_for(object.account)
+ end
+
+ def proper_object
+ ActivityPub::TagManager.instance.uri_for(object)
+ end
+
+ def target
+ account_collection_url(object.account, :featured)
+ end
+end
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index 4fa1981ed..216cf5446 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -17,15 +17,17 @@ class InitialStateSerializer < ActiveModel::Serializer
locale: I18n.locale,
domain: Rails.configuration.x.local_domain,
admin: object.admin&.id&.to_s,
+ search_enabled: Chewy.enabled?,
}
if object.current_account
- store[:me] = object.current_account.id.to_s
- store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal
- store[:boost_modal] = object.current_account.user.setting_boost_modal
- store[:delete_modal] = object.current_account.user.setting_delete_modal
- store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
- store[:reduce_motion] = object.current_account.user.setting_reduce_motion
+ store[:me] = object.current_account.id.to_s
+ store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal
+ store[:boost_modal] = object.current_account.user.setting_boost_modal
+ store[:delete_modal] = object.current_account.user.setting_delete_modal
+ store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
+ store[:display_sensitive_media] = object.current_account.user.setting_display_sensitive_media
+ store[:reduce_motion] = object.current_account.user.setting_reduce_motion
end
store
diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb
index ae1dbe6b5..e3e64ea87 100644
--- a/app/serializers/rest/instance_serializer.rb
+++ b/app/serializers/rest/instance_serializer.rb
@@ -4,7 +4,12 @@ class REST::InstanceSerializer < ActiveModel::Serializer
include RoutingHelper
attributes :uri, :title, :description, :email,
- :version, :urls, :stats, :thumbnail
+ :version, :urls, :stats, :thumbnail,
+ :languages
+
+ has_one :contact_account, serializer: REST::AccountSerializer
+
+ delegate :contact_account, to: :instance_presenter
def uri
Rails.configuration.x.local_domain
@@ -42,6 +47,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
{ streaming_api: Rails.configuration.x.streaming_api_base_url }
end
+ def languages
+ [I18n.default_locale]
+ end
+
private
def instance_presenter
diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb
index e6270f902..fe3dc9bfc 100644
--- a/app/serializers/rest/status_serializer.rb
+++ b/app/serializers/rest/status_serializer.rb
@@ -15,7 +15,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
belongs_to :account, serializer: REST::AccountSerializer
has_many :media_attachments, serializer: REST::MediaAttachmentSerializer
- has_many :mentions
+ has_many :ordered_mentions, key: :mentions
has_many :tags
has_many :emojis, serializer: REST::CustomEmojiSerializer
@@ -86,6 +86,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
%w(public unlisted).include?(object.visibility)
end
+ def ordered_mentions
+ object.mentions.to_a.sort_by(&:id)
+ end
+
class ApplicationSerializer < ActiveModel::Serializer
attributes :name, :website
end
diff --git a/app/services/activitypub/fetch_featured_collection_service.rb b/app/services/activitypub/fetch_featured_collection_service.rb
new file mode 100644
index 000000000..40714e980
--- /dev/null
+++ b/app/services/activitypub/fetch_featured_collection_service.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+class ActivityPub::FetchFeaturedCollectionService < BaseService
+ include JsonLdHelper
+
+ def call(account)
+ @account = account
+ @json = fetch_resource(@account.featured_collection_url, true)
+
+ return unless supported_context?
+ return if @account.suspended? || @account.local?
+
+ case @json['type']
+ when 'Collection', 'CollectionPage'
+ process_items @json['items']
+ when 'OrderedCollection', 'OrderedCollectionPage'
+ process_items @json['orderedItems']
+ end
+ end
+
+ private
+
+ def process_items(items)
+ status_ids = items.map { |item| value_or_id(item) }
+ .reject { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }
+ .map { |uri| ActivityPub::FetchRemoteStatusService.new.call(uri) }
+ .compact
+ .select { |status| status.account_id == @account.id }
+ .map(&:id)
+
+ to_remove = []
+ to_add = status_ids
+
+ StatusPin.where(account: @account).pluck(:status_id).each do |status_id|
+ if status_ids.include?(status_id)
+ to_add.delete(status_id)
+ else
+ to_remove << status_id
+ end
+ end
+
+ StatusPin.where(account: @account, status_id: to_remove).delete_all unless to_remove.empty?
+
+ to_add.each do |status_id|
+ StatusPin.create!(account: @account, status_id: status_id)
+ end
+ end
+
+ def supported_context?
+ super(@json)
+ end
+end
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index f43edafe7..7d8dc1369 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -16,7 +16,7 @@ class ActivityPub::ProcessAccountService < BaseService
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
- @account = Account.find_by(uri: @uri)
+ @account = Account.find_remote(@username, @domain)
@old_public_key = @account&.public_key
@old_protocol = @account&.protocol
@@ -27,6 +27,7 @@ class ActivityPub::ProcessAccountService < BaseService
after_protocol_change! if protocol_changed?
after_key_change! if key_changed?
+ check_featured_collection! if @account.featured_collection_url.present?
@account
rescue Oj::ParseError
@@ -57,14 +58,15 @@ class ActivityPub::ProcessAccountService < BaseService
end
def set_immediate_attributes!
- @account.inbox_url = @json['inbox'] || ''
- @account.outbox_url = @json['outbox'] || ''
- @account.shared_inbox_url = (@json['endpoints'].is_a?(Hash) ? @json['endpoints']['sharedInbox'] : @json['sharedInbox']) || ''
- @account.followers_url = @json['followers'] || ''
- @account.url = url || @uri
- @account.display_name = @json['name'] || ''
- @account.note = @json['summary'] || ''
- @account.locked = @json['manuallyApprovesFollowers'] || false
+ @account.inbox_url = @json['inbox'] || ''
+ @account.outbox_url = @json['outbox'] || ''
+ @account.shared_inbox_url = (@json['endpoints'].is_a?(Hash) ? @json['endpoints']['sharedInbox'] : @json['sharedInbox']) || ''
+ @account.followers_url = @json['followers'] || ''
+ @account.featured_collection_url = @json['featured'] || ''
+ @account.url = url || @uri
+ @account.display_name = @json['name'] || ''
+ @account.note = @json['summary'] || ''
+ @account.locked = @json['manuallyApprovesFollowers'] || false
end
def set_fetchable_attributes!
@@ -85,6 +87,10 @@ class ActivityPub::ProcessAccountService < BaseService
RefollowWorker.perform_async(@account.id)
end
+ def check_featured_collection!
+ ActivityPub::SynchronizeFeaturedCollectionWorker.perform_async(@account.id)
+ end
+
def image_url(key)
value = first_of_value(@json[key])
diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb
new file mode 100644
index 000000000..8492c1117
--- /dev/null
+++ b/app/services/backup_service.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require 'rubygems/package'
+
+class BackupService < BaseService
+ attr_reader :account, :backup, :collection
+
+ def call(backup)
+ @backup = backup
+ @account = backup.user.account
+
+ build_json!
+ build_archive!
+ end
+
+ private
+
+ def build_json!
+ @collection = serialize(collection_presenter, ActivityPub::CollectionSerializer)
+
+ account.statuses.with_includes.find_in_batches do |statuses|
+ statuses.each do |status|
+ item = serialize(status, ActivityPub::ActivitySerializer)
+ item.delete(:'@context')
+
+ unless item[:type] == 'Announce' || item[:object][:attachment].blank?
+ item[:object][:attachment].each do |attachment|
+ attachment[:url] = Addressable::URI.parse(attachment[:url]).path.gsub(/\A\/system\//, '')
+ end
+ end
+
+ @collection[:orderedItems] << item
+ end
+
+ GC.start
+ end
+ end
+
+ def build_archive!
+ tmp_file = Tempfile.new(%w(archive .tar.gz))
+
+ File.open(tmp_file, 'wb') do |file|
+ Zlib::GzipWriter.wrap(file) do |gz|
+ Gem::Package::TarWriter.new(gz) do |tar|
+ dump_media_attachments!(tar)
+ dump_outbox!(tar)
+ dump_actor!(tar)
+ end
+ end
+ end
+
+ archive_filename = ['archive', Time.now.utc.strftime('%Y%m%d%H%M%S'), SecureRandom.hex(16)].join('-') + '.tar.gz'
+
+ @backup.dump = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file, filename: archive_filename)
+ @backup.processed = true
+ @backup.save!
+ ensure
+ tmp_file.close
+ tmp_file.unlink
+ end
+
+ def dump_media_attachments!(tar)
+ MediaAttachment.attached.where(account: account).find_in_batches do |media_attachments|
+ media_attachments.each do |m|
+ download_to_tar(tar, m.file, m.file.path)
+ end
+
+ GC.start
+ end
+ end
+
+ def dump_outbox!(tar)
+ json = Oj.dump(collection)
+
+ tar.add_file_simple('outbox.json', 0o444, json.bytesize) do |io|
+ io.write(json)
+ end
+ end
+
+ def dump_actor!(tar)
+ actor = serialize(account, ActivityPub::ActorSerializer)
+
+ actor[:icon][:url] = 'avatar' + File.extname(actor[:icon][:url]) if actor[:icon]
+ actor[:image][:url] = 'header' + File.extname(actor[:image][:url]) if actor[:image]
+
+ download_to_tar(tar, account.avatar, 'avatar' + File.extname(account.avatar.path)) if account.avatar.exists?
+ download_to_tar(tar, account.header, 'header' + File.extname(account.header.path)) if account.header.exists?
+
+ json = Oj.dump(actor)
+
+ tar.add_file_simple('actor.json', 0o444, json.bytesize) do |io|
+ io.write(json)
+ end
+
+ tar.add_file_simple('key.pem', 0o444, account.private_key.bytesize) do |io|
+ io.write(account.private_key)
+ end
+ end
+
+ def collection_presenter
+ ActivityPub::CollectionPresenter.new(
+ id: account_outbox_url(account),
+ type: :ordered,
+ size: account.statuses_count,
+ items: []
+ )
+ end
+
+ def serialize(object, serializer)
+ ActiveModelSerializers::SerializableResource.new(
+ object,
+ serializer: serializer,
+ adapter: ActivityPub::Adapter
+ ).as_json
+ end
+
+ CHUNK_SIZE = 1.megabyte
+
+ def download_to_tar(tar, attachment, filename)
+ adapter = Paperclip.io_adapters.for(attachment)
+
+ tar.add_file_simple(filename, 0o444, adapter.size) do |io|
+ while (buffer = adapter.read(CHUNK_SIZE))
+ io.write(buffer)
+ end
+ end
+ end
+end
diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb
index eefdc0dbf..d082de40b 100644
--- a/app/services/block_domain_service.rb
+++ b/app/services/block_domain_service.rb
@@ -5,13 +5,14 @@ class BlockDomainService < BaseService
def call(domain_block)
@domain_block = domain_block
- process_domain_block
+ process_domain_block!
end
private
- def process_domain_block
+ def process_domain_block!
clear_media! if domain_block.reject_media?
+
if domain_block.silence?
silence_accounts!
elsif domain_block.suspend?
@@ -19,14 +20,26 @@ class BlockDomainService < BaseService
end
end
+ def invalidate_association_caches!
+ # Normally, associated models of a status are immutable (except for accounts)
+ # so they are aggressively cached. After updating the media attachments to no
+ # longer point to a local file, we need to clear the cache to make those
+ # changes appear in the API and UI
+ @affected_status_ids.each { |id| Rails.cache.delete_matched("statuses/#{id}-*") }
+ end
+
def silence_accounts!
blocked_domain_accounts.in_batches.update_all(silenced: true)
end
def clear_media!
- clear_account_images
- clear_account_attachments
- clear_emojos
+ @affected_status_ids = []
+
+ clear_account_images!
+ clear_account_attachments!
+ clear_emojos!
+
+ invalidate_association_caches!
end
def suspend_accounts!
@@ -36,23 +49,25 @@ class BlockDomainService < BaseService
end
end
- def clear_account_images
+ def clear_account_images!
blocked_domain_accounts.find_each do |account|
- account.avatar.destroy
- account.header.destroy
+ account.avatar.destroy if account.avatar.exists?
+ account.header.destroy if account.header.exists?
account.save
end
end
- def clear_account_attachments
+ def clear_account_attachments!
media_from_blocked_domain.find_each do |attachment|
- attachment.file.destroy
+ @affected_status_ids << attachment.status_id if attachment.status_id.present?
+
+ attachment.file.destroy if attachment.file.exists?
attachment.type = :unknown
attachment.save
end
end
- def clear_emojos
+ def clear_emojos!
emojis_from_blocked_domains.destroy_all
end
diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index d0472a1d7..8f252e64c 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -91,17 +91,19 @@ class FetchLinkCardService < BaseService
case @card.type
when 'link'
- @card.image = URI.parse(embed.thumbnail_url) if embed.respond_to?(:thumbnail_url)
+ @card.image_remote_url = embed.thumbnail_url if embed.respond_to?(:thumbnail_url)
when 'photo'
return false unless embed.respond_to?(:url)
- @card.embed_url = embed.url
- @card.image = URI.parse(embed.url)
- @card.width = embed.width.presence || 0
- @card.height = embed.height.presence || 0
+
+ @card.embed_url = embed.url
+ @card.image_remote_url = embed.url
+ @card.width = embed.width.presence || 0
+ @card.height = embed.height.presence || 0
when 'video'
- @card.width = embed.width.presence || 0
- @card.height = embed.height.presence || 0
- @card.html = Formatter.instance.sanitize(embed.html, Sanitize::Config::MASTODON_OEMBED)
+ @card.width = embed.width.presence || 0
+ @card.height = embed.height.presence || 0
+ @card.html = Formatter.instance.sanitize(embed.html, Sanitize::Config::MASTODON_OEMBED)
+ @card.image_remote_url = embed.thumbnail_url if embed.respond_to?(:thumbnail_url)
when 'rich'
# Most providers rely on