From cb7bd8ee0333b608b168c20f32acf3337412dac0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 26 Nov 2020 14:42:12 +0100 Subject: New Crowdin updates (#15137) * New translations en.yml (Kabyle) [ci skip] * New translations en.json (Kabyle) [ci skip] * New translations en.yml (Sanskrit) [ci skip] * New translations en.json (Sanskrit) [ci skip] * New translations en.yml (Sardinian) [ci skip] * New translations en.json (Sardinian) [ci skip] * New translations simple_form.en.yml (Corsican) [ci skip] * New translations simple_form.en.yml (Sorani (Kurdish)) [ci skip] * New translations en.yml (Welsh) [ci skip] * New translations simple_form.en.yml (Swedish) [ci skip] * New translations en.json (Tamil) [ci skip] * New translations simple_form.en.yml (Persian) [ci skip] * New translations en.yml (Persian) [ci skip] * New translations en.json (Persian) [ci skip] * New translations simple_form.en.yml (Indonesian) [ci skip] * New translations en.yml (Indonesian) [ci skip] * New translations simple_form.en.yml (Portuguese, Brazilian) [ci skip] * New translations en.yml (Portuguese, Brazilian) [ci skip] * New translations simple_form.en.yml (Galician) [ci skip] * New translations simple_form.en.yml (Tamil) [ci skip] * New translations en.yml (Urdu (Pakistan)) [ci skip] * New translations en.json (Urdu (Pakistan)) [ci skip] * New translations simple_form.en.yml (Chinese Traditional) [ci skip] * New translations en.yml (Chinese Traditional) [ci skip] * New translations en.json (Chinese Traditional) [ci skip] * New translations simple_form.en.yml (Chinese Simplified) [ci skip] * New translations en.yml (Chinese Simplified) [ci skip] * New translations simple_form.en.yml (Ukrainian) [ci skip] * New translations en.yml (Ukrainian) [ci skip] * New translations en.json (Ukrainian) [ci skip] * New translations en.yml (Tamil) [ci skip] * New translations en.yml (Telugu) [ci skip] * New translations en.yml (Estonian) [ci skip] * New translations en.json (Telugu) [ci skip] * New translations en.yml (Malay) [ci skip] * New translations en.json (Malay) [ci skip] * New translations en.yml (Hindi) [ci skip] * New translations en.json (Hindi) [ci skip] * New translations en.yml (Latvian) [ci skip] * New translations en.json (Latvian) [ci skip] * New translations simple_form.en.yml (Estonian) [ci skip] * New translations en.json (Estonian) [ci skip] * New translations en.json (Bengali) [ci skip] * New translations simple_form.en.yml (Kazakh) [ci skip] * New translations en.yml (Kazakh) [ci skip] * New translations en.json (Kazakh) [ci skip] * New translations simple_form.en.yml (Norwegian Nynorsk) [ci skip] * New translations en.yml (Norwegian Nynorsk) [ci skip] * New translations en.json (Norwegian Nynorsk) [ci skip] * New translations en.json (Thai) [ci skip] * New translations en.yml (Marathi) [ci skip] * New translations en.json (Marathi) [ci skip] * New translations simple_form.en.yml (Bengali) [ci skip] * New translations en.yml (Bengali) [ci skip] * New translations en.json (Chinese Traditional) [ci skip] * New translations en.json (Chinese Traditional) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.json (Spanish, Mexico) [ci skip] * New translations doorkeeper.en.yml (Spanish, Mexico) [ci skip] * New translations simple_form.en.yml (Spanish, Mexico) [ci skip] * New translations en.yml (Spanish, Mexico) [ci skip] * New translations simple_form.en.yml (Spanish) [ci skip] * New translations en.json (Spanish) [ci skip] * New translations en.yml (Spanish) [ci skip] * New translations en.yml (French) [ci skip] * New translations en.json (French) [ci skip] * New translations en.yml (French) [ci skip] * New translations en.yml (Esperanto) [ci skip] * New translations en.yml (Japanese) [ci skip] * New translations devise.en.yml (Japanese) [ci skip] * New translations en.yml (Japanese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Japanese) [ci skip] * New translations en.yml (French) [ci skip] * New translations en.yml (Occitan) [ci skip] * New translations simple_form.en.yml (Occitan) [ci skip] * New translations en.yml (Occitan) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Standard Moroccan Tamazight) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Standard Moroccan Tamazight) [ci skip] * New translations en.json (Standard Moroccan Tamazight) [ci skip] * New translations en.yml (Standard Moroccan Tamazight) [ci skip] * New translations doorkeeper.en.yml (Standard Moroccan Tamazight) [ci skip] * New translations en.json (Standard Moroccan Tamazight) [ci skip] * New translations en.json (Standard Moroccan Tamazight) [ci skip] * New translations en.json (Kannada) [ci skip] * New translations en.yml (French) [ci skip] * New translations en.json (Esperanto) [ci skip] * New translations en.yml (Esperanto) [ci skip] * New translations simple_form.en.yml (Esperanto) [ci skip] * New translations doorkeeper.en.yml (Esperanto) [ci skip] * New translations doorkeeper.en.yml (Esperanto) [ci skip] * New translations en.json (Esperanto) [ci skip] * New translations en.json (Esperanto) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations doorkeeper.en.yml (Spanish) [ci skip] * New translations en.json (Spanish, Mexico) [ci skip] * New translations doorkeeper.en.yml (Spanish, Mexico) [ci skip] * New translations simple_form.en.yml (Spanish, Mexico) [ci skip] * New translations en.yml (Spanish, Mexico) [ci skip] * New translations simple_form.en.yml (Spanish) [ci skip] * New translations en.json (Spanish) [ci skip] * New translations en.yml (Spanish) [ci skip] * New translations en.yml (Portuguese) [ci skip] * New translations en.yml (Hungarian) [ci skip] * New translations en.yml (Turkish) [ci skip] * New translations en.yml (Swedish) [ci skip] * New translations en.yml (Icelandic) [ci skip] * New translations en.yml (Spanish, Argentina) [ci skip] * New translations en.yml (Spanish, Argentina) [ci skip] * New translations en.yml (Esperanto) [ci skip] * New translations en.yml (Catalan) [ci skip] * New translations en.yml (Italian) [ci skip] * New translations en.yml (Corsican) [ci skip] * New translations en.yml (Spanish) [ci skip] * New translations en.yml (Russian) [ci skip] * New translations en.yml (French) [ci skip] * New translations en.yml (Danish) [ci skip] * New translations en.yml (Korean) [ci skip] * New translations en.yml (Chinese Simplified) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.json (Chinese Traditional) [ci skip] * New translations en.json (Japanese) [ci skip] * New translations en.yml (Galician) [ci skip] * New translations en.yml (Japanese) [ci skip] * New translations en.yml (Albanian) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Galician) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations simple_form.en.yml (Vietnamese) [ci skip] * New translations en.yml (Indonesian) [ci skip] * New translations devise.en.yml (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations doorkeeper.en.yml (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations simple_form.en.yml (Vietnamese) [ci skip] * New translations doorkeeper.en.yml (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.json (Persian) [ci skip] * New translations en.yml (Esperanto) [ci skip] * New translations en.json (Standard Moroccan Tamazight) [ci skip] * New translations en.yml (Turkish) [ci skip] * New translations en.yml (Turkish) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations devise.en.yml (Czech) [ci skip] * New translations devise.en.yml (Czech) [ci skip] * New translations en.json (Czech) [ci skip] * New translations en.json (Polish) [ci skip] * New translations en.yml (Polish) [ci skip] * New translations simple_form.en.yml (Polish) [ci skip] * New translations en.yml (Thai) [ci skip] * New translations en.yml (Thai) [ci skip] * New translations en.json (Czech) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.json (Galician) [ci skip] * New translations en.json (Armenian) [ci skip] * New translations en.json (Armenian) [ci skip] * New translations simple_form.en.yml (Armenian) [ci skip] * New translations en.yml (Armenian) [ci skip] * New translations doorkeeper.en.yml (Spanish) [ci skip] * New translations en.json (Spanish, Mexico) [ci skip] * New translations doorkeeper.en.yml (Spanish, Mexico) [ci skip] * New translations simple_form.en.yml (Spanish, Mexico) [ci skip] * New translations en.yml (Spanish, Mexico) [ci skip] * New translations simple_form.en.yml (Spanish) [ci skip] * New translations en.json (Spanish) [ci skip] * New translations en.yml (Spanish) [ci skip] * New translations en.yml (Portuguese) [ci skip] * New translations en.yml (Russian) [ci skip] * New translations en.json (Russian) [ci skip] * New translations en.yml (Swedish) [ci skip] * New translations en.yml (Spanish) [ci skip] * New translations en.yml (Spanish, Argentina) [ci skip] * New translations en.yml (Albanian) [ci skip] * New translations en.yml (Hungarian) [ci skip] * New translations en.yml (Italian) [ci skip] * New translations en.yml (Persian) [ci skip] * New translations en.yml (Portuguese, Brazilian) [ci skip] * New translations simple_form.en.yml (Portuguese, Brazilian) [ci skip] * New translations en.yml (French) [ci skip] * New translations en.yml (Catalan) [ci skip] * New translations en.yml (Turkish) [ci skip] * New translations en.yml (Armenian) [ci skip] * New translations en.yml (Armenian) [ci skip] * New translations simple_form.en.yml (Armenian) [ci skip] * New translations en.yml (Armenian) [ci skip] * New translations en.yml (Korean) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.yml (Danish) [ci skip] * New translations en.yml (Polish) [ci skip] * New translations en.yml (Thai) [ci skip] * New translations en.yml (Galician) [ci skip] * New translations en.yml (Indonesian) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Japanese) [ci skip] * New translations en.json (Japanese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Vietnamese) [ci skip] * New translations en.json (Chinese Traditional, Hong Kong) [ci skip] * New translations en.json (Chinese Traditional, Hong Kong) [ci skip] * New translations en.yml (Chinese Traditional, Hong Kong) [ci skip] * New translations simple_form.en.yml (Chinese Traditional, Hong Kong) [ci skip] * New translations simple_form.en.yml (French) [ci skip] * New translations en.json (Chinese Traditional, Hong Kong) [ci skip] * New translations en.json (Chinese Traditional, Hong Kong) [ci skip] * New translations en.json (Chinese Traditional, Hong Kong) [ci skip] * New translations en.json (Chinese Traditional, Hong Kong) [ci skip] * New translations en.json (Chinese Traditional, Hong Kong) [ci skip] * New translations devise.en.yml (Chinese Traditional, Hong Kong) [ci skip] * New translations devise.en.yml (Chinese Traditional, Hong Kong) [ci skip] * New translations devise.en.yml (Chinese Traditional, Hong Kong) [ci skip] * New translations en.yml (Chinese Simplified) [ci skip] * New translations en.json (Turkish) [ci skip] * New translations en.yml (German) [ci skip] * New translations en.yml (Galician) [ci skip] * New translations en.json (Galician) [ci skip] * New translations devise.en.yml (Galician) [ci skip] * New translations en.json (Armenian) [ci skip] * New translations en.yml (Armenian) [ci skip] * New translations en.json (Armenian) [ci skip] * New translations en.yml (Armenian) [ci skip] * New translations en.json (Sardinian) [ci skip] * New translations en.json (Sardinian) [ci skip] * New translations en.json (Sardinian) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.json (Vietnamese) [ci skip] * New translations en.yml (Chinese Traditional) [ci skip] * New translations en.yml (Chinese Traditional) [ci skip] * New translations simple_form.en.yml (Chinese Traditional) [ci skip] * New translations en.yml (Sorani (Kurdish)) [ci skip] * New translations doorkeeper.en.yml (Sorani (Kurdish)) [ci skip] * Ran `i18n-tasks normalize` * Ran `yarn manage:translations` Co-authored-by: Yamagishi Kazutoshi --- app/javascript/mastodon/locales/cs.json | 8 ++-- .../mastodon/locales/defaultMessages.json | 13 ++++++ app/javascript/mastodon/locales/eo.json | 6 +-- app/javascript/mastodon/locales/fa.json | 2 +- app/javascript/mastodon/locales/fr.json | 2 +- app/javascript/mastodon/locales/gl.json | 10 ++--- app/javascript/mastodon/locales/hy.json | 26 +++++------ app/javascript/mastodon/locales/ja.json | 4 +- app/javascript/mastodon/locales/kn.json | 8 ++-- app/javascript/mastodon/locales/pl.json | 4 +- app/javascript/mastodon/locales/ru.json | 2 +- app/javascript/mastodon/locales/sc.json | 52 +++++++++++----------- app/javascript/mastodon/locales/tr.json | 2 +- app/javascript/mastodon/locales/vi.json | 36 +++++++-------- app/javascript/mastodon/locales/zgh.json | 20 ++++----- app/javascript/mastodon/locales/zh-HK.json | 52 +++++++++++----------- app/javascript/mastodon/locales/zh-TW.json | 18 ++++---- 17 files changed, 139 insertions(+), 126 deletions(-) (limited to 'app') diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index ad47b064c..c43b3c875 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -1,5 +1,5 @@ { - "account.account_note_header": "Note", + "account.account_note_header": "Poznámka", "account.add_or_remove_from_list": "Přidat nebo odstranit ze seznamů", "account.badges.bot": "Robot", "account.badges.group": "Skupina", @@ -326,14 +326,14 @@ "notifications.column_settings.reblog": "Boosty:", "notifications.column_settings.show": "Zobrazit ve sloupci", "notifications.column_settings.sound": "Přehrát zvuk", - "notifications.column_settings.status": "New toots:", + "notifications.column_settings.status": "Nové tooty:", "notifications.filter.all": "Vše", "notifications.filter.boosts": "Boosty", "notifications.filter.favourites": "Oblíbení", "notifications.filter.follows": "Sledování", "notifications.filter.mentions": "Zmínky", "notifications.filter.polls": "Výsledky anket", - "notifications.filter.statuses": "Updates from people you follow", + "notifications.filter.statuses": "Aktuality od lidí, které sledujete", "notifications.group": "{count} oznámení", "notifications.mark_as_read": "Mark every notification as read", "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request", @@ -458,7 +458,7 @@ "upload_form.audio_description": "Popis pro sluchově postižené", "upload_form.description": "Popis pro zrakově postižené", "upload_form.edit": "Upravit", - "upload_form.thumbnail": "Change thumbnail", + "upload_form.thumbnail": "Změnit miniaturu", "upload_form.undo": "Smazat", "upload_form.video_description": "Popis pro sluchově či zrakově postižené", "upload_modal.analyzing_picture": "Analyzuji obrázek…", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index c49c6399d..ae9ba853e 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -2388,6 +2388,10 @@ }, { "descriptors": [ + { + "defaultMessage": "Close", + "id": "lightbox.close" + }, { "defaultMessage": "Never miss a thing", "id": "notifications_permission_banner.title" @@ -2478,6 +2482,15 @@ ], "path": "app/javascript/mastodon/features/picture_in_picture/components/footer.json" }, + { + "descriptors": [ + { + "defaultMessage": "Close", + "id": "lightbox.close" + } + ], + "path": "app/javascript/mastodon/features/picture_in_picture/components/header.json" + }, { "descriptors": [ { diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index b401e5bbe..1b5f18884 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -236,7 +236,7 @@ "keyboard_shortcuts.hotkey": "Rapidklavo", "keyboard_shortcuts.legend": "montri ĉi tiun noton", "keyboard_shortcuts.local": "malfermi la lokan tempolinion", - "keyboard_shortcuts.mention": "por mencii la aŭtoron", + "keyboard_shortcuts.mention": "mencii la aŭtoron", "keyboard_shortcuts.muted": "malfermi la liston de silentigitaj uzantoj", "keyboard_shortcuts.my_profile": "malfermi vian profilon", "keyboard_shortcuts.notifications": "malfermi la kolumnon de sciigoj", @@ -246,7 +246,7 @@ "keyboard_shortcuts.reply": "respondi", "keyboard_shortcuts.requests": "malfermi la liston de petoj de sekvado", "keyboard_shortcuts.search": "enfokusigi la serĉilon", - "keyboard_shortcuts.spoilers": "to show/hide CW field", + "keyboard_shortcuts.spoilers": "montri/kaŝi la kampon de enhava averto", "keyboard_shortcuts.start": "malfermi la kolumnon «por komenci»", "keyboard_shortcuts.toggle_hidden": "montri/kaŝi tekston malantaŭ enhava averto", "keyboard_shortcuts.toggle_sensitivity": "montri/kaŝi aŭdovidaĵojn", @@ -326,7 +326,7 @@ "notifications.column_settings.reblog": "Diskonigoj:", "notifications.column_settings.show": "Montri en kolumno", "notifications.column_settings.sound": "Eligi sonon", - "notifications.column_settings.status": "New toots:", + "notifications.column_settings.status": "Novaj mesaĝoj:", "notifications.filter.all": "Ĉiuj", "notifications.filter.boosts": "Diskonigoj", "notifications.filter.favourites": "Stelumoj", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index a38d21e1c..44c936142 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -80,7 +80,7 @@ "column_header.pin": "ثابت‌کردن", "column_header.show_settings": "نمایش تنظیمات", "column_header.unpin": "رهاکردن", - "column_subheading.settings": "تنظیمات", + "column_subheading.settings": "ساماندهی", "community.column_settings.local_only": "تنها بومی", "community.column_settings.media_only": "فقط رسانه", "community.column_settings.remote_only": "تنها دوردست", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index a70a5c1c7..19c86722d 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -99,7 +99,7 @@ "compose_form.publish": "Pouet", "compose_form.publish_loud": "{publish} !", "compose_form.sensitive.hide": "Marquer le média comme sensible", - "compose_form.sensitive.marked": "Média marqué comme sensible", + "compose_form.sensitive.marked": "{count, plural, one {Le média est marqué comme sensible} other {Les médias sont marqués comme sensibles}}", "compose_form.sensitive.unmarked": "Le média n’est pas 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é", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 591d1c13a..6fe22e1d4 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -73,7 +73,7 @@ "column.notifications": "Notificacións", "column.pins": "Toots fixados", "column.public": "Cronoloxía federada", - "column_back_button.label": "Voltar", + "column_back_button.label": "Volver", "column_header.hide_settings": "Agochar axustes", "column_header.moveLeft_settings": "Mover columna cara a esquerda", "column_header.moveRight_settings": "Mover columna cara a dereita", @@ -219,7 +219,7 @@ "introduction.welcome.action": "Imos!", "introduction.welcome.headline": "Primeiros pasos", "introduction.welcome.text": "Benvida ó fediverso! Nun intre poderás difundir mensaxes e falar coas túas amizades nun grande número de servidores. Mais este servidor, {domain}, é especial—hospeda o teu perfil, por iso lémbra o seu nome.", - "keyboard_shortcuts.back": "para voltar atrás", + "keyboard_shortcuts.back": "para volver atrás", "keyboard_shortcuts.blocked": "abrir lista de usuarias bloqueadas", "keyboard_shortcuts.boost": "promover", "keyboard_shortcuts.column": "para destacar un toot nunha das columnas", @@ -336,10 +336,10 @@ "notifications.filter.statuses": "Actualizacións de xente á que segues", "notifications.group": "{count} notificacións", "notifications.mark_as_read": "Marcar todas as notificacións como lidas", - "notifications.permission_denied": "Non se activaron as notificacións de escritorio porque se denegou o permiso.", + "notifications.permission_denied": "Non se activaron as notificacións de escritorio porque se denegou o permiso", "notifications.permission_denied_alert": "Non se poden activar as notificacións de escritorio, xa que o permiso para o navegador foi denegado previamente", "notifications_permission_banner.enable": "Activar notificacións de escritorio", - "notifications_permission_banner.how_to_control": "Activa as notificacións de escritorio para recibir notificacións mentras Mastodon non está aberto. Podes controlar de xeito preciso o tipo de interaccións crean as notificacións de escritorio a través da {icon} superior unha vez activadas.", + "notifications_permission_banner.how_to_control": "Activa as notificacións de escritorio para recibir notificacións mentras Mastodon non está aberto. Podes controlar de xeito preciso o tipo de interaccións que crean as notificacións de escritorio a través da {icon} superior unha vez están activadas.", "notifications_permission_banner.title": "Non perder nada", "picture_in_picture.restore": "Devolver", "poll.closed": "Pechado", @@ -411,7 +411,7 @@ "status.pinned": "Toot fixado", "status.read_more": "Ler máis", "status.reblog": "Promover", - "status.reblog_private": "Compartir á audiencia orixinal", + "status.reblog_private": "Compartir coa audiencia orixinal", "status.reblogged_by": "{name} promoveu", "status.reblogs.empty": "Aínda ninguén promoveu este toot. Cando alguén o faga, amosarase aquí.", "status.redraft": "Eliminar e reescribir", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index 5033b8ca2..dd904bfca 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -9,10 +9,10 @@ "account.browse_more_on_origin_server": "Դիտել աւելին իրական պրոֆիլում", "account.cancel_follow_request": "չեղարկել հետեւելու հայցը", "account.direct": "Նամակ գրել @{name} -ին", - "account.disable_notifications": "Stop notifying me when @{name} posts", + "account.disable_notifications": "Ծանուցումները անջատել @{name} գրառումների համար", "account.domain_blocked": "Տիրոյթը արգելափակուած է", "account.edit_profile": "Խմբագրել անձնական էջը", - "account.enable_notifications": "Notify me when @{name} posts", + "account.enable_notifications": "Ծանուցել ինձ @{name} գրառումների մասին", "account.endorse": "Ցուցադրել անձնական էջում", "account.follow": "Հետեւել", "account.followers": "Հետեւողներ", @@ -168,9 +168,9 @@ "empty_column.notifications": "Ոչ մի ծանուցում դեռ չունես։ Բզիր միւսներին՝ խօսակցութիւնը սկսելու համար։", "empty_column.public": "Այստեղ բան չկա՛յ։ Հրապարակային մի բան գրիր կամ հետեւիր այլ հանգոյցներից էակների՝ այն լցնելու համար։", "error.unexpected_crash.explanation": "Մեր ծրագրակազմում վրիպակի կամ դիտարկչի անհամատեղելիութեան պատճառով այս էջը չի կարող լիարժէք պատկերուել։", - "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.", + "error.unexpected_crash.explanation_addons": "Այս էջի ճիշտ պատկերումը չի ստացում։ Խափանումը հաւանաբար առաջացել է դիտարկիչի յավելվածից կամ առցանց թարգմանիչից։", "error.unexpected_crash.next_steps": "Փորձիր թարմացնել էջը։ Եթե դա չօգնի ապա կարող ես օգտվել Մաստադոնից ուրիշ դիտարկիչով կամ հավելվածով։", - "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "error.unexpected_crash.next_steps_addons": "Փորձիր անջատել յաւելուածները եւ թարմացնել էջը։ Եթե դա չօգնի, կարող ես օգտուել Մաստադոնից այլ դիտարկիչով կամ յաւելուածով։", "errors.unexpected_crash.copy_stacktrace": "Պատճենել սթաքթրեյսը սեղմատախտակին", "errors.unexpected_crash.report_issue": "Զեկուցել խնդրի մասին", "follow_request.authorize": "Վավերացնել", @@ -266,9 +266,9 @@ "lists.edit.submit": "Փոխել վերնագիրը", "lists.new.create": "Ավելացնել ցանկ", "lists.new.title_placeholder": "Նոր ցանկի վերնագիր", - "lists.replies_policy.all_replies": "Any followed user", - "lists.replies_policy.list_replies": "Members of the list", - "lists.replies_policy.no_replies": "No one", + "lists.replies_policy.all_replies": "Ում հետևում եմ", + "lists.replies_policy.list_replies": "Ցանկի անդամները", + "lists.replies_policy.no_replies": "Ոչ ոք", "lists.replies_policy.title": "Show replies to:", "lists.search": "Փնտրել քո հետեւած մարդկանց մեջ", "lists.subheading": "Քո ցանկերը", @@ -277,9 +277,9 @@ "media_gallery.toggle_visible": "Ցուցադրել/թաքցնել", "missing_indicator.label": "Չգտնվեց", "missing_indicator.sublabel": "Պաշարը չի գտնւում", - "mute_modal.duration": "Duration", + "mute_modal.duration": "Տևողություն", "mute_modal.hide_notifications": "Թաքցնե՞լ ցանուցումներն այս օգտատիրոջից։", - "mute_modal.indefinite": "Indefinite", + "mute_modal.indefinite": "Անժամկետ", "navigation_bar.apps": "Դիւրակիր յաւելուածներ", "navigation_bar.blocks": "Արգելափակված օգտատերեր", "navigation_bar.bookmarks": "Էջանիշեր", @@ -310,7 +310,7 @@ "notification.own_poll": "Հարցումդ աւարտուեց", "notification.poll": "Հարցումը, ուր դու քուէարկել ես, աւարտուեց։", "notification.reblog": "{name} տարածեց թութդ", - "notification.status": "{name} just posted", + "notification.status": "{name} հենց նոր թթեց", "notifications.clear": "Մաքրել ծանուցումները", "notifications.clear_confirmation": "Վստա՞հ ես, որ ուզում ես մշտապէս մաքրել քո բոլոր ծանուցումները։", "notifications.column_settings.alert": "Աշխատատիրոյթի ծանուցումներ", @@ -326,7 +326,7 @@ "notifications.column_settings.reblog": "Տարածածներից՝", "notifications.column_settings.show": "Ցուցադրել սիւնում", "notifications.column_settings.sound": "Ձայն հանել", - "notifications.column_settings.status": "New toots:", + "notifications.column_settings.status": "Նոր թթեր։", "notifications.filter.all": "Բոլորը", "notifications.filter.boosts": "Տարածածները", "notifications.filter.favourites": "Հաւանածները", @@ -335,10 +335,10 @@ "notifications.filter.polls": "Հարցման արդիւնքները", "notifications.filter.statuses": "Updates from people you follow", "notifications.group": "{count} ծանուցում", - "notifications.mark_as_read": "Mark every notification as read", + "notifications.mark_as_read": "Համարել բոլոր ծանուցումները ընթերցած", "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request", "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before", - "notifications_permission_banner.enable": "Enable desktop notifications", + "notifications_permission_banner.enable": "Միացնել դիտարկչից ծանուցումները", "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.", "notifications_permission_banner.title": "Never miss a thing", "picture_in_picture.restore": "Put it back", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 207bcf441..ec60e0782 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -333,7 +333,7 @@ "notifications.filter.follows": "フォロー", "notifications.filter.mentions": "返信", "notifications.filter.polls": "アンケート結果", - "notifications.filter.statuses": "Updates from people you follow", + "notifications.filter.statuses": "フォローしている人の新着情報", "notifications.group": "{count} 件の通知", "notifications.mark_as_read": "すべて既読にする", "notifications.permission_denied": "ブラウザの通知が拒否されているためデスクトップ通知は利用できません", @@ -357,7 +357,7 @@ "privacy.private.short": "フォロワー限定", "privacy.public.long": "誰でも閲覧可、公開TLに表示", "privacy.public.short": "公開", - "privacy.unlisted.long": "誰でも閲覧可、公開タイムラインに非表示", + "privacy.unlisted.long": "誰でも閲覧可、公開TLに非表示", "privacy.unlisted.short": "未収載", "refresh": "更新", "regeneration_indicator.label": "読み込み中…", diff --git a/app/javascript/mastodon/locales/kn.json b/app/javascript/mastodon/locales/kn.json index 1ac564a70..0f6c71fb9 100644 --- a/app/javascript/mastodon/locales/kn.json +++ b/app/javascript/mastodon/locales/kn.json @@ -14,8 +14,8 @@ "account.edit_profile": "Edit profile", "account.enable_notifications": "Notify me when @{name} posts", "account.endorse": "Feature on profile", - "account.follow": "Follow", - "account.followers": "Followers", + "account.follow": "ಹಿಂಬಾಲಿಸಿ", + "account.followers": "ಹಿಂಬಾಲಕರು", "account.followers.empty": "No one follows this user yet.", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}", @@ -32,7 +32,7 @@ "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", "account.never_active": "Never", - "account.posts": "Toots", + "account.posts": "ಟೂಟ್‌ಗಳು", "account.posts_with_replies": "Toots and replies", "account.report": "Report @{name}", "account.requested": "Awaiting approval", @@ -54,7 +54,7 @@ "autosuggest_hashtag.per_week": "{count} per week", "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", + "bundle_column_error.retry": "ಮರಳಿ ಪ್ರಯತ್ನಿಸಿ", "bundle_column_error.title": "Network error", "bundle_modal_error.close": "Close", "bundle_modal_error.message": "Something went wrong while loading this component.", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index e06952d19..3009471f4 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -254,8 +254,8 @@ "keyboard_shortcuts.unfocus": "aby opuścić pole wyszukiwania/pisania", "keyboard_shortcuts.up": "aby przejść na górę listy", "lightbox.close": "Zamknij", - "lightbox.compress": "Compress image view box", - "lightbox.expand": "Expand image view box", + "lightbox.compress": "Zmniejsz pole widoku obrazu", + "lightbox.expand": "Rozwiń pole widoku obrazu", "lightbox.next": "Następne", "lightbox.previous": "Poprzednie", "lightbox.view_context": "Pokaż kontekst", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 5a2d5a54e..c0022e524 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -98,7 +98,7 @@ "compose_form.poll.switch_to_single": "Переключить в режим выбора одного ответа", "compose_form.publish": "Запостить", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Пометить медиафайл как чувствительный", + "compose_form.sensitive.hide": "{count, plural, one {Отметить медифайл как деликатный} other {Отметить медифайлы как деликатные}}", "compose_form.sensitive.marked": "Медиафайл отмечен как деликатный", "compose_form.sensitive.unmarked": "Медиафайл не отмечен как деликатный", "compose_form.spoiler.marked": "Текст скрыт за предупреждением", diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index 055a52cab..cf4ceb5d0 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -6,18 +6,18 @@ "account.block": "Bloca @{name}", "account.block_domain": "Bloca domìniu{domain}", "account.blocked": "Blocadu", - "account.browse_more_on_origin_server": "Browse more on the original profile", + "account.browse_more_on_origin_server": "Esplora de prus in su profilu originale", "account.cancel_follow_request": "Annulla rechesta de sighidura", "account.direct": "Messàgiu deretu a @{name}", - "account.disable_notifications": "Stop notifying me when @{name} posts", + "account.disable_notifications": "Acaba·la de mi notificare cando @{name} publicat carchi cosa", "account.domain_blocked": "Domìniu blocadu", "account.edit_profile": "Modìfica profilu", - "account.enable_notifications": "Notify me when @{name} posts", + "account.enable_notifications": "Notìfica·mi cando @{name} pùblicat carchi cosa", "account.endorse": "Cussìgia in su profilu tuo", "account.follow": "Sighi", "account.followers": "Sighiduras", "account.followers.empty": "Nemos sighit ancora custa persone.", - "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", + "account.followers_counter": "{count, plural, one {{counter} Sighidore} other {{counter} Sighidores}}", "account.following_counter": "{count, plural, one {{counter} Sighidu} other {{counter} Sighidos}}", "account.follows.empty": "Custa persone non sighit ancora a nemos.", "account.follows_you": "Ti sighit", @@ -38,14 +38,14 @@ "account.requested": "Incarca pro annullare sa rechesta de sighidura", "account.share": "Cumpartzi su profilu de @{name}", "account.show_reblogs": "Ammustra is cumpartziduras de @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", + "account.statuses_counter": "{count, plural, one {{counter} Tut} other {{counter} Tuts}}", "account.unblock": "Isbloca @{name}", "account.unblock_domain": "Isbloca su domìniu {domain}", "account.unendorse": "Non cussiges in su profilu", "account.unfollow": "Non sigas prus", "account.unmute": "Torra a ativare @{name}", "account.unmute_notifications": "Ativa notìficas pro @{name}", - "account_note.placeholder": "Click to add a note", + "account_note.placeholder": "Incarca pro agiùnghere una nota", "alert.rate_limited.message": "Torra·bi a proare a pustis de {retry_time, time, medium}.", "alert.rate_limited.title": "Màssimu de rechestas barigadu", "alert.unexpected.message": "B'at àpidu una faddina.", @@ -81,9 +81,9 @@ "column_header.show_settings": "Ammustra is cunfiguratziones", "column_header.unpin": "Boga dae pitzu", "column_subheading.settings": "Cunfiguratziones", - "community.column_settings.local_only": "Local only", + "community.column_settings.local_only": "Locale ebbia", "community.column_settings.media_only": "Multimediale isceti", - "community.column_settings.remote_only": "Remote only", + "community.column_settings.remote_only": "Remotu ebbia", "compose_form.direct_message_warning": "Custu tut at a èssere imbiadu isceti a is persones mentovadas.", "compose_form.direct_message_warning_learn_more": "Àteras informatziones", "compose_form.hashtag_warning": "Custu tut no at a èssere ammustradu in peruna eticheta, dae chi no est listadu.", @@ -113,7 +113,7 @@ "confirmations.delete_list.confirm": "Cantzella", "confirmations.delete_list.message": "Seguru chi boles cantzellare custa lista in manera permanente?", "confirmations.domain_block.confirm": "Cua totu su domìniu", - "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.domain_block.message": "Ses seguru a beru, ma a beru a beru, de bòlere blocare su {domain} intreu? In sa parte manna de is casos pagos blocos o silentziamentos de utentes sunt sufitzientes e preferìbiles. No as a bìdere cuntenutos dae custu domìniu in peruna lìnia de tempus pùblica o in is notìficas tuas. Is sighidores tuos dae cussu domìniu ant a èssere bogados.", "confirmations.logout.confirm": "Essi·nche", "confirmations.logout.message": "Seguru chi boles essire?", "confirmations.mute.confirm": "A sa muda", @@ -168,15 +168,15 @@ "empty_column.notifications": "Non tenes ancora peruna notìfica. Chistiona cun una persone pro cumintzare un'arresonada.", "empty_column.public": "Nudda inoghe. Iscrie calicuna cosa pùblica, o sighi àteras persones de àteros serbidores pro prenare custu ispàtziu", "error.unexpected_crash.explanation": "A càusa de una faddina in su còdighe nostru o unu problema de cumpatibilidade de su navigadore, custa pàgina diat pòdere no èssere ammustrada in manera curreta.", - "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.", + "error.unexpected_crash.explanation_addons": "Custa pàgina diat pòdere no èssere ammustrada comente si tocat. Custa faddina est probàbile chi dipendat dae un'estensione de su navigadore o dae ainas automàticas de tradutzione.", "error.unexpected_crash.next_steps": "Proa de atualizare sa pàgina. Si custu non acontza su problema, podes chircare de impreare Mastodon in unu navigadore diferente o in un'aplicatzione nativa.", - "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "error.unexpected_crash.next_steps_addons": "Proa a ddos disabilitare e a atualizare sa pàgina. Si custu non acontzat su problema, podes chircare de impreare Mastodon in unu navigadore diferente o in un'aplicatzione nativa.", "errors.unexpected_crash.copy_stacktrace": "Còpia stacktrace in punta de billete", "errors.unexpected_crash.report_issue": "Signala unu problema", "follow_request.authorize": "Autoriza", "follow_request.reject": "Refuda", "follow_requests.unlocked_explanation": "Fintzas si su contu tuo no est blocadu, su personale de {domain} at pensadu chi forsis bolias revisionare a manu is rechestas de custos contos.", - "generic.saved": "Saved", + "generic.saved": "Sarvadu", "getting_started.developers": "Iscuadra de isvilupu", "getting_started.directory": "Diretòriu de profilos", "getting_started.documentation": "Documentatzione", @@ -246,7 +246,7 @@ "keyboard_shortcuts.reply": "pro rispòndere", "keyboard_shortcuts.requests": "pro abèrrere sa lista de rechestas de sighidura", "keyboard_shortcuts.search": "pro atzentrare sa chirca", - "keyboard_shortcuts.spoilers": "to show/hide CW field", + "keyboard_shortcuts.spoilers": "pro ammustrare/cuare su campu AC", "keyboard_shortcuts.start": "pro abèrrere sa colunna \"Cumintza\"", "keyboard_shortcuts.toggle_hidden": "pro ammustrare o cuare testu de is AC", "keyboard_shortcuts.toggle_sensitivity": "pro ammustrare o cuare mèdias", @@ -254,8 +254,8 @@ "keyboard_shortcuts.unfocus": "pro essire de s'àrea de cumpositzione de testu o de chirca", "keyboard_shortcuts.up": "pro mòere in susu in sa lista", "lightbox.close": "Serra", - "lightbox.compress": "Compress image view box", - "lightbox.expand": "Expand image view box", + "lightbox.compress": "Cumprime sa casella de visualizatzione de is immàgines", + "lightbox.expand": "Ismànnia sa casella de visualizatzione de is immàgines", "lightbox.next": "Sighi", "lightbox.previous": "Pretzedente", "lightbox.view_context": "Bide su cuntestu", @@ -266,10 +266,10 @@ "lists.edit.submit": "Muda su tìtulu", "lists.new.create": "Agiunghe lista", "lists.new.title_placeholder": "Lista noa", - "lists.replies_policy.all_replies": "Any followed user", - "lists.replies_policy.list_replies": "Members of the list", - "lists.replies_policy.no_replies": "No one", - "lists.replies_policy.title": "Show replies to:", + "lists.replies_policy.all_replies": "Cale si siat utente sighidu", + "lists.replies_policy.list_replies": "Membros de sa lista", + "lists.replies_policy.no_replies": "Perunu", + "lists.replies_policy.title": "Ammustra is rispostas a:", "lists.search": "Chircare intre sa gente chi ses sighende", "lists.subheading": "Is listas tuas", "load_pending": "{count, plural, one {# elementu nou} other {# elementos noos}}", @@ -279,7 +279,7 @@ "missing_indicator.sublabel": "Resursa no agatada", "mute_modal.duration": "Duration", "mute_modal.hide_notifications": "Boles cuare is notìficas de custa persone?", - "mute_modal.indefinite": "Indefinite", + "mute_modal.indefinite": "Indefinida", "navigation_bar.apps": "Aplicatziones mòbiles", "navigation_bar.blocks": "Persones blocadas", "navigation_bar.bookmarks": "Sinnalibros", @@ -310,7 +310,7 @@ "notification.own_poll": "Sondàgiu acabbadu", "notification.poll": "Unu sondàgiu in su chi as votadu est acabbadu", "notification.reblog": "{name} at cumpartzidu s'istadu tuo", - "notification.status": "{name} just posted", + "notification.status": "{name} at publicadu cosa", "notifications.clear": "Lìmpia notìficas", "notifications.clear_confirmation": "Seguru chi boles isboidare in manera permanente totu is notìficas tuas?", "notifications.column_settings.alert": "Notìficas de iscrivania", @@ -326,19 +326,19 @@ "notifications.column_settings.reblog": "Cumpartziduras:", "notifications.column_settings.show": "Ammustra in sa colunna", "notifications.column_settings.sound": "Reprodue unu sonu", - "notifications.column_settings.status": "New toots:", + "notifications.column_settings.status": "Tuts noos:", "notifications.filter.all": "Totus", "notifications.filter.boosts": "Cumpartziduras", "notifications.filter.favourites": "Preferidos", "notifications.filter.follows": "Sighende", "notifications.filter.mentions": "Mentovos", "notifications.filter.polls": "Resurtados dae su sondàgiu", - "notifications.filter.statuses": "Updates from people you follow", + "notifications.filter.statuses": "Agiornamentos dae is persones chi sighis", "notifications.group": "{count} notìficas", "notifications.mark_as_read": "Mark every notification as read", - "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request", - "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before", - "notifications_permission_banner.enable": "Enable desktop notifications", + "notifications.permission_denied": "Is notìficas de iscrivania non sunt a disponimentu pro neghe de rechestas de permissu chi sunt istadas dennegadas in antis", + "notifications.permission_denied_alert": "Is notìficas de iscrivania non podent èssere abilitadas, ca su permissu de su navigadore est istadu dennegadu in antis", + "notifications_permission_banner.enable": "Abìlita is notìficas de iscrivania", "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.", "notifications_permission_banner.title": "Never miss a thing", "picture_in_picture.restore": "Put it back", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 650fbe806..ec5f65a0c 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -154,7 +154,7 @@ "empty_column.blocks": "Henüz bir kullanıcıyı engellemediniz.", "empty_column.bookmarked_statuses": "Henüz yer imine eklediğiniz toot yok. Yer imine eklendiğinde burada görünecek.", "empty_column.community": "Yerel zaman çizelgesi boş. Daha fazla eğlence için herkese açık bir gönderi paylaşın!", - "empty_column.direct": "Henüz doğrudan mesajınız yok. Bir tane gönderdiğinizde veya aldığınızda burada görünecektir.", + "empty_column.direct": "Henüz direkt mesajınız yok. Bir tane gönderdiğinizde veya aldığınızda burada görünecektir.", "empty_column.domain_blocks": "Henüz hiçbir gizli alan adı yok.", "empty_column.favourited_statuses": "Hiç favori tootunuz yok. Favori olduğunda burada görünecek.", "empty_column.favourites": "Kimse bu tootu favorilerine eklememiş. Biri eklediğinde burada görünecek.", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 5a3988ce8..3da7cf375 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -18,13 +18,13 @@ "account.followers": "Người theo dõi", "account.followers.empty": "Chưa có người theo dõi nào.", "account.followers_counter": "{count, plural, one {{counter} Người theo dõi} other {{counter} Người theo dõi}}", - "account.following_counter": "{count, plural, one {{counter} Đang theo dõi} other {{counter} Đang theo dõi}}", + "account.following_counter": "{count, plural, one {{counter} Theo dõi} other {{counter} Theo dõi}}", "account.follows.empty": "Người dùng này chưa theo dõi ai.", "account.follows_you": "Đang theo dõi bạn", "account.hide_reblogs": "Ẩn chia sẻ từ @{name}", - "account.last_status": "Trực tuyến", + "account.last_status": "Online", "account.link_verified_on": "Liên kết này đã được xác thực vào {date}", - "account.locked_info": "Người dùng này thiết lập trạng thái ẩn. Họ sẽ tự mình xét duyệt các yêu cầu theo dõi.", + "account.locked_info": "Đây là tài khoản riêng tư. Họ sẽ tự mình xét duyệt các yêu cầu theo dõi.", "account.media": "Bộ sưu tập", "account.mention": "Nhắc đến @{name}", "account.moved_to": "{name} đã dời sang:", @@ -60,7 +60,7 @@ "bundle_modal_error.message": "Đã có lỗi xảy ra trong khi tải nội dung này.", "bundle_modal_error.retry": "Thử lại", "column.blocks": "Người dùng đã chặn", - "column.bookmarks": "Những tút bạn đã lưu", + "column.bookmarks": "Để dành đọc lại", "column.community": "Máy chủ của bạn", "column.direct": "Tin nhắn của bạn", "column.directory": "Tìm người cùng sở thích", @@ -72,7 +72,7 @@ "column.mutes": "Người dùng đã ẩn", "column.notifications": "Thông báo", "column.pins": "Tút ghim", - "column.public": "Từ những máy chủ khác", + "column.public": "Mạng liên hợp", "column_back_button.label": "Quay lại", "column_header.hide_settings": "Ẩn bộ lọc", "column_header.moveLeft_settings": "Dời cột sang bên trái", @@ -99,8 +99,8 @@ "compose_form.publish": "Tút", "compose_form.publish_loud": "{publish}!", "compose_form.sensitive.hide": "Nội dung nhạy cảm", - "compose_form.sensitive.marked": "Nội dung đã đánh dấu nhạy cảm", - "compose_form.sensitive.unmarked": "Nội dung không đánh dấu nhạy cảm", + "compose_form.sensitive.marked": "{count, plural, one {Nội dung đã đánh dấu là nhạy cảm} other {Nội dung đã đánh dấu là nhạy cảm}}", + "compose_form.sensitive.unmarked": "{count, plural, one {Nội dung này bình thường} other {Nội dung này bình thường}}", "compose_form.spoiler.marked": "Hủy nội dung ẩn", "compose_form.spoiler.unmarked": "Tạo nội dung ẩn", "compose_form.spoiler_placeholder": "Viết nội dung ẩn của bạn ở đây", @@ -150,7 +150,7 @@ "emoji_button.symbols": "Biểu tượng", "emoji_button.travel": "Du lịch", "empty_column.account_timeline": "Chưa có tút nào!", - "empty_column.account_unavailable": "Tài khoản không còn nữa", + "empty_column.account_unavailable": "Tài khoản bị đình chỉ", "empty_column.blocks": "Bạn chưa chặn bất cứ ai.", "empty_column.bookmarked_statuses": "Bạn chưa lưu tút nào. Nếu có, nó sẽ hiển thị ở đây.", "empty_column.community": "Máy chủ của bạn chưa có tút nào công khai. Bạn hãy thử viết gì đó đi!", @@ -159,7 +159,7 @@ "empty_column.favourited_statuses": "Bạn chưa thích tút nào. Hãy thử đi, nó sẽ xuất hiện ở đây.", "empty_column.favourites": "Chưa có ai thích tút này.", "empty_column.follow_requests": "Bạn chưa có yêu cầu theo dõi nào.", - "empty_column.hashtag": "Chưa có bài đăng nào sử dụng hashtag này.", + "empty_column.hashtag": "Chưa có bài đăng nào dùng hashtag này.", "empty_column.home": "Chưa có bất cứ gì! Hãy bắt đầu bằng cách tìm kiếm hoặc truy cập {public} để theo dõi những người bạn quan tâm.", "empty_column.home.public_timeline": "tút công khai", "empty_column.list": "Chưa có tút. Khi những người trong danh sách này đăng tút mới, chúng sẽ xuất hiện ở đây.", @@ -246,7 +246,7 @@ "keyboard_shortcuts.reply": "trả lời", "keyboard_shortcuts.requests": "mở danh sách yêu cầu theo dõi", "keyboard_shortcuts.search": "mở tìm kiếm", - "keyboard_shortcuts.spoilers": "Hiện/ẩn nội dung nhạy cảm", + "keyboard_shortcuts.spoilers": "hiện/ẩn nội dung nhạy cảm", "keyboard_shortcuts.start": "mở mục \"Dành cho người mới\"", "keyboard_shortcuts.toggle_hidden": "ẩn/hiện văn bản bên dưới spoil", "keyboard_shortcuts.toggle_sensitivity": "ẩn/hiện ảnh hoặc video", @@ -273,8 +273,8 @@ "lists.search": "Tìm kiếm những người mà bạn quan tâm", "lists.subheading": "Danh sách của bạn", "load_pending": "{count, plural, one {# tút mới} other {# tút mới}}", - "loading_indicator.label": "Chờ xíu...", - "media_gallery.toggle_visible": "Ẩn {number, plural, one {ảnh} other {ảnh}}", + "loading_indicator.label": "Đang tải...", + "media_gallery.toggle_visible": "Ẩn {number, plural, one {hình ảnh} other {hình ảnh}}", "missing_indicator.label": "Không tìm thấy", "missing_indicator.sublabel": "Nội dung này không còn tồn tại", "mute_modal.duration": "Thời hạn", @@ -311,7 +311,7 @@ "notification.poll": "Một cuộc bình chọn mà bạn tham gia đã kết thúc", "notification.reblog": "{name} chia sẻ tút của bạn", "notification.status": "{name} vừa đăng", - "notifications.clear": "Làm trống thông báo", + "notifications.clear": "Xóa hết thông báo", "notifications.clear_confirmation": "Bạn có chắc chắn muốn xóa vĩnh viễn tất cả thông báo của mình?", "notifications.column_settings.alert": "Thông báo trên máy tính", "notifications.column_settings.favourite": "Lượt thích:", @@ -371,13 +371,13 @@ "reply_indicator.cancel": "Hủy bỏ", "report.forward": "Chuyển đến {target}", "report.forward_hint": "Người dùng này ở máy chủ khác. Gửi một báo cáo ẩn danh tới máy chủ đó?", - "report.hint": "Hãy cho quản trị viên biết lý do vì sao bạn báo cáo tài khoản này:", + "report.hint": "Hãy cho quản trị viên biết lý do vì sao bạn báo cáo người dùng này:", "report.placeholder": "Bổ sung thêm", "report.submit": "Gửi đi", "report.target": "Báo cáo {target}", "search.placeholder": "Tìm kiếm", "search_popout.search_format": "Gợi ý", - "search_popout.tips.full_text": "Nội dung trả về bao gồm những tút do bạn viết, yêu thích, đã chia sẻ hoặc những tút có nhắc đến bạn. Bạn cũng có thể tìm địa chỉ người dùng, tên hiển thị và hashtag.", + "search_popout.tips.full_text": "Nội dung trả về bao gồm những tút mà bạn đã viết, thích, chia sẻ hoặc những tút có nhắc đến bạn. Bạn cũng có thể tìm địa chỉ người dùng, tên hiển thị và hashtag.", "search_popout.tips.hashtag": "hashtag", "search_popout.tips.status": "tút", "search_popout.tips.text": "Nội dung trả về là địa chỉ người dùng, tên hiển thị và hashtag", @@ -419,14 +419,14 @@ "status.reply": "Trả lời", "status.replyAll": "Trả lời tất cả", "status.report": "Báo cáo @{name}", - "status.sensitive_warning": "Nội dung nhạy cảm", + "status.sensitive_warning": "Nhạy cảm", "status.share": "Chia sẻ", "status.show_less": "Thu gọn", - "status.show_less_all": "Thu gọn tất cả", + "status.show_less_all": "Thu gọn toàn bộ", "status.show_more": "Mở rộng", "status.show_more_all": "Hiển thị tất cả", "status.show_thread": "Xem thêm", - "status.uncached_media_warning": "N/A", + "status.uncached_media_warning": "Giới hạn", "status.unmute_conversation": "Quan tâm", "status.unpin": "Bỏ ghim trên trang cá nhân", "suggestions.dismiss": "Tắt đề xuất", diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json index b877d76b4..e79399ea1 100644 --- a/app/javascript/mastodon/locales/zgh.json +++ b/app/javascript/mastodon/locales/zgh.json @@ -1,13 +1,13 @@ { - "account.account_note_header": "Note", - "account.add_or_remove_from_list": "Add or Remove from lists", - "account.badges.bot": "Bot", + "account.account_note_header": "ⵍⵎⴷ ⵓⴳⴳⴰⵔ", + "account.add_or_remove_from_list": "ⵔⵏⵓ ⵏⵖ ⵙⵉⵜⵜⵢ ⵙⴳ ⵜⵍⴳⴰⵎⵜ", + "account.badges.bot": "ⴰⴱⵓⵜ", "account.badges.group": "ⵜⴰⵔⴰⴱⴱⵓⵜ", "account.block": "ⴳⴷⵍ @{name}", "account.block_domain": "Block domain {domain}", - "account.blocked": "Blocked", + "account.blocked": "ⵉⵜⵜⵓⴳⴷⵍ", "account.browse_more_on_origin_server": "Browse more on the original profile", - "account.cancel_follow_request": "Cancel follow request", + "account.cancel_follow_request": "ⵙⵔ ⵜⵓⵜⵔⴰ ⵏ ⵓⴹⴼⵕ", "account.direct": "Direct message @{name}", "account.disable_notifications": "Stop notifying me when @{name} posts", "account.domain_blocked": "Domain blocked", @@ -25,7 +25,7 @@ "account.last_status": "Last active", "account.link_verified_on": "Ownership of this link was checked on {date}", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", - "account.media": "Media", + "account.media": "ⴰⵙⵏⵖⵎⵉⵙ", "account.mention": "Mention @{name}", "account.moved_to": "{name} has moved to:", "account.mute": "Mute @{name}", @@ -70,7 +70,7 @@ "column.home": "ⴰⵙⵏⵓⴱⴳ", "column.lists": "ⵜⵉⵍⴳⴰⵎⵉⵏ", "column.mutes": "Muted users", - "column.notifications": "Notifications", + "column.notifications": "ⵜⵉⵏⵖⵎⵉⵙⵉⵏ", "column.pins": "Pinned toot", "column.public": "Federated timeline", "column_back_button.label": "Back", @@ -104,7 +104,7 @@ "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", + "confirmation_modal.cancel": "ⵙⵔ", "confirmations.block.block_and_report": "Block & Report", "confirmations.block.confirm": "Block", "confirmations.block.message": "Are you sure you want to block {name}?", @@ -183,7 +183,7 @@ "getting_started.heading": "Getting started", "getting_started.invite": "Invite people", "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.", - "getting_started.security": "Security", + "getting_started.security": "ⵜⵉⵙⵖⴰⵍ ⵏ ⵓⵎⵉⴹⴰⵏ", "getting_started.terms": "Terms of service", "hashtag.column_header.tag_mode.all": "and {additional}", "hashtag.column_header.tag_mode.any": "or {additional}", @@ -311,7 +311,7 @@ "notification.poll": "A poll you have voted in has ended", "notification.reblog": "{name} boosted your status", "notification.status": "{name} just posted", - "notifications.clear": "Clear notifications", + "notifications.clear": "ⵙⴼⴹ ⵜⵉⵏⵖⵎⵉⵙⵉⵏ", "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", "notifications.column_settings.alert": "Desktop notifications", "notifications.column_settings.favourite": "Favourites:", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 346bf058e..832e2a6a0 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -1,6 +1,6 @@ { "account.account_note_header": "筆記", - "account.add_or_remove_from_list": "從名單中新增或移除", + "account.add_or_remove_from_list": "從列表中新增或移除", "account.badges.bot": "機械人", "account.badges.group": "群組", "account.block": "封鎖 @{name}", @@ -9,10 +9,10 @@ "account.browse_more_on_origin_server": "在該服務器的個人檔案頁上瀏覽更多", "account.cancel_follow_request": "取消關注請求", "account.direct": "私訊 @{name}", - "account.disable_notifications": "Stop notifying me when @{name} posts", + "account.disable_notifications": "如果 @{name} 發文請不要再通知我", "account.domain_blocked": "服務站被隱藏", "account.edit_profile": "修改個人資料", - "account.enable_notifications": "Notify me when @{name} posts", + "account.enable_notifications": "如果 @{name} 發文請通知我", "account.endorse": "在個人資料推薦對方", "account.follow": "關注", "account.followers": "關注的人", @@ -168,9 +168,9 @@ "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。", "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。", "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,故無法正常顯示頁面。", - "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.", + "error.unexpected_crash.explanation_addons": "此頁面無法被正確顯示,這可能是由於瀏覽器的附加元件或網頁自動翻譯工具造成的。", "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有進展,你可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。", - "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "error.unexpected_crash.next_steps_addons": "請嘗試停止使用這些附加元件然後重新載入頁面。如果問題沒有解決,你仍然可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。", "errors.unexpected_crash.copy_stacktrace": "複製到剪貼簿", "errors.unexpected_crash.report_issue": "舉報問題", "follow_request.authorize": "批准", @@ -254,10 +254,10 @@ "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索", "keyboard_shortcuts.up": "在列表往上移動", "lightbox.close": "關閉", - "lightbox.compress": "Compress image view box", - "lightbox.expand": "Expand image view box", - "lightbox.next": "繼續", - "lightbox.previous": "回退", + "lightbox.compress": "縮小檢視", + "lightbox.expand": "擴大檢視", + "lightbox.next": "下一頁", + "lightbox.previous": "上一頁", "lightbox.view_context": "檢視內文", "lists.account.add": "新增到列表", "lists.account.remove": "從列表刪除", @@ -266,10 +266,10 @@ "lists.edit.submit": "變更標題", "lists.new.create": "新增列表", "lists.new.title_placeholder": "新列表標題", - "lists.replies_policy.all_replies": "Any followed user", - "lists.replies_policy.list_replies": "Members of the list", - "lists.replies_policy.no_replies": "No one", - "lists.replies_policy.title": "Show replies to:", + "lists.replies_policy.all_replies": "任何關注的用戶", + "lists.replies_policy.list_replies": "列表中的用戶", + "lists.replies_policy.no_replies": "沒有人", + "lists.replies_policy.title": "顯示回應文章︰", "lists.search": "從你關注的用戶中搜索", "lists.subheading": "列表", "load_pending": "{count, plural, other {# 個新項目}}", @@ -277,9 +277,9 @@ "media_gallery.toggle_visible": "打開或關上", "missing_indicator.label": "找不到內容", "missing_indicator.sublabel": "無法找到內容", - "mute_modal.duration": "Duration", + "mute_modal.duration": "時間", "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?", - "mute_modal.indefinite": "Indefinite", + "mute_modal.indefinite": "沒期限", "navigation_bar.apps": "封鎖的使用者", "navigation_bar.blocks": "被你封鎖的用戶", "navigation_bar.bookmarks": "書籤", @@ -310,7 +310,7 @@ "notification.own_poll": "您的投票已結束", "notification.poll": "您投過的投票已經結束", "notification.reblog": "{name} 轉推你的文章", - "notification.status": "{name} just posted", + "notification.status": "{name} 剛剛發了嘟文", "notifications.clear": "清空通知紀錄", "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?", "notifications.column_settings.alert": "顯示桌面通知", @@ -326,22 +326,22 @@ "notifications.column_settings.reblog": "轉推你的文章:", "notifications.column_settings.show": "在通知欄顯示", "notifications.column_settings.sound": "播放音效", - "notifications.column_settings.status": "New toots:", + "notifications.column_settings.status": "新的文章", "notifications.filter.all": "全部", "notifications.filter.boosts": "轉嘟", "notifications.filter.favourites": "最愛", "notifications.filter.follows": "關注的使用者", "notifications.filter.mentions": "提及", "notifications.filter.polls": "投票結果", - "notifications.filter.statuses": "Updates from people you follow", + "notifications.filter.statuses": "已關注的用戶的最新動態", "notifications.group": "{count} 條通知", - "notifications.mark_as_read": "Mark every notification as read", - "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request", - "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before", - "notifications_permission_banner.enable": "Enable desktop notifications", - "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.", - "notifications_permission_banner.title": "Never miss a thing", - "picture_in_picture.restore": "Put it back", + "notifications.mark_as_read": "標記所有通知為已讀", + "notifications.permission_denied": "瀏覽器的桌面通知權限設定為拒絕,因此不可以啟用桌面通知", + "notifications.permission_denied_alert": "瀏覽器的桌面通知權限設定為拒絕,因此不可以啟用桌面通知", + "notifications_permission_banner.enable": "啟用桌面通知", + "notifications_permission_banner.how_to_control": "啟用桌面通知可以在 Mastodon 沒有開啟的時候接收通知。在已經啟用桌面通知的時候,你可以透過上面的 {icon} 按鈕準確控制哪些類型的互動會產生桌面通知。", + "notifications_permission_banner.title": "不放過任何通知", + "picture_in_picture.restore": "還原影片播放器", "poll.closed": "已關閉", "poll.refresh": "重新整理", "poll.total_people": "{count, plural, one {# 個投票} other {# 個投票}}", @@ -468,7 +468,7 @@ "upload_modal.detect_text": "從圖片偵測文字", "upload_modal.edit_media": "編輯媒體", "upload_modal.hint": "點擊或拖曳圓圈以選擇預覽縮圖。", - "upload_modal.preparing_ocr": "Preparing OCR…", + "upload_modal.preparing_ocr": "準備辨識圖片文字…", "upload_modal.preview_label": "預覽 ({ratio})", "upload_progress.label": "上載中……", "video.close": "關閉影片", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 598407a30..a38a7c07a 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -254,8 +254,8 @@ "keyboard_shortcuts.unfocus": "取消輸入文字區塊 / 搜尋的焦點", "keyboard_shortcuts.up": "往上移動名單項目", "lightbox.close": "關閉", - "lightbox.compress": "Compress image view box", - "lightbox.expand": "Expand image view box", + "lightbox.compress": "折疊圖片檢視框", + "lightbox.expand": "展開圖片檢視框", "lightbox.next": "下一步", "lightbox.previous": "上一步", "lightbox.view_context": "檢視內文", @@ -280,7 +280,7 @@ "mute_modal.duration": "持續時間", "mute_modal.hide_notifications": "隱藏來自這位使用者的通知?", "mute_modal.indefinite": "無期限", - "navigation_bar.apps": "封鎖的使用者", + "navigation_bar.apps": "行動應用程式", "navigation_bar.blocks": "封鎖使用者", "navigation_bar.bookmarks": "書籤", "navigation_bar.community_timeline": "本機時間軸", @@ -335,12 +335,12 @@ "notifications.filter.polls": "投票結果", "notifications.filter.statuses": "已跟隨使用者的最新動態", "notifications.group": "{count} 條通知", - "notifications.mark_as_read": "Mark every notification as read", - "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request", - "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before", - "notifications_permission_banner.enable": "Enable desktop notifications", - "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.", - "notifications_permission_banner.title": "Never miss a thing", + "notifications.mark_as_read": "將所有通知都標記為已讀", + "notifications.permission_denied": "由於之前拒絕了瀏覽器請求,因此桌面通知不可用", + "notifications.permission_denied_alert": "因為之前瀏覽器權限被拒絕,無法啟用桌面通知", + "notifications_permission_banner.enable": "啟用桌面通知", + "notifications_permission_banner.how_to_control": "啟用桌面通知以在 Mastodon 沒有開啟的時候接收通知。在已經啟用桌面通知的時候,你可以透過上面的 {icon} 按鈕準確的控制哪些類型的互動會產生桌面通知。", + "notifications_permission_banner.title": "不要錯過任何東西!", "picture_in_picture.restore": "還原", "poll.closed": "已關閉", "poll.refresh": "重新整理", -- cgit From 1e89e2ed988d2103ecd46e06476d863cb40c57c7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 27 Nov 2020 03:24:11 +0100 Subject: Change media modals look in web UI (#15217) - Change overlay background to match color of viewed image - Add interactive reply/boost/favourite buttons to footer of modal - Change ugly "View context" link to button among the action bar --- app/javascript/mastodon/components/modal_root.js | 18 ++- app/javascript/mastodon/components/status.js | 18 ++- .../mastodon/containers/status_container.js | 8 +- .../mastodon/features/account_gallery/index.js | 9 +- .../picture_in_picture/components/footer.js | 28 +++- app/javascript/mastodon/features/status/index.js | 10 +- .../mastodon/features/ui/components/media_modal.js | 174 +++++++++++++++++---- .../mastodon/features/ui/components/modal_root.js | 13 +- .../mastodon/features/ui/components/video_modal.js | 20 +-- app/javascript/mastodon/features/video/index.js | 5 +- app/javascript/styles/mastodon/boost.scss | 36 +++-- app/javascript/styles/mastodon/components.scss | 136 ++++++++++------ package.json | 1 + yarn.lock | 5 + 14 files changed, 337 insertions(+), 144 deletions(-) (limited to 'app') diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.js index fe573ffda..9bfc0e49f 100644 --- a/app/javascript/mastodon/components/modal_root.js +++ b/app/javascript/mastodon/components/modal_root.js @@ -1,12 +1,18 @@ import React from 'react'; import PropTypes from 'prop-types'; import 'wicg-inert'; +import { normal } from 'color-blend'; export default class ModalRoot extends React.PureComponent { static propTypes = { children: PropTypes.node, onClose: PropTypes.func.isRequired, + backgroundColor: PropTypes.shape({ + r: PropTypes.number, + g: PropTypes.number, + b: PropTypes.number, + }), }; activeElement = this.props.children ? document.activeElement : null; @@ -62,9 +68,7 @@ export default class ModalRoot extends React.PureComponent { Promise.resolve().then(() => { this.activeElement.focus({ preventScroll: true }); this.activeElement = null; - }).catch((error) => { - console.error(error); - }); + }).catch(console.error); } } @@ -91,10 +95,16 @@ export default class ModalRoot extends React.PureComponent { ); } + let backgroundColor = null; + + if (this.props.backgroundColor) { + backgroundColor = normal({ ...this.props.backgroundColor, a: 1 }, { r: 0, g: 0, b: 0, a: 0.3 }); + } + return (
-
+
{children}
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 8f288bdf9..27d96e588 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -193,22 +193,24 @@ class Status extends ImmutablePureComponent { } handleOpenVideo = (media, options) => { - this.props.onOpenVideo(media, options); + this.props.onOpenVideo(this._properStatus().get('id'), media, options); + } + + handleOpenMedia = (media, index) => { + this.props.onOpenMedia(this._properStatus().get('id'), media, index); } handleHotkeyOpenMedia = e => { const { onOpenMedia, onOpenVideo } = this.props; - const status = this._properStatus(); + const statusId = this._properStatus().get('id'); e.preventDefault(); if (status.get('media_attachments').size > 0) { - if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { - // TODO: toggle play/paused? - } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { - onOpenVideo(status.getIn(['media_attachments', 0]), { startTime: 0 }); + if (status.getIn(['media_attachments', 0, 'type']) === 'video') { + onOpenVideo(statusId, status.getIn(['media_attachments', 0]), { startTime: 0 }); } else { - onOpenMedia(status.get('media_attachments'), 0); + onOpenMedia(statusId, status.get('media_attachments'), 0); } } } @@ -416,7 +418,7 @@ class Status extends ImmutablePureComponent { media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} - onOpenMedia={this.props.onOpenMedia} + onOpenMedia={this.handleOpenMedia} cacheWidth={this.props.cacheMediaWidth} defaultWidth={this.props.cachedMediaWidth} visible={this.state.showMedia} diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index fe81981a7..ef520b96a 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -152,12 +152,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(mentionCompose(account, router)); }, - onOpenMedia (media, index) { - dispatch(openModal('MEDIA', { media, index })); + onOpenMedia (statusId, media, index) { + dispatch(openModal('MEDIA', { statusId, media, index })); }, - onOpenVideo (media, options) { - dispatch(openModal('VIDEO', { media, options })); + onOpenVideo (statusId, media, options) { + dispatch(openModal('VIDEO', { statusId, media, options })); }, onBlock (status) { diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js index e5caec0bc..597ca8af6 100644 --- a/app/javascript/mastodon/features/account_gallery/index.js +++ b/app/javascript/mastodon/features/account_gallery/index.js @@ -105,15 +105,18 @@ class AccountGallery extends ImmutablePureComponent { } handleOpenMedia = attachment => { + const { dispatch } = this.props; + const statusId = attachment.getIn(['status', 'id']); + if (attachment.get('type') === 'video') { - this.props.dispatch(openModal('VIDEO', { media: attachment, status: attachment.get('status'), options: { autoPlay: true } })); + dispatch(openModal('VIDEO', { media: attachment, statusId, options: { autoPlay: true } })); } else if (attachment.get('type') === 'audio') { - this.props.dispatch(openModal('AUDIO', { media: attachment, status: attachment.get('status'), options: { autoPlay: true } })); + dispatch(openModal('AUDIO', { media: attachment, statusId, options: { autoPlay: true } })); } else { const media = attachment.getIn(['status', 'media_attachments']); const index = media.findIndex(x => x.get('id') === attachment.get('id')); - this.props.dispatch(openModal('MEDIA', { media, index, status: attachment.get('status') })); + dispatch(openModal('MEDIA', { media, index, statusId })); } } diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.js b/app/javascript/mastodon/features/picture_in_picture/components/footer.js index 086cda954..1b1ec6d54 100644 --- a/app/javascript/mastodon/features/picture_in_picture/components/footer.js +++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.js @@ -22,6 +22,7 @@ const messages = defineMessages({ favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, + open: { id: 'status.open', defaultMessage: 'Expand this status' }, }); const makeMapStateToProps = () => { @@ -49,11 +50,19 @@ class Footer extends ImmutablePureComponent { intl: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, askReplyConfirmation: PropTypes.bool, + withOpenButton: PropTypes.bool, + onClose: PropTypes.func, }; _performReply = () => { - const { dispatch, status } = this.props; - dispatch(replyCompose(status, this.context.router.history)); + const { dispatch, status, onClose } = this.props; + const { router } = this.context; + + if (onClose) { + onClose(); + } + + dispatch(replyCompose(status, router.history)); }; handleReplyClick = () => { @@ -97,8 +106,20 @@ class Footer extends ImmutablePureComponent { } }; + handleOpenClick = e => { + const { router } = this.context; + + if (e.button !== 0 || !router) { + return; + } + + const { status } = this.props; + + router.history.push(`/statuses/${status.get('id')}`); + } + render () { - const { status, intl } = this.props; + const { status, intl, withOpenButton } = this.props; const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; @@ -130,6 +151,7 @@ class Footer extends ImmutablePureComponent { + {withOpenButton && }
); } diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index cf3a5fa44..c5e7ba776 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -276,22 +276,20 @@ class Status extends ImmutablePureComponent { } handleOpenMedia = (media, index) => { - this.props.dispatch(openModal('MEDIA', { media, index })); + this.props.dispatch(openModal('MEDIA', { statusId: this.props.status.get('id'), media, index })); } handleOpenVideo = (media, options) => { - this.props.dispatch(openModal('VIDEO', { media, options })); + this.props.dispatch(openModal('VIDEO', { statusId: this.props.status.get('id'), media, options })); } handleHotkeyOpenMedia = e => { - const status = this._properStatus(); + const { status } = this.props; e.preventDefault(); if (status.get('media_attachments').size > 0) { - if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { - // TODO: toggle play/paused? - } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { + if (status.getIn(['media_attachments', 0, 'type']) === 'video') { this.handleOpenVideo(status.getIn(['media_attachments', 0]), { startTime: 0 }); } else { this.handleOpenMedia(status.get('media_attachments'), 0); diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index d18f26b4e..bcec19a49 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -4,13 +4,14 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import Video from 'mastodon/features/video'; import classNames from 'classnames'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import IconButton from 'mastodon/components/icon_button'; import ImmutablePureComponent from 'react-immutable-pure-component'; import ImageLoader from './image_loader'; import Icon from 'mastodon/components/icon'; import GIFV from 'mastodon/components/gifv'; import { disableSwiping } from 'mastodon/initial_state'; +import Footer from 'mastodon/features/picture_in_picture/components/footer'; const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, @@ -20,15 +21,121 @@ const messages = defineMessages({ export const previewState = 'previewMediaModal'; +const digitCharacters = [ + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + '#', + '$', + '%', + '*', + '+', + ',', + '-', + '.', + ':', + ';', + '=', + '?', + '@', + '[', + ']', + '^', + '_', + '{', + '|', + '}', + '~', +]; + +const decode83 = (str) => { + let value = 0; + let c, digit; + + for (let i = 0; i < str.length; i++) { + c = str[i]; + digit = digitCharacters.indexOf(c); + value = value * 83 + digit; + } + + return value; +}; + +const decodeRGB = int => ({ + r: Math.max(0, (int >> 16)), + g: Math.max(0, (int >> 8) & 255), + b: Math.max(0, (int & 255)), +}); + export default @injectIntl class MediaModal extends ImmutablePureComponent { static propTypes = { media: ImmutablePropTypes.list.isRequired, - status: ImmutablePropTypes.map, + statusId: PropTypes.string, index: PropTypes.number.isRequired, onClose: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, + onChangeBackgroundColor: PropTypes.func.isRequired, }; static contextTypes = { @@ -67,6 +174,7 @@ class MediaModal extends ImmutablePureComponent { handleChangeIndex = (e) => { const index = Number(e.currentTarget.getAttribute('data-index')); + this.setState({ index: index % this.props.media.size, zoomButtonHidden: true, @@ -100,6 +208,22 @@ class MediaModal extends ImmutablePureComponent { this.props.onClose(); }); } + + this._sendBackgroundColor(); + } + + componentDidUpdate (prevProps, prevState) { + if (prevState.index !== this.state.index) { + this._sendBackgroundColor(); + } + } + + _sendBackgroundColor () { + const { media, onChangeBackgroundColor } = this.props; + const index = this.getIndex(); + const backgroundColor = decodeRGB(decode83(media.getIn([index, 'blurhash']).slice(2, 6))); + + onChangeBackgroundColor(backgroundColor); } componentWillUnmount () { @@ -112,6 +236,8 @@ class MediaModal extends ImmutablePureComponent { this.context.router.history.goBack(); } } + + this.props.onChangeBackgroundColor(null); } getIndex () { @@ -127,30 +253,19 @@ class MediaModal extends ImmutablePureComponent { handleStatusClick = e => { if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); - this.context.router.history.push(`/statuses/${this.props.status.get('id')}`); + this.context.router.history.push(`/statuses/${this.props.statusId}`); } } render () { - const { media, status, intl, onClose } = this.props; + const { media, statusId, intl, onClose } = this.props; const { navigationHidden } = this.state; const index = this.getIndex(); - let pagination = []; const leftNav = media.size > 1 && ; const rightNav = media.size > 1 && ; - if (media.size > 1) { - pagination = media.map((item, i) => { - const classes = ['media-modal__button']; - if (i === index) { - classes.push('media-modal__button--active'); - } - return (
  • ); - }); - } - const content = media.map((image) => { const width = image.getIn(['meta', 'original', 'width']) || null; const height = image.getIn(['meta', 'original', 'height']) || null; @@ -218,13 +333,19 @@ class MediaModal extends ImmutablePureComponent { 'media-modal__navigation--hidden': navigationHidden, }); + let pagination; + + if (media.size > 1) { + pagination = media.map((item, i) => ( + + )); + } + return (
    -
    +
    1 })}> - -
    - )} - -
      - {pagination} -
    +
    + {pagination &&
      {pagination}
    } + {statusId &&
    } +
    ); diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index 5cf70a0cc..3403830e4 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -45,6 +45,10 @@ export default class ModalRoot extends React.PureComponent { onClose: PropTypes.func.isRequired, }; + state = { + backgroundColor: null, + }; + getSnapshotBeforeUpdate () { return { visible: !!this.props.type }; } @@ -59,6 +63,10 @@ export default class ModalRoot extends React.PureComponent { } } + setBackgroundColor = color => { + this.setState({ backgroundColor: color }); + } + renderLoading = modalId => () => { return ['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? : null; } @@ -71,13 +79,14 @@ export default class ModalRoot extends React.PureComponent { render () { const { type, props, onClose } = this.props; + const { backgroundColor } = this.state; const visible = !!type; return ( - + {visible && ( - {(SpecificComponent) => } + {(SpecificComponent) => } )} diff --git a/app/javascript/mastodon/features/ui/components/video_modal.js b/app/javascript/mastodon/features/ui/components/video_modal.js index cce6756c3..2c3c026c8 100644 --- a/app/javascript/mastodon/features/ui/components/video_modal.js +++ b/app/javascript/mastodon/features/ui/components/video_modal.js @@ -3,9 +3,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import Video from 'mastodon/features/video'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { FormattedMessage } from 'react-intl'; -import classNames from 'classnames'; -import Icon from 'mastodon/components/icon'; export const previewState = 'previewVideoModal'; @@ -13,7 +10,7 @@ export default class VideoModal extends ImmutablePureComponent { static propTypes = { media: ImmutablePropTypes.map.isRequired, - status: ImmutablePropTypes.map, + statusId: PropTypes.string, options: PropTypes.shape({ startTime: PropTypes.number, autoPlay: PropTypes.bool, @@ -48,15 +45,8 @@ export default class VideoModal extends ImmutablePureComponent { } } - handleStatusClick = e => { - if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - this.context.router.history.push(`/statuses/${this.props.status.get('id')}`); - } - } - render () { - const { media, status, onClose } = this.props; + const { media, onClose } = this.props; const options = this.props.options || {}; return ( @@ -75,12 +65,6 @@ export default class VideoModal extends ImmutablePureComponent { alt={media.get('description')} />
    - - {status && ( -
    - -
    - )} ); } diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js index 83e638ff6..46eaebd9b 100644 --- a/app/javascript/mastodon/features/video/index.js +++ b/app/javascript/mastodon/features/video/index.js @@ -118,7 +118,6 @@ class Video extends React.PureComponent { deployPictureInPicture: PropTypes.func, intl: PropTypes.object.isRequired, blurhash: PropTypes.string, - link: PropTypes.node, autoPlay: PropTypes.bool, volume: PropTypes.number, muted: PropTypes.bool, @@ -534,7 +533,7 @@ class Video extends React.PureComponent { } render () { - const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link, editable, blurhash } = this.props; + const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, editable, blurhash } = this.props; const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state; const progress = Math.min((currentTime / duration) * 100, 100); const playerStyle = {}; @@ -648,8 +647,6 @@ class Video extends React.PureComponent { {formatTime(Math.floor(duration))} )} - - {link && {link}}
    diff --git a/app/javascript/styles/mastodon/boost.scss b/app/javascript/styles/mastodon/boost.scss index f94c0aa46..fb1451cb2 100644 --- a/app/javascript/styles/mastodon/boost.scss +++ b/app/javascript/styles/mastodon/boost.scss @@ -1,22 +1,32 @@ -button.icon-button i.fa-retweet { - background-image: url("data:image/svg+xml;utf8,"); +button.icon-button { + i.fa-retweet { + background-image: url("data:image/svg+xml;utf8,"); + } - &:hover { + &:hover i.fa-retweet { background-image: url("data:image/svg+xml;utf8,"); } -} -button.icon-button.reblogPrivate i.fa-retweet { - background-image: url("data:image/svg+xml;utf8,"); + &.reblogPrivate { + i.fa-retweet { + background-image: url("data:image/svg+xml;utf8,"); + } - &:hover { - background-image: url("data:image/svg+xml;utf8,"); + &:hover i.fa-retweet { + background-image: url("data:image/svg+xml;utf8,"); + } + } + + &.disabled { + i.fa-retweet, + &:hover i.fa-retweet { + background-image: url("data:image/svg+xml;utf8,"); + } } -} -button.icon-button.disabled i.fa-retweet { - &, - &:hover { - background-image: url("data:image/svg+xml;utf8,"); + .media-modal__overlay .picture-in-picture__footer & { + i.fa-retweet { + background-image: url("data:image/svg+xml;utf8,"); + } } } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index ab2a5ac61..2fc1c12de 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1652,11 +1652,11 @@ a.account__display-name { } } -.star-icon.active { +.icon-button.star-icon.active { color: $gold-star; } -.bookmark-icon.active { +.icon-button.bookmark-icon.active { color: $red-bookmark; } @@ -3007,7 +3007,6 @@ button.icon-button i.fa-retweet { &::before { display: none !important; } - } button.icon-button.active i.fa-retweet { @@ -4487,16 +4486,19 @@ a.status-card.compact:hover { height: 100%; position: relative; - .extended-video-player { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; + &__close, + &__zoom-button { + color: rgba($white, 0.7); - video { - max-width: $media-modal-media-max-width; - max-height: $media-modal-media-max-height; + &:hover, + &:focus, + &:active { + color: $white; + background-color: rgba($white, 0.15); + } + + &:focus { + background-color: rgba($white, 0.3); } } } @@ -4533,10 +4535,10 @@ a.status-card.compact:hover { } .media-modal__nav { - background: rgba($base-overlay-background, 0.5); + background: transparent; box-sizing: border-box; border: 0; - color: $primary-text-color; + color: rgba($primary-text-color, 0.7); cursor: pointer; display: flex; align-items: center; @@ -4547,6 +4549,12 @@ a.status-card.compact:hover { position: absolute; top: 0; bottom: 0; + + &:hover, + &:focus, + &:active { + color: $primary-text-color; + } } .media-modal__nav--left { @@ -4557,58 +4565,86 @@ a.status-card.compact:hover { right: 0; } -.media-modal__pagination { - width: 100%; - text-align: center; +.media-modal__overlay { + max-width: 600px; position: absolute; left: 0; - bottom: 20px; - pointer-events: none; -} + right: 0; + bottom: 0; + margin: 0 auto; -.media-modal__meta { - text-align: center; - position: absolute; - left: 0; - bottom: 20px; - width: 100%; - pointer-events: none; + .picture-in-picture__footer { + border-radius: 0; + background: transparent; + padding: 20px 0; - &--shifted { - bottom: 62px; - } + .icon-button { + color: $white; - a { - pointer-events: auto; - text-decoration: none; - font-weight: 500; - color: $ui-secondary-color; + &:hover, + &:focus, + &:active { + color: $white; + background-color: rgba($white, 0.15); + } - &:hover, - &:focus, - &:active { - text-decoration: underline; + &:focus { + background-color: rgba($white, 0.3); + } + + &.active { + color: $highlight-text-color; + + &:hover, + &:focus, + &:active { + background: rgba($highlight-text-color, 0.15); + } + + &:focus { + background: rgba($highlight-text-color, 0.3); + } + } + + &.star-icon.active { + color: $gold-star; + + &:hover, + &:focus, + &:active { + background: rgba($gold-star, 0.15); + } + + &:focus { + background: rgba($gold-star, 0.3); + } + } } } } -.media-modal__page-dot { - display: inline-block; +.media-modal__pagination { + display: flex; + justify-content: center; + margin-bottom: 20px; } -.media-modal__button { - background-color: $primary-text-color; - height: 12px; - width: 12px; - border-radius: 6px; - margin: 10px; +.media-modal__page-dot { + flex: 0 0 auto; + background-color: $white; + opacity: 0.4; + height: 6px; + width: 6px; + border-radius: 50%; + margin: 0 4px; padding: 0; border: 0; font-size: 0; -} + transition: opacity .2s ease-in-out; -.media-modal__button--active { - background-color: $highlight-text-color; + &.active { + opacity: 1; + } } .media-modal__close { diff --git a/package.json b/package.json index f4ca18e4d..7b8f49dd8 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "babel-runtime": "^6.26.0", "blurhash": "^1.1.3", "classnames": "^2.2.5", + "color-blend": "^3.0.0", "compression-webpack-plugin": "^6.1.1", "cross-env": "^7.0.2", "css-loader": "^5.0.1", diff --git a/yarn.lock b/yarn.lock index d6c175ea7..1a0ef66e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2941,6 +2941,11 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" +color-blend@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/color-blend/-/color-blend-3.0.0.tgz#077073ee59ebce15e084f00590c5bf7577899cb5" + integrity sha512-m21ytRyjsIkVOGG1jrrpijhx7icji0MljlxUoa0ER7lgGW11as0GPLrXQQuMULH1BWJ7OsR11Dy2S6A5lehg5A== + color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" -- cgit From e1a6526c8dccec4464667b422cc2336b28504d2c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 27 Nov 2020 15:41:58 +0100 Subject: Fix media modal regression on public pages (#15221) --- app/javascript/mastodon/containers/media_container.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/javascript/mastodon/containers/media_container.js b/app/javascript/mastodon/containers/media_container.js index ba55ecbc7..afed6868e 100644 --- a/app/javascript/mastodon/containers/media_container.js +++ b/app/javascript/mastodon/containers/media_container.js @@ -30,6 +30,7 @@ export default class MediaContainer extends PureComponent { media: null, index: null, time: null, + backgroundColor: null, }; handleOpenMedia = (media, index) => { @@ -52,7 +53,16 @@ export default class MediaContainer extends PureComponent { document.body.classList.remove('with-modals--active'); document.documentElement.style.marginRight = 0; - this.setState({ media: null, index: null, time: null }); + this.setState({ + media: null, + index: null, + time: null, + backgroundColor: null, + }); + } + + setBackgroundColor = color => { + this.setState({ backgroundColor: color }); } render () { @@ -85,13 +95,14 @@ export default class MediaContainer extends PureComponent { ); })} - + {this.state.media && ( )} -- cgit From e7e099d1a06700d14fa10258f5509fc5c52738c8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 27 Nov 2020 15:48:31 +0100 Subject: Fix deletes not reaching every server that interacted with status (#15200) Extract logic for determining ActivityPub inboxes to send deletes to to its own class and explicitly include the person the status replied to (even if not mentioned), people who favourited it, and people who replied to it (though that one is still not recursive) --- app/lib/status_reach_finder.rb | 52 ++++++++++++++++++++ app/services/remove_status_service.rb | 91 +++++++++++++++++------------------ 2 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 app/lib/status_reach_finder.rb (limited to 'app') diff --git a/app/lib/status_reach_finder.rb b/app/lib/status_reach_finder.rb new file mode 100644 index 000000000..35b191dad --- /dev/null +++ b/app/lib/status_reach_finder.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class StatusReachFinder + def initialize(status) + @status = status + end + + def inboxes + Account.where(id: reached_account_ids).inboxes + end + + private + + def reached_account_ids + [ + replied_to_account_id, + reblog_of_account_id, + mentioned_account_ids, + reblogs_account_ids, + favourites_account_ids, + replies_account_ids, + ].tap do |arr| + arr.flatten! + arr.compact! + arr.uniq! + end + end + + def replied_to_account_id + @status.in_reply_to_account_id + end + + def reblog_of_account_id + @status.reblog.account_id if @status.reblog? + end + + def mentioned_account_ids + @status.mentions.pluck(:account_id) + end + + def reblogs_account_ids + @status.reblogs.pluck(:account_id) + end + + def favourites_account_ids + @status.favourites.pluck(:account_id) + end + + def replies_account_ids + @status.replies.pluck(:account_id) + end +end diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 4f0edc3cf..d6043fb5d 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -9,44 +9,47 @@ class RemoveStatusService < BaseService # @param [Hash] options # @option [Boolean] :redraft # @option [Boolean] :immediate - # @option [Boolean] :original_removed + # @option [Boolean] :original_removed def call(status, **options) @payload = Oj.dump(event: :delete, payload: status.id.to_s) @status = status @account = status.account - @tags = status.tags.pluck(:name).to_a - @mentions = status.active_mentions.includes(:account).to_a - @reblogs = status.reblogs.includes(:account).to_a @options = options RedisLock.acquire(lock_options) do |lock| if lock.acquired? - remove_from_self if status.account.local? + remove_from_self if @account.local? remove_from_followers remove_from_lists - remove_from_affected - remove_reblogs - remove_from_hashtags - remove_from_public - remove_from_media if status.media_attachments.any? - remove_from_spam_check - remove_media + + # There is no reason to send out Undo activities when the + # cause is that the original object has been removed, since + # original object being removed implicitly removes reblogs + # of it. The Delete activity of the original is forwarded + # separately. + if @account.local? && !@options[:original_removed] + remove_from_remote_followers + remove_from_remote_reach + end + + # Since reblogs don't mention anyone, don't get reblogged, + # favourited and don't contain their own media attachments + # or hashtags, this can be skipped + unless @status.reblog? + remove_from_mentions + remove_reblogs + remove_from_hashtags + remove_from_public + remove_from_media if @status.media_attachments.any? + remove_from_spam_check + remove_media + end @status.destroy! if @options[:immediate] || !@status.reported? else raise Mastodon::RaceConditionError end end - - # There is no reason to send out Undo activities when the - # cause is that the original object has been removed, since - # original object being removed implicitly removes reblogs - # of it. The Delete activity of the original is forwarded - # separately. - return if !@account.local? || @options[:original_removed] - - remove_from_remote_followers - remove_from_remote_affected end private @@ -67,31 +70,35 @@ class RemoveStatusService < BaseService end end - def remove_from_affected - @mentions.map(&:account).select(&:local?).each do |account| - redis.publish("timeline:#{account.id}", @payload) + def remove_from_mentions + # For limited visibility statuses, the mentions that determine + # who receives them in their home feed are a subset of followers + # and therefore the delete is already handled by sending it to all + # followers. Here we send a delete to actively mentioned accounts + # that may not follow the account + + @status.active_mentions.find_each do |mention| + redis.publish("timeline:#{mention.account_id}", @payload) end end - def remove_from_remote_affected + def remove_from_remote_reach + return if @status.reblog? + # People who got mentioned in the status, or who # reblogged it from someone else might not follow # the author and wouldn't normally receive the # delete notification - so here, we explicitly # send it to them - target_accounts = (@mentions.map(&:account).reject(&:local?) + @reblogs.map(&:account).reject(&:local?)) - target_accounts << @status.reblog.account if @status.reblog? && !@status.reblog.account.local? - target_accounts.uniq!(&:id) + status_reach_finder = StatusReachFinder.new(@status) - # ActivityPub - ActivityPub::DeliveryWorker.push_bulk(target_accounts.select(&:activitypub?).uniq(&:preferred_inbox_url)) do |target_account| - [signed_activity_json, @account.id, target_account.preferred_inbox_url] + ActivityPub::DeliveryWorker.push_bulk(status_reach_finder.inboxes) do |inbox_url| + [signed_activity_json, @account.id, inbox_url] end end def remove_from_remote_followers - # ActivityPub ActivityPub::DeliveryWorker.push_bulk(@account.followers.inboxes) do |inbox_url| [signed_activity_json, @account.id, inbox_url] end @@ -118,19 +125,19 @@ class RemoveStatusService < BaseService # because once original status is gone, reblogs will disappear # without us being able to do all the fancy stuff - @reblogs.each do |reblog| + @status.reblogs.includes(:account).find_each do |reblog| RemoveStatusService.new.call(reblog, original_removed: true) end end def remove_from_hashtags - @account.featured_tags.where(tag_id: @status.tags.pluck(:id)).each do |featured_tag| + @account.featured_tags.where(tag_id: @status.tags.map(&:id)).each do |featured_tag| featured_tag.decrement(@status.id) end return unless @status.public_visibility? - @tags.each do |hashtag| + @status.tags.map(&:name).each do |hashtag| redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload) redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @payload) if @status.local? end @@ -140,22 +147,14 @@ class RemoveStatusService < BaseService return unless @status.public_visibility? redis.publish('timeline:public', @payload) - if @status.local? - redis.publish('timeline:public:local', @payload) - else - redis.publish('timeline:public:remote', @payload) - end + redis.publish(@status.local? ? 'timeline:public:local' : 'timeline:public:remote', @payload) end def remove_from_media return unless @status.public_visibility? redis.publish('timeline:public:media', @payload) - if @status.local? - redis.publish('timeline:public:local:media', @payload) - else - redis.publish('timeline:public:remote:media', @payload) - end + redis.publish(@status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', @payload) end def remove_media -- cgit From 13206fcfb86844ba4a0c872eaf8c11a61ea848df Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 28 Nov 2020 03:37:01 +0100 Subject: Fix media modal crashing when media has no blurhash (#15229) --- app/javascript/mastodon/features/ui/components/media_modal.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index bcec19a49..58cef1e9d 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -221,9 +221,12 @@ class MediaModal extends ImmutablePureComponent { _sendBackgroundColor () { const { media, onChangeBackgroundColor } = this.props; const index = this.getIndex(); - const backgroundColor = decodeRGB(decode83(media.getIn([index, 'blurhash']).slice(2, 6))); + const blurhash = media.getIn([index, 'blurhash']); - onChangeBackgroundColor(backgroundColor); + if (blurhash) { + const backgroundColor = decodeRGB(decode83(blurhash.slice(2, 6))); + onChangeBackgroundColor(backgroundColor); + } } componentWillUnmount () { -- cgit From 13b07b88f1aa79c31291473362ac77b31602c374 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 28 Nov 2020 05:17:53 +0100 Subject: Fix omniauth (SAML/CAS) sign-in routes not having CSRF protection (#15228) --- Gemfile | 1 + Gemfile.lock | 4 ++++ app/views/auth/sessions/new.html.haml | 3 +-- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/Gemfile b/Gemfile index 950bc59bb..63becb7cc 100644 --- a/Gemfile +++ b/Gemfile @@ -44,6 +44,7 @@ gem 'net-ldap', '~> 0.16' gem 'omniauth-cas', '~> 2.0' gem 'omniauth-saml', '~> 1.10' gem 'omniauth', '~> 1.9' +gem 'omniauth-rails_csrf_protection', '~> 0.1' gem 'color_diff', '~> 0.1' gem 'discard', '~> 1.2' diff --git a/Gemfile.lock b/Gemfile.lock index b8134a985..f7192d084 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -375,6 +375,9 @@ GEM addressable (~> 2.3) nokogiri (~> 1.5) omniauth (~> 1.2) + omniauth-rails_csrf_protection (0.1.2) + actionpack (>= 4.2) + omniauth (>= 1.3.1) omniauth-saml (1.10.3) omniauth (~> 1.3, >= 1.3.2) ruby-saml (~> 1.9) @@ -741,6 +744,7 @@ DEPENDENCIES oj (~> 3.10) omniauth (~> 1.9) omniauth-cas (~> 2.0) + omniauth-rails_csrf_protection (~> 0.1) omniauth-saml (~> 1.10) ox (~> 2.13) paperclip (~> 6.0) diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml index ceb169408..9713bdaeb 100644 --- a/app/views/auth/sessions/new.html.haml +++ b/app/views/auth/sessions/new.html.haml @@ -22,7 +22,6 @@ .actions - resource_class.omniauth_providers.each do |provider| - = link_to omniauth_authorize_path(resource_name, provider), class: "button button-#{provider}" do - = t("auth.providers.#{provider}", default: provider.to_s.chomp("_oauth2").capitalize) + = link_to t("auth.providers.#{provider}", default: provider.to_s.chomp("_oauth2").capitalize), omniauth_authorize_path(resource_name, provider), class: "button button-#{provider}", method: :post .form-footer= render 'auth/shared/links' -- cgit From 68775b60392152d32deda45a261bc1d4f848b44a Mon Sep 17 00:00:00 2001 From: Mélanie Chauvel Date: Mon, 30 Nov 2020 12:09:34 +0100 Subject: Fix character count not ignoring hidden CW field (#15236) --- .../features/compose/components/compose_form.js | 26 +++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'app') diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index 47e189251..8af806ec4 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -77,6 +77,18 @@ class ComposeForm extends ImmutablePureComponent { } } + getFulltextForCharacterCounting = () => { + return [this.props.spoiler? this.props.spoilerText: '', countableText(this.props.text)].join(''); + } + + canSubmit = () => { + const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props; + const fulltext = this.getFulltextForCharacterCounting(); + const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0; + + return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 500 || (isOnlyWhitespace && !anyMedia)); + } + handleSubmit = () => { if (this.props.text !== this.autosuggestTextarea.textarea.value) { // Something changed the text inside the textarea (e.g. browser extensions like Grammarly) @@ -84,11 +96,7 @@ class ComposeForm extends ImmutablePureComponent { this.props.onChange(this.autosuggestTextarea.textarea.value); } - // Submit disabled: - const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props; - const fulltext = [this.props.spoilerText, countableText(this.props.text)].join(''); - - if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > 500 || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) { + if (!this.canSubmit()) { return; } @@ -178,10 +186,8 @@ class ComposeForm extends ImmutablePureComponent { } render () { - const { intl, onPaste, showSearch, anyMedia } = this.props; + const { intl, onPaste, showSearch } = this.props; const disabled = this.props.isSubmitting; - const text = [this.props.spoilerText, countableText(this.props.text)].join(''); - const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > 500 || (text.length !== 0 && text.trim().length === 0 && !anyMedia); let publishText = ''; if (this.props.privacy === 'private' || this.props.privacy === 'direct') { @@ -243,11 +249,11 @@ class ComposeForm extends ImmutablePureComponent {
    -
    +
    -
    +
    ); -- cgit From a55e6e99c0a8e757eca54ceb1b0496c7124ec9c4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 30 Nov 2020 23:02:32 +0100 Subject: Fix `ku` locale not being right-to-left (#15252) --- app/helpers/application_helper.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8914b015c..bf5742d34 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -7,6 +7,13 @@ module ApplicationHelper follow ).freeze + RTL_LOCALES = %i( + ar + fa + he + ku + ).freeze + def active_nav_class(*paths) paths.any? { |path| current_page?(path) } ? 'active' : '' end @@ -44,7 +51,7 @@ module ApplicationHelper end def locale_direction - if [:ar, :fa, :he].include?(I18n.locale) + if RTL_LOCALES.include?(I18n.locale) 'rtl' else 'ltr' -- cgit From 9136be480f9660dcdf0e5a17ed929e2eb5ee650c Mon Sep 17 00:00:00 2001 From: ThibG Date: Wed, 2 Dec 2020 21:20:00 +0100 Subject: Fix followers hash cache not being invalidated on account merge (#15256) Also clear relationships cache. --- app/models/concerns/account_merging.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'app') diff --git a/app/models/concerns/account_merging.rb b/app/models/concerns/account_merging.rb index 691d02e03..45050c269 100644 --- a/app/models/concerns/account_merging.rb +++ b/app/models/concerns/account_merging.rb @@ -39,5 +39,10 @@ module AccountMerging end end end + + # Some follow relationships have moved, so the cache is stale + Rails.cache.delete_matched("followers_hash:#{id}:*") + Rails.cache.delete_matched("relationships:#{id}:*") + Rails.cache.delete_matched("relationships:*:#{id}") end end -- cgit From d849aad85206bff2058fbbd9e187b0048c793cb9 Mon Sep 17 00:00:00 2001 From: ThibG Date: Wed, 2 Dec 2020 21:21:44 +0100 Subject: Change public thread view to hide "Show thread" link (#15266) Fixes #15262 --- app/views/statuses/_simple_status.html.haml | 5 ++++- app/views/statuses/_status.html.haml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index 192192700..d60ade22f 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -1,3 +1,6 @@ +:ruby + hide_show_thread ||= false + .status{ class: "status-#{status.visibility}" } .status__info = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener noreferrer' do @@ -47,7 +50,7 @@ - elsif status.preview_card = react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json - - if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id + - if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id && !hide_show_thread = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__content__read-more-button', target: stream_link_target, rel: 'noopener noreferrer' do = t 'statuses.show_thread' diff --git a/app/views/statuses/_status.html.haml b/app/views/statuses/_status.html.haml index 650f9b679..13a06519c 100644 --- a/app/views/statuses/_status.html.haml +++ b/app/views/statuses/_status.html.haml @@ -39,7 +39,7 @@ %span = t('stream_entries.pinned') - = render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay + = render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay, hide_show_thread: is_predecessor || is_successor - if include_threads - if @since_descendant_thread_id -- cgit From fbe75192436e81b778319c597e0461d0415b5c6f Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 4 Dec 2020 02:23:51 +0100 Subject: Fix account merging in maintenance script (#15264) Also include AccountNote and AccountDeletionRequest to the list of classes needing to be reassigned the merged account. --- app/models/concerns/account_merging.rb | 7 ++++-- lib/mastodon/maintenance_cli.rb | 45 +++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/models/concerns/account_merging.rb b/app/models/concerns/account_merging.rb index 45050c269..c3b7018f2 100644 --- a/app/models/concerns/account_merging.rb +++ b/app/models/concerns/account_merging.rb @@ -15,7 +15,7 @@ module AccountMerging Status, StatusPin, MediaAttachment, Poll, Report, Tombstone, Favourite, Follow, FollowRequest, Block, Mute, AccountIdentityProof, AccountModerationNote, AccountPin, AccountStat, ListAccount, - PollVote, Mention + PollVote, Mention, AccountDeletionRequest, AccountNote ] owned_classes.each do |klass| @@ -28,7 +28,10 @@ module AccountMerging end end - target_classes = [Follow, FollowRequest, Block, Mute, AccountModerationNote, AccountPin] + target_classes = [ + Follow, FollowRequest, Block, Mute, AccountModerationNote, AccountPin, + AccountNote + ] target_classes.each do |klass| klass.where(target_account_id: other_account.id).find_each do |record| diff --git a/lib/mastodon/maintenance_cli.rb b/lib/mastodon/maintenance_cli.rb index 547238ec6..99d13f43d 100644 --- a/lib/mastodon/maintenance_cli.rb +++ b/lib/mastodon/maintenance_cli.rb @@ -55,8 +55,8 @@ module Mastodon belongs_to :account, inverse_of: :account_stat end + # Dummy class, to make migration possible across version changes class Account < ApplicationRecord - # Dummy class, to make migration possible across version changes has_one :user, inverse_of: :account has_one :account_stat, inverse_of: :account @@ -69,6 +69,49 @@ module Mastodon def acct local? ? username : "#{username}@#{domain}" end + + # This is a duplicate of the AccountMerging concern because we need it to + # be independent from code version. + def merge_with!(other_account) + # Since it's the same remote resource, the remote resource likely + # already believes we are following/blocking, so it's safe to + # re-attribute the relationships too. However, during the presence + # of the index bug users could have *also* followed the reference + # account already, therefore mass update will not work and we need + # to check for (and skip past) uniqueness errors + + owned_classes = [ + Status, StatusPin, MediaAttachment, Poll, Report, Tombstone, Favourite, + Follow, FollowRequest, Block, Mute, AccountIdentityProof, + AccountModerationNote, AccountPin, AccountStat, ListAccount, + PollVote, Mention + ] + owned_classes << AccountDeletionRequest if ActiveRecord::Base.connection.table_exists?(:account_deletion_requests) + owned_classes << AccountNote if ActiveRecord::Base.connection.table_exists?(:account_notes) + + owned_classes.each do |klass| + klass.where(account_id: other_account.id).find_each do |record| + begin + record.update_attribute(:account_id, id) + rescue ActiveRecord::RecordNotUnique + next + end + end + end + + target_classes = [Follow, FollowRequest, Block, Mute, AccountModerationNote, AccountPin] + target_classes << AccountNote if ActiveRecord::Base.connection.table_exists?(:account_notes) + + target_classes.each do |klass| + klass.where(target_account_id: other_account.id).find_each do |record| + begin + record.update_attribute(:target_account_id, id) + rescue ActiveRecord::RecordNotUnique + next + end + end + end + end end class User < ApplicationRecord -- cgit From 44d5c6bc8ffd92cd201380dabe35748e50b6af68 Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Fri, 4 Dec 2020 12:22:35 +0900 Subject: Fix incorrect conditions for suspended accounts in Get API for account featured tags (#15270) --- app/controllers/api/v1/accounts/featured_tags_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/controllers/api/v1/accounts/featured_tags_controller.rb b/app/controllers/api/v1/accounts/featured_tags_controller.rb index 014d71956..dc01b577c 100644 --- a/app/controllers/api/v1/accounts/featured_tags_controller.rb +++ b/app/controllers/api/v1/accounts/featured_tags_controller.rb @@ -17,6 +17,6 @@ class Api::V1::Accounts::FeaturedTagsController < Api::BaseController end def set_featured_tags - @featured_tags = @account.suspended? ? @account.featured_tags : [] + @featured_tags = @account.suspended? ? [] : @account.featured_tags end end -- cgit