From 2ad0b32dd5c10bf4194f0a2c46c7fad08a35ea4c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 9 Mar 2026 05:31:56 -0400 Subject: [PATCH 001/203] Add basic ES-enabled index/service coverage (#38097) --- spec/chewy/accounts_index_spec.rb | 9 +++++++ spec/chewy/public_statuses_index_spec.rb | 9 +++++++ spec/chewy/statuses_index_spec.rb | 9 +++++++ spec/chewy/tags_index_spec.rb | 9 +++++++ spec/services/account_search_service_spec.rb | 12 +++++++++ spec/services/statuses_search_service_spec.rb | 22 ++++++++++++++++ spec/services/tag_search_service_spec.rb | 25 +++++++++++++------ 7 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 spec/services/statuses_search_service_spec.rb diff --git a/spec/chewy/accounts_index_spec.rb b/spec/chewy/accounts_index_spec.rb index f7b5b2e249b6fa..3e5f38a408df02 100644 --- a/spec/chewy/accounts_index_spec.rb +++ b/spec/chewy/accounts_index_spec.rb @@ -3,6 +3,15 @@ require 'rails_helper' RSpec.describe AccountsIndex do + context 'when elasticsearch is enabled', :search do + describe 'indexing records' do + it 'indexes records from scope' do + expect { Fabricate :account } + .to change(described_class, :count).by(1) + end + end + end + describe 'Searching the index' do before do mock_elasticsearch_response(described_class, raw_response) diff --git a/spec/chewy/public_statuses_index_spec.rb b/spec/chewy/public_statuses_index_spec.rb index 6bc08832f33992..f9786772605ffd 100644 --- a/spec/chewy/public_statuses_index_spec.rb +++ b/spec/chewy/public_statuses_index_spec.rb @@ -3,6 +3,15 @@ require 'rails_helper' RSpec.describe PublicStatusesIndex do + context 'when elasticsearch is enabled', :search do + describe 'indexing records' do + it 'indexes records from scope' do + expect { Fabricate :status, visibility: :public } + .to change(described_class, :count).by(1) + end + end + end + describe 'Searching the index' do before do mock_elasticsearch_response(described_class, raw_response) diff --git a/spec/chewy/statuses_index_spec.rb b/spec/chewy/statuses_index_spec.rb index e3899f3a1767a5..78c86b4387cb7e 100644 --- a/spec/chewy/statuses_index_spec.rb +++ b/spec/chewy/statuses_index_spec.rb @@ -3,6 +3,15 @@ require 'rails_helper' RSpec.describe StatusesIndex do + context 'when elasticsearch is enabled', :search do + describe 'indexing records' do + it 'indexes records from scope' do + expect { Fabricate :status } + .to change(described_class, :count).by(1) + end + end + end + describe 'Searching the index' do before do mock_elasticsearch_response(described_class, raw_response) diff --git a/spec/chewy/tags_index_spec.rb b/spec/chewy/tags_index_spec.rb index 6b57da65e4d737..fac5f2914402d2 100644 --- a/spec/chewy/tags_index_spec.rb +++ b/spec/chewy/tags_index_spec.rb @@ -3,6 +3,15 @@ require 'rails_helper' RSpec.describe TagsIndex do + context 'when elasticsearch is enabled', :search do + describe 'indexing records' do + it 'indexes records from scope' do + expect { Fabricate :tag, listable: true } + .to change(described_class, :count).by(1) + end + end + end + describe 'Searching the index' do before do mock_elasticsearch_response(described_class, raw_response) diff --git a/spec/services/account_search_service_spec.rb b/spec/services/account_search_service_spec.rb index 7d251641ee45db..6295d2f8ffffbe 100644 --- a/spec/services/account_search_service_spec.rb +++ b/spec/services/account_search_service_spec.rb @@ -86,4 +86,16 @@ expect(results).to eq [] end end + + context 'when elasticsearch is enabled', :search do + it 'returns matching accounts' do + account = Fabricate(:account, username: 'matchingusername') + + AccountsIndex.import! + + results = subject.call('match', nil, limit: 5) + + expect(results).to eq [account] + end + end end diff --git a/spec/services/statuses_search_service_spec.rb b/spec/services/statuses_search_service_spec.rb new file mode 100644 index 00000000000000..982b67ac870f6b --- /dev/null +++ b/spec/services/statuses_search_service_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe StatusesSearchService do + describe '#call' do + let!(:status) { Fabricate(:status, text: 'status number one') } + let(:results) { subject.call('one', status.account, limit: 5) } + + before { Fabricate(:status, text: 'status number two') } + + context 'when elasticsearch is enabled', :search do + it 'runs a search for statuses' do + expect(results) + .to have_attributes( + size: 1, + first: eq(status) + ) + end + end + end +end diff --git a/spec/services/tag_search_service_spec.rb b/spec/services/tag_search_service_spec.rb index de42e54071e823..4339424a1ace36 100644 --- a/spec/services/tag_search_service_spec.rb +++ b/spec/services/tag_search_service_spec.rb @@ -5,17 +5,28 @@ RSpec.describe TagSearchService do describe '#call' do let!(:one) { Fabricate(:tag, name: 'one') } + let(:results) { subject.call('#one', limit: 5) } before { Fabricate(:tag, name: 'two') } - it 'runs a search for tags' do - results = subject.call('#one', limit: 5) + context 'with postgres search' do + it 'runs a search for tags' do + expect(results) + .to have_attributes( + size: 1, + first: eq(one) + ) + end + end - expect(results) - .to have_attributes( - size: 1, - first: eq(one) - ) + context 'when elasticsearch is enabled', :search do + it 'runs a search for tags' do + expect(results) + .to have_attributes( + size: 1, + first: eq(one) + ) + end end end end From 6a6a97f314951979a0e57487f80570d0784cb1d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 10:34:01 +0100 Subject: [PATCH 002/203] Update dependency json-schema to v6.2.0 (#38093) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4096fb39d1d08f..69a77098dd9ba5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -372,7 +372,7 @@ GEM json-ld-preloaded (3.3.2) json-ld (~> 3.3) rdf (~> 3.3) - json-schema (6.1.0) + json-schema (6.2.0) addressable (~> 2.8) bigdecimal (>= 3.1, < 5) jsonapi-renderer (0.2.2) From 5090036edf83f820555796737a79ef23e560690d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 10:34:19 +0100 Subject: [PATCH 003/203] Update dependency hiredis-client to v0.27.0 (#38092) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 69a77098dd9ba5..729db3455c73a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -304,8 +304,8 @@ GEM highline (3.1.2) reline hiredis (0.6.3) - hiredis-client (0.26.4) - redis-client (= 0.26.4) + hiredis-client (0.27.0) + redis-client (= 0.27.0) hkdf (0.3.0) htmlentities (4.4.2) http (5.3.1) @@ -709,7 +709,7 @@ GEM reline redcarpet (3.6.1) redis (4.8.1) - redis-client (0.26.4) + redis-client (0.27.0) connection_pool regexp_parser (2.11.3) reline (0.6.3) From eabbda3148b9b72873a050860c30768ff9c5a570 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 9 Mar 2026 05:34:58 -0400 Subject: [PATCH 004/203] Fix `Style/SelectByKind` cop (#38084) --- app/lib/request.rb | 2 +- spec/requests/activitypub/replies_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/lib/request.rb b/app/lib/request.rb index 81e59fb2ec6891..cc741f212d5e35 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -295,7 +295,7 @@ def open(host, *args) Resolv::DNS.open do |dns| dns.timeouts = 5 addresses = dns.getaddresses(host) - addresses = addresses.filter { |addr| addr.is_a?(Resolv::IPv6) }.take(2) + addresses.filter { |addr| !addr.is_a?(Resolv::IPv6) }.take(2) + addresses = addresses.grep(Resolv::IPv6).take(2) + addresses.grep_v(Resolv::IPv6).take(2) end end diff --git a/spec/requests/activitypub/replies_spec.rb b/spec/requests/activitypub/replies_spec.rb index 02832c049a28f2..95bc1afe61dd1a 100644 --- a/spec/requests/activitypub/replies_spec.rb +++ b/spec/requests/activitypub/replies_spec.rb @@ -252,13 +252,13 @@ def inlined_replies response .parsed_body[:first][:items] - .select { |x| x.is_a?(Hash) } + .grep(Hash) end def remote_replies response .parsed_body[:first][:items] - .reject { |x| x.is_a?(Hash) } + .grep_v(Hash) end def parsed_uri_query_values(uri) From d7a1bead51353066f156a484b3a1c900b77e5c86 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 09:35:20 +0000 Subject: [PATCH 005/203] Update dependency aws-sdk-s3 to v1.215.0 (#38067) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 729db3455c73a4..e867009e83b0fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -111,8 +111,8 @@ GEM aws-sdk-kms (1.122.0) aws-sdk-core (~> 3, >= 3.241.4) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.213.0) - aws-sdk-core (~> 3, >= 3.241.4) + aws-sdk-s3 (1.215.0) + aws-sdk-core (~> 3, >= 3.243.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) aws-sigv4 (1.12.1) From 3b1705a406e791fa598a946abe4d9355af53cba6 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 9 Mar 2026 05:38:00 -0400 Subject: [PATCH 006/203] Add `TermsOfService#usable_effective_date` to DRY up view logic (#38095) --- app/models/terms_of_service.rb | 4 ++++ .../show.html.haml | 7 +++++-- .../terms_of_service_changed.html.haml | 2 +- .../terms_of_service_changed.text.erb | 2 +- spec/models/terms_of_service_spec.rb | 18 ++++++++++++++++++ 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/app/models/terms_of_service.rb b/app/models/terms_of_service.rb index 41afaf10d9cc75..7fb025ea3fd878 100644 --- a/app/models/terms_of_service.rb +++ b/app/models/terms_of_service.rb @@ -31,6 +31,10 @@ def self.current live.first || upcoming.first # For the case when none of the published terms have become effective yet end + def usable_effective_date + effective_date || Time.zone.today + end + def published? published_at.present? end diff --git a/app/views/terms_of_service_interstitial/show.html.haml b/app/views/terms_of_service_interstitial/show.html.haml index c7fb3595f3b42a..4fed5eba932661 100644 --- a/app/views/terms_of_service_interstitial/show.html.haml +++ b/app/views/terms_of_service_interstitial/show.html.haml @@ -6,8 +6,11 @@ .simple_form %h1.title= t('terms_of_service_interstitial.title', domain: site_hostname) - - effective_date = @terms_of_service.effective_date || Time.zone.today - %p.lead= effective_date.past? ? t('terms_of_service_interstitial.past_preamble_html') : t('terms_of_service_interstitial.future_preamble_html', date: l(effective_date)) + %p.lead< + - if @terms_of_service.usable_effective_date.past? + = t('terms_of_service_interstitial.past_preamble_html') + - else + = t('terms_of_service_interstitial.future_preamble_html', date: l(@terms_of_service.usable_effective_date)) %p.lead= t('user_mailer.terms_of_service_changed.agreement', domain: site_hostname) diff --git a/app/views/user_mailer/terms_of_service_changed.html.haml b/app/views/user_mailer/terms_of_service_changed.html.haml index 2e34eb49901f97..efa4911743931a 100644 --- a/app/views/user_mailer/terms_of_service_changed.html.haml +++ b/app/views/user_mailer/terms_of_service_changed.html.haml @@ -9,7 +9,7 @@ %table.email-inner-card-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } %tr %td.email-inner-card-td.email-prose - %p= t('user_mailer.terms_of_service_changed.description_html', path: terms_of_service_version_url(date: @terms_of_service.effective_date), domain: site_hostname, date: l(@terms_of_service.effective_date || Time.zone.today)) + %p= t('user_mailer.terms_of_service_changed.description_html', path: terms_of_service_version_url(date: @terms_of_service.effective_date), domain: site_hostname, date: l(@terms_of_service.usable_effective_date)) %p %strong= t('user_mailer.terms_of_service_changed.changelog') = markdown(@terms_of_service.changelog) diff --git a/app/views/user_mailer/terms_of_service_changed.text.erb b/app/views/user_mailer/terms_of_service_changed.text.erb index ccf332ce8970b8..933be1122c17a8 100644 --- a/app/views/user_mailer/terms_of_service_changed.text.erb +++ b/app/views/user_mailer/terms_of_service_changed.text.erb @@ -2,7 +2,7 @@ === -<%= t('user_mailer.terms_of_service_changed.description', domain: site_hostname, date: l(@terms_of_service.effective_date || Time.zone.today)) %> +<%= t('user_mailer.terms_of_service_changed.description', domain: site_hostname, date: l(@terms_of_service.usable_effective_date)) %> => <%= terms_of_service_version_url(date: @terms_of_service.effective_date) %> diff --git a/spec/models/terms_of_service_spec.rb b/spec/models/terms_of_service_spec.rb index 16cd5dd2ebfd17..99a7caa6131028 100644 --- a/spec/models/terms_of_service_spec.rb +++ b/spec/models/terms_of_service_spec.rb @@ -108,6 +108,24 @@ end end + describe '#usable_effective_date' do + subject { terms_of_service.usable_effective_date } + + let(:terms_of_service) { Fabricate.build(:terms_of_service, effective_date:) } + + context 'when effective_date value is set' do + let(:effective_date) { 5.days.ago } + + it { is_expected.to eq(effective_date.to_date) } + end + + context 'when effective_date value is not set' do + let(:effective_date) { nil } + + it { is_expected.to eq(Time.zone.today) } + end + end + describe '::current' do context 'when no terms exist' do it 'returns nil' do From 5838fce8f4c7b44cad3276452f42acbdcd79684d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 09:50:28 +0000 Subject: [PATCH 007/203] New Crowdin Translations (automated) (#38100) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/cy.json | 29 ++++++++++ app/javascript/mastodon/locales/da.json | 12 ++++ app/javascript/mastodon/locales/de.json | 24 ++++++-- app/javascript/mastodon/locales/el.json | 12 ++++ app/javascript/mastodon/locales/en-GB.json | 12 ++++ app/javascript/mastodon/locales/eo.json | 16 +++++- app/javascript/mastodon/locales/es-AR.json | 12 ++++ app/javascript/mastodon/locales/es-MX.json | 38 ++++++++++++ app/javascript/mastodon/locales/es.json | 40 ++++++++++++- app/javascript/mastodon/locales/fi.json | 10 ++++ app/javascript/mastodon/locales/fr-CA.json | 35 +++++++++++ app/javascript/mastodon/locales/fr.json | 35 +++++++++++ app/javascript/mastodon/locales/ga.json | 15 +++++ app/javascript/mastodon/locales/gl.json | 12 ++++ app/javascript/mastodon/locales/he.json | 35 +++++++++++ app/javascript/mastodon/locales/is.json | 19 ++++++ app/javascript/mastodon/locales/it.json | 15 +++++ app/javascript/mastodon/locales/ko.json | 3 +- app/javascript/mastodon/locales/nan-TW.json | 43 ++++++++++++++ app/javascript/mastodon/locales/pt-BR.json | 30 ++++++++++ app/javascript/mastodon/locales/pt-PT.json | 14 +++++ app/javascript/mastodon/locales/sq.json | 11 ++++ app/javascript/mastodon/locales/sv.json | 64 +++++++++++++++++++++ app/javascript/mastodon/locales/szl.json | 36 +++++++++--- app/javascript/mastodon/locales/tr.json | 15 +++++ app/javascript/mastodon/locales/vi.json | 12 ++++ app/javascript/mastodon/locales/zh-CN.json | 35 +++++++++++ app/javascript/mastodon/locales/zh-TW.json | 12 ++++ config/locales/eo.yml | 2 +- config/locales/fi.yml | 4 +- config/locales/nan-TW.yml | 9 +++ config/locales/pt-BR.yml | 2 + config/locales/pt-PT.yml | 3 + config/locales/simple_form.de.yml | 2 +- config/locales/simple_form.eo.yml | 2 +- 35 files changed, 645 insertions(+), 25 deletions(-) diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index abd60569696c26..3bd13587c21291 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -151,13 +151,25 @@ "account_edit.button.edit": "Golygu {item}", "account_edit.column_button": "Gorffen", "account_edit.column_title": "Golygu Proffil", + "account_edit.custom_fields.name": "maes", "account_edit.custom_fields.placeholder": "Ychwanegwch eich rhagenwau, dolenni allanol, neu unrhyw beth arall hoffech ei rannu.", + "account_edit.custom_fields.tip_content": "Gallwch chi ychwanegu hygrededd at eich cyfrif Mastodon yn hawdd trwy wirio dolenni i unrhyw wefannau rydych chi'n berchen arnyn nhw.", + "account_edit.custom_fields.tip_title": "Awgrym: Ychwanegu dolenni wedi'u gwirio", "account_edit.custom_fields.title": "Meysydd cyfaddas", + "account_edit.custom_fields.verified_hint": "Sut ydw i'n ychwanegu dolen wedi'i gwirio?", "account_edit.display_name.placeholder": "Eich enw dangos yw sut mae'ch enw'n ymddangos ar eich proffil ac mewn llinellau amser.", "account_edit.display_name.title": "Enw dangos", "account_edit.featured_hashtags.item": "hashnodau", "account_edit.featured_hashtags.placeholder": "Helpwch eraill i adnabod, a chael mynediad cyflym at eich hoff bynciau.", "account_edit.featured_hashtags.title": "Hashnodau dan sylw", + "account_edit.field_delete_modal.confirm": "Ydych chi'n siŵr eich bod chi eisiau dileu'r maes cyfaddas hwn? Does dim modd dadwneud y weithred hon.", + "account_edit.field_delete_modal.delete_button": "Dileu", + "account_edit.field_delete_modal.title": "Dileu maes cyfaddas?", + "account_edit.field_edit_modal.add_title": "Ychwanegu maes cyfaddas", + "account_edit.field_edit_modal.edit_title": "Golygu maes cyfaddas", + "account_edit.field_edit_modal.name_hint": "e.e. “Gwefan bersonol”", + "account_edit.field_edit_modal.value_hint": "e.e. “enghraifft.fi”", + "account_edit.field_edit_modal.value_label": "Gwerth", "account_edit.name_modal.add_title": "Ychwanegu enw dangos", "account_edit.name_modal.edit_title": "Golygu enw dangos", "account_edit.profile_tab.button_label": "Cyfaddasu", @@ -172,6 +184,14 @@ "account_edit.profile_tab.subtitle": "Cyfaddaswch y tabiau ar eich proffil a'r hyn maen nhw'n ei ddangos.", "account_edit.profile_tab.title": "Gosodiadau tab proffil", "account_edit.save": "Cadw", + "account_edit.verified_modal.details": "Ychwanegwch hygrededd at eich proffil Mastodon trwy wirio dolenni i wefannau personol. Dyma sut mae'n gweithio:", + "account_edit.verified_modal.invisible_link.details": "Ychwanegwch y ddolen at eich pennyn. Y rhan bwysig yw rel=\"me\" sy'n atal dynwared ar wefannau gyda chynnwys sy'n cael ei gynhyrchu gan ddefnyddwyr. Gallwch hyd yn oed ddefnyddio tag dolen ym mhennyn y dudalen yn lle {tag}, ond rhaid bod yr HTML yn hygyrch ac heb weithredu JavaScript.", + "account_edit.verified_modal.invisible_link.summary": "Sut ydw i'n gwneud y ddolen yn anweledig?", + "account_edit.verified_modal.step1.header": "Copïwch y cod HTML isod a'i gludo i bennyn eich gwefan", + "account_edit.verified_modal.step2.details": "Os ydych chi eisoes wedi ychwanegu eich gwefan fel maes cyfaddas, bydd angen i chi ei dileu a'i hail-ychwanegu i cychwyn dilysu.", + "account_edit.verified_modal.step2.header": "Ychwanegwch eich gwefan fel maes cyfaddas", + "account_edit.verified_modal.title": "Sut i ychwanegu dolen wedi'i gwirio", + "account_edit_tags.add_tag": "Ychwanegu #{tagName}", "account_edit_tags.column_title": "Golygu hashnodau dan sylw", "account_edit_tags.help_text": "Mae hashnodau dan sylw yn helpu defnyddwyr i ddarganfod a rhyngweithio â'ch proffil. Maen nhw'n ymddangos fel hidlwyr ar olwg Gweithgaredd eich tudalen Proffil.", "account_edit_tags.search_placeholder": "Rhowch hashnod…", @@ -274,6 +294,8 @@ "callout.dismiss": "Cau", "carousel.current": "Sleid {current, number} / {max, number}", "carousel.slide": "Sleid {current, number} o {max, number}", + "character_counter.recommended": "{currentLength}/{maxLength} nod sy'n cael eu hargymell", + "character_counter.required": "{currentLength}/{maxLength} nod", "closed_registrations.other_server_instructions": "Gan fod Mastodon yn ddatganoledig, gallwch greu cyfrif ar weinydd arall a dal i ryngweithio gyda hwn.", "closed_registrations_modal.description": "Ar hyn o bryd nid yw'n bosib creu cyfrif ar {domain}, ond cadwch mewn cof nad oes raid i chi gael cyfrif yn benodol ar {domain} i ddefnyddio Mastodon.", "closed_registrations_modal.find_another_server": "Dod o hyd i weinydd arall", @@ -304,9 +326,12 @@ "collections.delete_collection": "Dileu casgliad", "collections.description_length_hint": "Terfyn o 100 nod", "collections.detail.accounts_heading": "Cyfrifon", + "collections.detail.author_added_you": "Ychwanegodd {author} chi at y casgliad hwn", "collections.detail.curated_by_author": "Wedi'i guradu gan {author}", "collections.detail.curated_by_you": "Wedi'i guradu gennych chi", "collections.detail.loading": "Yn llwytho casgliad…", + "collections.detail.other_accounts_in_collection": "Eraill yn y casgliad hwn:", + "collections.detail.sensitive_note": "Mae'r casgliad hwn yn cynnwys cyfrifon a chynnwys a allai fod yn sensitif i rai defnyddwyr.", "collections.detail.share": "Rhannu'r casgliad hwn", "collections.edit_details": "Golygu manylion", "collections.error_loading_collections": "Bu gwall wrth geisio llwytho eich casgliadau.", @@ -766,6 +791,7 @@ "navigation_bar.automated_deletion": "Dileu postiadau'n awtomatig", "navigation_bar.blocks": "Defnyddwyr wedi'u rhwystro", "navigation_bar.bookmarks": "Nodau Tudalen", + "navigation_bar.collections": "Casgliadau", "navigation_bar.direct": "Crybwylliadau preifat", "navigation_bar.domain_blocks": "Parthau wedi'u rhwystro", "navigation_bar.favourites": "Ffefrynnau", @@ -1069,6 +1095,9 @@ "sign_in_banner.mastodon_is": "Mastodon yw'r ffordd orau o gadw i fyny â'r hyn sy'n digwydd.", "sign_in_banner.sign_in": "Mewngofnodi", "sign_in_banner.sso_redirect": "Mewngofnodi neu Gofrestru", + "skip_links.hotkey": "Allwedd boeth {hotkey}", + "skip_links.skip_to_content": "Symud i'r prif gynnwys", + "skip_links.skip_to_navigation": "Symud i'r prif lywio", "status.admin_account": "Agor rhyngwyneb cymedroli @{name}", "status.admin_domain": "Agor rhyngwyneb cymedroli {domain}", "status.admin_status": "Agor y postiad hwn yn y rhyngwyneb cymedroli", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 12f05b0324aad9..a294b3810caf15 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Rediger profil", "account_edit.custom_fields.name": "felt", "account_edit.custom_fields.placeholder": "Tilføj dine pronominer, eksterne links eller andet, du gerne vil dele.", + "account_edit.custom_fields.reorder_button": "Omsorter felter", "account_edit.custom_fields.tip_content": "Du kan nemt øge troværdigheden af din Mastodon-konto ved at verificere links til alle websteder, du ejer.", "account_edit.custom_fields.tip_title": "Tip: Tilføjelse af bekræftede links", "account_edit.custom_fields.title": "Brugerdefinerede felter", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Slet brugerdefineret felt?", "account_edit.field_edit_modal.add_title": "Tilføj brugerdefineret felt", "account_edit.field_edit_modal.edit_title": "Rediger brugerdefineret felt", + "account_edit.field_edit_modal.limit_header": "Anbefalet tegngrænse overskredet", + "account_edit.field_edit_modal.limit_message": "Mobilbrugere kan muligvis ikke se hele dit felt.", + "account_edit.field_edit_modal.link_emoji_warning": "Vi fraråder brug af brugerdefinerede emoji i kombination med url'er. Brugerdefinerede felter, der indeholder begge dele, vises kun som tekst i stedet for som et link for at undgå forvirring hos brugerne.", "account_edit.field_edit_modal.name_hint": "F.eks. “Personligt websted”", "account_edit.field_edit_modal.name_label": "Etiket", "account_edit.field_edit_modal.value_hint": "F.eks. “eksempel.me”", "account_edit.field_edit_modal.value_label": "Værdi", + "account_edit.field_reorder_modal.drag_cancel": "Trækningen blev annulleret. Feltet \"{item}\" blev sluppet.", + "account_edit.field_reorder_modal.drag_end": "Feltet \"{item}\" blev sluppet.", + "account_edit.field_reorder_modal.drag_instructions": "For at omarrangere brugerdefinerede felter skal du trykke på mellemrumstasten eller Enter. Mens du trækker, kan du bruge piletasterne til at flytte feltet op eller ned. Tryk på mellemrumstasten eller Enter igen for at placere feltet i sin nye position, eller tryk på Escape for at annullere.", + "account_edit.field_reorder_modal.drag_move": "Feltet \"{item}\" blev flyttet.", + "account_edit.field_reorder_modal.drag_over": "Feltet \"{item}\" blev flyttet over \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Samlede feltet \"{item}\" op.", + "account_edit.field_reorder_modal.handle_label": "Træk feltet \"{item}\"", + "account_edit.field_reorder_modal.title": "Omarrangér felter", "account_edit.name_modal.add_title": "Tilføj visningsnavn", "account_edit.name_modal.edit_title": "Rediger visningsnavn", "account_edit.profile_tab.button_label": "Tilpas", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index b3426e51ccc4f2..4d6e9ff55baeb6 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -107,7 +107,7 @@ "account.mutual": "Ihr folgt einander", "account.name.help.domain": "{domain} ist der Server, auf dem das Profil registriert ist und die Beiträge verwaltet werden.", "account.name.help.domain_self": "{domain} ist der Server, auf dem du registriert bist und deine Beiträge verwaltet werden.", - "account.name.help.footer": "So wie du E-Mails an andere trotz unterschiedlicher E-Mail-Clients senden kannst, so kannst du auch mit anderen Profilen auf unterschiedlichen Mastodon-Servern interagieren. Wenn andere soziale Apps die gleichen Kommunikationsregeln (das ActivityPub-Protokoll) wie Mastodon verwenden, dann funktioniert die Kommunikation auch dort.", + "account.name.help.footer": "So wie du E-Mails an andere trotz unterschiedlicher E-Mail-Provider senden kannst, so kannst du auch mit anderen Profilen auf unterschiedlichen Mastodon-Servern interagieren. Wenn andere soziale Apps die gleichen Kommunikationsregeln (das ActivityPub-Protokoll) wie Mastodon verwenden, dann funktioniert die Kommunikation auch dort.", "account.name.help.header": "Deine Adresse im Fediverse ist wie eine E-Mail-Adresse", "account.name.help.username": "{username} ist der Profilname auf deren Server. Es ist möglich, dass jemand auf einem anderen Server den gleichen Profilnamen hat.", "account.name.help.username_self": "{username} ist dein Profilname auf diesem Server. Es ist möglich, dass jemand auf einem anderen Server den gleichen Profilnamen hat.", @@ -153,6 +153,7 @@ "account_edit.column_title": "Profil bearbeiten", "account_edit.custom_fields.name": "Feld", "account_edit.custom_fields.placeholder": "Ergänze deine Pronomen, weiterführenden Links oder etwas anderes, das du teilen möchtest.", + "account_edit.custom_fields.reorder_button": "Felder neu anordnen", "account_edit.custom_fields.tip_content": "Du kannst deine Echtheit im Mastodon-Profil beweisen, wenn du verifizierte Links zu deinen Websites bereitstellst.", "account_edit.custom_fields.tip_title": "Tipp: Ergänze verifizierte Links", "account_edit.custom_fields.title": "Zusatzfelder", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Zusatzfeld löschen?", "account_edit.field_edit_modal.add_title": "Zusatzfeld hinzufügen", "account_edit.field_edit_modal.edit_title": "Zusatzfeld bearbeiten", + "account_edit.field_edit_modal.limit_header": "Empfohlenes Zeichenlimit überschritten", + "account_edit.field_edit_modal.limit_message": "Auf mobilen Endgeräten wird das Feld möglicherweise nicht vollständig angezeigt.", + "account_edit.field_edit_modal.link_emoji_warning": "Das Verwenden von Emojis wird bei URLs nicht empfohlen. Die Zusatzfelder werden bei dieser Kombination nur als Text und nicht als Link dargestellt.", "account_edit.field_edit_modal.name_hint": "z. B. „Meine Website“", "account_edit.field_edit_modal.name_label": "Beschriftung", "account_edit.field_edit_modal.value_hint": "z. B. „example.me“", "account_edit.field_edit_modal.value_label": "Inhalt", + "account_edit.field_reorder_modal.drag_cancel": "Das Ziehen wurde abgebrochen und das Feld „{item}“ wurde abgelegt.", + "account_edit.field_reorder_modal.drag_end": "Das Feld „{item}“ wurde abgelegt.", + "account_edit.field_reorder_modal.drag_instructions": "Drücke zum Anordnen der Zusatzfelder die Eingabe- oder Leertaste. Verwende beim Ziehen die Pfeiltasten, um es hoch oder runter zu bewegen. Drücke erneut die Eingabe- oder Leertaste, um das Zusatzfeld an der gewünschten Position abzulegen. Mit der Escape-Taste kannst du den Vorgang abbrechen.", + "account_edit.field_reorder_modal.drag_move": "Das Feld „{item}“ wurde verschoben.", + "account_edit.field_reorder_modal.drag_over": "Das Feld „{item}“ wurde über „{over}“ verschoben.", + "account_edit.field_reorder_modal.drag_start": "Das Feld „{item}“ wurde ausgewählt.", + "account_edit.field_reorder_modal.handle_label": "Das Feld „{item}“ verschieben", + "account_edit.field_reorder_modal.title": "Felder neu anordnen", "account_edit.name_modal.add_title": "Anzeigenamen hinzufügen", "account_edit.name_modal.edit_title": "Anzeigenamen bearbeiten", "account_edit.profile_tab.button_label": "Anpassen", @@ -936,7 +948,7 @@ "notifications.policy.filter_private_mentions_title": "unerwünschten privaten Erwähnungen", "notifications.policy.title": "Benachrichtigungen verwalten von …", "notifications_permission_banner.enable": "Aktiviere Desktop-Benachrichtigungen", - "notifications_permission_banner.how_to_control": "Um Benachrichtigungen zu erhalten, wenn Mastodon nicht geöffnet ist, aktiviere die Desktop-Benachrichtigungen. Du kannst genau bestimmen, welche Arten von Interaktionen Desktop-Benachrichtigungen über die {icon} -Taste erzeugen, sobald diese aktiviert sind.", + "notifications_permission_banner.how_to_control": "Aktiviere Desktop-Benachrichtigungen, um Mitteilungen zu erhalten, wenn Mastodon nicht geöffnet ist. Du kannst für jede Kategorie einstellen, ob du Desktop-Benachrichtigungen erhalten möchtest. Sobald sie aktiviert sind, klicke dafür auf das {icon} -Symbol.", "notifications_permission_banner.title": "Nichts verpassen", "onboarding.follows.back": "Zurück", "onboarding.follows.done": "Fertig", @@ -1153,7 +1165,7 @@ "status.quote_error.pending_approval": "Veröffentlichung ausstehend", "status.quote_error.pending_approval_popout.body": "Auf Mastodon kannst du selbst bestimmen, ob du von anderen zitiert werden darfst oder nicht – oder nur nach individueller Genehmigung. Wir warten in diesem Fall noch auf die Genehmigung des ursprünglichen Profils. Bis dahin steht die Veröffentlichung des Beitrags mit dem zitierten Post noch aus.", "status.quote_error.revoked": "Beitrag durch Autor*in entfernt", - "status.quote_followers_only": "Nur Follower können diesen Beitrag zitieren", + "status.quote_followers_only": "Nur Follower dürfen zitieren", "status.quote_manual_review": "Autor*in wird deine Anfrage manuell überprüfen", "status.quote_noun": "Zitat", "status.quote_policy_change": "Ändern, wer zitieren darf", @@ -1220,11 +1232,11 @@ "upload_error.limit": "Dateiupload-Limit überschritten.", "upload_error.poll": "Medien-Anhänge sind zusammen mit Umfragen nicht erlaubt.", "upload_error.quote": "Medien-Anhänge sind zusammen mit Zitaten nicht erlaubt.", - "upload_form.drag_and_drop.instructions": "Drücke zum Aufnehmen eines Medienanhangs die Eingabe- oder Leertaste. Verwende beim Ziehen die Pfeiltasten, um den Medienanhang zur gewünschten Position zu bewegen. Drücke erneut die Eingabe- oder Leertaste, um den Medienanhang an der gewünschten Position abzulegen. Mit der Escape-Taste kannst du den Vorgang abbrechen.", + "upload_form.drag_and_drop.instructions": "Drücke zum Auswählen eines Medienanhangs die Eingabe- oder Leertaste. Verwende beim Ziehen die Pfeiltasten, um den Medienanhang zur gewünschten Position zu bewegen. Drücke erneut die Eingabe- oder Leertaste, um den Medienanhang an der gewünschten Position abzulegen. Mit der Escape-Taste kannst du den Vorgang abbrechen.", "upload_form.drag_and_drop.on_drag_cancel": "Das Ziehen wurde abgebrochen und der Medienanhang {item} wurde abgelegt.", "upload_form.drag_and_drop.on_drag_end": "Der Medienanhang {item} wurde abgelegt.", "upload_form.drag_and_drop.on_drag_over": "Der Medienanhang {item} wurde bewegt.", - "upload_form.drag_and_drop.on_drag_start": "Der Medienanhang {item} wurde aufgenommen.", + "upload_form.drag_and_drop.on_drag_start": "Der Medienanhang {item} wurde ausgewählt.", "upload_form.edit": "Bearbeiten", "upload_progress.label": "Upload läuft …", "upload_progress.processing": "Wird verarbeitet …", @@ -1252,7 +1264,7 @@ "visibility_modal.helper.privacy_private_self_quote": "Beiträge mit privaten Erwähnungen können öffentlich nicht zitiert werden.", "visibility_modal.helper.private_quoting": "Beiträge, die nur für deine Follower bestimmt sind und auf Mastodon verfasst wurden, können nicht von anderen zitiert werden.", "visibility_modal.helper.unlisted_quoting": "Sollten dich andere zitieren, werden ihre zitierten Beiträge ebenfalls nicht in den Trends und öffentlichen Timelines angezeigt.", - "visibility_modal.instructions": "Lege fest, wer mit diesem Beitrag interagieren darf. Du hast auch die Möglichkeit, diese Einstellung auf alle zukünftigen Beiträge anzuwenden. Gehe zu: Einstellungen > Standardeinstellungen für Beiträge", + "visibility_modal.instructions": "Lege fest, wer mit diesem Beitrag interagieren darf. Du hast auch die Möglichkeit, diese Einstellung auf alle zukünftigen Beiträge anzuwenden. Navigiere zu: Einstellungen > Standardeinstellungen für Beiträge", "visibility_modal.privacy_label": "Sichtbarkeit", "visibility_modal.quote_followers": "Nur meine Follower dürfen mich zitieren", "visibility_modal.quote_label": "Wer darf mich zitieren?", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 49ba9480f38e1a..4513944af21cbb 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Επεξεργασία Προφίλ", "account_edit.custom_fields.name": "πεδίο", "account_edit.custom_fields.placeholder": "Προσθέστε τις αντωνυμίες σας, εξωτερικούς συνδέσμους ή οτιδήποτε άλλο θέλετε να μοιραστείτε.", + "account_edit.custom_fields.reorder_button": "Αναδιάταξη πεδίων", "account_edit.custom_fields.tip_content": "Μπορείς εύκολα να προσθέσεις αξιοπιστία στον Mastodon λογαριασμό σου επαληθεύοντας συνδέσμους σε οποιεσδήποτε ιστοσελίδες κατέχεις.", "account_edit.custom_fields.tip_title": "Συμβουλή: Προσθήκη επαληθευμένων συνδέσμων", "account_edit.custom_fields.title": "Προσαρμοσμένα πεδία", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Διαγραφή προσαρμοσμένου πεδίου;", "account_edit.field_edit_modal.add_title": "Προσθήκη προσαρμοσμένου πεδίου", "account_edit.field_edit_modal.edit_title": "Επεξεργασία προσαρμοσμένου πεδίου", + "account_edit.field_edit_modal.limit_header": "Ξεπεράστηκε το συνιστώμενο όριο χαρακτήρων", + "account_edit.field_edit_modal.limit_message": "Οι χρήστες κινητών ενδέχεται να μην βλέπουν πλήρως το πεδίο σας.", + "account_edit.field_edit_modal.link_emoji_warning": "Δεν συνιστούμε τη χρήση προσαρμοσμένων emoji σε συνδυασμό με URL. Τα προσαρμοσμένα πεδία που περιέχουν και τα δύο θα εμφανίζονται ως κείμενο μόνο αντί ως σύνδεσμος, προκειμένου να αποφευχθεί η σύγχυση του χρήστη.", "account_edit.field_edit_modal.name_hint": "Π.χ. “Προσωπική ιστοσελίδα”", "account_edit.field_edit_modal.name_label": "Ετικέτα", "account_edit.field_edit_modal.value_hint": "Π.χ. “example.me”", "account_edit.field_edit_modal.value_label": "Τιμή", + "account_edit.field_reorder_modal.drag_cancel": "Η μετακίνηση ακυρώθηκε. Το πεδίο \"{item}\" αφέθηκε.", + "account_edit.field_reorder_modal.drag_end": "Το πεδίο \"{item}\" αφέθηκε.", + "account_edit.field_reorder_modal.drag_instructions": "Για να αναδιατάξετε τα προσαρμοσμένα πεδία, πατήστε Space ή Enter. Κατά τη μετακίνηση, χρησιμοποιήστε τα πλήκτρα βέλους για να μετακινήσετε το πεδίο πάνω ή κάτω. Πατήστε Space ή Enter ξανά για να ρίξετε το πεδίο στη νέα του θέση ή πατήστε Escape για να ακυρώσετε.", + "account_edit.field_reorder_modal.drag_move": "Το πεδίο \"{item}\" μετακινήθηκε.", + "account_edit.field_reorder_modal.drag_over": "Το πεδίο \"{item}\" μετακινήθηκε στο \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Το πεδίο \"{item}\" σηκώθηκε.", + "account_edit.field_reorder_modal.handle_label": "Μετακίνηση πεδίου \"{item}\"", + "account_edit.field_reorder_modal.title": "Αναδιάταξη πεδίων", "account_edit.name_modal.add_title": "Προσθήκη εμφανιζόμενου ονόματος", "account_edit.name_modal.edit_title": "Επεξεργασία εμφανιζόμενου ονόματος", "account_edit.profile_tab.button_label": "Προσαρμογή", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 0637704eb206d7..5735cd7c4c79cd 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Edit Profile", "account_edit.custom_fields.name": "field", "account_edit.custom_fields.placeholder": "Add your pronouns, external links, or anything else you’d like to share.", + "account_edit.custom_fields.reorder_button": "Reorder fields", "account_edit.custom_fields.tip_content": "You can easily add credibility to your Mastodon account by verifying links to any websites you own.", "account_edit.custom_fields.tip_title": "Tip: adding verified links", "account_edit.custom_fields.title": "Custom fields", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Delete custom field?", "account_edit.field_edit_modal.add_title": "Add custom field", "account_edit.field_edit_modal.edit_title": "Edit custom field", + "account_edit.field_edit_modal.limit_header": "Recommended character limit exceeded", + "account_edit.field_edit_modal.limit_message": "Mobile users might not see your field in full.", + "account_edit.field_edit_modal.link_emoji_warning": "We recommend against the use of custom emoji in combination with URLs. Custom fields containing both will display as text only instead of as a link, in order to prevent user confusion.", "account_edit.field_edit_modal.name_hint": "Eg “Personal website”", "account_edit.field_edit_modal.name_label": "Label", "account_edit.field_edit_modal.value_hint": "Eg “example.me”", "account_edit.field_edit_modal.value_label": "Value", + "account_edit.field_reorder_modal.drag_cancel": "Dragging was cancelled. Field \"{item}\" was dropped.", + "account_edit.field_reorder_modal.drag_end": "Field \"{item}\" was dropped.", + "account_edit.field_reorder_modal.drag_instructions": "To rearrange custom fields, press space or enter. While dragging, use the arrow keys to move the field up or down. Press space or enter again to drop the field in its new position, or press escape to cancel.", + "account_edit.field_reorder_modal.drag_move": "Field \"{item}\" was moved.", + "account_edit.field_reorder_modal.drag_over": "Field \"{item}\" was moved over \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Picked up field \"{item}\".", + "account_edit.field_reorder_modal.handle_label": "Drag field \"{item}\"", + "account_edit.field_reorder_modal.title": "Rearrange fields", "account_edit.name_modal.add_title": "Add display name", "account_edit.name_modal.edit_title": "Edit display name", "account_edit.profile_tab.button_label": "Customise", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 29be4fbf40bcfc..b577a18a8552e8 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -14,6 +14,7 @@ "about.powered_by": "Malcentrigita socia retejo pere de {mastodon}", "about.rules": "Reguloj de la servilo", "account.account_note_header": "Personaj notoj", + "account.add_note": "Aldoni personan noton", "account.add_or_remove_from_list": "Aldoni al aŭ forigi el listoj", "account.badges.bot": "Aŭtomata", "account.badges.group": "Grupo", @@ -33,7 +34,7 @@ "account.endorse": "Montri en profilo", "account.familiar_followers_one": "Sekvita de {name1}", "account.familiar_followers_two": "Sekvita de {name1} kaj {name2}", - "account.featured": "Montrita", + "account.featured": "Elstarigitaj", "account.featured.accounts": "Profiloj", "account.featured.hashtags": "Kradvortoj", "account.featured_tags.last_status_at": "Lasta afîŝo je {date}", @@ -59,6 +60,9 @@ "account.locked_info": "La privateco de tiu konto estas elektita kiel fermita. La posedanto povas mane akcepti tiun, kiu povas sekvi rin.", "account.media": "Aŭdovidaĵoj", "account.mention": "Mencii @{name}", + "account.menu.add_to_list": "Aldoni al listo…", + "account.menu.block": "Bloki konton", + "account.menu.block_domain": "Bloki {domain}", "account.moved_to": "{name} indikis, ke ria nova konto estas nun:", "account.mute": "Silentigi @{name}", "account.mute_notifications_short": "Silentigu sciigojn", @@ -67,6 +71,7 @@ "account.muting": "Silentas", "account.mutual": "Vi sekvas unu la alian", "account.no_bio": "Neniu priskribo estas provizita.", + "account.node_modal.field_label": "Persona noto", "account.open_original_page": "Malfermi la originalan paĝon", "account.posts": "Afiŝoj", "account.posts_with_replies": "Afiŝoj kaj respondoj", @@ -86,6 +91,10 @@ "account.unmute": "Malsilentigi @{name}", "account.unmute_notifications_short": "Malsilentigu sciigojn", "account.unmute_short": "Ne plu silentigi", + "account_edit.featured_hashtags.item": "kradvortoj", + "account_edit.featured_hashtags.title": "Elstarigitaj kradvortoj", + "account_edit.save": "Konservi", + "account_edit_tags.add_tag": "Aldoni #{tagName}", "account_note.placeholder": "Alklaku por aldoni noton", "admin.dashboard.daily_retention": "Uzantoretenprocento laŭ tag post registro", "admin.dashboard.monthly_retention": "Uzantoretenprocento laŭ monato post registro", @@ -309,8 +318,8 @@ "emoji_button.search_results": "Serĉaj rezultoj", "emoji_button.symbols": "Simboloj", "emoji_button.travel": "Vojaĝoj kaj lokoj", - "empty_column.account_featured.me": "Vi ankoraŭ nenion prezentis. Ĉu vi sciis, ke vi povas prezenti viajn plej ofte uzatajn kradvortojn, kaj eĉ la kontojn de viaj amikoj sur via profilo?", - "empty_column.account_featured_other.unknown": "Ĉi tiu konto ankoraŭ ne montris ion ajn.", + "empty_column.account_featured.me": "Vi ankoraŭ elstarigis nenion. Ĉu vi sciis, ke vi povas elstarigi viajn plej ofte uzatajn kradvortojn, kaj eĉ la kontojn de viaj amikoj sur via profilo?", + "empty_column.account_featured_other.unknown": "Ĉi tiu konto ankoraŭ ne elstarigis ion ajn.", "empty_column.account_hides_collections": "Ĉi tiu uzanto elektis ne disponebligi ĉi tiu informon", "empty_column.account_suspended": "Konto suspendita", "empty_column.account_timeline": "Neniuj afiŝoj ĉi tie!", @@ -345,6 +354,7 @@ "explore.trending_tags": "Kradvortoj", "featured_carousel.header": "{count, plural, one {Alpinglita afiŝo} other {Alpinglitaj afiŝoj}}", "featured_carousel.slide": "Afiŝo {current, number} de {max, number}", + "featured_tags.more_items": "+{count}", "filter_modal.added.context_mismatch_explanation": "Ĉi tiu filtrilkategorio ne kongruas kun la kunteksto en kiu vi akcesis ĉi tiun afiŝon. Se vi volas ke la afiŝo estas ankaŭ filtrita en ĉi tiu kunteksto, vi devus redakti la filtrilon.", "filter_modal.added.context_mismatch_title": "Ne kongruas la kunteksto!", "filter_modal.added.expired_explanation": "Ĉi tiu filtrilkategorio eksvalidiĝis, vu bezonos ŝanĝi la eksvaliddaton por ĝi.", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index b1d21b738224df..4015a79eccb2f4 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Editar perfil", "account_edit.custom_fields.name": "campo", "account_edit.custom_fields.placeholder": "Agregá tus pronombres personales, enlaces externos o cualquier otra cosa que quisieras compartir.", + "account_edit.custom_fields.reorder_button": "Reordenar campos", "account_edit.custom_fields.tip_content": "Podés agregar fácilmente credibilidad a tu cuenta de Mastodon verificando enlaces a cualquier sitio web que tengas.", "account_edit.custom_fields.tip_title": "Consejo: Agregá enlaces verificados", "account_edit.custom_fields.title": "Campos personalizados", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "¿Eliminar campo personalizado?", "account_edit.field_edit_modal.add_title": "Agregar campo personalizado", "account_edit.field_edit_modal.edit_title": "Editar campo personalizado", + "account_edit.field_edit_modal.limit_header": "Se excedió el límite de caracteres recomendado", + "account_edit.field_edit_modal.limit_message": "Es posible que los usuarios en dispositivos móviles no vean tu campo completamente.", + "account_edit.field_edit_modal.link_emoji_warning": "No recomendamos el uso de emojis personalizados en combinación con direcciones web. Los campos personalizados que contengan ambos solo se mostrarán como texto en lugar de como enlace, con el fin de evitar la confusión del usuario.", "account_edit.field_edit_modal.name_hint": "Por ejemplo: «Sitio web personal»", "account_edit.field_edit_modal.name_label": "Etiqueta", "account_edit.field_edit_modal.value_hint": "Por ejemplo: «ejemplo.com.ar»", "account_edit.field_edit_modal.value_label": "Valor", + "account_edit.field_reorder_modal.drag_cancel": "El arrastre fue cancelado. Se soltó el campo «{item}».", + "account_edit.field_reorder_modal.drag_end": "Se soltó el campo «{item}».", + "account_edit.field_reorder_modal.drag_instructions": "Para reordenar campos personalizados, pulsá la tecla «Espacio» o «Intro ⏎». Mientras arrastrás, usá las teclas de flecha para mover el campo hacia arriba o hacia abajo. Presioná la tecla «Espacio» o «Intro ⏎» de nuevo para soltar el campo en su nueva posición, o presioná la tecla «Escape» para cancelar.", + "account_edit.field_reorder_modal.drag_move": "El campo «{item}» fue movido.", + "account_edit.field_reorder_modal.drag_over": "El campo «{item}» fue movido sobre «{over}».", + "account_edit.field_reorder_modal.drag_start": "Campo elegido «{item}».", + "account_edit.field_reorder_modal.handle_label": "Arrastrá el campo «{item}»", + "account_edit.field_reorder_modal.title": "Reordená los campos", "account_edit.name_modal.add_title": "Agregar nombre a mostrar", "account_edit.name_modal.edit_title": "Editar nombre a mostrar", "account_edit.profile_tab.button_label": "Personalizar", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index bb9aa90281d5b4..1d1d89569f4380 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "Editar {item}", "account_edit.column_button": "Hecho", "account_edit.column_title": "Editar perfil", + "account_edit.custom_fields.name": "campo", "account_edit.custom_fields.placeholder": "Añade tus pronombres, enlaces externos o cualquier otra información que quieras compartir.", + "account_edit.custom_fields.reorder_button": "Reordenar campos", + "account_edit.custom_fields.tip_content": "Puedes añadir fácilmente credibilidad a tu cuenta de Mastodon verificando los enlaces a cualquier sitio web que poseas.", + "account_edit.custom_fields.tip_title": "Consejo: Añadir enlaces verificados", "account_edit.custom_fields.title": "Campos personalizados", + "account_edit.custom_fields.verified_hint": "¿Cómo agrego un enlace verificado?", "account_edit.display_name.placeholder": "Tu nombre de usuario es el nombre que aparece en tu perfil y en las líneas de tiempo.", "account_edit.display_name.title": "Nombre para mostrar", "account_edit.featured_hashtags.item": "etiquetas", "account_edit.featured_hashtags.placeholder": "Ayuda a otros a identificar tus temas favoritos y a acceder rápidamente a ellos.", "account_edit.featured_hashtags.title": "Etiquetas destacadas", + "account_edit.field_delete_modal.confirm": "¿Estás seguro de que deseas eliminar este campo personalizado? Esta acción no se puede deshacer.", + "account_edit.field_delete_modal.delete_button": "Eliminar", + "account_edit.field_delete_modal.title": "¿Eliminar campo personalizado?", + "account_edit.field_edit_modal.add_title": "Agregar campo personalizado", + "account_edit.field_edit_modal.edit_title": "Editar campo personalizado", + "account_edit.field_edit_modal.limit_header": "Se ha superado el límite de caracteres recomendado", + "account_edit.field_edit_modal.limit_message": "Es posible que los usuarios de dispositivos móviles no vean tu campo completo.", + "account_edit.field_edit_modal.link_emoji_warning": "No recomendamos el uso de emojis personalizados en combinación con direcciones URL. Los campos personalizados que contengan ambos se mostrarán solo como texto en lugar de como un enlace, con el fin de evitar confusiones al usuario.", + "account_edit.field_edit_modal.name_hint": "Por ejemplo, «sitio web personal»", + "account_edit.field_edit_modal.name_label": "Etiqueta", + "account_edit.field_edit_modal.value_hint": "Por ejemplo, «ejemplo.me»", + "account_edit.field_edit_modal.value_label": "Valor", + "account_edit.field_reorder_modal.drag_cancel": "Se canceló el arrastre. Se eliminó el campo «{item}».", + "account_edit.field_reorder_modal.drag_end": "Se ha eliminado el campo «{item}».", + "account_edit.field_reorder_modal.drag_instructions": "Para reorganizar los campos personalizados, presiona la barra espaciadora o Intro. Mientras arrastras, utiliza las teclas de flecha para mover el campo hacia arriba o hacia abajo. Presiona la barra espaciadora o Intro de nuevo para soltar el campo en su nueva posición, o presiona Esc para cancelar.", + "account_edit.field_reorder_modal.drag_move": "Se ha movido el campo «{item}».", + "account_edit.field_reorder_modal.drag_over": "El campo «{item}» se ha movido a «{over}».", + "account_edit.field_reorder_modal.drag_start": "Recogido el campo «{item}».", + "account_edit.field_reorder_modal.handle_label": "Arrastra el campo «{item}»", + "account_edit.field_reorder_modal.title": "Reorganizar campos", "account_edit.name_modal.add_title": "Añadir nombre para mostrar", "account_edit.name_modal.edit_title": "Editar nombre para mostrar", "account_edit.profile_tab.button_label": "Personalizar", @@ -172,6 +197,13 @@ "account_edit.profile_tab.subtitle": "Personaliza las pestañas de tu perfil y lo que muestran.", "account_edit.profile_tab.title": "Configuración de la pestaña de perfil", "account_edit.save": "Guardar", + "account_edit.verified_modal.details": "Agrega credibilidad a tu perfil de Mastodon verificando los enlaces a sitios web personales. Así es como funciona:", + "account_edit.verified_modal.invisible_link.details": "Agrega el enlace a tu encabezado. La parte importante es rel=\"me\", que evita la suplantación de identidad en sitios web con contenido generado por los usuarios. Incluso puedes usar una etiqueta de enlace en el encabezado de la página en lugar de {tag}, pero el HTML debe ser accesible sin ejecutar JavaScript.", + "account_edit.verified_modal.invisible_link.summary": "¿Cómo hago para que el enlace sea invisible?", + "account_edit.verified_modal.step1.header": "Copia el código HTML que aparece a continuación y pégalo en el encabezado de tu sitio web", + "account_edit.verified_modal.step2.details": "Si ya has agregado tu sitio web como campo personalizado, deberás eliminarlo y volver a agregarlo para activar la verificación.", + "account_edit.verified_modal.step2.header": "Agrega tu sitio web como campo personalizado", + "account_edit.verified_modal.title": "Cómo agregar un enlace verificado", "account_edit_tags.add_tag": "Añadir #{tagName}", "account_edit_tags.column_title": "Editar etiquetas destacadas", "account_edit_tags.help_text": "Las etiquetas destacadas ayudan a los usuarios a descubrir tu perfil e interactuar con él. Aparecen como filtros en la vista Actividad de tu página de perfil.", @@ -275,6 +307,8 @@ "callout.dismiss": "Descartar", "carousel.current": "Diapositiva {current, number} / {max, number}", "carousel.slide": "Diapositiva {current, number} de {max, number}", + "character_counter.recommended": "{currentLength}/{maxLength} caracteres recomendados", + "character_counter.required": "{currentLength}/{maxLength} caracteres", "closed_registrations.other_server_instructions": "Como Mastodon es descentralizado, puedes crear una cuenta en otro servidor y seguir interactuando con este.", "closed_registrations_modal.description": "La creación de una cuenta en {domain} no es posible actualmente, pero ten en cuenta que no necesitas una cuenta específicamente en {domain} para usar Mastodon.", "closed_registrations_modal.find_another_server": "Buscar otro servidor", @@ -305,9 +339,12 @@ "collections.delete_collection": "Eliminar colección", "collections.description_length_hint": "Limitado a 100 caracteres", "collections.detail.accounts_heading": "Cuentas", + "collections.detail.author_added_you": "{author} te ha añadido a esta colección", "collections.detail.curated_by_author": "Seleccionado por {author}", "collections.detail.curated_by_you": "Seleccionado por ti", "collections.detail.loading": "Cargando colección…", + "collections.detail.other_accounts_in_collection": "Otros en esta colección:", + "collections.detail.sensitive_note": "Esta colección contiene cuentas y contenido que pueden resultar sensibles para algunos usuarios.", "collections.detail.share": "Compartir esta colección", "collections.edit_details": "Editar detalles", "collections.error_loading_collections": "Se produjo un error al intentar cargar tus colecciones.", @@ -767,6 +804,7 @@ "navigation_bar.automated_deletion": "Eliminación automática de publicaciones", "navigation_bar.blocks": "Usuarios bloqueados", "navigation_bar.bookmarks": "Marcadores", + "navigation_bar.collections": "Colecciones", "navigation_bar.direct": "Menciones privadas", "navigation_bar.domain_blocks": "Dominios ocultos", "navigation_bar.favourites": "Favoritos", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index e7c3d8a22a1e3f..2982ab4a4352c7 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "Editar {item}", "account_edit.column_button": "Hecho", "account_edit.column_title": "Editar perfil", + "account_edit.custom_fields.name": "campo", "account_edit.custom_fields.placeholder": "Añade tus pronombres, enlaces externos o cualquier otra cosa que quieras compartir.", + "account_edit.custom_fields.reorder_button": "Reordenar campos", + "account_edit.custom_fields.tip_content": "Puedes añadir credibilidad fácilmente a tu cuenta de Mastodon verificando los enlaces a tus propias webs.", + "account_edit.custom_fields.tip_title": "Consejo: Añade enlaces verificados", "account_edit.custom_fields.title": "Campos personalizados", + "account_edit.custom_fields.verified_hint": "¿Cómo añado un enlace verificado?", "account_edit.display_name.placeholder": "Tu nombre de usuario es el nombre que aparece en tu perfil y en las cronologías.", "account_edit.display_name.title": "Nombre para mostrar", "account_edit.featured_hashtags.item": "etiquetas", "account_edit.featured_hashtags.placeholder": "Ayuda a otros a identificar tus temas favoritos y a acceder rápidamente a ellos.", "account_edit.featured_hashtags.title": "Etiquetas destacadas", + "account_edit.field_delete_modal.confirm": "¿Estás seguro de que quieres borrar este campo personalizado? La acción no se puede deshacer.", + "account_edit.field_delete_modal.delete_button": "Borrar", + "account_edit.field_delete_modal.title": "¿Borrar campo personalizado?", + "account_edit.field_edit_modal.add_title": "Añadir campo personalizado", + "account_edit.field_edit_modal.edit_title": "Editar campo personalizado", + "account_edit.field_edit_modal.limit_header": "Se ha sobrepasado el límite de caracteres recomendado", + "account_edit.field_edit_modal.limit_message": "Los usuarios de móviles no verán tu campo entero.", + "account_edit.field_edit_modal.link_emoji_warning": "Recomendamos no usar emojis personalizados combinados con enlaces. Los campos personalizados que contengan ambos solo se mostrarán como texto en vez de un enlace, para evitar confusiones.", + "account_edit.field_edit_modal.name_hint": "Ej. \"Web personal\"", + "account_edit.field_edit_modal.name_label": "Etiqueta", + "account_edit.field_edit_modal.value_hint": "Ej. \"ejemplo.yo\"", + "account_edit.field_edit_modal.value_label": "Valor", + "account_edit.field_reorder_modal.drag_cancel": "El arrastre se ha cancelado. El campo \"{item}\" se ha soltado.", + "account_edit.field_reorder_modal.drag_end": "El campo \"{item}\" se ha soltado.", + "account_edit.field_reorder_modal.drag_instructions": "Para reorganizar los campos personalizados, pulsa espacio o enter. Mientras arrastras, usa las flechas del teclado para mover un campo arriba o abajo. Pulsa espacio o enter de nuevo para situar el campo en su nueva posición, o pulsa escape para cancelar.", + "account_edit.field_reorder_modal.drag_move": "El campo \"{item}\" se ha movido.", + "account_edit.field_reorder_modal.drag_over": "El campo \"{item}\" se ha movido sobre \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Campo \"{item}\" seleccionado.", + "account_edit.field_reorder_modal.handle_label": "Arrastra el campo \"{item}\"", + "account_edit.field_reorder_modal.title": "Reorganizar campos", "account_edit.name_modal.add_title": "Añadir nombre para mostrar", "account_edit.name_modal.edit_title": "Editar nombre para mostrar", "account_edit.profile_tab.button_label": "Personalizar", @@ -172,6 +197,13 @@ "account_edit.profile_tab.subtitle": "Personaliza las pestañas de tu perfil y lo que muestran.", "account_edit.profile_tab.title": "Configuración de la pestaña de perfil", "account_edit.save": "Guardar", + "account_edit.verified_modal.details": "Añade credibilidad a tu perfil de Mastodon verificando enlaces a tus webs personales. Así es como funciona:", + "account_edit.verified_modal.invisible_link.details": "Añade el enlace en el encabezado. La parte importante es rel=\"me\", que evita la suplantación de identidad en webs con contenido generado por usuarios. Incluso puedes utilizar un enlace con etiqueta en el encabezado de la página en vez de {tag}, pero el HTML debe ser accesible sin ejecutar JavaScript.", + "account_edit.verified_modal.invisible_link.summary": "¿Cómo puedo hacer el enlace invisible?", + "account_edit.verified_modal.step1.header": "Copia el código HTML que hay debajo y pégalo en el encabezado de tu web", + "account_edit.verified_modal.step2.details": "Si ya lo has añadido a tu web como un campo personalizado, deberás borrarlo y añadirlo de nuevo para activar la verificación.", + "account_edit.verified_modal.step2.header": "Añade tu web como un campo personalizado", + "account_edit.verified_modal.title": "Cómo añadir un enlace verificado", "account_edit_tags.add_tag": "Agregar #{tagName}", "account_edit_tags.column_title": "Editar etiquetas destacadas", "account_edit_tags.help_text": "Las etiquetas destacadas ayudan a los usuarios a descubrir e interactuar con tu perfil. Aparecen como filtros en la vista de actividad de tu página de perfil.", @@ -275,6 +307,8 @@ "callout.dismiss": "Descartar", "carousel.current": "Diapositiva {current, number} / {max, number}", "carousel.slide": "Diapositiva {current, number} de {max, number}", + "character_counter.recommended": "{currentLength}/{maxLength} caracteres recomendados", + "character_counter.required": "{currentLength}/{maxLength} caracteres", "closed_registrations.other_server_instructions": "Como Mastodon es descentralizado, puedes crear una cuenta en otro servidor y seguir interactuando con este.", "closed_registrations_modal.description": "La creación de una cuenta en {domain} no es posible actualmente, pero ten en cuenta que no necesitas una cuenta específicamente en {domain} para usar Mastodon.", "closed_registrations_modal.find_another_server": "Buscar otro servidor", @@ -305,9 +339,12 @@ "collections.delete_collection": "Eliminar colección", "collections.description_length_hint": "Limitado a 100 caracteres", "collections.detail.accounts_heading": "Cuentas", + "collections.detail.author_added_you": "{author} te añadió a esta colección", "collections.detail.curated_by_author": "Seleccionado por {author}", "collections.detail.curated_by_you": "Seleccionado por ti", "collections.detail.loading": "Cargando colección…", + "collections.detail.other_accounts_in_collection": "Otros en esta colección:", + "collections.detail.sensitive_note": "Esta colección contiene cuentas y contenido que puede ser sensible para algunos usuarios.", "collections.detail.share": "Compartir esta colección", "collections.edit_details": "Editar detalles", "collections.error_loading_collections": "Se ha producido un error al intentar cargar tus colecciones.", @@ -767,6 +804,7 @@ "navigation_bar.automated_deletion": "Eliminación automática de publicaciones", "navigation_bar.blocks": "Usuarios bloqueados", "navigation_bar.bookmarks": "Marcadores", + "navigation_bar.collections": "Colecciones", "navigation_bar.direct": "Menciones privadas", "navigation_bar.domain_blocks": "Dominios ocultos", "navigation_bar.favourites": "Favoritos", @@ -1071,7 +1109,7 @@ "sign_in_banner.sign_in": "Iniciar sesión", "sign_in_banner.sso_redirect": "Iniciar sesión o Registrarse", "skip_links.hotkey": "Atajo {hotkey}", - "skip_links.skip_to_content": "Abrir Saltar al contenido principal", + "skip_links.skip_to_content": "Saltar al contenido principal", "skip_links.skip_to_navigation": "Saltar a la navegación principal", "status.admin_account": "Abrir interfaz de moderación para @{name}", "status.admin_domain": "Abrir interfaz de moderación para {domain}", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index f6a91c724e8c45..9fae80fe2a285f 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -153,16 +153,21 @@ "account_edit.column_title": "Muokkaa profiilia", "account_edit.custom_fields.name": "kenttä", "account_edit.custom_fields.placeholder": "Lisää pronominisi, ulkoisia linkkejä tai mitä tahansa muuta, jonka haluat jakaa.", + "account_edit.custom_fields.tip_content": "Voit helposti lisätä Mastodon-tilisi uskottavuutta vahvistamalla mihin tahansa omistamaasi verkkosivustoon ohjaavat linkit.", + "account_edit.custom_fields.tip_title": "Vinkki: Vahvistettujen linkkien lisääminen", "account_edit.custom_fields.title": "Mukautetut kentät", + "account_edit.custom_fields.verified_hint": "Miten lisään vahvistetun linkin?", "account_edit.display_name.placeholder": "Näyttönimesi on nimi, joka näkyy profiilissasi ja aikajanoilla.", "account_edit.display_name.title": "Näyttönimi", "account_edit.featured_hashtags.item": "aihetunnisteet", "account_edit.featured_hashtags.placeholder": "Auta muita tunnistamaan suosikkiaiheesi ja saamaan nopea pääsy niihin.", "account_edit.featured_hashtags.title": "Esiteltävät aihetunnisteet", + "account_edit.field_delete_modal.confirm": "Haluatko varmasti poistaa tämän mukautetun kentän? Tätä toimintoa ei voi peruuttaa.", "account_edit.field_delete_modal.delete_button": "Poista", "account_edit.field_delete_modal.title": "Poistetaanko mukautettu kenttä?", "account_edit.field_edit_modal.add_title": "Lisää mukautettu kenttä", "account_edit.field_edit_modal.edit_title": "Muokkaa mukautettua kenttää", + "account_edit.field_edit_modal.limit_header": "Suositeltu merkkiraja ylitetty", "account_edit.field_edit_modal.name_hint": "Esim. ”Henkilökohtainen verkkosivusto”", "account_edit.field_edit_modal.name_label": "Nimike", "account_edit.field_edit_modal.value_label": "Arvo", @@ -180,6 +185,8 @@ "account_edit.profile_tab.subtitle": "Mukauta profiilisi välilehtiä ja sitä, mitä niissä näkyy.", "account_edit.profile_tab.title": "Profiilin välilehtien asetukset", "account_edit.save": "Tallenna", + "account_edit.verified_modal.details": "Lisää Mastodon-profiiliisi uskottavuutta vahvistamalla linkit henkilökohtaisiin verkkosivustoihin. Näin se toimii:", + "account_edit.verified_modal.step2.header": "Lisää verkkosivustosi mukautettuna kenttänä", "account_edit_tags.add_tag": "Lisää #{tagName}", "account_edit_tags.column_title": "Muokkaa esiteltäviä aihetunnisteita", "account_edit_tags.help_text": "Esiteltävät aihetunnisteet auttavat käyttäjiä löytämään profiilisi ja olemaan vuorovaikutuksessa sen kanssa. Ne näkyvät suodattimina profiilisivusi Toiminta-näkymässä.", @@ -315,9 +322,12 @@ "collections.delete_collection": "Poista kokoelma", "collections.description_length_hint": "100 merkin rajoitus", "collections.detail.accounts_heading": "Tilit", + "collections.detail.author_added_you": "{author} lisäsi sinut tähän kokoelmaan", "collections.detail.curated_by_author": "Koonnut {author}", "collections.detail.curated_by_you": "Itse kokoamasi", "collections.detail.loading": "Ladataan kokoelmaa…", + "collections.detail.other_accounts_in_collection": "Muut tässä kokoelmassa:", + "collections.detail.sensitive_note": "Tämä kokoelma sisältää tilejä ja sisältöä, jotka saattavat olla arkaluonteisia joillekin käyttäjille.", "collections.detail.share": "Jaa tämä kokoelma", "collections.edit_details": "Muokkaa tietoja", "collections.error_loading_collections": "Kokoelmien latauksessa tapahtui virhe.", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 2733ef316b12d3..a27b980d3e5332 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "Modifier {item}", "account_edit.column_button": "Terminé", "account_edit.column_title": "Modifier le profil", + "account_edit.custom_fields.name": "champ", "account_edit.custom_fields.placeholder": "Ajouter vos pronoms, vos sites, ou tout ce que vous voulez partager.", + "account_edit.custom_fields.reorder_button": "Réorganiser les champs", + "account_edit.custom_fields.tip_content": "Vous pouvez facilement ajouter de la crédibilité à votre compte Mastodon en vérifiant les liens vers tous les sites Web que vous possédez.", + "account_edit.custom_fields.tip_title": "Astuce : ajout de liens vérifiés", "account_edit.custom_fields.title": "Champs personnalisés", + "account_edit.custom_fields.verified_hint": "Comment ajouter un lien vérifié ?", "account_edit.display_name.placeholder": "Votre nom public est le nom qui apparaît sur votre profil et dans les fils d'actualités.", "account_edit.display_name.title": "Nom public", "account_edit.featured_hashtags.item": "hashtags", "account_edit.featured_hashtags.placeholder": "Aider les autres à identifier et à accéder rapidement à vos sujets préférés.", "account_edit.featured_hashtags.title": "Hashtags mis en avant", + "account_edit.field_delete_modal.confirm": "Voulez-vous vraiment supprimer ce champ personnalisé ? Cette action ne peut pas être annulée.", + "account_edit.field_delete_modal.delete_button": "Supprimer", + "account_edit.field_delete_modal.title": "Supprimer le champ personnalisé ?", + "account_edit.field_edit_modal.add_title": "Ajouter un champ personnalisé", + "account_edit.field_edit_modal.edit_title": "Modifier un champ personnalisé", + "account_edit.field_edit_modal.limit_header": "Limite de caractères recommandée dépassée", + "account_edit.field_edit_modal.limit_message": "L'affichage du champ peut être tronqué sur les téléphones.", + "account_edit.field_edit_modal.link_emoji_warning": "Nous déconseillons l'usage d'émoji personnalisé avec les URL. Les champs personnalisés contenant les deux seront affichés comme du texte et non un lien, afin d'éviter toute confusion.", + "account_edit.field_edit_modal.name_hint": "Par exemple « Site Web personnel »", + "account_edit.field_edit_modal.name_label": "Libellé", + "account_edit.field_edit_modal.value_hint": "Par exemple « exemple.me »", + "account_edit.field_edit_modal.value_label": "Valeur", + "account_edit.field_reorder_modal.drag_cancel": "Déplacement annulé. Le champ « {item} » a été redéposé.", + "account_edit.field_reorder_modal.drag_end": "Le champ « {item} » a été déposé.", + "account_edit.field_reorder_modal.drag_instructions": "Pour réorganiser les champs personnalisés, appuyer sur espace ou entrée. Utiliser les touches fléchées pour déplacer le champ vers le haut ou vers le bas. Appuyer à nouveau sur espace ou entrée pour déposer le champ dans sa nouvelle position, ou appuyer sur échap pour annuler.", + "account_edit.field_reorder_modal.drag_move": "Le champ « {item} » a été déplacé.", + "account_edit.field_reorder_modal.drag_over": "Le champ « {item} » a été déplacé au-dessus de « {over} ».", + "account_edit.field_reorder_modal.drag_start": "Champ « {item} » sélectionné.", + "account_edit.field_reorder_modal.handle_label": "Faites glisser le champ « {item} »", + "account_edit.field_reorder_modal.title": "Réorganiser les champs", "account_edit.name_modal.add_title": "Ajouter un nom public", "account_edit.name_modal.edit_title": "Modifier le nom public", "account_edit.profile_tab.button_label": "Personnaliser", @@ -172,6 +197,13 @@ "account_edit.profile_tab.subtitle": "Personnaliser les onglets de votre profil et leur contenu.", "account_edit.profile_tab.title": "Paramètres de l'onglet du profil", "account_edit.save": "Enregistrer", + "account_edit.verified_modal.details": "Ajouter de la crédibilité à votre profil Mastodon en vérifiant les liens vers vos sites Web personnels. Voici comment cela fonctionne :", + "account_edit.verified_modal.invisible_link.details": "Ajouter le lien dans votre en-tête. La partie importante est « rel=\"me\" » qui empêche l'usurpation d'identité sur des sites Web ayant du contenu généré par d'autres utilisateur·rice·s. Vous pouvez aussi utiliser une balise link dans l'en-tête de la page au lieu de {tag}, mais le code HTML doit être accessible sans avoir besoin d'exécuter du JavaScript.", + "account_edit.verified_modal.invisible_link.summary": "Comment rendre le lien invisible ?", + "account_edit.verified_modal.step1.header": "Copier-coller le code HTML ci-dessous dans l'en-tête de votre site web", + "account_edit.verified_modal.step2.details": "Si vous avez déjà ajouté votre site Web en tant que champ personnalisé, vous devrez le supprimer et le rajouter pour déclencher la vérification.", + "account_edit.verified_modal.step2.header": "Ajouter votre site Web en tant que champ personnalisé", + "account_edit.verified_modal.title": "Comment ajouter un lien vérifié ?", "account_edit_tags.add_tag": "Ajouter #{tagName}", "account_edit_tags.column_title": "Modifier les hashtags mis en avant", "account_edit_tags.help_text": "Les hashtags mis en avant aident les personnes à découvrir et interagir avec votre profil. Ils apparaissent comme des filtres dans la vue « Activité » de votre profil.", @@ -307,9 +339,12 @@ "collections.delete_collection": "Supprimer la collection", "collections.description_length_hint": "Maximum 100 caractères", "collections.detail.accounts_heading": "Comptes", + "collections.detail.author_added_you": "{author} vous a ajouté·e à cette collection", "collections.detail.curated_by_author": "Organisée par {author}", "collections.detail.curated_by_you": "Organisée par vous", "collections.detail.loading": "Chargement de la collection…", + "collections.detail.other_accounts_in_collection": "Autres comptes dans cette collection :", + "collections.detail.sensitive_note": "Cette collection contient des comptes et du contenu qui peut être sensibles.", "collections.detail.share": "Partager la collection", "collections.edit_details": "Modifier les détails", "collections.error_loading_collections": "Une erreur s'est produite durant le chargement de vos collections.", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 8d34fc935d93b8..1bf320aa01130a 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "Modifier {item}", "account_edit.column_button": "Terminé", "account_edit.column_title": "Modifier le profil", + "account_edit.custom_fields.name": "champ", "account_edit.custom_fields.placeholder": "Ajouter vos pronoms, vos sites, ou tout ce que vous voulez partager.", + "account_edit.custom_fields.reorder_button": "Réorganiser les champs", + "account_edit.custom_fields.tip_content": "Vous pouvez facilement ajouter de la crédibilité à votre compte Mastodon en vérifiant les liens vers tous les sites Web que vous possédez.", + "account_edit.custom_fields.tip_title": "Astuce : ajout de liens vérifiés", "account_edit.custom_fields.title": "Champs personnalisés", + "account_edit.custom_fields.verified_hint": "Comment ajouter un lien vérifié ?", "account_edit.display_name.placeholder": "Votre nom public est le nom qui apparaît sur votre profil et dans les fils d'actualités.", "account_edit.display_name.title": "Nom public", "account_edit.featured_hashtags.item": "hashtags", "account_edit.featured_hashtags.placeholder": "Aider les autres à identifier et à accéder rapidement à vos sujets préférés.", "account_edit.featured_hashtags.title": "Hashtags mis en avant", + "account_edit.field_delete_modal.confirm": "Voulez-vous vraiment supprimer ce champ personnalisé ? Cette action ne peut pas être annulée.", + "account_edit.field_delete_modal.delete_button": "Supprimer", + "account_edit.field_delete_modal.title": "Supprimer le champ personnalisé ?", + "account_edit.field_edit_modal.add_title": "Ajouter un champ personnalisé", + "account_edit.field_edit_modal.edit_title": "Modifier un champ personnalisé", + "account_edit.field_edit_modal.limit_header": "Limite de caractères recommandée dépassée", + "account_edit.field_edit_modal.limit_message": "L'affichage du champ peut être tronqué sur les téléphones.", + "account_edit.field_edit_modal.link_emoji_warning": "Nous déconseillons l'usage d'émoji personnalisé avec les URL. Les champs personnalisés contenant les deux seront affichés comme du texte et non un lien, afin d'éviter toute confusion.", + "account_edit.field_edit_modal.name_hint": "Par exemple « Site Web personnel »", + "account_edit.field_edit_modal.name_label": "Libellé", + "account_edit.field_edit_modal.value_hint": "Par exemple « exemple.me »", + "account_edit.field_edit_modal.value_label": "Valeur", + "account_edit.field_reorder_modal.drag_cancel": "Déplacement annulé. Le champ « {item} » a été redéposé.", + "account_edit.field_reorder_modal.drag_end": "Le champ « {item} » a été déposé.", + "account_edit.field_reorder_modal.drag_instructions": "Pour réorganiser les champs personnalisés, appuyer sur espace ou entrée. Utiliser les touches fléchées pour déplacer le champ vers le haut ou vers le bas. Appuyer à nouveau sur espace ou entrée pour déposer le champ dans sa nouvelle position, ou appuyer sur échap pour annuler.", + "account_edit.field_reorder_modal.drag_move": "Le champ « {item} » a été déplacé.", + "account_edit.field_reorder_modal.drag_over": "Le champ « {item} » a été déplacé au-dessus de « {over} ».", + "account_edit.field_reorder_modal.drag_start": "Champ « {item} » sélectionné.", + "account_edit.field_reorder_modal.handle_label": "Faites glisser le champ « {item} »", + "account_edit.field_reorder_modal.title": "Réorganiser les champs", "account_edit.name_modal.add_title": "Ajouter un nom public", "account_edit.name_modal.edit_title": "Modifier le nom public", "account_edit.profile_tab.button_label": "Personnaliser", @@ -172,6 +197,13 @@ "account_edit.profile_tab.subtitle": "Personnaliser les onglets de votre profil et leur contenu.", "account_edit.profile_tab.title": "Paramètres de l'onglet du profil", "account_edit.save": "Enregistrer", + "account_edit.verified_modal.details": "Ajouter de la crédibilité à votre profil Mastodon en vérifiant les liens vers vos sites Web personnels. Voici comment cela fonctionne :", + "account_edit.verified_modal.invisible_link.details": "Ajouter le lien dans votre en-tête. La partie importante est « rel=\"me\" » qui empêche l'usurpation d'identité sur des sites Web ayant du contenu généré par d'autres utilisateur·rice·s. Vous pouvez aussi utiliser une balise link dans l'en-tête de la page au lieu de {tag}, mais le code HTML doit être accessible sans avoir besoin d'exécuter du JavaScript.", + "account_edit.verified_modal.invisible_link.summary": "Comment rendre le lien invisible ?", + "account_edit.verified_modal.step1.header": "Copier-coller le code HTML ci-dessous dans l'en-tête de votre site web", + "account_edit.verified_modal.step2.details": "Si vous avez déjà ajouté votre site Web en tant que champ personnalisé, vous devrez le supprimer et le rajouter pour déclencher la vérification.", + "account_edit.verified_modal.step2.header": "Ajouter votre site Web en tant que champ personnalisé", + "account_edit.verified_modal.title": "Comment ajouter un lien vérifié ?", "account_edit_tags.add_tag": "Ajouter #{tagName}", "account_edit_tags.column_title": "Modifier les hashtags mis en avant", "account_edit_tags.help_text": "Les hashtags mis en avant aident les personnes à découvrir et interagir avec votre profil. Ils apparaissent comme des filtres dans la vue « Activité » de votre profil.", @@ -307,9 +339,12 @@ "collections.delete_collection": "Supprimer la collection", "collections.description_length_hint": "Maximum 100 caractères", "collections.detail.accounts_heading": "Comptes", + "collections.detail.author_added_you": "{author} vous a ajouté·e à cette collection", "collections.detail.curated_by_author": "Organisée par {author}", "collections.detail.curated_by_you": "Organisée par vous", "collections.detail.loading": "Chargement de la collection…", + "collections.detail.other_accounts_in_collection": "Autres comptes dans cette collection :", + "collections.detail.sensitive_note": "Cette collection contient des comptes et du contenu qui peut être sensibles.", "collections.detail.share": "Partager la collection", "collections.edit_details": "Modifier les détails", "collections.error_loading_collections": "Une erreur s'est produite durant le chargement de vos collections.", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index fae885d1bde03f..d0472365e77e74 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Cuir Próifíl in Eagar", "account_edit.custom_fields.name": "réimse", "account_edit.custom_fields.placeholder": "Cuir do fhorainmneacha, naisc sheachtracha, nó aon rud eile ar mhaith leat a roinnt leis.", + "account_edit.custom_fields.reorder_button": "Athordaigh réimsí", "account_edit.custom_fields.tip_content": "Is féidir leat creidiúnacht a chur le do chuntas Mastodon go héasca trí naisc chuig aon suíomhanna Gréasáin ar leatsa iad a fhíorú.", "account_edit.custom_fields.tip_title": "Leid: Ag cur naisc fhíoraithe leis", "account_edit.custom_fields.title": "Réimsí saincheaptha", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "An bhfuil fonn ort an réimse saincheaptha a scriosadh?", "account_edit.field_edit_modal.add_title": "Cuir réimse saincheaptha leis", "account_edit.field_edit_modal.edit_title": "Cuir réimse saincheaptha in eagar", + "account_edit.field_edit_modal.limit_header": "Sáraíodh an teorainn carachtar molta", + "account_edit.field_edit_modal.limit_message": "B’fhéidir nach bhfeicfidh úsáideoirí soghluaiste do réimse ina iomláine.", + "account_edit.field_edit_modal.link_emoji_warning": "Molaimid gan emoji saincheaptha a úsáid i gcomhar le Urlanna. Taispeánfar réimsí saincheaptha ina bhfuil an dá cheann mar théacs amháin seachas mar nasc, chun mearbhall úsáideoirí a sheachaint.", "account_edit.field_edit_modal.name_hint": "M.sh. “Suíomh Gréasáin pearsanta”", "account_edit.field_edit_modal.name_label": "Lipéad", "account_edit.field_edit_modal.value_hint": "M.sh. “shampla.me”", "account_edit.field_edit_modal.value_label": "Luach", + "account_edit.field_reorder_modal.drag_cancel": "Cuireadh an tarraingt ar ceal. Baineadh an réimse \"{item}\".", + "account_edit.field_reorder_modal.drag_end": "Baineadh an réimse \"{item}\".", + "account_edit.field_reorder_modal.drag_instructions": "Chun réimsí saincheaptha a athshocrú, brúigh spás nó enter. Agus tú ag tarraingt, bain úsáid as na heochracha saigheada chun an réimse a bhogadh suas nó síos. Brúigh spás nó enter arís chun an réimse a scaoileadh ina shuíomh nua, nó brúigh escape chun cealú.", + "account_edit.field_reorder_modal.drag_move": "Bogadh an réimse \"{item}\".", + "account_edit.field_reorder_modal.drag_over": "Bogadh réimse \"{item}\" thar \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Réimse \"{item}\" bailithe.", + "account_edit.field_reorder_modal.handle_label": "Tarraing réimse \"{item}\"", + "account_edit.field_reorder_modal.title": "Athshocraigh réimsí", "account_edit.name_modal.add_title": "Cuir ainm taispeána leis", "account_edit.name_modal.edit_title": "Cuir ainm taispeána in eagar", "account_edit.profile_tab.button_label": "Saincheap", @@ -327,9 +339,12 @@ "collections.delete_collection": "Scrios bailiúchán", "collections.description_length_hint": "Teorainn 100 carachtar", "collections.detail.accounts_heading": "Cuntais", + "collections.detail.author_added_you": "Chuir {author} leis an mbailiúchán seo thú", "collections.detail.curated_by_author": "Curtha i dtoll a chéile ag {author}", "collections.detail.curated_by_you": "Curtha i dtoll a chéile agatsa", "collections.detail.loading": "Ag lódáil an bhailiúcháin…", + "collections.detail.other_accounts_in_collection": "Daoine eile sa bhailiúchán seo:", + "collections.detail.sensitive_note": "Tá cuntais agus ábhar sa bhailiúchán seo a d'fhéadfadh a bheith íogair do roinnt úsáideoirí.", "collections.detail.share": "Comhroinn an bailiúchán seo", "collections.edit_details": "Cuir sonraí in eagar", "collections.error_loading_collections": "Tharla earráid agus iarracht á déanamh do bhailiúcháin a luchtú.", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 93b3e0ea011c5c..808bc1f41050dd 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Editar perfil", "account_edit.custom_fields.name": "campo", "account_edit.custom_fields.placeholder": "Engade os teus pronomes, ligazóns externas, ou o que queiras compartir.", + "account_edit.custom_fields.reorder_button": "Reordenar os campos", "account_edit.custom_fields.tip_content": "Podes darlle maior credibilidade á túa conta Mastodon se verificas as ligazóns a sitios web da túa propiedade.", "account_edit.custom_fields.tip_title": "Consello: Engadir ligazóns verificadas", "account_edit.custom_fields.title": "Campos personalizados", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Eliminar campo persoal?", "account_edit.field_edit_modal.add_title": "Engadir campo persoal", "account_edit.field_edit_modal.edit_title": "Editar campo persoal", + "account_edit.field_edit_modal.limit_header": "Superouse o límite de caracteres recomendado", + "account_edit.field_edit_modal.limit_message": "Nos dispositivos móbiles podería non verse o campo completo.", + "account_edit.field_edit_modal.link_emoji_warning": "Non recomendamos o uso de emojis persoais combinados con URLs. Os campos persoais que conteñen ambos móstranse só como texto e non como unha ligazón, para evitar a confusión de quen os lea.", "account_edit.field_edit_modal.name_hint": "Ex. \"Páxina web persoal\"", "account_edit.field_edit_modal.name_label": "Etiqueta", "account_edit.field_edit_modal.value_hint": "Ex. \"exemplo.gal\"", "account_edit.field_edit_modal.value_label": "Valor", + "account_edit.field_reorder_modal.drag_cancel": "Cancelouse o arrastrado. Soltouse o campo \"{item}\".", + "account_edit.field_reorder_modal.drag_end": "Soltouse o campo \"{item}\".", + "account_edit.field_reorder_modal.drag_instructions": "Para ordear os campos persoais, preme espazo ou enter. Ao arrastrar, usa as teclas de frecha para mover o campo arriba ou abaixo. Preme espazo ou enter outra vez para soltar o campo na nova posición, ou preme escape para cancelar.", + "account_edit.field_reorder_modal.drag_move": "Moveuse o campo \"{item}\".", + "account_edit.field_reorder_modal.drag_over": "Moveuse o campo \"{item}\" enriba de \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Seleccionado o campo \"{item}\".", + "account_edit.field_reorder_modal.handle_label": "Arrastra o campo \"{item}\"", + "account_edit.field_reorder_modal.title": "Ordear campos", "account_edit.name_modal.add_title": "Engadir nome público", "account_edit.name_modal.edit_title": "Editar o nome público", "account_edit.profile_tab.button_label": "Personalizar", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 54af1720afca2a..cb8a9dae0f9bc2 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "עריכת {item}", "account_edit.column_button": "סיום", "account_edit.column_title": "עריכת הפרופיל", + "account_edit.custom_fields.name": "שדה", "account_edit.custom_fields.placeholder": "הוסיפו צורת פניה, קישורים חיצוניים וכל דבר שתרצו לשתף.", + "account_edit.custom_fields.reorder_button": "הגדרת סדר השדות", + "account_edit.custom_fields.tip_content": "ניתן להוסיף אמינות לחשבון המסטודון שלך על ידי וידוא קישורים לאתרים שבבעלותך.", + "account_edit.custom_fields.tip_title": "טיפ: הוספת קישורים מוודאים", "account_edit.custom_fields.title": "שדות בהתאמה אישית", + "account_edit.custom_fields.verified_hint": "כיצד תוסיפו קישורים מוודאים?", "account_edit.display_name.placeholder": "שם התצוגה שלכן הוא איך שהשם יופיע בפרופיל ובצירי הזמנים.", "account_edit.display_name.title": "שם תצוגה", "account_edit.featured_hashtags.item": "תגיות", "account_edit.featured_hashtags.placeholder": "עזרו לאחרים לזהות ולגשת בקלות לנושאים החביבים עליכם.", "account_edit.featured_hashtags.title": "תגיות נבחרות", + "account_edit.field_delete_modal.confirm": "האם אתם בטוחיםות שברצונכן למחוק את השדה המיוחד הזה? פעולה זו לא ניתנת לביטול.", + "account_edit.field_delete_modal.delete_button": "מחק", + "account_edit.field_delete_modal.title": "מחיקת שדה מתואם אישית?", + "account_edit.field_edit_modal.add_title": "הוסף שדה מותאם אישית", + "account_edit.field_edit_modal.edit_title": "עריכת שדה מותאם אישית", + "account_edit.field_edit_modal.limit_header": "עברת את מגבלת התווים המומלצת", + "account_edit.field_edit_modal.limit_message": "משתמשים מטלפון חכם עלולים לא לראות את השדה במלואו.", + "account_edit.field_edit_modal.link_emoji_warning": "אנו ממליצים נגד שימוש באמוג'י ייחודיים ביחד עם URL. שדות מיוחדים שמכילים את שניהם יופיעו כמלל בלבד ולא כקישור, כדי למנוע בלבול משתמשים.", + "account_edit.field_edit_modal.name_hint": "למשל \"אתר אישי\"", + "account_edit.field_edit_modal.name_label": "תווית", + "account_edit.field_edit_modal.value_hint": "למשל “example.me”", + "account_edit.field_edit_modal.value_label": "ערך", + "account_edit.field_reorder_modal.drag_cancel": "הגרירה בוטלה. השדה \"{item}\" נעזב.", + "account_edit.field_reorder_modal.drag_end": "השדה \"{item}\" נעזב.", + "account_edit.field_reorder_modal.drag_instructions": "כדי לארגן מחדש את השדות המיוחדים, לחצו רווח או אנטר. כאשר גוררים, השתמשו במקשי החיצים כדי להזיז שדה מעלה או מטה. לחצו רווח או אנטר בשנית כדי לעזוב את השדה במיקומו החדש, או ESC לביטול.", + "account_edit.field_reorder_modal.drag_move": "השדה \"{item}\" הוזז.", + "account_edit.field_reorder_modal.drag_over": "השדה \"{item}\" הוזז על \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "השדה \"{item}\" נבחר.", + "account_edit.field_reorder_modal.handle_label": "הזזת השדה \"{item}\"", + "account_edit.field_reorder_modal.title": "סידור שדות", "account_edit.name_modal.add_title": "הוספת שם תצוגה", "account_edit.name_modal.edit_title": "עריכת שם תצוגה", "account_edit.profile_tab.button_label": "התאמה אישית", @@ -172,6 +197,13 @@ "account_edit.profile_tab.subtitle": "התאימו את הטאבים בפרופיל שלכם ומה שהם יציגו.", "account_edit.profile_tab.title": "הגדרות טאבים לפרופיל", "account_edit.save": "שמירה", + "account_edit.verified_modal.details": "הוספת אמינות לחשבון המסטודון על ידי הוספת קישורים מוודאים לאתרים אישיים. כך זה עובד:", + "account_edit.verified_modal.invisible_link.details": "הוסיפו את הקישור בכותרת. החלק החשוב הוא rel=\"me\" שמונע התחזות על אתרים עם תוכן משתמשים. ניתן גם ליצור תגית link בכותרת העמוד במקום קישור {tag} אבל קוד ה־HTML חייב להופיע שם ללא הרצה של ג'אווהסקריפט.", + "account_edit.verified_modal.invisible_link.summary": "כיצד לגרום לקישור להיות בלתי נראה?", + "account_edit.verified_modal.step1.header": "העתיקו את קוד HTML שלמטה והדביקו אותו לכותרת האתר שלכם", + "account_edit.verified_modal.step2.details": "אם כבר הוספתן את אתרכן בשדה המיוחד, תצטרכו למחוק וליצור אותו מחדש כדי לגרום לתהליך הווידוא.", + "account_edit.verified_modal.step2.header": "הוסיפו את אתרכן בשדה המיוחד", + "account_edit.verified_modal.title": "כיצד תוסיפו קישורים מוודאים", "account_edit_tags.add_tag": "הוספת #{tagName}", "account_edit_tags.column_title": "עריכת תגיות נבחרות", "account_edit_tags.help_text": "תגיות נבחרות עוזרות למשתמשים לגלות ולהשתמש בפרופיל שלך. הן יופיעו כסננים במבט הפעילויות על עמוד הפרופיל שלך.", @@ -307,9 +339,12 @@ "collections.delete_collection": "מחיקת האוסף", "collections.description_length_hint": "מגבלה של 100 תווים", "collections.detail.accounts_heading": "חשבונות", + "collections.detail.author_added_you": "{author} הוסיפו אותך לאוסף", "collections.detail.curated_by_author": "נאצר על ידי {author}", "collections.detail.curated_by_you": "נאצר על ידיך", "collections.detail.loading": "טוען אוסף…", + "collections.detail.other_accounts_in_collection": "אחרים באוסף:", + "collections.detail.sensitive_note": "האוסף מכיל חשבונות ותכנים שאולי יחשבו רגישים לחלק מהמשתמשים.", "collections.detail.share": "שיתוף אוסף", "collections.edit_details": "עריכת פרטים", "collections.error_loading_collections": "חלה שגיאה בנסיון לטעון את אוספיך.", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 43b095e011c815..117cdb82810841 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Breyta notandasniði", "account_edit.custom_fields.name": "reitur", "account_edit.custom_fields.placeholder": "Settu inn fornöfn sem þú vilt nota, ytri tengla eða hvaðeina sem þú vilt deila með öðrum.", + "account_edit.custom_fields.reorder_button": "Endurraða reitum", "account_edit.custom_fields.tip_content": "Þú getur á einfaldan hátt aukið trúverðugleika Mastodon-aðgangsins þíns með því að bæta við staðfestingartenglum sem vísa á vefsvæði sem þú átt.", "account_edit.custom_fields.tip_title": "Ábending: Bæta við staðfestingartenglum", "account_edit.custom_fields.title": "Sérsniðnir reitir", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Eyða sérsniðnum reit?", "account_edit.field_edit_modal.add_title": "Bæta við sérsniðnum reit", "account_edit.field_edit_modal.edit_title": "Breyta sérsniðnum reit", + "account_edit.field_edit_modal.limit_header": "Fór yfir takmörk á fjölda stafa", + "account_edit.field_edit_modal.limit_message": "Notendur á símtækjum gætu lent því að sjá ekki allan reitinn.", + "account_edit.field_edit_modal.link_emoji_warning": "Við mælum gegn því að nota sérsniðin tjáningartákn saman með vefslóðum. Sérsniðnir reitir sem innihalda hvort tveggja munu birtast sem einungis texti í stað þess að vera tenglar, til að koma í veg fyrir að notendur taki annað í misgripum fyrir hitt.", "account_edit.field_edit_modal.name_hint": "T.d. \"Eigið vefsvæði\"", "account_edit.field_edit_modal.name_label": "Skýring", "account_edit.field_edit_modal.value_hint": "T.d. \"minnvefur.is\"", "account_edit.field_edit_modal.value_label": "Gildi", + "account_edit.field_reorder_modal.drag_cancel": "Hætt var við að draga. Reitnum \"{item}\" var sleppt.", + "account_edit.field_reorder_modal.drag_end": "Reitnum \"{item}\" var sleppt.", + "account_edit.field_reorder_modal.drag_instructions": "Til að endurraða sérsniðnum reitum skaltu ýta á bilslá eða Enter. Á meðan verið er að draga geturðu notað örvalyklana til að færa reitinn upp eða niður. Ýttu aftur á bilslá eða Enter til að to sleppa reitnum á nýja staðinn sinn, eða ýtt á Escape til að hætta við.", + "account_edit.field_reorder_modal.drag_move": "Reiturinn \"{item}\" var færður.", + "account_edit.field_reorder_modal.drag_over": "Reiturinn \"{item}\" var færður yfir \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Náði reitnum \"{item}\".", + "account_edit.field_reorder_modal.handle_label": "Dragðu reitinn \"{item}\"", + "account_edit.field_reorder_modal.title": "Endurraða gagnasviðum", "account_edit.name_modal.add_title": "Bættu við birtingarnafni", "account_edit.name_modal.edit_title": "Breyta birtingarnafni", "account_edit.profile_tab.button_label": "Sérsníða", @@ -186,7 +198,11 @@ "account_edit.profile_tab.title": "Stillingar notandasniðsflipa", "account_edit.save": "Vista", "account_edit.verified_modal.details": "Auktu trúverðugleika Mastodon-aðgangsins þíns með því að bæta við staðfestingartenglum sem vísa á vefsvæðin þín. Hérna sérðu hvernig það virkar:", + "account_edit.verified_modal.invisible_link.details": "Bættu tenglinum í hausinn hjá þér. Mikilvægi hlutinn er rel=\"me\" sem kemur í veg fyrir blekkingu verðandi persónuauðkenni á vefsvæðum með notandaframleiddu efni. Þú getur jafnvel notað tengimerkið í haus síðunnar í staðinn fyrir {tag}, en HTML-kóðinn verður samt að vera aðgengilegur án þess að keyra þurfi JavaScript.", "account_edit.verified_modal.invisible_link.summary": "Hvernig geri ég tengilinn ósýnilegan?", + "account_edit.verified_modal.step1.header": "Afritaðu HTML-kóðann hér fyrir neðan og límdu hann inn í haus vefsvæðisins þíns", + "account_edit.verified_modal.step2.details": "Ef þú hefur þegar bættu vefsvæðinu þínu inn sem sérsniðnum reit, þá muntu þurfa að eyða honum og bæta við aftur til að gangsetja sannvottun.", + "account_edit.verified_modal.step2.header": "Bættu vefsvæðinu þínu inn sem sérsniðinn reit", "account_edit.verified_modal.title": "Hvernig er hægt að bæta við staðfestingartengli", "account_edit_tags.add_tag": "Bæta við #{tagName}", "account_edit_tags.column_title": "Breyta myllumerkjum með aukið vægi", @@ -323,9 +339,12 @@ "collections.delete_collection": "Eyða safni", "collections.description_length_hint": "100 stafa takmörk", "collections.detail.accounts_heading": "Aðgangar", + "collections.detail.author_added_you": "{author} bætti þér í þetta safn", "collections.detail.curated_by_author": "Safnað saman af {author}", "collections.detail.curated_by_you": "Safnað saman af þér", "collections.detail.loading": "Hleð inn safni…", + "collections.detail.other_accounts_in_collection": "Aðrir í þessu safni:", + "collections.detail.sensitive_note": "Þetta safn inniheldur aðganga og efni sem sumir notendur gætu verið viðkvæmir fyrir.", "collections.detail.share": "Deila þessu safni", "collections.edit_details": "Breyta ítarupplýsingum", "collections.error_loading_collections": "Villa kom upp þegar reynt var að hlaða inn söfnunum þínum.", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index e6e1e21435e205..29aacb056126b7 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Modifica il profilo", "account_edit.custom_fields.name": "campo", "account_edit.custom_fields.placeholder": "Aggiungi i tuoi pronomi, collegamenti esterni o qualsiasi altra cosa desideri condividere.", + "account_edit.custom_fields.reorder_button": "Riordina i campi", "account_edit.custom_fields.tip_content": "Puoi facilmente aggiungere credibilità al tuo account Mastodon, verificando i collegamenti a qualsiasi sito web di tua proprietà.", "account_edit.custom_fields.tip_title": "Suggerimento: aggiunta di collegamenti verificati", "account_edit.custom_fields.title": "Campi personalizzati", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Eliminare il campo personalizzato?", "account_edit.field_edit_modal.add_title": "Aggiungi campo personalizzato", "account_edit.field_edit_modal.edit_title": "Modifica campo personalizzato", + "account_edit.field_edit_modal.limit_header": "Superato il limite di caratteri consigliato", + "account_edit.field_edit_modal.limit_message": "Gli utenti dai dispositivi mobili potrebbero non visualizzare completamente il tuo campo.", + "account_edit.field_edit_modal.link_emoji_warning": "Sconsigliamo l'uso di emoji personalizzate in combinazione con gli URL. I campi personalizzati che contengono entrambi verranno visualizzati solo come testo anziché come link, in modo da evitare confusione nell'utente.", "account_edit.field_edit_modal.name_hint": "Ad esempio: “Sito web personale”", "account_edit.field_edit_modal.name_label": "Etichetta", "account_edit.field_edit_modal.value_hint": "Ad esempio: “example.me”", "account_edit.field_edit_modal.value_label": "Valore", + "account_edit.field_reorder_modal.drag_cancel": "Il trascinamento è stato annullato. Il campo \"{item}\" è stato eliminato.", + "account_edit.field_reorder_modal.drag_end": "Il campo \"{item}\" è stato eliminato.", + "account_edit.field_reorder_modal.drag_instructions": "Per riorganizzare i campi personalizzati, premi la barra spaziatrice o Invio. Durante il trascinamento, usa i tasti freccia per spostare il campo verso l'alto o verso il basso. Premi di nuovo la barra spaziatrice o Invio per rilasciare il campo nella sua nuova posizione, oppure premi Esc per annullare.", + "account_edit.field_reorder_modal.drag_move": "Il campo \"{item}\" è stato spostato.", + "account_edit.field_reorder_modal.drag_over": "Il campo \"{item}\" è stato spostato su \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Campo \"{item}\" selezionato.", + "account_edit.field_reorder_modal.handle_label": "Trascina il campo \"{item}\"", + "account_edit.field_reorder_modal.title": "Riorganizza i campi", "account_edit.name_modal.add_title": "Aggiungi il nome mostrato", "account_edit.name_modal.edit_title": "Modifica il nome mostrato", "account_edit.profile_tab.button_label": "Personalizza", @@ -327,9 +339,12 @@ "collections.delete_collection": "Cancella la collezione", "collections.description_length_hint": "Limite di 100 caratteri", "collections.detail.accounts_heading": "Account", + "collections.detail.author_added_you": "{author} ti ha aggiunto a questa collezione", "collections.detail.curated_by_author": "Curata da {author}", "collections.detail.curated_by_you": "Curata da te", "collections.detail.loading": "Caricamento della collezione…", + "collections.detail.other_accounts_in_collection": "Altri in questa collezione:", + "collections.detail.sensitive_note": "Questa collezione contiene account e contenuto che potrebbero essere sensibili ad alcuni utenti.", "collections.detail.share": "Condividi questa collezione", "collections.edit_details": "Modifica i dettagli", "collections.error_loading_collections": "Si è verificato un errore durante il tentativo di caricare le tue collezioni.", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index af65322b112ff2..b19fde5bdf1924 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -830,7 +830,8 @@ "privacy.private.short": "팔로워", "privacy.public.long": "마스토돈 내외 모두", "privacy.public.short": "공개", - "privacy.quote.disabled": "{visibility}, 인용 비활성화", + "privacy.quote.anyone": "{visibility}, 인용 허용", + "privacy.quote.disabled": "{visibility}, 인용 비활성", "privacy.quote.limited": "{visibility}, 제한된 인용", "privacy.unlisted.additional": "공개와 똑같지만 게시물이 실시간 피드나 해시태그, 둘러보기, (계정 설정에서 허용했더라도) 마스토돈 검색에서 제외됩니다.", "privacy.unlisted.long": "마스토돈 검색결과, 유행, 공개 타임라인에서 숨기기", diff --git a/app/javascript/mastodon/locales/nan-TW.json b/app/javascript/mastodon/locales/nan-TW.json index 0c863ab16ea29f..97055039768986 100644 --- a/app/javascript/mastodon/locales/nan-TW.json +++ b/app/javascript/mastodon/locales/nan-TW.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "編 {item}", "account_edit.column_button": "做好ah", "account_edit.column_title": "編輯個人資料", + "account_edit.custom_fields.name": "框á", "account_edit.custom_fields.placeholder": "加lí ê代名詞、外部連結,á是其他lí beh分享ê。", + "account_edit.custom_fields.reorder_button": "重排框á", + "account_edit.custom_fields.tip_content": "Lí通用驗證連kàu lí 所有ê網站ê連結,來增加lí ê Mastodon口座ê通信ê程度。", + "account_edit.custom_fields.tip_title": "撇步:加驗證過ê連結", "account_edit.custom_fields.title": "自訂欄", + "account_edit.custom_fields.verified_hint": "我beh án-tsuánn加驗證過ê連結?", "account_edit.display_name.placeholder": "Lí ê顯示ê名是lí ê名佇lí ê個人資料kap時間線出現ê方式。", "account_edit.display_name.title": "顯示ê名", "account_edit.featured_hashtags.item": "hashtag", "account_edit.featured_hashtags.placeholder": "幫tsān別lâng認捌,kap緊緊接近使用lí收藏ê主題。", "account_edit.featured_hashtags.title": "特色ê hashtag", + "account_edit.field_delete_modal.confirm": "Lí敢確定behthâi掉tsit ê自訂ê框á?Tsit ê動作bē當改倒轉。", + "account_edit.field_delete_modal.delete_button": "Thâi掉", + "account_edit.field_delete_modal.title": "敢beh thâi掉自訂ê框á?", + "account_edit.field_edit_modal.add_title": "加自訂ê框á", + "account_edit.field_edit_modal.edit_title": "編自訂ê框á", + "account_edit.field_edit_modal.limit_header": "超過建議ê字數限制ah", + "account_edit.field_edit_modal.limit_message": "行動設備ê用者有可能bē當看著lí所有ê框á。", + "account_edit.field_edit_modal.link_emoji_warning": "Lán無建議佇URL內底用自訂ê emoji。為著避免用者舞花去,自訂ê框á若包含自訂emoji kap URL,kan-ta ē顯示做文字。", + "account_edit.field_edit_modal.name_hint": "例:「個人網站」", + "account_edit.field_edit_modal.name_label": "標簽", + "account_edit.field_edit_modal.value_hint": "例:「example.me」", + "account_edit.field_edit_modal.value_label": "值", + "account_edit.field_reorder_modal.drag_cancel": "Giú ê動作取消ah。框á「{item}」予lâng khǹg ah。", + "account_edit.field_reorder_modal.drag_end": "框á「{item}」予lâng khǹg ah。", + "account_edit.field_reorder_modal.drag_instructions": "Beh重整理自訂ê框á,請tshi̍h空白khí或者是Enter。Teh suá振動ê時,請用方向khí來kā框á suá kah頂懸á是下底。請koh tshi̍h空白khí或者是Enter來kā框á khǹg佇新ê位置,á是tshi̍h ESC khí取消。", + "account_edit.field_reorder_modal.drag_move": "框á「{item}」suá振動ah。", + "account_edit.field_reorder_modal.drag_over": "框á「{item}」已經suá kah「{over}」面頂。", + "account_edit.field_reorder_modal.drag_start": "有揀框á「{item}」。", + "account_edit.field_reorder_modal.handle_label": "Giú框á「{item}」", + "account_edit.field_reorder_modal.title": "重整理框á", "account_edit.name_modal.add_title": "加添顯示ê名", "account_edit.name_modal.edit_title": "編顯示ê名", "account_edit.profile_tab.button_label": "自訂", @@ -172,6 +197,14 @@ "account_edit.profile_tab.subtitle": "自訂lí ê個人資料ê分頁kap顯示ê內容。", "account_edit.profile_tab.title": "個人資料分頁設定", "account_edit.save": "儲存", + "account_edit.verified_modal.details": "用驗證連kàu個人網站ê連結來加添lí ê Mastodon個人檔案ê通信ê程度。下kha是運作ê方法:", + "account_edit.verified_modal.invisible_link.details": "加連結kàu lí ê網頁頭(header)。上重要ê部份是 rel=\"me\",伊防止通過用者生成ê網站內容來做假包。Lí甚至佇網頁ê header毋免用 {tag},反轉用link標簽,但是HTML定著佇無執行JavaScript ê時陣,就ē當接近使用。", + "account_edit.verified_modal.invisible_link.summary": "Án-tsuánn khàm掉tsit ê連結?", + "account_edit.verified_modal.step1.header": "Khóo-pih 下kha ê HTML程式碼,貼佇lí ê網站ê網頁頭(header)底", + "account_edit.verified_modal.step2.details": "Lí若有加lí ê網站成做自訂ê框á,lí需要thâi掉,koh加tse來啟動驗證。", + "account_edit.verified_modal.step2.header": "Kā lí ê網站加做自訂ê框á", + "account_edit.verified_modal.title": "Án-tsuánn加驗證過ê連結", + "account_edit_tags.add_tag": "加 #{tagName}", "account_edit_tags.column_title": "編收藏ê hashtag", "account_edit_tags.help_text": "收藏ê hashtag幫tsān用者發現kap hām lí ê個人資料互動。In會成做過濾器,佇lí ê個人資料頁ê活動內底出現。", "account_edit_tags.search_placeholder": "編輯hashtag……", @@ -274,6 +307,8 @@ "callout.dismiss": "忽略", "carousel.current": "頁面 {current, number} / {max, number}", "carousel.slide": "{max, number} 頁內底ê第 {current, number} 頁", + "character_counter.recommended": "{currentLength}/{maxLength} ê建議字數", + "character_counter.required": "{currentLength}/{maxLength} 字數", "closed_registrations.other_server_instructions": "因為Mastodon非中心化,所以lí ē當tī別ê服侍器建立口座,iáu ē當kap tsit ê服侍器來往。", "closed_registrations_modal.description": "Tann bē當tī {domain} 建立新ê口座,m̄-koh著記得,lí bô需要 {domain} 服侍器ê帳號,mā ē當用 Mastodon。", "closed_registrations_modal.find_another_server": "Tshuē別ê服侍器", @@ -304,9 +339,12 @@ "collections.delete_collection": "Thâi掉收藏", "collections.description_length_hint": "限制 100 字", "collections.detail.accounts_heading": "口座", + "collections.detail.author_added_you": "{author} kā lí加kàu tsit ê收藏", "collections.detail.curated_by_author": "{author} 揀ê", "collections.detail.curated_by_you": "Lí揀ê", "collections.detail.loading": "載入收藏……", + "collections.detail.other_accounts_in_collection": "Tsit ê收藏內ê別lâng:", + "collections.detail.sensitive_note": "Tsit ê收藏包含對一寡用者敏感ê口座kap內容。", "collections.detail.share": "分享tsit ê收藏", "collections.edit_details": "編輯詳細", "collections.error_loading_collections": "佇載入lí ê收藏ê時陣出tshê。", @@ -679,6 +717,7 @@ "keyboard_shortcuts.direct": "Phah開私人提起ê欄", "keyboard_shortcuts.down": "佇列單內kā suá khah 下kha", "keyboard_shortcuts.enter": "Phah開PO文", + "keyboard_shortcuts.explore": "拍開趨勢ê時間線", "keyboard_shortcuts.favourite": "收藏PO文", "keyboard_shortcuts.favourites": "Phah開收藏ê列單", "keyboard_shortcuts.federated": "Phah開聯邦ê時間線", @@ -765,6 +804,7 @@ "navigation_bar.automated_deletion": "自動thâi PO文", "navigation_bar.blocks": "封鎖ê用者", "navigation_bar.bookmarks": "冊籤", + "navigation_bar.collections": "收藏", "navigation_bar.direct": "私人ê提起", "navigation_bar.domain_blocks": "封鎖ê域名", "navigation_bar.favourites": "Siōng kah意", @@ -1068,6 +1108,9 @@ "sign_in_banner.mastodon_is": "Mastodon是跟tuè siánn物當teh發生ê上贊ê方法。", "sign_in_banner.sign_in": "登入", "sign_in_banner.sso_redirect": "登入á是註冊", + "skip_links.hotkey": "組合khí {hotkey}", + "skip_links.skip_to_content": "跳kàu主內容", + "skip_links.skip_to_navigation": "跳kàu主導覽", "status.admin_account": "Phah開 @{name} ê管理界面", "status.admin_domain": "Phah開 {domain} ê管理界面", "status.admin_status": "Tī管理界面內底看tsit篇PO文", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index ded303958f1eba..d330ee430231a3 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -151,17 +151,31 @@ "account_edit.button.edit": "Editar {item}", "account_edit.column_button": "Feito", "account_edit.column_title": "Editar perfil", + "account_edit.custom_fields.name": "Campo", "account_edit.custom_fields.placeholder": "Insira seus pronomes, links externos ou qualquer coisa que queira compartilhar.", + "account_edit.custom_fields.tip_content": "Você pode facilmente dar credibilidade à sua conta Mastodon verificando os links para os seus sites.", + "account_edit.custom_fields.tip_title": "Dica: Adicionando links verificados", "account_edit.custom_fields.title": "Campos personalizados", + "account_edit.custom_fields.verified_hint": "Como adiciono um link verificado?", "account_edit.display_name.placeholder": "Seu nome de exibição é a forma com que seu nome aparece em seu perfil e em suas linhas do tempo.", "account_edit.display_name.title": "Nome de exibição", "account_edit.featured_hashtags.item": "hashtags", "account_edit.featured_hashtags.placeholder": "Ajude outros a identificar e ter acesso rápido a seus tópicos favoritos.", "account_edit.featured_hashtags.title": "Hashtags em destaque", + "account_edit.field_delete_modal.confirm": "Tem certeza que deseja excluir este campo personalizado? Esta ação não pode ser desfeita.", + "account_edit.field_delete_modal.delete_button": "Excluir", + "account_edit.field_delete_modal.title": "Excluir campo personalizado?", + "account_edit.field_edit_modal.add_title": "Adicionar campo personalizado", + "account_edit.field_edit_modal.edit_title": "Editar campo personalizado", + "account_edit.field_edit_modal.name_hint": "Ex. \"Site pessoal\"", + "account_edit.field_edit_modal.name_label": "Descrição", + "account_edit.field_edit_modal.value_label": "Valor", "account_edit.name_modal.add_title": "Inserir nome de exibição", "account_edit.name_modal.edit_title": "Editar nome de exibição", "account_edit.profile_tab.button_label": "Personalizar", + "account_edit.profile_tab.hint.description": "Essas configurações definem o que os usuários veem no {servidor} nos apps oficiais, mas podem não se aplicar a usuários em servidores e apps de terceiros.", "account_edit.profile_tab.hint.title": "Exibições divergem", + "account_edit.profile_tab.show_featured.description": "'Em Destaque' é uma aba opcional onde você pode exibir outras contas.", "account_edit.profile_tab.show_featured.title": "Mostrar aba \"Destaque\"", "account_edit.profile_tab.show_media.description": "\"Mídia\" é uma aba opcional que mostra seus posts, contendo imagens ou vídeos.", "account_edit.profile_tab.show_media.title": "Mostrar aba \"Mídia\"", @@ -170,6 +184,13 @@ "account_edit.profile_tab.subtitle": "Personalizar as abas em seu perfil e o que elas exibem.", "account_edit.profile_tab.title": "Configurações da aba de perfil", "account_edit.save": "Salvar", + "account_edit.verified_modal.details": "Dê credibilidade ao seu perfil do Mastodon, verificando links para sites pessoais. Veja como funciona:", + "account_edit.verified_modal.invisible_link.summary": "Como posso tornar o link invisível?", + "account_edit.verified_modal.step1.header": "Copie o código HTML abaixo e cole no cabeçalho do seu site", + "account_edit.verified_modal.step2.details": "Se já adicionou seu site como um campo personalizado, deverá excluí-lo e adicioná-lo novamente para acionar a verificação.", + "account_edit.verified_modal.step2.header": "Adicione seu site como um campo personalizado", + "account_edit.verified_modal.title": "Como adicionar um link verificado", + "account_edit_tags.add_tag": "Adicionar #{tagName}", "account_edit_tags.column_title": "Editar hashtags em destaque", "account_edit_tags.help_text": "Hashtags em destaque ajudam os usuários a descobrir e interagir com seu perfil. Elas aparecem como filtros na visualização de Atividade da sua página de Perfil.", "account_edit_tags.search_placeholder": "Insira uma hashtag…", @@ -272,6 +293,8 @@ "callout.dismiss": "Rejeitar", "carousel.current": "Slide {current, number} / {max, number}", "carousel.slide": "Slide {current, number} de {max, number}", + "character_counter.recommended": "{currentLength}/{maxLength} caracteres sugeridos", + "character_counter.required": "{currentLength}/{maxLength} caracteres", "closed_registrations.other_server_instructions": "Como o Mastodon é descentralizado, você pode criar uma conta em outro servidor e ainda pode interagir com este.", "closed_registrations_modal.description": "Não é possível criar uma conta em {domain} no momento, mas atente que você não precisa de uma conta especificamente em {domain} para usar o Mastodon.", "closed_registrations_modal.find_another_server": "Encontrar outro servidor", @@ -302,9 +325,12 @@ "collections.delete_collection": "Eliminar coleção", "collections.description_length_hint": "Limite de 100 caracteres", "collections.detail.accounts_heading": "Contas", + "collections.detail.author_added_you": "{author} adicionou você a esta coleção", "collections.detail.curated_by_author": "Curadoria de {author}", "collections.detail.curated_by_you": "Curadoria por você", "collections.detail.loading": "Carregando coleção…", + "collections.detail.other_accounts_in_collection": "Outros nesta coleção:", + "collections.detail.sensitive_note": "Esta coleção contém contas e conteúdo que podem ser sensíveis a alguns usuários.", "collections.detail.share": "Compartilhar esta coleção", "collections.edit_details": "Editar detalhes", "collections.error_loading_collections": "Houve um erro ao tentar carregar suas coleções.", @@ -677,6 +703,7 @@ "keyboard_shortcuts.direct": "Abrir coluna de menções privadas", "keyboard_shortcuts.down": "mover para baixo", "keyboard_shortcuts.enter": "Abrir publicação", + "keyboard_shortcuts.explore": "Abrir linha do tempo atual", "keyboard_shortcuts.favourite": "Favoritar publicação", "keyboard_shortcuts.favourites": "Abrir lista de favoritos", "keyboard_shortcuts.federated": "abrir linha global", @@ -763,6 +790,7 @@ "navigation_bar.automated_deletion": "Publicação de eliminação automático", "navigation_bar.blocks": "Usuários bloqueados", "navigation_bar.bookmarks": "Salvos", + "navigation_bar.collections": "Coleções", "navigation_bar.direct": "Menções privadas", "navigation_bar.domain_blocks": "Domínios bloqueados", "navigation_bar.favourites": "Favoritos", @@ -1066,6 +1094,8 @@ "sign_in_banner.mastodon_is": "O Mastodon é a melhor maneira de acompanhar o que está acontecendo.", "sign_in_banner.sign_in": "Entrar", "sign_in_banner.sso_redirect": "Entrar ou Registrar-se", + "skip_links.skip_to_content": "Conteúdo principal", + "skip_links.skip_to_navigation": "Navegação principal", "status.admin_account": "Abrir interface de moderação para @{name}", "status.admin_domain": "Abrir interface de moderação para {domain}", "status.admin_status": "Abrir esta publicação na interface de moderação", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 9ee10e95d49694..4653db73b14a73 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -151,13 +151,27 @@ "account_edit.button.edit": "Editar {item}", "account_edit.column_button": "Concluído", "account_edit.column_title": "Editar Perfil", + "account_edit.custom_fields.name": "campo", "account_edit.custom_fields.placeholder": "Adicione os seus pronomes, hiperligações externas ou qualquer outra coisa que queira partilhar.", + "account_edit.custom_fields.reorder_button": "Reordenar campos", + "account_edit.custom_fields.tip_content": "Pode adicionar facilmente credibilidade à sua conta Mastodon, verificando ligações para qualquer website que possua.", "account_edit.custom_fields.title": "Campos personalizados", "account_edit.display_name.placeholder": "Como o seu nome vai aparecer no seu perfil e nas linhas do tempo.", "account_edit.display_name.title": "Nome a mostrar", "account_edit.featured_hashtags.item": "etiquetas", "account_edit.featured_hashtags.placeholder": "Ajude à sua identificação por outros e tenha acesso rápido aos seus tópicos favoritos.", "account_edit.featured_hashtags.title": "Etiquetas em destaque", + "account_edit.field_edit_modal.limit_header": "Limite de caracteres recomendado excedido", + "account_edit.field_edit_modal.limit_message": "Os utilizadores de dispositivos móveis podem não conseguir ver o seu campo na totalidade.", + "account_edit.field_edit_modal.link_emoji_warning": "Recomendamos que não utilize emojis personalizados em conjunto com URLs. Os campos personalizados que contenham ambos serão exibidos apenas como texto em vez de uma hiperligação, de modo a evitar a confusão dos utilizadores.", + "account_edit.field_reorder_modal.drag_cancel": "O arrastamento foi cancelado. O campo \"{item}\" foi largado.", + "account_edit.field_reorder_modal.drag_end": "O campo \"{item}\" foi largado.", + "account_edit.field_reorder_modal.drag_instructions": "Para reorganizar os campos personalizados, prima a tecla de espaço ou enter. Enquanto arrasta, utilize as teclas de setas para mover o campo para cima ou para baixo. Prima novamente a tecla de espaço ou enter para largar o campo na nova posição, ou prima escape para cancelar.", + "account_edit.field_reorder_modal.drag_move": "O campo \"{item}\" foi movido.", + "account_edit.field_reorder_modal.drag_over": "O campo \"{item}\" foi movido para cima de \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Apanhou o campo \"{item}\".", + "account_edit.field_reorder_modal.handle_label": "Arrastar o campo \"{item}\"", + "account_edit.field_reorder_modal.title": "Reordenar campos", "account_edit.name_modal.add_title": "Adicionar nome a mostrar", "account_edit.name_modal.edit_title": "Editar o nome a mostrar", "account_edit.profile_tab.button_label": "Personalizar", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 5c4974e1828f95..db08801ac4449c 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Përpunoni Profil", "account_edit.custom_fields.name": "fushë", "account_edit.custom_fields.placeholder": "Shtoni përemrat tuaj, lidhje të jashme, ose gjithçka tjetë që do të donit të ndanit me të tjerë.", + "account_edit.custom_fields.reorder_button": "Rirenditi fushat", "account_edit.custom_fields.tip_content": "Mundeni të shtoni kollak besueshmëri për llogarinë tuaj Mastodon duke verifikuar lidhje për te çfarëdo sajti që është pronë e juaja.", "account_edit.custom_fields.tip_title": "Ndihmëz: Duke shtuar lidhje të verifikuara", "account_edit.custom_fields.title": "Fusha vetjake", @@ -167,10 +168,20 @@ "account_edit.field_delete_modal.title": "Të fshihet fushë e përshtatur?", "account_edit.field_edit_modal.add_title": "Shtoni fushë të përshtatur", "account_edit.field_edit_modal.edit_title": "Përpunoni fushë të përshtatur", + "account_edit.field_edit_modal.limit_header": "U tejkalua kufi i rekomanduar shenjash", + "account_edit.field_edit_modal.limit_message": "Përdorues me celular mund të mos e shohin të plotë fushën tuaj.", + "account_edit.field_edit_modal.link_emoji_warning": "Rekomandojmë të mos përdoren emoji të përshtatur tok me url-ra. Fusha të përshtatura që i përmbajnë të dyja llojetn do t’i shfaqin si tekst, në vend se si një lidhje, për të parandaluar ngatërrim të përdoruesve.", "account_edit.field_edit_modal.name_hint": "P.sh., “Sajt personal”", "account_edit.field_edit_modal.name_label": "Etiketë", "account_edit.field_edit_modal.value_hint": "P.sh., “shembull.me”", "account_edit.field_edit_modal.value_label": "Vlerë", + "account_edit.field_reorder_modal.drag_cancel": "Tërheqja u anulua. Fusha “{item}” u la jashtë.", + "account_edit.field_reorder_modal.drag_instructions": "Që të risistemoni fusha vetjake, shtypni tastin Space, ose Enter. Teksa tërhiqen, përdorni tastet shigjetë që të lëvizn fushat lart ose poshtë. Shtypni sërish tastin Space ose Enter që të lihet fusha te pozicioni i saj i ri, ose shtypni tastin Esc, që të anulohet.", + "account_edit.field_reorder_modal.drag_move": "Fusha “{item}” u lëviz.", + "account_edit.field_reorder_modal.drag_over": "Fusha “{item}” u kaluar sipër “{over}”.", + "account_edit.field_reorder_modal.drag_start": "U mor fusha “{item}”.", + "account_edit.field_reorder_modal.handle_label": "Tërhiqni fushën “{item}”", + "account_edit.field_reorder_modal.title": "Risistemoni fusha", "account_edit.name_modal.add_title": "Shtoni emër në ekran", "account_edit.name_modal.edit_title": "Përpunoni emër në ekran", "account_edit.profile_tab.button_label": "Përshtateni", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 95df6fade65707..f89245307e0777 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -48,6 +48,13 @@ "account.featured.hashtags": "Fyrkantstaggar", "account.featured_tags.last_status_at": "Senaste inlägg den {date}", "account.featured_tags.last_status_never": "Inga inlägg", + "account.field_overflow": "Visa hela innehållet", + "account.filters.all": "All aktivitet", + "account.filters.boosts_toggle": "Visa förstärkningar", + "account.filters.posts_boosts": "Inlägg och förstärkningar", + "account.filters.posts_only": "Inlägg", + "account.filters.posts_replies": "Inlägg och svar", + "account.filters.replies_toggle": "Visa svar", "account.follow": "Följ", "account.follow_back": "Följ tillbaka", "account.follow_back_short": "Följ tillbaka", @@ -73,6 +80,24 @@ "account.locked_info": "Detta konto har låst integritetsstatus. Ägaren väljer manuellt vem som kan följa det.", "account.media": "Media", "account.mention": "Nämn @{name}", + "account.menu.add_to_list": "Lägg till i lista…", + "account.menu.block": "Blockera konto", + "account.menu.block_domain": "Blockera {domain}", + "account.menu.copied": "Kopierade kontolänk till urklipp", + "account.menu.copy": "Kopiera länk", + "account.menu.direct": "Nämn privat", + "account.menu.hide_reblogs": "Dölj förstärkningar i tidslinjen", + "account.menu.mention": "Nämn", + "account.menu.mute": "Tysta konto", + "account.menu.note.description": "Endast synlig för dig", + "account.menu.open_original_page": "Visa på {domain}", + "account.menu.remove_follower": "Ta bort följare", + "account.menu.report": "Rapportera konto", + "account.menu.share": "Dela…", + "account.menu.show_reblogs": "Visa förstärkningar i tidslinjen", + "account.menu.unblock": "Avblockera konto", + "account.menu.unblock_domain": "Avblockera {domain}", + "account.menu.unmute": "Avtysta konto", "account.moved_to": "{name} har indikerat att hen har ett nytt konto:", "account.mute": "Tysta @{name}", "account.mute_notifications_short": "Stäng av aviseringsljud", @@ -80,7 +105,15 @@ "account.muted": "Tystad", "account.muting": "Stänger av ljud", "account.mutual": "Ni följer varandra", + "account.name.help.domain": "{domain} är servern som är värd för användarens profil och inlägg.", + "account.name.help.domain_self": "{domain} är din server som är värd för användarens profil och inlägg.", + "account.name.help.footer": "Precis som du kan skicka e-post till personer med olika e-postklienter, du kan interagera med personer på andra Mastodon-servrar – och med vem som helst på andra sociala appar som drivs av samma uppsättning regler som Mastodon använder (ActivityPub-protokollet).", "account.no_bio": "Ingen beskrivning angiven.", + "account.node_modal.field_label": "Personlig anteckning", + "account.node_modal.save": "Spara", + "account.node_modal.title": "Lägg till en personlig anteckning", + "account.note.edit_button": "Redigera", + "account.note.title": "Personlig anteckning (endast synlig för dig)", "account.open_original_page": "Öppna den ursprungliga sidan", "account.posts": "Inlägg", "account.posts_with_replies": "Inlägg och svar", @@ -91,6 +124,8 @@ "account.share": "Dela @{name}s profil", "account.show_reblogs": "Visa boostar från @{name}", "account.statuses_counter": "{count, plural, one {{counter} inlägg} other {{counter} inlägg}}", + "account.timeline.pinned": "Fastnålad", + "account.timeline.pinned.view_all": "Visa alla fästa inlägg", "account.unblock": "Avblockera @{name}", "account.unblock_domain": "Avblockera {domain}", "account.unblock_domain_short": "Avblockera", @@ -100,8 +135,37 @@ "account.unmute": "Sluta tysta @{name}", "account.unmute_notifications_short": "Aktivera aviseringsljud", "account.unmute_short": "Avtysta", + "account_edit.bio.placeholder": "Lägg till en kort introduktion för att hjälpa andra att identifiera dig.", + "account_edit.bio.title": "Biografi", + "account_edit.bio_modal.add_title": "Lägg till biografi", + "account_edit.bio_modal.edit_title": "Redigera biografi", + "account_edit.button.add": "Lägg till {item}", + "account_edit.button.delete": "Radera {item}", + "account_edit.button.edit": "Redigera {item}", "account_edit.column_button": "Klar", "account_edit.column_title": "Redigera profil", + "account_edit.custom_fields.name": "fält", + "account_edit.custom_fields.placeholder": "Lägg till dina pronomen, externa länkar eller något annat du vill dela.", + "account_edit.custom_fields.reorder_button": "Ändra ordning för fält", + "account_edit.custom_fields.tip_content": "Du kan enkelt lägga till trovärdighet till ditt Mastodon-konto genom att verifiera länkar till alla webbplatser du äger.", + "account_edit.custom_fields.tip_title": "Tips: Lägga till verifierade länkar", + "account_edit.custom_fields.title": "Tilläggsfält", + "account_edit.custom_fields.verified_hint": "Hur lägger jag till en verifierad länk?", + "account_edit.display_name.placeholder": "Visningsnamnet är hur ditt namn ser ut på din profil och i tidslinjer.", + "account_edit.display_name.title": "Visningsnamn", + "account_edit.featured_hashtags.item": "hashtaggar", + "account_edit.featured_hashtags.placeholder": "Hjälp andra att identifiera, och få snabb tillgång till, dina favoritämnen.", + "account_edit.featured_hashtags.title": "Utvalda hashtaggar", + "account_edit.field_delete_modal.confirm": "Är du säker på att du vill ta bort detta tilläggsfält? Denna åtgärd kan inte ångras.", + "account_edit.field_delete_modal.delete_button": "Radera", + "account_edit.field_delete_modal.title": "Radera tilläggsfält?", + "account_edit.field_edit_modal.add_title": "Lägg till tilläggsfält", + "account_edit.field_edit_modal.edit_title": "Redigera tilläggsfält", + "account_edit.field_edit_modal.limit_header": "Rekommenderad teckengräns överskriden", + "account_edit.field_edit_modal.limit_message": "Mobilanvändare kanske inte ser ditt fält i sin helhet.", + "account_edit.field_edit_modal.name_hint": "T.ex. “Personlig webbplats”", + "account_edit.field_edit_modal.name_label": "Etikett", + "account_edit.field_edit_modal.value_hint": "T.ex. \"example.me”", "account_edit.profile_tab.button_label": "Anpassa", "account_note.placeholder": "Klicka för att lägga till anteckning", "admin.dashboard.daily_retention": "Användarlojalitet per dag efter registrering", diff --git a/app/javascript/mastodon/locales/szl.json b/app/javascript/mastodon/locales/szl.json index 577a3e8a327f48..9f03864932df84 100644 --- a/app/javascript/mastodon/locales/szl.json +++ b/app/javascript/mastodon/locales/szl.json @@ -1,9 +1,9 @@ { "about.blocks": "Moderowane serwery", "about.contact": "Kōntakt:", - "about.disclaimer": "Mastodōn je wolnym a ôtwartozdrzōdłowym ôprogramowaniym ôraz znakiym towarowym ôd Mastodon gGmbH.", - "about.domain_blocks.no_reason_available": "Grund niydostympny", - "about.domain_blocks.preamble": "Mastodōn normalniy pozwŏlŏ na ôglōndaniy treściōw a interakcyje ze używŏczami inkszych serwerōw we fediverse, ale sōm ôd tygo wyjōntki, kere bōły poczyniōne na tym serwerze.", + "about.disclaimer": "Mastodon je wolne a ôtwartozdrzōdłowe ôprogramowanie i towarowy znak ôd Mastodon gGmbH.", + "about.domain_blocks.no_reason_available": "Brak prziczyny", + "about.domain_blocks.preamble": "Mastodon z wiynksza dŏwŏ ôglōndać treści i kōmunikować sie ze używŏczami inkszych serwerōw we fediverse. To sōm wyjōntki, co fungujōm na tym kōnkretnym serwerze.", "about.domain_blocks.silenced.explanation": "Normalniy niy bydziesz widzieć profilōw a treściōw ze tygo serwera. Ôboczysz je ino jak specjalniy bydziesz ich szukać abo jak je zaôbserwujesz.", "about.domain_blocks.silenced.title": "Ôgraniczone", "about.domain_blocks.suspended.explanation": "Żŏdne dane ze tygo serwera niy bydōm przetwarzane, przechowywane abo wymieniane, beztoż wszelakŏ interakcyjŏ abo komunikacyjŏ ze używŏczami tygo serwera bydzie niymożliwŏ.", @@ -15,16 +15,24 @@ "account.block": "Zablokuj @{name}", "account.block_domain": "Zablokuj domena {domain}", "account.cancel_follow_request": "Withdraw follow request", - "account.media": "Mydia", + "account.media": "Media", "account.mute": "Wycisz @{name}", - "account.posts": "Toots", - "account.posts_with_replies": "Toots and replies", - "account_note.placeholder": "Click to add a note", + "account.posts": "Posty", + "account.posts_with_replies": "Posty i ôdpowiedzi", + "account_note.placeholder": "Wybier, żeby przidac notka", + "column.bookmarks": "Zokłodki", + "column.direct": "Prywatne spōmniynia", + "column.favourites": "Spamiyntane", + "column.firehose": "Kanały na żywo", + "column.home": "Przodek", + "column.lists": "Wykazy", + "column.notifications": "Uwiadōmiynia", "column.pins": "Pinned toot", - "community.column_settings.media_only": "Media only", + "community.column_settings.media_only": "Ino media", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.", - "compose_form.placeholder": "What is on your mind?", + "compose_form.placeholder": "Co nowego?", + "compose_form.publish": "Wyślij", "compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.unmarked": "Text is not hidden", "confirmations.delete.message": "Are you sure you want to delete this status?", @@ -33,6 +41,11 @@ "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.", "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}", "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "explore.title": "Popularne", + "explore.trending_links": "Nowiny", + "explore.trending_statuses": "Posty", + "explore.trending_tags": "Etykety", + "followed_tags": "Torowane etykety", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", "keyboard_shortcuts.back": "to navigate back", "keyboard_shortcuts.blocked": "to open blocked users list", @@ -63,7 +76,10 @@ "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", + "navigation_bar.bookmarks": "Zokłodki", "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.more": "Wiyncyj", + "navigation_bar.preferences": "Sztalōnki", "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.", "notification.reblog": "{name} boosted your status", "notifications.column_settings.status": "New toots:", @@ -72,6 +88,7 @@ "report.submit": "Submit report", "report.target": "Report {target}", "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached", + "search.search_or_paste": "Szukej abo wraź URL", "search_results.statuses": "Toots", "sign_in_banner.sign_in": "Sign in", "status.admin_status": "Open this status in the moderation interface", @@ -81,5 +98,6 @@ "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}", + "trends.trending_now": "Prawie popularne", "upload_progress.label": "Uploading…" } diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index ea92132687c214..a6d249925a7e19 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Profili Düzenle", "account_edit.custom_fields.name": "alan", "account_edit.custom_fields.placeholder": "Zamirlerinizi, harici bağlantılarınızı veya paylaşmak istediğiniz diğer bilgileri ekleyin.", + "account_edit.custom_fields.reorder_button": "Alanları yeniden sırala", "account_edit.custom_fields.tip_content": "Sahip olduğunuz web sitelerine bağlantıları doğrulayarak Mastodon hesabınıza kolayca güvenilirlik katabilirsiniz.", "account_edit.custom_fields.tip_title": "İpucu: Doğrulanmış bağlantılar ekleme", "account_edit.custom_fields.title": "Özel alanlar", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Özel alanı sil?", "account_edit.field_edit_modal.add_title": "Özel alan ekle", "account_edit.field_edit_modal.edit_title": "Özel alanı düzenle", + "account_edit.field_edit_modal.limit_header": "Önerilen karakter sınırı aşıldı", + "account_edit.field_edit_modal.limit_message": "Mobil cihaz kullanıcıları sahayı tam olarak görmeyebilir.", + "account_edit.field_edit_modal.link_emoji_warning": "Url'lerle birlikte özel emoji kullanmamanızı öneririz. Her ikisini de içeren özel alanlar, kullanıcıların kafasını karıştırmamak için bağlantı yerine yalnızca metin olarak görüntülenir.", "account_edit.field_edit_modal.name_hint": "Örn. \"Kişisel web sitesi\"", "account_edit.field_edit_modal.name_label": "Etiket", "account_edit.field_edit_modal.value_hint": "Örn. \"example.me\"", "account_edit.field_edit_modal.value_label": "Değer", + "account_edit.field_reorder_modal.drag_cancel": "Sürükleme iptal edildi. \"{item}\" alanı bırakıldı.", + "account_edit.field_reorder_modal.drag_end": "\"{item}\" alanı bırakıldı.", + "account_edit.field_reorder_modal.drag_instructions": "Özel alanları yeniden düzenlemek için boşluk tuşuna veya Enter tuşuna basın. Sürüklerken, ok tuşlarını kullanarak alanı yukarı veya aşağı taşıyın. Alanı yeni konumuna bırakmak için boşluk tuşuna veya Enter tuşuna tekrar basın veya iptal etmek için Escape tuşuna basın.", + "account_edit.field_reorder_modal.drag_move": "\"{item}\" alanı hareket ettirildi.", + "account_edit.field_reorder_modal.drag_over": "\"{item}\" alanı \"{over}\" üzerine hareket ettirildi.", + "account_edit.field_reorder_modal.drag_start": "\"{item}\" alanı seçildi.", + "account_edit.field_reorder_modal.handle_label": "\"{item}\" alanını sürükle", + "account_edit.field_reorder_modal.title": "Alanları yeniden düzenle", "account_edit.name_modal.add_title": "Görünen ad ekle", "account_edit.name_modal.edit_title": "Görünen adı düzenle", "account_edit.profile_tab.button_label": "Özelleştir", @@ -327,9 +339,12 @@ "collections.delete_collection": "Koleksiyonu sil", "collections.description_length_hint": "100 karakterle sınırlı", "collections.detail.accounts_heading": "Hesaplar", + "collections.detail.author_added_you": "{author} sizi koleksiyonuna ekledi", "collections.detail.curated_by_author": "{author} tarafından derlenen", "collections.detail.curated_by_you": "Sizin derledikleriniz", "collections.detail.loading": "Koleksiyon yükleniyor…", + "collections.detail.other_accounts_in_collection": "Bu koleksiyondaki diğer kişiler:", + "collections.detail.sensitive_note": "Bu koleksiyon bazı kullanıcılar için hassas olabilecek hesap ve içerik içerebilir.", "collections.detail.share": "Bu koleksiyonu paylaş", "collections.edit_details": "Ayrıntıları düzenle", "collections.error_loading_collections": "Koleksiyonlarınızı yüklemeye çalışırken bir hata oluştu.", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index c2a73a77e45ab3..583cfbbe4416fe 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Sửa hồ sơ", "account_edit.custom_fields.name": "trường", "account_edit.custom_fields.placeholder": "Thêm nghề nghiệp, liên kết ngoài hoặc bất kỳ gì mà bạn muốn.", + "account_edit.custom_fields.reorder_button": "Sắp xếp trường", "account_edit.custom_fields.tip_content": "Bạn có thể dễ dàng tăng độ tin cậy cho tài khoản Mastodon của mình bằng cách xác minh liên kết đến bất kỳ trang web nào bạn sở hữu.", "account_edit.custom_fields.tip_title": "Mẹo: Thêm liên kết xác minh", "account_edit.custom_fields.title": "Trường tùy chỉnh", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "Xóa trường tùy chỉnh?", "account_edit.field_edit_modal.add_title": "Thêm trường tùy chỉnh", "account_edit.field_edit_modal.edit_title": "Sửa trường tùy chỉnh", + "account_edit.field_edit_modal.limit_header": "Đã vượt giới hạn ký tự đề xuất", + "account_edit.field_edit_modal.limit_message": "Người dùng di động sẽ không thể thấy đầy đủ trường.", + "account_edit.field_edit_modal.link_emoji_warning": "Không nên dùng emoji tùy chỉnh với url. Trường tùy chỉnh chứa cả hai sẽ chỉ hiển thị văn bản, để ngăn chặn việc bối rối.", "account_edit.field_edit_modal.name_hint": "Vd: “Website cá nhân”", "account_edit.field_edit_modal.name_label": "Nhãn", "account_edit.field_edit_modal.value_hint": "Vd: “example.me”", "account_edit.field_edit_modal.value_label": "Giá trị", + "account_edit.field_reorder_modal.drag_cancel": "Đã hủy kéo. Trường \"{item}\" đã được thả.", + "account_edit.field_reorder_modal.drag_end": "Trường \"{item}\" đã được thả.", + "account_edit.field_reorder_modal.drag_instructions": "Để sắp xếp lại các trường tùy chỉnh, nhấn phím cách hoặc phím Enter. Trong khi kéo, sử dụng các phím mũi tên để di chuyển trường lên hoặc xuống. Nhấn phím cách hoặc phím Enter một lần nữa để thả trường vào vị trí mới, hoặc nhấn phím Escape để hủy bỏ.", + "account_edit.field_reorder_modal.drag_move": "Trường \"{item}\" đã được di chuyển.", + "account_edit.field_reorder_modal.drag_over": "Trường \"{item}\" đã được di chuyển lên trên \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Đã chọn trường \"{item}\".", + "account_edit.field_reorder_modal.handle_label": "Kéo trường \"{item}\"", + "account_edit.field_reorder_modal.title": "Sắp xếp lại trường", "account_edit.name_modal.add_title": "Thêm tên gọi", "account_edit.name_modal.edit_title": "Sửa tên gọi", "account_edit.profile_tab.button_label": "Tùy chỉnh", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index d8d2dd76b4ecd6..190b4fab201456 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "编辑 {item}", "account_edit.column_button": "完成", "account_edit.column_title": "修改个人资料", + "account_edit.custom_fields.name": "字段", "account_edit.custom_fields.placeholder": "添加你的人称代词、外部链接,或其他你想分享的内容。", + "account_edit.custom_fields.reorder_button": "重新排序字段", + "account_edit.custom_fields.tip_content": "通过验证任意你拥有网站的链接,你可以轻松增加 Mastodon 账号的可信度。", + "account_edit.custom_fields.tip_title": "小贴士:添加已验证的链接", "account_edit.custom_fields.title": "自定义字段", + "account_edit.custom_fields.verified_hint": "我如何添加已验证的链接?", "account_edit.display_name.placeholder": "你的显示名称是指你的名字在个人资料及时间线上出现时的样子。", "account_edit.display_name.title": "显示名称", "account_edit.featured_hashtags.item": "话题标签", "account_edit.featured_hashtags.placeholder": "帮助其他人认识并快速访问你最喜欢的话题。", "account_edit.featured_hashtags.title": "精选话题标签", + "account_edit.field_delete_modal.confirm": "你确定要删除此自定义字段吗?此操作无法撤消。", + "account_edit.field_delete_modal.delete_button": "删除", + "account_edit.field_delete_modal.title": "删除自定义字段?", + "account_edit.field_edit_modal.add_title": "添加自定义字段", + "account_edit.field_edit_modal.edit_title": "编辑自定义字段", + "account_edit.field_edit_modal.limit_header": "已超过建议字数限制", + "account_edit.field_edit_modal.limit_message": "移动端用户可能无法完整看见字段内容。", + "account_edit.field_edit_modal.link_emoji_warning": "我们建议不要同时使用自定义表情和网址。同时包含两者的自定义字段将会显示为纯文本而不是链接形式,以避免用户混淆。", + "account_edit.field_edit_modal.name_hint": "例如:“个人网站”", + "account_edit.field_edit_modal.name_label": "标签", + "account_edit.field_edit_modal.value_hint": "例如:“example.me”", + "account_edit.field_edit_modal.value_label": "值", + "account_edit.field_reorder_modal.drag_cancel": "拖拽已终止。字段“{item}”已被丢弃。", + "account_edit.field_reorder_modal.drag_end": "字段“{item}”已被丢弃。", + "account_edit.field_reorder_modal.drag_instructions": "要重新排列自定义字段,请按空格键或回车键。在拖拽时,使用方向键将字段向上或向下移动。再次按空格键或回车键可将字段放置在新位置,或按 Esc 键取消。", + "account_edit.field_reorder_modal.drag_move": "字段“{item}”已被移动。", + "account_edit.field_reorder_modal.drag_over": "字段“{item}”已被移动到“{over}”上方。", + "account_edit.field_reorder_modal.drag_start": "已选中字段“{item}”。", + "account_edit.field_reorder_modal.handle_label": "拖拽字段“{item}”", + "account_edit.field_reorder_modal.title": "重新排列字段", "account_edit.name_modal.add_title": "添加显示名称", "account_edit.name_modal.edit_title": "编辑显示名称", "account_edit.profile_tab.button_label": "自定义", @@ -172,6 +197,13 @@ "account_edit.profile_tab.subtitle": "自定义你个人资料的标签页及其显示的内容。", "account_edit.profile_tab.title": "个人资料标签页设置", "account_edit.save": "保存", + "account_edit.verified_modal.details": "要增加 Mastodon 个人资料的可信度,可以验证指向个人网站的链接。运作方式如下:", + "account_edit.verified_modal.invisible_link.details": "将链接添加到标头(header)中。其中很重要的部分是 rel=\"me\",可以防止他人通过用户生成内容模仿。你甚至可以在页面标头中使用 link 标签而不是 {tag},但包含该部分的 HTML 必须在没有 JavaScript 执行环境下访问时依然存在。", + "account_edit.verified_modal.invisible_link.summary": "如何隐藏此链接?", + "account_edit.verified_modal.step1.header": "复制下面的 HTML 代码并粘贴到你网站的标头(header)中", + "account_edit.verified_modal.step2.details": "如果已经将你的网站添加到自定义字段中,你需要删除并重新添加以触发验证。", + "account_edit.verified_modal.step2.header": "将你的网站添加到自定义字段", + "account_edit.verified_modal.title": "如何添加已验证的链接", "account_edit_tags.add_tag": "添加 #{tagName}", "account_edit_tags.column_title": "编辑精选话题标签", "account_edit_tags.help_text": "精选话题标签可以帮助他人发现并与你的个人资料互动。这些标签会作为过滤器条件出现在你个人资料页面的活动视图中。", @@ -307,9 +339,12 @@ "collections.delete_collection": "删除收藏列表", "collections.description_length_hint": "100字限制", "collections.detail.accounts_heading": "账号", + "collections.detail.author_added_you": "{author} 将你添加到了此收藏列表", "collections.detail.curated_by_author": "由 {author} 精心挑选", "collections.detail.curated_by_you": "由你精心挑选", "collections.detail.loading": "正在加载收藏列表…", + "collections.detail.other_accounts_in_collection": "此收藏列表中的其他人:", + "collections.detail.sensitive_note": "此收藏列表可能包含某些对部分用户而言为敏感内容的账号或内容。", "collections.detail.share": "分享此收藏列表", "collections.edit_details": "编辑详情", "collections.error_loading_collections": "加载你的收藏列表时发生错误。", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index f380953cae3235..a8dfa74dc91a09 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -153,6 +153,7 @@ "account_edit.column_title": "編輯個人檔案", "account_edit.custom_fields.name": "欄位", "account_edit.custom_fields.placeholder": "加入您的稱謂、外部連結、或其他您想分享的。", + "account_edit.custom_fields.reorder_button": "重新排序欄位", "account_edit.custom_fields.tip_content": "您能透過驗證任何您擁有網站之連結,以輕鬆增加您 Mastodon 帳號之可信度。", "account_edit.custom_fields.tip_title": "小撇步:新增驗證連結", "account_edit.custom_fields.title": "自訂欄位", @@ -167,10 +168,21 @@ "account_edit.field_delete_modal.title": "是否刪除自訂欄位?", "account_edit.field_edit_modal.add_title": "新增自訂欄位", "account_edit.field_edit_modal.edit_title": "編輯自訂欄位", + "account_edit.field_edit_modal.limit_header": "已超過建議字數限制", + "account_edit.field_edit_modal.limit_message": "行動裝置使用者可能無法看見您完整欄位。", + "account_edit.field_edit_modal.link_emoji_warning": "我們不建議於 URL 中使用自訂 emoji 表情符號。為了避免使用者混淆,包含兩者之自訂欄位將僅顯示為文字,而不是顯示為連結。", "account_edit.field_edit_modal.name_hint": "例如:「個人網站」", "account_edit.field_edit_modal.name_label": "標籤", "account_edit.field_edit_modal.value_hint": "例如「example.me」", "account_edit.field_edit_modal.value_label": "值", + "account_edit.field_reorder_modal.drag_cancel": "拖放操作已取消。欄位「{item}」已放置。", + "account_edit.field_reorder_modal.drag_end": "欄位「{item}」已放置。", + "account_edit.field_reorder_modal.drag_instructions": "請按空白鍵或 Enter 重新整理自訂欄位。使用方向鍵上下移動欄位。按下空白鍵或 Enter 鍵於新位置放置欄位,或按下 ESC 鍵取消。", + "account_edit.field_reorder_modal.drag_move": "欄位「{item}」已移動。", + "account_edit.field_reorder_modal.drag_over": "欄位「{item}」已移動至「{over}」上方。", + "account_edit.field_reorder_modal.drag_start": "已選取欄位「{item}」。", + "account_edit.field_reorder_modal.handle_label": "拖放欄位「{item}」", + "account_edit.field_reorder_modal.title": "重新整理欄位", "account_edit.name_modal.add_title": "新增顯示名稱", "account_edit.name_modal.edit_title": "編輯顯示名稱", "account_edit.profile_tab.button_label": "自訂", diff --git a/config/locales/eo.yml b/config/locales/eo.yml index d8700963c2cd07..de46b0a7be197c 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -1404,7 +1404,7 @@ eo: add_new: Aldoni novan errors: limit: Vi jam elstarigis la maksimuman kvanton da kradvortoj - hint_html: "Kio estas la trajtaj kradvortoj? Ili bone videblas en via publika profilo kaj permesas al homoj trarigardi viajn publikajn mesaĝojn specife laŭ tiuj kradvortoj. Ili estas bonaj iloj por sekvi la evoluon de kreadaj laboroj aŭ longdaŭraj projektoj." + hint_html: "Elstarigu viajn plej gravajn kradvortojn en via profilo. Bona ilo por sekvi viaj kreaĵoj aŭ longdaŭraj projektoj, elstarigitaj kradvortoj bone videblas en via publika profilo kaj ebligas rapidan aliron al viaj propraj afiŝoj." filters: contexts: account: Profiloj diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 94c4793bdc01c8..66b29268b53a9f 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -1219,7 +1219,7 @@ fi: advanced_settings: Edistyneet asetukset animations_and_accessibility: Animaatiot ja saavutettavuus boosting_preferences: Tehostusasetukset - boosting_preferences_info_html: "Vihje: Asetuksista riippumatta Vaihto + napsautus %{icon} Tehosta-kuvakkeeseen tehostaa välittömästi." + boosting_preferences_info_html: "Vinkki: Asetuksista riippumatta Vaihto + napsautus %{icon} Tehosta-kuvakkeeseen tehostaa välittömästi." discovery: Löydettävyys localization: body: Mastodonin ovat kääntäneet vapaaehtoiset. @@ -1333,7 +1333,7 @@ fi: title: Tekijän nimeäminen challenge: confirm: Jatka - hint_html: "Vihje: Emme pyydä sinulta salasanaa uudelleen seuraavan tunnin aikana." + hint_html: "Vinkki: Emme pyydä sinulta salasanaa uudelleen seuraavan tunnin aikana." invalid_password: Väärä salasana prompt: Jatka vahvistalla salasanasi color_scheme: diff --git a/config/locales/nan-TW.yml b/config/locales/nan-TW.yml index 3b89a0270745c8..06b133d83225b5 100644 --- a/config/locales/nan-TW.yml +++ b/config/locales/nan-TW.yml @@ -1832,6 +1832,7 @@ nan-TW: nokia: Nokia S40 Ovi 瀏覽器 opera: Opera otter: Otter + phantom_js: PhantomJS qq: QQ 瀏覽器 safari: Safari uc_browser: UC 瀏覽器 @@ -1884,6 +1885,14 @@ nan-TW: strikes: 管理ê警告 two_factor_authentication: 雙因素驗證 webauthn_authentication: 安全鎖匙 + severed_relationships: + download: 載落去(%{count} 份) + event_type: + account_suspension: 中止權限ê口座(%{target_name}) + domain_block: 中止權限ê服侍器(%{target_name}) + user_domain_block: Lí kā %{target_name} 封鎖ah + lost_followers: 失去ê 跟tuè lí ê + lost_follows: 失去ê lí 跟tuè ê statuses: default_language: Kap界面ê語言sio kâng visibilities: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index a52cee4a701d94..e8789d4236f20b 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -267,6 +267,7 @@ pt-BR: demote_user_html: "%{name} rebaixou o usuário %{target}" destroy_announcement_html: "%{name} excluiu o anúncio %{target}" destroy_canonical_email_block_html: "%{name} desbloqueou o endereço e-mail com o hash %{target}" + destroy_collection_html: "%{name} removeu coleção de %{target}" destroy_custom_emoji_html: "%{name} apagou o emoji %{target}" destroy_domain_allow_html: "%{name} bloqueou federação com domínio %{target}" destroy_domain_block_html: "%{name} desbloqueou o domínio %{target}" @@ -306,6 +307,7 @@ pt-BR: unsilence_account_html: "%{name} removeu a limitação da conta de %{target}" unsuspend_account_html: "%{name} removeu a suspenção da conta de %{target}" update_announcement_html: "%{name} atualizou o anúncio %{target}" + update_collection_html: "%{name} atualizou a coleção de %{target}" update_custom_emoji_html: "%{name} atualizou o emoji %{target}" update_domain_block_html: "%{name} atualizou o bloqueio de domínio de %{target}" update_ip_block_html: "%{name} alterou a regra para o IP %{target}" diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index b85b5fbd1829e8..c388a0a48e2e92 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -347,6 +347,9 @@ pt-PT: accounts: Contas collection_title: Colecionado por %{name} contents: Conteúdo + number_of_accounts: + one: 1 conta + other: "%{count} contas" open: Abrir view_publicly: Ver publicamente critical_update_pending: Atualização crítica pendente diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 1c85042bc016c6..5aec2c90ade381 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -90,7 +90,7 @@ de: backups_retention_period: Nutzer*innen haben die Möglichkeit, Archive ihrer Beiträge zu erstellen, die sie später herunterladen können. Wenn ein positiver Wert gesetzt ist, werden diese Archive nach der festgelegten Anzahl von Tagen automatisch aus deinem Speicher gelöscht. bootstrap_timeline_accounts: Diese Konten werden neu registrierten Profilen empfohlen und in der Empfehlungsliste oben angeheftet. Gib eine durch Kommata getrennte Liste von Konten an. closed_registrations_message: Wird angezeigt, wenn Registrierungen deaktiviert sind - content_cache_retention_period: Sämtliche Beiträge von anderen Servern (einschließlich geteilte Beiträge und Antworten) werden, unabhängig von der Interaktion der lokalen Nutzer*innen mit diesen Beiträgen, nach der festgelegten Anzahl von Tagen gelöscht. Das betrifft auch Beiträge, die von lokalen Nutzer*innen favorisiert oder als Lesezeichen gespeichert wurden. Private Erwähnungen zwischen Nutzer*innen von verschiedenen Servern werden ebenfalls verloren gehen und können nicht wiederhergestellt werden. Diese Option richtet sich ausschließlich an Server mit speziellen Zwecken und wird die allgemeine Nutzungserfahrung beeinträchtigen, wenn sie für den allgemeinen Gebrauch aktiviert ist. + content_cache_retention_period: Sämtliche Beiträge von anderen Servern (einschließlich geteilte Beiträge und Antworten) werden, unabhängig von der Interaktion der lokalen Nutzer*innen mit diesen Beiträgen, nach der festgelegten Anzahl von Tagen gelöscht. Davon sind auch Beiträge betroffen, die von lokalen Nutzer*innen favorisiert oder als Lesezeichen gespeichert wurden. Private Erwähnungen zwischen Nutzer*innen von verschiedenen Servern werden ebenfalls verloren gehen und können nicht wiederhergestellt werden. Diese Option richtet sich ausschließlich an Server mit speziellen Zwecken und wird die allgemeine Nutzungserfahrung beeinträchtigen, wenn sie für den allgemeinen Gebrauch aktiviert ist. custom_css: Du kannst eigene Stylesheets für das Webinterface von Mastodon verwenden. favicon: WebP, PNG, GIF oder JPG. Überschreibt das Standard-Mastodon-Favicon mit einem eigenen Favicon. landing_page: Legt fest, welchen Bereich (nicht angemeldete) Besucher*innen sehen, wenn sie deinen Server besuchen. Für „Trends“ müssen die Trends in den Entdecken-Einstellungen aktiviert sein. Für „Lokaler Feed“ muss „Zugriff auf Live-Feeds, die lokale Beiträge beinhalten“ in den Entdecken-Einstellungen auf „Alle“ gesetzt werden. diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index aad88989241524..da17198daa388f 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -4,7 +4,7 @@ eo: hints: account: attribution_domains: Unu por linio. Protektas kontraŭ falsaj atribuoj. - discoverable: Viaj publikaj afiŝoj kaj profilo povas esti prezentitaj aŭ rekomenditaj en diversaj lokoj de Mastodon kaj via profilo povas esti proponita al aliaj uzantoj. + discoverable: Viaj publikaj afiŝoj kaj profilo povas esti elstarigitaj aŭ rekomenditaj en diversaj lokoj de Mastodon kaj via profilo povas esti proponita al aliaj uzantoj. display_name: Via plena nomo aŭ via kromnomo. fields: Via retpaĝo, pronomoj, aĝo, ĉio, kion vi volas. indexable: Viaj publikaj afiŝoj povas aperi en serĉrezultoj ĉe Mastodon. Homoj, kiuj interagis kun viaj afiŝoj, eble povos serĉi ilin sendepende. From 3a796544e3c20687c14632e87c5d099154a0b8e5 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Mon, 9 Mar 2026 10:54:17 +0100 Subject: [PATCH 008/203] Allow removing yourself from a collection (#38096) --- app/javascript/mastodon/api/collections.ts | 6 ++ .../mastodon/api_types/collections.ts | 2 +- ...{collection_list.tsx => accounts_list.tsx} | 62 +++++++++++++- .../collections/detail/collection_menu.tsx | 83 +++++++++++++------ .../features/collections/detail/index.tsx | 2 +- .../revoke_collection_inclusion_modal.tsx | 82 ++++++++++++++++++ .../collections/detail/styles.module.scss | 21 +++++ .../confirmation_modal.tsx | 4 +- .../features/ui/components/modal_root.jsx | 1 + app/javascript/mastodon/locales/en.json | 8 ++ .../mastodon/reducers/slices/collections.ts | 25 +++++- 11 files changed, 261 insertions(+), 35 deletions(-) rename app/javascript/mastodon/features/collections/detail/{collection_list.tsx => accounts_list.tsx} (77%) create mode 100644 app/javascript/mastodon/features/collections/detail/revoke_collection_inclusion_modal.tsx diff --git a/app/javascript/mastodon/api/collections.ts b/app/javascript/mastodon/api/collections.ts index 8e35be41c1eac8..862f98b60a688d 100644 --- a/app/javascript/mastodon/api/collections.ts +++ b/app/javascript/mastodon/api/collections.ts @@ -49,3 +49,9 @@ export const apiRemoveCollectionItem = (collectionId: string, itemId: string) => apiRequestDelete( `v1_alpha/collections/${collectionId}/items/${itemId}`, ); + +export const apiRevokeCollectionInclusion = ( + collectionId: string, + itemId: string, +) => + apiRequestPost(`v1_alpha/collections/${collectionId}/items/${itemId}/revoke`); diff --git a/app/javascript/mastodon/api_types/collections.ts b/app/javascript/mastodon/api_types/collections.ts index 23f835f5fc91c6..fae95875d1e3b3 100644 --- a/app/javascript/mastodon/api_types/collections.ts +++ b/app/javascript/mastodon/api_types/collections.ts @@ -52,7 +52,7 @@ export interface ApiCollectionWithAccountsJSON extends ApiWrappedCollectionJSON /** * Nested account item */ -interface CollectionAccountItem { +export interface CollectionAccountItem { id: string; account_id?: string; // Only present when state is 'accepted' (or the collection is your own) state: 'pending' | 'accepted' | 'rejected' | 'revoked'; diff --git a/app/javascript/mastodon/features/collections/detail/collection_list.tsx b/app/javascript/mastodon/features/collections/detail/accounts_list.tsx similarity index 77% rename from app/javascript/mastodon/features/collections/detail/collection_list.tsx rename to app/javascript/mastodon/features/collections/detail/accounts_list.tsx index f66fd855cfe6b9..e458dd27f06fcb 100644 --- a/app/javascript/mastodon/features/collections/detail/collection_list.tsx +++ b/app/javascript/mastodon/features/collections/detail/accounts_list.tsx @@ -2,17 +2,23 @@ import { Fragment, useCallback, useRef, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { Button } from '@/mastodon/components/button'; -import { useRelationship } from '@/mastodon/hooks/useRelationship'; -import type { ApiCollectionJSON } from 'mastodon/api_types/collections'; +import { openModal } from 'mastodon/actions/modal'; +import type { + ApiCollectionJSON, + CollectionAccountItem, +} from 'mastodon/api_types/collections'; import { Account } from 'mastodon/components/account'; +import { Button } from 'mastodon/components/button'; import { DisplayName } from 'mastodon/components/display_name'; import { Article, ItemList, } from 'mastodon/components/scrollable_list/components'; import { useAccount } from 'mastodon/hooks/useAccount'; +import { useDismissible } from 'mastodon/hooks/useDismissible'; +import { useRelationship } from 'mastodon/hooks/useRelationship'; import { me } from 'mastodon/initial_state'; +import { useAppDispatch } from 'mastodon/store'; import classes from './styles.module.scss'; @@ -62,6 +68,52 @@ const AccountItem: React.FC<{ ); }; +const RevokeControls: React.FC<{ + collectionId: string; + collectionItem: CollectionAccountItem; +}> = ({ collectionId, collectionItem }) => { + const dispatch = useAppDispatch(); + + const confirmRevoke = useCallback(() => { + void dispatch( + openModal({ + modalType: 'REVOKE_COLLECTION_INCLUSION', + modalProps: { + collectionId, + collectionItemId: collectionItem.id, + }, + }), + ); + }, [collectionId, collectionItem.id, dispatch]); + + const { wasDismissed, dismiss } = useDismissible( + `collection-revoke-hint-${collectionItem.id}`, + ); + + if (wasDismissed) { + return null; + } + + return ( +
+ + +
+ ); +}; + const SensitiveScreen: React.FC<{ sensitive: boolean | undefined; focusTargetRef: React.RefObject; @@ -166,6 +218,10 @@ export const CollectionAccountsList: React.FC<{ accountId={currentUserInCollection.account_id} collectionOwnerId={collection.account_id} /> +

item.account_id === me, + ); + + const openRevokeConfirmation = useCallback(() => { + void dispatch( + openModal({ + modalType: 'REVOKE_COLLECTION_INCLUSION', + modalProps: { + collectionId: collection.id, + collectionItemId: currentAccountInCollection?.id, + }, + }), + ); + }, [collection.id, currentAccountInCollection?.id, dispatch]); + const menu = useMemo(() => { if (isOwnCollection) { const commonItems: MenuItem[] = [ @@ -99,34 +119,43 @@ export const CollectionMenu: React.FC<{ } else { return commonItems; } - } else if (ownerAccount) { - const items: MenuItem[] = [ - { - text: intl.formatMessage(messages.report), - action: openReportModal, - }, - ]; - const featuredCollectionsPath = `/@${ownerAccount.acct}/featured`; - // Don't show menu link to featured collections while on that very page - if ( - !matchPath(location.pathname, { - path: featuredCollectionsPath, - exact: true, - }) - ) { - items.unshift( - ...[ - { - text: intl.formatMessage(messages.viewOtherCollections), - to: featuredCollectionsPath, - }, - null, - ], - ); + } else { + const items: MenuItem[] = []; + + if (ownerAccount) { + const featuredCollectionsPath = `/@${ownerAccount.acct}/featured`; + // Don't show menu link to featured collections while on that very page + if ( + !matchPath(location.pathname, { + path: featuredCollectionsPath, + exact: true, + }) + ) { + items.push( + ...[ + { + text: intl.formatMessage(messages.viewOtherCollections), + to: featuredCollectionsPath, + }, + null, + ], + ); + } } + + if (currentAccountInCollection) { + items.push({ + text: intl.formatMessage(messages.revoke), + action: openRevokeConfirmation, + }); + } + + items.push({ + text: intl.formatMessage(messages.report), + action: openReportModal, + }); + return items; - } else { - return []; } }, [ isOwnCollection, @@ -134,6 +163,8 @@ export const CollectionMenu: React.FC<{ id, openDeleteConfirmation, context, + currentAccountInCollection, + openRevokeConfirmation, ownerAccount, openReportModal, ]); diff --git a/app/javascript/mastodon/features/collections/detail/index.tsx b/app/javascript/mastodon/features/collections/detail/index.tsx index 4f21e7d2672826..9870e44bc68868 100644 --- a/app/javascript/mastodon/features/collections/detail/index.tsx +++ b/app/javascript/mastodon/features/collections/detail/index.tsx @@ -24,7 +24,7 @@ import { me } from 'mastodon/initial_state'; import { fetchCollection } from 'mastodon/reducers/slices/collections'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; -import { CollectionAccountsList } from './collection_list'; +import { CollectionAccountsList } from './accounts_list'; import { CollectionMetaData } from './collection_list_item'; import { CollectionMenu } from './collection_menu'; import classes from './styles.module.scss'; diff --git a/app/javascript/mastodon/features/collections/detail/revoke_collection_inclusion_modal.tsx b/app/javascript/mastodon/features/collections/detail/revoke_collection_inclusion_modal.tsx new file mode 100644 index 00000000000000..c2c2bafe9dd2a5 --- /dev/null +++ b/app/javascript/mastodon/features/collections/detail/revoke_collection_inclusion_modal.tsx @@ -0,0 +1,82 @@ +import { useCallback } from 'react'; + +import { defineMessages, useIntl } from 'react-intl'; + +import { showAlert } from 'mastodon/actions/alerts'; +import type { BaseConfirmationModalProps } from 'mastodon/features/ui/components/confirmation_modals/confirmation_modal'; +import { ConfirmationModal } from 'mastodon/features/ui/components/confirmation_modals/confirmation_modal'; +import { revokeCollectionInclusion } from 'mastodon/reducers/slices/collections'; +import { useAppDispatch, useAppSelector } from 'mastodon/store'; + +const messages = defineMessages({ + revokeCollectionInclusionTitle: { + id: 'confirmations.revoke_collection_inclusion.title', + defaultMessage: 'Remove yourself from this collection?', + }, + revokeCollectionInclusionMessage: { + id: 'confirmations.revoke_collection_inclusion.message', + defaultMessage: + "This action is permanent, and the curator won't be able to re-add you to the collection later on.", + }, + revokeCollectionInclusionConfirm: { + id: 'confirmations.revoke_collection_inclusion.confirm', + defaultMessage: 'Remove me', + }, +}); + +export const RevokeCollectionInclusionModal: React.FC< + { + collectionId: string; + collectionItemId: string; + } & BaseConfirmationModalProps +> = ({ collectionId, collectionItemId, onClose }) => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const collectionName = useAppSelector( + (state) => state.collections.collections[collectionId]?.name, + ); + + const onConfirm = useCallback(async () => { + try { + await dispatch( + revokeCollectionInclusion({ + collectionId, + itemId: collectionItemId, + }), + ).unwrap(); + + dispatch( + showAlert({ + message: intl.formatMessage( + { + id: 'collections.revoke_inclusion.confirmation', + defaultMessage: 'You\'ve been removed from "{collection}"', + }, + { + collection: collectionName, + }, + ), + }), + ); + } catch { + dispatch( + showAlert({ + message: intl.formatMessage({ + id: 'collections.revoke_inclusion.error', + defaultMessage: 'There was an error, please try again later.', + }), + }), + ); + } + }, [dispatch, collectionId, collectionName, collectionItemId, intl]); + + return ( + + ); +}; diff --git a/app/javascript/mastodon/features/collections/detail/styles.module.scss b/app/javascript/mastodon/features/collections/detail/styles.module.scss index ad084eaed6fff1..786c0e7000b98e 100644 --- a/app/javascript/mastodon/features/collections/detail/styles.module.scss +++ b/app/javascript/mastodon/features/collections/detail/styles.module.scss @@ -103,3 +103,24 @@ line-height: 1.5; cursor: default; } + +.revokeControlWrapper { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; + margin-top: -10px; + padding-bottom: 16px; + padding-inline: calc(26px + var(--avatar-width)) 16px; + + :global(.button) { + min-width: 30%; + white-space: normal; + } + + --avatar-width: 46px; + + @container (width < 360px) { + --avatar-width: 35px; + } +} diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx b/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx index 19898fb57d0fbc..b0397f4d7be8f1 100644 --- a/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx +++ b/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx @@ -21,7 +21,7 @@ interface ConfirmationModalProps { cancel?: React.ReactNode; secondary?: React.ReactNode; onSecondary?: () => void; - onConfirm: () => void; + onConfirm: () => void | Promise; noCloseOnConfirm?: boolean; extraContent?: React.ReactNode; children?: React.ReactNode; @@ -56,7 +56,7 @@ export const ConfirmationModal: React.FC< onClose(); } - onConfirm(); + void onConfirm(); }, [onClose, onConfirm, noCloseOnConfirm]); const handleSecondary = useCallback(() => { diff --git a/app/javascript/mastodon/features/ui/components/modal_root.jsx b/app/javascript/mastodon/features/ui/components/modal_root.jsx index 3493deb0eb657f..7bd1aa1872827c 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.jsx +++ b/app/javascript/mastodon/features/ui/components/modal_root.jsx @@ -80,6 +80,7 @@ export const MODAL_COMPONENTS = { 'REPORT': ReportModal, 'REPORT_COLLECTION': ReportCollectionModal, 'SHARE_COLLECTION': () => import('@/mastodon/features/collections/detail/share_modal').then(module => ({ default: module.CollectionShareModal })), + 'REVOKE_COLLECTION_INCLUSION': () => import('@/mastodon/features/collections/detail/revoke_collection_inclusion_modal').then(module => ({ default: module.RevokeCollectionInclusionModal })), 'ACTIONS': () => Promise.resolve({ default: ActionsModal }), 'EMBED': EmbedModal, 'FOCAL_POINT': () => Promise.resolve({ default: AltTextModal }), diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 649849d528526e..57b868c848b8e9 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -338,12 +338,14 @@ "collections.create_collection": "Create collection", "collections.delete_collection": "Delete collection", "collections.description_length_hint": "100 characters limit", + "collections.detail.accept_inclusion": "Okay", "collections.detail.accounts_heading": "Accounts", "collections.detail.author_added_you": "{author} added you to this collection", "collections.detail.curated_by_author": "Curated by {author}", "collections.detail.curated_by_you": "Curated by you", "collections.detail.loading": "Loading collection…", "collections.detail.other_accounts_in_collection": "Others in this collection:", + "collections.detail.revoke_inclusion": "Remove me", "collections.detail.sensitive_note": "This collection contains accounts and content that may be sensitive to some users.", "collections.detail.share": "Share this collection", "collections.edit_details": "Edit details", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Last posted over a week ago", "collections.remove_account": "Remove this account", "collections.report_collection": "Report this collection", + "collections.revoke_collection_inclusion": "Remove myself from this collection", + "collections.revoke_inclusion.confirmation": "You've been removed from \"{collection}\"", + "collections.revoke_inclusion.error": "There was an error, please try again later.", "collections.search_accounts_label": "Search for accounts to add…", "collections.search_accounts_max_reached": "You have added the maximum number of accounts", "collections.sensitive": "Sensitive", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Remove follower", "confirmations.remove_from_followers.message": "{name} will stop following you. Are you sure you want to proceed?", "confirmations.remove_from_followers.title": "Remove follower?", + "confirmations.revoke_collection_inclusion.confirm": "Remove me", + "confirmations.revoke_collection_inclusion.message": "This action is permanent, and the curator won't be able to re-add you to the collection later on.", + "confirmations.revoke_collection_inclusion.title": "Remove yourself from this collection?", "confirmations.revoke_quote.confirm": "Remove post", "confirmations.revoke_quote.message": "This action cannot be undone.", "confirmations.revoke_quote.title": "Remove post?", diff --git a/app/javascript/mastodon/reducers/slices/collections.ts b/app/javascript/mastodon/reducers/slices/collections.ts index c3bec4b1c6cd1e..127794b47820c6 100644 --- a/app/javascript/mastodon/reducers/slices/collections.ts +++ b/app/javascript/mastodon/reducers/slices/collections.ts @@ -9,6 +9,7 @@ import { apiDeleteCollection, apiAddCollectionItem, apiRemoveCollectionItem, + apiRevokeCollectionInclusion, } from '@/mastodon/api/collections'; import type { ApiCollectionJSON, @@ -17,6 +18,7 @@ import type { } from '@/mastodon/api_types/collections'; import { me } from '@/mastodon/initial_state'; import { + createAppAsyncThunk, createAppSelector, createDataLoadingThunk, } from '@/mastodon/store/typed_functions'; @@ -158,7 +160,10 @@ const collectionSlice = createSlice({ * Removing an account from a collection */ - builder.addCase(removeCollectionItem.fulfilled, (state, action) => { + const removeAccountFromCollection = ( + state: CollectionState, + action: { meta: { arg: { itemId: string; collectionId: string } } }, + ) => { const { itemId, collectionId } = action.meta.arg; const collection = state.collections[collectionId]; @@ -167,7 +172,17 @@ const collectionSlice = createSlice({ (item) => item.id !== itemId, ); } - }); + }; + + builder.addCase( + removeCollectionItem.fulfilled, + removeAccountFromCollection, + ); + + builder.addCase( + revokeCollectionInclusion.fulfilled, + removeAccountFromCollection, + ); }, }); @@ -218,6 +233,12 @@ export const removeCollectionItem = createDataLoadingThunk( apiRemoveCollectionItem(collectionId, itemId), ); +export const revokeCollectionInclusion = createAppAsyncThunk( + `${collectionSlice.name}/revokeCollectionInclusion`, + ({ collectionId, itemId }: { collectionId: string; itemId: string }) => + apiRevokeCollectionInclusion(collectionId, itemId), +); + export const collections = collectionSlice.reducer; /** From fcc3fac8a877fa7c5ca289487fc6512c49e82bd3 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 9 Mar 2026 06:01:12 -0400 Subject: [PATCH 009/203] Fix `Style/ReduceToHash` cop (#38088) --- app/lib/emoji_formatter.rb | 2 +- app/lib/importer/base_importer.rb | 2 +- app/models/concerns/account/mappings.rb | 24 +++++++++---------- app/models/status.rb | 8 +++---- .../account_relationships_presenter.rb | 2 +- app/presenters/tag_relationships_presenter.rb | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/lib/emoji_formatter.rb b/app/lib/emoji_formatter.rb index c193df9bb65075..05f776c4fa219e 100644 --- a/app/lib/emoji_formatter.rb +++ b/app/lib/emoji_formatter.rb @@ -71,7 +71,7 @@ def to_s private def emoji_map - @emoji_map ||= custom_emojis.each_with_object({}) { |e, h| h[e.shortcode] = [full_asset_url(e.image.url), full_asset_url(e.image.url(:static))] } + @emoji_map ||= custom_emojis.to_h { |e| [e.shortcode, [full_asset_url(e.image.url), full_asset_url(e.image.url(:static))]] } end def tag_for_emoji(shortcode, emoji) diff --git a/app/lib/importer/base_importer.rb b/app/lib/importer/base_importer.rb index 7688426b48cbf6..679e3a5198d81f 100644 --- a/app/lib/importer/base_importer.rb +++ b/app/lib/importer/base_importer.rb @@ -51,7 +51,7 @@ def clean_up! raise ActiveRecord::UnknownPrimaryKey, index.adapter.target if primary_key.nil? ids = documents.pluck('_id') - existence_map = index.adapter.target.where(primary_key => ids).pluck(primary_key).each_with_object({}) { |id, map| map[id.to_s] = true } + existence_map = index.adapter.target.where(primary_key => ids).pluck(primary_key).to_h { |id| [id.to_s, true] } tmp = ids.reject { |id| existence_map[id] } next if tmp.empty? diff --git a/app/models/concerns/account/mappings.rb b/app/models/concerns/account/mappings.rb index b44ff9c844ad60..76c0f64a1c8d20 100644 --- a/app/models/concerns/account/mappings.rb +++ b/app/models/concerns/account/mappings.rb @@ -5,12 +5,12 @@ module Account::Mappings class_methods do def following_map(target_account_ids, account_id) - Follow.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |follow, mapping| - mapping[follow.target_account_id] = { + Follow.where(target_account_id: target_account_ids, account_id: account_id).to_h do |follow| + [follow.target_account_id, { reblogs: follow.show_reblogs?, notify: follow.notify?, languages: follow.languages, - } + }] end end @@ -36,21 +36,21 @@ def blocked_by_map(target_account_ids, account_id) end def muting_map(target_account_ids, account_id) - Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping| - mapping[mute.target_account_id] = { + Mute.where(target_account_id: target_account_ids, account_id: account_id).to_h do |mute| + [mute.target_account_id, { notifications: mute.hide_notifications?, expires_at: mute.expires_at, - } + }] end end def requested_map(target_account_ids, account_id) - FollowRequest.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |follow_request, mapping| - mapping[follow_request.target_account_id] = { + FollowRequest.where(target_account_id: target_account_ids, account_id: account_id).to_h do |follow_request| + [follow_request.target_account_id, { reblogs: follow_request.show_reblogs?, notify: follow_request.notify?, languages: follow_request.languages, - } + }] end end @@ -69,10 +69,10 @@ def endorsed_map(target_account_ids, account_id) end def account_note_map(target_account_ids, account_id) - AccountNote.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |note, mapping| - mapping[note.target_account_id] = { + AccountNote.where(target_account_id: target_account_ids, account_id: account_id).to_h do |note| + [note.target_account_id, { comment: note.comment, - } + }] end end diff --git a/app/models/status.rb b/app/models/status.rb index 2b5f8e58a3ca61..d5cfe2df681239 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -362,7 +362,7 @@ def requires_review_notification? class << self def favourites_map(status_ids, account_id) - Favourite.select(:status_id).where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |f, h| h[f.status_id] = true } + Favourite.select(:status_id).where(status_id: status_ids).where(account_id: account_id).to_h { |f| [f.status_id, true] } end def bookmarks_map(status_ids, account_id) @@ -370,15 +370,15 @@ def bookmarks_map(status_ids, account_id) end def reblogs_map(status_ids, account_id) - unscoped.select(:reblog_of_id).where(reblog_of_id: status_ids).where(account_id: account_id).each_with_object({}) { |s, h| h[s.reblog_of_id] = true } + unscoped.select(:reblog_of_id).where(reblog_of_id: status_ids).where(account_id: account_id).to_h { |s| [s.reblog_of_id, true] } end def mutes_map(conversation_ids, account_id) - ConversationMute.select(:conversation_id).where(conversation_id: conversation_ids).where(account_id: account_id).each_with_object({}) { |m, h| h[m.conversation_id] = true } + ConversationMute.select(:conversation_id).where(conversation_id: conversation_ids).where(account_id: account_id).to_h { |m| [m.conversation_id, true] } end def pins_map(status_ids, account_id) - StatusPin.select(:status_id).where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |p, h| h[p.status_id] = true } + StatusPin.select(:status_id).where(status_id: status_ids).where(account_id: account_id).to_h { |p| [p.status_id, true] } end def from_text(text) diff --git a/app/presenters/account_relationships_presenter.rb b/app/presenters/account_relationships_presenter.rb index f06aeb8674113a..d924e1f2a31eb6 100644 --- a/app/presenters/account_relationships_presenter.rb +++ b/app/presenters/account_relationships_presenter.rb @@ -60,7 +60,7 @@ def domain_blocking_map Rails.cache.write_multi(to_cache, expires_in: 1.day) # Return formatted value - @accounts.each_with_object({}) { |account, h| h[account.id] = blocks_by_domain[account.domain] } + @accounts.to_h { |account| [account.id, blocks_by_domain[account.domain]] } end def cached diff --git a/app/presenters/tag_relationships_presenter.rb b/app/presenters/tag_relationships_presenter.rb index 922eb7a39ba979..669b35886250d5 100644 --- a/app/presenters/tag_relationships_presenter.rb +++ b/app/presenters/tag_relationships_presenter.rb @@ -8,8 +8,8 @@ def initialize(tags, current_account_id = nil, **options) @following_map = {} @featuring_map = {} else - @following_map = TagFollow.select(:tag_id).where(tag_id: tags.map(&:id), account_id: current_account_id).each_with_object({}) { |f, h| h[f.tag_id] = true }.merge(options[:following_map] || {}) - @featuring_map = FeaturedTag.select(:tag_id).where(tag_id: tags.map(&:id), account_id: current_account_id).each_with_object({}) { |f, h| h[f.tag_id] = true }.merge(options[:featuring_map] || {}) + @following_map = TagFollow.select(:tag_id).where(tag_id: tags.map(&:id), account_id: current_account_id).to_h { |f| [f.tag_id, true] }.merge(options[:following_map] || {}) + @featuring_map = FeaturedTag.select(:tag_id).where(tag_id: tags.map(&:id), account_id: current_account_id).to_h { |f| [f.tag_id, true] }.merge(options[:featuring_map] || {}) end end end From 73d97153d867153943d0af73b7f82b228dfe170c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 9 Mar 2026 06:01:48 -0400 Subject: [PATCH 010/203] Update `connection_pool` to version 3.0.2 (#38103) --- Gemfile.lock | 2 +- app/lib/connection_pool/shared_connection_pool.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e867009e83b0fd..5176eca307ac74 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -170,7 +170,7 @@ GEM cocoon (1.2.15) color_diff (0.2) concurrent-ruby (1.3.6) - connection_pool (2.5.5) + connection_pool (3.0.2) cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) diff --git a/app/lib/connection_pool/shared_connection_pool.rb b/app/lib/connection_pool/shared_connection_pool.rb index c7dd747edabc0f..2a66afbdc999c3 100644 --- a/app/lib/connection_pool/shared_connection_pool.rb +++ b/app/lib/connection_pool/shared_connection_pool.rb @@ -4,7 +4,7 @@ require_relative 'shared_timed_stack' class ConnectionPool::SharedConnectionPool < ConnectionPool - def initialize(options = {}, &block) + def initialize(**, &block) super @available = ConnectionPool::SharedTimedStack.new(@size, &block) From 4211b1b34ce4fa50c14f3dff671948df04177856 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 11:47:20 +0100 Subject: [PATCH 011/203] Update dependency faker to v3.6.1 (#38074) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5176eca307ac74..120b28f7577fe9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -233,7 +233,7 @@ GEM excon (1.3.2) logger fabrication (3.0.0) - faker (3.6.0) + faker (3.6.1) i18n (>= 1.8.11, < 2) faraday (2.14.1) faraday-net_http (>= 2.0, < 3.5) From e235c446c96280c015fb27f7eefe4e95a5a39892 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 9 Mar 2026 12:26:16 +0100 Subject: [PATCH 012/203] Fix broken option defaults in `tootctl email-domain-blocks` (#38107) --- lib/mastodon/cli/email_domain_blocks.rb | 4 ++-- spec/lib/mastodon/cli/email_domain_blocks_spec.rb | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/mastodon/cli/email_domain_blocks.rb b/lib/mastodon/cli/email_domain_blocks.rb index a6093685b9bb35..818ca8d8f87325 100644 --- a/lib/mastodon/cli/email_domain_blocks.rb +++ b/lib/mastodon/cli/email_domain_blocks.rb @@ -5,7 +5,7 @@ module Mastodon::CLI class EmailDomainBlocks < Base - option :only_blocked, type: :boolean, defaut: false + option :only_blocked, type: :boolean, default: false option :only_with_approval, type: :boolean, default: false desc 'list', 'List blocked e-mail domains' long_desc <<-LONG_DESC @@ -43,7 +43,7 @@ def list end option :with_dns_records, type: :boolean - option :allow_with_approval, type: :boolean, defaut: false + option :allow_with_approval, type: :boolean, default: false desc 'add DOMAIN...', 'Block e-mail domain(s)' long_desc <<-LONG_DESC Blocking an e-mail domain prevents users from signing up diff --git a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb index 1662785f392a71..a564600eafd95f 100644 --- a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb +++ b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb @@ -101,7 +101,6 @@ context 'when no blocks exist' do let(:domain) { 'host.example' } let(:arguments) { [domain] } - let(:options) { { allow_with_approval: false } } it 'adds a new block' do expect { subject } @@ -113,7 +112,7 @@ context 'with --with-dns-records true' do let(:domain) { 'host.example' } let(:arguments) { [domain] } - let(:options) { { allow_with_approval: false, with_dns_records: true } } + let(:options) { { with_dns_records: true } } before do configure_mx(domain: domain, exchange: 'other.host') From 2c6d0721756b936a121c23ed2ca31b9cf3e0ccc0 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 9 Mar 2026 07:30:41 -0400 Subject: [PATCH 013/203] Extract `ErrorResponses` from application controller (#38105) --- app/controllers/application_controller.rb | 59 +------- app/controllers/concerns/error_responses.rb | 68 +++++++++ .../application_controller_spec.rb | 134 ++---------------- .../concerns/error_responses_spec.rb | 128 +++++++++++++++++ 4 files changed, 205 insertions(+), 184 deletions(-) create mode 100644 app/controllers/concerns/error_responses.rb create mode 100644 spec/controllers/concerns/error_responses_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a19fcc7a0aeb1b..deb60f69bc7f8f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -9,6 +9,7 @@ class ApplicationController < ActionController::Base include UserTrackingConcern include SessionTrackingConcern include CacheConcern + include ErrorResponses include PreloadingConcern include DomainControlHelper include DatabaseHelper @@ -23,21 +24,6 @@ class ApplicationController < ActionController::Base helper_method :limited_federation_mode? helper_method :skip_csrf_meta_tags? - rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request - rescue_from Mastodon::NotPermittedError, with: :forbidden - rescue_from ActionController::RoutingError, ActiveRecord::RecordNotFound, with: :not_found - rescue_from ActionController::UnknownFormat, with: :not_acceptable - rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_content - rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests - - rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error) - rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable - - rescue_from Seahorse::Client::NetworkingError do |e| - Rails.logger.warn "Storage server error: #{e}" - service_unavailable - end - before_action :check_self_destruct! before_action :store_referrer, except: :raise_not_found, if: :devise_controller? @@ -118,42 +104,6 @@ def truthy_param?(key) ActiveModel::Type::Boolean.new.cast(params[key]) end - def forbidden - respond_with_error(403) - end - - def not_found - respond_with_error(404) - end - - def gone - respond_with_error(410) - end - - def unprocessable_content - respond_with_error(422) - end - - def not_acceptable - respond_with_error(406) - end - - def bad_request - respond_with_error(400) - end - - def internal_server_error - respond_with_error(500) - end - - def service_unavailable - respond_with_error(503) - end - - def too_many_requests - respond_with_error(429) - end - def single_user_mode? @single_user_mode ||= Rails.configuration.x.single_user_mode && Account.without_internal.exists? end @@ -178,13 +128,6 @@ def current_session @current_session = SessionActivation.find_by(session_id: cookies.signed['_session_id']) if cookies.signed['_session_id'].present? end - def respond_with_error(code) - respond_to do |format| - format.any { render "errors/#{code}", layout: 'error', status: code, formats: [:html] } - format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code } - end - end - def check_self_destruct! return unless self_destruct? diff --git a/app/controllers/concerns/error_responses.rb b/app/controllers/concerns/error_responses.rb new file mode 100644 index 00000000000000..402ade0066a098 --- /dev/null +++ b/app/controllers/concerns/error_responses.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module ErrorResponses + extend ActiveSupport::Concern + + included do + rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_content + rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request + rescue_from ActionController::RoutingError, ActiveRecord::RecordNotFound, with: :not_found + rescue_from ActionController::UnknownFormat, with: :not_acceptable + rescue_from Mastodon::NotPermittedError, with: :forbidden + rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable + rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests + rescue_from(*Mastodon::HTTP_CONNECTION_ERRORS, with: :internal_server_error) + + rescue_from Seahorse::Client::NetworkingError do |e| + Rails.logger.warn "Storage server error: #{e}" + service_unavailable + end + end + + protected + + def bad_request + respond_with_error(400) + end + + def forbidden + respond_with_error(403) + end + + def gone + respond_with_error(410) + end + + def internal_server_error + respond_with_error(500) + end + + def not_acceptable + respond_with_error(406) + end + + def not_found + respond_with_error(404) + end + + def service_unavailable + respond_with_error(503) + end + + def too_many_requests + respond_with_error(429) + end + + def unprocessable_content + respond_with_error(422) + end + + private + + def respond_with_error(code) + respond_to do |format| + format.any { render "errors/#{code}", layout: 'error', formats: [:html], status: code } + format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code } + end + end +end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 24b775a5ad8c2c..898edb3e233370 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -6,52 +6,21 @@ render_views controller do - def success - head 200 - end - - def routing_error - raise ActionController::RoutingError, '' - end - - def record_not_found - raise ActiveRecord::RecordNotFound, '' - end - - def invalid_authenticity_token - raise ActionController::InvalidAuthenticityToken, '' - end - end - - shared_examples 'error response' do |code| - it "returns http #{code} for http and renders template" do - subject - - expect(response) - .to have_http_status(code) - expect(response.parsed_body) - .to have_css('body[class=error]') - expect(response.parsed_body.css('h1').to_s) - .to include(error_content(code)) - end - - def error_content(code) - if code == 422 - I18n.t('errors.422.content') - else - I18n.t("errors.#{code}") - end - end + def success = head(200) end context 'with a forgery' do - subject do + before do ActionController::Base.allow_forgery_protection = true routes.draw { post 'success' => 'anonymous#success' } - post 'success' end - it_behaves_like 'error response', 422 + it 'responds with 422 and error page' do + post 'success' + + expect(response) + .to have_http_status(422) + end end describe 'helper_method :current_account' do @@ -85,33 +54,6 @@ def error_content(code) end end - context 'with ActionController::RoutingError' do - subject do - routes.draw { get 'routing_error' => 'anonymous#routing_error' } - get 'routing_error' - end - - it_behaves_like 'error response', 404 - end - - context 'with ActiveRecord::RecordNotFound' do - subject do - routes.draw { get 'record_not_found' => 'anonymous#record_not_found' } - get 'record_not_found' - end - - it_behaves_like 'error response', 404 - end - - context 'with ActionController::InvalidAuthenticityToken' do - subject do - routes.draw { get 'invalid_authenticity_token' => 'anonymous#invalid_authenticity_token' } - get 'invalid_authenticity_token' - end - - it_behaves_like 'error response', 422 - end - describe 'before_action :check_suspension' do before do routes.draw { get 'success' => 'anonymous#success' } @@ -141,64 +83,4 @@ def error_content(code) expect { controller.raise_not_found }.to raise_error(ActionController::RoutingError, 'No route matches unmatched') end end - - describe 'forbidden' do - controller do - def route_forbidden - forbidden - end - end - - subject do - routes.draw { get 'route_forbidden' => 'anonymous#route_forbidden' } - get 'route_forbidden' - end - - it_behaves_like 'error response', 403 - end - - describe 'not_found' do - controller do - def route_not_found - not_found - end - end - - subject do - routes.draw { get 'route_not_found' => 'anonymous#route_not_found' } - get 'route_not_found' - end - - it_behaves_like 'error response', 404 - end - - describe 'gone' do - controller do - def route_gone - gone - end - end - - subject do - routes.draw { get 'route_gone' => 'anonymous#route_gone' } - get 'route_gone' - end - - it_behaves_like 'error response', 410 - end - - describe 'unprocessable_content' do - controller do - def route_unprocessable_content - unprocessable_content - end - end - - subject do - routes.draw { get 'route_unprocessable_content' => 'anonymous#route_unprocessable_content' } - get 'route_unprocessable_content' - end - - it_behaves_like 'error response', 422 - end end diff --git a/spec/controllers/concerns/error_responses_spec.rb b/spec/controllers/concerns/error_responses_spec.rb new file mode 100644 index 00000000000000..678d8765180f73 --- /dev/null +++ b/spec/controllers/concerns/error_responses_spec.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ErrorResponses do + render_views + + shared_examples 'error response' do |code| + before { routes.draw { get 'show' => 'anonymous#show' } } + + it "returns http #{code} and renders error template" do + get 'show' + + expect(response) + .to have_http_status(code) + expect(response.parsed_body) + .to have_css('body[class=error]') + .and have_css('h1', text: error_content(code)) + end + + def error_content(code) + I18n.t("errors.#{code}") + .then { |value| I18n.t("errors.#{code}.content") if value.is_a?(Hash) } + end + end + + describe 'bad_request' do + controller(ApplicationController) do + def show = bad_request + end + + it_behaves_like 'error response', 400 + end + + describe 'forbidden' do + controller(ApplicationController) do + def show = forbidden + end + + it_behaves_like 'error response', 403 + end + + describe 'gone' do + controller(ApplicationController) do + def show = gone + end + + it_behaves_like 'error response', 410 + end + + describe 'internal_server_error' do + controller(ApplicationController) do + def show = internal_server_error + end + + it_behaves_like 'error response', 500 + end + + describe 'not_acceptable' do + controller(ApplicationController) do + def show = not_acceptable + end + + it_behaves_like 'error response', 406 + end + + describe 'not_found' do + controller(ApplicationController) do + def show = not_found + end + + it_behaves_like 'error response', 404 + end + + describe 'service_unavailable' do + controller(ApplicationController) do + def show = service_unavailable + end + + it_behaves_like 'error response', 503 + end + + describe 'too_many_requests' do + controller(ApplicationController) do + def show = too_many_requests + end + + it_behaves_like 'error response', 429 + end + + describe 'unprocessable_content' do + controller(ApplicationController) do + def show = unprocessable_content + end + + it_behaves_like 'error response', 422 + end + + context 'with ActionController::RoutingError' do + controller(ApplicationController) do + def show + raise ActionController::RoutingError, '' + end + end + + it_behaves_like 'error response', 404 + end + + context 'with ActiveRecord::RecordNotFound' do + controller(ApplicationController) do + def show + raise ActiveRecord::RecordNotFound, '' + end + end + + it_behaves_like 'error response', 404 + end + + context 'with ActionController::InvalidAuthenticityToken' do + controller(ApplicationController) do + def show + raise ActionController::InvalidAuthenticityToken, '' + end + end + + it_behaves_like 'error response', 422 + end +end From 1d46558e8d949860acc4f3a86516b9989bd74f79 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 9 Mar 2026 15:59:57 +0100 Subject: [PATCH 014/203] Ingestion of remote collection items (#38106) --- app/lib/activitypub/activity/add.rb | 7 + app/models/collection_item.rb | 2 +- .../process_featured_item_service.rb | 34 ++++ .../verify_featured_item_service.rb | 34 ++++ .../verify_featured_item_worker.rb | 20 +++ spec/lib/activitypub/activity/add_spec.rb | 154 +++++++++++------- spec/models/collection_item_spec.rb | 8 +- .../process_featured_item_service_spec.rb | 82 ++++++++++ .../verify_featured_item_service_spec.rb | 86 ++++++++++ .../verify_featured_item_worker_spec.rb | 32 ++++ 10 files changed, 399 insertions(+), 60 deletions(-) create mode 100644 app/services/activitypub/process_featured_item_service.rb create mode 100644 app/services/activitypub/verify_featured_item_service.rb create mode 100644 app/workers/activitypub/verify_featured_item_worker.rb create mode 100644 spec/services/activitypub/process_featured_item_service_spec.rb create mode 100644 spec/services/activitypub/verify_featured_item_service_spec.rb create mode 100644 spec/workers/activitypub/verify_featured_item_worker_spec.rb diff --git a/app/lib/activitypub/activity/add.rb b/app/lib/activitypub/activity/add.rb index 9e2483983dcdad..c86862c0a30ce7 100644 --- a/app/lib/activitypub/activity/add.rb +++ b/app/lib/activitypub/activity/add.rb @@ -12,6 +12,9 @@ def perform else add_featured end + else + @collection = @account.collections.find_by(uri: @json['target']) + add_collection_item if @collection && Mastodon::Feature.collections_federation_enabled? end end @@ -30,4 +33,8 @@ def add_featured_tags FeaturedTag.create!(account: @account, name: name) if name.present? end + + def add_collection_item + ActivityPub::ProcessFeaturedItemService.new.call(@collection, @object) + end end diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb index e113b3b5227e88..1cc8d80e625ecc 100644 --- a/app/models/collection_item.rb +++ b/app/models/collection_item.rb @@ -29,7 +29,7 @@ class CollectionItem < ApplicationRecord validates :position, numericality: { only_integer: true, greater_than: 0 } validates :activity_uri, presence: true, if: :local_item_with_remote_account? - validates :approval_uri, absence: true, unless: :local? + validates :approval_uri, presence: true, unless: -> { local? || account&.local? } validates :account, presence: true, if: :accepted? validates :object_uri, presence: true, if: -> { account.nil? } validates :uri, presence: true, if: :remote? diff --git a/app/services/activitypub/process_featured_item_service.rb b/app/services/activitypub/process_featured_item_service.rb new file mode 100644 index 00000000000000..24f3bbfaed15c4 --- /dev/null +++ b/app/services/activitypub/process_featured_item_service.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class ActivityPub::ProcessFeaturedItemService + include JsonLdHelper + include Lockable + include Redisable + + def call(collection, uri_or_object) + item_json = uri_or_object.is_a?(String) ? fetch_resource(uri_or_object, true) : uri_or_object + return if non_matching_uri_hosts?(collection.uri, item_json['id']) + + with_redis_lock("collection_item:#{item_json['id']}") do + return if collection.collection_items.exists?(uri: item_json['id']) + + @collection_item = collection.collection_items.create!( + uri: item_json['id'], + object_uri: item_json['featuredObject'], + approval_uri: item_json['featureAuthorization'] + ) + + verify_authorization! + + @collection_item + end + end + + private + + def verify_authorization! + ActivityPub::VerifyFeaturedItemService.new.call(@collection_item) + rescue Mastodon::RecursionLimitExceededError, Mastodon::UnexpectedResponseError, *Mastodon::HTTP_CONNECTION_ERRORS + ActivityPub::VerifyFeaturedItemWorker.perform_in(rand(30..600).seconds, @collection_item.id) + end +end diff --git a/app/services/activitypub/verify_featured_item_service.rb b/app/services/activitypub/verify_featured_item_service.rb new file mode 100644 index 00000000000000..f3dcccf4a0f2db --- /dev/null +++ b/app/services/activitypub/verify_featured_item_service.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class ActivityPub::VerifyFeaturedItemService + include JsonLdHelper + + def call(collection_item) + @collection_item = collection_item + @authorization = fetch_resource(@collection_item.approval_uri, true, raise_on_error: :temporary) + + if @authorization.nil? + @collection_item.update!(state: :rejected) + return + end + + return if non_matching_uri_hosts?(@collection_item.approval_uri, @authorization['interactionTarget']) + return unless matching_type? && matching_collection_uri? + + account = Account.where(uri: @collection_item.object_uri).first + account ||= ActivityPub::FetchRemoteAccountService.new.call(@collection_item.object_uri) + return if account.blank? + + @collection_item.update!(account:, state: :accepted) + end + + private + + def matching_type? + supported_context?(@authorization) && equals_or_includes?(@authorization['type'], 'FeatureAuthorization') + end + + def matching_collection_uri? + @collection_item.collection.uri == @authorization['interactingObject'] + end +end diff --git a/app/workers/activitypub/verify_featured_item_worker.rb b/app/workers/activitypub/verify_featured_item_worker.rb new file mode 100644 index 00000000000000..6eda194717047d --- /dev/null +++ b/app/workers/activitypub/verify_featured_item_worker.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class ActivityPub::VerifyFeaturedItemWorker + include Sidekiq::Worker + include ExponentialBackoff + include JsonLdHelper + + sidekiq_options queue: 'pull', retry: 5 + + def perform(collection_item_id) + collection_item = CollectionItem.find(collection_item_id) + + ActivityPub::VerifyFeaturedItemService.new.call(collection_item) + rescue ActiveRecord::RecordNotFound + # Do nothing + nil + rescue Mastodon::UnexpectedResponseError => e + raise e unless response_error_unsalvageable?(e.response) + end +end diff --git a/spec/lib/activitypub/activity/add_spec.rb b/spec/lib/activitypub/activity/add_spec.rb index c0abd9f39376d4..0f8ce53cfb9109 100644 --- a/spec/lib/activitypub/activity/add_spec.rb +++ b/spec/lib/activitypub/activity/add_spec.rb @@ -3,78 +3,118 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Add do - let(:sender) { Fabricate(:account, featured_collection_url: 'https://example.com/featured', domain: 'example.com') } - let(:status) { Fabricate(:status, account: sender, visibility: :private) } - - let(:json) do - { - '@context': 'https://www.w3.org/ns/activitystreams', - id: 'foo', - type: 'Add', - actor: ActivityPub::TagManager.instance.uri_for(sender), - object: ActivityPub::TagManager.instance.uri_for(status), - target: sender.featured_collection_url, - }.with_indifferent_access - end - - describe '#perform' do - subject { described_class.new(json, sender) } + context 'when the target is the featured collection' do + let(:sender) { Fabricate(:account, featured_collection_url: 'https://example.com/featured', domain: 'example.com') } + let(:status) { Fabricate(:status, account: sender, visibility: :private) } - it 'creates a pin' do - subject.perform - expect(sender.pinned?(status)).to be true + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Add', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: ActivityPub::TagManager.instance.uri_for(status), + target: sender.featured_collection_url, + }.with_indifferent_access end - context 'when status was not known before' do - let(:service_stub) { instance_double(ActivityPub::FetchRemoteStatusService) } - - let(:json) do - { - '@context': 'https://www.w3.org/ns/activitystreams', - id: 'foo', - type: 'Add', - actor: ActivityPub::TagManager.instance.uri_for(sender), - object: 'https://example.com/unknown', - target: sender.featured_collection_url, - }.with_indifferent_access - end + describe '#perform' do + subject { described_class.new(json, sender) } - before do - allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(service_stub) + it 'creates a pin' do + subject.perform + expect(sender.pinned?(status)).to be true end - context 'when there is a local follower' do + context 'when status was not known before' do + let(:service_stub) { instance_double(ActivityPub::FetchRemoteStatusService) } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Add', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: 'https://example.com/unknown', + target: sender.featured_collection_url, + }.with_indifferent_access + end + before do - account = Fabricate(:account) - account.follow!(sender) + allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(service_stub) end - it 'fetches the status and pins it' do - allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, **| - expect(uri).to eq 'https://example.com/unknown' - expect(id).to be true - expect(on_behalf_of&.following?(sender)).to be true - status + context 'when there is a local follower' do + before do + account = Fabricate(:account) + account.follow!(sender) + end + + it 'fetches the status and pins it' do + allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, **| + expect(uri).to eq 'https://example.com/unknown' + expect(id).to be true + expect(on_behalf_of&.following?(sender)).to be true + status + end + subject.perform + expect(service_stub).to have_received(:call) + expect(sender.pinned?(status)).to be true end - subject.perform - expect(service_stub).to have_received(:call) - expect(sender.pinned?(status)).to be true end - end - context 'when there is no local follower' do - it 'tries to fetch the status' do - allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, **| - expect(uri).to eq 'https://example.com/unknown' - expect(id).to be true - expect(on_behalf_of).to be_nil - nil + context 'when there is no local follower' do + it 'tries to fetch the status' do + allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, **| + expect(uri).to eq 'https://example.com/unknown' + expect(id).to be true + expect(on_behalf_of).to be_nil + nil + end + subject.perform + expect(service_stub).to have_received(:call) + expect(sender.pinned?(status)).to be false end - subject.perform - expect(service_stub).to have_received(:call) - expect(sender.pinned?(status)).to be false end end end end + + context 'when the target is a collection', feature: :collections_federation do + subject { described_class.new(activity_json, collection.account) } + + let(:collection) { Fabricate(:remote_collection) } + let(:featured_item_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => 'https://other.example.com/featured_item/1', + 'type' => 'FeaturedItem', + 'featuredObject' => 'https://example.com/actor/1', + 'featuredObjectType' => 'Person', + 'featureAuthorization' => 'https://example.com/auth/1', + } + end + let(:activity_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'type' => 'Add', + 'actor' => collection.account.uri, + 'target' => collection.uri, + 'object' => featured_item_json, + } + end + let(:stubbed_service) do + instance_double(ActivityPub::ProcessFeaturedItemService, call: true) + end + + before do + allow(ActivityPub::ProcessFeaturedItemService).to receive(:new).and_return(stubbed_service) + end + + it 'determines the correct collection and calls the service' do + subject.perform + + expect(stubbed_service).to have_received(:call).with(collection, featured_item_json) + end + end end diff --git a/spec/models/collection_item_spec.rb b/spec/models/collection_item_spec.rb index e4905535cfc263..e8be8c260b0316 100644 --- a/spec/models/collection_item_spec.rb +++ b/spec/models/collection_item_spec.rb @@ -21,9 +21,13 @@ let(:remote_collection) { Fabricate.build(:collection, local: false) } - it { is_expected.to validate_absence_of(:approval_uri) } - it { is_expected.to validate_presence_of(:uri) } + + context 'when account is not present' do + subject { Fabricate.build(:collection_item, collection: remote_collection, account: nil) } + + it { is_expected.to validate_presence_of(:approval_uri) } + end end context 'when account is not present' do diff --git a/spec/services/activitypub/process_featured_item_service_spec.rb b/spec/services/activitypub/process_featured_item_service_spec.rb new file mode 100644 index 00000000000000..dab26f846bb433 --- /dev/null +++ b/spec/services/activitypub/process_featured_item_service_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::ProcessFeaturedItemService do + subject { described_class.new } + + let(:collection) { Fabricate(:remote_collection, uri: 'https://other.example.com/collection/1') } + let(:featured_item_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => 'https://other.example.com/featured_item/1', + 'type' => 'FeaturedItem', + 'featuredObject' => 'https://example.com/actor/1', + 'featuredObjectType' => 'Person', + 'featureAuthorization' => 'https://example.com/auth/1', + } + end + let(:stubbed_service) do + instance_double(ActivityPub::VerifyFeaturedItemService, call: true) + end + + before do + allow(ActivityPub::VerifyFeaturedItemService).to receive(:new).and_return(stubbed_service) + end + + shared_examples 'non-matching URIs' do + context "when the item's URI does not match the collection's" do + let(:collection) { Fabricate(:remote_collection) } + + it 'does not create a collection item and returns `nil`' do + expect do + expect(subject.call(collection, object)).to be_nil + end.to_not change(CollectionItem, :count) + end + end + end + + context 'when the collection item is inlined' do + let(:object) { featured_item_json } + + it_behaves_like 'non-matching URIs' + + it 'creates and verifies the item' do + expect { subject.call(collection, object) }.to change(collection.collection_items, :count).by(1) + + expect(stubbed_service).to have_received(:call) + + new_item = collection.collection_items.last + expect(new_item.object_uri).to eq 'https://example.com/actor/1' + expect(new_item.approval_uri).to eq 'https://example.com/auth/1' + end + end + + context 'when only the id of the collection item is given' do + let(:object) { featured_item_json['id'] } + let(:featured_item_request) do + stub_request(:get, object) + .to_return_json( + status: 200, + body: featured_item_json, + headers: { 'Content-Type' => 'application/activity+json' } + ) + end + + before do + featured_item_request + end + + it_behaves_like 'non-matching URIs' + + it 'fetches the collection item' do + expect { subject.call(collection, object) }.to change(collection.collection_items, :count).by(1) + + expect(featured_item_request).to have_been_requested + + new_item = collection.collection_items.last + expect(new_item.object_uri).to eq 'https://example.com/actor/1' + expect(new_item.approval_uri).to eq 'https://example.com/auth/1' + end + end +end diff --git a/spec/services/activitypub/verify_featured_item_service_spec.rb b/spec/services/activitypub/verify_featured_item_service_spec.rb new file mode 100644 index 00000000000000..5976ffeffc9120 --- /dev/null +++ b/spec/services/activitypub/verify_featured_item_service_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::VerifyFeaturedItemService do + subject { described_class.new } + + let(:collection) { Fabricate(:remote_collection) } + let(:collection_item) do + Fabricate(:collection_item, + collection:, + account: nil, + state: :pending, + uri: 'https://other.example.com/items/1', + object_uri: 'https://example.com/actor/1', + approval_uri: verification_json['id']) + end + let(:verification_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'type' => 'FeatureAuthorization', + 'id' => 'https://example.com/auth/1', + 'interactionTarget' => 'https://example.com/actor/1', + 'interactingObject' => collection.uri, + } + end + let(:verification_request) do + stub_request(:get, 'https://example.com/auth/1') + .to_return_json( + status: 200, + body: verification_json, + headers: { 'Content-Type' => 'application/activity+json' } + ) + end + let(:featured_account) { Fabricate(:remote_account, uri: 'https://example.com/actor/1') } + + before { verification_request } + + context 'when the authorization can be verified' do + context 'when the featured account is known' do + before { featured_account } + + it 'verifies and creates the item' do + subject.call(collection_item) + + expect(verification_request).to have_been_requested + + expect(collection_item.account_id).to eq featured_account.id + expect(collection_item).to be_accepted + end + end + + context 'when the featured account is not known' do + let(:stubbed_service) { instance_double(ActivityPub::FetchRemoteAccountService) } + + before do + allow(stubbed_service).to receive(:call).with('https://example.com/actor/1') { featured_account } + allow(ActivityPub::FetchRemoteAccountService).to receive(:new).and_return(stubbed_service) + end + + it 'fetches the actor and creates the item' do + subject.call(collection_item) + + expect(stubbed_service).to have_received(:call) + expect(verification_request).to have_been_requested + + expect(collection_item.account_id).to eq featured_account.id + expect(collection_item).to be_accepted + end + end + end + + context 'when the authorization cannot be verified' do + let(:verification_request) do + stub_request(:get, 'https://example.com/auth/1') + .to_return(status: 404) + end + + it 'creates item without attached account and in proper state' do + subject.call(collection_item) + + expect(collection_item.account_id).to be_nil + expect(collection_item).to be_rejected + end + end +end diff --git a/spec/workers/activitypub/verify_featured_item_worker_spec.rb b/spec/workers/activitypub/verify_featured_item_worker_spec.rb new file mode 100644 index 00000000000000..f94313ce1d3759 --- /dev/null +++ b/spec/workers/activitypub/verify_featured_item_worker_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::VerifyFeaturedItemWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(ActivityPub::VerifyFeaturedItemService, call: true) } + + describe '#perform' do + let(:collection_item) { Fabricate(:unverified_remote_collection_item) } + + before { stub_service } + + it 'sends the status to the service' do + worker.perform(collection_item.id) + + expect(service).to have_received(:call).with(collection_item) + end + + it 'returns nil for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be_nil + end + end + + def stub_service + allow(ActivityPub::VerifyFeaturedItemService) + .to receive(:new) + .and_return(service) + end +end From 71f9763e68a29fb4498d2f4c5f2621e6cf665b7c Mon Sep 17 00:00:00 2001 From: diondiondion Date: Mon, 9 Mar 2026 18:42:54 +0100 Subject: [PATCH 015/203] Fix erratic scroll-to-right on delete & redraft in Advanced UI (#38116) --- app/javascript/mastodon/components/router.tsx | 3 +++ .../mastodon/features/status/index.jsx | 7 +++++- app/javascript/mastodon/features/ui/index.jsx | 23 ++++++++++--------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/app/javascript/mastodon/components/router.tsx b/app/javascript/mastodon/components/router.tsx index 1dc1d45083dfd9..1c251d6abcda9c 100644 --- a/app/javascript/mastodon/components/router.tsx +++ b/app/javascript/mastodon/components/router.tsx @@ -17,6 +17,9 @@ import { isDevelopment } from 'mastodon/utils/environment'; interface MastodonLocationState { fromMastodon?: boolean; mastodonModalKey?: string; + // Prevent the rightmost column in advanced UI from scrolling + // into view on location changes + preventMultiColumnAutoScroll?: string; } export type LocationState = MastodonLocationState | null | undefined; diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx index 77e8af4b679f44..a94c94492df314 100644 --- a/app/javascript/mastodon/features/status/index.jsx +++ b/app/javascript/mastodon/features/status/index.jsx @@ -255,7 +255,12 @@ class Status extends ImmutablePureComponent { const { dispatch, history } = this.props; const handleDeleteSuccess = () => { - history.push('/'); + history.push('/', { + // Preventing the default "scroll to right" on + // location change in advanced UI to avoid conflict + // with the composer being focused + preventMultiColumnAutoScroll: true + }); }; if (!deleteModal) { diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 55bc8f990114fe..47fe6ff0ccc183 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -136,7 +136,9 @@ class SwitchingColumnsArea extends PureComponent { } handleChildrenContentChange() { - if (!this.props.singleColumn) { + const {preventMultiColumnAutoScroll} = this.props.location.state ?? {}; + + if (!this.props.singleColumn && !preventMultiColumnAutoScroll) { const isRtlLayout = document.getElementsByTagName('body')[0] ?.classList.contains('rtl'); const modifier = isRtlLayout ? -1 : 1; @@ -156,24 +158,23 @@ class SwitchingColumnsArea extends PureComponent { const { signedIn } = this.props.identity; const pathName = this.props.location.pathname; - let redirect; - + let rootRedirect; if (signedIn) { if (forceOnboarding) { - redirect = ; + rootRedirect = '/start'; } else if (singleColumn) { - redirect = ; + rootRedirect = '/home'; } else { - redirect = ; + rootRedirect = '/deck/getting-started'; } } else if (singleUserMode && owner && initialState?.accounts[owner]) { - redirect = ; + rootRedirect = `/@${initialState.accounts[owner].username}`; } else if (trendsEnabled && landingPage === 'trends') { - redirect = ; + rootRedirect = '/explore'; } else if (localLiveFeedAccess === 'public' && landingPage === 'local_feed') { - redirect = ; + rootRedirect = '/public/local'; } else { - redirect = ; + rootRedirect = '/about'; } const profileRedesignRoutes = []; @@ -194,7 +195,7 @@ class SwitchingColumnsArea extends PureComponent { - {redirect} + {singleColumn ? : null} {singleColumn && pathName.startsWith('/deck/') ? : null} From cec60d5b71ea98a7e15c368869cfceccdc4d49cd Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 10 Mar 2026 05:46:03 -0400 Subject: [PATCH 016/203] Clean up `search` tagged specs (#38085) --- .../models/concerns/account/search_spec.rb | 68 ++++++++-------- .../concerns/account/statuses_search_spec.rb | 73 +++++++++-------- spec/support/search.rb | 32 ++++++++ spec/support/search_data_manager.rb | 81 ------------------- 4 files changed, 105 insertions(+), 149 deletions(-) create mode 100644 spec/support/search.rb delete mode 100644 spec/support/search_data_manager.rb diff --git a/spec/search/models/concerns/account/search_spec.rb b/spec/search/models/concerns/account/search_spec.rb index de12161ef9f3a6..7b1fc695dbaf74 100644 --- a/spec/search/models/concerns/account/search_spec.rb +++ b/spec/search/models/concerns/account/search_spec.rb @@ -3,48 +3,48 @@ require 'rails_helper' RSpec.describe Account::Search do - describe 'a non-discoverable account becoming discoverable' do - let(:account) { Account.find_by(username: 'search_test_account_1') } - - context 'when picking a non-discoverable account' do - it 'its bio is not in the AccountsIndex' do - results = AccountsIndex.filter(term: { username: account.username }) - expect(results.count).to eq(1) - expect(results.first.text).to be_nil + describe 'Callbacks for discoverable changes' do + let(:results) { AccountsIndex.filter(term: { username: account.username }) } + + context 'with a non-discoverable account' do + let(:account) { Fabricate :account, discoverable: false, note: 'Account note' } + + context 'when looking for the non discoverable account' do + it 'is missing account bio in the AccountsIndex' do + expect(results.count) + .to eq(1) + expect(results.first.text) + .to be_nil + end end - end - - context 'when the non-discoverable account becomes discoverable' do - it 'its bio is added to the AccountsIndex' do - account.discoverable = true - account.save! - results = AccountsIndex.filter(term: { username: account.username }) - expect(results.count).to eq(1) - expect(results.first.text).to eq(account.note) + context 'when the account becomes discoverable' do + it 'has an account bio in the AccountsIndex' do + expect { account.update! discoverable: true } + .to change { results.first.text }.from(be_blank).to(account.note) + .and not_change(results, :count).from(1) + end end end - end - describe 'a discoverable account becoming non-discoverable' do - let(:account) { Account.find_by(username: 'search_test_account_0') } + describe 'with a discoverable account' do + let(:account) { Fabricate :account, discoverable: true } - context 'when picking an discoverable account' do - it 'has its bio in the AccountsIndex' do - results = AccountsIndex.filter(term: { username: account.username }) - expect(results.count).to eq(1) - expect(results.first.text).to eq(account.note) + context 'when looking for the account' do + it 'is present in the AccountsIndex' do + expect(results.count) + .to eq(1) + expect(results.first.text) + .to eq(account.note) + end end - end - - context 'when the discoverable account becomes non-discoverable' do - it 'its bio is removed from the AccountsIndex' do - account.discoverable = false - account.save! - results = AccountsIndex.filter(term: { username: account.username }) - expect(results.count).to eq(1) - expect(results.first.text).to be_nil + context 'when the account becomes non-discoverable' do + it 'is missing from the AccountsIndex' do + expect { account.update! discoverable: false } + .to change { results.first.text }.from(account.note).to(be_blank) + .and not_change(results, :count).from(1) + end end end end diff --git a/spec/search/models/concerns/account/statuses_search_spec.rb b/spec/search/models/concerns/account/statuses_search_spec.rb index bce1aecd7505f3..716d1b28ffebde 100644 --- a/spec/search/models/concerns/account/statuses_search_spec.rb +++ b/spec/search/models/concerns/account/statuses_search_spec.rb @@ -3,50 +3,55 @@ require 'rails_helper' RSpec.describe Account::StatusesSearch, :inline_jobs do - describe 'a non-indexable account becoming indexable' do - let(:account) { Account.find_by(username: 'search_test_account_1') } + describe 'Callbacks for indexable changes' do + let(:account) { Fabricate :account, indexable: } + let(:public_statuses_results) { PublicStatusesIndex.filter(term: { account_id: account.id }) } + let(:statuses_results) { StatusesIndex.filter(term: { account_id: account.id }) } + + before do + Fabricate :status, account:, visibility: :private + Fabricate :status, account:, visibility: :public + end - context 'when picking a non-indexable account' do - it 'has no statuses in the PublicStatusesIndex' do - expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(0) - end + context 'with a non-indexable account' do + let(:indexable) { false } - it 'has statuses in the StatusesIndex' do - expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count) + context 'when looking for statuses from the account' do + it 'does not have public index statuses' do + expect(public_statuses_results.count) + .to eq(0) + expect(statuses_results.count) + .to eq(account.statuses.count) + end end - end - context 'when the non-indexable account becomes indexable' do - it 'adds the public statuses to the PublicStatusesIndex' do - account.indexable = true - account.save! - - expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.public_visibility.count) - expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count) + context 'when the non-indexable account becomes indexable' do + it 'does have public index statuses' do + expect { account.update! indexable: true } + .to change(public_statuses_results, :count).to(account.statuses.public_visibility.count) + .and not_change(statuses_results, :count).from(account.statuses.count) + end end end - end - describe 'an indexable account becoming non-indexable' do - let(:account) { Account.find_by(username: 'search_test_account_0') } + describe 'with an indexable account' do + let(:indexable) { true } - context 'when picking an indexable account' do - it 'has statuses in the PublicStatusesIndex' do - expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.public_visibility.count) + context 'when picking an indexable account' do + it 'does have public index statuses' do + expect(public_statuses_results.count) + .to eq(account.statuses.public_visibility.count) + expect(statuses_results.count) + .to eq(account.statuses.count) + end end - it 'has statuses in the StatusesIndex' do - expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count) - end - end - - context 'when the indexable account becomes non-indexable' do - it 'removes the statuses from the PublicStatusesIndex' do - account.indexable = false - account.save! - - expect(PublicStatusesIndex.filter(term: { account_id: account.id }).count).to eq(0) - expect(StatusesIndex.filter(term: { account_id: account.id }).count).to eq(account.statuses.count) + context 'when the indexable account becomes non-indexable' do + it 'does not have public index statuses' do + expect { account.update! indexable: false } + .to change(public_statuses_results, :count).to(0) + .and not_change(statuses_results, :count).from(account.statuses.count) + end end end end diff --git a/spec/support/search.rb b/spec/support/search.rb new file mode 100644 index 00000000000000..adf99caf9e56e9 --- /dev/null +++ b/spec/support/search.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +RSpec.configure do |config| + config.before :suite do + if search_examples_present? + Chewy.settings[:enabled] = true + # Configure chewy to use `urgent` strategy to index documents immediately + Chewy.strategy(:urgent) + else + Chewy.settings[:enabled] = false + end + end + + config.after :each, :search do + search_indices.each(&:delete) + end + + private + + def search_indices + [ + AccountsIndex, + PublicStatusesIndex, + StatusesIndex, + TagsIndex, + ] + end + + def search_examples_present? + RSpec.world.filtered_examples.values.flatten.any? { |example| example.metadata[:search] == true } + end +end diff --git a/spec/support/search_data_manager.rb b/spec/support/search_data_manager.rb deleted file mode 100644 index d521bceeed5738..00000000000000 --- a/spec/support/search_data_manager.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true - -class SearchDataManager - def prepare_test_data - 4.times do |i| - username = "search_test_account_#{i}" - account = Fabricate.create(:account, username: username, indexable: i.even?, discoverable: i.even?, note: "Lover of #{i}.") - 2.times do |j| - Fabricate.create(:status, account: account, text: "#{username}'s #{j} post", visibility: j.even? ? :public : :private) - end - end - - 3.times do |i| - Fabricate.create(:tag, name: "search_test_tag_#{i}") - end - end - - def indexes - [ - AccountsIndex, - PublicStatusesIndex, - StatusesIndex, - TagsIndex, - ] - end - - def populate_indexes - indexes.each do |index_class| - index_class.purge! - index_class.import! - end - end - - def remove_indexes - indexes.each(&:delete!) - end - - def cleanup_test_data - Status.destroy_all - Account.destroy_all - Tag.destroy_all - end -end - -RSpec.configure do |config| - config.before :suite do - if search_examples_present? - Chewy.settings[:enabled] = true - # Configure chewy to use `urgent` strategy to index documents - Chewy.strategy(:urgent) - - # Create search data - search_data_manager.prepare_test_data - else - Chewy.settings[:enabled] = false - end - end - - config.after :suite do - if search_examples_present? - # Clean up after search data - search_data_manager.cleanup_test_data - end - end - - config.around :each, :search do |example| - search_data_manager.populate_indexes - example.run - search_data_manager.remove_indexes - end - - private - - def search_data_manager - @search_data_manager ||= SearchDataManager.new - end - - def search_examples_present? - RSpec.world.filtered_examples.values.flatten.any? { |example| example.metadata[:search] == true } - end -end From b64bed5acdab19bfb3c90476e714303030c51405 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:01:38 +0000 Subject: [PATCH 017/203] New Crowdin Translations (automated) (#38122) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ast.json | 9 +++ app/javascript/mastodon/locales/da.json | 8 +++ app/javascript/mastodon/locales/de.json | 8 +++ app/javascript/mastodon/locales/el.json | 18 +++-- app/javascript/mastodon/locales/en-GB.json | 8 +++ app/javascript/mastodon/locales/es-AR.json | 8 +++ app/javascript/mastodon/locales/es-MX.json | 8 +++ app/javascript/mastodon/locales/es.json | 8 +++ app/javascript/mastodon/locales/fi.json | 27 +++++++- app/javascript/mastodon/locales/ga.json | 8 +++ app/javascript/mastodon/locales/gl.json | 8 +++ app/javascript/mastodon/locales/he.json | 8 +++ app/javascript/mastodon/locales/hu.json | 24 +++++++ app/javascript/mastodon/locales/is.json | 8 +++ app/javascript/mastodon/locales/it.json | 8 +++ app/javascript/mastodon/locales/nl.json | 46 ++++++++++++ app/javascript/mastodon/locales/pt-PT.json | 81 +++++++++++++++++----- app/javascript/mastodon/locales/sq.json | 7 ++ app/javascript/mastodon/locales/sv.json | 4 ++ app/javascript/mastodon/locales/vi.json | 8 +++ app/javascript/mastodon/locales/zh-CN.json | 8 +++ app/javascript/mastodon/locales/zh-TW.json | 8 +++ config/locales/el.yml | 10 +-- config/locales/simple_form.el.yml | 4 +- 24 files changed, 310 insertions(+), 32 deletions(-) diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json index 3ef11b39c99104..92bc871b5f0a2d 100644 --- a/app/javascript/mastodon/locales/ast.json +++ b/app/javascript/mastodon/locales/ast.json @@ -28,6 +28,7 @@ "account.featured_tags.last_status_never": "Nun hai nenguna publicación", "account.follow": "Siguir", "account.follow_back": "Siguir tamién", + "account.follow_request_short": "Solicitú", "account.followers": "Siguidores", "account.followers.empty": "Naide sigue a esti perfil.", "account.following": "Siguiendo", @@ -43,6 +44,7 @@ "account.mute": "Desactivar los avisos de @{name}", "account.mute_notifications_short": "Silenciar avisos", "account.mute_short": "Silenciar", + "account.name.help.header": "Un \"handle\" ye como una dirección de corréu", "account.no_bio": "Nun se fornió nenguna descripción.", "account.open_original_page": "Abrir la páxina orixinal", "account.posts": "Artículos", @@ -59,10 +61,17 @@ "account.unmute": "Activar los avisos de @{name}", "account.unmute_notifications_short": "Dexar de silenciar notificaciones", "account.unmute_short": "Activar los avisos", + "account_edit.custom_fields.placeholder": "Añade los tos pronombres, enllaces externos o cualquier otra cosa que quieras compartir.", + "account_edit.custom_fields.tip_title": "Conseyu: Añadir enllaces verificáos", + "account_edit.display_name.title": "Nome a mostrar", + "account_edit.field_edit_modal.name_hint": "Por exemplu: \"Web personal\"", + "account_edit.profile_tab.hint.title": "Les visualizaciones sigan variando", + "account_edit.verified_modal.details": "Añade credibilidá al to perfil de Mastodon verificando enllaces a webs personales. Asina ye como funciona:", "account_note.placeholder": "Calca equí p'amestar una nota", "admin.dashboard.retention.average": "Media", "admin.dashboard.retention.cohort": "Mes de rexistru", "admin.dashboard.retention.cohort_size": "Perfiles nuevos", + "admin.impact_report.instance_followers": "Seguidores que perderíen los nuestros usuarios", "alert.rate_limited.message": "Volvi tentalo dempués de la hora: {retry_time, time, medium}.", "alert.unexpected.message": "Prodúxose un error inesperáu.", "alert.unexpected.title": "¡Meca!", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index a294b3810caf15..237fdea063902b 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -338,12 +338,14 @@ "collections.create_collection": "Opret samling", "collections.delete_collection": "Slet samling", "collections.description_length_hint": "Begrænset til 100 tegn", + "collections.detail.accept_inclusion": "Okay", "collections.detail.accounts_heading": "Konti", "collections.detail.author_added_you": "{author} tilføjede dig til denne samling", "collections.detail.curated_by_author": "Kurateret af {author}", "collections.detail.curated_by_you": "Kurateret af dig", "collections.detail.loading": "Indlæser samling…", "collections.detail.other_accounts_in_collection": "Andre i denne samling:", + "collections.detail.revoke_inclusion": "Fjern mig", "collections.detail.sensitive_note": "Denne samling indeholder konti og indhold, der kan være følsomt for nogle brugere.", "collections.detail.share": "Del denne samling", "collections.edit_details": "Rediger detaljer", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Seneste indlæg er fra over en uge siden", "collections.remove_account": "Fjern denne konto", "collections.report_collection": "Anmeld denne samling", + "collections.revoke_collection_inclusion": "Fjern mig selv fra denne samling", + "collections.revoke_inclusion.confirmation": "Du er blevet fjernet fra \"{collection}\"", + "collections.revoke_inclusion.error": "Der opstod en fejl, prøv igen senere.", "collections.search_accounts_label": "Søg efter konti for at tilføje…", "collections.search_accounts_max_reached": "Du har tilføjet det maksimale antal konti", "collections.sensitive": "Sensitivt", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Fjern følger", "confirmations.remove_from_followers.message": "{name} vil ikke længere følge dig. Er du sikker på, at du vil fortsætte?", "confirmations.remove_from_followers.title": "Fjern følger?", + "confirmations.revoke_collection_inclusion.confirm": "Fjern mig", + "confirmations.revoke_collection_inclusion.message": "Denne handling er permanent, og kuratoren vil ikke kunne føje dig til samlingen igen senere.", + "confirmations.revoke_collection_inclusion.title": "Fjern dig selv fra denne samling?", "confirmations.revoke_quote.confirm": "Fjern indlæg", "confirmations.revoke_quote.message": "Denne handling kan ikke fortrydes.", "confirmations.revoke_quote.title": "Fjern indlæg?", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 4d6e9ff55baeb6..520f949a6a6ba9 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -338,12 +338,14 @@ "collections.create_collection": "Sammlung erstellen", "collections.delete_collection": "Sammlung löschen", "collections.description_length_hint": "Maximal 100 Zeichen", + "collections.detail.accept_inclusion": "Einverstanden", "collections.detail.accounts_heading": "Konten", "collections.detail.author_added_you": "{author} hat dich zur Sammlung hinzugefügt", "collections.detail.curated_by_author": "Kuratiert von {author}", "collections.detail.curated_by_you": "Kuratiert von dir", "collections.detail.loading": "Sammlung wird geladen …", "collections.detail.other_accounts_in_collection": "Weitere Profile in dieser Sammlung:", + "collections.detail.revoke_inclusion": "Mich entfernen", "collections.detail.sensitive_note": "Diese Sammlung enthält Profile und Inhalte, die manche als anstößig empfinden.", "collections.detail.share": "Sammlung teilen", "collections.edit_details": "Details bearbeiten", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Neuester Beitrag mehr als eine Woche alt", "collections.remove_account": "Dieses Konto entfernen", "collections.report_collection": "Sammlung melden", + "collections.revoke_collection_inclusion": "Mich aus dieser Sammlung entfernen", + "collections.revoke_inclusion.confirmation": "Du wurdest aus „{collection}“ entfernt", + "collections.revoke_inclusion.error": "Es ist ein Fehler aufgetreten. Bitte versuche es später erneut.", "collections.search_accounts_label": "Suche nach Konten, um sie hinzuzufügen …", "collections.search_accounts_max_reached": "Du hast die Höchstzahl an Konten hinzugefügt", "collections.sensitive": "Inhaltswarnung", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Follower entfernen", "confirmations.remove_from_followers.message": "{name} wird dir nicht länger folgen. Bist du dir sicher?", "confirmations.remove_from_followers.title": "Follower entfernen?", + "confirmations.revoke_collection_inclusion.confirm": "Mich entfernen", + "confirmations.revoke_collection_inclusion.message": "Diese Aktion kann nicht rückgängig gemacht werden. Es wird daher nicht möglich sein, dich zu dieser Sammlung erneut hinzuzufügen.", + "confirmations.revoke_collection_inclusion.title": "Möchtest du wirklich aus dieser Sammlung entfernt werden?", "confirmations.revoke_quote.confirm": "Zitat entfernen", "confirmations.revoke_quote.message": "Diese Aktion kann nicht rückgängig gemacht werden.", "confirmations.revoke_quote.title": "Mein Zitat aus diesem Beitrag entfernen?", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 4513944af21cbb..7ec926b496711b 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -231,7 +231,7 @@ "alt_text_modal.change_thumbnail": "Αλλαγή μικρογραφίας", "alt_text_modal.describe_for_people_with_hearing_impairments": "Περιέγραψε αυτό για άτομα με προβλήματα ακοής…", "alt_text_modal.describe_for_people_with_visual_impairments": "Περιέγραψε αυτό για άτομα με προβλήματα όρασης…", - "alt_text_modal.done": "Ολοκληρώθηκε", + "alt_text_modal.done": "Τέλος", "announcement.announcement": "Ανακοίνωση", "annual_report.announcement.action_build": "Φτιάξε το Wrapstodon μου", "annual_report.announcement.action_dismiss": "Όχι, ευχαριστώ", @@ -338,12 +338,14 @@ "collections.create_collection": "Δημιουργία συλλογής", "collections.delete_collection": "Διαγραφή συλλογής", "collections.description_length_hint": "Όριο 100 χαρακτήρων", + "collections.detail.accept_inclusion": "Εντάξει", "collections.detail.accounts_heading": "Λογαριασμοί", "collections.detail.author_added_you": "Ο/Η {author} σας πρόσθεσε σε αυτήν τη συλλογή", "collections.detail.curated_by_author": "Επιμέλεια από {author}", "collections.detail.curated_by_you": "Επιμέλεια από εσάς", "collections.detail.loading": "Γίνεται φόρτωση της συλλογής…", "collections.detail.other_accounts_in_collection": "Άλλοι σε αυτήν τη συλλογή:", + "collections.detail.revoke_inclusion": "Αφαίρεσε με", "collections.detail.sensitive_note": "Αυτή η συλλογή περιέχει λογαριασμούς και περιεχόμενο που μπορεί να είναι ευαίσθητα σε ορισμένους χρήστες.", "collections.detail.share": "Κοινοποιήστε αυτήν τη συλλογή", "collections.edit_details": "Επεξεργασία λεπτομερειών", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Τελευταία ανάρτηση πριν από μια εβδομάδα", "collections.remove_account": "Αφαίρεση λογαριασμού", "collections.report_collection": "Αναφορά αυτής της συλλογής", + "collections.revoke_collection_inclusion": "Αφαίρεσε τον εαυτό μου από αυτήν τη συλλογή", + "collections.revoke_inclusion.confirmation": "Έχεις αφαιρεθεί από τη συλλογή \"{collection}\"", + "collections.revoke_inclusion.error": "Υπήρξε ένα σφάλμα, παρακαλούμε προσπαθήστε ξανά αργότερα.", "collections.search_accounts_label": "Αναζήτηση λογαριασμών για προσθήκη…", "collections.search_accounts_max_reached": "Έχετε προσθέσει τον μέγιστο αριθμό λογαριασμών", "collections.sensitive": "Ευαίσθητο", @@ -405,7 +410,7 @@ "combobox.no_results_found": "Κανένα αποτέλεσμα για αυτήν την αναζήτηση", "combobox.open_results": "Άνοιγμα αποτελεσμάτων", "combobox.results_available": "{count, plural, one {# πρόταση διαθέσιμη} other {# προτάσεις διαθέσιμες}}. Χρησιμοποιήστε τα βελάκια πάνω και κάτω για να πλοηγηθείτε. Πατήστε Enter για να επιλέξετε.", - "community.column_settings.local_only": "Τοπικά μόνο", + "community.column_settings.local_only": "Τοπική μόνο", "community.column_settings.media_only": "Μόνο πολυμέσα", "community.column_settings.remote_only": "Απομακρυσμένα μόνο", "compose.error.blank_post": "Η ανάρτηση δεν μπορεί να είναι κενή.", @@ -462,7 +467,7 @@ "confirmations.logout.confirm": "Αποσύνδεση", "confirmations.logout.message": "Σίγουρα θέλεις να αποσυνδεθείς;", "confirmations.logout.title": "Αποσύνδεση;", - "confirmations.missing_alt_text.confirm": "Προσθήκη εναλ κειμένου", + "confirmations.missing_alt_text.confirm": "Προσθήκη εναλλακτικού κειμένου", "confirmations.missing_alt_text.message": "Η ανάρτησή σου περιέχει πολυμέσα χωρίς εναλλακτικό κείμενο. Η προσθήκη περιγραφών βοηθά να γίνει το περιεχόμενό σου προσβάσιμο σε περισσότερους ανθρώπους.", "confirmations.missing_alt_text.secondary": "Δημοσίευση όπως και να ΄χει", "confirmations.missing_alt_text.title": "Προσθήκη εναλλακτικού κειμένου;", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Αφαίρεση ακολούθου", "confirmations.remove_from_followers.message": "Ο χρήστης {name} θα σταματήσει να σε ακολουθεί. Σίγουρα θες να συνεχίσεις;", "confirmations.remove_from_followers.title": "Αφαίρεση ακολούθου;", + "confirmations.revoke_collection_inclusion.confirm": "Αφαίρεσε με", + "confirmations.revoke_collection_inclusion.message": "Αυτή η ενέργεια είναι μόνιμη, και ο επιμελητής δεν θα μπορεί να σε προσθέσει ξανά στη συλλογή αργότερα.", + "confirmations.revoke_collection_inclusion.title": "Αφαίρεσε τον εαυτό σου από αυτήν τη συλλογή;", "confirmations.revoke_quote.confirm": "Αφαίρεση ανάρτησης", "confirmations.revoke_quote.message": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", "confirmations.revoke_quote.title": "Αφαίρεση ανάρτησης;", @@ -541,7 +549,7 @@ "emoji_button.custom": "Προσαρμοσμένα", "emoji_button.flags": "Σημαίες", "emoji_button.food": "Φαγητά & Ποτά", - "emoji_button.label": "Εισάγετε emoji", + "emoji_button.label": "Εισαγωγή emoji", "emoji_button.nature": "Φύση", "emoji_button.not_found": "Δε βρέθηκε αντιστοίχιση εμότζι", "emoji_button.objects": "Αντικείμενα", @@ -697,7 +705,7 @@ "ignore_notifications_modal.not_following_title": "Αγνόηση ειδοποιήσεων από άτομα που δεν ακολουθείς;", "ignore_notifications_modal.private_mentions_title": "Αγνόηση ειδοποιήσεων από μη ζητηθείσες ιδιωτικές επισημάνσεις;", "info_button.label": "Βοήθεια", - "info_button.what_is_alt_text": "Το εναλλακτικό κείμενο παρέχει περιγραφές εικόνας για άτομα με προβλήματα όρασης, διαδικτυακές συνδέσεις χαμηλής ταχύτητας ή για άτομα που αναζητούν επιπλέον περιεχόμενο.\\n\\nΜπορείς να βελτιώσεις την προσβασιμότητα και την κατανόηση για όλους, γράφοντας σαφές, συνοπτικό και αντικειμενικό εναλλακτικό κείμενο.\\n\\n
  • Κατέγραψε σημαντικά στοιχεία
  • \\n
  • Συνόψισε το κείμενο στις εικόνες
  • \\n
  • Χρησιμοποίησε δομή κανονικής πρότασης
  • \\n
  • Απέφυγε περιττές πληροφορίες
  • \\n
  • Εστίασε στις τάσεις και τα βασικά ευρήματα σε σύνθετα οπτικά στοιχεία (όπως διαγράμματα ή χάρτες)
", + "info_button.what_is_alt_text": "

Τι είναι το εναλλακτικό κείμενο;

Το εναλλακτικό κείμενο (alt text) παρέχει περιγραφές εικόνας για άτομα με προβλήματα όρασης, διαδικτυακές συνδέσεις χαμηλής ταχύτητας ή για άτομα που αναζητούν επιπλέον περιεχόμενο.

Μπορείς να βελτιώσεις την προσβασιμότητα και την κατανόηση για όλους, γράφοντας σαφές, συνοπτικό και αντικειμενικό εναλλακτικό κείμενο.

  • Κατέγραψε σημαντικά στοιχεία
  • Συνόψισε το κείμενο στις εικόνες
  • Χρησιμοποίησε δομή κανονικής πρότασης
  • Απέφυγε περιττές πληροφορίες
  • Εστίασε στις τάσεις και τα βασικά ευρήματα σε σύνθετα οπτικά στοιχεία (όπως διαγράμματα ή χάρτες)
", "interaction_modal.action": "Για να αλληλεπιδράσετε με την ανάρτηση του/της {name}, πρέπει να συνδεθείτε στον λογαριασμό σας σε οποιονδήποτε διακομιστή Mastodon χρησιμοποιείτε.", "interaction_modal.go": "Πάμε", "interaction_modal.no_account_yet": "Δεν έχεις ακόμη λογαριασμό;", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 5735cd7c4c79cd..4bd474bc16979a 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -338,12 +338,14 @@ "collections.create_collection": "Create collection", "collections.delete_collection": "Delete collection", "collections.description_length_hint": "100 characters limit", + "collections.detail.accept_inclusion": "OK", "collections.detail.accounts_heading": "Accounts", "collections.detail.author_added_you": "{author} added you to this collection", "collections.detail.curated_by_author": "Curated by {author}", "collections.detail.curated_by_you": "Curated by you", "collections.detail.loading": "Loading collection…", "collections.detail.other_accounts_in_collection": "Others in this collection:", + "collections.detail.revoke_inclusion": "Remove me", "collections.detail.sensitive_note": "This collection contains accounts and content that may be sensitive to some users.", "collections.detail.share": "Share this collection", "collections.edit_details": "Edit details", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Last posted over a week ago", "collections.remove_account": "Remove this account", "collections.report_collection": "Report this collection", + "collections.revoke_collection_inclusion": "Remove myself from this collection", + "collections.revoke_inclusion.confirmation": "You've been removed from \"{collection}\"", + "collections.revoke_inclusion.error": "There was an error, please try again later.", "collections.search_accounts_label": "Search for accounts to add…", "collections.search_accounts_max_reached": "You have added the maximum number of accounts", "collections.sensitive": "Sensitive", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Remove follower", "confirmations.remove_from_followers.message": "{name} will stop following you. Are you sure you want to proceed?", "confirmations.remove_from_followers.title": "Remove follower?", + "confirmations.revoke_collection_inclusion.confirm": "Remove me", + "confirmations.revoke_collection_inclusion.message": "This action is permanent, and the curator won't be able to re-add you to the collection later on.", + "confirmations.revoke_collection_inclusion.title": "Remove yourself from this collection?", "confirmations.revoke_quote.confirm": "Remove post", "confirmations.revoke_quote.message": "This action cannot be undone.", "confirmations.revoke_quote.title": "Remove post?", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 4015a79eccb2f4..4473576b81cce3 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -338,12 +338,14 @@ "collections.create_collection": "Crear colección", "collections.delete_collection": "Eliminar colección", "collections.description_length_hint": "Límite de 100 caracteres", + "collections.detail.accept_inclusion": "Aceptar", "collections.detail.accounts_heading": "Cuentas", "collections.detail.author_added_you": "{author} te agregó a esta colección", "collections.detail.curated_by_author": "Curado por {author}", "collections.detail.curated_by_you": "Curado por vos", "collections.detail.loading": "Cargando colección…", "collections.detail.other_accounts_in_collection": "Otras cuentas en esta colección:", + "collections.detail.revoke_inclusion": "Quitarme", "collections.detail.sensitive_note": "Esta colección contiene cuentas y contenido que pueden ser sensibles a algunos usuarios.", "collections.detail.share": "Compartir esta colección", "collections.edit_details": "Editar detalles", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Último mensaje hace más de una semana", "collections.remove_account": "Eliminar esta cuenta", "collections.report_collection": "Denunciar esta colección", + "collections.revoke_collection_inclusion": "Quitarme de esta colección", + "collections.revoke_inclusion.confirmation": "Saliste de «{collection}»", + "collections.revoke_inclusion.error": "Hubo un error; por favor, intentalo de nuevo más tarde.", "collections.search_accounts_label": "Buscar cuentas para agregar…", "collections.search_accounts_max_reached": "Agregaste el número máximo de cuentas", "collections.sensitive": "Sensible", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Quitar seguidor", "confirmations.remove_from_followers.message": "{name} dejará de seguirte. ¿Estás seguro de que querés continuar?", "confirmations.remove_from_followers.title": "¿Quitar seguidor?", + "confirmations.revoke_collection_inclusion.confirm": "Quitarme", + "confirmations.revoke_collection_inclusion.message": "Esta acción es permanente, y el autor no podrá volver a agregarte a la colección más adelante.", + "confirmations.revoke_collection_inclusion.title": "¿Querés quitarte de esta colección?", "confirmations.revoke_quote.confirm": "Eliminar mensaje", "confirmations.revoke_quote.message": "Esta acción no se puede deshacer.", "confirmations.revoke_quote.title": "¿Eliminar mensaje?", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 1d1d89569f4380..2259fc08b43ca2 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -338,12 +338,14 @@ "collections.create_collection": "Crear colección", "collections.delete_collection": "Eliminar colección", "collections.description_length_hint": "Limitado a 100 caracteres", + "collections.detail.accept_inclusion": "Aceptar", "collections.detail.accounts_heading": "Cuentas", "collections.detail.author_added_you": "{author} te ha añadido a esta colección", "collections.detail.curated_by_author": "Seleccionado por {author}", "collections.detail.curated_by_you": "Seleccionado por ti", "collections.detail.loading": "Cargando colección…", "collections.detail.other_accounts_in_collection": "Otros en esta colección:", + "collections.detail.revoke_inclusion": "Excluirme", "collections.detail.sensitive_note": "Esta colección contiene cuentas y contenido que pueden resultar sensibles para algunos usuarios.", "collections.detail.share": "Compartir esta colección", "collections.edit_details": "Editar detalles", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Última publicación hace más de una semana", "collections.remove_account": "Eliminar esta cuenta", "collections.report_collection": "Reportar esta colección", + "collections.revoke_collection_inclusion": "Excluirme de esta colección", + "collections.revoke_inclusion.confirmation": "Has sido excluido de \"{collection}\"", + "collections.revoke_inclusion.error": "Se ha producido un error, por favor, inténtalo de nuevo más tarde.", "collections.search_accounts_label": "Buscar cuentas para añadir…", "collections.search_accounts_max_reached": "Has añadido el número máximo de cuentas", "collections.sensitive": "Sensible", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Eliminar seguidor", "confirmations.remove_from_followers.message": "{name} dejará de seguirte. ¿Estás seguro de que quieres continuar?", "confirmations.remove_from_followers.title": "¿Eliminar seguidor?", + "confirmations.revoke_collection_inclusion.confirm": "Excluirme", + "confirmations.revoke_collection_inclusion.message": "Esta acción es permanente y el autor no podrá volver a agregarte a la colección más adelante.", + "confirmations.revoke_collection_inclusion.title": "¿Deseas excluirte de esta colección?", "confirmations.revoke_quote.confirm": "Eliminar publicación", "confirmations.revoke_quote.message": "Esta acción no se puede deshacer.", "confirmations.revoke_quote.title": "¿Deseas eliminar la publicación?", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 2982ab4a4352c7..a63428c453169b 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -338,12 +338,14 @@ "collections.create_collection": "Crear colección", "collections.delete_collection": "Eliminar colección", "collections.description_length_hint": "Limitado a 100 caracteres", + "collections.detail.accept_inclusion": "Aceptar", "collections.detail.accounts_heading": "Cuentas", "collections.detail.author_added_you": "{author} te añadió a esta colección", "collections.detail.curated_by_author": "Seleccionado por {author}", "collections.detail.curated_by_you": "Seleccionado por ti", "collections.detail.loading": "Cargando colección…", "collections.detail.other_accounts_in_collection": "Otros en esta colección:", + "collections.detail.revoke_inclusion": "Eliminar", "collections.detail.sensitive_note": "Esta colección contiene cuentas y contenido que puede ser sensible para algunos usuarios.", "collections.detail.share": "Compartir esta colección", "collections.edit_details": "Editar detalles", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Última publicación hace más de una semana", "collections.remove_account": "Quitar esta cuenta", "collections.report_collection": "Informar de esta colección", + "collections.revoke_collection_inclusion": "Eliminarme de esta colección", + "collections.revoke_inclusion.confirmation": "Ha sido eliminado de\"{collection}\"", + "collections.revoke_inclusion.error": "Se ha producido un error. Inténtelo de nuevo más tarde.", "collections.search_accounts_label": "Buscar cuentas para añadir…", "collections.search_accounts_max_reached": "Has añadido el número máximo de cuentas", "collections.sensitive": "Sensible", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Eliminar seguidor", "confirmations.remove_from_followers.message": "{name} dejará de seguirte. ¿Estás seguro de que quieres continuar?", "confirmations.remove_from_followers.title": "¿Eliminar seguidor?", + "confirmations.revoke_collection_inclusion.confirm": "Eliminar", + "confirmations.revoke_collection_inclusion.message": "Esta acción es permanente, y el curador no podrá volver a añadirle a la colección más adelante.", + "confirmations.revoke_collection_inclusion.title": "¿Eliminarse de esta colección?", "confirmations.revoke_quote.confirm": "Eliminar publicación", "confirmations.revoke_quote.message": "Esta acción no tiene vuelta atrás.", "confirmations.revoke_quote.title": "¿Eliminar la publicación?", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 9fae80fe2a285f..2d84047f134505 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Muokkaa profiilia", "account_edit.custom_fields.name": "kenttä", "account_edit.custom_fields.placeholder": "Lisää pronominisi, ulkoisia linkkejä tai mitä tahansa muuta, jonka haluat jakaa.", + "account_edit.custom_fields.reorder_button": "Järjestele kenttiä", "account_edit.custom_fields.tip_content": "Voit helposti lisätä Mastodon-tilisi uskottavuutta vahvistamalla mihin tahansa omistamaasi verkkosivustoon ohjaavat linkit.", "account_edit.custom_fields.tip_title": "Vinkki: Vahvistettujen linkkien lisääminen", "account_edit.custom_fields.title": "Mukautetut kentät", @@ -162,15 +163,26 @@ "account_edit.featured_hashtags.item": "aihetunnisteet", "account_edit.featured_hashtags.placeholder": "Auta muita tunnistamaan suosikkiaiheesi ja saamaan nopea pääsy niihin.", "account_edit.featured_hashtags.title": "Esiteltävät aihetunnisteet", - "account_edit.field_delete_modal.confirm": "Haluatko varmasti poistaa tämän mukautetun kentän? Tätä toimintoa ei voi peruuttaa.", + "account_edit.field_delete_modal.confirm": "Haluatko varmasti poistaa tämän mukautetun kentän? Tätä toimea ei voi peruuttaa.", "account_edit.field_delete_modal.delete_button": "Poista", "account_edit.field_delete_modal.title": "Poistetaanko mukautettu kenttä?", "account_edit.field_edit_modal.add_title": "Lisää mukautettu kenttä", "account_edit.field_edit_modal.edit_title": "Muokkaa mukautettua kenttää", "account_edit.field_edit_modal.limit_header": "Suositeltu merkkiraja ylitetty", + "account_edit.field_edit_modal.limit_message": "Mobiilikäyttäjät eivät välttämättä näe kenttää kokonaan.", + "account_edit.field_edit_modal.link_emoji_warning": "Emme suosittele käyttämään mukautettuja emojeita URL-osoitteiden kanssa. Molempia sisältävät mukautetut kentät näkyvät vain tekstinä linkin sijaan, jotta estetään käyttäjien sekaannus.", "account_edit.field_edit_modal.name_hint": "Esim. ”Henkilökohtainen verkkosivusto”", "account_edit.field_edit_modal.name_label": "Nimike", + "account_edit.field_edit_modal.value_hint": "Esim. ”example.me”", "account_edit.field_edit_modal.value_label": "Arvo", + "account_edit.field_reorder_modal.drag_cancel": "Veto peruttu. Kenttää ”{item}” ei siirretty.", + "account_edit.field_reorder_modal.drag_end": "Kenttä ”{item}” pudotettu.", + "account_edit.field_reorder_modal.drag_instructions": "Siirrä mukautettuja kenttiä painamalla välilyöntiä tai Enter-näppäintä. Raahatessa siirrä kenttää ylös tai alas nuolinäppäimillä. Pudota kenttä uuteen kohtaansa painamalla uudelleen välilyöntiä tai Enter-näppäintä, tai peru painamalla Escape-näppäintä.", + "account_edit.field_reorder_modal.drag_move": "Kenttä ”{item}” siirretty.", + "account_edit.field_reorder_modal.drag_over": "Kenttä ”{item}” siirretty kentän ”{over}” päälle.", + "account_edit.field_reorder_modal.drag_start": "Valittu kenttä ”{item}”.", + "account_edit.field_reorder_modal.handle_label": "Siirrä kenttää ”{item}”", + "account_edit.field_reorder_modal.title": "Järjestele kenttiä", "account_edit.name_modal.add_title": "Lisää näyttönimi", "account_edit.name_modal.edit_title": "Muokkaa näyttönimeä", "account_edit.profile_tab.button_label": "Mukauta", @@ -186,7 +198,12 @@ "account_edit.profile_tab.title": "Profiilin välilehtien asetukset", "account_edit.save": "Tallenna", "account_edit.verified_modal.details": "Lisää Mastodon-profiiliisi uskottavuutta vahvistamalla linkit henkilökohtaisiin verkkosivustoihin. Näin se toimii:", + "account_edit.verified_modal.invisible_link.details": "Lisää linkki HTML:n head-osaan. Tärkeä kohta on rel=\"me\", joka estää toiseksi tekeytymisen sivustoilla, joilla on käyttäjien luomaa sisältöä. Voit jopa käyttää link-tunnistetta sivun head-osassa {tag}-tunnisteen sijaan, mutta HTML:n tulee olla saatavilla suorittamatta JavaScriptia.", + "account_edit.verified_modal.invisible_link.summary": "Miten teen linkistä näkymättömän?", + "account_edit.verified_modal.step1.header": "Kopioi alla oleva HTML-koodi ja liitä se verkkosivustosi head-osaan", + "account_edit.verified_modal.step2.details": "Jos olet jo lisännyt verkkosivustosi mukautettuna kenttänä, sinun tulee poistaa ja lisätä se uudelleen, jotta voit käynnistää vahvistuksen.", "account_edit.verified_modal.step2.header": "Lisää verkkosivustosi mukautettuna kenttänä", + "account_edit.verified_modal.title": "Miten lisätä vahvistettu linkki", "account_edit_tags.add_tag": "Lisää #{tagName}", "account_edit_tags.column_title": "Muokkaa esiteltäviä aihetunnisteita", "account_edit_tags.help_text": "Esiteltävät aihetunnisteet auttavat käyttäjiä löytämään profiilisi ja olemaan vuorovaikutuksessa sen kanssa. Ne näkyvät suodattimina profiilisivusi Toiminta-näkymässä.", @@ -321,12 +338,14 @@ "collections.create_collection": "Luo kokoelma", "collections.delete_collection": "Poista kokoelma", "collections.description_length_hint": "100 merkin rajoitus", + "collections.detail.accept_inclusion": "Selvä", "collections.detail.accounts_heading": "Tilit", "collections.detail.author_added_you": "{author} lisäsi sinut tähän kokoelmaan", "collections.detail.curated_by_author": "Koonnut {author}", "collections.detail.curated_by_you": "Itse kokoamasi", "collections.detail.loading": "Ladataan kokoelmaa…", "collections.detail.other_accounts_in_collection": "Muut tässä kokoelmassa:", + "collections.detail.revoke_inclusion": "Poista minut", "collections.detail.sensitive_note": "Tämä kokoelma sisältää tilejä ja sisältöä, jotka saattavat olla arkaluonteisia joillekin käyttäjille.", "collections.detail.share": "Jaa tämä kokoelma", "collections.edit_details": "Muokkaa tietoja", @@ -342,6 +361,9 @@ "collections.old_last_post_note": "Julkaissut viimeksi yli viikko sitten", "collections.remove_account": "Poista tämä tili", "collections.report_collection": "Raportoi tämä kokoelma", + "collections.revoke_collection_inclusion": "Poista itseni tästä kokoelmasta", + "collections.revoke_inclusion.confirmation": "Sinut on poistettu kokoelmasta ”{collection}”", + "collections.revoke_inclusion.error": "Tapahtui virhe – yritä myöhemmin uudelleen.", "collections.search_accounts_label": "Hae lisättäviä tilejä…", "collections.search_accounts_max_reached": "Olet lisännyt enimmäismäärän tilejä", "collections.sensitive": "Arkaluonteinen", @@ -465,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Poista seuraaja", "confirmations.remove_from_followers.message": "{name} lakkaa seuraamasta sinua. Haluatko varmasti jatkaa?", "confirmations.remove_from_followers.title": "Poistetaanko seuraaja?", + "confirmations.revoke_collection_inclusion.confirm": "Poista minut", + "confirmations.revoke_collection_inclusion.message": "Tämä toimi on pysyvä, eikä kokoaja pysty lisäämään sinua kokoelmaan myöhemmin uudelleen.", + "confirmations.revoke_collection_inclusion.title": "Poistetaanko itsesi tästä kokoelmasta?", "confirmations.revoke_quote.confirm": "Poista julkaisu", "confirmations.revoke_quote.message": "Tätä toimea ei voi peruuttaa.", "confirmations.revoke_quote.title": "Poistetaanko julkaisu?", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index d0472365e77e74..48139071456bad 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -338,12 +338,14 @@ "collections.create_collection": "Cruthaigh bailiúchán", "collections.delete_collection": "Scrios bailiúchán", "collections.description_length_hint": "Teorainn 100 carachtar", + "collections.detail.accept_inclusion": "Ceart go leor", "collections.detail.accounts_heading": "Cuntais", "collections.detail.author_added_you": "Chuir {author} leis an mbailiúchán seo thú", "collections.detail.curated_by_author": "Curtha i dtoll a chéile ag {author}", "collections.detail.curated_by_you": "Curtha i dtoll a chéile agatsa", "collections.detail.loading": "Ag lódáil an bhailiúcháin…", "collections.detail.other_accounts_in_collection": "Daoine eile sa bhailiúchán seo:", + "collections.detail.revoke_inclusion": "Bain mé", "collections.detail.sensitive_note": "Tá cuntais agus ábhar sa bhailiúchán seo a d'fhéadfadh a bheith íogair do roinnt úsáideoirí.", "collections.detail.share": "Comhroinn an bailiúchán seo", "collections.edit_details": "Cuir sonraí in eagar", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Postáilte go deireanach breis agus seachtain ó shin", "collections.remove_account": "Bain an cuntas seo", "collections.report_collection": "Tuairiscigh an bailiúchán seo", + "collections.revoke_collection_inclusion": "Bain mé féin as an mbailiúchán seo", + "collections.revoke_inclusion.confirmation": "Baineadh as \"{collection}\" thú", + "collections.revoke_inclusion.error": "Tharla earráid, déan iarracht arís ar ball.", "collections.search_accounts_label": "Cuardaigh cuntais le cur leis…", "collections.search_accounts_max_reached": "Tá an líon uasta cuntas curtha leis agat", "collections.sensitive": "Íogair", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Bain leantóir", "confirmations.remove_from_followers.message": "Scoirfidh {name} de bheith ag leanúint leat. An bhfuil tú cinnte gur mian leat leanúint ar aghaidh?", "confirmations.remove_from_followers.title": "Bain an leantóir?", + "confirmations.revoke_collection_inclusion.confirm": "Bain mé", + "confirmations.revoke_collection_inclusion.message": "Is gníomh buan é seo, agus ní bheidh an coimeádaí in ann tú a chur leis an mbailiúchán arís níos déanaí.", + "confirmations.revoke_collection_inclusion.title": "Bain tú féin den bhailiúchán seo?", "confirmations.revoke_quote.confirm": "Bain postáil", "confirmations.revoke_quote.message": "Ní féidir an gníomh seo a chealú.", "confirmations.revoke_quote.title": "Bain postáil?", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 808bc1f41050dd..3d912253614656 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -338,12 +338,14 @@ "collections.create_collection": "Crear colección", "collections.delete_collection": "Eliminar colección", "collections.description_length_hint": "Límite de 100 caracteres", + "collections.detail.accept_inclusion": "Vale", "collections.detail.accounts_heading": "Contas", "collections.detail.author_added_you": "{author} engadíute a esta colección", "collections.detail.curated_by_author": "Seleccionadas por {author}", "collections.detail.curated_by_you": "Seleccionadas por ti", "collections.detail.loading": "Cargando colección…", "collections.detail.other_accounts_in_collection": "Outras contas na colección:", + "collections.detail.revoke_inclusion": "Non quero", "collections.detail.sensitive_note": "Esta colección presenta contas e contido que poderían ser sensibles para algunhas persoas.", "collections.detail.share": "Compartir esta colección", "collections.edit_details": "Editar detalles", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Hai máis dunha semana da última publicación", "collections.remove_account": "Retirar esta conta", "collections.report_collection": "Denunciar esta colección", + "collections.revoke_collection_inclusion": "Sácame desta colección", + "collections.revoke_inclusion.confirmation": "Quitámoste da colección \"{collection}\"", + "collections.revoke_inclusion.error": "Algo fallou, inténtao outra vez máis tarde.", "collections.search_accounts_label": "Buscar contas para engadir…", "collections.search_accounts_max_reached": "Acadaches o máximo de contas permitidas", "collections.sensitive": "Sensible", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Quitar seguidora", "confirmations.remove_from_followers.message": "{name} vai deixar de seguirte. É isto o que queres?", "confirmations.remove_from_followers.title": "Quitar seguidora?", + "confirmations.revoke_collection_inclusion.confirm": "Quítame", + "confirmations.revoke_collection_inclusion.message": "A acción é definitiva, a creadora da colección non poderá volver a engadirte máis adiante.", + "confirmations.revoke_collection_inclusion.title": "Queres que non te inclúan nesta colección?", "confirmations.revoke_quote.confirm": "Eliminar publicación", "confirmations.revoke_quote.message": "Esta acción non se pode desfacer.", "confirmations.revoke_quote.title": "Eliminar publicación?", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index cb8a9dae0f9bc2..66fd6f710b9712 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -338,12 +338,14 @@ "collections.create_collection": "יצירת אוסף", "collections.delete_collection": "מחיקת האוסף", "collections.description_length_hint": "מגבלה של 100 תווים", + "collections.detail.accept_inclusion": "אישור", "collections.detail.accounts_heading": "חשבונות", "collections.detail.author_added_you": "{author} הוסיפו אותך לאוסף", "collections.detail.curated_by_author": "נאצר על ידי {author}", "collections.detail.curated_by_you": "נאצר על ידיך", "collections.detail.loading": "טוען אוסף…", "collections.detail.other_accounts_in_collection": "אחרים באוסף:", + "collections.detail.revoke_inclusion": "הסירוני", "collections.detail.sensitive_note": "האוסף מכיל חשבונות ותכנים שאולי יחשבו רגישים לחלק מהמשתמשים.", "collections.detail.share": "שיתוף אוסף", "collections.edit_details": "עריכת פרטים", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "פרסמו לאחרונה לפני יותר משבוע", "collections.remove_account": "הסר חשבון זה", "collections.report_collection": "דיווח על אוסף זה", + "collections.revoke_collection_inclusion": "הסירוני מאוסף זה", + "collections.revoke_inclusion.confirmation": "הוסרת מֿ\"{collection}\"", + "collections.revoke_inclusion.error": "הייתה שגיאה. נסו שוב מאוחר יותר.", "collections.search_accounts_label": "לחפש חשבונות להוספה…", "collections.search_accounts_max_reached": "הגעת למספר החשבונות המירבי", "collections.sensitive": "רגיש", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "הסרת עוקב", "confirmations.remove_from_followers.message": "{name} יוסר/תוסר ממעקב אחריך. האם להמשיך?", "confirmations.remove_from_followers.title": "להסיר עוקב/עוקבת?", + "confirmations.revoke_collection_inclusion.confirm": "הסירוני", + "confirmations.revoke_collection_inclusion.message": "פעולה זו היא סופית, והאוצרים לא יוכלו להוסיף אותך יותר לאוסף בעתיד.", + "confirmations.revoke_collection_inclusion.title": "להסירך מאוסף זה?", "confirmations.revoke_quote.confirm": "הסרת הודעה", "confirmations.revoke_quote.message": "פעולה זו אינה הפיכה.", "confirmations.revoke_quote.title": "הסרת הודעה?", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 05df7cc4021f2c..665cd22df80d5e 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -153,20 +153,33 @@ "account_edit.column_title": "Profil szerkesztése", "account_edit.custom_fields.name": "mező", "account_edit.custom_fields.placeholder": "Add meg a névmásaidat, külső hivatkozásaidat vagy bármi mást, amelyet megosztanál.", + "account_edit.custom_fields.reorder_button": "Mezők átrendezése", + "account_edit.custom_fields.tip_content": "Könnyedén nagyobb hitelességet adhatsz a Mastodon-fiókodnak a saját weboldalaidra mutató hivatkozások megerősítésével.", + "account_edit.custom_fields.tip_title": "Tipp: Ellenőrzött hivatkozások hozzáadása", "account_edit.custom_fields.title": "Egyéni mezők", + "account_edit.custom_fields.verified_hint": "Hogyan kell ellenőrzött hivatkozást hozzáadni?", "account_edit.display_name.placeholder": "A megjelenítendő név az, ahogy a neved megjelenik a profilodon és az idővonalakon.", "account_edit.display_name.title": "Megjelenítendő név", "account_edit.featured_hashtags.item": "hashtagek", "account_edit.featured_hashtags.placeholder": "Segíts másoknak, hogy azonosíthassák a kedvenc témáid, és gyorsan elérjék azokat.", "account_edit.featured_hashtags.title": "Kiemelt hashtagek", + "account_edit.field_delete_modal.confirm": "Biztos, hogy töröld ezt az egyéni mezőt? Ez a művelet nem vonható vissza.", "account_edit.field_delete_modal.delete_button": "Törlés", "account_edit.field_delete_modal.title": "Egyéni mező törlése?", "account_edit.field_edit_modal.add_title": "Egyéni mező hozzáadása", "account_edit.field_edit_modal.edit_title": "Egyéni mező szerkesztése", + "account_edit.field_edit_modal.limit_header": "Az ajánlott karakterkorlát túllépve", + "account_edit.field_edit_modal.limit_message": "A mobilos felhasználók lehet, hogy nem fogják a teljes mezőt látni.", + "account_edit.field_edit_modal.link_emoji_warning": "Nem javasoljuk az egyéni emodzsik és webcímek együttes használatát. A mindkettőt tartalmazó egyéni mezők a felhasználók megzavarásának elkerülése érdekében csak szövegként jelennek meg, nem hivatkozásként.", "account_edit.field_edit_modal.name_hint": "Például „Személyes webhely”", "account_edit.field_edit_modal.name_label": "Címke", "account_edit.field_edit_modal.value_hint": "Például „example.me”", "account_edit.field_edit_modal.value_label": "Érték", + "account_edit.field_reorder_modal.drag_cancel": "Az áthúzás megszakítva. A(z) „{item}” mező el lett dobva.", + "account_edit.field_reorder_modal.drag_end": "A(z) „{item}” mező el lett dobva.", + "account_edit.field_reorder_modal.drag_move": "A(z) „{item}” mező át lett helyezve.", + "account_edit.field_reorder_modal.handle_label": "A(z) „{item}” mező húzása", + "account_edit.field_reorder_modal.title": "Mezők átrendezése", "account_edit.name_modal.add_title": "Megjelenítendő név hozzáadása", "account_edit.name_modal.edit_title": "Megjelenítendő név szerkesztése", "account_edit.profile_tab.button_label": "Testreszabás", @@ -181,6 +194,9 @@ "account_edit.profile_tab.subtitle": "Szabd testre a profilodon látható lapokat, és a megjelenített tartalmukat.", "account_edit.profile_tab.title": "Profil lap beállításai", "account_edit.save": "Mentés", + "account_edit.verified_modal.invisible_link.summary": "Hogyan lehet egy hivatkozás láthatatlanná tenni?", + "account_edit.verified_modal.step1.header": "Másold a lenti HTML-kódot és illeszd be a webhelyed fejlécébe", + "account_edit.verified_modal.step2.details": "Ha már egyéni mezőként hozzáadtad a webhelyedet, akkor törölnöd kell, újból hozzá kell adnod, hogy újra ellenőrizve legyen.", "account_edit.verified_modal.step2.header": "Saját webhely hozzáadása egyéni mezőként", "account_edit.verified_modal.title": "Hogyan kell ellenőrzött hivatkozást hozzáadni", "account_edit_tags.add_tag": "#{tagName} hozzáadása", @@ -317,12 +333,14 @@ "collections.create_collection": "Gyűjtemény létrehozása", "collections.delete_collection": "Gyűjtemény törlése", "collections.description_length_hint": "100 karakteres korlát", + "collections.detail.accept_inclusion": "Rendben", "collections.detail.accounts_heading": "Fiókok", "collections.detail.author_added_you": "{author} hozzáadott ehhez a gyűjteményhez", "collections.detail.curated_by_author": "Válogatta: {author}", "collections.detail.curated_by_you": "Te válogattad", "collections.detail.loading": "Gyűjtemény betöltése…", "collections.detail.other_accounts_in_collection": "Mások ebben a gyűjteményben:", + "collections.detail.revoke_inclusion": "Saját magam eltávolítása", "collections.detail.sensitive_note": "Ebben a gyűjteményben egyesek számára érzékeny fiókok és tartalmak vannak.", "collections.detail.share": "Gyűjtemény megosztása", "collections.edit_details": "Részletek szerkesztése", @@ -338,6 +356,9 @@ "collections.old_last_post_note": "Egy hete osztott meg legutóbb", "collections.remove_account": "Fiók eltávolítása", "collections.report_collection": "Gyűjtemény jelentése", + "collections.revoke_collection_inclusion": "Saját magam eltávolítása ebből a gyűjteményből", + "collections.revoke_inclusion.confirmation": "El lettél távolítva innen: „{collection}”", + "collections.revoke_inclusion.error": "Hiba történt, próbáld újra később.", "collections.search_accounts_label": "Hozzáadandó fiókok keresése…", "collections.search_accounts_max_reached": "Elérte a hozzáadott fiókok maximális számát", "collections.sensitive": "Érzékeny", @@ -461,6 +482,9 @@ "confirmations.remove_from_followers.confirm": "Követő eltávolítása", "confirmations.remove_from_followers.message": "{name} követ téged. Biztos, hogy folytatod?", "confirmations.remove_from_followers.title": "Követő eltávolítása?", + "confirmations.revoke_collection_inclusion.confirm": "Saját magam eltávolítása", + "confirmations.revoke_collection_inclusion.message": "Ez a művelet végleges, és a kurátor nem fog tudni újra hozzáadni ehhez a gyűjteményhez.", + "confirmations.revoke_collection_inclusion.title": "Eltávolítod magadat ebből a gyűjteményből?", "confirmations.revoke_quote.confirm": "Bejegyzés eltávolítása", "confirmations.revoke_quote.message": "Ez a művelet nem vonható vissza.", "confirmations.revoke_quote.title": "Bejegyzés eltávolítása?", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 117cdb82810841..706e9f486526b6 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -338,12 +338,14 @@ "collections.create_collection": "Búa til safn", "collections.delete_collection": "Eyða safni", "collections.description_length_hint": "100 stafa takmörk", + "collections.detail.accept_inclusion": "Í lagi", "collections.detail.accounts_heading": "Aðgangar", "collections.detail.author_added_you": "{author} bætti þér í þetta safn", "collections.detail.curated_by_author": "Safnað saman af {author}", "collections.detail.curated_by_you": "Safnað saman af þér", "collections.detail.loading": "Hleð inn safni…", "collections.detail.other_accounts_in_collection": "Aðrir í þessu safni:", + "collections.detail.revoke_inclusion": "Fjarlægja mig", "collections.detail.sensitive_note": "Þetta safn inniheldur aðganga og efni sem sumir notendur gætu verið viðkvæmir fyrir.", "collections.detail.share": "Deila þessu safni", "collections.edit_details": "Breyta ítarupplýsingum", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Birti síðast fyrir meira en viku síðan", "collections.remove_account": "Fjarlægja þennan aðgang", "collections.report_collection": "Kæra þetta safn", + "collections.revoke_collection_inclusion": "Fjarlægja mig úr þessu safni", + "collections.revoke_inclusion.confirmation": "Þú varst fjarlægð/ur úr \"{collection}\"", + "collections.revoke_inclusion.error": "Upp kom villa, reyndu aftur síðar.", "collections.search_accounts_label": "Leita að aðgöngum til að bæta við…", "collections.search_accounts_max_reached": "Þú hefur þegar bætt við leyfilegum hámarksfjölda aðganga", "collections.sensitive": "Viðkvæmt", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Fjarlægja fylgjanda", "confirmations.remove_from_followers.message": "{name} mun hætta að fylgjast með þér. Ertu viss um að þú viljir halda áfram?", "confirmations.remove_from_followers.title": "Fjarlægja fylgjanda?", + "confirmations.revoke_collection_inclusion.confirm": "Fjarlægja mig", + "confirmations.revoke_collection_inclusion.message": "Þessi aðgerð er varanleg og umsjónaraðili safnsins mun ekki geta bætt þér aftur við síðar.", + "confirmations.revoke_collection_inclusion.title": "Á að fjarlægja þig úr þessu safni?", "confirmations.revoke_quote.confirm": "Fjarlægja færslu", "confirmations.revoke_quote.message": "Þessa aðgerð er ekki hægt að afturkalla.", "confirmations.revoke_quote.title": "Fjarlægja færslu?", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 29aacb056126b7..9ae862aeddd165 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -338,12 +338,14 @@ "collections.create_collection": "Crea la collezione", "collections.delete_collection": "Cancella la collezione", "collections.description_length_hint": "Limite di 100 caratteri", + "collections.detail.accept_inclusion": "Va bene", "collections.detail.accounts_heading": "Account", "collections.detail.author_added_you": "{author} ti ha aggiunto a questa collezione", "collections.detail.curated_by_author": "Curata da {author}", "collections.detail.curated_by_you": "Curata da te", "collections.detail.loading": "Caricamento della collezione…", "collections.detail.other_accounts_in_collection": "Altri in questa collezione:", + "collections.detail.revoke_inclusion": "Rimuovimi", "collections.detail.sensitive_note": "Questa collezione contiene account e contenuto che potrebbero essere sensibili ad alcuni utenti.", "collections.detail.share": "Condividi questa collezione", "collections.edit_details": "Modifica i dettagli", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Ultimo post più di una settimana fa", "collections.remove_account": "Rimuovi questo account", "collections.report_collection": "Segnala questa collezione", + "collections.revoke_collection_inclusion": "Rimuovimi da questa collezione", + "collections.revoke_inclusion.confirmation": "Sei stato/a rimosso/a da \"{collection}\"", + "collections.revoke_inclusion.error": "Si è verificato un errore, si prega di riprovare più tardi.", "collections.search_accounts_label": "Cerca account da aggiungere…", "collections.search_accounts_max_reached": "Hai aggiunto il numero massimo di account", "collections.sensitive": "Sensibile", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Rimuovi il follower", "confirmations.remove_from_followers.message": "{name} smetterà di seguirti. Si è sicuri di voler procedere?", "confirmations.remove_from_followers.title": "Rimuovere il follower?", + "confirmations.revoke_collection_inclusion.confirm": "Rimuovimi", + "confirmations.revoke_collection_inclusion.message": "Questa azione è permanente e l'utente responsabile della collezione non sarà in grado di aggiungerti nuovamente ad essa in seguito.", + "confirmations.revoke_collection_inclusion.title": "Rimuovere te stesso/a da questa collezione?", "confirmations.revoke_quote.confirm": "Elimina il post", "confirmations.revoke_quote.message": "Questa azione non può essere annullata.", "confirmations.revoke_quote.title": "Rimuovere il post?", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 54025d2f06b84a..df2191a094563f 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -151,13 +151,38 @@ "account_edit.button.edit": "{item} bewerken", "account_edit.column_button": "Klaar", "account_edit.column_title": "Profiel bewerken", + "account_edit.custom_fields.name": "veld", "account_edit.custom_fields.placeholder": "Voeg je voornaamwoorden, externe links of iets anders toe dat je wilt delen.", + "account_edit.custom_fields.reorder_button": "Velden opnieuw ordenen", + "account_edit.custom_fields.tip_content": "Je kunt gemakkelijk geloofwaardigheid toevoegen aan je Mastodon account door links te controleren naar websites die je bezit.", + "account_edit.custom_fields.tip_title": "Tip: Geverifieerde links toevoegen", "account_edit.custom_fields.title": "Extra velden", + "account_edit.custom_fields.verified_hint": "Hoe voeg ik een geverifieerde link toe?", "account_edit.display_name.placeholder": "Je weergavenaam wordt op jouw profiel en op tijdlijnen weergegeven.", "account_edit.display_name.title": "Weergavenaam", "account_edit.featured_hashtags.item": "hashtags", "account_edit.featured_hashtags.placeholder": "Geef anderen een overzicht van en snel toegang tot je favoriete onderwerpen.", "account_edit.featured_hashtags.title": "Uitgelichte hashtags", + "account_edit.field_delete_modal.confirm": "Weet je zeker dat je dit aangepaste veld wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.", + "account_edit.field_delete_modal.delete_button": "Verwijderen", + "account_edit.field_delete_modal.title": "Aangepast veld verwijderen?", + "account_edit.field_edit_modal.add_title": "Aangepast veld toevoegen", + "account_edit.field_edit_modal.edit_title": "Aangepast veld bewerken", + "account_edit.field_edit_modal.limit_header": "Aanbevolen tekenlimiet overschreden", + "account_edit.field_edit_modal.limit_message": "Mobiele gebruikers zien mogelijk het veld niet volledig.", + "account_edit.field_edit_modal.link_emoji_warning": "We raden aan om aangepaste emoji in combinatie met urls te gebruiken. Aangepaste velden die beide bevatten worden alleen als tekst weergegeven in plaats van als een link, om verwarring van de gebruiker te voorkomen.", + "account_edit.field_edit_modal.name_hint": "Bijv. \"Persoonlijke website\"", + "account_edit.field_edit_modal.name_label": "Label", + "account_edit.field_edit_modal.value_hint": "Bijv. \"voorbeeld.me\"", + "account_edit.field_edit_modal.value_label": "Waarde", + "account_edit.field_reorder_modal.drag_cancel": "Slepen is geannuleerd. Veld \"{item}\" is weggevallen.", + "account_edit.field_reorder_modal.drag_end": "Veld \"{item}\" was weggevallen.", + "account_edit.field_reorder_modal.drag_instructions": "Druk op spatie of enter om aangepaste velden te herschikken. Gebruik de pijltjestoetsen om het veld omhoog of omlaag te verplaatsen. Druk opnieuw op spatie of enter om het veld in zijn nieuwe positie te laten vallen, of druk op escape om te annuleren.", + "account_edit.field_reorder_modal.drag_move": "Veld \"{item}\" is verplaatst.", + "account_edit.field_reorder_modal.drag_over": "Veld \"{item}\" is over \"{over} \" verplaatst.", + "account_edit.field_reorder_modal.drag_start": "Opgepakt veld \"{item}\".", + "account_edit.field_reorder_modal.handle_label": "Veld \"{item}\" slepen", + "account_edit.field_reorder_modal.title": "Velden herschikken", "account_edit.name_modal.add_title": "Weergavenaam toevoegen", "account_edit.name_modal.edit_title": "Weergavenaam bewerken", "account_edit.profile_tab.button_label": "Aanpassen", @@ -172,6 +197,13 @@ "account_edit.profile_tab.subtitle": "De tabbladen op je profiel aanpassen en wat er op wordt weergegeven.", "account_edit.profile_tab.title": "Instellingen voor tabblad Profiel", "account_edit.save": "Opslaan", + "account_edit.verified_modal.details": "Voeg geloofwaardigheid toe aan je Mastodonprofiel door links naar persoonlijke websites te verifiëren. Zo werkt het:", + "account_edit.verified_modal.invisible_link.details": "Voeg de link toe aan uw header. Het belangrijke onderdeel is rel=\"me\" om te voorkomen dat websites impersoneren met door de gebruiker gegenereerde inhoud. Je kunt zelfs een linktag gebruiken in de kop van de pagina in plaats van {tag}, maar de HTML moet toegankelijk zijn zonder JavaScript uit te voeren.", + "account_edit.verified_modal.invisible_link.summary": "Hoe maak ik de link onzichtbaar?", + "account_edit.verified_modal.step1.header": "Kopieer de onderstaande HTML-code en plak deze in de koptekst van je website", + "account_edit.verified_modal.step2.details": "Als je je website al als een aangepast veld hebt toegevoegd, moet je deze verwijderen en opnieuw toevoegen om de verificatie te activeren.", + "account_edit.verified_modal.step2.header": "Voeg je website toe als een aangepast veld", + "account_edit.verified_modal.title": "Hoe voeg je een geverifieerde link toe", "account_edit_tags.add_tag": "#{tagName} toevoegen", "account_edit_tags.column_title": "Uitgelichte hashtags bewerken", "account_edit_tags.help_text": "Uitgelichte hashtags helpen gebruikers je profiel te ontdekken en om er interactie mee te communiceren. Ze verschijnen als filters op je Profielpagina onder het tabblad Activiteit.", @@ -275,6 +307,8 @@ "callout.dismiss": "Afwijzen", "carousel.current": "Bericht {current, number} / {max, number}", "carousel.slide": "Bericht {current, number} van {max, number}", + "character_counter.recommended": "{currentLength}/{maxLength} aanbevolen tekens", + "character_counter.required": "{currentLength}/{maxLength} tekens", "closed_registrations.other_server_instructions": "Omdat Mastodon gedecentraliseerd is, kun je op een andere server een account registreren en vanaf daar nog steeds met deze server communiceren.", "closed_registrations_modal.description": "Momenteel is het niet mogelijk om op {domain} een account aan te maken. Hou echter in gedachte dat om Mastodon te kunnen gebruiken het niet een vereiste is om op {domain} een account te hebben.", "closed_registrations_modal.find_another_server": "Een andere server zoeken", @@ -304,10 +338,15 @@ "collections.create_collection": "Verzameling aanmaken", "collections.delete_collection": "Verzameling verwijderen", "collections.description_length_hint": "Maximaal 100 karakters", + "collections.detail.accept_inclusion": "Oké", "collections.detail.accounts_heading": "Accounts", + "collections.detail.author_added_you": "{author} heeft je aan deze verzameling toegevoegd", "collections.detail.curated_by_author": "Samengesteld door {author}", "collections.detail.curated_by_you": "Samengesteld door jou", "collections.detail.loading": "Verzameling laden…", + "collections.detail.other_accounts_in_collection": "Anderen in deze verzameling:", + "collections.detail.revoke_inclusion": "Verwijder mij", + "collections.detail.sensitive_note": "Deze verzameling bevat accounts en inhoud die mogelijk gevoelig zijn voor sommige gebruikers.", "collections.detail.share": "Deze verzameling delen", "collections.edit_details": "Gegevens bewerken", "collections.error_loading_collections": "Er is een fout opgetreden bij het laden van je verzamelingen.", @@ -322,6 +361,9 @@ "collections.old_last_post_note": "Meer dan een week geleden voor het laatst een bericht geplaatst", "collections.remove_account": "Dit account verwijderen", "collections.report_collection": "Deze verzameling rapporteren", + "collections.revoke_collection_inclusion": "Verwijder mezelf uit deze collectie", + "collections.revoke_inclusion.confirmation": "Je bent verwijderd uit \"{collection}\"", + "collections.revoke_inclusion.error": "Er is een fout opgetreden. Probeer het later opnieuw.", "collections.search_accounts_label": "Naar accounts zoeken om toe te voegen…", "collections.search_accounts_max_reached": "Je hebt het maximum aantal accounts toegevoegd", "collections.sensitive": "Gevoelig", @@ -445,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Volger verwijderen", "confirmations.remove_from_followers.message": "{name} zal je niet meer volgen. Weet je zeker dat je wilt doorgaan?", "confirmations.remove_from_followers.title": "Volger verwijderen?", + "confirmations.revoke_collection_inclusion.confirm": "Verwijder mij", + "confirmations.revoke_collection_inclusion.message": "Deze actie is definitief en de curator kan je later niet opnieuw aan de verzameling toevoegen.", + "confirmations.revoke_collection_inclusion.title": "Verwijder jezelf van deze collectie?", "confirmations.revoke_quote.confirm": "Bericht verwijderen", "confirmations.revoke_quote.message": "Deze actie kan niet ongedaan worden gemaakt.", "confirmations.revoke_quote.title": "Bericht verwijderen?", @@ -767,6 +812,7 @@ "navigation_bar.automated_deletion": "Automatisch berichten verwijderen", "navigation_bar.blocks": "Geblokkeerde gebruikers", "navigation_bar.bookmarks": "Bladwijzers", + "navigation_bar.collections": "Verzamelingen", "navigation_bar.direct": "Privéberichten", "navigation_bar.domain_blocks": "Geblokkeerde servers", "navigation_bar.favourites": "Favorieten", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 4653db73b14a73..51042e538ff168 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -2,12 +2,12 @@ "about.blocks": "Servidores moderados", "about.contact": "Contacto:", "about.default_locale": "Padrão", - "about.disclaimer": "O Mastodon é um ‘software’ livre, de código aberto e uma marca registada de Mastodon gGmbH.", + "about.disclaimer": "O Mastodon é um 'software' livre, de código aberto e marca registada de Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Motivo não disponível", - "about.domain_blocks.preamble": "O Mastodon ver e interagir com o conteúdo de utilizadores de qualquer outra instância no fediverso. Estas são as exceções desta instância em específico.", - "about.domain_blocks.silenced.explanation": "Normalmente não verás perfis e conteúdos deste servidor, a não ser que os procures explicitamente ou optes por segui-los.", + "about.domain_blocks.preamble": "O Mastodon, geralmente, permite-lhe ver conteúdo e interagir com utilizadores de qualquer outro servidor na fediverso. Estas são as exceções aplicadas neste servidor em particular.", + "about.domain_blocks.silenced.explanation": "Normalmente não verá perfis e conteúdos deste servidor, a não ser que os procures explicitamente ou opte por segui-los.", "about.domain_blocks.silenced.title": "Limitados", - "about.domain_blocks.suspended.explanation": "Nenhum dado deste servidor será processado, armazenado ou trocado, tornando impossível qualquer interação ou comunicação com os utilizadores a partir deste servidor.", + "about.domain_blocks.suspended.explanation": "Nenhum dado deste servidor será processado, armazenado ou trocado, impossibilitando qualquer interação ou comunicação com os utilizadores a partir deste servidor.", "about.domain_blocks.suspended.title": "Suspensos", "about.language_label": "Idioma", "about.not_available": "Esta informação não foi disponibilizada neste servidor.", @@ -132,7 +132,7 @@ "account.show_reblogs": "Mostrar partilhas de @{name}", "account.statuses_counter": "{count, plural, one {{counter} publicação} other {{counter} publicações}}", "account.timeline.pinned": "Fixado", - "account.timeline.pinned.view_all": "Ver todos as publicações fixadas", + "account.timeline.pinned.view_all": "Ver todas as publicações fixadas", "account.unblock": "Desbloquear @{name}", "account.unblock_domain": "Desbloquear o domínio {domain}", "account.unblock_domain_short": "Desbloquear", @@ -142,7 +142,7 @@ "account.unmute": "Desocultar @{name}", "account.unmute_notifications_short": "Desocultar notificações", "account.unmute_short": "Desocultar", - "account_edit.bio.placeholder": "Adicione uma breve introdução para ajudar à sua identificação por outros.", + "account_edit.bio.placeholder": "Adicione uma breve apresentação para ajudar os outros a identificá-lo.", "account_edit.bio.title": "Bio", "account_edit.bio_modal.add_title": "Adicionar biografia", "account_edit.bio_modal.edit_title": "Editar biografia", @@ -155,15 +155,26 @@ "account_edit.custom_fields.placeholder": "Adicione os seus pronomes, hiperligações externas ou qualquer outra coisa que queira partilhar.", "account_edit.custom_fields.reorder_button": "Reordenar campos", "account_edit.custom_fields.tip_content": "Pode adicionar facilmente credibilidade à sua conta Mastodon, verificando ligações para qualquer website que possua.", + "account_edit.custom_fields.tip_title": "Dica: Adicionando links verificados", "account_edit.custom_fields.title": "Campos personalizados", - "account_edit.display_name.placeholder": "Como o seu nome vai aparecer no seu perfil e nas linhas do tempo.", + "account_edit.custom_fields.verified_hint": "Como adiciono um link verificado?", + "account_edit.display_name.placeholder": "O seu nome de exibição é como o seu nome aparece no seu perfil e nas linhas do tempo.", "account_edit.display_name.title": "Nome a mostrar", "account_edit.featured_hashtags.item": "etiquetas", "account_edit.featured_hashtags.placeholder": "Ajude à sua identificação por outros e tenha acesso rápido aos seus tópicos favoritos.", "account_edit.featured_hashtags.title": "Etiquetas em destaque", + "account_edit.field_delete_modal.confirm": "Tem certeza de que deseja excluir este campo personalizado? Esta ação não pode ser desfeita.", + "account_edit.field_delete_modal.delete_button": "Excluir", + "account_edit.field_delete_modal.title": "Excluir campo personalizado?", + "account_edit.field_edit_modal.add_title": "Adicionar campo personalizado", + "account_edit.field_edit_modal.edit_title": "Editar campo personalizado", "account_edit.field_edit_modal.limit_header": "Limite de caracteres recomendado excedido", "account_edit.field_edit_modal.limit_message": "Os utilizadores de dispositivos móveis podem não conseguir ver o seu campo na totalidade.", - "account_edit.field_edit_modal.link_emoji_warning": "Recomendamos que não utilize emojis personalizados em conjunto com URLs. Os campos personalizados que contenham ambos serão exibidos apenas como texto em vez de uma hiperligação, de modo a evitar a confusão dos utilizadores.", + "account_edit.field_edit_modal.link_emoji_warning": "Não recomendamos o uso de emojis personalizados em combinação com URLs. Campos personalizados que contenham ambos serão exibidos apenas como texto, em vez de como hiperligação, para evitar confusão aos utilizadores.", + "account_edit.field_edit_modal.name_hint": "Ex.: \"Site pessoal\"", + "account_edit.field_edit_modal.name_label": "Rótulo", + "account_edit.field_edit_modal.value_hint": "Ex.: “exemplo.me”", + "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "O arrastamento foi cancelado. O campo \"{item}\" foi largado.", "account_edit.field_reorder_modal.drag_end": "O campo \"{item}\" foi largado.", "account_edit.field_reorder_modal.drag_instructions": "Para reorganizar os campos personalizados, prima a tecla de espaço ou enter. Enquanto arrasta, utilize as teclas de setas para mover o campo para cima ou para baixo. Prima novamente a tecla de espaço ou enter para largar o campo na nova posição, ou prima escape para cancelar.", @@ -177,7 +188,21 @@ "account_edit.profile_tab.button_label": "Personalizar", "account_edit.profile_tab.hint.description": "Estas configurações personalizam o que os utilizadores veem no {server} nas aplicações oficiais, mas podem não se aplicar aos utilizadores de outros servidores nem aplicações de terceiros.", "account_edit.profile_tab.hint.title": "A apresentação ainda pode variar", + "account_edit.profile_tab.show_featured.description": "\"Destaques\" é uma aba opcional onde pode mostrar outras contas.", + "account_edit.profile_tab.show_featured.title": "Exibir aba “Destaques”", + "account_edit.profile_tab.show_media.description": "\"Mídia\" é uma aba opcional que mostra as suas publicações contendo imagens ou vídeos.", + "account_edit.profile_tab.show_media.title": "Exibir aba \"Mídia\"", + "account_edit.profile_tab.show_media_replies.description": "Quando ativada, a aba \"Mídia\" exibe tanto as suas publicações quanto as suas respostas às publicações de outras pessoas.", + "account_edit.profile_tab.subtitle": "Personalize as abas do seu perfil e o que elas exibem.", + "account_edit.profile_tab.title": "Configurações da aba do perfil", "account_edit.save": "Guardar", + "account_edit.verified_modal.details": "Adicione credibilidade ao seu perfil no Mastodon verificando links para sites pessoais. Veja como funciona:", + "account_edit.verified_modal.invisible_link.details": "Adicione o link ao seu cabeçalho. A parte importante é rel=\"me\", que evita a personificação em sites com conteúdo gerado por utilizadores. Você também pode usar uma tag de link no cabeçalho da página em vez de {tag}, mas o HTML deve ser acessível sem executar JavaScript.", + "account_edit.verified_modal.invisible_link.summary": "Como faço para tornar o link invisível?", + "account_edit.verified_modal.step1.header": "Copie o código HTML abaixo e cole no cabeçalho do seu site", + "account_edit.verified_modal.step2.details": "Se já adicionou o seu site como um campo personalizado, será necessário excluí-lo e adicioná-lo novamente para acionar a verificação.", + "account_edit.verified_modal.step2.header": "Adicione o seu site como um campo personalizado", + "account_edit.verified_modal.title": "Como adicionar um link verificado", "account_edit_tags.add_tag": "Adicionar #{tagName}", "account_edit_tags.column_title": "Editar etiquetas em destaque", "account_edit_tags.help_text": "As etiquetas destacadas ajudam os utilizadores a descobrir e interagir com o seu perfil. Aparecem como filtros na vista de atividade da sua página de perfil.", @@ -210,13 +235,13 @@ "annual_report.announcement.action_build": "Criar o meu Wrapstodon", "annual_report.announcement.action_dismiss": "Não, obrigado", "annual_report.announcement.action_view": "Ver o meu Wrapstodon", - "annual_report.announcement.description": "Descobre mais sobre o teu envolvimento com o Mastodon durante o último ano.", + "annual_report.announcement.description": "Descubra mais sobre a sua interação no Mastodon ao longo do último ano.", "annual_report.announcement.title": "Chegou o Wrapstodon {year}", "annual_report.nav_item.badge": "Novo", "annual_report.shared_page.donate": "Doar", "annual_report.shared_page.footer": "Gerado com {heart} pela equipa do Mastodon", "annual_report.shared_page.footer_server_info": "{username} utiliza {domain}, uma das muitas comunidades baseadas no Mastodon.", - "annual_report.summary.archetype.booster.desc_public": "{name} permaneceu à procura de publicações para partilhar, promovendo outros criadores com uma precisão perfeita.", + "annual_report.summary.archetype.booster.desc_public": "{name} manteve a procura de publicações para impulsionar, amplificando outros criadores com precisão perfeita.", "annual_report.summary.archetype.booster.desc_self": "Permaneceu à procura de publicações para partilhar, promovendo outros criadores com uma precisão perfeita.", "annual_report.summary.archetype.booster.name": "O Arqueiro", "annual_report.summary.archetype.die_drei_fragezeichen": "???", @@ -233,7 +258,7 @@ "annual_report.summary.archetype.replier.desc_self": "Respondeu frequentemente às publicações de outras pessoas, polinizando o Mastodon com novas discussões.", "annual_report.summary.archetype.replier.name": "A Borboleta", "annual_report.summary.archetype.reveal": "Revelar o meu arquétipo", - "annual_report.summary.archetype.reveal_description": "Obrigado por fazer parte do Mastodon! É hora de descobrir qual arquétipo você encarnou em {year}.", + "annual_report.summary.archetype.reveal_description": "Obrigado por fazer parte do Mastodon! É hora de descobrir qual arquétipo foi incorporado em {year}.", "annual_report.summary.archetype.title_public": "Arquétipo de {name}", "annual_report.summary.archetype.title_self": "O seu arquétipo", "annual_report.summary.close": "Fechar", @@ -255,7 +280,7 @@ "annual_report.summary.share_on_mastodon": "Partilhar no Mastodon", "attachments_list.unprocessed": "(não processado)", "audio.hide": "Ocultar áudio", - "block_modal.remote_users_caveat": "Vamos pedir ao servidor {domain} para respeitar a tua decisão. No entanto, não é garantido o seu cumprimento, uma vez que alguns servidores podem tratar os bloqueios de forma diferente. As publicações públicas podem continuar a ser visíveis para utilizadores não autenticados.", + "block_modal.remote_users_caveat": "Solicitaremos ao servidor {domain} que respeite a sua decisão. No entanto, o cumprimento não é garantido, sendo que alguns servidores podem gerir bloqueios de forma diferente. As publicações públicas podem continuar visíveis para utilizadores não autenticados.", "block_modal.show_less": "Mostrar menos", "block_modal.show_more": "Mostrar mais", "block_modal.they_cant_mention": "Ele não o pode mencionar nem seguir.", @@ -269,7 +294,7 @@ "bundle_column_error.copy_stacktrace": "Copiar relatório de erros", "bundle_column_error.error.body": "A página solicitada não pôde ser sintetizada. Isto pode ser devido a uma falha no nosso código ou a um problema de compatibilidade com o navegador.", "bundle_column_error.error.title": "Ó, não!", - "bundle_column_error.network.body": "Houve um erro ao tentar carregar esta página. Isto pode ocorrer devido a um problema temporário com a tua conexão à internet ou a este servidor.", + "bundle_column_error.network.body": "Ocorreu um erro ao tentar carregar esta página. Isto poderá dever-se a um problema temporário na tua ligação à Internet ou neste servidor.", "bundle_column_error.network.title": "Erro de rede", "bundle_column_error.retry": "Tenta de novo", "bundle_column_error.return": "Voltar à página inicial", @@ -284,10 +309,11 @@ "character_counter.recommended": "{currentLength}/{maxLength} caracteres recomendados", "character_counter.required": "{currentLength}/{maxLength} caracteres", "closed_registrations.other_server_instructions": "Visto que o Mastodon é descentralizado, podes criar uma conta noutro servidor e interagir com este na mesma.", - "closed_registrations_modal.description": "Neste momento não é possível criar uma conta em {domain}, mas lembramos que não é preciso ter uma conta especificamente em {domain} para usar o Mastodon.", + "closed_registrations_modal.description": "Criar uma conta em {domain} não é atualmente possível, mas tenha em atenção que não é necessário ter uma conta especificamente em {domain} para usar o Mastodon.", "closed_registrations_modal.find_another_server": "Procurar outro servidor", - "closed_registrations_modal.preamble": "O Mastodon é descentralizado, por isso não importa onde a tua conta é criada, pois continuarás a poder acompanhar e interagir com qualquer um neste servidor. Podes até alojar o teu próprio servidor!", + "closed_registrations_modal.preamble": "O Mastodon é descentralizado, por isso, não importa onde crie a sua conta: poderá seguir e interagir com qualquer utilizador neste servidor. Pode até alojá-lo você próprio!", "closed_registrations_modal.title": "Criar uma conta no Mastodon", + "collection.share_modal.share_link_label": "Link de convite para partilha", "collection.share_modal.share_via_post": "Publicar no Mastodon", "collection.share_modal.share_via_system": "Compartilhar com…", "collection.share_modal.title": "Partilhar coleção", @@ -311,10 +337,15 @@ "collections.create_collection": "Criar coleção", "collections.delete_collection": "Eliminar coleção", "collections.description_length_hint": "Limite de 100 caracteres", + "collections.detail.accept_inclusion": "OK / Aceitar", "collections.detail.accounts_heading": "Contas", + "collections.detail.author_added_you": "{author} adicionou-o a esta coleção", "collections.detail.curated_by_author": "Curado por {author}", "collections.detail.curated_by_you": "Curado por si", "collections.detail.loading": "A carregar a coleção…", + "collections.detail.other_accounts_in_collection": "Outros nesta coleção:", + "collections.detail.revoke_inclusion": "Remover-me", + "collections.detail.sensitive_note": "Esta coleção contém contas e conteúdos que podem ser sensíveis para alguns utilizadores.", "collections.detail.share": "Partilhar esta coleção", "collections.edit_details": "Editar detalhes", "collections.error_loading_collections": "Ocorreu um erro ao tentar carregar as suas coleções.", @@ -329,6 +360,9 @@ "collections.old_last_post_note": "Última publicação há mais de uma semana", "collections.remove_account": "Remover esta conta", "collections.report_collection": "Denunciar esta coleção", + "collections.revoke_collection_inclusion": "Remover-me desta coleção", + "collections.revoke_inclusion.confirmation": "Foi removido da coleção \"{collection}\"", + "collections.revoke_inclusion.error": "Ocorreu um erro, por favor tente novamente mais tarde.", "collections.search_accounts_label": "Procurar contas para adicionar…", "collections.search_accounts_max_reached": "Já adicionou o máximo de contas", "collections.sensitive": "Sensível", @@ -342,7 +376,7 @@ "collections.visibility_unlisted_hint": "Visível para qualquer pessoa com uma hiperligação. Não aparece nos resultados de pesquisa e recomendações.", "column.about": "Sobre", "column.blocks": "Utilizadores bloqueados", - "column.bookmarks": "Marcadores", + "column.bookmarks": "Favoritos", "column.collections": "As minhas coleções", "column.community": "Cronologia local", "column.create_list": "Criar lista", @@ -452,6 +486,9 @@ "confirmations.remove_from_followers.confirm": "Remover seguidor", "confirmations.remove_from_followers.message": "{name} vai parar de seguir-te. Tens a certeza que prentedes continuar?", "confirmations.remove_from_followers.title": "Remover seguidor?", + "confirmations.revoke_collection_inclusion.confirm": "Remover-me", + "confirmations.revoke_collection_inclusion.message": "Esta ação é permanente, e o curador não poderá adicioná-lo novamente à coleção mais tarde.", + "confirmations.revoke_collection_inclusion.title": "Remover-se desta coleção?", "confirmations.revoke_quote.confirm": "Remover publicação", "confirmations.revoke_quote.message": "Esta ação é irreversível.", "confirmations.revoke_quote.title": "Remover publicação?", @@ -469,6 +506,7 @@ "conversation.open": "Ver conversa", "conversation.with": "Com {names}", "copy_icon_button.copied": "Copiado para a área de transferência", + "copy_icon_button.copy_this_text": "Copiar link para a área de transferência", "copypaste.copied": "Copiado", "copypaste.copy_to_clipboard": "Copiar para a área de transferência", "directory.federated": "Do fediverso conhecido", @@ -686,6 +724,7 @@ "keyboard_shortcuts.direct": "Abrir coluna de menções privadas", "keyboard_shortcuts.down": "mover para baixo na lista", "keyboard_shortcuts.enter": "abrir publicação", + "keyboard_shortcuts.explore": "Abrir linha do tempo em destaque", "keyboard_shortcuts.favourite": "assinalar como favorita", "keyboard_shortcuts.favourites": "abrir lista de favoritos", "keyboard_shortcuts.federated": "abrir a cronologia federada", @@ -772,6 +811,7 @@ "navigation_bar.automated_deletion": "Eliminação automática de publicações", "navigation_bar.blocks": "Utilizadores bloqueados", "navigation_bar.bookmarks": "Itens salvos", + "navigation_bar.collections": "Coleções", "navigation_bar.direct": "Menções privadas", "navigation_bar.domain_blocks": "Domínios escondidos", "navigation_bar.favourites": "Favoritos", @@ -808,7 +848,7 @@ "notification.annual_report.view": "Ver #Wrapstodon", "notification.favourite": "{name} assinalou a tua publicação como favorita", "notification.favourite.name_and_others_with_link": "{name} e {count, plural, one {# outro} other {# outros}} assinalaram a tua publicação como favorita", - "notification.favourite_pm": "{name} assinalou como favorita a tua menção privada", + "notification.favourite_pm": "{name} assinalou como favorita a sua menção privada", "notification.favourite_pm.name_and_others_with_link": "{name} e {count, plural, one {# outro favoritou} other {# outros favoritaram}} a tua menção privada", "notification.follow": "{name} começou a seguir-te", "notification.follow.name_and_others": "{name} e {count, plural, one {# outro seguiram-te} other {# outros seguiram-te}}", @@ -1003,7 +1043,7 @@ "report.forward": "Reencaminhar para {target}", "report.forward_hint": "A conta pertence a outro servidor. Enviar uma cópia anónima da denúncia para esse servidor também?", "report.mute": "Ocultar", - "report.mute_explanation": "Não verás as publicações dele. Ele não poderá ver as tuas publicações nem seguir-te. Ele não saberá que o ocultaste.", + "report.mute_explanation": "Não verá as publicações dele. Ele não poderá ver as suas publicações nem segui-lo. Ele não saberá que o ocultou.", "report.next": "Seguinte", "report.placeholder": "Comentários adicionais", "report.reasons.dislike": "Não gosto disto", @@ -1075,6 +1115,9 @@ "sign_in_banner.mastodon_is": "O Mastodon é a melhor maneira de acompanhar o que está a acontecer.", "sign_in_banner.sign_in": "Iniciar sessão", "sign_in_banner.sso_redirect": "Inicia a sessão ou cria uma conta", + "skip_links.hotkey": "Tecla de atalho {hotkey}", + "skip_links.skip_to_content": "Ir para o conteúdo principal", + "skip_links.skip_to_navigation": "Ir para a navegação principal", "status.admin_account": "Abrir a interface de moderação para @{name}", "status.admin_domain": "Abrir interface de moderação para {domain}", "status.admin_status": "Abrir esta publicação na interface de moderação", @@ -1147,7 +1190,7 @@ "status.reblogs.empty": "Ainda ninguém partilhou esta publicação. Quando alguém o fizer, aparecerá aqui.", "status.reblogs_count": "{count, plural, one {{counter} partilha} other {{counter} partilhas}}", "status.redraft": "Eliminar e reescrever", - "status.remove_bookmark": "Retirar dos marcadores", + "status.remove_bookmark": "Remover marcador", "status.remove_favourite": "Remover dos favoritos", "status.remove_quote": "Remover", "status.replied_in_thread": "Responder na conversa", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index db08801ac4449c..0076f1cdecd0d5 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -339,6 +339,7 @@ "collections.detail.curated_by_you": "Nën kujdesin tuaj", "collections.detail.loading": "Po ngarkohet koleksion…", "collections.detail.other_accounts_in_collection": "Të tjerë në këtë koleksion:", + "collections.detail.revoke_inclusion": "Hiqmëni", "collections.detail.sensitive_note": "Ky koleksion përmban llogari dhe lëndë që mund të jetë me spec për disa përdorues.", "collections.detail.share": "Ndajeni këtë koleksion me të tjerë", "collections.edit_details": "Përpunoni hollësi", @@ -354,6 +355,9 @@ "collections.old_last_post_note": "Të postuarat e fundit gjatë një jave më parë", "collections.remove_account": "Hiqe këtë llogari", "collections.report_collection": "Raportojeni këtë koleksion", + "collections.revoke_collection_inclusion": "Hiqmëni nga ky koleksion", + "collections.revoke_inclusion.confirmation": "U hoqët nga “{collection}”", + "collections.revoke_inclusion.error": "Pati një gabim, ju lutemi, riprovoni më vonë.", "collections.search_accounts_label": "Kërkoni për llogari për shtim…", "collections.search_accounts_max_reached": "Keni shtuar numrin maksimum të llogarive", "collections.sensitive": "Rezervat", @@ -477,6 +481,9 @@ "confirmations.remove_from_followers.confirm": "Hiqe ndjekësin", "confirmations.remove_from_followers.message": "{name} do të reshtë së ndjekuri ju. Jeni i sigurt se doni të vazhdohet?", "confirmations.remove_from_followers.title": "Të hiqet ndjekësi?", + "confirmations.revoke_collection_inclusion.confirm": "Hiqmëni", + "confirmations.revoke_collection_inclusion.message": "Ky veprim është i përhershëm dhe mirëmbajtësi s’do të jetë në gjendje t’ju rishtojë te koleksioni më vonë.", + "confirmations.revoke_collection_inclusion.title": "Të hiqeni nga ky koleksion?", "confirmations.revoke_quote.confirm": "Hiqe postimin", "confirmations.revoke_quote.message": "Ky veprim s’mund të zhbëhet.", "confirmations.revoke_quote.title": "Të hiqet postimi?", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index f89245307e0777..e58007425e477f 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -275,11 +275,14 @@ "collections.create_a_collection_hint": "Skapa en samling för att rekommendera eller dela dina favoritkonton med andra.", "collections.create_collection": "Skapa samling", "collections.delete_collection": "Radera samling", + "collections.detail.accept_inclusion": "Okej", "collections.detail.accounts_heading": "Konton", + "collections.detail.revoke_inclusion": "Ta bort mig", "collections.error_loading_collections": "Det uppstod ett fel när dina samlingar skulle laddas.", "collections.hints.accounts_counter": "{count} / {max} konton", "collections.no_collections_yet": "Inga samlingar än.", "collections.remove_account": "Ta bort detta konto", + "collections.revoke_inclusion.error": "Ett fel uppstod, försök igen senare.", "collections.search_accounts_label": "Sök efter konton för att lägga till…", "collections.search_accounts_max_reached": "Du har lagt till maximalt antal konton", "collections.view_collection": "Visa samling", @@ -384,6 +387,7 @@ "confirmations.remove_from_followers.confirm": "Ta bort följare", "confirmations.remove_from_followers.message": "{name} kommer att sluta följa dig. Är du säker på att du vill fortsätta?", "confirmations.remove_from_followers.title": "Ta bort följare?", + "confirmations.revoke_collection_inclusion.confirm": "Ta bort mig", "confirmations.revoke_quote.confirm": "Ta bort inlägg", "confirmations.revoke_quote.message": "Denna åtgärd kan inte ångras.", "confirmations.revoke_quote.title": "Ta bort inlägg?", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 583cfbbe4416fe..08791057711dc0 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -338,12 +338,14 @@ "collections.create_collection": "Tạo gói khởi đầu", "collections.delete_collection": "Xóa gói khởi đầu", "collections.description_length_hint": "Giới hạn 100 ký tự", + "collections.detail.accept_inclusion": "Okay", "collections.detail.accounts_heading": "Tài khoản", "collections.detail.author_added_you": "{author} đã thêm bạn vào gói khởi đầu này", "collections.detail.curated_by_author": "Tuyển chọn bởi {author}", "collections.detail.curated_by_you": "Tuyển chọn bởi bạn", "collections.detail.loading": "Đang tải gói khởi đầu…", "collections.detail.other_accounts_in_collection": "Những người khác trong gói khởi đầu này:", + "collections.detail.revoke_inclusion": "Xóa tôi", "collections.detail.sensitive_note": "Gói khởi đầu này chứa các tài khoản và nội dung có thể nhạy cảm đối với một số người.", "collections.detail.share": "Chia sẻ gói khởi đầu này", "collections.edit_details": "Sửa chi tiết", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Đăng lần cuối hơn một tuần trước", "collections.remove_account": "Gỡ tài khoản này", "collections.report_collection": "Báo cáo gói khởi đầu này", + "collections.revoke_collection_inclusion": "Xóa tôi khỏi gói khởi đầu này", + "collections.revoke_inclusion.confirmation": "Bạn đã được gỡ khỏi \"{collection}\"", + "collections.revoke_inclusion.error": "Đã có lỗi, xin vui lòng thử lại.", "collections.search_accounts_label": "Tìm tài khoản để thêm…", "collections.search_accounts_max_reached": "Bạn đã đạt đến số lượng tài khoản tối đa", "collections.sensitive": "Nhạy cảm", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Xóa người theo dõi", "confirmations.remove_from_followers.message": "{name} sẽ không còn theo dõi bạn.Bạn có chắc tiếp tục?", "confirmations.remove_from_followers.title": "Xóa người theo dõi?", + "confirmations.revoke_collection_inclusion.confirm": "Xóa tôi", + "confirmations.revoke_collection_inclusion.message": "Thao tác này là vĩnh viễn, người tuyển chọn sẽ không thể thêm lại bạn về sau này.", + "confirmations.revoke_collection_inclusion.title": "Xóa bạn khỏi gói khởi đầu này?", "confirmations.revoke_quote.confirm": "Gỡ tút", "confirmations.revoke_quote.message": "Hành động này không thể hoàn tác.", "confirmations.revoke_quote.title": "Gỡ tút?", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 190b4fab201456..7d4685116c1f8c 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -338,12 +338,14 @@ "collections.create_collection": "创建收藏列表", "collections.delete_collection": "删除收藏列表", "collections.description_length_hint": "100字限制", + "collections.detail.accept_inclusion": "确定", "collections.detail.accounts_heading": "账号", "collections.detail.author_added_you": "{author} 将你添加到了此收藏列表", "collections.detail.curated_by_author": "由 {author} 精心挑选", "collections.detail.curated_by_you": "由你精心挑选", "collections.detail.loading": "正在加载收藏列表…", "collections.detail.other_accounts_in_collection": "此收藏列表中的其他人:", + "collections.detail.revoke_inclusion": "移除我", "collections.detail.sensitive_note": "此收藏列表可能包含某些对部分用户而言为敏感内容的账号或内容。", "collections.detail.share": "分享此收藏列表", "collections.edit_details": "编辑详情", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "上次发言于一周多以前", "collections.remove_account": "移除此账号", "collections.report_collection": "举报此收藏列表", + "collections.revoke_collection_inclusion": "将自己从此收藏列表中移除", + "collections.revoke_inclusion.confirmation": "你已被从“{collection}”中移除", + "collections.revoke_inclusion.error": "出现错误,请稍后重试。", "collections.search_accounts_label": "搜索要添加的账号…", "collections.search_accounts_max_reached": "你添加的账号数量已达上限", "collections.sensitive": "敏感内容", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "移除关注者", "confirmations.remove_from_followers.message": "{name} 将停止关注你。你确定要继续吗?", "confirmations.remove_from_followers.title": "移除关注者?", + "confirmations.revoke_collection_inclusion.confirm": "移除我", + "confirmations.revoke_collection_inclusion.message": "此操作是永久的,且此收藏列表的制作者之后将无法再将你添加到其中。", + "confirmations.revoke_collection_inclusion.title": "将自己从此收藏列表中移除吗?", "confirmations.revoke_quote.confirm": "移除嘟文", "confirmations.revoke_quote.message": "此操作无法撤销。", "confirmations.revoke_quote.title": "移除嘟文?", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index a8dfa74dc91a09..913a0ab9d4a8e8 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -338,12 +338,14 @@ "collections.create_collection": "建立收藏名單", "collections.delete_collection": "刪除收藏名單", "collections.description_length_hint": "100 字限制", + "collections.detail.accept_inclusion": "Okay", "collections.detail.accounts_heading": "帳號", "collections.detail.author_added_you": "{author} 將您加入至此收藏名單", "collections.detail.curated_by_author": "由 {author} 精選", "collections.detail.curated_by_you": "由您精選", "collections.detail.loading": "讀取收藏名單中...", "collections.detail.other_accounts_in_collection": "此收藏名單中其他人:", + "collections.detail.revoke_inclusion": "移除我", "collections.detail.sensitive_note": "此收藏名單可能包含對某些使用者敏感之帳號或內容。", "collections.detail.share": "分享此收藏名單", "collections.edit_details": "編輯詳細資料", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "上次發表嘟文已超過一週", "collections.remove_account": "移除此帳號", "collections.report_collection": "檢舉此收藏名單", + "collections.revoke_collection_inclusion": "將我自此收藏名單中移除", + "collections.revoke_inclusion.confirmation": "您已自「{collection}」中被移除", + "collections.revoke_inclusion.error": "發生錯誤,請稍候重試。", "collections.search_accounts_label": "搜尋帳號以加入...", "collections.search_accounts_max_reached": "您新增之帳號數已達上限", "collections.sensitive": "敏感內容", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "移除跟隨者", "confirmations.remove_from_followers.message": "{name} 將停止跟隨您。您確定要繼續嗎?", "confirmations.remove_from_followers.title": "是否移除該跟隨者?", + "confirmations.revoke_collection_inclusion.confirm": "移除我", + "confirmations.revoke_collection_inclusion.message": "此操作永久有效,且該收藏名單之擁有者無法稍候將您再次加入至此收藏名單。", + "confirmations.revoke_collection_inclusion.title": "是否將您自此收藏名單中移除?", "confirmations.revoke_quote.confirm": "移除嘟文", "confirmations.revoke_quote.message": "此動作無法復原。", "confirmations.revoke_quote.title": "是否移除該嘟文?", diff --git a/config/locales/el.yml b/config/locales/el.yml index 3e0bc3a0601fd7..f46dbccd297a29 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -841,10 +841,10 @@ el: desc: Όταν οι χρήστες σου κάνουν κλικ συνδέσμους σε εξωτερικές ιστοσελίδες, το πρόγραμμα περιήγησής τους μπορεί να στείλει τη διεύθυνση του διακομιστή σας Mastodon ως αναφέρων. Απενεργοποίησέ το αν αυτό θα αναγνώριζε μοναδικά τους χρήστες σου, π.χ. αν αυτός είναι ένας προσωπικός διακομιστής Mastodon. title: Να επιτρέπεται σε εξωτερικούς ιστότοπους να βλέπουν τον διακομιστή Mastodon σου ως πηγή κίνησης appearance: - preamble: Προσάρμοσε την ιστοσελίδα του Mastodon. + preamble: Προσάρμοσε τη διεπαφή ιστού του Mastodon. title: Εμφάνιση branding: - preamble: Η ταυτότητα του διακομιστή σου, τον διαφοροποιεί από άλλους διακομιστές του δικτύου. Αυτές οι πληροφορίες μπορεί να εμφανίζονται σε διάφορα περιβάλλοντα, όπως η ιστοσελίδα του Mastodon, εγγενείς εφαρμογές, σε προεπισκοπήσεις συνδέσμου σε άλλους ιστότοπους και εντός εφαρμογών μηνυμάτων και ούτω καθεξής. Γι' αυτό, είναι καλύτερο να διατηρούνται αυτές οι πληροφορίες σαφείς, σύντομες και συνοπτικές. + preamble: Η ταυτότητα του διακομιστή σου, τον διαφοροποιεί από άλλους διακομιστές του δικτύου. Αυτές οι πληροφορίες μπορεί να εμφανίζονται σε διάφορα περιβάλλοντα, όπως η διεπαφή ιστού του Mastodon, εγγενείς εφαρμογές, σε προεπισκοπήσεις συνδέσμου σε άλλους ιστότοπους και εντός εφαρμογών μηνυμάτων και ούτω καθεξής. Γι' αυτό, είναι καλύτερο να διατηρούνται αυτές οι πληροφορίες σαφείς, σύντομες και συνοπτικές. title: Ταυτότητα captcha_enabled: desc_html: Αυτό βασίζεται σε εξωτερικά scripts από το hCaptcha, όπου υπάρχει ανησυχία πέρι ασφάλειας και ιδιωτηκότητας. Επιπρόσθετα, μπορεί να κάνει τη διαδικασία εγγραφής πολύ λιγότερο προσβάσιμη για κάποια άτομα (ειδικά αυτά με αναπηρίες). Για αυτούς τους λόγους, παρακαλώ σκέψου άλλου τρόπους εγγραφής όπως με αποδοχή ή με πρόσκληση. @@ -1433,7 +1433,7 @@ el: content: Λυπούμαστε, κάτι πήγε στραβά από τη δική μας μεριά. title: Η σελίδα αυτή δεν είναι σωστή '503': Η σελίδα δε μπόρεσε να εμφανιστεί λόγω προσωρινού σφάλματος του διακομιστή. - noscript_html: Για να χρησιμοποιήσεις τη δικτυακή εφαρμογή του Mastodon, ενεργοποίησε την Javascript. Εναλλακτικά, δοκίμασε μια από τις εφαρμογές για το Mastodon για την πλατφόρμα σου. + noscript_html: Για να χρησιμοποιήσεις την εφαρμογή ιστού του Mastodon, παρακαλούμε ενεργοποίησε την Javascript. Εναλλακτικά, δοκίμασε μια από τις εφαρμογές για το Mastodon για την πλατφόρμα σου. existing_username_validator: not_found: δεν βρέθηκε τοπικός χρήστης με αυτό το όνομα not_found_multiple: δεν βρέθηκε %{usernames} @@ -1471,7 +1471,7 @@ el: statuses_hint_html: Αυτό το φίλτρο εφαρμόζεται για την επιλογή μεμονωμένων αναρτήσεων, ανεξάρτητα από το αν αντιστοιχούν με τις λέξεις-κλειδιά παρακάτω. Επισκόπηση ή αφαίρεση αναρτήσεων από το φίλτρο. title: Επεξεργασία φίλτρου errors: - deprecated_api_multiple_keywords: Αυτές οι παράμετροι δεν μπορούν να αλλάξουν από αυτήν την εφαρμογή επειδή ισχύουν για περισσότερες από μία λέξεις-κλειδιά φίλτρου. Χρησιμοποίησε μια πιο πρόσφατη εφαρμογή ή την ιστοσελίδα. + deprecated_api_multiple_keywords: Αυτές οι παράμετροι δεν μπορούν να αλλάξουν από αυτήν την εφαρμογή επειδή ισχύουν για περισσότερες από μία λέξεις-κλειδιά φίλτρου. Χρησιμοποίησε μια πιο πρόσφατη εφαρμογή ή τη διεπαφή ιστού. invalid_context: Δόθηκε κενό ή μη έγκυρο περιεχόμενο index: contexts: Φίλτρα σε %{contexts} @@ -1497,7 +1497,7 @@ el: batch: remove: Αφαίρεση από φίλτρο index: - hint: Αυτό το φίλτρο ισχύει για την επιλογή μεμονωμένων αναρτήσεων ανεξάρτητα από άλλα κριτήρια. Μπορείς να προσθέσεις περισσότερες αναρτήσεις σε αυτό το φίλτρο από την ιστοσελίδα. + hint: Αυτό το φίλτρο ισχύει για την επιλογή μεμονωμένων αναρτήσεων ανεξάρτητα από άλλα κριτήρια. Μπορείς να προσθέσεις περισσότερες αναρτήσεις σε αυτό το φίλτρο από τη διεπαφή ιστού. title: Φιλτραρισμένες αναρτήσεις generic: all: Όλα diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml index d787d2bbc37f33..99601972edce22 100644 --- a/config/locales/simple_form.el.yml +++ b/config/locales/simple_form.el.yml @@ -91,7 +91,7 @@ el: bootstrap_timeline_accounts: Αυτοί οι λογαριασμοί θα καρφιτσωθούν στην κορυφή των προτεινόμενων ακολουθήσεων για νέους χρήστες. Παρέχετε μια λίστα λογαριασμών χωρισμένη με κόμμα. closed_registrations_message: Εμφανίζεται όταν κλείνουν οι εγγραφές content_cache_retention_period: Όλες οι αναρτήσεις από άλλους διακομιστές (συμπεριλαμβανομένων των ενισχύσεων και απαντήσεων) θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών, χωρίς να λαμβάνεται υπόψη οποιαδήποτε αλληλεπίδραση τοπικού χρήστη με αυτές τις αναρτήσεις. Αυτό περιλαμβάνει αναρτήσεις όπου ένας τοπικός χρήστης την έχει χαρακτηρίσει ως σελιδοδείκτη ή αγαπημένη. Θα χαθούν επίσης ιδιωτικές επισημάνσεις μεταξύ χρηστών από διαφορετικές οντότητες και θα είναι αδύνατο να αποκατασταθούν. Η χρήση αυτής της ρύθμισης προορίζεται για οντότητες ειδικού σκοπού και χαλάει πολλές προσδοκίες του χρήστη όταν εφαρμόζεται για χρήση γενική σκοπού. - custom_css: Μπορείς να εφαρμόσεις προσαρμοσμένα στυλ στην έκδοση ιστοσελίδας του Mastodon. + custom_css: Μπορείς να εφαρμόσεις προσαρμοσμένα στυλ στην έκδοση ιστού του Mastodon. favicon: WEBP, PNG, GIF ή JPG. Παρακάμπτει το προεπιλεγμένο favicon του Mastodon με ένα προσαρμοσμένο εικονίδιο. landing_page: Επιλέγει ποια σελίδα βλέπουν οι νέοι επισκέπτες όταν φτάνουν για πρώτη φορά στο διακομιστή σας. Αν επιλέξετε "Τάσεις", τότε οι τάσεις πρέπει να είναι ενεργοποιημένες στις Ρυθμίσεις Ανακάλυψης. Αν επιλέξετε "Τοπική ροή", τότε το "Πρόσβαση σε ζωντανές ροές με τοπικές αναρτήσεις" πρέπει να οριστεί σε "Όλοι" στις Ρυθμίσεις Ανακάλυψης. mascot: Παρακάμπτει την εικονογραφία στην προηγμένη διεπαφή ιστού. @@ -236,7 +236,7 @@ el: otp_attempt: Κωδικός δυο παραγόντων password: Συνθηματικό phrase: Λέξη-κλειδί ή φράση - setting_advanced_layout: Ενεργοποίηση προηγμένης λειτουργίας χρήσης + setting_advanced_layout: Ενεργοποίηση προηγμένης διεπαφής ιστού setting_aggregate_reblogs: Ομαδοποίηση προωθήσεων στις ροές setting_always_send_emails: Πάντα να αποστέλλονται ειδοποίησεις μέσω email setting_auto_play_gif: Αυτόματη αναπαραγωγή των GIF From 7d58ce309c12fcda44e79420d2d5a197c47efbea Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 10 Mar 2026 11:02:03 +0100 Subject: [PATCH 018/203] Fix collections not shown on profile when there are no featured tags or accounts (#38113) --- app/javascript/mastodon/features/account_featured/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/account_featured/index.tsx b/app/javascript/mastodon/features/account_featured/index.tsx index 1172ff3dc506e4..57edf04b64689f 100644 --- a/app/javascript/mastodon/features/account_featured/index.tsx +++ b/app/javascript/mastodon/features/account_featured/index.tsx @@ -103,7 +103,11 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({ ); } - if (featuredTags.isEmpty() && featuredAccountIds.isEmpty()) { + if ( + featuredTags.isEmpty() && + featuredAccountIds.isEmpty() && + listedCollections.length === 0 + ) { return ( Date: Tue, 10 Mar 2026 11:21:24 +0100 Subject: [PATCH 019/203] Add support for FEP-3b86 (Activity Intents) (#38120) --- .../entrypoints/remote_interaction_helper.ts | 93 +++++++++++++++---- .../mastodon/components/follow_button.tsx | 1 + app/javascript/mastodon/components/poll.tsx | 1 + .../components/status/boost_button.tsx | 2 + .../components/status_action_bar/index.jsx | 6 +- .../mastodon/containers/status_container.jsx | 5 +- .../features/interaction_modal/index.tsx | 32 +++++-- .../picture_in_picture/components/footer.tsx | 2 + .../mastodon/features/status/index.jsx | 17 ++-- app/serializers/webfinger_serializer.rb | 2 + 10 files changed, 119 insertions(+), 42 deletions(-) diff --git a/app/javascript/entrypoints/remote_interaction_helper.ts b/app/javascript/entrypoints/remote_interaction_helper.ts index f50203747d8f28..26f9e1f4e0a955 100644 --- a/app/javascript/entrypoints/remote_interaction_helper.ts +++ b/app/javascript/entrypoints/remote_interaction_helper.ts @@ -39,18 +39,57 @@ const findLink = (rel: string, data: unknown): JRDLink | undefined => { } }; -const findTemplateLink = (data: unknown) => - findLink('http://ostatus.org/schema/1.0/subscribe', data)?.template; +const intentParams = (intent: string) => { + switch (intent) { + case 'follow': + return ['https://w3id.org/fep/3b86/Follow', 'object'] as [string, string]; + case 'reblog': + return ['https://w3id.org/fep/3b86/Announce', 'object'] as [ + string, + string, + ]; + case 'favourite': + return ['https://w3id.org/fep/3b86/Like', 'object'] as [string, string]; + case 'vote': + case 'reply': + return ['https://w3id.org/fep/3b86/Object', 'object'] as [string, string]; + default: + return null; + } +}; + +const findTemplateLink = (data: unknown, intent: string) => { + const [needle, param] = intentParams(intent) ?? [ + 'http://ostatus.org/schema/1.0/subscribe', + 'uri', + ]; + + const match = findLink(needle, data); + + if (match) { + return [match.template, param] as [string, string]; + } + + const fallback = findLink('http://ostatus.org/schema/1.0/subscribe', data); + + if (fallback) { + return [fallback.template, 'uri'] as [string, string]; + } + + return [null, null]; +}; const fetchInteractionURLSuccess = ( uri_or_domain: string, template: string, + param: string, ) => { window.parent.postMessage( { type: 'fetchInteractionURL-success', uri_or_domain, template, + param, }, window.origin, ); @@ -74,7 +113,7 @@ const isValidDomain = (value: unknown) => { }; // Attempt to find a remote interaction URL from a domain -const fromDomain = (domain: string) => { +const fromDomain = (domain: string, intent: string) => { const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`; axios @@ -82,17 +121,21 @@ const fromDomain = (domain: string) => { params: { resource: `https://${domain}` }, }) .then(({ data }) => { - const template = findTemplateLink(data); - fetchInteractionURLSuccess(domain, template ?? fallbackTemplate); + const [template, param] = findTemplateLink(data, intent); + fetchInteractionURLSuccess( + domain, + template ?? fallbackTemplate, + param ?? 'uri', + ); return; }) .catch(() => { - fetchInteractionURLSuccess(domain, fallbackTemplate); + fetchInteractionURLSuccess(domain, fallbackTemplate, 'uri'); }); }; // Attempt to find a remote interaction URL from an arbitrary URL -const fromURL = (url: string) => { +const fromURL = (url: string, intent: string) => { const domain = new URL(url).host; const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`; @@ -101,17 +144,21 @@ const fromURL = (url: string) => { params: { resource: url }, }) .then(({ data }) => { - const template = findTemplateLink(data); - fetchInteractionURLSuccess(url, template ?? fallbackTemplate); + const [template, param] = findTemplateLink(data, intent); + fetchInteractionURLSuccess( + url, + template ?? fallbackTemplate, + param ?? 'uri', + ); return; }) .catch(() => { - fromDomain(domain); + fromDomain(domain, intent); }); }; // Attempt to find a remote interaction URL from a `user@domain` string -const fromAcct = (acct: string) => { +const fromAcct = (acct: string, intent: string) => { acct = acct.replace(/^@/, ''); const segments = acct.split('@'); @@ -134,25 +181,29 @@ const fromAcct = (acct: string) => { params: { resource: `acct:${acct}` }, }) .then(({ data }) => { - const template = findTemplateLink(data); - fetchInteractionURLSuccess(acct, template ?? fallbackTemplate); + const [template, param] = findTemplateLink(data, intent); + fetchInteractionURLSuccess( + acct, + template ?? fallbackTemplate, + param ?? 'uri', + ); return; }) .catch(() => { // TODO: handle host-meta? - fromDomain(domain); + fromDomain(domain, intent); }); }; -const fetchInteractionURL = (uri_or_domain: string) => { +const fetchInteractionURL = (uri_or_domain: string, intent: string) => { if (uri_or_domain === '') { fetchInteractionURLFailure(); } else if (/^https?:\/\//.test(uri_or_domain)) { - fromURL(uri_or_domain); + fromURL(uri_or_domain, intent); } else if (uri_or_domain.includes('@')) { - fromAcct(uri_or_domain); + fromAcct(uri_or_domain, intent); } else { - fromDomain(uri_or_domain); + fromDomain(uri_or_domain, intent); } }; @@ -172,8 +223,10 @@ window.addEventListener('message', (event: MessageEvent) => { 'type' in event.data && event.data.type === 'fetchInteractionURL' && 'uri_or_domain' in event.data && - typeof event.data.uri_or_domain === 'string' + typeof event.data.uri_or_domain === 'string' && + 'intent' in event.data && + typeof event.data.intent === 'string' ) { - fetchInteractionURL(event.data.uri_or_domain); + fetchInteractionURL(event.data.uri_or_domain, event.data.intent); } }); diff --git a/app/javascript/mastodon/components/follow_button.tsx b/app/javascript/mastodon/components/follow_button.tsx index e715de51c8b920..a682dd95521737 100644 --- a/app/javascript/mastodon/components/follow_button.tsx +++ b/app/javascript/mastodon/components/follow_button.tsx @@ -92,6 +92,7 @@ export const FollowButton: React.FC<{ openModal({ modalType: 'INTERACTION', modalProps: { + intent: 'follow', accountId: accountId, url: account?.url, }, diff --git a/app/javascript/mastodon/components/poll.tsx b/app/javascript/mastodon/components/poll.tsx index 2b7134185e3641..b5b5fb36732c14 100644 --- a/app/javascript/mastodon/components/poll.tsx +++ b/app/javascript/mastodon/components/poll.tsx @@ -110,6 +110,7 @@ export const Poll: React.FC = ({ pollId, disabled, status }) => { openModal({ modalType: 'INTERACTION', modalProps: { + intent: 'vote', accountId: status.getIn(['account', 'id']), url: status.get('uri'), }, diff --git a/app/javascript/mastodon/components/status/boost_button.tsx b/app/javascript/mastodon/components/status/boost_button.tsx index 023ba8ff1956a4..95d799f81bd3ea 100644 --- a/app/javascript/mastodon/components/status/boost_button.tsx +++ b/app/javascript/mastodon/components/status/boost_button.tsx @@ -47,6 +47,7 @@ const StandaloneBoostButton: FC = ({ status, counters }) => { openModal({ modalType: 'INTERACTION', modalProps: { + intent: 'reblog', accountId: status.getIn(['account', 'id']), url: status.get('uri'), }, @@ -120,6 +121,7 @@ const BoostOrQuoteMenu: FC = ({ status, counters }) => { openModal({ modalType: 'INTERACTION', modalProps: { + intent: 'reblog', accountId: status.getIn(['account', 'id']), url: status.get('uri'), }, diff --git a/app/javascript/mastodon/components/status_action_bar/index.jsx b/app/javascript/mastodon/components/status_action_bar/index.jsx index 5c79970e23f211..2e0440d0125da1 100644 --- a/app/javascript/mastodon/components/status_action_bar/index.jsx +++ b/app/javascript/mastodon/components/status_action_bar/index.jsx @@ -124,7 +124,7 @@ class StatusActionBar extends ImmutablePureComponent { if (signedIn) { this.props.onReply(this.props.status); } else { - this.props.onInteractionModal(this.props.status); + this.props.onInteractionModal(this.props.status, 'reply'); } }; @@ -146,7 +146,7 @@ class StatusActionBar extends ImmutablePureComponent { if (signedIn) { this.props.onFavourite(this.props.status); } else { - this.props.onInteractionModal(this.props.status); + this.props.onInteractionModal(this.props.status, 'favourite'); } }; @@ -382,7 +382,7 @@ class StatusActionBar extends ImmutablePureComponent { const bookmarkTitle = intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark); const favouriteTitle = intl.formatMessage(status.get('favourited') ? messages.removeFavourite : messages.favourite); const isReply = status.get('in_reply_to_account_id') === status.getIn(['account', 'id']); - + const shouldShowQuoteRemovalHint = isQuotingMe && contextType === 'notifications'; return ( diff --git a/app/javascript/mastodon/containers/status_container.jsx b/app/javascript/mastodon/containers/status_container.jsx index baf4157f96e512..bf49bf3f557fe5 100644 --- a/app/javascript/mastodon/containers/status_container.jsx +++ b/app/javascript/mastodon/containers/status_container.jsx @@ -77,7 +77,7 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({ onReblog (status, e) { dispatch(toggleReblog(status.get('id'), e.shiftKey)); }, - + onQuote (status) { dispatch(quoteComposeById(status.get('id'))); }, @@ -231,10 +231,11 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({ dispatch(deployPictureInPicture({statusId: status.get('id'), accountId: status.getIn(['account', 'id']), playerType: type, props: mediaProps})); }, - onInteractionModal (status) { + onInteractionModal (status, intent) { dispatch(openModal({ modalType: 'INTERACTION', modalProps: { + intent, accountId: status.getIn(['account', 'id']), url: status.get('uri'), }, diff --git a/app/javascript/mastodon/features/interaction_modal/index.tsx b/app/javascript/mastodon/features/interaction_modal/index.tsx index 03cfc2c484165e..624ecd5613f8c4 100644 --- a/app/javascript/mastodon/features/interaction_modal/index.tsx +++ b/app/javascript/mastodon/features/interaction_modal/index.tsx @@ -25,6 +25,8 @@ const messages = defineMessages({ }, }); +type InteractionIntent = 'follow' | 'reblog' | 'favourite' | 'reply' | 'vote'; + interface LoginFormMessage { type: | 'fetchInteractionURL' @@ -32,6 +34,8 @@ interface LoginFormMessage { | 'fetchInteractionURL-success'; uri_or_domain: string; template?: string; + param?: string; + intent?: InteractionIntent; } const PERSISTENCE_KEY = 'mastodon_home'; @@ -110,7 +114,11 @@ const isValueValid = (value: string) => { } }; -const sendToFrame = (frame: HTMLIFrameElement | null, value: string): void => { +const sendToFrame = ( + frame: HTMLIFrameElement | null, + value: string, + intent: string, +): void => { if (valueToDomain(value.trim()) === localDomain) { window.location.href = '/auth/sign_in'; return; @@ -120,6 +128,7 @@ const sendToFrame = (frame: HTMLIFrameElement | null, value: string): void => { { type: 'fetchInteractionURL', uri_or_domain: value.trim(), + intent, }, window.origin, ); @@ -127,7 +136,8 @@ const sendToFrame = (frame: HTMLIFrameElement | null, value: string): void => { const LoginForm: React.FC<{ resourceUrl: string; -}> = ({ resourceUrl }) => { + intent: string; +}> = ({ resourceUrl, intent }) => { const intl = useIntl(); const [value, setValue] = useState( localStorage.getItem(PERSISTENCE_KEY) ?? '', @@ -161,7 +171,7 @@ const LoginForm: React.FC<{ try { const url = new URL( event.data.template.replace( - '{uri}', + `{${event.data.param}}`, encodeURIComponent(resourceUrl), ), ); @@ -242,8 +252,8 @@ const LoginForm: React.FC<{ const handleSubmit = useCallback(() => { setIsSubmitting(true); - sendToFrame(iframeRef.current, value); - }, [setIsSubmitting, value]); + sendToFrame(iframeRef.current, value, intent); + }, [setIsSubmitting, value, intent]); const handleFocus = useCallback(() => { setExpanded(true); @@ -287,7 +297,7 @@ const LoginForm: React.FC<{ setError(false); setValue(selectedOptionValue); setIsSubmitting(true); - sendToFrame(iframeRef.current, selectedOptionValue); + sendToFrame(iframeRef.current, selectedOptionValue, intent); } break; @@ -300,6 +310,7 @@ const LoginForm: React.FC<{ setValue, selectedOption, options, + intent, ], ); @@ -318,9 +329,9 @@ const LoginForm: React.FC<{ setValue(option); setError(false); setIsSubmitting(true); - sendToFrame(iframeRef.current, option); + sendToFrame(iframeRef.current, option, intent); }, - [options, setSelectedOption, setValue, setError], + [options, setSelectedOption, setValue, setError, intent], ); const domain = (valueToDomain(value) ?? '').trim(); @@ -404,7 +415,8 @@ const LoginForm: React.FC<{ const InteractionModal: React.FC<{ accountId: string; url: string; -}> = ({ accountId, url }) => { + intent: string; +}> = ({ accountId, url, intent }) => { const dispatch = useAppDispatch(); const signupUrl = useAppSelector( (state) => @@ -479,7 +491,7 @@ const InteractionModal: React.FC<{

- +

{descendants} - + Date: Tue, 10 Mar 2026 11:28:44 +0000 Subject: [PATCH 020/203] Fix short numbers rounding up instead of truncating (#37899) (#38114) --- .../__tests__/short_number-test.tsx | 80 +++++++++++++++++++ .../mastodon/components/short_number.tsx | 1 + 2 files changed, 81 insertions(+) create mode 100644 app/javascript/mastodon/components/__tests__/short_number-test.tsx diff --git a/app/javascript/mastodon/components/__tests__/short_number-test.tsx b/app/javascript/mastodon/components/__tests__/short_number-test.tsx new file mode 100644 index 00000000000000..e221ca1eb8f57e --- /dev/null +++ b/app/javascript/mastodon/components/__tests__/short_number-test.tsx @@ -0,0 +1,80 @@ +import { IntlProvider } from 'react-intl'; + +import { render, screen } from '@testing-library/react'; + +import { ShortNumber } from '../short_number'; + +function renderShortNumber(value: number) { + return render( + + + , + ); +} + +describe('ShortNumber Component', () => { + it('does not abbreviate numbers under 1000', () => { + renderShortNumber(999); + expect(screen.getByText('999')).toBeDefined(); + }); + + it('formats thousands correctly for 1000', () => { + renderShortNumber(1000); + expect(screen.getByText('1K')).toBeDefined(); + }); + + it('truncates decimals for 1051', () => { + renderShortNumber(1051); + expect(screen.getByText('1K')).toBeDefined(); + }); + + it('truncates decimals for 2999', () => { + renderShortNumber(2999); + expect(screen.getByText('2.9K')).toBeDefined(); + }); + + it('truncates decimals for 9999', () => { + renderShortNumber(9999); + expect(screen.getByText('9.9K')).toBeDefined(); + }); + + it('truncates decimals for 10501', () => { + renderShortNumber(10501); + expect(screen.getByText('10K')).toBeDefined(); + }); + + it('truncates decimals for 11000', () => { + renderShortNumber(11000); + expect(screen.getByText('11K')).toBeDefined(); + }); + + it('truncates decimals for 99999', () => { + renderShortNumber(99999); + expect(screen.getByText('99K')).toBeDefined(); + }); + + it('truncates decimals for 100501', () => { + renderShortNumber(100501); + expect(screen.getByText('100K')).toBeDefined(); + }); + + it('truncates decimals for 101000', () => { + renderShortNumber(101000); + expect(screen.getByText('101K')).toBeDefined(); + }); + + it('truncates decimals for 999999', () => { + renderShortNumber(999999); + expect(screen.getByText('999K')).toBeDefined(); + }); + + it('truncates decimals for 2999999', () => { + renderShortNumber(2999999); + expect(screen.getByText('2.9M')).toBeDefined(); + }); + + it('truncates decimals for 9999999', () => { + renderShortNumber(9999999); + expect(screen.getByText('9.9M')).toBeDefined(); + }); +}); diff --git a/app/javascript/mastodon/components/short_number.tsx b/app/javascript/mastodon/components/short_number.tsx index 37201a5e1d7126..5fd0bba8edd58b 100644 --- a/app/javascript/mastodon/components/short_number.tsx +++ b/app/javascript/mastodon/components/short_number.tsx @@ -51,6 +51,7 @@ const ShortNumberCounter: React.FC = ({ value }) => { ); From c6322d80072fe3b90317a5a3af98ae4530c866ca Mon Sep 17 00:00:00 2001 From: Andy Piper Date: Tue, 10 Mar 2026 12:53:20 +0000 Subject: [PATCH 021/203] Update contributing docs (#38125) Signed-off-by: Andy Piper --- CONTRIBUTING.md | 5 ++++- docs/DEVELOPMENT.md | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d0b3e9eb2a6af4..b2f4165615dc6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,8 @@ You can contribute in the following ways: Please review the org-level [contribution guidelines] for high-level acceptance criteria guidance and the [DEVELOPMENT] guide for environment-specific details. +You should also read the project's [AI Contribution Policy] to understand how we approach +AI-assisted contributions. ## API Changes and Additions @@ -41,7 +43,7 @@ reviewed and merged into the codebase. Our time is limited and PRs making large, unsolicited changes are unlikely to get a response. Changes which link to an existing confirmed issue, or which come -from a "help wanted" issue or other request are more likely to be reviewed. +from a "help wanted" issue or other request, are more likely to be reviewed. The smaller and more narrowly focused the changes in a PR are, the easier they are to review and potentially merge. If the change only makes sense in some @@ -89,3 +91,4 @@ and API docs. Improvements are made via PRs to the [documentation repository]. [keepachangelog]: https://keepachangelog.com/en/1.0.0/ [Mastodon documentation]: https://docs.joinmastodon.org [SECURITY]: SECURITY.md +[AI Contribution Policy]: https://github.com/mastodon/.github/blob/main/AI_POLICY.md diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 0425f8892b6438..57b992021d958b 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -3,7 +3,7 @@ ## Overview Before starting local development, read the [CONTRIBUTING] guide to understand -what changes are desirable and what general processes to use. +what changes are desirable and what general processes to use. You should also read the project's [AI Contribution Policy] to understand how we approach AI-assisted contributions. ## Environments @@ -103,3 +103,4 @@ development environment configured with the software needed for this project. [GitHub Codespaces]: https://docs.github.com/en/codespaces [Homebrew]: https://brew.sh [Mastodon docs]: https://docs.joinmastodon.org/dev/setup/#working-with-emails-in-development +[AI Contribution Policy]: https://github.com/mastodon/.github/blob/main/AI_POLICY.md From f6ea52e82250bd0a1303192815d7ce851c494770 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 10 Mar 2026 10:45:17 -0400 Subject: [PATCH 022/203] Add `InstancesIndex` coverage (#38129) --- spec/chewy/instances_index_spec.rb | 45 +++++++++++++++++++ spec/rails_helper.rb | 6 --- spec/support/search.rb | 1 + .../instance_refresh_scheduler_spec.rb | 12 ++++- 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 spec/chewy/instances_index_spec.rb diff --git a/spec/chewy/instances_index_spec.rb b/spec/chewy/instances_index_spec.rb new file mode 100644 index 00000000000000..afd5b70a69a3b6 --- /dev/null +++ b/spec/chewy/instances_index_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe InstancesIndex do + context 'when elasticsearch is enabled', :search do + describe 'indexing records' do + before do + Fabricate :account, domain: 'host.example' + Instance.refresh + end + + it 'indexes records from scope' do + expect { described_class.import } + .to change(described_class, :count).by(1) + end + end + end + + describe 'Searching the index' do + before do + mock_elasticsearch_response(described_class, raw_response) + end + + it 'returns results from a query' do + results = described_class.query(match: { name: 'account' }) + + expect(results).to eq [] + end + end + + def raw_response + { + took: 3, + hits: { + hits: [ + { + _id: '0', + _score: 1.6375021, + }, + ], + }, + } + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 48a3268bf04f4f..31487cc3ef2ede 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -138,12 +138,6 @@ def sign_in(resource, _deprecated = nil, scope: nil) example.run end - config.around(:each, type: :search) do |example| - Chewy.settings[:enabled] = true - example.run - Chewy.settings[:enabled] = false - end - config.before :each, type: :cli do stub_reset_connection_pools end diff --git a/spec/support/search.rb b/spec/support/search.rb index adf99caf9e56e9..159068c1c55fbe 100644 --- a/spec/support/search.rb +++ b/spec/support/search.rb @@ -20,6 +20,7 @@ def search_indices [ AccountsIndex, + InstancesIndex, PublicStatusesIndex, StatusesIndex, TagsIndex, diff --git a/spec/workers/scheduler/instance_refresh_scheduler_spec.rb b/spec/workers/scheduler/instance_refresh_scheduler_spec.rb index 37682ebb8f1a4f..8ddeffa12eb902 100644 --- a/spec/workers/scheduler/instance_refresh_scheduler_spec.rb +++ b/spec/workers/scheduler/instance_refresh_scheduler_spec.rb @@ -7,7 +7,17 @@ describe 'perform' do it 'runs without error' do - expect { worker.perform }.to_not raise_error + expect { worker.perform } + .to_not raise_error + end + end + + context 'with elasticsearch enabled', :search do + before { Fabricate :remote_account } + + it 'updates search indexes' do + expect { worker.perform } + .to change(InstancesIndex, :count).by(1) end end end From 3b6d94ce6247b5a4c5546bce2831cf5eefe1ea71 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Tue, 10 Mar 2026 15:54:38 +0100 Subject: [PATCH 023/203] Add column for html descriptions of collections (#38124) --- app/models/collection.rb | 8 ++++++-- app/serializers/rest/collection_serializer.rb | 4 ++++ ...310095021_add_description_html_to_collections.rb | 13 +++++++++++++ db/schema.rb | 5 +++-- spec/fabricators/collection_fabricator.rb | 2 ++ spec/models/collection_spec.rb | 4 ++++ spec/serializers/rest/collection_serializer_spec.rb | 9 +++++++++ 7 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20260310095021_add_description_html_to_collections.rb diff --git a/app/models/collection.rb b/app/models/collection.rb index 3b8ee82a3cfe10..0061e7ff5c747f 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -5,7 +5,8 @@ # Table name: collections # # id :bigint(8) not null, primary key -# description :text not null +# description :text +# description_html :text # discoverable :boolean not null # item_count :integer default(0), not null # language :string @@ -30,7 +31,10 @@ class Collection < ApplicationRecord has_many :collection_reports, dependent: :delete_all validates :name, presence: true - validates :description, presence: true + validates :description, presence: true, + if: :local? + validates :description_html, presence: true, + if: :remote? validates :local, inclusion: [true, false] validates :sensitive, inclusion: [true, false] validates :discoverable, inclusion: [true, false] diff --git a/app/serializers/rest/collection_serializer.rb b/app/serializers/rest/collection_serializer.rb index 9296a5cf4ad82f..370384c22011ab 100644 --- a/app/serializers/rest/collection_serializer.rb +++ b/app/serializers/rest/collection_serializer.rb @@ -13,6 +13,10 @@ def id object.id.to_s end + def description + object.local? ? object.description : object.description_html + end + def items object.items_for(current_user&.account) end diff --git a/db/migrate/20260310095021_add_description_html_to_collections.rb b/db/migrate/20260310095021_add_description_html_to_collections.rb new file mode 100644 index 00000000000000..ef6a9aaecf3d17 --- /dev/null +++ b/db/migrate/20260310095021_add_description_html_to_collections.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class AddDescriptionHtmlToCollections < ActiveRecord::Migration[8.1] + def change + add_column :collections, :description_html, :text + + reversible do |direction| + direction.up { change_column :collections, :description, :text, null: true } + + direction.down { change_column :collections, :description, :text, null: false } + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f1dddcbe26af8d..d01d1af500ba5b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_03_03_144409) do +ActiveRecord::Schema[8.1].define(version: 2026_03_10_095021) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -387,7 +387,8 @@ create_table "collections", id: :bigint, default: -> { "timestamp_id('collections'::text)" }, force: :cascade do |t| t.bigint "account_id", null: false t.datetime "created_at", null: false - t.text "description", null: false + t.text "description" + t.text "description_html" t.boolean "discoverable", null: false t.integer "item_count", default: 0, null: false t.string "language" diff --git a/spec/fabricators/collection_fabricator.rb b/spec/fabricators/collection_fabricator.rb index 7e0e14a765f64a..cbce4bd478d0b9 100644 --- a/spec/fabricators/collection_fabricator.rb +++ b/spec/fabricators/collection_fabricator.rb @@ -12,6 +12,8 @@ Fabricator(:remote_collection, from: :collection) do account { Fabricate.build(:remote_account) } local false + description nil + description_html '

People to follow

' uri { sequence(:uri) { |i| "https://example.com/collections/#{i}" } } original_number_of_items 0 end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index ba1819fa6cda2d..fc833d354b3b0f 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -23,6 +23,10 @@ context 'when collection is remote' do subject { Fabricate.build :collection, local: false } + it { is_expected.to_not validate_presence_of(:description) } + + it { is_expected.to validate_presence_of(:description_html) } + it { is_expected.to validate_presence_of(:uri) } it { is_expected.to validate_presence_of(:original_number_of_items) } diff --git a/spec/serializers/rest/collection_serializer_spec.rb b/spec/serializers/rest/collection_serializer_spec.rb index 0fbe955b2ebf45..816b1873f6b15f 100644 --- a/spec/serializers/rest/collection_serializer_spec.rb +++ b/spec/serializers/rest/collection_serializer_spec.rb @@ -43,4 +43,13 @@ 'items' => [] ) end + + context 'when the collection is remote' do + let(:collection) { Fabricate(:remote_collection, description_html: '

remote

') } + + it 'includes the html description' do + expect(subject) + .to include('description' => '

remote

') + end + end end From 64629eadb71afab79a741b016dc56a8bdf1d46f2 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 10 Mar 2026 12:37:59 -0400 Subject: [PATCH 024/203] Do not prepare returning user for unpersisted records (#38136) --- app/models/user.rb | 6 ++++-- spec/models/user_spec.rb | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 88599ab75c4d65..f1ac710e8e96ae 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -209,8 +209,10 @@ def update_sign_in!(new_sign_in: false) increment(:sign_in_count) if new_sign_in - save(validate: false) unless new_record? - prepare_returning_user! + unless new_record? + save(validate: false) + prepare_returning_user! + end end def pending? diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4ea2e6a79ca649..187f05f02e85c4 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -208,9 +208,13 @@ context 'with a new user' do let(:user) { Fabricate.build :user } + before { allow(ActivityTracker).to receive(:record) } + it 'does not persist the user' do expect { user.update_sign_in! } .to_not change(user, :persisted?).from(false) + expect(ActivityTracker) + .to_not have_received(:record).with('activity:logins', anything) end end end From 316290ba9d25358f88a9616ba9cbc30b8ccef453 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 11 Mar 2026 08:42:36 +0100 Subject: [PATCH 025/203] Prevent hover card from showing unintentionally (#38112) --- .../components/hover_card_controller.tsx | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/components/hover_card_controller.tsx b/app/javascript/mastodon/components/hover_card_controller.tsx index d9352018bb8a26..a0c704a4e7c995 100644 --- a/app/javascript/mastodon/components/hover_card_controller.tsx +++ b/app/javascript/mastodon/components/hover_card_controller.tsx @@ -14,6 +14,10 @@ import { useTimeout } from 'mastodon/hooks/useTimeout'; const offset = [-12, 4] as OffsetValue; const enterDelay = 750; const leaveDelay = 150; +// Only open the card if the mouse was moved within this time, +// to avoid triggering the card without intentional mouse movement +// (e.g. when content changed underneath the mouse cursor) +const activeMovementThreshold = 150; const popperConfig = { strategy: 'fixed' } as UsePopperOptions; const isHoverCardAnchor = (element: HTMLElement) => @@ -23,10 +27,10 @@ export const HoverCardController: React.FC = () => { const [open, setOpen] = useState(false); const [accountId, setAccountId] = useState(); const [anchor, setAnchor] = useState(null); - const isUsingTouchRef = useRef(false); const cardRef = useRef(null); const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout(); const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout(); + const [setMoveTimeout, cancelMoveTimeout] = useTimeout(); const [setScrollTimeout] = useTimeout(); const handleClose = useCallback(() => { @@ -45,6 +49,8 @@ export const HoverCardController: React.FC = () => { useEffect(() => { let isScrolling = false; + let isUsingTouch = false; + let isActiveMouseMovement = false; let currentAnchor: HTMLElement | null = null; let currentTitle: string | null = null; @@ -66,7 +72,7 @@ export const HoverCardController: React.FC = () => { const handleTouchStart = () => { // Keeping track of touch events to prevent the // hover card from being displayed on touch devices - isUsingTouchRef.current = true; + isUsingTouch = true; }; const handleMouseEnter = (e: MouseEvent) => { @@ -78,13 +84,14 @@ export const HoverCardController: React.FC = () => { return; } - // Bail out if a touch is active - if (isUsingTouchRef.current) { + // Bail out if we're scrolling, a touch is active, + // or if there was no active mouse movement + if (isScrolling || !isActiveMouseMovement || isUsingTouch) { return; } // We've entered an anchor - if (!isScrolling && isHoverCardAnchor(target)) { + if (isHoverCardAnchor(target)) { cancelLeaveTimeout(); currentAnchor?.removeAttribute('aria-describedby'); @@ -99,10 +106,7 @@ export const HoverCardController: React.FC = () => { } // We've entered the hover card - if ( - !isScrolling && - (target === currentAnchor || target === cardRef.current) - ) { + if (target === currentAnchor || target === cardRef.current) { cancelLeaveTimeout(); } }; @@ -141,10 +145,17 @@ export const HoverCardController: React.FC = () => { }; const handleMouseMove = () => { - if (isUsingTouchRef.current) { - isUsingTouchRef.current = false; + if (isUsingTouch) { + isUsingTouch = false; } + delayEnterTimeout(enterDelay); + + cancelMoveTimeout(); + isActiveMouseMovement = true; + setMoveTimeout(() => { + isActiveMouseMovement = false; + }, activeMovementThreshold); }; document.body.addEventListener('touchstart', handleTouchStart, { @@ -188,6 +199,8 @@ export const HoverCardController: React.FC = () => { setOpen, setAccountId, setAnchor, + setMoveTimeout, + cancelMoveTimeout, ]); return ( From 9916c786e67080e71b6f8fc36d11f5d8daa233d9 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 11 Mar 2026 10:42:24 +0100 Subject: [PATCH 026/203] Add fallback to `Object` intent for FEP-3b86 in remote interaction helper (#38130) --- FEDERATION.md | 3 +- .../entrypoints/remote_interaction_helper.ts | 36 +++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/FEDERATION.md b/FEDERATION.md index 0ac44afc3cd37c..2d007dada12877 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -13,7 +13,8 @@ - [FEP-f1d5: NodeInfo in Fediverse Software](https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md) - [FEP-8fcf: Followers collection synchronization across servers](https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md) - [FEP-5feb: Search indexing consent for actors](https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md) -- [FEP-044f: Consent-respecting quote posts](https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md): partial support for incoming quote-posts +- [FEP-044f: Consent-respecting quote posts](https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md) +- [FEP-3b86: Activity Intents](https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md): offer handlers for `Object` and `Create` (with support for the `content` parameter only), has support for the `Follow`, `Announce`, `Like` and `Object` intents ## ActivityPub in Mastodon diff --git a/app/javascript/entrypoints/remote_interaction_helper.ts b/app/javascript/entrypoints/remote_interaction_helper.ts index 26f9e1f4e0a955..093f6a7ec29147 100644 --- a/app/javascript/entrypoints/remote_interaction_helper.ts +++ b/app/javascript/entrypoints/remote_interaction_helper.ts @@ -39,26 +39,27 @@ const findLink = (rel: string, data: unknown): JRDLink | undefined => { } }; -const intentParams = (intent: string) => { +const intentParams = (intent: string): [string, string] | null => { switch (intent) { case 'follow': - return ['https://w3id.org/fep/3b86/Follow', 'object'] as [string, string]; + return ['https://w3id.org/fep/3b86/Follow', 'object']; case 'reblog': - return ['https://w3id.org/fep/3b86/Announce', 'object'] as [ - string, - string, - ]; + return ['https://w3id.org/fep/3b86/Announce', 'object']; case 'favourite': - return ['https://w3id.org/fep/3b86/Like', 'object'] as [string, string]; + return ['https://w3id.org/fep/3b86/Like', 'object']; case 'vote': case 'reply': - return ['https://w3id.org/fep/3b86/Object', 'object'] as [string, string]; + return ['https://w3id.org/fep/3b86/Object', 'object']; default: return null; } }; -const findTemplateLink = (data: unknown, intent: string) => { +const findTemplateLink = ( + data: unknown, + intent: string, +): [string, string] | [null, null] => { + // Find the FEP-3b86 handler for the specific intent const [needle, param] = intentParams(intent) ?? [ 'http://ostatus.org/schema/1.0/subscribe', 'uri', @@ -66,14 +67,21 @@ const findTemplateLink = (data: unknown, intent: string) => { const match = findLink(needle, data); - if (match) { - return [match.template, param] as [string, string]; + if (match?.template) { + return [match.template, param]; } - const fallback = findLink('http://ostatus.org/schema/1.0/subscribe', data); + // If the specific intent wasn't found, try the FEP-3b86 handler for the `Object` intent + let fallback = findLink('https://w3id.org/fep/3b86/Object', data); + if (fallback?.template) { + return [fallback.template, 'object']; + } + + // If it's still not found, try the legacy OStatus subscribe handler + fallback = findLink('http://ostatus.org/schema/1.0/subscribe', data); - if (fallback) { - return [fallback.template, 'uri'] as [string, string]; + if (fallback?.template) { + return [fallback.template, 'uri']; } return [null, null]; From 32fc5304a7176c9383de791ea881803f46da4bf4 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 11 Mar 2026 10:49:52 +0100 Subject: [PATCH 027/203] Change HTTP signatures to skip the `Accept` header (#38132) --- app/lib/request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/request.rb b/app/lib/request.rb index cc741f212d5e35..66d7ece70f7980 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -208,7 +208,7 @@ def re_sign_on_redirect(_response, request) return end - signature_value = @signing.sign(signed_headers.without('User-Agent', 'Accept-Encoding'), @verb, Addressable::URI.parse(request.uri)) + signature_value = @signing.sign(signed_headers.without('User-Agent', 'Accept-Encoding', 'Accept'), @verb, Addressable::URI.parse(request.uri)) request.headers['Signature'] = signature_value end From 53f4d7f0292a97770081a40a4c3562d414786739 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 11 Mar 2026 05:51:49 -0400 Subject: [PATCH 028/203] Update `RemoteIp` patch with Rails 8.1 changes (#38139) --- lib/action_dispatch/remote_ip_extensions.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/action_dispatch/remote_ip_extensions.rb b/lib/action_dispatch/remote_ip_extensions.rb index e5c48bf3c5b0ee..bf78c69439d600 100644 --- a/lib/action_dispatch/remote_ip_extensions.rb +++ b/lib/action_dispatch/remote_ip_extensions.rb @@ -17,11 +17,11 @@ class RemoteIp module GetIpExtensions def calculate_ip # Set by the Rack web server, this is a single value. - remote_addr = ips_from(@req.remote_addr).last + remote_addr = sanitize_ips(ips_from(@req.remote_addr)).last # Could be a CSV list and/or repeated headers that were concatenated. - client_ips = ips_from(@req.client_ip).reverse! - forwarded_ips = ips_from(@req.x_forwarded_for).reverse! + client_ips = sanitize_ips(ips_from(@req.client_ip)).reverse! + forwarded_ips = sanitize_ips(@req.forwarded_for || []).reverse! # `Client-Ip` and `X-Forwarded-For` should not, generally, both be set. If they # are both set, it means that either: From 68f4fe74252176d71eb1c63458872d79dafbb64a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:05:54 +0100 Subject: [PATCH 029/203] Update dependency fastimage to v2.4.1 (#38135) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 120b28f7577fe9..65abef60ffa585 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -246,7 +246,7 @@ GEM faraday-net_http (3.4.2) net-http (~> 0.5) fast_blank (1.0.1) - fastimage (2.4.0) + fastimage (2.4.1) ffi (1.17.3) ffi-compiler (1.3.2) ffi (>= 1.15.5) From d39f8679311c76ddf1ff76cb64fdec1f7ab0c8a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:16:06 +0100 Subject: [PATCH 030/203] New Crowdin Translations (automated) (#38143) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/fr-CA.json | 8 ++++++++ app/javascript/mastodon/locales/fr.json | 8 ++++++++ app/javascript/mastodon/locales/hu.json | 5 +++++ app/javascript/mastodon/locales/ro.json | 12 ++++++++++++ app/javascript/mastodon/locales/tr.json | 8 ++++++++ 5 files changed, 41 insertions(+) diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index a27b980d3e5332..6360c49eeb2f9d 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -338,12 +338,14 @@ "collections.create_collection": "Créer une collection", "collections.delete_collection": "Supprimer la collection", "collections.description_length_hint": "Maximum 100 caractères", + "collections.detail.accept_inclusion": "D'accord", "collections.detail.accounts_heading": "Comptes", "collections.detail.author_added_you": "{author} vous a ajouté·e à cette collection", "collections.detail.curated_by_author": "Organisée par {author}", "collections.detail.curated_by_you": "Organisée par vous", "collections.detail.loading": "Chargement de la collection…", "collections.detail.other_accounts_in_collection": "Autres comptes dans cette collection :", + "collections.detail.revoke_inclusion": "Me retirer", "collections.detail.sensitive_note": "Cette collection contient des comptes et du contenu qui peut être sensibles.", "collections.detail.share": "Partager la collection", "collections.edit_details": "Modifier les détails", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Dernière publication il y a plus d'une semaine", "collections.remove_account": "Supprimer ce compte", "collections.report_collection": "Signaler cette collection", + "collections.revoke_collection_inclusion": "Me retirer de cette collection", + "collections.revoke_inclusion.confirmation": "Vous avez été retiré·e de « {collection} »", + "collections.revoke_inclusion.error": "Une erreur s'est produite, veuillez réessayer plus tard.", "collections.search_accounts_label": "Chercher des comptes à ajouter…", "collections.search_accounts_max_reached": "Vous avez ajouté le nombre maximum de comptes", "collections.sensitive": "Sensible", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Supprimer l'abonné·e", "confirmations.remove_from_followers.message": "{name} cessera de vous suivre. Voulez-vous vraiment continuer ?", "confirmations.remove_from_followers.title": "Supprimer l'abonné·e ?", + "confirmations.revoke_collection_inclusion.confirm": "Me retirer", + "confirmations.revoke_collection_inclusion.message": "Cette action est permanente, la personne qui gère la collection ne pourra plus vous y rajouter plus tard.", + "confirmations.revoke_collection_inclusion.title": "Vous retirer de cette collection ?", "confirmations.revoke_quote.confirm": "Retirer le message", "confirmations.revoke_quote.message": "Cette action ne peut pas être annulée.", "confirmations.revoke_quote.title": "Retirer le message ?", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 1bf320aa01130a..acde4d4a9271d5 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -338,12 +338,14 @@ "collections.create_collection": "Créer une collection", "collections.delete_collection": "Supprimer la collection", "collections.description_length_hint": "Maximum 100 caractères", + "collections.detail.accept_inclusion": "D'accord", "collections.detail.accounts_heading": "Comptes", "collections.detail.author_added_you": "{author} vous a ajouté·e à cette collection", "collections.detail.curated_by_author": "Organisée par {author}", "collections.detail.curated_by_you": "Organisée par vous", "collections.detail.loading": "Chargement de la collection…", "collections.detail.other_accounts_in_collection": "Autres comptes dans cette collection :", + "collections.detail.revoke_inclusion": "Me retirer", "collections.detail.sensitive_note": "Cette collection contient des comptes et du contenu qui peut être sensibles.", "collections.detail.share": "Partager la collection", "collections.edit_details": "Modifier les détails", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Dernière publication il y a plus d'une semaine", "collections.remove_account": "Supprimer ce compte", "collections.report_collection": "Signaler cette collection", + "collections.revoke_collection_inclusion": "Me retirer de cette collection", + "collections.revoke_inclusion.confirmation": "Vous avez été retiré·e de « {collection} »", + "collections.revoke_inclusion.error": "Une erreur s'est produite, veuillez réessayer plus tard.", "collections.search_accounts_label": "Chercher des comptes à ajouter…", "collections.search_accounts_max_reached": "Vous avez ajouté le nombre maximum de comptes", "collections.sensitive": "Sensible", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Supprimer l'abonné·e", "confirmations.remove_from_followers.message": "{name} cessera de vous suivre. Voulez-vous vraiment continuer ?", "confirmations.remove_from_followers.title": "Supprimer l'abonné·e ?", + "confirmations.revoke_collection_inclusion.confirm": "Me retirer", + "confirmations.revoke_collection_inclusion.message": "Cette action est permanente, la personne qui gère la collection ne pourra plus vous y rajouter plus tard.", + "confirmations.revoke_collection_inclusion.title": "Vous retirer de cette collection ?", "confirmations.revoke_quote.confirm": "Retirer le message", "confirmations.revoke_quote.message": "Cette action ne peut pas être annulée.", "confirmations.revoke_quote.title": "Retirer le message ?", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 665cd22df80d5e..4e3c3fb7df9247 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -177,7 +177,10 @@ "account_edit.field_edit_modal.value_label": "Érték", "account_edit.field_reorder_modal.drag_cancel": "Az áthúzás megszakítva. A(z) „{item}” mező el lett dobva.", "account_edit.field_reorder_modal.drag_end": "A(z) „{item}” mező el lett dobva.", + "account_edit.field_reorder_modal.drag_instructions": "Az egyéni mezők átrendezéséhez nyomj Szóközt vagy Entert. Húzás közben használd a nyílgombokat a mező felfelé vagy lefelé mozgatásához. A mező új pozícióba helyezéséhez nyomd meg a Szóközt vagy az Entert, vagy a megszakításhoz nyomd meg az Esc gombot.", "account_edit.field_reorder_modal.drag_move": "A(z) „{item}” mező át lett helyezve.", + "account_edit.field_reorder_modal.drag_over": "A(z) „{item}” mező át lett helyezve ennek a helyére: „{over}”.", + "account_edit.field_reorder_modal.drag_start": "A(z) „{item}” mező áthelyezéshez felvéve.", "account_edit.field_reorder_modal.handle_label": "A(z) „{item}” mező húzása", "account_edit.field_reorder_modal.title": "Mezők átrendezése", "account_edit.name_modal.add_title": "Megjelenítendő név hozzáadása", @@ -194,6 +197,8 @@ "account_edit.profile_tab.subtitle": "Szabd testre a profilodon látható lapokat, és a megjelenített tartalmukat.", "account_edit.profile_tab.title": "Profil lap beállításai", "account_edit.save": "Mentés", + "account_edit.verified_modal.details": "Növeld a Mastodon-profilod hitelességét a személyes webhelyekre mutató hivatkozások ellenőrzésével. Így működik:", + "account_edit.verified_modal.invisible_link.details": "A hivatkozás hozzáadása a fejlécedhez. A fontos rész a rel=\"me\", mely megakadályozza, hogy mások a nevedben lépjenek fel olyan oldalakon, ahol van felhasználók által előállított tartalom. A(z) {tag} helyett a „link” címkét is használhatod az oldal fejlécében, de a HTML-nek elérhetőnek kell lennie JavaScript futtatása nélkül is.", "account_edit.verified_modal.invisible_link.summary": "Hogyan lehet egy hivatkozás láthatatlanná tenni?", "account_edit.verified_modal.step1.header": "Másold a lenti HTML-kódot és illeszd be a webhelyed fejlécébe", "account_edit.verified_modal.step2.details": "Ha már egyéni mezőként hozzáadtad a webhelyedet, akkor törölnöd kell, újból hozzá kell adnod, hogy újra ellenőrizve legyen.", diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json index bdfbb605d4cc70..fd7dfcc56927b2 100644 --- a/app/javascript/mastodon/locales/ro.json +++ b/app/javascript/mastodon/locales/ro.json @@ -1,6 +1,7 @@ { "about.blocks": "Servere moderate", "about.contact": "Contact:", + "about.default_locale": "Standard", "about.disclaimer": "Mastodon este o aplicație gratuită, cu sursă deschisă și o marcă înregistrată a Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Motivul nu este disponibil", "about.domain_blocks.preamble": "Mastodon îți permite în general să vezi conținut de la și să interacționezi cu utilizatori de pe oricare server în fediverse. Acestea sunt excepțiile care au fost făcute pe acest server.", @@ -8,22 +9,33 @@ "about.domain_blocks.silenced.title": "Limitat", "about.domain_blocks.suspended.explanation": "Nicio informație de la acest server nu va fi procesată, stocată sau trimisă, făcând imposibilă orice interacțiune sau comunicare cu utilizatorii de pe acest server.", "about.domain_blocks.suspended.title": "Suspendat", + "about.language_label": "Limbă", "about.not_available": "Această informație nu a fost pusă la dispoziție pe acest server.", "about.powered_by": "Media socială descentralizată furnizată de {mastodon}", "about.rules": "Reguli server", "account.account_note_header": "Notă personală", + "account.activity": "Activități", + "account.add_note": "Adaugă o notă personală", "account.add_or_remove_from_list": "Adaugă sau elimină din liste", + "account.badges.admin": "Admin", + "account.badges.blocked": "Blocat", "account.badges.bot": "Robot", + "account.badges.domain_blocked": "Domeniu blocat", "account.badges.group": "Grup", + "account.badges.muted": "Silențios", + "account.badges.muted_until": "Silențios până la {until}", "account.block": "Blochează pe @{name}", "account.block_domain": "Blochează domeniul {domain}", "account.block_short": "Blochează", "account.blocked": "Blocat", + "account.blocking": "Blocarea", "account.cancel_follow_request": "Retrage cererea de urmărire", "account.copy": "Copiază link-ul profilului", "account.direct": "Menționează pe @{name} în privat", "account.disable_notifications": "Nu îmi mai trimite notificări când postează @{name}", + "account.edit_note": "Editare notă personală", "account.edit_profile": "Modifică profilul", + "account.edit_profile_short": "Editare", "account.enable_notifications": "Trimite-mi o notificare când postează @{name}", "account.endorse": "Promovează pe profil", "account.featured_tags.last_status_at": "Ultima postare pe {date}", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index a6d249925a7e19..40ac1f4deec3ae 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -338,12 +338,14 @@ "collections.create_collection": "Koleksiyon oluştur", "collections.delete_collection": "Koleksiyonu sil", "collections.description_length_hint": "100 karakterle sınırlı", + "collections.detail.accept_inclusion": "Tamam", "collections.detail.accounts_heading": "Hesaplar", "collections.detail.author_added_you": "{author} sizi koleksiyonuna ekledi", "collections.detail.curated_by_author": "{author} tarafından derlenen", "collections.detail.curated_by_you": "Sizin derledikleriniz", "collections.detail.loading": "Koleksiyon yükleniyor…", "collections.detail.other_accounts_in_collection": "Bu koleksiyondaki diğer kişiler:", + "collections.detail.revoke_inclusion": "Beni çıkar", "collections.detail.sensitive_note": "Bu koleksiyon bazı kullanıcılar için hassas olabilecek hesap ve içerik içerebilir.", "collections.detail.share": "Bu koleksiyonu paylaş", "collections.edit_details": "Ayrıntıları düzenle", @@ -359,6 +361,9 @@ "collections.old_last_post_note": "Son gönderi bir haftadan önce", "collections.remove_account": "Bu hesabı çıkar", "collections.report_collection": "Bu koleksiyonu bildir", + "collections.revoke_collection_inclusion": "Beni bu koleksiyondan çıkar", + "collections.revoke_inclusion.confirmation": "\"{collection}\" koleksiyonundan çıkarıldınız", + "collections.revoke_inclusion.error": "Bir hata oluştu, lütfen daha sonra tekrar deneyin.", "collections.search_accounts_label": "Eklemek için hesap arayın…", "collections.search_accounts_max_reached": "Maksimum hesabı eklediniz", "collections.sensitive": "Hassas", @@ -482,6 +487,9 @@ "confirmations.remove_from_followers.confirm": "Takipçi kaldır", "confirmations.remove_from_followers.message": "{name} sizi takip etmeyi bırakacaktır. Devam etmek istediğinize emin misiniz?", "confirmations.remove_from_followers.title": "Takipçiyi kaldır?", + "confirmations.revoke_collection_inclusion.confirm": "Beni çıkar", + "confirmations.revoke_collection_inclusion.message": "Bu eylem kalıcıdır ve koleksiyonu derleyen kişi daha sonra sizi koleksiyona tekrar ekleyemeyecektir.", + "confirmations.revoke_collection_inclusion.title": "Kendini bu koleksiyondan çıkar?", "confirmations.revoke_quote.confirm": "Gönderiyi kaldır", "confirmations.revoke_quote.message": "Bu işlem geri alınamaz.", "confirmations.revoke_quote.title": "Gönderiyi silmek ister misiniz?", From d047a10cf5defc4f87093748461e7376c79fc3b0 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 11 Mar 2026 06:18:24 -0400 Subject: [PATCH 031/203] Use `around_action` to set locale in admin/notification mailers (#38140) --- app/mailers/admin_mailer.rb | 34 ++++++++++++------------------ app/mailers/notification_mailer.rb | 30 +++++++++++--------------- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index cc2a537b3c862e..fe2325b6f37d2e 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -11,30 +11,26 @@ class AdminMailer < ApplicationMailer after_action :set_important_headers!, only: :new_critical_software_updates + around_action :set_locale + default to: -> { @me.user_email } def new_report(report) @report = report - locale_for_account(@me) do - mail subject: default_i18n_subject(instance: @instance, id: @report.id) - end + mail subject: default_i18n_subject(instance: @instance, id: @report.id) end def new_appeal(appeal) @appeal = appeal - locale_for_account(@me) do - mail subject: default_i18n_subject(instance: @instance, username: @appeal.account.username) - end + mail subject: default_i18n_subject(instance: @instance, username: @appeal.account.username) end def new_pending_account(user) @account = user.account - locale_for_account(@me) do - mail subject: default_i18n_subject(instance: @instance, username: @account.username) - end + mail subject: default_i18n_subject(instance: @instance, username: @account.username) end def new_trends(links, tags, statuses) @@ -42,31 +38,23 @@ def new_trends(links, tags, statuses) @tags = tags @statuses = statuses - locale_for_account(@me) do - mail subject: default_i18n_subject(instance: @instance) - end + mail subject: default_i18n_subject(instance: @instance) end def new_software_updates @software_updates = SoftwareUpdate.by_version - locale_for_account(@me) do - mail subject: default_i18n_subject(instance: @instance) - end + mail subject: default_i18n_subject(instance: @instance) end def new_critical_software_updates @software_updates = SoftwareUpdate.urgent.by_version - locale_for_account(@me) do - mail subject: default_i18n_subject(instance: @instance) - end + mail subject: default_i18n_subject(instance: @instance) end def auto_close_registrations - locale_for_account(@me) do - mail subject: default_i18n_subject(instance: @instance) - end + mail subject: default_i18n_subject(instance: @instance) end private @@ -79,6 +67,10 @@ def set_instance @instance = Rails.configuration.x.local_domain end + def set_locale(&block) + locale_for_account(@me, &block) + end + def set_important_headers! headers( 'Importance' => 'high', diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 54dde1bb0dda46..ecb37509686828 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -15,6 +15,8 @@ class NotificationMailer < ApplicationMailer before_deliver :verify_functional_user + around_action :set_locale + default to: -> { email_address_with_name(@user.email, @me.username) } layout 'mailer' @@ -22,45 +24,33 @@ class NotificationMailer < ApplicationMailer def mention return if @status.blank? - locale_for_account(@me) do - mail subject: default_i18n_subject(name: @status.account.acct) - end + mail subject: default_i18n_subject(name: @status.account.acct) end def quote return if @status.blank? - locale_for_account(@me) do - mail subject: default_i18n_subject(name: @status.account.acct) - end + mail subject: default_i18n_subject(name: @status.account.acct) end def follow - locale_for_account(@me) do - mail subject: default_i18n_subject(name: @account.acct) - end + mail subject: default_i18n_subject(name: @account.acct) end def favourite return if @status.blank? - locale_for_account(@me) do - mail subject: default_i18n_subject(name: @account.acct) - end + mail subject: default_i18n_subject(name: @account.acct) end def reblog return if @status.blank? - locale_for_account(@me) do - mail subject: default_i18n_subject(name: @account.acct) - end + mail subject: default_i18n_subject(name: @account.acct) end def follow_request - locale_for_account(@me) do - mail subject: default_i18n_subject(name: @account.acct) - end + mail subject: default_i18n_subject(name: @account.acct) end private @@ -81,6 +71,10 @@ def set_account @account = @notification.from_account end + def set_locale(&block) + locale_for_account(@me, &block) + end + def verify_functional_user throw(:abort) unless @user.functional? end From dc004caf71c30e9b4debebc8b52722a7d37b62aa Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 11 Mar 2026 06:49:07 -0400 Subject: [PATCH 032/203] Convert attempt IP from EmailDomainBlock history tracking to string before recording (#38137) --- app/models/email_domain_block.rb | 2 +- app/models/trends/history.rb | 4 ++-- spec/models/email_domain_block_spec.rb | 10 +++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb index 583d2e6c1b18fa..44d6bc6987a5ca 100644 --- a/app/models/email_domain_block.rb +++ b/app/models/email_domain_block.rb @@ -59,7 +59,7 @@ def invalid_uri? def blocking?(allow_with_approval: false) blocks = EmailDomainBlock.where(domain: domains_with_variants, allow_with_approval: allow_with_approval).by_domain_length - blocks.each { |block| block.history.add(@attempt_ip) } if @attempt_ip.present? + blocks.each { |block| block.history.add(@attempt_ip.to_s) } if @attempt_ip.present? blocks.any? end diff --git a/app/models/trends/history.rb b/app/models/trends/history.rb index 21331f00dca466..9e4d173475a717 100644 --- a/app/models/trends/history.rb +++ b/app/models/trends/history.rb @@ -40,11 +40,11 @@ def uses with_redis { |redis| redis.get(key_for(:uses)).to_i } end - def add(account_id) + def add(value) with_redis do |redis| redis.pipelined do |pipeline| pipeline.incrby(key_for(:uses), 1) - pipeline.pfadd(key_for(:accounts), account_id) + pipeline.pfadd(key_for(:accounts), value) pipeline.expire(key_for(:uses), EXPIRE_AFTER) pipeline.expire(key_for(:accounts), EXPIRE_AFTER) end diff --git a/spec/models/email_domain_block_spec.rb b/spec/models/email_domain_block_spec.rb index c3662b2d6cd83f..5dbc4a5aff5c69 100644 --- a/spec/models/email_domain_block_spec.rb +++ b/spec/models/email_domain_block_spec.rb @@ -56,16 +56,20 @@ end describe '.requires_approval?' do - subject { described_class.requires_approval?(input) } + subject { described_class.requires_approval?(input, attempt_ip: IPAddr.new('100.100.100.100')) } let(:input) { nil } context 'with a matching block requiring approval' do - before { Fabricate :email_domain_block, domain: input, allow_with_approval: true } + let!(:email_domain_block) { Fabricate :email_domain_block, domain: input, allow_with_approval: true } let(:input) { 'host.example' } - it { is_expected.to be true } + it 'returns true and records attempt' do + expect do + expect(subject).to be(true) + end.to change { email_domain_block.history.get(Date.current).accounts }.by(1) + end end context 'with a matching block not requiring approval' do From da4b717211656f8710fb7194c538864db589253a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:53:38 +0100 Subject: [PATCH 033/203] Update dependency rspec-rails to v8.0.4 (#38146) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 65abef60ffa585..68b7cb0a6092fa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -738,17 +738,17 @@ GEM rspec-support (~> 3.13.0) rspec-github (3.0.0) rspec-core (~> 3.0) - rspec-mocks (3.13.7) + rspec-mocks (3.13.8) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (8.0.3) + rspec-rails (8.0.4) actionpack (>= 7.2) activesupport (>= 7.2) railties (>= 7.2) - rspec-core (~> 3.13) - rspec-expectations (~> 3.13) - rspec-mocks (~> 3.13) - rspec-support (~> 3.13) + rspec-core (>= 3.13.0, < 5.0.0) + rspec-expectations (>= 3.13.0, < 5.0.0) + rspec-mocks (>= 3.13.0, < 5.0.0) + rspec-support (>= 3.13.0, < 5.0.0) rspec-sidekiq (5.3.0) rspec-core (~> 3.0) rspec-expectations (~> 3.0) From f971670c620db61da4dadff07efe86b51c145bfd Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 11 Mar 2026 13:09:54 +0100 Subject: [PATCH 034/203] Profile editing: Fix bug with reordering (#38147) --- .../features/account_edit/modals/fields_reorder_modal.tsx | 6 ++---- app/javascript/mastodon/reducers/slices/profile_edit.ts | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/account_edit/modals/fields_reorder_modal.tsx b/app/javascript/mastodon/features/account_edit/modals/fields_reorder_modal.tsx index 5eee431a27214d..8a94c99ac28a1d 100644 --- a/app/javascript/mastodon/features/account_edit/modals/fields_reorder_modal.tsx +++ b/app/javascript/mastodon/features/account_edit/modals/fields_reorder_modal.tsx @@ -212,11 +212,9 @@ export const ReorderFieldsModal: FC = ({ onClose }) => { return; } newFields.push({ name: field.name, value: field.value }); - - void dispatch(patchProfile({ fields_attributes: newFields })).then( - onClose, - ); } + + void dispatch(patchProfile({ fields_attributes: newFields })).then(onClose); }, [dispatch, fieldKeys, fields, onClose]); const emojis = useAppSelector((state) => state.custom_emojis); diff --git a/app/javascript/mastodon/reducers/slices/profile_edit.ts b/app/javascript/mastodon/reducers/slices/profile_edit.ts index e4840c642dedf9..62a908e5b12f34 100644 --- a/app/javascript/mastodon/reducers/slices/profile_edit.ts +++ b/app/javascript/mastodon/reducers/slices/profile_edit.ts @@ -221,7 +221,12 @@ export const patchProfile = createDataLoadingThunk( `${profileEditSlice.name}/patchProfile`, (params: Partial) => apiPatchProfile(params), transformProfile, - { useLoadingBar: false }, + { + useLoadingBar: false, + condition(_, { getState }) { + return !getState().profileEdit.isPending; + }, + }, ); export const selectFieldById = createAppSelector( From 12c6c6dcf9a6875db03dbb9d66eed897c1d964e3 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 11 Mar 2026 14:19:39 +0100 Subject: [PATCH 035/203] Profile editing: Add warning for links (#38148) --- .../account_edit/modals/fields_modals.tsx | 20 +++++++++++++- app/javascript/mastodon/locales/en.json | 3 ++- app/javascript/mastodon/utils/checks.test.ts | 21 +++++++++++++++ app/javascript/mastodon/utils/checks.ts | 26 +++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 app/javascript/mastodon/utils/checks.test.ts diff --git a/app/javascript/mastodon/features/account_edit/modals/fields_modals.tsx b/app/javascript/mastodon/features/account_edit/modals/fields_modals.tsx index c7b3b6ebc54fa6..b5a095cf68b26d 100644 --- a/app/javascript/mastodon/features/account_edit/modals/fields_modals.tsx +++ b/app/javascript/mastodon/features/account_edit/modals/fields_modals.tsx @@ -18,6 +18,7 @@ import { useAppDispatch, useAppSelector, } from '@/mastodon/store'; +import { isUrlWithoutProtocol } from '@/mastodon/utils/checks'; import { ConfirmationModal } from '../../ui/components/confirmation_modals'; import type { DialogModalProps } from '../../ui/components/dialog_modal'; @@ -48,7 +49,7 @@ const messages = defineMessages({ }, editValueHint: { id: 'account_edit.field_edit_modal.value_hint', - defaultMessage: 'E.g. “example.me”', + defaultMessage: 'E.g. “https://example.me”', }, limitHeader: { id: 'account_edit.field_edit_modal.limit_header', @@ -109,6 +110,10 @@ export const EditFieldModal: FC = ({ ); return hasLink && hasEmoji; }, [customEmojiCodes, newLabel, newValue]); + const hasLinkWithoutProtocol = useMemo( + () => isUrlWithoutProtocol(newValue), + [newValue], + ); const dispatch = useAppDispatch(); const handleSave = useCallback(() => { @@ -175,6 +180,19 @@ export const EditFieldModal: FC = ({ /> )} + + {hasLinkWithoutProtocol && ( + + https://, + }} + /> + + )}
); }; diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 57b868c848b8e9..2ede5449d14ed8 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "We recommend against the use of custom emoji in combination with urls. Custom fields containing both will display as text only instead of as a link, in order to prevent user confusion.", "account_edit.field_edit_modal.name_hint": "E.g. “Personal website”", "account_edit.field_edit_modal.name_label": "Label", - "account_edit.field_edit_modal.value_hint": "E.g. “example.me”", + "account_edit.field_edit_modal.url_warning": "To add a link, please include {protocol} at the beginning.", + "account_edit.field_edit_modal.value_hint": "E.g. “https://example.me”", "account_edit.field_edit_modal.value_label": "Value", "account_edit.field_reorder_modal.drag_cancel": "Dragging was cancelled. Field \"{item}\" was dropped.", "account_edit.field_reorder_modal.drag_end": "Field \"{item}\" was dropped.", diff --git a/app/javascript/mastodon/utils/checks.test.ts b/app/javascript/mastodon/utils/checks.test.ts new file mode 100644 index 00000000000000..862a2a0abfedd4 --- /dev/null +++ b/app/javascript/mastodon/utils/checks.test.ts @@ -0,0 +1,21 @@ +import { isUrlWithoutProtocol } from './checks'; + +describe('isUrlWithoutProtocol', () => { + test.concurrent.each([ + ['example.com', true], + ['sub.domain.co.uk', true], + ['example', false], // No dot + ['example..com', false], // Consecutive dots + ['example.com.', false], // Trailing dot + ['example.c', false], // TLD too short + ['example.123', false], // Numeric TLDs are not valid + ['example.com/path', true], // Paths are allowed + ['example.com?query=string', true], // Query strings are allowed + ['example.com#fragment', true], // Fragments are allowed + ['example .com', false], // Spaces are not allowed + ['example://com', false], // Protocol inside the string is not allowed + ['example.com^', false], // Invalid characters not allowed + ])('should return %s for input "%s"', (input, expected) => { + expect(isUrlWithoutProtocol(input)).toBe(expected); + }); +}); diff --git a/app/javascript/mastodon/utils/checks.ts b/app/javascript/mastodon/utils/checks.ts index 8b05ac24a7feda..d5d528bdc6eb08 100644 --- a/app/javascript/mastodon/utils/checks.ts +++ b/app/javascript/mastodon/utils/checks.ts @@ -9,3 +9,29 @@ export function isValidUrl( return false; } } + +/** + * Checks if the input string is probably a URL without a protocol. Note this is not full URL validation, + * and is mostly used to detect link-like inputs. + * @see https://www.xjavascript.com/blog/check-if-a-javascript-string-is-a-url/ + * @param input The input string to check + */ +export function isUrlWithoutProtocol(input: string): boolean { + if (!input.length || input.includes(' ') || input.includes('://')) { + return false; + } + + try { + const url = new URL(`http://${input}`); + const { host } = url; + return ( + host !== '' && // Host is not empty + host.includes('.') && // Host contains at least one dot + !host.endsWith('.') && // No trailing dot + !host.includes('..') && // No consecutive dots + /\.[\w]{2,}$/.test(host) // TLD is at least 2 characters + ); + } catch {} + + return false; +} From 20932752fee1dc3da856fdb9fadc8df5aaae23f1 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 11 Mar 2026 14:20:56 +0100 Subject: [PATCH 036/203] Refactor collection editor state handling (#38133) --- .../features/collections/detail/index.tsx | 11 ++- .../collections/detail/share_modal.tsx | 2 +- .../features/collections/editor/accounts.tsx | 84 +++++++++--------- .../features/collections/editor/details.tsx | 86 ++++++++++--------- .../features/collections/editor/index.tsx | 20 ++++- .../features/collections/editor/state.ts | 52 ----------- .../mastodon/reducers/slices/collections.ts | 65 +++++++++++++- 7 files changed, 180 insertions(+), 140 deletions(-) delete mode 100644 app/javascript/mastodon/features/collections/editor/state.ts diff --git a/app/javascript/mastodon/features/collections/detail/index.tsx b/app/javascript/mastodon/features/collections/detail/index.tsx index 9870e44bc68868..8db00e73d34813 100644 --- a/app/javascript/mastodon/features/collections/detail/index.tsx +++ b/app/javascript/mastodon/features/collections/detail/index.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { Helmet } from 'react-helmet'; -import { useLocation, useParams } from 'react-router'; +import { useHistory, useLocation, useParams } from 'react-router'; import { openModal } from '@/mastodon/actions/modal'; import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react'; @@ -84,6 +84,7 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({ const intl = useIntl(); const { name, description, tag, account_id } = collection; const dispatch = useAppDispatch(); + const history = useHistory(); const handleShare = useCallback(() => { dispatch( @@ -97,12 +98,14 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({ }, [collection, dispatch]); const location = useLocation<{ newCollection?: boolean } | undefined>(); - const wasJustCreated = location.state?.newCollection; + const isNewCollection = location.state?.newCollection; useEffect(() => { - if (wasJustCreated) { + if (isNewCollection) { + // Replace with current pathname to clear `newCollection` state + history.replace(location.pathname); handleShare(); } - }, [handleShare, wasJustCreated]); + }, [history, handleShare, isNewCollection, location.pathname]); return (
diff --git a/app/javascript/mastodon/features/collections/detail/share_modal.tsx b/app/javascript/mastodon/features/collections/detail/share_modal.tsx index 0f4681d07762a3..26bab6abe00c88 100644 --- a/app/javascript/mastodon/features/collections/detail/share_modal.tsx +++ b/app/javascript/mastodon/features/collections/detail/share_modal.tsx @@ -64,7 +64,7 @@ export const CollectionShareModal: React.FC<{ onClose(); dispatch(changeCompose(shareMessage)); dispatch(focusCompose()); - }, [collectionLink, dispatch, intl, isOwnCollection, onClose]); + }, [onClose, collectionLink, dispatch, intl, isOwnCollection]); return ( diff --git a/app/javascript/mastodon/features/collections/editor/accounts.tsx b/app/javascript/mastodon/features/collections/editor/accounts.tsx index 47af9e211c0e62..423b72e628a163 100644 --- a/app/javascript/mastodon/features/collections/editor/accounts.tsx +++ b/app/javascript/mastodon/features/collections/editor/accounts.tsx @@ -2,7 +2,7 @@ import { useCallback, useId, useMemo, useState } from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; -import { useHistory, useLocation } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import CancelIcon from '@/material-icons/400-24px/cancel.svg?react'; import CheckIcon from '@/material-icons/400-24px/check.svg?react'; @@ -30,12 +30,12 @@ import { useAccount } from 'mastodon/hooks/useAccount'; import { me } from 'mastodon/initial_state'; import { addCollectionItem, + getCollectionItemIds, removeCollectionItem, + updateCollectionEditorField, } from 'mastodon/reducers/slices/collections'; import { store, useAppDispatch, useAppSelector } from 'mastodon/store'; -import type { TempCollectionState } from './state'; -import { getCollectionEditorState } from './state'; import classes from './styles.module.scss'; import { WizardStepHeader } from './wizard_step_header'; @@ -52,9 +52,8 @@ function isOlderThanAWeek(date?: string): boolean { const AddedAccountItem: React.FC<{ accountId: string; - isRemovable: boolean; onRemove: (id: string) => void; -}> = ({ accountId, isRemovable, onRemove }) => { +}> = ({ accountId, onRemove }) => { const intl = useIntl(); const account = useAccount(accountId); @@ -86,17 +85,15 @@ const AddedAccountItem: React.FC<{ id={accountId} extraAccountInfo={lastPostHint} > - {isRemovable && ( - - )} + ); }; @@ -139,28 +136,25 @@ export const CollectionAccounts: React.FC<{ const intl = useIntl(); const dispatch = useAppDispatch(); const history = useHistory(); - const location = useLocation(); - const { id, initialItemIds } = getCollectionEditorState( - collection, - location.state, - ); + + const { id, items } = collection ?? {}; const isEditMode = !!id; - const collectionItems = collection?.items; + const collectionItems = items; - const [searchValue, setSearchValue] = useState(''); - // This state is only used when creating a new collection. - // In edit mode, the collection will be updated instantly - const [addedAccountIds, setAccountIds] = useState(initialItemIds); + const addedAccountIds = useAppSelector( + (state) => state.collections.editor.accountIds, + ); + + // In edit mode, we're bypassing state and just return collection items directly, + // since they're edited "live", saving after each addition/deletion const accountIds = useMemo( () => - isEditMode - ? (collectionItems - ?.map((item) => item.account_id) - .filter((id): id is string => !!id) ?? []) - : addedAccountIds, + isEditMode ? getCollectionItemIds(collectionItems) : addedAccountIds, [isEditMode, collectionItems, addedAccountIds], ); + const [searchValue, setSearchValue] = useState(''); + const hasMaxAccounts = accountIds.length === MAX_ACCOUNT_COUNT; const { @@ -233,28 +227,41 @@ export const CollectionAccounts: React.FC<{ [dispatch, relationships], ); - const removeAccountItem = useCallback((accountId: string) => { - setAccountIds((ids) => ids.filter((id) => id !== accountId)); - }, []); + const removeAccountItem = useCallback( + (accountId: string) => { + dispatch( + updateCollectionEditorField({ + field: 'accountIds', + value: accountIds.filter((id) => id !== accountId), + }), + ); + }, + [accountIds, dispatch], + ); const addAccountItem = useCallback( (accountId: string) => { confirmFollowStatus(accountId, () => { - setAccountIds((ids) => [...ids, accountId]); + dispatch( + updateCollectionEditorField({ + field: 'accountIds', + value: [...accountIds, accountId], + }), + ); }); }, - [confirmFollowStatus], + [accountIds, confirmFollowStatus, dispatch], ); const toggleAccountItem = useCallback( (item: SuggestionItem) => { - if (addedAccountIds.includes(item.id)) { + if (accountIds.includes(item.id)) { removeAccountItem(item.id); } else { addAccountItem(item.id); } }, - [addAccountItem, addedAccountIds, removeAccountItem], + [accountIds, addAccountItem, removeAccountItem], ); const instantRemoveAccountItem = useCallback( @@ -406,7 +413,6 @@ export const CollectionAccounts: React.FC<{ > diff --git a/app/javascript/mastodon/features/collections/editor/details.tsx b/app/javascript/mastodon/features/collections/editor/details.tsx index 6234bca5142713..875d09c9ebbabb 100644 --- a/app/javascript/mastodon/features/collections/editor/details.tsx +++ b/app/javascript/mastodon/features/collections/editor/details.tsx @@ -1,13 +1,12 @@ -import { useCallback, useState } from 'react'; +import { useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; -import { useHistory, useLocation } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { isFulfilled } from '@reduxjs/toolkit'; import type { - ApiCollectionJSON, ApiCreateCollectionPayload, ApiUpdateCollectionPayload, } from 'mastodon/api_types/collections'; @@ -23,70 +22,77 @@ import { TextInputField } from 'mastodon/components/form_fields/text_input_field import { createCollection, updateCollection, + updateCollectionEditorField, } from 'mastodon/reducers/slices/collections'; -import { useAppDispatch } from 'mastodon/store'; +import { useAppDispatch, useAppSelector } from 'mastodon/store'; -import type { TempCollectionState } from './state'; -import { getCollectionEditorState } from './state'; import classes from './styles.module.scss'; import { WizardStepHeader } from './wizard_step_header'; -export const CollectionDetails: React.FC<{ - collection?: ApiCollectionJSON | null; -}> = ({ collection }) => { +export const CollectionDetails: React.FC = () => { const dispatch = useAppDispatch(); const history = useHistory(); - const location = useLocation(); - - const { - id, - initialName, - initialDescription, - initialTopic, - initialItemIds, - initialDiscoverable, - initialSensitive, - } = getCollectionEditorState(collection, location.state); - - const [name, setName] = useState(initialName); - const [description, setDescription] = useState(initialDescription); - const [topic, setTopic] = useState(initialTopic); - const [discoverable, setDiscoverable] = useState(initialDiscoverable); - const [sensitive, setSensitive] = useState(initialSensitive); + const { id, name, description, topic, discoverable, sensitive, accountIds } = + useAppSelector((state) => state.collections.editor); const handleNameChange = useCallback( (event: React.ChangeEvent) => { - setName(event.target.value); + dispatch( + updateCollectionEditorField({ + field: 'name', + value: event.target.value, + }), + ); }, - [], + [dispatch], ); const handleDescriptionChange = useCallback( (event: React.ChangeEvent) => { - setDescription(event.target.value); + dispatch( + updateCollectionEditorField({ + field: 'description', + value: event.target.value, + }), + ); }, - [], + [dispatch], ); const handleTopicChange = useCallback( (event: React.ChangeEvent) => { - setTopic(event.target.value); + dispatch( + updateCollectionEditorField({ + field: 'topic', + value: event.target.value, + }), + ); }, - [], + [dispatch], ); const handleDiscoverableChange = useCallback( (event: React.ChangeEvent) => { - setDiscoverable(event.target.value === 'public'); + dispatch( + updateCollectionEditorField({ + field: 'discoverable', + value: event.target.value === 'public', + }), + ); }, - [], + [dispatch], ); const handleSensitiveChange = useCallback( (event: React.ChangeEvent) => { - setSensitive(event.target.checked); + dispatch( + updateCollectionEditorField({ + field: 'sensitive', + value: event.target.checked, + }), + ); }, - [], + [dispatch], ); const handleSubmit = useCallback( @@ -112,7 +118,7 @@ export const CollectionDetails: React.FC<{ description, discoverable, sensitive, - account_ids: initialItemIds, + account_ids: accountIds, }; if (topic) { payload.tag_name = topic; @@ -124,9 +130,7 @@ export const CollectionDetails: React.FC<{ }), ).then((result) => { if (isFulfilled(result)) { - history.replace( - `/collections/${result.payload.collection.id}/edit/details`, - ); + history.replace(`/collections`); history.push(`/collections/${result.payload.collection.id}`, { newCollection: true, }); @@ -143,7 +147,7 @@ export const CollectionDetails: React.FC<{ sensitive, dispatch, history, - initialItemIds, + accountIds, ], ); diff --git a/app/javascript/mastodon/features/collections/editor/index.tsx b/app/javascript/mastodon/features/collections/editor/index.tsx index 2200bccb1751b8..ff1549b9423db3 100644 --- a/app/javascript/mastodon/features/collections/editor/index.tsx +++ b/app/javascript/mastodon/features/collections/editor/index.tsx @@ -16,7 +16,10 @@ import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react'; import { Column } from 'mastodon/components/column'; import { ColumnHeader } from 'mastodon/components/column_header'; import { LoadingIndicator } from 'mastodon/components/loading_indicator'; -import { fetchCollection } from 'mastodon/reducers/slices/collections'; +import { + collectionEditorActions, + fetchCollection, +} from 'mastodon/reducers/slices/collections'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { CollectionAccounts } from './accounts'; @@ -68,6 +71,7 @@ export const CollectionEditorPage: React.FC<{ const collection = useAppSelector((state) => id ? state.collections.collections[id] : undefined, ); + const editorStateId = useAppSelector((state) => state.collections.editor.id); const isEditMode = !!id; const isLoading = isEditMode && !collection; @@ -77,6 +81,18 @@ export const CollectionEditorPage: React.FC<{ } }, [dispatch, id]); + useEffect(() => { + if (id !== editorStateId) { + void dispatch(collectionEditorActions.reset()); + } + }, [dispatch, editorStateId, id]); + + useEffect(() => { + if (collection) { + void dispatch(collectionEditorActions.init(collection)); + } + }, [dispatch, collection]); + const pageTitle = intl.formatMessage(usePageTitle(id)); return ( @@ -104,7 +120,7 @@ export const CollectionEditorPage: React.FC<{ exact path={`${path}/details`} // eslint-disable-next-line react/jsx-no-bind - render={() => } + render={() => } /> )} diff --git a/app/javascript/mastodon/features/collections/editor/state.ts b/app/javascript/mastodon/features/collections/editor/state.ts deleted file mode 100644 index abac0b94b54c90..00000000000000 --- a/app/javascript/mastodon/features/collections/editor/state.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { - ApiCollectionJSON, - ApiCreateCollectionPayload, -} from '@/mastodon/api_types/collections'; - -/** - * Temporary editor state across creation steps, - * kept in location state - */ -export type TempCollectionState = - | Partial - | undefined; - -/** - * Resolve initial editor state. Temporary location state - * trumps stored data, otherwise initial values are returned. - */ -export function getCollectionEditorState( - collection: ApiCollectionJSON | null | undefined, - locationState: TempCollectionState, -) { - const { - id, - name = '', - description = '', - tag, - language = '', - discoverable = true, - sensitive = false, - items, - } = collection ?? {}; - - const collectionItemIds = - items?.map((item) => item.account_id).filter(onlyExistingIds) ?? []; - - const initialItemIds = ( - locationState?.account_ids ?? collectionItemIds - ).filter(onlyExistingIds); - - return { - id, - initialItemIds, - initialName: locationState?.name ?? name, - initialDescription: locationState?.description ?? description, - initialTopic: locationState?.tag_name ?? tag?.name ?? '', - initialLanguage: locationState?.language ?? language, - initialDiscoverable: locationState?.discoverable ?? discoverable, - initialSensitive: locationState?.sensitive ?? sensitive, - }; -} - -const onlyExistingIds = (id?: string): id is string => !!id; diff --git a/app/javascript/mastodon/reducers/slices/collections.ts b/app/javascript/mastodon/reducers/slices/collections.ts index 127794b47820c6..dc20b9873231d4 100644 --- a/app/javascript/mastodon/reducers/slices/collections.ts +++ b/app/javascript/mastodon/reducers/slices/collections.ts @@ -1,3 +1,4 @@ +import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import { importFetchedAccounts } from '@/mastodon/actions/importer'; @@ -36,17 +37,69 @@ interface CollectionState { status: QueryStatus; } >; + editor: { + id: string | undefined; + name: string; + description: string; + topic: string; + language: string | null; + discoverable: boolean; + sensitive: boolean; + accountIds: string[]; + }; +} + +type EditorField = CollectionState['editor']; + +interface UpdateEditorFieldPayload { + field: K; + value: EditorField[K]; } const initialState: CollectionState = { collections: {}, accountCollections: {}, + editor: { + id: undefined, + name: '', + description: '', + topic: '', + language: null, + discoverable: true, + sensitive: false, + accountIds: [], + }, }; const collectionSlice = createSlice({ name: 'collections', initialState, - reducers: {}, + reducers: { + init(state, action: PayloadAction) { + const collection = action.payload; + + state.editor = { + id: collection?.id, + name: collection?.name ?? '', + description: collection?.description ?? '', + topic: collection?.tag?.name ?? '', + language: collection?.language ?? '', + discoverable: collection?.discoverable ?? true, + sensitive: collection?.sensitive ?? false, + accountIds: getCollectionItemIds(collection?.items ?? []), + }; + }, + reset(state) { + state.editor = initialState.editor; + }, + updateEditorField( + state: CollectionState, + action: PayloadAction>, + ) { + const { field, value } = action.payload; + state.editor[field] = value; + }, + }, extraReducers(builder) { /** * Fetching account collections @@ -104,6 +157,7 @@ const collectionSlice = createSlice({ builder.addCase(updateCollection.fulfilled, (state, action) => { const { collection } = action.payload; state.collections[collection.id] = collection; + state.editor = initialState.editor; }); /** @@ -132,6 +186,7 @@ const collectionSlice = createSlice({ const { collection } = actions.payload; state.collections[collection.id] = collection; + state.editor = initialState.editor; if (state.accountCollections[collection.account_id]) { state.accountCollections[collection.account_id]?.collectionIds.unshift( @@ -240,6 +295,9 @@ export const revokeCollectionInclusion = createAppAsyncThunk( ); export const collections = collectionSlice.reducer; +export const collectionEditorActions = collectionSlice.actions; +export const updateCollectionEditorField = + collectionSlice.actions.updateEditorField; /** * Selectors @@ -278,3 +336,8 @@ export const selectAccountCollections = createAppSelector( } satisfies AccountCollectionQuery; }, ); + +const onlyExistingIds = (id?: string): id is string => !!id; + +export const getCollectionItemIds = (items?: ApiCollectionJSON['items']) => + items?.map((item) => item.account_id).filter(onlyExistingIds) ?? []; From 4a08ab64d1d22e0572e85a2ace18347ec00b2ff6 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 11 Mar 2026 14:52:44 +0100 Subject: [PATCH 037/203] Profile editing: Always show field buttons (#38152) --- .../mastodon/features/account_edit/index.tsx | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/app/javascript/mastodon/features/account_edit/index.tsx b/app/javascript/mastodon/features/account_edit/index.tsx index 43a13f612a1302..7dc2397f8b4130 100644 --- a/app/javascript/mastodon/features/account_edit/index.tsx +++ b/app/javascript/mastodon/features/account_edit/index.tsx @@ -205,24 +205,21 @@ export const AccountEdit: FC = () => { showDescription={!hasFields} buttons={ <> - {profile.fields.length > 1 && ( - - )} - {hasFields && ( - = maxFieldCount} + + = maxFieldCount} + /> } > From 3091e2e52527930cec91dbf4cdd921a80def5ec9 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Wed, 11 Mar 2026 15:29:00 +0100 Subject: [PATCH 038/203] Ingestion of remote collections (#38144) --- FEDERATION.md | 2 + app/models/collection.rb | 16 +++- app/serializers/rest/collection_serializer.rb | 4 +- .../process_featured_collection_service.rb | 52 +++++++++++++ .../process_featured_item_worker.rb | 16 ++++ spec/models/collection_spec.rb | 8 ++ .../rest/collection_serializer_spec.rb | 9 +++ ...rocess_featured_collection_service_spec.rb | 76 +++++++++++++++++++ .../process_featured_item_worker_spec.rb | 25 ++++++ 9 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 app/services/activitypub/process_featured_collection_service.rb create mode 100644 app/workers/activitypub/process_featured_item_worker.rb create mode 100644 spec/services/activitypub/process_featured_collection_service_spec.rb create mode 100644 spec/workers/activitypub/process_featured_item_worker_spec.rb diff --git a/FEDERATION.md b/FEDERATION.md index 2d007dada12877..7593d6d953d83e 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -69,3 +69,5 @@ The following table summarizes those limits. | Account aliases (actor `alsoKnownAs`) | 256 | List will be truncated | | Custom emoji shortcode (`Emoji` `name`) | 2048 | Emoji will be rejected | | Media and avatar/header descriptions (`name`/`summary`) | 1500 | Description will be truncated | +| Collection name (`FeaturedCollection` `name`) | 256 | Name will be truncated | +| Collection description (`FeaturedCollection` `summary`) | 2048 | Description will be truncated | diff --git a/app/models/collection.rb b/app/models/collection.rb index 0061e7ff5c747f..3be633bbf1f7ac 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -22,6 +22,8 @@ # class Collection < ApplicationRecord MAX_ITEMS = 25 + NAME_LENGTH_HARD_LIMIT = 256 + DESCRIPTION_LENGTH_HARD_LIMIT = 2048 belongs_to :account belongs_to :tag, optional: true @@ -31,10 +33,16 @@ class Collection < ApplicationRecord has_many :collection_reports, dependent: :delete_all validates :name, presence: true - validates :description, presence: true, - if: :local? - validates :description_html, presence: true, - if: :remote? + validates :name, length: { maximum: 40 }, if: :local? + validates :name, length: { maximum: NAME_LENGTH_HARD_LIMIT }, if: :remote? + validates :description, + presence: true, + length: { maximum: 100 }, + if: :local? + validates :description_html, + presence: true, + length: { maximum: DESCRIPTION_LENGTH_HARD_LIMIT }, + if: :remote? validates :local, inclusion: [true, false] validates :sensitive, inclusion: [true, false] validates :discoverable, inclusion: [true, false] diff --git a/app/serializers/rest/collection_serializer.rb b/app/serializers/rest/collection_serializer.rb index 370384c22011ab..ac7c8ad0267f47 100644 --- a/app/serializers/rest/collection_serializer.rb +++ b/app/serializers/rest/collection_serializer.rb @@ -14,7 +14,9 @@ def id end def description - object.local? ? object.description : object.description_html + return object.description if object.local? + + Sanitize.fragment(object.description_html, Sanitize::Config::MASTODON_STRICT) end def items diff --git a/app/services/activitypub/process_featured_collection_service.rb b/app/services/activitypub/process_featured_collection_service.rb new file mode 100644 index 00000000000000..edbb50c5335451 --- /dev/null +++ b/app/services/activitypub/process_featured_collection_service.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class ActivityPub::ProcessFeaturedCollectionService + include JsonLdHelper + include Lockable + include Redisable + + ITEMS_LIMIT = 150 + + def call(account, json) + @account = account + @json = json + return if non_matching_uri_hosts?(@account.uri, @json['id']) + + with_redis_lock("collection:#{@json['id']}") do + return if @account.collections.exists?(uri: @json['id']) + + @collection = @account.collections.create!( + local: false, + uri: @json['id'], + name: (@json['name'] || '')[0, Collection::NAME_LENGTH_HARD_LIMIT], + description_html: truncated_summary, + language:, + sensitive: @json['sensitive'], + discoverable: @json['discoverable'], + original_number_of_items: @json['totalItems'] || 0, + tag_name: @json.dig('topic', 'name') + ) + + process_items! + + @collection + end + end + + private + + def truncated_summary + text = @json['summaryMap']&.values&.first || @json['summary'] || '' + text[0, Collection::DESCRIPTION_LENGTH_HARD_LIMIT] + end + + def language + @json['summaryMap']&.keys&.first + end + + def process_items! + @json['orderedItems'].take(ITEMS_LIMIT).each do |item_json| + ActivityPub::ProcessFeaturedItemWorker.perform_async(@collection.id, item_json) + end + end +end diff --git a/app/workers/activitypub/process_featured_item_worker.rb b/app/workers/activitypub/process_featured_item_worker.rb new file mode 100644 index 00000000000000..dd765e7df6d507 --- /dev/null +++ b/app/workers/activitypub/process_featured_item_worker.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class ActivityPub::ProcessFeaturedItemWorker + include Sidekiq::Worker + include ExponentialBackoff + + sidekiq_options queue: 'pull', retry: 3 + + def perform(collection_id, id_or_json) + collection = Collection.find(collection_id) + + ActivityPub::ProcessFeaturedItemService.new.call(collection, id_or_json) + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index fc833d354b3b0f..6937829ebbac50 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -8,8 +8,12 @@ it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_length_of(:name).is_at_most(40) } + it { is_expected.to validate_presence_of(:description) } + it { is_expected.to validate_length_of(:description).is_at_most(100) } + it { is_expected.to_not allow_value(nil).for(:local) } it { is_expected.to_not allow_value(nil).for(:sensitive) } @@ -23,10 +27,14 @@ context 'when collection is remote' do subject { Fabricate.build :collection, local: false } + it { is_expected.to validate_length_of(:name).is_at_most(Collection::NAME_LENGTH_HARD_LIMIT) } + it { is_expected.to_not validate_presence_of(:description) } it { is_expected.to validate_presence_of(:description_html) } + it { is_expected.to validate_length_of(:description_html).is_at_most(Collection::DESCRIPTION_LENGTH_HARD_LIMIT) } + it { is_expected.to validate_presence_of(:uri) } it { is_expected.to validate_presence_of(:original_number_of_items) } diff --git a/spec/serializers/rest/collection_serializer_spec.rb b/spec/serializers/rest/collection_serializer_spec.rb index 816b1873f6b15f..67ff464d187fa5 100644 --- a/spec/serializers/rest/collection_serializer_spec.rb +++ b/spec/serializers/rest/collection_serializer_spec.rb @@ -51,5 +51,14 @@ expect(subject) .to include('description' => '

remote

') end + + context 'when the description contains unwanted HTML' do + let(:description_html) { '

Nice people

' } + let(:collection) { Fabricate(:remote_collection, description_html:) } + + it 'scrubs the HTML' do + expect(subject).to include('description' => '

Nice people

') + end + end end end diff --git a/spec/services/activitypub/process_featured_collection_service_spec.rb b/spec/services/activitypub/process_featured_collection_service_spec.rb new file mode 100644 index 00000000000000..3a0fdd82f1db1c --- /dev/null +++ b/spec/services/activitypub/process_featured_collection_service_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::ProcessFeaturedCollectionService do + subject { described_class.new } + + let(:account) { Fabricate(:remote_account) } + let(:summary) { '

A list of remote actors you should follow.

' } + let(:base_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => 'https://example.com/featured_collections/1', + 'type' => 'FeaturedCollection', + 'attributedTo' => account.uri, + 'name' => 'Good people from other servers', + 'sensitive' => false, + 'discoverable' => true, + 'topic' => { + 'type' => 'Hashtag', + 'name' => '#people', + }, + 'published' => '2026-03-09T15:19:25Z', + 'totalItems' => 2, + 'orderedItems' => [ + 'https://example.com/featured_items/1', + 'https://example.com/featured_items/2', + ], + } + end + let(:featured_collection_json) { base_json.merge('summary' => summary) } + + context "when the collection's URI does not match the account's" do + let(:non_matching_account) { Fabricate(:remote_account, domain: 'other.example.com') } + + it 'does not create a collection and returns `nil`' do + expect do + expect(subject.call(non_matching_account, featured_collection_json)).to be_nil + end.to_not change(Collection, :count) + end + end + + context 'when URIs match up' do + it 'creates a collection and queues jobs to handle its items' do + expect { subject.call(account, featured_collection_json) }.to change(account.collections, :count).by(1) + + new_collection = account.collections.last + expect(new_collection.uri).to eq 'https://example.com/featured_collections/1' + expect(new_collection.name).to eq 'Good people from other servers' + expect(new_collection.description_html).to eq '

A list of remote actors you should follow.

' + expect(new_collection.sensitive).to be false + expect(new_collection.discoverable).to be true + expect(new_collection.tag.formatted_name).to eq '#people' + + expect(ActivityPub::ProcessFeaturedItemWorker).to have_enqueued_sidekiq_job.exactly(2).times + end + end + + context 'when the json includes a summary map' do + let(:featured_collection_json) do + base_json.merge({ + 'summaryMap' => { + 'en' => summary, + }, + }) + end + + it 'sets language and summary correctly' do + expect { subject.call(account, featured_collection_json) }.to change(account.collections, :count).by(1) + + new_collection = account.collections.last + expect(new_collection.language).to eq 'en' + expect(new_collection.description_html).to eq '

A list of remote actors you should follow.

' + end + end +end diff --git a/spec/workers/activitypub/process_featured_item_worker_spec.rb b/spec/workers/activitypub/process_featured_item_worker_spec.rb new file mode 100644 index 00000000000000..f27ec21c357407 --- /dev/null +++ b/spec/workers/activitypub/process_featured_item_worker_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::ProcessFeaturedItemWorker do + subject { described_class.new } + + let(:collection) { Fabricate(:remote_collection) } + let(:object) { 'https://example.com/featured_items/1' } + let(:stubbed_service) do + instance_double(ActivityPub::ProcessFeaturedItemService, call: true) + end + + before do + allow(ActivityPub::ProcessFeaturedItemService).to receive(:new).and_return(stubbed_service) + end + + describe 'perform' do + it 'calls the service to process the item' do + subject.perform(collection.id, object) + + expect(stubbed_service).to have_received(:call).with(collection, object) + end + end +end From 3ef7d2835a5ef36bac71cd392710b5ea10602cb1 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 11 Mar 2026 18:04:37 +0100 Subject: [PATCH 039/203] Collection editor: Format topic as hashtag (#38153) --- .../features/collections/editor/details.tsx | 6 +++- .../mastodon/reducers/slices/collections.ts | 32 +++++++++---------- .../mastodon/utils/hashtags.test.ts | 28 ++++++++++++++++ app/javascript/mastodon/utils/hashtags.ts | 32 +++++++++++++++++++ 4 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 app/javascript/mastodon/utils/hashtags.test.ts diff --git a/app/javascript/mastodon/features/collections/editor/details.tsx b/app/javascript/mastodon/features/collections/editor/details.tsx index 875d09c9ebbabb..f59bd4de51b0ca 100644 --- a/app/javascript/mastodon/features/collections/editor/details.tsx +++ b/app/javascript/mastodon/features/collections/editor/details.tsx @@ -6,6 +6,7 @@ import { useHistory } from 'react-router-dom'; import { isFulfilled } from '@reduxjs/toolkit'; +import { inputToHashtag } from '@/mastodon/utils/hashtags'; import type { ApiCreateCollectionPayload, ApiUpdateCollectionPayload, @@ -64,7 +65,7 @@ export const CollectionDetails: React.FC = () => { dispatch( updateCollectionEditorField({ field: 'topic', - value: event.target.value, + value: inputToHashtag(event.target.value), }), ); }, @@ -219,6 +220,9 @@ export const CollectionDetails: React.FC = () => { } value={topic} onChange={handleTopicChange} + autoCapitalize='off' + autoCorrect='off' + spellCheck='false' maxLength={40} /> diff --git a/app/javascript/mastodon/reducers/slices/collections.ts b/app/javascript/mastodon/reducers/slices/collections.ts index dc20b9873231d4..a534a134404811 100644 --- a/app/javascript/mastodon/reducers/slices/collections.ts +++ b/app/javascript/mastodon/reducers/slices/collections.ts @@ -37,30 +37,30 @@ interface CollectionState { status: QueryStatus; } >; - editor: { - id: string | undefined; - name: string; - description: string; - topic: string; - language: string | null; - discoverable: boolean; - sensitive: boolean; - accountIds: string[]; - }; + editor: EditorState; } -type EditorField = CollectionState['editor']; +interface EditorState { + id: string | null; + name: string; + description: string; + topic: string; + language: string | null; + discoverable: boolean; + sensitive: boolean; + accountIds: string[]; +} -interface UpdateEditorFieldPayload { +interface UpdateEditorFieldPayload { field: K; - value: EditorField[K]; + value: EditorState[K]; } const initialState: CollectionState = { collections: {}, accountCollections: {}, editor: { - id: undefined, + id: null, name: '', description: '', topic: '', @@ -79,7 +79,7 @@ const collectionSlice = createSlice({ const collection = action.payload; state.editor = { - id: collection?.id, + id: collection?.id ?? null, name: collection?.name ?? '', description: collection?.description ?? '', topic: collection?.tag?.name ?? '', @@ -92,7 +92,7 @@ const collectionSlice = createSlice({ reset(state) { state.editor = initialState.editor; }, - updateEditorField( + updateEditorField( state: CollectionState, action: PayloadAction>, ) { diff --git a/app/javascript/mastodon/utils/hashtags.test.ts b/app/javascript/mastodon/utils/hashtags.test.ts new file mode 100644 index 00000000000000..05b79b1d52c175 --- /dev/null +++ b/app/javascript/mastodon/utils/hashtags.test.ts @@ -0,0 +1,28 @@ +import { inputToHashtag } from './hashtags'; + +describe('inputToHashtag', () => { + test.concurrent.each([ + ['', ''], + // Prepend or keep hashtag + ['mastodon', '#mastodon'], + ['#mastodon', '#mastodon'], + // Preserve trailing whitespace + ['mastodon ', '#mastodon '], + [' ', '# '], + // Collapse whitespace & capitalise first character + ['cats of mastodon', '#catsOfMastodon'], + ['x y z', '#xYZ'], + [' mastodon', '#mastodon'], + // Preserve initial casing + ['Log in', '#LogIn'], + ['#NaturePhotography', '#NaturePhotography'], + // Normalise hash symbol variant + ['#nature', '#nature'], + ['#Nature Photography', '#NaturePhotography'], + // Allow special characters + ['hello-world', '#hello-world'], + ['hello,world', '#hello,world'], + ])('for input "%s", return "%s"', (input, expected) => { + expect(inputToHashtag(input)).toBe(expected); + }); +}); diff --git a/app/javascript/mastodon/utils/hashtags.ts b/app/javascript/mastodon/utils/hashtags.ts index 0c5505c6c9a088..d14efe5db33e63 100644 --- a/app/javascript/mastodon/utils/hashtags.ts +++ b/app/javascript/mastodon/utils/hashtags.ts @@ -27,3 +27,35 @@ const buildHashtagRegex = () => { export const HASHTAG_PATTERN_REGEX = buildHashtagPatternRegex(); export const HASHTAG_REGEX = buildHashtagRegex(); + +/** + * Formats an input string as a hashtag: + * - Prepends `#` unless present + * - Strips spaces (except at the end, to allow typing it) + * - Capitalises first character after stripped space + */ +export const inputToHashtag = (input: string): string => { + if (!input) { + return ''; + } + + const trailingSpace = /\s+$/.exec(input)?.[0] ?? ''; + const trimmedInput = input.trimEnd(); + + const withoutHash = + trimmedInput.startsWith('#') || trimmedInput.startsWith('#') + ? trimmedInput.slice(1) + : trimmedInput; + + // Split by space, filter empty strings, and capitalise the start of each word but the first + const words = withoutHash + .split(/\s+/) + .filter((word) => word.length > 0) + .map((word, index) => + index === 0 + ? word + : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), + ); + + return `#${words.join('')}${trailingSpace}`; +}; From 0a216003ffb67b4199747fd2eed2618adb21dfa4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 11 Mar 2026 13:11:00 -0400 Subject: [PATCH 040/203] Disable `use_multi_json` for json validator / match_json_schema (#38151) --- config/environments/test.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/environments/test.rb b/config/environments/test.rb index 0c4f1de41e0e08..12709d5f0bf954 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -92,3 +92,6 @@ Sidekiq.strict_args! Redis.raise_deprecations = true + +# Silence deprecation warning from json-schema +JSON::Validator.use_multi_json = false From 811575a10903cada549580979cc809ca98ad570c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 11 Mar 2026 13:11:28 -0400 Subject: [PATCH 041/203] Use bundler version 4.0.8 (#38150) --- Gemfile.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 68b7cb0a6092fa..5ab80abb8c5bb0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,7 @@ GEM ast (2.4.3) attr_required (1.0.2) aws-eventstream (1.4.0) - aws-partitions (1.1222.0) + aws-partitions (1.1223.0) aws-sdk-core (3.243.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -157,7 +157,7 @@ GEM case_transform (0.2) activesupport cbor (0.5.10.1) - cgi (0.4.2) + cgi (0.5.1) charlock_holmes (0.7.9) chewy (7.6.0) activesupport (>= 5.2) @@ -209,7 +209,7 @@ GEM activerecord (>= 4.2, < 9.0) docile (1.4.1) domain_name (0.6.20240107) - doorkeeper (5.8.2) + doorkeeper (5.9.0) railties (>= 5) dotenv (3.2.0) drb (2.2.3) @@ -230,7 +230,7 @@ GEM erubi (1.13.1) et-orbi (1.4.0) tzinfo - excon (1.3.2) + excon (1.4.0) logger fabrication (3.0.0) faker (3.6.1) @@ -276,9 +276,9 @@ GEM raabro (~> 1.4) globalid (1.3.0) activesupport (>= 6.1) - google-protobuf (4.33.5) + google-protobuf (4.34.0) bigdecimal - rake (>= 13) + rake (~> 13.3) googleapis-common-protos-types (1.22.0) google-protobuf (~> 4.26) haml (7.2.0) @@ -352,7 +352,7 @@ GEM azure-blob (~> 0.5.2) hashie (~> 5.0) jmespath (1.6.2) - json (2.18.1) + json (2.19.1) json-canonicalization (1.0.0) json-jwt (1.17.0) activesupport (>= 4.2) @@ -446,7 +446,7 @@ GEM mime-types (3.7.0) logger mime-types-data (~> 3.2025, >= 3.2025.0507) - mime-types-data (3.2026.0224) + mime-types-data (3.2026.0303) mini_mime (1.1.5) mini_portile2 (2.8.9) minitest (6.0.2) @@ -507,7 +507,7 @@ GEM tzinfo validate_url webfinger (~> 2.0) - openssl (3.3.2) + openssl (4.0.1) openssl-signature_algorithm (1.3.0) openssl (> 2.0) opentelemetry-api (1.7.0) @@ -766,7 +766,7 @@ GEM rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.49.0) + rubocop-ast (1.49.1) parser (>= 3.3.7.2) prism (~> 1.7) rubocop-capybara (2.22.1) @@ -792,7 +792,7 @@ GEM lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) rubocop-rspec (~> 3.5) - ruby-prof (2.0.2) + ruby-prof (2.0.4) base64 ostruct ruby-progressbar (1.13.0) @@ -847,7 +847,7 @@ GEM stackprof (0.2.28) starry (0.2.0) base64 - stoplight (5.7.0) + stoplight (5.8.0) concurrent-ruby zeitwerk stringio (3.2.0) @@ -867,7 +867,7 @@ GEM test-prof (1.5.2) thor (1.5.0) tilt (2.7.0) - timeout (0.6.0) + timeout (0.6.1) tpm-key_attestation (0.14.1) bindata (~> 2.4) openssl (> 2.0) @@ -1100,4 +1100,4 @@ RUBY VERSION ruby 3.4.8 BUNDLED WITH - 4.0.7 + 4.0.8 From 51894ac247c84f577b46e69094b3581fd6e78375 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 11 Mar 2026 22:07:31 +0100 Subject: [PATCH 042/203] Change order of onboarding steps (#38121) --- app/javascript/mastodon/features/onboarding/follows.tsx | 7 +++---- app/javascript/mastodon/features/onboarding/profile.tsx | 7 ++++--- app/javascript/mastodon/features/ui/index.jsx | 4 ++-- app/javascript/mastodon/locales/en.json | 4 ++-- app/javascript/styles/mastodon/components.scss | 3 +++ 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/features/onboarding/follows.tsx b/app/javascript/mastodon/features/onboarding/follows.tsx index d30834d0b6a87b..d53f1ea50ddaa2 100644 --- a/app/javascript/mastodon/features/onboarding/follows.tsx +++ b/app/javascript/mastodon/features/onboarding/follows.tsx @@ -121,7 +121,6 @@ export const Follows: React.FC<{ icon='person' iconComponent={PersonIcon} multiColumn={multiColumn} - showBackButton /> 0 &&
}
- +
diff --git a/app/javascript/mastodon/features/onboarding/profile.tsx b/app/javascript/mastodon/features/onboarding/profile.tsx index 3f1168abe5c1f9..0c6b5215b65ca6 100644 --- a/app/javascript/mastodon/features/onboarding/profile.tsx +++ b/app/javascript/mastodon/features/onboarding/profile.tsx @@ -133,7 +133,7 @@ export const Profile: React.FC<{ }), ) .then(() => { - history.push('/start/follows'); + history.push('/home'); dispatch(closeOnboarding()); return ''; }) @@ -160,6 +160,7 @@ export const Profile: React.FC<{ icon='person' iconComponent={PersonIcon} multiColumn={multiColumn} + showBackButton />
@@ -297,8 +298,8 @@ export const Profile: React.FC<{ ) : ( )} diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 47fe6ff0ccc183..3e14b016e97a70 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -230,8 +230,8 @@ class SwitchingColumnsArea extends PureComponent { - - + + diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 2ede5449d14ed8..ee0a8ded7c0b77 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -960,17 +960,17 @@ "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", "onboarding.follows.back": "Back", - "onboarding.follows.done": "Done", "onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", + "onboarding.follows.next": "Next: Setup your profile", "onboarding.follows.search": "Search", "onboarding.follows.title": "Follow people to get started", "onboarding.profile.discoverable": "Make my profile discoverable", "onboarding.profile.discoverable_hint": "When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.", "onboarding.profile.display_name": "Display name", "onboarding.profile.display_name_hint": "Your full name or your fun name…", + "onboarding.profile.finish": "Finish", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "You can @mention other people or #hashtags…", - "onboarding.profile.save_and_continue": "Save and continue", "onboarding.profile.title": "Profile setup", "onboarding.profile.upload_avatar": "Upload profile picture", "onboarding.profile.upload_header": "Upload profile header", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 22a788e2d0fcb5..2f1d7740b4c65c 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -11509,6 +11509,9 @@ noscript { .column-footer { padding: 16px; + position: sticky; + bottom: 0; + background: var(--color-bg-primary); } .lists-scrollable { From 84d802016587e8fce9b5b9d0e233d187ae8ace14 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Thu, 12 Mar 2026 10:11:32 +0100 Subject: [PATCH 043/203] Store a remote actor's `featuredCollections` URI (#38166) --- app/models/account.rb | 1 + .../activitypub/process_account_service.rb | 1 + ...1152331_add_collections_url_to_accounts.rb | 7 +++++ db/schema.rb | 3 +- .../process_account_service_spec.rb | 28 +++++++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20260311152331_add_collections_url_to_accounts.rb diff --git a/app/models/account.rb b/app/models/account.rb index 0e80b99d8d466e..ceb5f857a965fd 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -15,6 +15,7 @@ # avatar_remote_url :string # avatar_storage_schema_version :integer # avatar_updated_at :datetime +# collections_url :string # discoverable :boolean # display_name :string default(""), not null # domain :string diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index efdaa269747539..e23f4a15bbeca9 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -125,6 +125,7 @@ def valid_collection_uri(uri) def set_immediate_attributes! @account.featured_collection_url = valid_collection_uri(@json['featured']) + @account.collections_url = valid_collection_uri(@json['featuredCollections']) @account.display_name = (@json['name'] || '')[0...(Account::DISPLAY_NAME_LENGTH_HARD_LIMIT)] @account.note = (@json['summary'] || '')[0...(Account::NOTE_LENGTH_HARD_LIMIT)] @account.locked = @json['manuallyApprovesFollowers'] || false diff --git a/db/migrate/20260311152331_add_collections_url_to_accounts.rb b/db/migrate/20260311152331_add_collections_url_to_accounts.rb new file mode 100644 index 00000000000000..7fc0bb29443a04 --- /dev/null +++ b/db/migrate/20260311152331_add_collections_url_to_accounts.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddCollectionsURLToAccounts < ActiveRecord::Migration[8.1] + def change + add_column :accounts, :collections_url, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index d01d1af500ba5b..c85565211c2e07 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_03_10_095021) do +ActiveRecord::Schema[8.1].define(version: 2026_03_11_152331) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -162,6 +162,7 @@ t.string "avatar_remote_url" t.integer "avatar_storage_schema_version" t.datetime "avatar_updated_at", precision: nil + t.string "collections_url" t.datetime "created_at", precision: nil, null: false t.boolean "discoverable" t.string "display_name", default: "", null: false diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index b2c6bc17b2228d..1d8d8f8ac53811 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -63,6 +63,34 @@ end end + context 'with collection URIs' do + let(:payload) do + { + 'id' => 'https://foo.test', + 'type' => 'Actor', + 'inbox' => 'https://foo.test/inbox', + 'featured' => 'https://foo.test/featured', + 'followers' => 'https://foo.test/followers', + 'following' => 'https://foo.test/following', + 'featuredCollections' => 'https://foo.test/featured_collections', + } + end + + before do + stub_request(:get, %r{^https://foo\.test/follow}) + .to_return(status: 200, body: '', headers: {}) + end + + it 'parses and sets the URIs' do + account = subject.call('alice', 'example.com', payload) + + expect(account.featured_collection_url).to eq 'https://foo.test/featured' + expect(account.followers_url).to eq 'https://foo.test/followers' + expect(account.following_url).to eq 'https://foo.test/following' + expect(account.collections_url).to eq 'https://foo.test/featured_collections' + end + end + context 'with attribution domains' do let(:payload) do { From 2330f11cadc890d8b7124c03ccb618ddec3dcf42 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 12 Mar 2026 05:22:56 -0400 Subject: [PATCH 044/203] Remove unused `@markdown` from user mailer terms action (#38160) --- app/mailers/user_mailer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index fffc80a8e8c8bc..3ca313ddd0ba8d 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -216,7 +216,6 @@ def failed_2fa(user, remote_ip, user_agent, timestamp) def terms_of_service_changed(user, terms_of_service) @resource = user @terms_of_service = terms_of_service - @markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, escape_html: true, no_images: true) I18n.with_locale(locale) do mail subject: default_i18n_subject From 7f9df6d02db29da4d079b3aa74c54c4a75c73081 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 10:53:59 +0100 Subject: [PATCH 045/203] New Crowdin Translations (automated) (#38164) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ar.json | 2 -- app/javascript/mastodon/locales/ast.json | 1 - app/javascript/mastodon/locales/az.json | 2 -- app/javascript/mastodon/locales/be.json | 2 -- app/javascript/mastodon/locales/bg.json | 2 -- app/javascript/mastodon/locales/br.json | 2 -- app/javascript/mastodon/locales/ca.json | 2 -- app/javascript/mastodon/locales/cs.json | 2 -- app/javascript/mastodon/locales/cy.json | 3 --- app/javascript/mastodon/locales/da.json | 7 ++++--- app/javascript/mastodon/locales/de.json | 7 ++++--- app/javascript/mastodon/locales/el.json | 7 ++++--- app/javascript/mastodon/locales/en-GB.json | 7 ++++--- app/javascript/mastodon/locales/eo.json | 2 -- app/javascript/mastodon/locales/es-AR.json | 5 ++--- app/javascript/mastodon/locales/es-MX.json | 7 ++++--- app/javascript/mastodon/locales/es.json | 3 --- app/javascript/mastodon/locales/et.json | 2 -- app/javascript/mastodon/locales/eu.json | 2 -- app/javascript/mastodon/locales/fa.json | 2 -- app/javascript/mastodon/locales/fi.json | 7 ++++--- app/javascript/mastodon/locales/fil.json | 1 - app/javascript/mastodon/locales/fo.json | 2 -- app/javascript/mastodon/locales/fr-CA.json | 15 +++++++-------- app/javascript/mastodon/locales/fr.json | 17 ++++++++--------- app/javascript/mastodon/locales/fy.json | 2 -- app/javascript/mastodon/locales/ga.json | 3 --- app/javascript/mastodon/locales/gd.json | 2 -- app/javascript/mastodon/locales/gl.json | 7 ++++--- app/javascript/mastodon/locales/he.json | 3 --- app/javascript/mastodon/locales/hu.json | 5 ++--- app/javascript/mastodon/locales/ia.json | 2 -- app/javascript/mastodon/locales/ie.json | 1 - app/javascript/mastodon/locales/io.json | 2 -- app/javascript/mastodon/locales/is.json | 5 ++--- app/javascript/mastodon/locales/it.json | 9 +++++---- app/javascript/mastodon/locales/ja.json | 2 -- app/javascript/mastodon/locales/kab.json | 2 -- app/javascript/mastodon/locales/ko.json | 2 -- app/javascript/mastodon/locales/lad.json | 2 -- app/javascript/mastodon/locales/lt.json | 2 -- app/javascript/mastodon/locales/lv.json | 1 - app/javascript/mastodon/locales/ms.json | 1 - app/javascript/mastodon/locales/my.json | 1 - app/javascript/mastodon/locales/nan-TW.json | 3 --- app/javascript/mastodon/locales/nl.json | 3 --- app/javascript/mastodon/locales/nn.json | 2 -- app/javascript/mastodon/locales/no.json | 2 -- app/javascript/mastodon/locales/pa.json | 2 -- app/javascript/mastodon/locales/pl.json | 2 -- app/javascript/mastodon/locales/pt-BR.json | 18 ++++++++++++++++-- app/javascript/mastodon/locales/pt-PT.json | 3 --- app/javascript/mastodon/locales/ru.json | 2 -- app/javascript/mastodon/locales/sc.json | 2 -- app/javascript/mastodon/locales/si.json | 2 -- app/javascript/mastodon/locales/sk.json | 2 -- app/javascript/mastodon/locales/sl.json | 2 -- app/javascript/mastodon/locales/sq.json | 5 ++--- app/javascript/mastodon/locales/sr-Latn.json | 1 - app/javascript/mastodon/locales/sr.json | 1 - app/javascript/mastodon/locales/sv.json | 7 ++++--- app/javascript/mastodon/locales/th.json | 2 -- app/javascript/mastodon/locales/tr.json | 13 ++++++------- app/javascript/mastodon/locales/uk.json | 2 -- app/javascript/mastodon/locales/vi.json | 7 ++++--- app/javascript/mastodon/locales/zh-CN.json | 3 --- app/javascript/mastodon/locales/zh-HK.json | 2 -- app/javascript/mastodon/locales/zh-TW.json | 7 ++++--- config/locales/de.yml | 2 +- config/locales/it.yml | 2 +- 70 files changed, 92 insertions(+), 172 deletions(-) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index e3c75bd21b5281..bfd0f8bd60a52b 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -717,7 +717,6 @@ "notifications_permission_banner.how_to_control": "لتلقي الإشعارات عندما لا يكون ماستدون مفتوح، قم بتفعيل إشعارات سطح المكتب، يمكنك التحكم بدقة في أنواع التفاعلات التي تولد إشعارات سطح المكتب من خلال زر الـ{icon} أعلاه بمجرد تفعيلها.", "notifications_permission_banner.title": "لا تفوت شيئاً أبداً", "onboarding.follows.back": "عودة", - "onboarding.follows.done": "تمّ", "onboarding.follows.empty": "نأسف، لا يمكن عرض نتائج في الوقت الحالي. جرب البحث أو انتقل لصفحة الاستكشاف لإيجاد أشخاص للمتابعة، أو حاول مرة أخرى.", "onboarding.follows.search": "بحث", "onboarding.follows.title": "للبدء قم بمتابعة أشخاص", @@ -727,7 +726,6 @@ "onboarding.profile.display_name_hint": "اسمك الكامل أو اسمك المرح…", "onboarding.profile.note": "نبذة عنك", "onboarding.profile.note_hint": "يمكنك @ذِكر أشخاص آخرين أو استعمال #الوسوم…", - "onboarding.profile.save_and_continue": "حفظ و إستمرار", "onboarding.profile.title": "إعداد الملف الشخصي", "onboarding.profile.upload_avatar": "تحميل صورة الملف الشخصي", "onboarding.profile.upload_header": "تحميل رأسية الملف الشخصي", diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json index 92bc871b5f0a2d..850971819ae53a 100644 --- a/app/javascript/mastodon/locales/ast.json +++ b/app/javascript/mastodon/locales/ast.json @@ -392,7 +392,6 @@ "notifications.permission_required": "Los avisos d'escritoriu nun tán disponibles porque nun se concedió'l permisu riquíu.", "notifications.policy.accept": "Aceptar", "notifications.policy.accept_hint": "Amosar n'avisos", - "onboarding.follows.done": "Fecho", "onboarding.profile.note": "Biografía", "onboarding.profile.note_hint": "Pues @mentar a otros perfiles o poner #etiquetes…", "password_confirmation.exceeds_maxlength": "La contraseña de confirmación supera la llongura de caráuteres máxima", diff --git a/app/javascript/mastodon/locales/az.json b/app/javascript/mastodon/locales/az.json index e4b5758c44624b..e664b6bd0cb8cd 100644 --- a/app/javascript/mastodon/locales/az.json +++ b/app/javascript/mastodon/locales/az.json @@ -661,7 +661,6 @@ "notifications_permission_banner.enable": "Masaüstü bildirişləri fəallaşdır", "notifications_permission_banner.title": "Heç nəyi buraxmayın", "onboarding.follows.back": "Geri", - "onboarding.follows.done": "Hazırdır", "onboarding.follows.search": "Axtar", "onboarding.follows.title": "Başlamaq üçün insanları izləyin", "onboarding.profile.discoverable": "Profilimi kəşf edilə bilən et", @@ -670,7 +669,6 @@ "onboarding.profile.display_name_hint": "Tam adınız və ya ləqəbiniz…", "onboarding.profile.note": "Bioqrafiya", "onboarding.profile.note_hint": "Digər insanların @adını_çəkə və ya #mövzu_etiketləri istifadə edə bilərsiniz…", - "onboarding.profile.save_and_continue": "Saxla və davam et", "onboarding.profile.title": "Profili ayarla", "onboarding.profile.upload_avatar": "Profil şəkli yüklə", "onboarding.profile.upload_header": "Profil başlığı yüklə", diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index b4a6c58bfd205c..c155c8ffdcd205 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -915,7 +915,6 @@ "notifications_permission_banner.how_to_control": "Каб атрымліваць апавяшчэнні, калі Mastodon не адкрыты, уключыце апавяшчэнні працоўнага стала. Вы зможаце дакладна кантраляваць, якія падзеі будуць ствараць апавяшчэнні з дапамогай {icon} кнопкі, як толькі яны будуць уключаны.", "notifications_permission_banner.title": "Не прапусціце нічога", "onboarding.follows.back": "Назад", - "onboarding.follows.done": "Гатова", "onboarding.follows.empty": "На жаль, зараз немагчыма паказаць вынікі. Вы можаце паспрабаваць выкарыстоўваць пошук і праглядзець старонку агляду, каб знайсці людзей, на якіх можна падпісацца, або паўтарыць спробу пазней.", "onboarding.follows.search": "Пошук", "onboarding.follows.title": "Падпішыцеся на некага, каб пачаць", @@ -925,7 +924,6 @@ "onboarding.profile.display_name_hint": "Ваша поўнае імя або ваш псеўданім…", "onboarding.profile.note": "Біяграфія", "onboarding.profile.note_hint": "Вы можаце @згадваць іншых людзей або выкарыстоўваць #хэштэгі…", - "onboarding.profile.save_and_continue": "Захаваць і працягнуць", "onboarding.profile.title": "Налады профілю", "onboarding.profile.upload_avatar": "Загрузіць фота профілю", "onboarding.profile.upload_header": "Загрузіць шапку профілю", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 3549c0ae2a3992..850f1175440ded 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -680,7 +680,6 @@ "notifications_permission_banner.how_to_control": "За да получавате известия, когато Mastodon не е отворен, включете известията на работния плот. Може да управлявате точно кои видове взаимодействия пораждат известия на работния плот чрез бутона {icon} по-горе, след като бъдат включени.", "notifications_permission_banner.title": "Никога не пропускайте нищо", "onboarding.follows.back": "Назад", - "onboarding.follows.done": "Готово", "onboarding.follows.empty": "За съжаление, в момента не могат да се показват резултати. Може да опитате посредством търсене или сърфиране да разгледате страницата, за да намерите хора за последване, или опитайте пак по-късно.", "onboarding.follows.search": "Търсене", "onboarding.follows.title": "Последвайте хора, за да започнете", @@ -690,7 +689,6 @@ "onboarding.profile.display_name_hint": "Вашето пълно име или псевдоним…", "onboarding.profile.note": "Биография", "onboarding.profile.note_hint": "Може да @споменавате други хора или #хаштагове…", - "onboarding.profile.save_and_continue": "Запазване и продължаване", "onboarding.profile.title": "Настройване на профила", "onboarding.profile.upload_avatar": "Качване на снимка на профила", "onboarding.profile.upload_header": "Качване на заглавка на профила", diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json index 0e9e9d33e6ad1d..cb55e4a68a3da5 100644 --- a/app/javascript/mastodon/locales/br.json +++ b/app/javascript/mastodon/locales/br.json @@ -533,13 +533,11 @@ "notifications_permission_banner.how_to_control": "Evit reseviñ kemennoù pa ne vez ket digoret Mastodon, lezelit kemennoù war ar burev. Gallout a rit kontrollañ peseurt eskemmoù a c'henel kemennoù war ar burev gant ar {icon} nozelenn a-us kentre ma'z int lezelet.", "notifications_permission_banner.title": "Na vankit netra morse", "onboarding.follows.back": "Distreiñ", - "onboarding.follows.done": "Graet", "onboarding.follows.search": "Klask", "onboarding.profile.display_name": "Anv diskouezet", "onboarding.profile.display_name_hint": "Hoc'h anv klok pe hoc'h anv fentus…", "onboarding.profile.note": "Berr-ha-berr", "onboarding.profile.note_hint": "Gallout a rit @menegiñ tud all pe #gerioù-klik…", - "onboarding.profile.save_and_continue": "Enrollañ ha kenderc'hel", "onboarding.profile.title": "Kefluniañ ar profil", "onboarding.profile.upload_avatar": "Enporzhiañ ur skeudenn profil", "password_confirmation.mismatching": "Disheñvel eo an daou c'her-termen-se", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 496cb0d0c96006..029c61cf0692d2 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -786,7 +786,6 @@ "notifications_permission_banner.how_to_control": "Per a rebre notificacions quan Mastodon no és obert cal activar les notificacions d’escriptori. Pots controlar amb precisió quins tipus d’interaccions generen notificacions d’escriptori després d’activar el botó {icon} de dalt.", "notifications_permission_banner.title": "No et perdis mai res", "onboarding.follows.back": "Enrere", - "onboarding.follows.done": "Fet", "onboarding.follows.empty": "Malauradament, cap resultat pot ser mostrat ara mateix. Pots provar de fer servir la cerca o visitar la pàgina Explora per a trobar gent a qui seguir o provar-ho de nou més tard.", "onboarding.follows.search": "Cerca", "onboarding.follows.title": "Seguiu gent per a començar", @@ -796,7 +795,6 @@ "onboarding.profile.display_name_hint": "El teu nom complet o el teu malnom…", "onboarding.profile.note": "Biografia", "onboarding.profile.note_hint": "Pots @mencionar altra gent o #etiquetes…", - "onboarding.profile.save_and_continue": "Desa i continua", "onboarding.profile.title": "Configuració del perfil", "onboarding.profile.upload_avatar": "Importa una foto de perfil", "onboarding.profile.upload_header": "Importa una capçalera de perfil", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index d79d67d4da5402..3ab9bcc5f3d9b3 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -841,7 +841,6 @@ "notifications_permission_banner.how_to_control": "Chcete-li dostávat oznámení, i když nemáte Mastodon otevřený, povolte oznámení na ploše. Můžete si zvolit, o kterých druzích interakcí chcete být oznámením na ploše informování pod tlačítkem {icon} výše.", "notifications_permission_banner.title": "Nenechte si nic uniknout", "onboarding.follows.back": "Zpět", - "onboarding.follows.done": "Hotovo", "onboarding.follows.empty": "Bohužel, žádné výsledky nelze momentálně zobrazit. Můžete zkusit najít uživatele ke sledování za pomocí vyhledávání nebo na stránce „Objevit“, nebo to zkuste znovu později.", "onboarding.follows.search": "Hledat", "onboarding.follows.title": "Sledujte lidi a začněte", @@ -851,7 +850,6 @@ "onboarding.profile.display_name_hint": "Vaše celé jméno nebo přezdívka…", "onboarding.profile.note": "O vás", "onboarding.profile.note_hint": "Můžete @zmínit jiné osoby nebo #hashtagy…", - "onboarding.profile.save_and_continue": "Uložit a pokračovat", "onboarding.profile.title": "Nastavení profilu", "onboarding.profile.upload_avatar": "Nahrát profilový obrázek", "onboarding.profile.upload_header": "Nahrát hlavičku profilu", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 3bd13587c21291..dbbe7ae181083e 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -168,7 +168,6 @@ "account_edit.field_edit_modal.add_title": "Ychwanegu maes cyfaddas", "account_edit.field_edit_modal.edit_title": "Golygu maes cyfaddas", "account_edit.field_edit_modal.name_hint": "e.e. “Gwefan bersonol”", - "account_edit.field_edit_modal.value_hint": "e.e. “enghraifft.fi”", "account_edit.field_edit_modal.value_label": "Gwerth", "account_edit.name_modal.add_title": "Ychwanegu enw dangos", "account_edit.name_modal.edit_title": "Golygu enw dangos", @@ -938,7 +937,6 @@ "notifications_permission_banner.how_to_control": "I dderbyn hysbysiadau pan nad yw Mastodon ar agor, galluogwch hysbysiadau bwrdd gwaith. Gallwch reoli'n union pa fathau o ryngweithiadau sy'n cynhyrchu hysbysiadau bwrdd gwaith trwy'r botwm {icon} uchod unwaith y byddan nhw wedi'u galluogi.", "notifications_permission_banner.title": "Peidiwch â cholli dim", "onboarding.follows.back": "Nôl", - "onboarding.follows.done": "Wedi gorffen", "onboarding.follows.empty": "Yn anffodus, nid oes modd dangos unrhyw ganlyniadau ar hyn o bryd. Gallwch geisio defnyddio chwilio neu bori'r dudalen archwilio i ddod o hyd i bobl i'w dilyn, neu ceisio eto yn nes ymlaen.", "onboarding.follows.search": "Chwilio", "onboarding.follows.title": "Dilynwch bobl i gychwyn arni", @@ -948,7 +946,6 @@ "onboarding.profile.display_name_hint": "Eich enw llawn neu'ch enw hwyl…", "onboarding.profile.note": "Bywgraffiad", "onboarding.profile.note_hint": "Gallwch @grybwyll pobl eraill neu #hashnodau…", - "onboarding.profile.save_and_continue": "Cadw a pharhau", "onboarding.profile.title": "Gosodiad proffil", "onboarding.profile.upload_avatar": "Llwytho llun proffil", "onboarding.profile.upload_header": "Llwytho pennyn proffil", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 237fdea063902b..3dde4254e740a3 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Vi fraråder brug af brugerdefinerede emoji i kombination med url'er. Brugerdefinerede felter, der indeholder begge dele, vises kun som tekst i stedet for som et link for at undgå forvirring hos brugerne.", "account_edit.field_edit_modal.name_hint": "F.eks. “Personligt websted”", "account_edit.field_edit_modal.name_label": "Etiket", - "account_edit.field_edit_modal.value_hint": "F.eks. “eksempel.me”", + "account_edit.field_edit_modal.url_warning": "For at tilføje et link, skal du inkludere {protocol} i begyndelsen.", + "account_edit.field_edit_modal.value_hint": "F.eks. “https://example.me”", "account_edit.field_edit_modal.value_label": "Værdi", "account_edit.field_reorder_modal.drag_cancel": "Trækningen blev annulleret. Feltet \"{item}\" blev sluppet.", "account_edit.field_reorder_modal.drag_end": "Feltet \"{item}\" blev sluppet.", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "Aktivér computernotifikationer for at få besked, når Mastodon ikke er åben. Når de er aktiveret, kan man via knappen {icon} ovenfor præcist styre, hvilke typer af interaktioner, som genererer computernotifikationer.", "notifications_permission_banner.title": "Gå aldrig glip af noget", "onboarding.follows.back": "Tilbage", - "onboarding.follows.done": "Færdig", "onboarding.follows.empty": "Ingen resultater tilgængelige pt. Prøv at bruge søgning eller gennemse siden for at finde personer at følge, eller forsøg igen senere.", + "onboarding.follows.next": "Næste: Opsætning af din profil", "onboarding.follows.search": "Søg", "onboarding.follows.title": "Følg folk for at komme i gang", "onboarding.profile.discoverable": "Gør min profil synlig", "onboarding.profile.discoverable_hint": "Når du vælger at være synlig på Mastodon, kan dine indlæg blive vist i søgeresultater og trender, og din profil kan blive foreslået til personer med samme interesser som dig.", "onboarding.profile.display_name": "Vist navn", "onboarding.profile.display_name_hint": "Dit fulde navn eller dit sjove navn…", + "onboarding.profile.finish": "Afslut", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "Du kan @omtale andre personer eller #hashtags…", - "onboarding.profile.save_and_continue": "Gem og fortsæt", "onboarding.profile.title": "Profilopsætning", "onboarding.profile.upload_avatar": "Upload profilbillede", "onboarding.profile.upload_header": "Upload profilbanner", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 520f949a6a6ba9..3b2d249828913c 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Das Verwenden von Emojis wird bei URLs nicht empfohlen. Die Zusatzfelder werden bei dieser Kombination nur als Text und nicht als Link dargestellt.", "account_edit.field_edit_modal.name_hint": "z. B. „Meine Website“", "account_edit.field_edit_modal.name_label": "Beschriftung", - "account_edit.field_edit_modal.value_hint": "z. B. „example.me“", + "account_edit.field_edit_modal.url_warning": "Um einen Link hinzuzufügen, füge {protocol} an den Anfang ein.", + "account_edit.field_edit_modal.value_hint": "z. B. „https://example.me“", "account_edit.field_edit_modal.value_label": "Inhalt", "account_edit.field_reorder_modal.drag_cancel": "Das Ziehen wurde abgebrochen und das Feld „{item}“ wurde abgelegt.", "account_edit.field_reorder_modal.drag_end": "Das Feld „{item}“ wurde abgelegt.", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "Aktiviere Desktop-Benachrichtigungen, um Mitteilungen zu erhalten, wenn Mastodon nicht geöffnet ist. Du kannst für jede Kategorie einstellen, ob du Desktop-Benachrichtigungen erhalten möchtest. Sobald sie aktiviert sind, klicke dafür auf das {icon} -Symbol.", "notifications_permission_banner.title": "Nichts verpassen", "onboarding.follows.back": "Zurück", - "onboarding.follows.done": "Fertig", "onboarding.follows.empty": "Bedauerlicherweise können aktuell keine Ergebnisse angezeigt werden. Du kannst die Suche verwenden oder den Reiter „Entdecken“ auswählen, um neue Leute zum Folgen zu finden – oder du versuchst es später erneut.", + "onboarding.follows.next": "Nächster Schritt: Profil einrichten", "onboarding.follows.search": "Suchen", "onboarding.follows.title": "Folge Profilen, um loszulegen", "onboarding.profile.discoverable": "Mein Profil darf entdeckt werden", "onboarding.profile.discoverable_hint": "Wenn du entdeckt werden möchtest, dann können deine Beiträge in Suchergebnissen und Trends erscheinen. Dein Profil kann ebenfalls anderen mit ähnlichen Interessen vorgeschlagen werden.", "onboarding.profile.display_name": "Anzeigename", "onboarding.profile.display_name_hint": "Dein richtiger Name oder dein Fantasiename …", + "onboarding.profile.finish": "Fertig", "onboarding.profile.note": "Über mich", "onboarding.profile.note_hint": "Du kannst andere @Profile erwähnen oder #Hashtags verwenden …", - "onboarding.profile.save_and_continue": "Speichern und fortfahren", "onboarding.profile.title": "Profil einrichten", "onboarding.profile.upload_avatar": "Profilbild hochladen", "onboarding.profile.upload_header": "Titelbild hochladen", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 7ec926b496711b..100fce7e9a7245 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Δεν συνιστούμε τη χρήση προσαρμοσμένων emoji σε συνδυασμό με URL. Τα προσαρμοσμένα πεδία που περιέχουν και τα δύο θα εμφανίζονται ως κείμενο μόνο αντί ως σύνδεσμος, προκειμένου να αποφευχθεί η σύγχυση του χρήστη.", "account_edit.field_edit_modal.name_hint": "Π.χ. “Προσωπική ιστοσελίδα”", "account_edit.field_edit_modal.name_label": "Ετικέτα", - "account_edit.field_edit_modal.value_hint": "Π.χ. “example.me”", + "account_edit.field_edit_modal.url_warning": "Για να προσθέσεις έναν σύνδεσμο, παρακαλούμε να συμπεριλάβεις το {protocol} στην αρχή.", + "account_edit.field_edit_modal.value_hint": "Π.χ. “https://example.me”", "account_edit.field_edit_modal.value_label": "Τιμή", "account_edit.field_reorder_modal.drag_cancel": "Η μετακίνηση ακυρώθηκε. Το πεδίο \"{item}\" αφέθηκε.", "account_edit.field_reorder_modal.drag_end": "Το πεδίο \"{item}\" αφέθηκε.", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "Για να λαμβάνεις ειδοποιήσεις όταν το Mastodon δεν είναι ανοιχτό, ενεργοποίησε τις ειδοποιήσεις επιφάνειας εργασίας. Μπορείς να ελέγξεις με ακρίβεια ποιοι τύποι αλληλεπιδράσεων δημιουργούν ειδοποιήσεις επιφάνειας εργασίας μέσω του κουμπιού {icon} μόλις ενεργοποιηθούν.", "notifications_permission_banner.title": "Μη χάσεις στιγμή", "onboarding.follows.back": "Πίσω", - "onboarding.follows.done": "Έγινε", "onboarding.follows.empty": "Δυστυχώς, δεν μπορούν να εμφανιστούν αποτελέσματα αυτή τη στιγμή. Μπορείς να προσπαθήσεις να χρησιμοποιήσεις την αναζήτηση ή να περιηγηθείς στη σελίδα εξερεύνησης για να βρεις άτομα να ακολουθήσεις ή να δοκιμάσεις ξανά αργότερα.", + "onboarding.follows.next": "Επόμενο: Ρυθμίστε το προφίλ σας", "onboarding.follows.search": "Αναζήτηση", "onboarding.follows.title": "Ακολούθησε άτομα για να ξεκινήσεις", "onboarding.profile.discoverable": "Κάνε το προφίλ μου ανακαλύψιμο", "onboarding.profile.discoverable_hint": "Όταν επιλέγεις την δυνατότητα ανακάλυψης στο Mastodon, οι αναρτήσεις σου μπορεί να εμφανιστούν στα αποτελέσματα αναζήτησης και τις τάσεις, και το προφίλ σου μπορεί να προτείνεται σε άτομα με παρόμοια ενδιαφέροντα με εσένα.", "onboarding.profile.display_name": "Εμφανιζόμενο όνομα", "onboarding.profile.display_name_hint": "Το πλήρες ή το διασκεδαστικό σου όνομα…", + "onboarding.profile.finish": "Ολοκλήρωση", "onboarding.profile.note": "Βιογραφικό", "onboarding.profile.note_hint": "Μπορείς να @επισημάνεις άλλα άτομα ή #ετικέτες…", - "onboarding.profile.save_and_continue": "Αποθήκευση και συνέχεια", "onboarding.profile.title": "Ρύθμιση προφίλ", "onboarding.profile.upload_avatar": "Μεταφόρτωση εικόνας προφίλ", "onboarding.profile.upload_header": "Μεταφόρτωση κεφαλίδας προφίλ", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 4bd474bc16979a..25cb548c19e31d 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "We recommend against the use of custom emoji in combination with URLs. Custom fields containing both will display as text only instead of as a link, in order to prevent user confusion.", "account_edit.field_edit_modal.name_hint": "Eg “Personal website”", "account_edit.field_edit_modal.name_label": "Label", - "account_edit.field_edit_modal.value_hint": "Eg “example.me”", + "account_edit.field_edit_modal.url_warning": "To add a link, please include {protocol} at the beginning.", + "account_edit.field_edit_modal.value_hint": "Eg “https://example.me”", "account_edit.field_edit_modal.value_label": "Value", "account_edit.field_reorder_modal.drag_cancel": "Dragging was cancelled. Field \"{item}\" was dropped.", "account_edit.field_reorder_modal.drag_end": "Field \"{item}\" was dropped.", @@ -959,17 +960,17 @@ "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", "onboarding.follows.back": "Back", - "onboarding.follows.done": "Done", "onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", + "onboarding.follows.next": "Next: set up your profile", "onboarding.follows.search": "Search", "onboarding.follows.title": "Follow people to get started", "onboarding.profile.discoverable": "Make my profile discoverable", "onboarding.profile.discoverable_hint": "When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.", "onboarding.profile.display_name": "Display name", "onboarding.profile.display_name_hint": "Your full name or your fun name…", + "onboarding.profile.finish": "Finish", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "You can @mention other people or #hashtags…", - "onboarding.profile.save_and_continue": "Save and continue", "onboarding.profile.title": "Profile setup", "onboarding.profile.upload_avatar": "Upload profile picture", "onboarding.profile.upload_header": "Upload profile header", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index b577a18a8552e8..67de7b360b1e87 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -698,7 +698,6 @@ "notifications_permission_banner.how_to_control": "Por ricevi sciigojn kiam Mastodon ne estas malfermita, ebligu labortablajn sciigojn. Vi povas regi precize kiuj specoj de interagoj generas labortablajn sciigojn per la supra butono {icon} post kiam ili estas ebligitaj.", "notifications_permission_banner.title": "Neniam preterlasas iun ajn", "onboarding.follows.back": "Reen", - "onboarding.follows.done": "Farita", "onboarding.follows.empty": "Bedaŭrinde, neniu rezulto estas montrebla nuntempe. Vi povas provi serĉi aŭ foliumi la esploran paĝon por trovi kontojn por sekvi, aŭ retrovi baldaŭ.", "onboarding.follows.search": "Serĉi", "onboarding.follows.title": "Sekvi homojn por komenci", @@ -708,7 +707,6 @@ "onboarding.profile.display_name_hint": "Via plena nomo aŭ via kromnomo…", "onboarding.profile.note": "Sinprezento", "onboarding.profile.note_hint": "Vi povas @mencii aliajn homojn aŭ #kradvortojn…", - "onboarding.profile.save_and_continue": "Konservi kaj daŭrigi", "onboarding.profile.title": "Profila fikso", "onboarding.profile.upload_avatar": "Alŝuti profilbildon", "onboarding.profile.upload_header": "Alŝuti profilkapbildon", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 4473576b81cce3..51e30303e05503 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "No recomendamos el uso de emojis personalizados en combinación con direcciones web. Los campos personalizados que contengan ambos solo se mostrarán como texto en lugar de como enlace, con el fin de evitar la confusión del usuario.", "account_edit.field_edit_modal.name_hint": "Por ejemplo: «Sitio web personal»", "account_edit.field_edit_modal.name_label": "Etiqueta", - "account_edit.field_edit_modal.value_hint": "Por ejemplo: «ejemplo.com.ar»", + "account_edit.field_edit_modal.url_warning": "Para agregar un enlace, por favor, incluí {protocol} al comienzo.", + "account_edit.field_edit_modal.value_hint": "Por ejemplo: «https://ejemplo.com»", "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "El arrastre fue cancelado. Se soltó el campo «{item}».", "account_edit.field_reorder_modal.drag_end": "Se soltó el campo «{item}».", @@ -959,7 +960,6 @@ "notifications_permission_banner.how_to_control": "Para recibir notificaciones cuando Mastodon no está abierto, habilitá las notificaciones de escritorio. Podés controlar con precisión qué tipos de interacciones generan notificaciones de escritorio a través del botón {icon} de arriba, una vez que estén habilitadas.", "notifications_permission_banner.title": "No te pierdas nada", "onboarding.follows.back": "Volver", - "onboarding.follows.done": "Listo", "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Podés intentar usar la búsqueda o navegar por la página de exploración para encontrar cuentas a las que seguir, o intentarlo de nuevo más tarde.", "onboarding.follows.search": "Buscar", "onboarding.follows.title": "Para comenzar, empezá a seguir cuentas", @@ -969,7 +969,6 @@ "onboarding.profile.display_name_hint": "Tu nombre completo o tu pseudónimo…", "onboarding.profile.note": "Biografía", "onboarding.profile.note_hint": "Podés @mencionar otras cuentas o usar #etiquetas…", - "onboarding.profile.save_and_continue": "Guardar y continuar", "onboarding.profile.title": "Configuración del perfil", "onboarding.profile.upload_avatar": "Subir avatar", "onboarding.profile.upload_header": "Subir cabecera", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 2259fc08b43ca2..6d99e2dbf51fd7 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "No recomendamos el uso de emojis personalizados en combinación con direcciones URL. Los campos personalizados que contengan ambos se mostrarán solo como texto en lugar de como un enlace, con el fin de evitar confusiones al usuario.", "account_edit.field_edit_modal.name_hint": "Por ejemplo, «sitio web personal»", "account_edit.field_edit_modal.name_label": "Etiqueta", - "account_edit.field_edit_modal.value_hint": "Por ejemplo, «ejemplo.me»", + "account_edit.field_edit_modal.url_warning": "Para agregar un enlace, incluye {protocol} al principio.", + "account_edit.field_edit_modal.value_hint": "Por ejemplo: “https://ejemplo.me”", "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "Se canceló el arrastre. Se eliminó el campo «{item}».", "account_edit.field_reorder_modal.drag_end": "Se ha eliminado el campo «{item}».", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "Para recibir notificaciones cuando Mastodon no esté abierto, habilite las notificaciones de escritorio. Puedes controlar con precisión qué tipos de interacciones generan notificaciones de escritorio a través del botón {icon} de arriba una vez que estén habilitadas.", "notifications_permission_banner.title": "Nunca te pierdas nada", "onboarding.follows.back": "Volver", - "onboarding.follows.done": "Hecho", "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar gente a la que seguir, o inténtalo de nuevo más tarde.", + "onboarding.follows.next": "Siguiente: Configura tu perfil", "onboarding.follows.search": "Buscar", "onboarding.follows.title": "Sigue personas para comenzar", "onboarding.profile.discoverable": "Hacer que mi perfil aparezca en búsquedas", "onboarding.profile.discoverable_hint": "Cuando permites que tu perfil aparezca en búsquedas en Mastodon, tus publicaciones pueden aparecer en los resultados de búsqueda y en las tendencias, y tu perfil puede ser sugerido a personas con intereses similares a los tuyos.", "onboarding.profile.display_name": "Nombre para mostrar", "onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…", + "onboarding.profile.finish": "Fiinalizar", "onboarding.profile.note": "Biografía", "onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #etiquetas…", - "onboarding.profile.save_and_continue": "Guardar y continuar", "onboarding.profile.title": "Configuración del perfil", "onboarding.profile.upload_avatar": "Subir foto de perfil", "onboarding.profile.upload_header": "Subir encabezado de perfil", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index a63428c453169b..3bf85dc81608ce 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -173,7 +173,6 @@ "account_edit.field_edit_modal.link_emoji_warning": "Recomendamos no usar emojis personalizados combinados con enlaces. Los campos personalizados que contengan ambos solo se mostrarán como texto en vez de un enlace, para evitar confusiones.", "account_edit.field_edit_modal.name_hint": "Ej. \"Web personal\"", "account_edit.field_edit_modal.name_label": "Etiqueta", - "account_edit.field_edit_modal.value_hint": "Ej. \"ejemplo.yo\"", "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "El arrastre se ha cancelado. El campo \"{item}\" se ha soltado.", "account_edit.field_reorder_modal.drag_end": "El campo \"{item}\" se ha soltado.", @@ -959,7 +958,6 @@ "notifications_permission_banner.how_to_control": "Para recibir notificaciones cuando Mastodon no esté abierto, habilite las notificaciones de escritorio. Puedes controlar con precisión qué tipos de interacciones generan notificaciones de escritorio a través del botón {icon} de arriba una vez que estén habilitadas.", "notifications_permission_banner.title": "Nunca te pierdas nada", "onboarding.follows.back": "Atrás", - "onboarding.follows.done": "Hecho", "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar personas a las que seguir, o inténtalo de nuevo más tarde.", "onboarding.follows.search": "Buscar", "onboarding.follows.title": "Sigue personas para comenzar", @@ -969,7 +967,6 @@ "onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…", "onboarding.profile.note": "Biografía", "onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #etiquetas…", - "onboarding.profile.save_and_continue": "Guardar y continuar", "onboarding.profile.title": "Configuración del perfil", "onboarding.profile.upload_avatar": "Subir foto de perfil", "onboarding.profile.upload_header": "Subir encabezado de perfil", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index 51a9abb35c3202..638e7238633bbf 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -845,7 +845,6 @@ "notifications_permission_banner.how_to_control": "Et saada teateid, ajal mil Mastodon pole avatud, luba töölauamärguanded. Saad täpselt määrata, mis tüüpi tegevused tekitavad märguandeid, kasutates peale teadaannete sisse lülitamist üleval olevat nuppu {icon}.", "notifications_permission_banner.title": "Ära jää millestki ilma", "onboarding.follows.back": "Tagasi", - "onboarding.follows.done": "Valmis", "onboarding.follows.empty": "Kahjuks ei saa hetkel tulemusi näidata. Proovi kasutada otsingut või lehitse uurimise lehte, et leida inimesi, keda jälgida, või proovi hiljem uuesti.", "onboarding.follows.search": "Otsi", "onboarding.follows.title": "Jälgi inimesi, et alustada", @@ -855,7 +854,6 @@ "onboarding.profile.display_name_hint": "Su täisnimi või naljanimi…", "onboarding.profile.note": "Elulugu", "onboarding.profile.note_hint": "Saad @mainida teisi kasutajaid või lisada #teemaviidet…", - "onboarding.profile.save_and_continue": "Salvesta ja jätka", "onboarding.profile.title": "Profiili seadistamine", "onboarding.profile.upload_avatar": "Laadi üles profiilipilt", "onboarding.profile.upload_header": "Laadi üles profiili päis", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index fb50eff98b0fc7..778207fc85a578 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -751,7 +751,6 @@ "notifications_permission_banner.how_to_control": "Mastodon irekita ez dagoenean jakinarazpenak jasotzeko, gaitu mahaigaineko jakinarazpenak. Mahaigaineko jakinarazpenak ze elkarrekintzak eragingo dituzten zehazki kontrolatu dezakezu goiko {icon} botoia erabiliz, gaituta daudenean.", "notifications_permission_banner.title": "Ez galdu ezer inoiz", "onboarding.follows.back": "Atzera", - "onboarding.follows.done": "Egina", "onboarding.follows.empty": "Zoritxarrez, ezin da emaitzik erakutsi orain. Bilaketa erabil dezakezu edo Arakatu orrian jendea bilatu jarraitzeko, edo saiatu geroago.", "onboarding.follows.search": "Bilatu", "onboarding.follows.title": "Jarraitu jendea hasteko", @@ -761,7 +760,6 @@ "onboarding.profile.display_name_hint": "Zure izena edo ezizena…", "onboarding.profile.note": "Biografia", "onboarding.profile.note_hint": "Beste pertsona batzuk @aipa ditzakezu edo #traolak erabili…", - "onboarding.profile.save_and_continue": "Gorde eta jarraitu", "onboarding.profile.title": "Profilaren konfigurazioa", "onboarding.profile.upload_avatar": "Igo profilaren irudia", "onboarding.profile.upload_header": "Igo profilaren goiburua", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 9501be8b939e71..3cd7bfaf801d87 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -751,7 +751,6 @@ "notifications_permission_banner.how_to_control": "برای دریافت آگاهی‌ها هنگام باز نبودن ماستودون، آگاهی‌های میزکار را به کار بیندازید. پس از به کار افتادنشان می‌توانید گونه‌های دقیق برهم‌کنش‌هایی که آگاهی‌های میزکار تولید می‌کنند را از {icon} بالا واپایید.", "notifications_permission_banner.title": "هرگز چیزی را از دست ندهید", "onboarding.follows.back": "بازگشت", - "onboarding.follows.done": "انجام شد", "onboarding.follows.empty": "متأسفانه هم‌اکنون نتیجه‌ای قابل نمایش نیست. می‌توانید استفاده از جست‌وجو یا مرور صفحهٔ کاوش را برای یافتن افرادی برای پی‌گیری آزموده یا دوباره تلاش کنید.", "onboarding.follows.search": "جست‌وجو", "onboarding.follows.title": "پی گرفتن افرادی برای آغاز", @@ -761,7 +760,6 @@ "onboarding.profile.display_name_hint": "نام کامل یا نام باحالتان…", "onboarding.profile.note": "درباره شما", "onboarding.profile.note_hint": "می‌توانید افراد دیگر را @نام‌بردن یا #برچسب بزنید…", - "onboarding.profile.save_and_continue": "ذخیره کن و ادامه بده", "onboarding.profile.title": "تنظیم نمایه", "onboarding.profile.upload_avatar": "بازگذاری تصویر نمایه", "onboarding.profile.upload_header": "بارگذاری تصویر سردر نمایه", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 2d84047f134505..f7f529a63921ed 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Emme suosittele käyttämään mukautettuja emojeita URL-osoitteiden kanssa. Molempia sisältävät mukautetut kentät näkyvät vain tekstinä linkin sijaan, jotta estetään käyttäjien sekaannus.", "account_edit.field_edit_modal.name_hint": "Esim. ”Henkilökohtainen verkkosivusto”", "account_edit.field_edit_modal.name_label": "Nimike", - "account_edit.field_edit_modal.value_hint": "Esim. ”example.me”", + "account_edit.field_edit_modal.url_warning": "Lisää linkki sisällyttämällä alkuun {protocol}.", + "account_edit.field_edit_modal.value_hint": "Esim. ”https://example.me”", "account_edit.field_edit_modal.value_label": "Arvo", "account_edit.field_reorder_modal.drag_cancel": "Veto peruttu. Kenttää ”{item}” ei siirretty.", "account_edit.field_reorder_modal.drag_end": "Kenttä ”{item}” pudotettu.", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "Saadaksesi ilmoituksia, kun Mastodon ei ole auki, ota työpöytäilmoitukset käyttöön. Voit hallita tarkasti, mistä saat työpöytäilmoituksia kun ilmoitukset on otettu käyttöön yllä olevan {icon}-painikkeen kautta.", "notifications_permission_banner.title": "Älä anna minkään mennä ohi", "onboarding.follows.back": "Takaisin", - "onboarding.follows.done": "Valmis", "onboarding.follows.empty": "Valitettavasti tuloksia ei voida näyttää juuri nyt. Voit kokeilla hakua tai selata tutustumissivua löytääksesi seurattavaa tai yrittää myöhemmin uudelleen.", + "onboarding.follows.next": "Seuraavaksi: määrittele profiilisi", "onboarding.follows.search": "Haku", "onboarding.follows.title": "Aloita seuraamalla käyttäjiä", "onboarding.profile.discoverable": "Aseta profiilini löydettäväksi", "onboarding.profile.discoverable_hint": "Kun olet määrittänyt itsesi löydettäväksi Mastodonista, julkaisusi voivat näkyä hakutuloksissa ja suosituissa kohteissa. Lisäksi profiiliasi voidaan ehdottaa käyttäjille, jotka ovat kiinnostuneita kanssasi samoista aiheista.", "onboarding.profile.display_name": "Näyttönimi", "onboarding.profile.display_name_hint": "Koko nimesi tai lempinimesi…", + "onboarding.profile.finish": "Valmis", "onboarding.profile.note": "Elämäkerta", "onboarding.profile.note_hint": "Voit @mainita muita käyttäjiä tai #aihetunnisteita…", - "onboarding.profile.save_and_continue": "Tallenna ja jatka", "onboarding.profile.title": "Profiilin määritys", "onboarding.profile.upload_avatar": "Lähetä profiilikuva", "onboarding.profile.upload_header": "Lähetä profiilin otsakekuva", diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json index 82c1616ddf2f7f..c31f8c7681ea35 100644 --- a/app/javascript/mastodon/locales/fil.json +++ b/app/javascript/mastodon/locales/fil.json @@ -313,7 +313,6 @@ "notifications.policy.filter_not_followers_title": "Mga taong hindi ka susundan", "notifications.policy.filter_not_following_title": "Mga taong hindi mo sinusundan", "onboarding.profile.note_hint": "Maaari mong @bangitin ang ibang mga tao o mga #hashtag…", - "onboarding.profile.save_and_continue": "Iimbak at magpatuloy", "picture_in_picture.restore": "Ilagay ito pabalik", "poll.closed": "Sarado", "poll.reveal": "Ipakita ang mga resulta", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 799b0a4bc4f129..d586fd57810f74 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -915,7 +915,6 @@ "notifications_permission_banner.how_to_control": "Ger skriviborðsfráboðanir virknar fyri at móttaka fráboðanir, tá Mastodon ikki er opið. Tá tær eru gjørdar virknar, kanst tú stýra, hvørji sløg av samvirkni geva skriviborðsfráboðanir. Hetta umvegis {icon} knøttin omanfyri.", "notifications_permission_banner.title": "Miss einki", "onboarding.follows.back": "Aftur", - "onboarding.follows.done": "Liðugt", "onboarding.follows.empty": "Tíverri kunnu eingi úrslit vísast beint nú. Tú kanst royna at brúka leiting ella at kaga gjøgnum Rannsaka síðuna fyri at finna fólk at fylgja - ella royna aftur seinni.", "onboarding.follows.search": "Leita", "onboarding.follows.title": "Fylg fólki fyri at koma í gongd", @@ -925,7 +924,6 @@ "onboarding.profile.display_name_hint": "Títt fulla navn ella títt stuttliga navn…", "onboarding.profile.note": "Ævilýsing", "onboarding.profile.note_hint": "Tú kanst @umrøða onnur fólk ella #frámerki…", - "onboarding.profile.save_and_continue": "Goym og halt fram", "onboarding.profile.title": "Vangauppsetan", "onboarding.profile.upload_avatar": "Legg vangamynd upp", "onboarding.profile.upload_header": "Legg vangahøvd upp", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 6360c49eeb2f9d..007baa530c28cd 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -18,12 +18,12 @@ "account.add_note": "Ajouter une note personnelle", "account.add_or_remove_from_list": "Ajouter ou enlever de listes", "account.badges.admin": "Admin", - "account.badges.blocked": "Bloqué", + "account.badges.blocked": "Bloqué·e", "account.badges.bot": "Bot", "account.badges.domain_blocked": "Domaine bloqué", "account.badges.group": "Groupe", - "account.badges.muted": "Masqué", - "account.badges.muted_until": "Masqué jusqu’au {until}", + "account.badges.muted": "Masqué·e", + "account.badges.muted_until": "Masqué·e jusqu’au {until}", "account.block": "Bloquer @{name}", "account.block_domain": "Bloquer le domaine {domain}", "account.block_short": "Bloquer", @@ -39,7 +39,7 @@ "account.edit_profile_short": "Modifier", "account.enable_notifications": "Me notifier quand @{name} publie", "account.endorse": "Inclure sur profil", - "account.familiar_followers_many": "Suivi par {name1}, {name2}, et {othersCount, plural, one {une autre personne que vous suivez} other {# autres personnes que vous suivez}}", + "account.familiar_followers_many": "Suivi·e par {name1}, {name2}, et {othersCount, plural, one {une autre personne que vous suivez} other {# autres personnes que vous suivez}}", "account.familiar_followers_one": "Suivi·e par {name1}", "account.familiar_followers_two": "Suivi·e par {name1} et {name2}", "account.featured": "En vedette", @@ -65,7 +65,7 @@ "account.followers": "abonné·e·s", "account.followers.empty": "Personne ne suit ce compte pour l'instant.", "account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}", - "account.followers_you_know_counter": "{count, plural, one {{counter} suivi·e}, other {{counter} suivi·e·s}}", + "account.followers_you_know_counter": "{counter} que vous suivez", "account.following": "Abonné·e", "account.following_counter": "{count, plural, one {{counter} abonnement} other {{counter} abonnements}}", "account.follows.empty": "Ce compte ne suit personne présentement.", @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Nous déconseillons l'usage d'émoji personnalisé avec les URL. Les champs personnalisés contenant les deux seront affichés comme du texte et non un lien, afin d'éviter toute confusion.", "account_edit.field_edit_modal.name_hint": "Par exemple « Site Web personnel »", "account_edit.field_edit_modal.name_label": "Libellé", - "account_edit.field_edit_modal.value_hint": "Par exemple « exemple.me »", + "account_edit.field_edit_modal.url_warning": "Pour ajouter un lien, veuillez inclure {protocol} au début.", + "account_edit.field_edit_modal.value_hint": "Par exemple « https://exemple.me »", "account_edit.field_edit_modal.value_label": "Valeur", "account_edit.field_reorder_modal.drag_cancel": "Déplacement annulé. Le champ « {item} » a été redéposé.", "account_edit.field_reorder_modal.drag_end": "Le champ « {item} » a été déposé.", @@ -959,7 +960,6 @@ "notifications_permission_banner.how_to_control": "Pour recevoir des notifications lorsque Mastodon n’est pas ouvert, activez les notifications de bureau. Vous pouvez contrôler précisément quels types d’interactions génèrent des notifications de bureau via le bouton {icon} ci-dessus une fois qu’elles sont activées.", "notifications_permission_banner.title": "Ne rien rater", "onboarding.follows.back": "Retour", - "onboarding.follows.done": "Terminé", "onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer de rechercher ou de parcourir la page \"Explorer\" pour trouver des personnes à suivre, ou réessayer plus tard.", "onboarding.follows.search": "Recherche", "onboarding.follows.title": "Suivre des personnes pour commencer", @@ -969,7 +969,6 @@ "onboarding.profile.display_name_hint": "Votre nom complet ou votre nom rigolo…", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "Vous pouvez @mentionner d'autres personnes ou #hashtags…", - "onboarding.profile.save_and_continue": "Enregistrer et continuer", "onboarding.profile.title": "Configuration du profil", "onboarding.profile.upload_avatar": "Importer une photo de profil", "onboarding.profile.upload_header": "Importer un entête de profil", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index acde4d4a9271d5..6eb2e0a216c6f2 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -18,12 +18,12 @@ "account.add_note": "Ajouter une note personnelle", "account.add_or_remove_from_list": "Ajouter ou retirer des listes", "account.badges.admin": "Admin", - "account.badges.blocked": "Bloqué", + "account.badges.blocked": "Bloqué·e", "account.badges.bot": "Bot", "account.badges.domain_blocked": "Domaine bloqué", "account.badges.group": "Groupe", - "account.badges.muted": "Masqué", - "account.badges.muted_until": "Masqué jusqu’au {until}", + "account.badges.muted": "Masqué·e", + "account.badges.muted_until": "Masqué·e jusqu’au {until}", "account.block": "Bloquer @{name}", "account.block_domain": "Bloquer le domaine {domain}", "account.block_short": "Bloquer", @@ -39,7 +39,7 @@ "account.edit_profile_short": "Modifier", "account.enable_notifications": "Me notifier les publications de @{name}", "account.endorse": "Recommander sur votre profil", - "account.familiar_followers_many": "Suivi par {name1}, {name2}, et {othersCount, plural, one {une autre personne que vous suivez} other {# autres personnes que vous suivez}}", + "account.familiar_followers_many": "Suivi·e par {name1}, {name2}, et {othersCount, plural, one {une autre personne que vous suivez} other {# autres personnes que vous suivez}}", "account.familiar_followers_one": "Suivi·e par {name1}", "account.familiar_followers_two": "Suivi·e par {name1} et {name2}", "account.featured": "En vedette", @@ -65,14 +65,14 @@ "account.followers": "Abonné·e·s", "account.followers.empty": "Personne ne suit cet·te utilisateur·rice pour l’instant.", "account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}", - "account.followers_you_know_counter": "{count, plural, one {{counter} suivi·e}, other {{counter} suivi·e·s}}", + "account.followers_you_know_counter": "{counter} que vous suivez", "account.following": "Abonnements", "account.following_counter": "{count, plural, one {{counter} abonnement} other {{counter} abonnements}}", "account.follows.empty": "Cet·te utilisateur·rice ne suit personne pour l’instant.", "account.follows_you": "Vous suit", "account.go_to_profile": "Voir le profil", "account.hide_reblogs": "Masquer les partages de @{name}", - "account.in_memoriam": "En mémoire de.", + "account.in_memoriam": "En mémoire.", "account.joined_long": "Ici depuis le {date}", "account.joined_short": "Ici depuis", "account.languages": "Modifier les langues d'abonnements", @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Nous déconseillons l'usage d'émoji personnalisé avec les URL. Les champs personnalisés contenant les deux seront affichés comme du texte et non un lien, afin d'éviter toute confusion.", "account_edit.field_edit_modal.name_hint": "Par exemple « Site Web personnel »", "account_edit.field_edit_modal.name_label": "Libellé", - "account_edit.field_edit_modal.value_hint": "Par exemple « exemple.me »", + "account_edit.field_edit_modal.url_warning": "Pour ajouter un lien, veuillez inclure {protocol} au début.", + "account_edit.field_edit_modal.value_hint": "Par exemple « https://exemple.me »", "account_edit.field_edit_modal.value_label": "Valeur", "account_edit.field_reorder_modal.drag_cancel": "Déplacement annulé. Le champ « {item} » a été redéposé.", "account_edit.field_reorder_modal.drag_end": "Le champ « {item} » a été déposé.", @@ -959,7 +960,6 @@ "notifications_permission_banner.how_to_control": "Pour recevoir des notifications lorsque Mastodon n’est pas ouvert, activez les notifications du bureau. Vous pouvez contrôler précisément quels types d’interactions génèrent des notifications de bureau via le bouton {icon} ci-dessus une fois qu’elles sont activées.", "notifications_permission_banner.title": "Toujours au courant", "onboarding.follows.back": "Retour", - "onboarding.follows.done": "Terminé", "onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer d'utiliser la recherche ou parcourir la page de découverte pour trouver des personnes à suivre, ou réessayez plus tard.", "onboarding.follows.search": "Recherche", "onboarding.follows.title": "Suivre des personnes pour commencer", @@ -969,7 +969,6 @@ "onboarding.profile.display_name_hint": "Votre nom complet ou votre nom rigolo…", "onboarding.profile.note": "Biographie", "onboarding.profile.note_hint": "Vous pouvez @mentionner d'autres personnes ou #hashtags…", - "onboarding.profile.save_and_continue": "Enregistrer et continuer", "onboarding.profile.title": "Configuration du profil", "onboarding.profile.upload_avatar": "Importer une photo de profil", "onboarding.profile.upload_header": "Importer un entête de profil", diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json index 230dae1d97a595..9592886fa6c182 100644 --- a/app/javascript/mastodon/locales/fy.json +++ b/app/javascript/mastodon/locales/fy.json @@ -664,7 +664,6 @@ "notifications_permission_banner.how_to_control": "Om meldingen te ûntfangen wannear’t Mastodon net iepen stiet. Jo kinne krekt bepale hokker soarte fan ynteraksjes wol of gjin desktopmeldingen jouwe fia de boppesteande {icon} knop.", "notifications_permission_banner.title": "Mis neat", "onboarding.follows.back": "Tebek", - "onboarding.follows.done": "Klear", "onboarding.follows.empty": "Spitigernôch kinne op dit stuit gjin resultaten toand wurde. Jo kinne probearje te sykjen of te blêdzjen troch de ferkenningsside om minsken te finen dy’t jo folgje kinne, of probearje it letter opnij.", "onboarding.follows.search": "Sykje", "onboarding.follows.title": "Folgje minsken om te begjinnen", @@ -674,7 +673,6 @@ "onboarding.profile.display_name_hint": "Jo folsleine namme of in aardige bynamme…", "onboarding.profile.note": "Biografy", "onboarding.profile.note_hint": "Jo kinne oare minsken @fermelde of #hashtags brûke…", - "onboarding.profile.save_and_continue": "Bewarje en trochgean", "onboarding.profile.title": "Profyl ynstelle", "onboarding.profile.upload_avatar": "Profylfoto oplade", "onboarding.profile.upload_header": "Omslachfoto foar profyl oplade", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 48139071456bad..3374b69c41af37 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -173,7 +173,6 @@ "account_edit.field_edit_modal.link_emoji_warning": "Molaimid gan emoji saincheaptha a úsáid i gcomhar le Urlanna. Taispeánfar réimsí saincheaptha ina bhfuil an dá cheann mar théacs amháin seachas mar nasc, chun mearbhall úsáideoirí a sheachaint.", "account_edit.field_edit_modal.name_hint": "M.sh. “Suíomh Gréasáin pearsanta”", "account_edit.field_edit_modal.name_label": "Lipéad", - "account_edit.field_edit_modal.value_hint": "M.sh. “shampla.me”", "account_edit.field_edit_modal.value_label": "Luach", "account_edit.field_reorder_modal.drag_cancel": "Cuireadh an tarraingt ar ceal. Baineadh an réimse \"{item}\".", "account_edit.field_reorder_modal.drag_end": "Baineadh an réimse \"{item}\".", @@ -959,7 +958,6 @@ "notifications_permission_banner.how_to_control": "Chun fógraí a fháil nuair nach bhfuil Mastodon oscailte, cumasaigh fógraí deisce. Is féidir leat a rialú go beacht cé na cineálacha idirghníomhaíochtaí a ghineann fógraí deisce tríd an gcnaipe {icon} thuas nuair a bhíonn siad cumasaithe.", "notifications_permission_banner.title": "Ná caill aon rud go deo", "onboarding.follows.back": "Ar ais", - "onboarding.follows.done": "Déanta", "onboarding.follows.empty": "Ar an drochuair, ní féidir aon torthaí a thaispeáint faoi láthair. Is féidir leat triail a bhaint as cuardach nó brabhsáil ar an leathanach taiscéalaíochta chun teacht ar dhaoine le leanúint, nó bain triail eile as níos déanaí.", "onboarding.follows.search": "Cuardach", "onboarding.follows.title": "Lean daoine le tosú", @@ -969,7 +967,6 @@ "onboarding.profile.display_name_hint": "D’ainm iomlán nó d’ainm spraíúil…", "onboarding.profile.note": "Bith", "onboarding.profile.note_hint": "Is féidir leat @ daoine eile a lua nó #hashtags…", - "onboarding.profile.save_and_continue": "Sábháil agus lean ar aghaidh", "onboarding.profile.title": "Socrú próifíle", "onboarding.profile.upload_avatar": "Íosluchtaigh pictiúr próifíl", "onboarding.profile.upload_header": "Íoslódáil an ceanntásca próifíl", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index 521dcbd200245b..0ca7f12e3ed18f 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -752,7 +752,6 @@ "notifications_permission_banner.how_to_control": "Airson brathan fhaighinn nuair nach eil Mastodon fosgailte, cuir na brathan deasga an comas. Tha an smachd agad fhèin air dè na seòrsaichean de chonaltradh a ghineas brathan deasga leis a’ phutan {icon} gu h-àrd nuair a bhios iad air an cur an comas.", "notifications_permission_banner.title": "Na caill dad gu bràth tuilleadh", "onboarding.follows.back": "Air ais", - "onboarding.follows.done": "Deiseil", "onboarding.follows.empty": "Gu mì-fhortanach, chan urrainn dhuinn toradh a shealltainn an-dràsta. Feuch gleus an luirg no duilleag an rùrachaidh airson daoine ri leantainn a lorg no feuch ris a-rithist an ceann tamaill.", "onboarding.follows.search": "Lorg", "onboarding.follows.title": "Lean daoine airson tòiseachadh", @@ -762,7 +761,6 @@ "onboarding.profile.display_name_hint": "D’ ainm slàn no spòrsail…", "onboarding.profile.note": "Cunntas-beatha", "onboarding.profile.note_hint": "’S urrainn dhut @iomradh a thoirt air càch no air #tagaicheanHais…", - "onboarding.profile.save_and_continue": "Sàbhail ’s lean air adhart", "onboarding.profile.title": "Suidheachadh na pròifile", "onboarding.profile.upload_avatar": "Luchdaich suas dealbh na pròifil", "onboarding.profile.upload_header": "Luchdaich suas bann-cinn na pròifil", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 3d912253614656..4a7780ff848504 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Non recomendamos o uso de emojis persoais combinados con URLs. Os campos persoais que conteñen ambos móstranse só como texto e non como unha ligazón, para evitar a confusión de quen os lea.", "account_edit.field_edit_modal.name_hint": "Ex. \"Páxina web persoal\"", "account_edit.field_edit_modal.name_label": "Etiqueta", - "account_edit.field_edit_modal.value_hint": "Ex. \"exemplo.gal\"", + "account_edit.field_edit_modal.url_warning": "Para engadir unha ligazón, inclúe {protocol} diante.", + "account_edit.field_edit_modal.value_hint": "Ex. “https://exemplo.me”", "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "Cancelouse o arrastrado. Soltouse o campo \"{item}\".", "account_edit.field_reorder_modal.drag_end": "Soltouse o campo \"{item}\".", @@ -959,17 +960,17 @@ "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", "onboarding.follows.back": "Volver", - "onboarding.follows.done": "Feito", "onboarding.follows.empty": "Desgraciadamente agora mesmo non hai nada que mostrar. Podes intentalo coa busca ou na páxina descubrir para atopar persoas ás que seguir, ou intentalo máis tarde.", + "onboarding.follows.next": "Seguinte: Configura o teu perfil", "onboarding.follows.search": "Buscar", "onboarding.follows.title": "Comeza seguindo algunhas persoas", "onboarding.profile.discoverable": "Que o meu perfil se poida atopar", "onboarding.profile.discoverable_hint": "Cando elixes que poidan atoparte en Mastodon as túas publicacións aparecerán nos resultados das buscas e nos temas en voga, e o teu perfil podería ser suxerido para seguimento a persoas con intereses semellantes aos teus.", "onboarding.profile.display_name": "Nome público", "onboarding.profile.display_name_hint": "O teu nome completo ou un nome divertido…", + "onboarding.profile.finish": "Finalizar", "onboarding.profile.note": "Acerca de ti", "onboarding.profile.note_hint": "Podes @mencionar a outras persoas ou usar #cancelos…", - "onboarding.profile.save_and_continue": "Gardar e continuar", "onboarding.profile.title": "Configuración do perfil", "onboarding.profile.upload_avatar": "Subir imaxe do perfil", "onboarding.profile.upload_header": "Subir cabeceira para o perfil", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 66fd6f710b9712..c3f7f818187908 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -173,7 +173,6 @@ "account_edit.field_edit_modal.link_emoji_warning": "אנו ממליצים נגד שימוש באמוג'י ייחודיים ביחד עם URL. שדות מיוחדים שמכילים את שניהם יופיעו כמלל בלבד ולא כקישור, כדי למנוע בלבול משתמשים.", "account_edit.field_edit_modal.name_hint": "למשל \"אתר אישי\"", "account_edit.field_edit_modal.name_label": "תווית", - "account_edit.field_edit_modal.value_hint": "למשל “example.me”", "account_edit.field_edit_modal.value_label": "ערך", "account_edit.field_reorder_modal.drag_cancel": "הגרירה בוטלה. השדה \"{item}\" נעזב.", "account_edit.field_reorder_modal.drag_end": "השדה \"{item}\" נעזב.", @@ -959,7 +958,6 @@ "notifications_permission_banner.how_to_control": "כדי לקבל התראות גם כאשר מסטודון סגור יש לאפשר התראות מסך. ניתן לשלוט בדיוק איזה סוג של אינטראקציות יביא להתראות מסך דרך כפתור ה- {icon} מרגע שהן מאופשרות.", "notifications_permission_banner.title": "לעולם אל תחמיץ דבר", "onboarding.follows.back": "בחזרה", - "onboarding.follows.done": "בוצע", "onboarding.follows.empty": "למצער, תוצאות לחיפושך אינן בנמצא. ניתן להשתמש בחיפוש או בדף החקירות לשם מציאת אנשים ולעקבם. אפשר גם לנסות שוב אחר כך.", "onboarding.follows.search": "חיפוש", "onboarding.follows.title": "כדי להתחיל, יש לעקוב אחרי אנשים", @@ -969,7 +967,6 @@ "onboarding.profile.display_name_hint": "שמך המלא או כינוי הכיף שלך…", "onboarding.profile.note": "אודות", "onboarding.profile.note_hint": "ניתן @לאזכר משתמשים אחרים או #תגיות…", - "onboarding.profile.save_and_continue": "לשמור ולהמשיך", "onboarding.profile.title": "הגדרת פרופיל", "onboarding.profile.upload_avatar": "העלאת תמונת פרופיל", "onboarding.profile.upload_header": "העלאת כותרת פרופיל", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 4e3c3fb7df9247..f58a33925dcf8f 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Nem javasoljuk az egyéni emodzsik és webcímek együttes használatát. A mindkettőt tartalmazó egyéni mezők a felhasználók megzavarásának elkerülése érdekében csak szövegként jelennek meg, nem hivatkozásként.", "account_edit.field_edit_modal.name_hint": "Például „Személyes webhely”", "account_edit.field_edit_modal.name_label": "Címke", - "account_edit.field_edit_modal.value_hint": "Például „example.me”", + "account_edit.field_edit_modal.url_warning": "Hivatkozás hozzáadásakor add meg a {protocol} protokollt az elején.", + "account_edit.field_edit_modal.value_hint": "Például „https://example.me”", "account_edit.field_edit_modal.value_label": "Érték", "account_edit.field_reorder_modal.drag_cancel": "Az áthúzás megszakítva. A(z) „{item}” mező el lett dobva.", "account_edit.field_reorder_modal.drag_end": "A(z) „{item}” mező el lett dobva.", @@ -959,7 +960,6 @@ "notifications_permission_banner.how_to_control": "Ahhoz, hogy értesítéseket kapj akkor, amikor a Mastodon nincs megnyitva, engedélyezd az asztali értesítéseket. Pontosan be tudod állítani, hogy milyen interakciókról értesülj a fenti {icon} gombon keresztül, ha egyszer már engedélyezted őket.", "notifications_permission_banner.title": "Soha ne mulassz el semmit", "onboarding.follows.back": "Vissza", - "onboarding.follows.done": "Kész", "onboarding.follows.empty": "Sajnos jelenleg nem jeleníthető meg eredmény. Kipróbálhatod a keresést vagy böngészheted a felfedező oldalon a követni kívánt személyeket, vagy próbáld meg később.", "onboarding.follows.search": "Keresés", "onboarding.follows.title": "A kezdéshez kezdj el embereket követni", @@ -969,7 +969,6 @@ "onboarding.profile.display_name_hint": "Teljes neved vagy vicces neved…", "onboarding.profile.note": "Bemutatkozás", "onboarding.profile.note_hint": "Megemlíthetsz @másokat vagy #hashtag-eket…", - "onboarding.profile.save_and_continue": "Mentés és folytatás", "onboarding.profile.title": "Profilbeállítás", "onboarding.profile.upload_avatar": "Profilkép feltöltése", "onboarding.profile.upload_header": "Profil fejléc feltöltése", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index d9a2f5e8447147..3f65776b91f016 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -707,7 +707,6 @@ "notifications_permission_banner.how_to_control": "Pro reciper notificationes quando Mastodon non es aperte, activa le notificationes de scriptorio. Post lor activation, es possibile controlar precisemente qual typos de interaction genera notificationes de scriptorio per medio del button {icon} hic supra.", "notifications_permission_banner.title": "Non mancar jammais a un cosa", "onboarding.follows.back": "Retro", - "onboarding.follows.done": "Facite", "onboarding.follows.empty": "Regrettabilemente, non es possibile monstrar resultatos al momento. Tu pote tentar usar le recerca o percurrer le pagina de exploration pro cercar personas a sequer, o tentar lo de novo plus tarde.", "onboarding.follows.search": "Cercar", "onboarding.follows.title": "Seque personas pro comenciar", @@ -717,7 +716,6 @@ "onboarding.profile.display_name_hint": "Tu nomine complete o tu supernomine…", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "Tu pote @mentionar altere personas o #hashtags…", - "onboarding.profile.save_and_continue": "Salvar e continuar", "onboarding.profile.title": "Configuration del profilo", "onboarding.profile.upload_avatar": "Incargar imagine de profilo", "onboarding.profile.upload_header": "Actualisar capite de profilo", diff --git a/app/javascript/mastodon/locales/ie.json b/app/javascript/mastodon/locales/ie.json index 1160973a63ddaf..77097b05c6482d 100644 --- a/app/javascript/mastodon/locales/ie.json +++ b/app/javascript/mastodon/locales/ie.json @@ -478,7 +478,6 @@ "onboarding.profile.display_name_hint": "Tui complet nómine o tui amusant nómine…", "onboarding.profile.note": "Biografie", "onboarding.profile.note_hint": "Tu posse @mentionar altri persones o #hashtags…", - "onboarding.profile.save_and_continue": "Conservar e avansar", "onboarding.profile.title": "Popular tu profil", "onboarding.profile.upload_avatar": "Cargar profil-portrete", "onboarding.profile.upload_header": "Cargar cap-image", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 13b38f40fe8977..9254164374f6fd 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -614,7 +614,6 @@ "notifications_permission_banner.how_to_control": "Por ganar savigi kande Mastodon ne es desklozita, ebligez komputilsavigi.", "notifications_permission_banner.title": "Irga kozo ne pasas vu", "onboarding.follows.back": "Retro", - "onboarding.follows.done": "Finis", "onboarding.follows.empty": "Regretinde, nula rezultajo povas montresar nune. Vu povas esforcar serchar, o irar al explorala pagino por trovar personi sequinda, o esforcar itere pose.", "onboarding.follows.search": "Serchar", "onboarding.follows.title": "Sequez personi por komencar", @@ -624,7 +623,6 @@ "onboarding.profile.display_name_hint": "Vua tota nomo o vua gaya nomo…", "onboarding.profile.note": "Biografio", "onboarding.profile.note_hint": "Vu povas @mencionar altra personi o #hashtagi…", - "onboarding.profile.save_and_continue": "Preservez e avancez", "onboarding.profile.title": "Kompletigez la profilo", "onboarding.profile.upload_avatar": "Kargez profiloportreto", "onboarding.profile.upload_header": "Kargez profilokapimajo", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 706e9f486526b6..603cf573cca140 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Við mælum gegn því að nota sérsniðin tjáningartákn saman með vefslóðum. Sérsniðnir reitir sem innihalda hvort tveggja munu birtast sem einungis texti í stað þess að vera tenglar, til að koma í veg fyrir að notendur taki annað í misgripum fyrir hitt.", "account_edit.field_edit_modal.name_hint": "T.d. \"Eigið vefsvæði\"", "account_edit.field_edit_modal.name_label": "Skýring", - "account_edit.field_edit_modal.value_hint": "T.d. \"minnvefur.is\"", + "account_edit.field_edit_modal.url_warning": "Til að bæta við tengli skaltu hafa {protocol} á undan.", + "account_edit.field_edit_modal.value_hint": "T.d. “https://vefur.is”", "account_edit.field_edit_modal.value_label": "Gildi", "account_edit.field_reorder_modal.drag_cancel": "Hætt var við að draga. Reitnum \"{item}\" var sleppt.", "account_edit.field_reorder_modal.drag_end": "Reitnum \"{item}\" var sleppt.", @@ -959,7 +960,6 @@ "notifications_permission_banner.how_to_control": "Til að taka á móti tilkynningum þegar Mastodon er ekki opið, skaltu virkja tilkynningar á skjáborði. Þegar þær eru orðnar virkar geturðu stýrt nákvæmlega hverskonar atvik framleiða tilkynningar með því að nota {icon}-hnappinn hér fyrir ofan.", "notifications_permission_banner.title": "Aldrei missa af neinu", "onboarding.follows.back": "Til baka", - "onboarding.follows.done": "Lokið", "onboarding.follows.empty": "Því miður er ekki hægt að birta neinar niðurstöður í augnablikinu. Þú getur reynt að nota leitina eða skoðað könnunarsíðuna til að finna fólk til að fylgjast með, nú eða prófað aftur síðar.", "onboarding.follows.search": "Leita", "onboarding.follows.title": "Þú ættir að fylgjast með fólki til að komast í gang", @@ -969,7 +969,6 @@ "onboarding.profile.display_name_hint": "Fullt nafn þitt eða eitthvað til gamans…", "onboarding.profile.note": "Æviágrip", "onboarding.profile.note_hint": "Þú getur @minnst á annað fólk eða #myllumerki…", - "onboarding.profile.save_and_continue": "Vista og halda áfram", "onboarding.profile.title": "Uppsetning notandasniðs", "onboarding.profile.upload_avatar": "Sendu inn auðkennismynd", "onboarding.profile.upload_header": "Sendu inn bakgrunnsmynd í haus notandasniðs", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 9ae862aeddd165..384723db6d3b29 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -171,9 +171,10 @@ "account_edit.field_edit_modal.limit_header": "Superato il limite di caratteri consigliato", "account_edit.field_edit_modal.limit_message": "Gli utenti dai dispositivi mobili potrebbero non visualizzare completamente il tuo campo.", "account_edit.field_edit_modal.link_emoji_warning": "Sconsigliamo l'uso di emoji personalizzate in combinazione con gli URL. I campi personalizzati che contengono entrambi verranno visualizzati solo come testo anziché come link, in modo da evitare confusione nell'utente.", - "account_edit.field_edit_modal.name_hint": "Ad esempio: “Sito web personale”", + "account_edit.field_edit_modal.name_hint": "Per esempio: “Sito web personale”", "account_edit.field_edit_modal.name_label": "Etichetta", - "account_edit.field_edit_modal.value_hint": "Ad esempio: “example.me”", + "account_edit.field_edit_modal.url_warning": "Per aggiungere un collegamento, si prega di includere {protocol} all’inizio.", + "account_edit.field_edit_modal.value_hint": "Per esempio: “https://example.me”", "account_edit.field_edit_modal.value_label": "Valore", "account_edit.field_reorder_modal.drag_cancel": "Il trascinamento è stato annullato. Il campo \"{item}\" è stato eliminato.", "account_edit.field_reorder_modal.drag_end": "Il campo \"{item}\" è stato eliminato.", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "Per ricevere le notifiche quando Mastodon non è aperto, abilita le notifiche desktop. Puoi controllare precisamente quali tipi di interazioni generano le notifiche destkop, tramite il pulsante {icon} sopra, una volta abilitate.", "notifications_permission_banner.title": "Non perderti mai nulla", "onboarding.follows.back": "Indietro", - "onboarding.follows.done": "Fatto", "onboarding.follows.empty": "Sfortunatamente, nessun risultato può essere mostrato in questo momento. Puoi provare a utilizzare la ricerca o sfogliare la pagina di esplorazione per trovare persone da seguire, oppure riprova più tardi.", + "onboarding.follows.next": "Successivo: imposta il tuo profilo", "onboarding.follows.search": "Cerca", "onboarding.follows.title": "Segui le persone per iniziare", "onboarding.profile.discoverable": "Rendi il mio profilo rilevabile", "onboarding.profile.discoverable_hint": "Quando attivi la rilevabilità su Mastodon, i tuoi post potrebbero apparire nei risultati di ricerca e nelle tendenze e il tuo profilo potrebbe essere suggerito a persone con interessi simili ai tuoi.", "onboarding.profile.display_name": "Nome da visualizzare", "onboarding.profile.display_name_hint": "Il tuo nome completo o il tuo nome divertente…", + "onboarding.profile.finish": "Fine", "onboarding.profile.note": "Biografia", "onboarding.profile.note_hint": "Puoi @menzionare altre persone o #hashtags…", - "onboarding.profile.save_and_continue": "Salva e continua", "onboarding.profile.title": "Configurazione del profilo", "onboarding.profile.upload_avatar": "Carica l'immagine del profilo", "onboarding.profile.upload_header": "Carica l'intestazione del profilo", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 45cce53213d5b2..add5561977398a 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -695,7 +695,6 @@ "notifications_permission_banner.how_to_control": "Mastodonを閉じている間でも通知を受信するにはデスクトップ通知を有効にしてください。有効にすると上の {icon} ボタンから通知の内容を細かくカスタマイズできます。", "notifications_permission_banner.title": "お見逃しなく", "onboarding.follows.back": "戻る", - "onboarding.follows.done": "完了", "onboarding.follows.empty": "表示できる結果はありません。検索やエクスプローラーを使ったり、ほかのアカウントをフォローしたり、後でもう一度試しください。", "onboarding.follows.search": "検索", "onboarding.follows.title": "最初にフォローする人を選ぶ", @@ -705,7 +704,6 @@ "onboarding.profile.display_name_hint": "フルネーム、あるいは面白い名前など", "onboarding.profile.note": "自己紹介", "onboarding.profile.note_hint": "ほかのユーザーへのメンション (@mention) や、 #ハッシュタグ が使用できます", - "onboarding.profile.save_and_continue": "保存して続ける", "onboarding.profile.title": "プロフィールの設定", "onboarding.profile.upload_avatar": "プロフィール画像をアップロード", "onboarding.profile.upload_header": "プロフィールのヘッダー画像をアップロード", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index 978e28bb59055d..9689a99036c299 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -592,14 +592,12 @@ "notifications_permission_banner.enable": "Rmed ilɣa n tnarit", "notifications_permission_banner.title": "Ur zeggel acemma", "onboarding.follows.back": "Uɣal", - "onboarding.follows.done": "Immed", "onboarding.follows.search": "Nadi", "onboarding.follows.title": "Ḍfeṛ walbɛaḍ i wakken ad ttebdud", "onboarding.profile.display_name": "Isem ara d-yettwaskanen", "onboarding.profile.display_name_hint": "Isem-ik·im ummid neɣ isem-ik·im n uqeṣṣer…", "onboarding.profile.note": "Tameddurt", "onboarding.profile.note_hint": "Tzemreḍ ad d-@tbedreḍ imdanen niḍen neɣ #ihacṭagen …", - "onboarding.profile.save_and_continue": "Sekles, tkemmleḍ", "onboarding.profile.title": "Asbadu n umaɣnu", "onboarding.profile.upload_avatar": "Sali tugna n umaɣnu", "onboarding.profile.upload_header": "Sali tacacit n umaɣnu", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index b19fde5bdf1924..6737c4b2eed04e 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -796,7 +796,6 @@ "notifications_permission_banner.how_to_control": "마스토돈이 열려 있지 않을 때에도 알림을 받으려면, 데스크탑 알림을 활성화 하세요. 당신은 어떤 종류의 반응이 데스크탑 알림을 발생할 지를 {icon} 버튼을 통해 세세하게 설정할 수 있습니다.", "notifications_permission_banner.title": "아무것도 놓치지 마세요", "onboarding.follows.back": "뒤로가기", - "onboarding.follows.done": "완료", "onboarding.follows.empty": "안타깝지만 아직은 아무 것도 보여드릴 수 없습니다. 검색을 이용하거나 둘러보기 페이지에서 팔로우 할 사람을 찾을 수 있습니다. 아니면 잠시 후에 다시 시도하세요.", "onboarding.follows.search": "검색", "onboarding.follows.title": "사람들을 팔로우하기", @@ -806,7 +805,6 @@ "onboarding.profile.display_name_hint": "진짜 이름 또는 재미난 이름…", "onboarding.profile.note": "자기소개", "onboarding.profile.note_hint": "남을 @mention 하거나 #hashtag 태그를 달 수 있습니다…", - "onboarding.profile.save_and_continue": "저장 및 계속", "onboarding.profile.title": "프로필 설정", "onboarding.profile.upload_avatar": "프로필 사진 업로드", "onboarding.profile.upload_header": "프로필 헤더 업로드", diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index b765dbbb51b2aa..f53292c751e37f 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -594,7 +594,6 @@ "notifications_permission_banner.how_to_control": "Para risivir avizos kuando Mastodon no esta avierto, kapasita avizos de ensimameza. Puedes kontrolar presizamente kualos tipos de enteraksiones djeneren avizos de ensimameza kon el boton {icon} arriva kuando esten kapasitadas.", "notifications_permission_banner.title": "Nunkua te piedres niente", "onboarding.follows.back": "Atras", - "onboarding.follows.done": "Fecho", "onboarding.follows.empty": "Malorozamente, no se pueden amostrar rezultados en este momento. Puedes aprovar uzar la bushkeda o navigar por la pajina de eksplorasyon para topar personas a las que segir, o aprovarlo de muevo mas tadre.", "onboarding.follows.search": "Bushka", "onboarding.follows.title": "Sige personas para ampezar", @@ -604,7 +603,6 @@ "onboarding.profile.display_name_hint": "Tu nombre para amostrar.", "onboarding.profile.note": "Tu deskripsyon", "onboarding.profile.note_hint": "Puedes @enmentar a otra djente o #etiketas…", - "onboarding.profile.save_and_continue": "Guadra i kontinua", "onboarding.profile.title": "Konfigurasyon de profil", "onboarding.profile.upload_avatar": "Karga imaje de profil", "onboarding.profile.upload_header": "Karga kavesera de profil", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index ef6fa617020c94..32b23ea1e2cca6 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -745,7 +745,6 @@ "notifications_permission_banner.how_to_control": "Jei nori gauti pranešimus, kai Mastodon nėra atidarytas, įjunk darbalaukio pranešimus. Įjungęs (-usi) darbalaukio pranešimus, gali tiksliai valdyti, kokių tipų sąveikos generuoja darbalaukio pranešimus, naudojant pirmiau esančiu mygtuku {icon}.", "notifications_permission_banner.title": "Niekada nieko nepraleisk", "onboarding.follows.back": "Atgal", - "onboarding.follows.done": "Atlikta", "onboarding.follows.empty": "Deja, šiuo metu jokių rezultatų parodyti negalima. Gali pabandyti naudoti paiešką arba naršyti atradimo puslapį, kad surastum žmonių, kuriuos nori sekti, arba bandyti vėliau.", "onboarding.follows.search": "Ieškoti", "onboarding.follows.title": "Sekite asmenis, kad pradėtumėte", @@ -755,7 +754,6 @@ "onboarding.profile.display_name_hint": "Tavo pilnas vardas arba linksmas vardas…", "onboarding.profile.note": "Biografija", "onboarding.profile.note_hint": "Gali @paminėti kitus žmones arba #saitažodžius…", - "onboarding.profile.save_and_continue": "Išsaugoti ir tęsti", "onboarding.profile.title": "Profilio sąranka", "onboarding.profile.upload_avatar": "Įkelti profilio nuotrauką", "onboarding.profile.upload_header": "Įkelti profilio antraštę", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index d73177da90712c..83e436c75638d7 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -610,7 +610,6 @@ "onboarding.profile.display_name_hint": "Tavs pilnais vārds vai Tavs joku vārds…", "onboarding.profile.note": "Apraksts", "onboarding.profile.note_hint": "Tu vari @pieminēt citus cilvēkus vai #tēmturus…", - "onboarding.profile.save_and_continue": "Saglabāt un turpināt", "onboarding.profile.title": "Profila iestatīšana", "onboarding.profile.upload_avatar": "Augšupielādēt profila attēlu", "onboarding.profile.upload_header": "Augšupielādēt profila galveni", diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index 92b12958fe3ed2..49a54815d8ab14 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -481,7 +481,6 @@ "onboarding.profile.display_name": "Nama paparan", "onboarding.profile.display_name_hint": "Nama penuh anda atau nama anda yang menyeronokkan…", "onboarding.profile.note_hint": "Anda boleh @menyebut orang lain atau #hashtags…", - "onboarding.profile.save_and_continue": "Simpan dan teruskan", "onboarding.profile.upload_avatar": "Muat naik gambar profil", "password_confirmation.exceeds_maxlength": "Pengesahan kata laluan melebihi panjang kata laluan maksimum", "password_confirmation.mismatching": "Pengesahan kata laluan tidak sepadan", diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json index ecfe89311174a1..ba1e90e5b9369e 100644 --- a/app/javascript/mastodon/locales/my.json +++ b/app/javascript/mastodon/locales/my.json @@ -385,7 +385,6 @@ "onboarding.profile.display_name": "ဖော်ပြမည့်အမည်", "onboarding.profile.display_name_hint": "သင့်အမည်အပြည့်အစုံ သို့မဟုတ် သင့်အမည်ပြောင်။", "onboarding.profile.note": "ကိုယ်ရေးအကျဉ်း", - "onboarding.profile.save_and_continue": "သိမ်းပြီး ဆက်လုပ်ပါ", "onboarding.profile.title": "ပရိုဖိုင်စနစ် ထည့်သွင်းခြင်း", "onboarding.profile.upload_avatar": "ပရိုဖိုင်ပုံ အပ်လုဒ်လုပ်ပါ", "password_confirmation.exceeds_maxlength": "စကားဝှက်အတည်ပြုခြင်းတွင် အများဆုံးစကားဝှက်အရှည်ထက် ကျော်လွန်နေပါသည်", diff --git a/app/javascript/mastodon/locales/nan-TW.json b/app/javascript/mastodon/locales/nan-TW.json index 97055039768986..3d108b00746dc9 100644 --- a/app/javascript/mastodon/locales/nan-TW.json +++ b/app/javascript/mastodon/locales/nan-TW.json @@ -173,7 +173,6 @@ "account_edit.field_edit_modal.link_emoji_warning": "Lán無建議佇URL內底用自訂ê emoji。為著避免用者舞花去,自訂ê框á若包含自訂emoji kap URL,kan-ta ē顯示做文字。", "account_edit.field_edit_modal.name_hint": "例:「個人網站」", "account_edit.field_edit_modal.name_label": "標簽", - "account_edit.field_edit_modal.value_hint": "例:「example.me」", "account_edit.field_edit_modal.value_label": "值", "account_edit.field_reorder_modal.drag_cancel": "Giú ê動作取消ah。框á「{item}」予lâng khǹg ah。", "account_edit.field_reorder_modal.drag_end": "框á「{item}」予lâng khǹg ah。", @@ -951,7 +950,6 @@ "notifications_permission_banner.how_to_control": "Nā beh佇Mastodon關起來ê時陣收通知,請啟用桌面通知。若準啟用,Lí ē當通過面頂ê {icon} 鈕á,準準控制siánn物互動ê類型ē生桌面通知。", "notifications_permission_banner.title": "逐ê著看", "onboarding.follows.back": "轉去", - "onboarding.follows.done": "做好ah", "onboarding.follows.empty": "可惜,tsit-má無半條結果通顯示。Lí ē當試用tshiau-tshuē á是瀏覽探索ê頁,來tshuē beh跟tuè ê lâng,或者是sió等leh koh試。", "onboarding.follows.search": "Tshiau-tshuē", "onboarding.follows.title": "請跟tuè lâng來開始。", @@ -961,7 +959,6 @@ "onboarding.profile.display_name_hint": "Lí ê全名á是別號……", "onboarding.profile.note": "個人紹介", "onboarding.profile.note_hint": "Lí ē當 @mention 別lâng á是用 #hashtag……", - "onboarding.profile.save_and_continue": "儲存了後繼續", "onboarding.profile.title": "個人資料ê設定", "onboarding.profile.upload_avatar": "Kā個人資料ê相片傳起去。", "onboarding.profile.upload_header": "Kā個人資料ê橫條á ê圖傳起去", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index df2191a094563f..c45babcc0d0326 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -173,7 +173,6 @@ "account_edit.field_edit_modal.link_emoji_warning": "We raden aan om aangepaste emoji in combinatie met urls te gebruiken. Aangepaste velden die beide bevatten worden alleen als tekst weergegeven in plaats van als een link, om verwarring van de gebruiker te voorkomen.", "account_edit.field_edit_modal.name_hint": "Bijv. \"Persoonlijke website\"", "account_edit.field_edit_modal.name_label": "Label", - "account_edit.field_edit_modal.value_hint": "Bijv. \"voorbeeld.me\"", "account_edit.field_edit_modal.value_label": "Waarde", "account_edit.field_reorder_modal.drag_cancel": "Slepen is geannuleerd. Veld \"{item}\" is weggevallen.", "account_edit.field_reorder_modal.drag_end": "Veld \"{item}\" was weggevallen.", @@ -959,7 +958,6 @@ "notifications_permission_banner.how_to_control": "Om meldingen te ontvangen wanneer Mastodon niet open staat. Je kunt precies bepalen welke soort interacties wel of geen desktopmeldingen geven via de bovenstaande {icon} knop.", "notifications_permission_banner.title": "Mis nooit meer iets", "onboarding.follows.back": "Terug", - "onboarding.follows.done": "Klaar", "onboarding.follows.empty": "Helaas kunnen op dit moment geen resultaten worden getoond. Je kunt proberen te zoeken of op de verkenningspagina te bladeren om mensen te vinden die je kunt volgen, of probeer het later opnieuw.", "onboarding.follows.search": "Zoeken", "onboarding.follows.title": "Volg mensen om te beginnen", @@ -969,7 +967,6 @@ "onboarding.profile.display_name_hint": "Jouw volledige naam of een leuke bijnaam…", "onboarding.profile.note": "Biografie", "onboarding.profile.note_hint": "Je kunt andere mensen @vermelden of #hashtags gebruiken…", - "onboarding.profile.save_and_continue": "Opslaan en doorgaan", "onboarding.profile.title": "Profiel instellen", "onboarding.profile.upload_avatar": "Profielfoto uploaden", "onboarding.profile.upload_header": "Omslagfoto voor het profiel uploaden", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 9d7ca37c0d29c2..e44f7e52b936c1 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -913,7 +913,6 @@ "notifications_permission_banner.how_to_control": "Aktiver skrivebordsvarsel for å få varsel når Mastodon ikkje er open. Du kan nøye bestemme kva samhandlingar som skal føre til skrivebordsvarsel gjennom {icon}-knappen ovanfor etter at varsel er aktivert.", "notifications_permission_banner.title": "Gå aldri glipp av noko", "onboarding.follows.back": "Tilbake", - "onboarding.follows.done": "Ferdig", "onboarding.follows.empty": "Me kan ikkje visa deg nokon resultat no. Du kan prøva å søkja eller bla gjennom utforsk-sida for å finna folk å fylgja, eller du kan prøva att seinare.", "onboarding.follows.search": "Søk", "onboarding.follows.title": "Fylg folk for å koma i gang", @@ -923,7 +922,6 @@ "onboarding.profile.display_name_hint": "Det fulle namnet eller kallenamnet ditt…", "onboarding.profile.note": "Om meg", "onboarding.profile.note_hint": "Du kan @nemna folk eller #emneknaggar…", - "onboarding.profile.save_and_continue": "Lagre og hald fram", "onboarding.profile.title": "Profiloppsett", "onboarding.profile.upload_avatar": "Last opp profilbilete", "onboarding.profile.upload_header": "Last opp profiltoppbilete", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index a6ab9f30e1614f..2cd2341d8bc3d6 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -643,7 +643,6 @@ "notifications_permission_banner.how_to_control": "For å motta varsler når Mastodon ikke er åpne, aktiver desktop varsler. Du kan kontrollere nøyaktig hvilke typer interaksjoner genererer skrivebordsvarsler gjennom {icon} -knappen ovenfor når de er aktivert.", "notifications_permission_banner.title": "Aldri gå glipp av noe", "onboarding.follows.back": "Tilbake", - "onboarding.follows.done": "Ferdig", "onboarding.follows.empty": "Dessverre kan ingen resultater vises akkurat nå. Du kan prøve å bruke søk eller bla gjennom utforske-siden for å finne folk å følge, eller prøve igjen senere.", "onboarding.follows.search": "Søk", "onboarding.follows.title": "Følg folk for å komme i gang", @@ -652,7 +651,6 @@ "onboarding.profile.display_name_hint": "Ditt fulle navn eller ditt morsomme navn…", "onboarding.profile.note": "Om meg", "onboarding.profile.note_hint": "Du kan @nevne andre eller #emneknagger…", - "onboarding.profile.save_and_continue": "Lagre og fortsett", "onboarding.profile.title": "Konfigurering av profil", "onboarding.profile.upload_avatar": "Last opp profilbilde", "onboarding.profile.upload_header": "Last opp profiltoppbilde", diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json index 11c7022100343c..7fcfeb9053d820 100644 --- a/app/javascript/mastodon/locales/pa.json +++ b/app/javascript/mastodon/locales/pa.json @@ -552,11 +552,9 @@ "notifications.policy.filter_not_following_hint": "ਜਦ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ ਨੂੰ ਖੁਦ ਮਨਜ਼ੂਰੀ ਨਹੀਂ ਦਿੰਦੇ", "notifications_permission_banner.enable": "ਡੈਸਕਟਾਪ ਸੂਚਨਾਵਾਂ ਸਮਰੱਥ ਕਰੋ", "onboarding.follows.back": "ਪਿੱਛੇ", - "onboarding.follows.done": "ਮੁਕੰਮਲ", "onboarding.follows.search": "ਖੋਜੋ", "onboarding.profile.display_name": "ਦਿਖਾਇਆ ਜਾਣ ਵਾਲਾ ਨਾਂ", "onboarding.profile.note": "ਜਾਣਕਾਰੀ", - "onboarding.profile.save_and_continue": "ਸੰਭਾਲੋ ਅਤੇ ਜਾਰੀ ਰੱਖੋ", "onboarding.profile.title": "ਪਰੋਫਾਈਲ ਸੈਟਅੱਪ", "onboarding.profile.upload_avatar": "ਪਰੋਫਾਈਲ ਤਸਵੀਰ ਅੱਪਲੋਡ ਕਰੋ", "poll.closed": "ਬੰਦ ਹੈ", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 0ef974c4b2b2e9..7b55c1e99d00d8 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -733,7 +733,6 @@ "notifications_permission_banner.how_to_control": "Aby otrzymywać powiadomienia, gdy Mastodon nie jest otwarty, włącz powiadomienia na pulpicie. Możesz wybrać, które dokładnie typy interakcji generują powiadomienia na pulpicie za pomocą przycisku {icon} powyżej po ich włączeniu.", "notifications_permission_banner.title": "Nigdy niczego nie przegapisz", "onboarding.follows.back": "Wróć", - "onboarding.follows.done": "Gotowe", "onboarding.follows.empty": "Niestety, w tej chwili nie można nic wyświetlić. Możesz użyć wyszukiwania lub przeglądać stronę główną, aby znaleźć osoby, które chcesz obserwować, albo spróbuj ponownie później.", "onboarding.follows.search": "Szukaj", "onboarding.follows.title": "Zaobserwuj kogoś, aby zacząć", @@ -743,7 +742,6 @@ "onboarding.profile.display_name_hint": "Twoje imię lub pseudonim…", "onboarding.profile.note": "Opis", "onboarding.profile.note_hint": "Możesz @wzmiankować innych lub dodawać #hashtagi…", - "onboarding.profile.save_and_continue": "Zapisz i kontynuuj", "onboarding.profile.title": "Ustawienia profilu", "onboarding.profile.upload_avatar": "Dodaj zdjęcie profilowe", "onboarding.profile.upload_header": "Dodaj baner", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index d330ee430231a3..dc02337994c06f 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Editar perfil", "account_edit.custom_fields.name": "Campo", "account_edit.custom_fields.placeholder": "Insira seus pronomes, links externos ou qualquer coisa que queira compartilhar.", + "account_edit.custom_fields.reorder_button": "Reordenar campos", "account_edit.custom_fields.tip_content": "Você pode facilmente dar credibilidade à sua conta Mastodon verificando os links para os seus sites.", "account_edit.custom_fields.tip_title": "Dica: Adicionando links verificados", "account_edit.custom_fields.title": "Campos personalizados", @@ -167,9 +168,16 @@ "account_edit.field_delete_modal.title": "Excluir campo personalizado?", "account_edit.field_edit_modal.add_title": "Adicionar campo personalizado", "account_edit.field_edit_modal.edit_title": "Editar campo personalizado", + "account_edit.field_edit_modal.limit_header": "Limite recomendado de caracteres excedido", + "account_edit.field_edit_modal.limit_message": "Usuários de dispositivos móveis podem não ver seu campo completo.", + "account_edit.field_edit_modal.link_emoji_warning": "Recomendamos não utilizar emojis personalizados combinados com URLs. Campos personalizados contendo ambos serão exibidos apenas como texto em vez de link, para evitar confusão dos usuários.", "account_edit.field_edit_modal.name_hint": "Ex. \"Site pessoal\"", "account_edit.field_edit_modal.name_label": "Descrição", + "account_edit.field_edit_modal.url_warning": "Para adicionar um link, por favor inclia {protocol} no início.", + "account_edit.field_edit_modal.value_hint": "Ex.: \"https://example.me\"", "account_edit.field_edit_modal.value_label": "Valor", + "account_edit.field_reorder_modal.drag_cancel": "O arrasto foi cancelado. O campo \"{item}\" foi descartado.", + "account_edit.field_reorder_modal.drag_end": "O campo \"{item}\" foi descartado.", "account_edit.name_modal.add_title": "Inserir nome de exibição", "account_edit.name_modal.edit_title": "Editar nome de exibição", "account_edit.profile_tab.button_label": "Personalizar", @@ -324,12 +332,14 @@ "collections.create_collection": "Criar coleção", "collections.delete_collection": "Eliminar coleção", "collections.description_length_hint": "Limite de 100 caracteres", + "collections.detail.accept_inclusion": "OK", "collections.detail.accounts_heading": "Contas", "collections.detail.author_added_you": "{author} adicionou você a esta coleção", "collections.detail.curated_by_author": "Curadoria de {author}", "collections.detail.curated_by_you": "Curadoria por você", "collections.detail.loading": "Carregando coleção…", "collections.detail.other_accounts_in_collection": "Outros nesta coleção:", + "collections.detail.revoke_inclusion": "Remover-me", "collections.detail.sensitive_note": "Esta coleção contém contas e conteúdo que podem ser sensíveis a alguns usuários.", "collections.detail.share": "Compartilhar esta coleção", "collections.edit_details": "Editar detalhes", @@ -345,6 +355,9 @@ "collections.old_last_post_note": "Publicado pela última vez semana passada", "collections.remove_account": "Remover esta conta", "collections.report_collection": "Denunciar esta coleção", + "collections.revoke_collection_inclusion": "Remover-me desta coleção", + "collections.revoke_inclusion.confirmation": "Você foi removido de \"{collection}\"", + "collections.revoke_inclusion.error": "Houve um erro, por favor tente novamente mais tarde.", "collections.search_accounts_label": "Buscar contas para adicionar…", "collections.search_accounts_max_reached": "Você acrescentou o numero máximo de contas", "collections.sensitive": "Sensível", @@ -468,6 +481,9 @@ "confirmations.remove_from_followers.confirm": "Remover seguidor", "confirmations.remove_from_followers.message": "{name} vai parar de te seguir. Tem certeza de que deseja continuar?", "confirmations.remove_from_followers.title": "Remover seguidor?", + "confirmations.revoke_collection_inclusion.confirm": "Remover-me", + "confirmations.revoke_collection_inclusion.message": "Esta ação é permanente e o curador não poderá adicionar-lhe de volta à coleção mais tarde.", + "confirmations.revoke_collection_inclusion.title": "Remover-se desta coleção?", "confirmations.revoke_quote.confirm": "Remover publicação", "confirmations.revoke_quote.message": "Esta ação não pode ser desfeita.", "confirmations.revoke_quote.title": "Remover publicação?", @@ -937,7 +953,6 @@ "notifications_permission_banner.how_to_control": "Para receber notificações quando o Mastodon não estiver aberto, ative as notificações no computador. Você pode controlar precisamente quais tipos de interações geram notificações no computador através do botão {icon}.", "notifications_permission_banner.title": "Nunca perca nada", "onboarding.follows.back": "Voltar", - "onboarding.follows.done": "Feito", "onboarding.follows.empty": "Infelizmente, não é possível mostrar resultados agora. Você pode tentar usar a busca ou navegar na página de exploração para encontrar pessoas para seguir, ou tentar novamente mais tarde.", "onboarding.follows.search": "Buscar", "onboarding.follows.title": "Comece seguindo pessoas", @@ -947,7 +962,6 @@ "onboarding.profile.display_name_hint": "Seu nome completo ou apelido…", "onboarding.profile.note": "Biografia", "onboarding.profile.note_hint": "Você pode @mencionar outras pessoas ou usar #hashtags…", - "onboarding.profile.save_and_continue": "Salvar e continuar", "onboarding.profile.title": "Configuração do perfil", "onboarding.profile.upload_avatar": "Enviar imagem de perfil", "onboarding.profile.upload_header": "Carregar cabeçalho do perfil", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 51042e538ff168..7bca941543889a 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -173,7 +173,6 @@ "account_edit.field_edit_modal.link_emoji_warning": "Não recomendamos o uso de emojis personalizados em combinação com URLs. Campos personalizados que contenham ambos serão exibidos apenas como texto, em vez de como hiperligação, para evitar confusão aos utilizadores.", "account_edit.field_edit_modal.name_hint": "Ex.: \"Site pessoal\"", "account_edit.field_edit_modal.name_label": "Rótulo", - "account_edit.field_edit_modal.value_hint": "Ex.: “exemplo.me”", "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "O arrastamento foi cancelado. O campo \"{item}\" foi largado.", "account_edit.field_reorder_modal.drag_end": "O campo \"{item}\" foi largado.", @@ -958,7 +957,6 @@ "notifications_permission_banner.how_to_control": "Para receberes notificações quando o Mastodon não estiver aberto, ativa as notificações no ambiente de trabalho. Após isso, podes controlar precisamente que tipos de interações geram notificações no ambiente de trabalho através do botão {icon} acima.", "notifications_permission_banner.title": "Nunca percas nada", "onboarding.follows.back": "Voltar", - "onboarding.follows.done": "Concluído", "onboarding.follows.empty": "Infelizmente não é possível mostrar resultados neste momento. Podes tentar pesquisar ou navegar na página \"Explorar\" para encontrares pessoas para seguires ou tentar novamente mais tarde.", "onboarding.follows.search": "Pesquisar", "onboarding.follows.title": "Segue pessoas para começar", @@ -968,7 +966,6 @@ "onboarding.profile.display_name_hint": "O teu nome completo ou o teu nome divertido…", "onboarding.profile.note": "Biografia", "onboarding.profile.note_hint": "Podes @mencionar outras pessoas e usar #etiquetas…", - "onboarding.profile.save_and_continue": "Guardar e continuar", "onboarding.profile.title": "Configuração do perfil", "onboarding.profile.upload_avatar": "Enviar foto de perfil", "onboarding.profile.upload_header": "Enviar cabeçalho do perfil", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 7424a73f12eada..44dd60505ac61c 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -725,7 +725,6 @@ "notifications_permission_banner.how_to_control": "Чтобы получать уведомления, даже когда Mastodon закрыт, включите уведомления на рабочем столе. После того как вы их включите, вы сможете тонко настроить виды взаимодействий, о которых вы будете оповещены через уведомления на рабочем столе, нажав на кнопку {icon} выше.", "notifications_permission_banner.title": "Будьте в курсе происходящего", "onboarding.follows.back": "Назад", - "onboarding.follows.done": "Готово", "onboarding.follows.empty": "К сожалению, на данный момент предложения отсутствуют. Чтобы найти, на кого подписаться, вы можете просматривать раздел «Актуальное» или воспользоваться поиском.", "onboarding.follows.search": "Поиск", "onboarding.follows.title": "Начните подписываться на людей", @@ -735,7 +734,6 @@ "onboarding.profile.display_name_hint": "Ваше полное имя или псевдоним…", "onboarding.profile.note": "О себе", "onboarding.profile.note_hint": "Вы можете @упоминать других людей, а также использовать #хештеги…", - "onboarding.profile.save_and_continue": "Сохранить и продолжить", "onboarding.profile.title": "Создайте свой профиль", "onboarding.profile.upload_avatar": "Загрузить фото профиля", "onboarding.profile.upload_header": "Загрузить обложку профиля", diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index ab9444c0b37314..d05c906d68d2bf 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -562,12 +562,10 @@ "notifications_permission_banner.how_to_control": "Pro retzire notìficas cando Mastodon no est abertu, abilita is notìficas de iscrivania. Podes controllare cun pretzisione is castas de interatziones chi ingendrant notìficas de iscrivania pro mèdiu de su butone {icon} in subra, cando sunt abilitadas.", "notifications_permission_banner.title": "Non ti perdas mai nudda", "onboarding.follows.back": "A coa", - "onboarding.follows.done": "Fatu", "onboarding.follows.search": "Chirca", "onboarding.follows.title": "Sighi a gente pro cumintzare", "onboarding.profile.display_name": "Nòmine visìbile", "onboarding.profile.note": "Biografia", - "onboarding.profile.save_and_continue": "Sarva e sighi", "onboarding.profile.title": "Cunfiguratzione de profilu", "onboarding.profile.upload_avatar": "Càrriga una fotografia de profilu", "picture_in_picture.restore": "Torra·ddu a ue fiat", diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json index 6f1f37f8bf45d9..126fc4d0720990 100644 --- a/app/javascript/mastodon/locales/si.json +++ b/app/javascript/mastodon/locales/si.json @@ -636,7 +636,6 @@ "notifications_permission_banner.how_to_control": "Mastodon විවෘතව නොමැති විට දැනුම්දීම් ලබා ගැනීමට, ඩෙස්ක්ටොප් දැනුම්දීම් සක්‍රීය කරන්න. ඒවා සක්‍රීය කළ පසු ඉහත {icon} බොත්තම හරහා ඩෙස්ක්ටොප් දැනුම්දීම් ජනනය කරන්නේ කුමන ආකාරයේ අන්තර්ක්‍රියාද යන්න ඔබට නිශ්චිතවම පාලනය කළ හැකිය.", "notifications_permission_banner.title": "කිසිවක් අතපසු නොකරන්න", "onboarding.follows.back": "ආපසු", - "onboarding.follows.done": "කළා", "onboarding.follows.empty": "අවාසනාවකට, දැන් ප්‍රතිඵල කිසිවක් පෙන්විය නොහැක. අනුගමනය කිරීමට පුද්ගලයින් සොයා ගැනීමට ඔබට සෙවීම භාවිතා කිරීමට හෝ ගවේෂණ පිටුව බ්‍රවුස් කිරීමට උත්සාහ කළ හැකිය, නැතහොත් පසුව නැවත උත්සාහ කරන්න.", "onboarding.follows.search": "සෙවීම", "onboarding.follows.title": "ආරම්භ කිරීමට පුද්ගලයින් අනුගමනය කරන්න", @@ -646,7 +645,6 @@ "onboarding.profile.display_name_hint": "ඔබේ සම්පූර්ණ නම හෝ ඔබේ විනෝදජනක නම…", "onboarding.profile.note": "ජෛව", "onboarding.profile.note_hint": "ඔබට වෙනත් පුද්ගලයින් හෝ #හැෂ් ටැග්…@සඳහන් කළ හැක.", - "onboarding.profile.save_and_continue": "සුරකින්න සහ ඉදිරියට යන්න", "onboarding.profile.title": "පැතිකඩ සැකසුම", "onboarding.profile.upload_avatar": "පැතිකඩ පින්තූරය උඩුගත කරන්න", "onboarding.profile.upload_header": "පැතිකඩ ශීර්ෂය උඩුගත කරන්න", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index b5c7605285c0d9..0d073db623793b 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -634,7 +634,6 @@ "notifications_permission_banner.how_to_control": "Ak chcete dostávať upozornenia, keď Mastodon nie je otvorený, povoľte upozornenia na ploche. Po ich zapnutí môžete presne kontrolovať, ktoré typy interakcií generujú upozornenia na ploche, a to prostredníctvom tlačidla {icon} vyššie.", "notifications_permission_banner.title": "Nenechajte si nič ujsť", "onboarding.follows.back": "Späť", - "onboarding.follows.done": "Hotovo", "onboarding.follows.empty": "Žiaľ, momentálne sa nedajú zobraziť žiadne výsledky. Môžete skúsiť použiť vyhľadávanie alebo navštíviť stránku objavovania a nájsť ľudí, ktorých chcete sledovať, alebo to skúste znova neskôr.", "onboarding.follows.search": "Hľadať", "onboarding.follows.title": "Pre začiatok nasleduj ľudí", @@ -644,7 +643,6 @@ "onboarding.profile.display_name_hint": "Vaše celé meno alebo pokojne aj vtipná prezývka…", "onboarding.profile.note": "Niečo o vás", "onboarding.profile.note_hint": "Môžete @označiť iných ľudí alebo #hashtagy…", - "onboarding.profile.save_and_continue": "Uložiť a pokračovať", "onboarding.profile.title": "Nastavenie profilu", "onboarding.profile.upload_avatar": "Nahrať profilový obrázok", "onboarding.profile.upload_header": "Nahrať obrázok v záhlaví profilu", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index ae960395408a00..bebc2b399873e5 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -706,7 +706,6 @@ "notifications_permission_banner.how_to_control": "Če želite prejemati obvestila, ko Mastodon ni odprt, omogočite namizna obvestila. Natančno lahko nadzirate, katere vrste interakcij naj tvorijo namizna obvestila; ko so omogočena, za to uporabite gumb {icon} zgoraj.", "notifications_permission_banner.title": "Nikoli ne zamudite ničesar", "onboarding.follows.back": "Nazaj", - "onboarding.follows.done": "Opravljeno", "onboarding.follows.empty": "Žal trenutno ni mogoče prikazati nobenih rezultatov. Lahko poskusite z iskanjem ali brskanjem po strani za raziskovanje, da poiščete osebe, ki jim želite slediti, ali poskusite znova pozneje.", "onboarding.follows.search": "Išči", "onboarding.follows.title": "Vaš prvi korak je, da sledite ljudem", @@ -716,7 +715,6 @@ "onboarding.profile.display_name_hint": "Vaše polno ime ali lažno ime ...", "onboarding.profile.note": "Biografija", "onboarding.profile.note_hint": "Lahko @omenite druge osebe ali dodate #ključnike ...", - "onboarding.profile.save_and_continue": "Shrani in nadaljuj", "onboarding.profile.title": "Nastavitev profila", "onboarding.profile.upload_avatar": "Naloži sliko profila", "onboarding.profile.upload_header": "Naloži glavo profila", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 0076f1cdecd0d5..55d52f66c25ed9 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Rekomandojmë të mos përdoren emoji të përshtatur tok me url-ra. Fusha të përshtatura që i përmbajnë të dyja llojetn do t’i shfaqin si tekst, në vend se si një lidhje, për të parandaluar ngatërrim të përdoruesve.", "account_edit.field_edit_modal.name_hint": "P.sh., “Sajt personal”", "account_edit.field_edit_modal.name_label": "Etiketë", - "account_edit.field_edit_modal.value_hint": "P.sh., “shembull.me”", + "account_edit.field_edit_modal.url_warning": "Që të shtoni një lidhje, ju lutemi, përfshini {protocol} në fillim.", + "account_edit.field_edit_modal.value_hint": "P.sh., “https://example.me”", "account_edit.field_edit_modal.value_label": "Vlerë", "account_edit.field_reorder_modal.drag_cancel": "Tërheqja u anulua. Fusha “{item}” u la jashtë.", "account_edit.field_reorder_modal.drag_instructions": "Që të risistemoni fusha vetjake, shtypni tastin Space, ose Enter. Teksa tërhiqen, përdorni tastet shigjetë që të lëvizn fushat lart ose poshtë. Shtypni sërish tastin Space ose Enter që të lihet fusha te pozicioni i saj i ri, ose shtypni tastin Esc, që të anulohet.", @@ -953,7 +954,6 @@ "notifications_permission_banner.how_to_control": "Për të marrë njoftime, kur Mastodon-i s’është i hapur, aktivizoni njoftime në desktop. Përmes butoni {icon} më sipër, mund të kontrolloni me përpikëri cilat lloje ndërveprimesh prodhojnë njoftime në desktop, pasi të jenë aktivizuar.", "notifications_permission_banner.title": "Mos t’ju shpëtojë gjë", "onboarding.follows.back": "Mbrapsht", - "onboarding.follows.done": "U bë", "onboarding.follows.empty": "Mjerisht, s’mund të shfaqen përfundime tani. Mund të provoni të përdorni kërkimin, ose të shfletoni faqen e eksplorimit, që të gjeni persona për ndjekje, ose të riprovoni më vonë.", "onboarding.follows.search": "Kërkoni", "onboarding.follows.title": "Që t’ia filloni, ndiqni persona", @@ -963,7 +963,6 @@ "onboarding.profile.display_name_hint": "Emri juaj i plotë, ose ç’të doni…", "onboarding.profile.note": "Jetëshkrim", "onboarding.profile.note_hint": "Mund të @përmendni persona të tjerë, ose #hashtagë…", - "onboarding.profile.save_and_continue": "Ruaje dhe vazhdo", "onboarding.profile.title": "Udjisje profili", "onboarding.profile.upload_avatar": "Ngarkoni foto profili", "onboarding.profile.upload_header": "Ngarkoni krye profili", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index 69ea0a1ee74af0..c9d62e2a9ff252 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -483,7 +483,6 @@ "onboarding.profile.display_name_hint": "Vaše puno ime ili nadimak…", "onboarding.profile.note": "Biografija", "onboarding.profile.note_hint": "Možete da @pomenete druge ljude ili #heš oznake…", - "onboarding.profile.save_and_continue": "Sačuvaj i nastavi", "onboarding.profile.title": "Podešavanje profila", "onboarding.profile.upload_avatar": "Otpremi sliku profila", "onboarding.profile.upload_header": "Otpremi zaglavlje profila", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index e6059201479aca..7313eaca462dcc 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -483,7 +483,6 @@ "onboarding.profile.display_name_hint": "Ваше пуно име или надимак…", "onboarding.profile.note": "Биографија", "onboarding.profile.note_hint": "Можете да @поменете друге људе или #хеш ознаке…", - "onboarding.profile.save_and_continue": "Сачувај и настави", "onboarding.profile.title": "Подешавање профила", "onboarding.profile.upload_avatar": "Отпреми слику профила", "onboarding.profile.upload_header": "Отпреми заглавље профила", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index e58007425e477f..158feb561ca109 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -165,7 +165,8 @@ "account_edit.field_edit_modal.limit_message": "Mobilanvändare kanske inte ser ditt fält i sin helhet.", "account_edit.field_edit_modal.name_hint": "T.ex. “Personlig webbplats”", "account_edit.field_edit_modal.name_label": "Etikett", - "account_edit.field_edit_modal.value_hint": "T.ex. \"example.me”", + "account_edit.field_edit_modal.url_warning": "För att lägga till en länk, vänligen inkludera {protocol} i början.", + "account_edit.field_edit_modal.value_hint": "T.ex. \"https://example.me”", "account_edit.profile_tab.button_label": "Anpassa", "account_note.placeholder": "Klicka för att lägga till anteckning", "admin.dashboard.daily_retention": "Användarlojalitet per dag efter registrering", @@ -851,17 +852,17 @@ "notifications_permission_banner.how_to_control": "För att ta emot aviseringar när Mastodon inte är öppet, aktivera skrivbordsaviseringar. När de är aktiverade kan du styra exakt vilka typer av interaktioner som aviseras via {icon} -knappen ovan.", "notifications_permission_banner.title": "Missa aldrig något", "onboarding.follows.back": "Tillbaka", - "onboarding.follows.done": "Färdig", "onboarding.follows.empty": "Tyvärr kan inga resultat visas just nu. Du kan prova att använda sökfunktionen eller utforska sidan för att hitta personer att följa, eller försök igen senare.", + "onboarding.follows.next": "Nästa: Ställ in din profil", "onboarding.follows.search": "Sök", "onboarding.follows.title": "Följ människor för att komma igång", "onboarding.profile.discoverable": "Gör min profil upptäckbar", "onboarding.profile.discoverable_hint": "När du väljer att vara upptäckbar på Mastodon kan dina inlägg visas i sök- och trendresultat, och din profil kan föreslås för personer med liknande intressen som du.", "onboarding.profile.display_name": "Visningsnamn", "onboarding.profile.display_name_hint": "Fullständigt namn eller ditt roliga namn…", + "onboarding.profile.finish": "Avsluta", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "Du kan @nämna andra personer eller #hashtags…", - "onboarding.profile.save_and_continue": "Spara och fortsätt", "onboarding.profile.title": "Konfiguration av profil", "onboarding.profile.upload_avatar": "Ladda upp profilbild", "onboarding.profile.upload_header": "Ladda upp profilbanner", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 52887b84515d3c..6a7aaeb4585f77 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -674,7 +674,6 @@ "notifications_permission_banner.how_to_control": "เพื่อรับการแจ้งเตือนเมื่อ Mastodon ไม่ได้เปิด เปิดใช้งานการแจ้งเตือนบนเดสก์ท็อป คุณสามารถควบคุมชนิดของการโต้ตอบที่สร้างการแจ้งเตือนบนเดสก์ท็อปได้อย่างแม่นยำผ่านปุ่ม {icon} ด้านบนเมื่อเปิดใช้งานการแจ้งเตือน", "notifications_permission_banner.title": "ไม่พลาดสิ่งใด", "onboarding.follows.back": "ย้อนกลับ", - "onboarding.follows.done": "เสร็จสิ้น", "onboarding.follows.empty": "น่าเสียดาย ไม่สามารถแสดงผลลัพธ์ได้ในตอนนี้ คุณสามารถลองใช้การค้นหาหรือเรียกดูหน้าสำรวจเพื่อค้นหาผู้คนที่จะติดตาม หรือลองอีกครั้งในภายหลัง", "onboarding.follows.search": "ค้นหา", "onboarding.follows.title": "ติดตามผู้คนเพื่อเริ่มต้นใช้งาน", @@ -684,7 +683,6 @@ "onboarding.profile.display_name_hint": "ชื่อเต็มของคุณหรือชื่อแบบสนุกสนานของคุณ…", "onboarding.profile.note": "ชีวประวัติ", "onboarding.profile.note_hint": "คุณสามารถ @กล่าวถึง ผู้คนอื่น ๆ หรือ #แฮชแท็ก…", - "onboarding.profile.save_and_continue": "บันทึกและดำเนินการต่อ", "onboarding.profile.title": "การตั้งค่าโปรไฟล์", "onboarding.profile.upload_avatar": "อัปโหลดรูปภาพโปรไฟล์", "onboarding.profile.upload_header": "อัปโหลดส่วนหัวโปรไฟล์", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 40ac1f4deec3ae..e84d8a5462fe61 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -28,12 +28,12 @@ "account.block_domain": "{domain} alan adını engelle", "account.block_short": "Engelle", "account.blocked": "Engellendi", - "account.blocking": "Engelleme", - "account.cancel_follow_request": "Takip isteğini geri çek", - "account.copy": "Gönderi bağlantısını kopyala", + "account.blocking": "Engelli", + "account.cancel_follow_request": "Takibi bırak", + "account.copy": "Profil bağlantısını kopyala", "account.direct": "@{name} kullanıcısından özel olarak bahset", "account.disable_notifications": "@{name} kişisinin gönderi bildirimlerini kapat", - "account.domain_blocking": "Alan adını engelleme", + "account.domain_blocking": "Alan adını engelle", "account.edit_note": "Kişisel notu düzenle", "account.edit_profile": "Profili düzenle", "account.edit_profile_short": "Düzenle", @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Url'lerle birlikte özel emoji kullanmamanızı öneririz. Her ikisini de içeren özel alanlar, kullanıcıların kafasını karıştırmamak için bağlantı yerine yalnızca metin olarak görüntülenir.", "account_edit.field_edit_modal.name_hint": "Örn. \"Kişisel web sitesi\"", "account_edit.field_edit_modal.name_label": "Etiket", - "account_edit.field_edit_modal.value_hint": "Örn. \"example.me\"", + "account_edit.field_edit_modal.url_warning": "Bağlantı eklemek için lütfen başlangıca {protocol} ekleyin.", + "account_edit.field_edit_modal.value_hint": "Örn. \"https://example.me\"", "account_edit.field_edit_modal.value_label": "Değer", "account_edit.field_reorder_modal.drag_cancel": "Sürükleme iptal edildi. \"{item}\" alanı bırakıldı.", "account_edit.field_reorder_modal.drag_end": "\"{item}\" alanı bırakıldı.", @@ -959,7 +960,6 @@ "notifications_permission_banner.how_to_control": "Mastodon açık olmadığında bildirim almak için masaüstü bildirimlerini etkinleştirin. Etkinleştirildikten sonra, yukarıdaki{icon} düğmesi aracılığıyla hangi etkileşim türlerinin masaüstü bildirimi oluşturacağını tam olarak kontrol edebilirsiniz.", "notifications_permission_banner.title": "Hiçbir şeyi kaçırmayın", "onboarding.follows.back": "Geri", - "onboarding.follows.done": "Tamamlandı", "onboarding.follows.empty": "Maalesef şu an bir sonuç gösterilemiyor. Takip edilecek kişileri bulmak için arama veya keşfet sayfasına gözatmayı kullanabilirsiniz veya daha sonra tekrar deneyin.", "onboarding.follows.search": "Ara", "onboarding.follows.title": "Başlamak için insanları takip edin", @@ -969,7 +969,6 @@ "onboarding.profile.display_name_hint": "Tam adınız veya kullanıcı adınız…", "onboarding.profile.note": "Kişisel bilgiler", "onboarding.profile.note_hint": "Diğer insanlara @değinebilir veya #etiketler kullanabilirsiniz…", - "onboarding.profile.save_and_continue": "Kaydet ve ilerle", "onboarding.profile.title": "Profilini ayarla", "onboarding.profile.upload_avatar": "Profil resmi yükle", "onboarding.profile.upload_header": "Profil başlığı yükle", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 3aa940e9524e13..65522d62d79022 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -739,7 +739,6 @@ "notifications_permission_banner.how_to_control": "Щоб отримувати сповіщення, коли Mastodon не відкрито, увімкніть сповіщення стільниці. Ви можете контролювати, які типи взаємодій створюють сповіщення через кнопку {icon} вгорі після їхнього увімкнення.", "notifications_permission_banner.title": "Не проґавте нічого", "onboarding.follows.back": "Назад", - "onboarding.follows.done": "Готово", "onboarding.follows.empty": "На жаль, жоден результат не може бути показаний просто зараз. Ви можете спробувати скористатися пошуком або переглядом сторінки огляду, щоб знайти людей для слідкування або повторіть спробу пізніше.", "onboarding.follows.search": "Пошук", "onboarding.follows.title": "Слідкуйте за людьми, щоб почати", @@ -749,7 +748,6 @@ "onboarding.profile.display_name_hint": "Ваше повне ім'я або ваш псевдонім…", "onboarding.profile.note": "Біографія", "onboarding.profile.note_hint": "Ви можете @згадувати інших людей або #гештеґи…", - "onboarding.profile.save_and_continue": "Зберегти і продовжити", "onboarding.profile.title": "Налаштування профілю", "onboarding.profile.upload_avatar": "Завантажити зображення профілю", "onboarding.profile.upload_header": "Завантажити заголовок профілю", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 08791057711dc0..911d1c0fcbe400 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Không nên dùng emoji tùy chỉnh với url. Trường tùy chỉnh chứa cả hai sẽ chỉ hiển thị văn bản, để ngăn chặn việc bối rối.", "account_edit.field_edit_modal.name_hint": "Vd: “Website cá nhân”", "account_edit.field_edit_modal.name_label": "Nhãn", - "account_edit.field_edit_modal.value_hint": "Vd: “example.me”", + "account_edit.field_edit_modal.url_warning": "Để thêm một liên kết, vui lòng đặt {protocol} ở đầu.", + "account_edit.field_edit_modal.value_hint": "Vd: “https://example.me”", "account_edit.field_edit_modal.value_label": "Giá trị", "account_edit.field_reorder_modal.drag_cancel": "Đã hủy kéo. Trường \"{item}\" đã được thả.", "account_edit.field_reorder_modal.drag_end": "Trường \"{item}\" đã được thả.", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "Hãy bật thông báo trên màn hình để không bỏ lỡ những thông báo từ Mastodon. Sau khi bật, bạn có thể lựa chọn từng loại thông báo khác nhau bằng nút {icon} bên dưới.", "notifications_permission_banner.title": "Không bỏ lỡ điều thú vị nào", "onboarding.follows.back": "Quay lại", - "onboarding.follows.done": "Xong", "onboarding.follows.empty": "Không có kết quả có thể được hiển thị lúc này. Bạn có thể thử sử dụng tính năng tìm kiếm hoặc duyệt qua trang khám phá để tìm những người theo dõi hoặc thử lại sau.", + "onboarding.follows.next": "Tiếp theo: Thiết lập tài khoản", "onboarding.follows.search": "Tìm kiếm", "onboarding.follows.title": "Tìm người để theo dõi", "onboarding.profile.discoverable": "Bật khám phá cho hồ sơ của tôi", "onboarding.profile.discoverable_hint": "Khi bạn bật khám phá trên Mastodon, các tút của bạn có thể xuất hiện trong kết quả tìm kiếm và xu hướng, đồng thời hồ sơ của bạn sẽ được đề xuất cho những người có cùng sở thích với bạn.", "onboarding.profile.display_name": "Tên gọi", "onboarding.profile.display_name_hint": "Tên thật hay biệt danh đều được…", + "onboarding.profile.finish": "Hoàn thành", "onboarding.profile.note": "Giới thiệu", "onboarding.profile.note_hint": "Bạn có thể @aiđó hoặc #hashtags…", - "onboarding.profile.save_and_continue": "Lưu và tiếp tục", "onboarding.profile.title": "Thiết lập hồ sơ", "onboarding.profile.upload_avatar": "Tải lên ảnh đại diện", "onboarding.profile.upload_header": "Tải lên ảnh bìa", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 7d4685116c1f8c..9002fd1b0156ba 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -173,7 +173,6 @@ "account_edit.field_edit_modal.link_emoji_warning": "我们建议不要同时使用自定义表情和网址。同时包含两者的自定义字段将会显示为纯文本而不是链接形式,以避免用户混淆。", "account_edit.field_edit_modal.name_hint": "例如:“个人网站”", "account_edit.field_edit_modal.name_label": "标签", - "account_edit.field_edit_modal.value_hint": "例如:“example.me”", "account_edit.field_edit_modal.value_label": "值", "account_edit.field_reorder_modal.drag_cancel": "拖拽已终止。字段“{item}”已被丢弃。", "account_edit.field_reorder_modal.drag_end": "字段“{item}”已被丢弃。", @@ -959,7 +958,6 @@ "notifications_permission_banner.how_to_control": "启用桌面通知以在 Mastodon 未打开时接收通知。你可以通过交互通过上面的 {icon} 按钮来精细控制可以发送桌面通知的交互类型。", "notifications_permission_banner.title": "精彩不容错过", "onboarding.follows.back": "返回", - "onboarding.follows.done": "完成", "onboarding.follows.empty": "很抱歉,现在无法显示任何结果。你可以尝试使用搜索或浏览探索页面来查找要关注的人,或稍后再试。", "onboarding.follows.search": "搜索", "onboarding.follows.title": "关注用户,玩转 Mastodon", @@ -969,7 +967,6 @@ "onboarding.profile.display_name_hint": "你的全名或昵称…", "onboarding.profile.note": "简介", "onboarding.profile.note_hint": "你可以提及 @其他人 或使用 #话题…", - "onboarding.profile.save_and_continue": "保存并继续", "onboarding.profile.title": "设置个人资料", "onboarding.profile.upload_avatar": "上传头像", "onboarding.profile.upload_header": "上传账号封面图", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 4b6bd119eadae7..e7e449a23973b3 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -547,7 +547,6 @@ "notifications_permission_banner.enable": "啟用桌面通知", "notifications_permission_banner.how_to_control": "只要啟用桌面通知,便可在 Mastodon 網站沒有打開時收到通知。在已經啟用桌面通知的時候,你可以透過上面的 {icon} 按鈕準確控制哪些類型的互動會產生桌面通知。", "notifications_permission_banner.title": "不放過任何事情", - "onboarding.follows.done": "完成", "onboarding.follows.empty": "很遺憾,現在無法顯示任何結果。你可以嘗試搜尋或瀏覽探索頁面來找使用者來追蹤,或者稍後再試。", "onboarding.follows.search": "搜尋", "onboarding.profile.discoverable": "將個人檔案設為可被搜尋", @@ -556,7 +555,6 @@ "onboarding.profile.display_name_hint": "你的全名或暱稱…", "onboarding.profile.note": "簡介", "onboarding.profile.note_hint": "你可以 @提及他人 或使用 #標籤…", - "onboarding.profile.save_and_continue": "儲存並繼續", "onboarding.profile.title": "個人檔案設定", "onboarding.profile.upload_avatar": "上載個人檔案頭像", "onboarding.profile.upload_header": "上載個人檔案橫幅圖片", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 913a0ab9d4a8e8..7dffcfc992294e 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -173,7 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "我們不建議於 URL 中使用自訂 emoji 表情符號。為了避免使用者混淆,包含兩者之自訂欄位將僅顯示為文字,而不是顯示為連結。", "account_edit.field_edit_modal.name_hint": "例如:「個人網站」", "account_edit.field_edit_modal.name_label": "標籤", - "account_edit.field_edit_modal.value_hint": "例如「example.me」", + "account_edit.field_edit_modal.url_warning": "如欲新增連結,請於開頭包含 {protocol}。", + "account_edit.field_edit_modal.value_hint": "例如「https://example.me」", "account_edit.field_edit_modal.value_label": "值", "account_edit.field_reorder_modal.drag_cancel": "拖放操作已取消。欄位「{item}」已放置。", "account_edit.field_reorder_modal.drag_end": "欄位「{item}」已放置。", @@ -959,17 +960,17 @@ "notifications_permission_banner.how_to_control": "啟用桌面通知以於 Mastodon 沒有開啟的時候接收通知。啟用桌面通知後,您可以透過上面的 {icon} 按鈕準確的控制哪些類型的互動會產生桌面通知。", "notifications_permission_banner.title": "不要錯過任何東西!", "onboarding.follows.back": "返回", - "onboarding.follows.done": "完成", "onboarding.follows.empty": "很遺憾,目前未能顯示任何結果。您可以嘗試使用搜尋、瀏覽探索頁面以找尋人們跟隨、或稍候再試。", + "onboarding.follows.next": "下一步:設定您的個人檔案", "onboarding.follows.search": "搜尋", "onboarding.follows.title": "開始跟隨一些人吧", "onboarding.profile.discoverable": "使我的個人檔案可以被找到", "onboarding.profile.discoverable_hint": "當您於 Mastodon 上選擇加入可發現性時,您的嘟文可能會顯示於搜尋結果與趨勢中。您的個人檔案可能會被推薦至與您志趣相投的人。", "onboarding.profile.display_name": "顯示名稱", "onboarding.profile.display_name_hint": "完整名稱或暱稱...", + "onboarding.profile.finish": "完成", "onboarding.profile.note": "個人簡介", "onboarding.profile.note_hint": "您可以 @mention 其他人或者使用 #主題標籤...", - "onboarding.profile.save_and_continue": "儲存並繼續", "onboarding.profile.title": "個人檔案設定", "onboarding.profile.upload_avatar": "上傳個人檔案大頭貼", "onboarding.profile.upload_header": "上傳個人檔案封面圖片", diff --git a/config/locales/de.yml b/config/locales/de.yml index 2315be3f8d7986..0e1368d1e33ff0 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1668,7 +1668,7 @@ de: validations: images_and_video: Es kann kein Video an einen Beitrag angehängt werden, der bereits Bilder enthält not_found: Medieninhalt(e) %{ids} nicht gefunden oder bereits an einen anderen Beitrag angehängt - not_ready: Dateien, die noch nicht verarbeitet wurden, können nicht angehängt werden. Versuche es gleich noch einmal! + not_ready: Dateien, die noch nicht verarbeitet wurden, können nicht angehängt werden. Warte einen Moment! too_many: Mehr als vier Dateien können nicht angehängt werden migrations: acct: umgezogen nach diff --git a/config/locales/it.yml b/config/locales/it.yml index 09dfb6a3493397..b42af3c673ecbf 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -838,7 +838,7 @@ it: rules_hint: C'è un'area dedicata per le regole che i tuoi utenti dovrebbero rispettare. title: Info allow_referrer_origin: - desc: Quando i tuoi utenti cliccano su link a siti esterni, il loro browser potrebbe inviare l'indirizzo del tuo server Mastodon come referente. Disattiva questa opzione se ciò identificherebbe in modo univoco i tuoi utenti, ad esempio se si tratta di un server Mastodon personale. + desc: Quando i tuoi utenti cliccano su link a siti esterni, il loro browser potrebbe inviare l'indirizzo del tuo server Mastodon come referente. Disattiva questa opzione se ciò identificherebbe in modo univoco i tuoi utenti, per esempio, se si tratta di un server Mastodon personale. title: Consenti ai siti esterni di vedere il tuo server Mastodon come fonte di traffico appearance: preamble: Personalizza l'interfaccia web di Mastodon. From 94aa5d7c9e27e537ec9ac455f91dc77da3838dd6 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Thu, 12 Mar 2026 11:14:22 +0100 Subject: [PATCH 046/203] Handle `Add` activity to `featuredCollections` (#38167) --- app/lib/activitypub/activity/add.rb | 8 +++++ spec/lib/activitypub/activity/add_spec.rb | 42 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/app/lib/activitypub/activity/add.rb b/app/lib/activitypub/activity/add.rb index c86862c0a30ce7..7b4576a06aed9b 100644 --- a/app/lib/activitypub/activity/add.rb +++ b/app/lib/activitypub/activity/add.rb @@ -12,6 +12,10 @@ def perform else add_featured end + when @account.collections_url + return unless Mastodon::Feature.collections_federation_enabled? + + add_collection else @collection = @account.collections.find_by(uri: @json['target']) add_collection_item if @collection && Mastodon::Feature.collections_federation_enabled? @@ -34,6 +38,10 @@ def add_featured_tags FeaturedTag.create!(account: @account, name: name) if name.present? end + def add_collection + ActivityPub::ProcessFeaturedCollectionService.new.call(@account, @object) + end + def add_collection_item ActivityPub::ProcessFeaturedItemService.new.call(@collection, @object) end diff --git a/spec/lib/activitypub/activity/add_spec.rb b/spec/lib/activitypub/activity/add_spec.rb index 0f8ce53cfb9109..d0bdfbe2185979 100644 --- a/spec/lib/activitypub/activity/add_spec.rb +++ b/spec/lib/activitypub/activity/add_spec.rb @@ -80,6 +80,48 @@ end end + context 'when the target is the `featuredCollections` collection', feature: :collections_federation do + subject { described_class.new(activity_json, account) } + + let(:account) { Fabricate(:remote_account, collections_url: 'https://example.com/actor/1/featured_collections') } + let(:featured_collection_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => 'https://other.example.com/featured_item/1', + 'type' => 'FeaturedCollection', + 'attributedTo' => account.uri, + 'name' => 'Cool people', + 'summary' => 'People you should follow.', + 'totalItems' => 0, + 'sensitive' => false, + 'discoverable' => true, + 'published' => '2026-03-09T15:19:25Z', + } + end + let(:activity_json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'type' => 'Add', + 'actor' => account.uri, + 'target' => 'https://example.com/actor/1/featured_collections', + 'object' => featured_collection_json, + } + end + let(:stubbed_service) do + instance_double(ActivityPub::ProcessFeaturedCollectionService, call: true) + end + + before do + allow(ActivityPub::ProcessFeaturedCollectionService).to receive(:new).and_return(stubbed_service) + end + + it 'calls the service' do + subject.perform + + expect(stubbed_service).to have_received(:call).with(account, featured_collection_json) + end + end + context 'when the target is a collection', feature: :collections_federation do subject { described_class.new(activity_json, collection.account) } From 4552cda15a502b81015771a29caf4d09ab18ecbd Mon Sep 17 00:00:00 2001 From: Hugo Gameiro Date: Thu, 12 Mar 2026 10:15:49 +0000 Subject: [PATCH 047/203] Fix OpenStack Swift Keystone token rate limiting (#38145) --- config/initializers/fog_connection_cache.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 config/initializers/fog_connection_cache.rb diff --git a/config/initializers/fog_connection_cache.rb b/config/initializers/fog_connection_cache.rb new file mode 100644 index 00000000000000..c9cf310d83a61d --- /dev/null +++ b/config/initializers/fog_connection_cache.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +if ENV['SWIFT_ENABLED'] == 'true' + module PaperclipFogConnectionCache + def connection + @connection ||= begin + key = fog_credentials.hash + Thread.current[:paperclip_fog_connections] ||= {} + Thread.current[:paperclip_fog_connections][key] ||= ::Fog::Storage.new(fog_credentials) + end + end + end + + Rails.application.config.after_initialize do + Paperclip::Storage::Fog.prepend(PaperclipFogConnectionCache) + end +end From 13c94db9e7d7f35264c7e1ac93f6e693b0014592 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 12 Mar 2026 11:19:06 +0100 Subject: [PATCH 048/203] Fix extra border on accounts in settings page (#38168) --- app/javascript/styles/mastodon/components.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 2f1d7740b4c65c..a89c9cca5cb2b0 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2010,7 +2010,9 @@ body > [data-popper-placement] { .account { padding: 16px; - &:not(&--without-border) { + // Using :where keeps specificity low, allowing for existing + // .account overrides to still apply + &:where(:not(&--without-border)) { border-bottom: 1px solid var(--color-border-primary); } From 353c8b2abf3808f2aa358df4f92264d2a9475802 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Thu, 12 Mar 2026 11:37:53 +0100 Subject: [PATCH 049/203] Handle `Remove` activity on `featuredCollections` (#38169) --- app/lib/activitypub/activity/remove.rb | 8 ++++++ spec/lib/activitypub/activity/remove_spec.rb | 30 +++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/lib/activitypub/activity/remove.rb b/app/lib/activitypub/activity/remove.rb index f5cbef67571d6f..2cf411317ecb37 100644 --- a/app/lib/activitypub/activity/remove.rb +++ b/app/lib/activitypub/activity/remove.rb @@ -12,6 +12,8 @@ def perform else remove_featured end + when @account.collections_url + remove_collection end end @@ -34,4 +36,10 @@ def remove_featured_tags featured_tag = FeaturedTag.by_name(name).find_by(account: @account) featured_tag&.destroy! end + + def remove_collection + collection = @account.collections.find_by(uri: value_or_id(@object)) + + collection&.destroy! + end end diff --git a/spec/lib/activitypub/activity/remove_spec.rb b/spec/lib/activitypub/activity/remove_spec.rb index 937b938e4fccce..e0563e5c4d7f23 100644 --- a/spec/lib/activitypub/activity/remove_spec.rb +++ b/spec/lib/activitypub/activity/remove_spec.rb @@ -3,7 +3,11 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Remove do - let(:sender) { Fabricate(:account, featured_collection_url: 'https://example.com/featured') } + let(:sender) do + Fabricate(:remote_account, + featured_collection_url: 'https://example.com/featured', + collections_url: 'https://example.com/actor/1/featured_collections') + end describe '#perform' do subject { described_class.new(json, sender) } @@ -59,5 +63,29 @@ .to change { sender.featured_tags.exists?(tag: tag) }.to(false) end end + + context 'when removing a featured collection' do + let(:collection) { Fabricate(:remote_collection, account: sender) } + let(:json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => 'foo', + 'type' => 'Remove', + 'actor' => ActivityPub::TagManager.instance.uri_for(sender), + 'object' => collection.uri, + 'target' => sender.collections_url, + } + end + + before do + Fabricate(:collection_item, collection:, uri: 'https://example.com/featured_items/1') + end + + it 'deletes the collection' do + expect { subject.perform } + .to change(sender.collections, :count).by(-1) + .and change(CollectionItem, :count).by(-1) + end + end end end From 420136e83b65a467a62da739a679ccb579525e63 Mon Sep 17 00:00:00 2001 From: Echo Date: Thu, 12 Mar 2026 11:42:29 +0100 Subject: [PATCH 050/203] Profile editing: Edit image menu (#38156) Co-authored-by: diondiondion --- .../mastodon/components/dropdown_menu.tsx | 3 + .../account_edit/components/image_edit.tsx | 154 ++++++++++++++++++ .../mastodon/features/account_edit/index.tsx | 7 +- .../account_edit/modals/image_alt.tsx | 11 ++ .../account_edit/modals/image_delete.tsx | 11 ++ .../account_edit/modals/image_upload.tsx | 11 ++ .../features/account_edit/modals/index.ts | 3 + .../features/account_edit/styles.module.scss | 53 +++++- .../components/redesign.module.scss | 6 +- .../features/ui/components/modal_root.jsx | 3 + app/javascript/mastodon/locales/en.json | 5 + .../400-24px/photo_camera-fill.svg | 1 + .../400-24px/replace_image-fill.svg | 1 + .../material-icons/400-24px/replace_image.svg | 1 + .../styles/mastodon/theme/_dark.scss | 5 + .../styles/mastodon/theme/_light.scss | 5 + 16 files changed, 273 insertions(+), 7 deletions(-) create mode 100644 app/javascript/mastodon/features/account_edit/components/image_edit.tsx create mode 100644 app/javascript/mastodon/features/account_edit/modals/image_alt.tsx create mode 100644 app/javascript/mastodon/features/account_edit/modals/image_delete.tsx create mode 100644 app/javascript/mastodon/features/account_edit/modals/image_upload.tsx create mode 100644 app/javascript/material-icons/400-24px/photo_camera-fill.svg create mode 100644 app/javascript/material-icons/400-24px/replace_image-fill.svg create mode 100644 app/javascript/material-icons/400-24px/replace_image.svg diff --git a/app/javascript/mastodon/components/dropdown_menu.tsx b/app/javascript/mastodon/components/dropdown_menu.tsx index 24d75080e9dfe3..c3b683392d1436 100644 --- a/app/javascript/mastodon/components/dropdown_menu.tsx +++ b/app/javascript/mastodon/components/dropdown_menu.tsx @@ -296,6 +296,7 @@ interface DropdownProps { children?: React.ReactElement; icon?: string; iconComponent?: IconProp; + iconClassName?: string; items?: Item[]; loading?: boolean; title?: string; @@ -326,6 +327,7 @@ export const Dropdown = ({ children, icon, iconComponent, + iconClassName, items, loading, title = 'Menu', @@ -499,6 +501,7 @@ export const Dropdown = ({ iconComponent={iconComponent} title={title} active={open} + className={iconClassName} {...buttonProps} /> ); diff --git a/app/javascript/mastodon/features/account_edit/components/image_edit.tsx b/app/javascript/mastodon/features/account_edit/components/image_edit.tsx new file mode 100644 index 00000000000000..b99b424aecfc42 --- /dev/null +++ b/app/javascript/mastodon/features/account_edit/components/image_edit.tsx @@ -0,0 +1,154 @@ +import { useCallback, useMemo } from 'react'; +import type { FC } from 'react'; + +import { defineMessages, useIntl } from 'react-intl'; + +import classNames from 'classnames'; + +import type { OffsetValue } from 'react-overlays/esm/usePopper'; + +import type { ModalType } from '@/mastodon/actions/modal'; +import { openModal } from '@/mastodon/actions/modal'; +import { Dropdown } from '@/mastodon/components/dropdown_menu'; +import { IconButton } from '@/mastodon/components/icon_button'; +import type { MenuItem } from '@/mastodon/models/dropdown_menu'; +import { + createAppSelector, + useAppDispatch, + useAppSelector, +} from '@/mastodon/store'; +import AddIcon from '@/material-icons/400-24px/add.svg?react'; +import DeleteIcon from '@/material-icons/400-24px/delete.svg?react'; +import EditIcon from '@/material-icons/400-24px/edit.svg?react'; +import CameraIcon from '@/material-icons/400-24px/photo_camera.svg?react'; +import ReplaceImageIcon from '@/material-icons/400-24px/replace_image.svg?react'; + +import classes from '../styles.module.scss'; + +const messages = defineMessages({ + add: { + id: 'account_edit.image_edit.add_button', + defaultMessage: 'Add image', + }, + replace: { + id: 'account_edit.image_edit.replace_button', + defaultMessage: 'Replace image', + }, + altAdd: { + id: 'account_edit.image_edit.alt_add_button', + description: 'Alt is short for "alternative".', + defaultMessage: 'Add alt text', + }, + altEdit: { + id: 'account_edit.image_edit.alt_edit_button', + description: 'Alt is short for "alternative".', + defaultMessage: 'Edit alt text', + }, + remove: { + id: 'account_edit.image_edit.remove_button', + defaultMessage: 'Remove image', + }, +}); + +export type ImageLocation = 'avatar' | 'header'; + +const selectImageInfo = createAppSelector( + [ + (state) => state.profileEdit.profile, + (_, location: ImageLocation) => location, + ], + (profile, location) => { + if (!profile) { + return { + hasImage: false, + hasAlt: false, + }; + } + + return { + hasImage: !!profile[`${location}Static`], + hasAlt: !!profile[`${location}Description`], + }; + }, +); + +export const AccountImageEdit: FC<{ + className?: string; + location: ImageLocation; +}> = ({ className, location }) => { + const intl = useIntl(); + const { hasAlt, hasImage } = useAppSelector((state) => + selectImageInfo(state, location), + ); + const dispatch = useAppDispatch(); + + const handleModal = useCallback( + (type: ModalType) => { + dispatch(openModal({ modalType: type, modalProps: { location } })); + }, + [dispatch, location], + ); + + const items = useMemo( + () => + [ + { + text: intl.formatMessage(messages.replace), + action: () => { + handleModal('ACCOUNT_EDIT_IMAGE_UPLOAD'); + }, + icon: ReplaceImageIcon, + }, + { + text: intl.formatMessage(hasAlt ? messages.altEdit : messages.altAdd), + action: () => { + handleModal('ACCOUNT_EDIT_IMAGE_ALT'); + }, + icon: hasAlt ? EditIcon : AddIcon, + }, + null, + { + text: intl.formatMessage(messages.remove), + action: () => { + handleModal('ACCOUNT_EDIT_IMAGE_DELETE'); + }, + icon: DeleteIcon, + dangerous: true, + }, + ] satisfies MenuItem[], + [handleModal, hasAlt, intl], + ); + + const handleAddImage = useCallback(() => { + handleModal('ACCOUNT_EDIT_IMAGE_UPLOAD'); + }, [handleModal]); + + const iconClassName = classNames(classes.imageButton, className); + + if (!hasImage) { + return ( + + ); + } + + return ( + + ); +}; + +const popperOffset = [0, 6] as OffsetValue; diff --git a/app/javascript/mastodon/features/account_edit/index.tsx b/app/javascript/mastodon/features/account_edit/index.tsx index 7dc2397f8b4130..a621de4c5de0da 100644 --- a/app/javascript/mastodon/features/account_edit/index.tsx +++ b/app/javascript/mastodon/features/account_edit/index.tsx @@ -23,6 +23,7 @@ import { AccountEditColumn, AccountEditEmptyColumn } from './components/column'; import { EditButton } from './components/edit_button'; import { AccountField } from './components/field'; import { AccountFieldActions } from './components/field_actions'; +import { AccountImageEdit } from './components/image_edit'; import { AccountEditSection } from './components/section'; import classes from './styles.module.scss'; @@ -164,8 +165,12 @@ export const AccountEdit: FC = () => {
{headerSrc && } + +
+
+ +
-
diff --git a/app/javascript/mastodon/features/account_edit/modals/image_alt.tsx b/app/javascript/mastodon/features/account_edit/modals/image_alt.tsx new file mode 100644 index 00000000000000..92360239def375 --- /dev/null +++ b/app/javascript/mastodon/features/account_edit/modals/image_alt.tsx @@ -0,0 +1,11 @@ +import type { FC } from 'react'; + +import { DialogModal } from '../../ui/components/dialog_modal'; +import type { DialogModalProps } from '../../ui/components/dialog_modal'; +import type { ImageLocation } from '../components/image_edit'; + +export const ImageAltModal: FC< + DialogModalProps & { location: ImageLocation } +> = ({ onClose }) => { + return ; +}; diff --git a/app/javascript/mastodon/features/account_edit/modals/image_delete.tsx b/app/javascript/mastodon/features/account_edit/modals/image_delete.tsx new file mode 100644 index 00000000000000..559ff67439b562 --- /dev/null +++ b/app/javascript/mastodon/features/account_edit/modals/image_delete.tsx @@ -0,0 +1,11 @@ +import type { FC } from 'react'; + +import { DialogModal } from '../../ui/components/dialog_modal'; +import type { DialogModalProps } from '../../ui/components/dialog_modal'; +import type { ImageLocation } from '../components/image_edit'; + +export const ImageDeleteModal: FC< + DialogModalProps & { location: ImageLocation } +> = ({ onClose }) => { + return ; +}; diff --git a/app/javascript/mastodon/features/account_edit/modals/image_upload.tsx b/app/javascript/mastodon/features/account_edit/modals/image_upload.tsx new file mode 100644 index 00000000000000..6eb7b73e6c53c1 --- /dev/null +++ b/app/javascript/mastodon/features/account_edit/modals/image_upload.tsx @@ -0,0 +1,11 @@ +import type { FC } from 'react'; + +import { DialogModal } from '../../ui/components/dialog_modal'; +import type { DialogModalProps } from '../../ui/components/dialog_modal'; +import type { ImageLocation } from '../components/image_edit'; + +export const ImageUploadModal: FC< + DialogModalProps & { location: ImageLocation } +> = ({ onClose }) => { + return ; +}; diff --git a/app/javascript/mastodon/features/account_edit/modals/index.ts b/app/javascript/mastodon/features/account_edit/modals/index.ts index 861e81f5977cd0..9b64300e463937 100644 --- a/app/javascript/mastodon/features/account_edit/modals/index.ts +++ b/app/javascript/mastodon/features/account_edit/modals/index.ts @@ -1,6 +1,9 @@ export * from './bio_modal'; export * from './fields_modals'; export * from './fields_reorder_modal'; +export * from './image_alt'; +export * from './image_delete'; +export * from './image_upload'; export * from './name_modal'; export * from './profile_display_modal'; export * from './verified_modal'; diff --git a/app/javascript/mastodon/features/account_edit/styles.module.scss b/app/javascript/mastodon/features/account_edit/styles.module.scss index bafe1bc5b12cf8..ea4aeb6920d107 100644 --- a/app/javascript/mastodon/features/account_edit/styles.module.scss +++ b/app/javascript/mastodon/features/account_edit/styles.module.scss @@ -5,6 +5,7 @@ background: var(--color-bg-secondary); border-bottom: 1px solid var(--color-border-primary); overflow: hidden; + position: relative; @container (width >= 500px) { height: 160px; @@ -16,12 +17,27 @@ width: 100%; height: 100%; } + + .imageButton { + top: 16px; + right: 24px; + } } .avatar { margin-top: -64px; margin-left: 18px; - border: 1px solid var(--color-border-primary); + position: relative; + width: 82px; + + > :global(.account__avatar) { + border: 1px solid var(--color-border-primary); + } + + .imageButton { + bottom: -8px; + right: -8px; + } } .field { @@ -153,6 +169,41 @@ font-size: 15px; } +// Image edit component + +.imageButton { + --default-bg-color: var(--color-bg-primary); + + &, + &:global(.active) { + // Overrides the transparent background added by default with .active + --hover-bg-color: var(--color-bg-brand-softer-solid); + } + + position: absolute; + width: 28px; + height: 28px; + border: 1px solid var(--color-border-primary); + border-radius: 9999px; + box-sizing: border-box; + padding: 4px; + transition: + color 0.2s ease-in-out, + background-color 0.2s ease-in-out; + + svg { + width: 18px; + height: 18px; + } +} + +.imageMenu { + svg { + width: 20px; + height: 20px; + } +} + // Item list component .itemList { diff --git a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss index 51a7962c762f2c..0d9036265a257d 100644 --- a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss +++ b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss @@ -293,11 +293,7 @@ svg.badgeIcon { .fieldOverflowButton { --default-bg-color: var(--color-bg-secondary-solid); - --hover-bg-color: color-mix( - in oklab, - var(--color-bg-brand-base), - var(--default-bg-color) var(--overlay-strength-brand) - ); + --hover-bg-color: var(--color-bg-brand-softer-solid); position: absolute; right: 8px; diff --git a/app/javascript/mastodon/features/ui/components/modal_root.jsx b/app/javascript/mastodon/features/ui/components/modal_root.jsx index 7bd1aa1872827c..2f3f5ee7c84ff8 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.jsx +++ b/app/javascript/mastodon/features/ui/components/modal_root.jsx @@ -102,6 +102,9 @@ export const MODAL_COMPONENTS = { 'ACCOUNT_EDIT_FIELD_EDIT': accountEditModal('EditFieldModal'), 'ACCOUNT_EDIT_FIELD_DELETE': accountEditModal('DeleteFieldModal'), 'ACCOUNT_EDIT_FIELDS_REORDER': accountEditModal('ReorderFieldsModal'), + 'ACCOUNT_EDIT_IMAGE_ALT': accountEditModal('ImageAltModal'), + 'ACCOUNT_EDIT_IMAGE_DELETE': accountEditModal('ImageDeleteModal'), + 'ACCOUNT_EDIT_IMAGE_UPLOAD': accountEditModal('ImageUploadModal'), }; /** @arg {keyof import('@/mastodon/features/account_edit/modals')} type */ diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index ee0a8ded7c0b77..7567c489c260cd 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Picked up field \"{item}\".", "account_edit.field_reorder_modal.handle_label": "Drag field \"{item}\"", "account_edit.field_reorder_modal.title": "Rearrange fields", + "account_edit.image_edit.add_button": "Add image", + "account_edit.image_edit.alt_add_button": "Add alt text", + "account_edit.image_edit.alt_edit_button": "Edit alt text", + "account_edit.image_edit.remove_button": "Remove image", + "account_edit.image_edit.replace_button": "Replace image", "account_edit.name_modal.add_title": "Add display name", "account_edit.name_modal.edit_title": "Edit display name", "account_edit.profile_tab.button_label": "Customize", diff --git a/app/javascript/material-icons/400-24px/photo_camera-fill.svg b/app/javascript/material-icons/400-24px/photo_camera-fill.svg new file mode 100644 index 00000000000000..6a9918605d8d88 --- /dev/null +++ b/app/javascript/material-icons/400-24px/photo_camera-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/replace_image-fill.svg b/app/javascript/material-icons/400-24px/replace_image-fill.svg new file mode 100644 index 00000000000000..e80c7b37f37c2c --- /dev/null +++ b/app/javascript/material-icons/400-24px/replace_image-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/replace_image.svg b/app/javascript/material-icons/400-24px/replace_image.svg new file mode 100644 index 00000000000000..a1a223c1447940 --- /dev/null +++ b/app/javascript/material-icons/400-24px/replace_image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/javascript/styles/mastodon/theme/_dark.scss b/app/javascript/styles/mastodon/theme/_dark.scss index 9af3385d45b0ef..a22c7cc8f444a6 100644 --- a/app/javascript/styles/mastodon/theme/_dark.scss +++ b/app/javascript/styles/mastodon/theme/_dark.scss @@ -81,6 +81,11 @@ var(--color-bg-brand-base), var(--overlay-strength-brand) )}; + --color-bg-brand-softer-solid: color-mix( + in srgb, + var(--color-bg-primary), + var(--color-bg-brand-base) var(--overlay-strength-brand) + ); // Error --overlay-strength-error: 10%; diff --git a/app/javascript/styles/mastodon/theme/_light.scss b/app/javascript/styles/mastodon/theme/_light.scss index 64390017b3680c..47d32320fa4eb2 100644 --- a/app/javascript/styles/mastodon/theme/_light.scss +++ b/app/javascript/styles/mastodon/theme/_light.scss @@ -78,6 +78,11 @@ #0012d8, var(--overlay-strength-brand) )}; + --color-bg-brand-softer-solid: color-mix( + in srgb, + var(--color-bg-primary), + var(--color-bg-brand-base) var(--overlay-strength-brand) + ); // Error --overlay-strength-error: 5%; From eeed483518d03fcf6153e548fdcee688ebecf1e0 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Thu, 12 Mar 2026 12:11:39 +0100 Subject: [PATCH 051/203] Handle `Remove` of an individual collection item (#38170) --- app/lib/activitypub/activity/remove.rb | 10 +++++++++ spec/lib/activitypub/activity/remove_spec.rb | 22 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/app/lib/activitypub/activity/remove.rb b/app/lib/activitypub/activity/remove.rb index 2cf411317ecb37..4a1f8d80be38a3 100644 --- a/app/lib/activitypub/activity/remove.rb +++ b/app/lib/activitypub/activity/remove.rb @@ -14,6 +14,10 @@ def perform end when @account.collections_url remove_collection + else + @collection = @account.collections.find_by(uri: @json['target']) + + remove_collection_item if @collection end end @@ -42,4 +46,10 @@ def remove_collection collection&.destroy! end + + def remove_collection_item + collection_item = @collection.collection_items.find_by(uri: value_or_id(@object)) + + collection_item&.destroy! + end end diff --git a/spec/lib/activitypub/activity/remove_spec.rb b/spec/lib/activitypub/activity/remove_spec.rb index e0563e5c4d7f23..e31b40b9db0dd6 100644 --- a/spec/lib/activitypub/activity/remove_spec.rb +++ b/spec/lib/activitypub/activity/remove_spec.rb @@ -87,5 +87,27 @@ .and change(CollectionItem, :count).by(-1) end end + + context 'when removing a featured item' do + let(:collection) { Fabricate(:remote_collection, account: sender) } + let(:collection_item) { Fabricate(:collection_item, collection:, uri: 'https://example.com/featured_items/1') } + let(:json) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => 'foo', + 'type' => 'Remove', + 'actor' => ActivityPub::TagManager.instance.uri_for(sender), + 'object' => collection_item.uri, + 'target' => collection.uri, + } + end + + before { json } + + it 'deletes the collection item' do + expect { subject.perform } + .to change(collection.collection_items, :count).by(-1) + end + end end end From adede83c7c9a943cf9954fe865becf6a0c2d2280 Mon Sep 17 00:00:00 2001 From: Echo Date: Thu, 12 Mar 2026 12:17:43 +0100 Subject: [PATCH 052/203] Updates to latest Material Icons (#38171) --- app/javascript/material-icons/400-20px/mood-fill.svg | 2 +- app/javascript/material-icons/400-20px/mood.svg | 2 +- app/javascript/material-icons/400-20px/warning-fill.svg | 2 +- app/javascript/material-icons/400-20px/warning.svg | 2 +- app/javascript/material-icons/400-24px/account_circle-fill.svg | 2 +- app/javascript/material-icons/400-24px/account_circle.svg | 2 +- app/javascript/material-icons/400-24px/alternate_email-fill.svg | 2 +- app/javascript/material-icons/400-24px/alternate_email.svg | 2 +- app/javascript/material-icons/400-24px/badge-fill.svg | 2 +- app/javascript/material-icons/400-24px/badge.svg | 2 +- app/javascript/material-icons/400-24px/block-fill.svg | 2 +- app/javascript/material-icons/400-24px/block.svg | 2 +- app/javascript/material-icons/400-24px/breaking_news-fill.svg | 2 +- app/javascript/material-icons/400-24px/breaking_news.svg | 2 +- app/javascript/material-icons/400-24px/contact_mail-fill.svg | 2 +- app/javascript/material-icons/400-24px/contact_mail.svg | 2 +- app/javascript/material-icons/400-24px/database-fill.svg | 2 +- app/javascript/material-icons/400-24px/error-fill.svg | 2 +- app/javascript/material-icons/400-24px/error.svg | 2 +- app/javascript/material-icons/400-24px/find_in_page-fill.svg | 2 +- app/javascript/material-icons/400-24px/find_in_page.svg | 2 +- app/javascript/material-icons/400-24px/format_quote.svg | 2 +- app/javascript/material-icons/400-24px/format_quote_off.svg | 2 +- app/javascript/material-icons/400-24px/forward_5-fill.svg | 2 +- app/javascript/material-icons/400-24px/forward_5.svg | 2 +- app/javascript/material-icons/400-24px/group-fill.svg | 2 +- app/javascript/material-icons/400-24px/group.svg | 2 +- app/javascript/material-icons/400-24px/help-fill.svg | 2 +- app/javascript/material-icons/400-24px/help.svg | 2 +- app/javascript/material-icons/400-24px/inbox-fill.svg | 2 +- app/javascript/material-icons/400-24px/inbox.svg | 2 +- app/javascript/material-icons/400-24px/info-fill.svg | 2 +- app/javascript/material-icons/400-24px/info.svg | 2 +- app/javascript/material-icons/400-24px/list_alt-fill.svg | 2 +- app/javascript/material-icons/400-24px/list_alt.svg | 2 +- app/javascript/material-icons/400-24px/lock-fill.svg | 2 +- app/javascript/material-icons/400-24px/lock.svg | 2 +- app/javascript/material-icons/400-24px/manufacturing-fill.svg | 2 +- app/javascript/material-icons/400-24px/manufacturing.svg | 2 +- app/javascript/material-icons/400-24px/mood-fill.svg | 2 +- app/javascript/material-icons/400-24px/mood.svg | 2 +- app/javascript/material-icons/400-24px/music_note-fill.svg | 2 +- app/javascript/material-icons/400-24px/music_note.svg | 2 +- app/javascript/material-icons/400-24px/person-fill.svg | 2 +- app/javascript/material-icons/400-24px/person.svg | 2 +- app/javascript/material-icons/400-24px/person_add-fill.svg | 2 +- app/javascript/material-icons/400-24px/person_add.svg | 2 +- app/javascript/material-icons/400-24px/person_alert-fill.svg | 2 +- app/javascript/material-icons/400-24px/person_alert.svg | 2 +- app/javascript/material-icons/400-24px/person_remove-fill.svg | 2 +- app/javascript/material-icons/400-24px/person_remove.svg | 2 +- app/javascript/material-icons/400-24px/photo_camera.svg | 2 +- app/javascript/material-icons/400-24px/public-fill.svg | 2 +- app/javascript/material-icons/400-24px/public.svg | 2 +- app/javascript/material-icons/400-24px/replay_5-fill.svg | 2 +- app/javascript/material-icons/400-24px/replay_5.svg | 2 +- app/javascript/material-icons/400-24px/safety_check-fill.svg | 2 +- app/javascript/material-icons/400-24px/safety_check.svg | 2 +- app/javascript/material-icons/400-24px/share.svg | 2 +- app/javascript/material-icons/400-24px/shield_question-fill.svg | 2 +- app/javascript/material-icons/400-24px/shield_question.svg | 2 +- app/javascript/material-icons/400-24px/smart_toy-fill.svg | 2 +- app/javascript/material-icons/400-24px/smart_toy.svg | 2 +- app/javascript/material-icons/400-24px/speed.svg | 2 +- app/javascript/material-icons/400-24px/visibility-fill.svg | 2 +- app/javascript/material-icons/400-24px/visibility.svg | 2 +- app/javascript/material-icons/400-24px/warning-fill.svg | 2 +- app/javascript/material-icons/400-24px/warning.svg | 2 +- 68 files changed, 68 insertions(+), 68 deletions(-) diff --git a/app/javascript/material-icons/400-20px/mood-fill.svg b/app/javascript/material-icons/400-20px/mood-fill.svg index febf012902326c..81a779d4f53f2c 100644 --- a/app/javascript/material-icons/400-20px/mood-fill.svg +++ b/app/javascript/material-icons/400-20px/mood-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-20px/mood.svg b/app/javascript/material-icons/400-20px/mood.svg index 898697c4cdbd2e..c4dd3904cc955a 100644 --- a/app/javascript/material-icons/400-20px/mood.svg +++ b/app/javascript/material-icons/400-20px/mood.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-20px/warning-fill.svg b/app/javascript/material-icons/400-20px/warning-fill.svg index a4fc7efc5321d7..f36c8d87d17646 100644 --- a/app/javascript/material-icons/400-20px/warning-fill.svg +++ b/app/javascript/material-icons/400-20px/warning-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-20px/warning.svg b/app/javascript/material-icons/400-20px/warning.svg index d67ad689aacef8..5c4a6ccd87ab64 100644 --- a/app/javascript/material-icons/400-20px/warning.svg +++ b/app/javascript/material-icons/400-20px/warning.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/account_circle-fill.svg b/app/javascript/material-icons/400-24px/account_circle-fill.svg index 1bf9d57a31a82e..5a5517d9805967 100644 --- a/app/javascript/material-icons/400-24px/account_circle-fill.svg +++ b/app/javascript/material-icons/400-24px/account_circle-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/account_circle.svg b/app/javascript/material-icons/400-24px/account_circle.svg index ce59194be0b8ef..70a0ccde4f37de 100644 --- a/app/javascript/material-icons/400-24px/account_circle.svg +++ b/app/javascript/material-icons/400-24px/account_circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/alternate_email-fill.svg b/app/javascript/material-icons/400-24px/alternate_email-fill.svg index 7648cf9755e9a2..d586e40677b303 100644 --- a/app/javascript/material-icons/400-24px/alternate_email-fill.svg +++ b/app/javascript/material-icons/400-24px/alternate_email-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/alternate_email.svg b/app/javascript/material-icons/400-24px/alternate_email.svg index 7648cf9755e9a2..d586e40677b303 100644 --- a/app/javascript/material-icons/400-24px/alternate_email.svg +++ b/app/javascript/material-icons/400-24px/alternate_email.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/badge-fill.svg b/app/javascript/material-icons/400-24px/badge-fill.svg index 2f7175b7f12b88..88ac07340ca5af 100644 --- a/app/javascript/material-icons/400-24px/badge-fill.svg +++ b/app/javascript/material-icons/400-24px/badge-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/badge.svg b/app/javascript/material-icons/400-24px/badge.svg index d413363a4cc91a..977a04acfa7fde 100644 --- a/app/javascript/material-icons/400-24px/badge.svg +++ b/app/javascript/material-icons/400-24px/badge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/block-fill.svg b/app/javascript/material-icons/400-24px/block-fill.svg index 2d3801613c97af..8b708f27d8eeb5 100644 --- a/app/javascript/material-icons/400-24px/block-fill.svg +++ b/app/javascript/material-icons/400-24px/block-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/block.svg b/app/javascript/material-icons/400-24px/block.svg index e9df4cdd35dfbc..8d798c9eab585c 100644 --- a/app/javascript/material-icons/400-24px/block.svg +++ b/app/javascript/material-icons/400-24px/block.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/breaking_news-fill.svg b/app/javascript/material-icons/400-24px/breaking_news-fill.svg index 633ca48d57d6b4..38f563b524eec0 100644 --- a/app/javascript/material-icons/400-24px/breaking_news-fill.svg +++ b/app/javascript/material-icons/400-24px/breaking_news-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/breaking_news.svg b/app/javascript/material-icons/400-24px/breaking_news.svg index c043f11a8b0207..9a1242b4437adc 100644 --- a/app/javascript/material-icons/400-24px/breaking_news.svg +++ b/app/javascript/material-icons/400-24px/breaking_news.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/contact_mail-fill.svg b/app/javascript/material-icons/400-24px/contact_mail-fill.svg index c42c799955cc9a..45edf82458a4a7 100644 --- a/app/javascript/material-icons/400-24px/contact_mail-fill.svg +++ b/app/javascript/material-icons/400-24px/contact_mail-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/contact_mail.svg b/app/javascript/material-icons/400-24px/contact_mail.svg index 4547c48ec5b7b4..4d54d1157018e6 100644 --- a/app/javascript/material-icons/400-24px/contact_mail.svg +++ b/app/javascript/material-icons/400-24px/contact_mail.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/database-fill.svg b/app/javascript/material-icons/400-24px/database-fill.svg index 3520f6961446e6..977a1abd1d551e 100644 --- a/app/javascript/material-icons/400-24px/database-fill.svg +++ b/app/javascript/material-icons/400-24px/database-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/error-fill.svg b/app/javascript/material-icons/400-24px/error-fill.svg index 5125e9acce627a..92a5cf4abe2a97 100644 --- a/app/javascript/material-icons/400-24px/error-fill.svg +++ b/app/javascript/material-icons/400-24px/error-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/error.svg b/app/javascript/material-icons/400-24px/error.svg index 86c4555326886c..9a83c9306239ec 100644 --- a/app/javascript/material-icons/400-24px/error.svg +++ b/app/javascript/material-icons/400-24px/error.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/find_in_page-fill.svg b/app/javascript/material-icons/400-24px/find_in_page-fill.svg index 146f838a278417..b180354536b83e 100644 --- a/app/javascript/material-icons/400-24px/find_in_page-fill.svg +++ b/app/javascript/material-icons/400-24px/find_in_page-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/find_in_page.svg b/app/javascript/material-icons/400-24px/find_in_page.svg index f21c2786cab932..1133db1429ce99 100644 --- a/app/javascript/material-icons/400-24px/find_in_page.svg +++ b/app/javascript/material-icons/400-24px/find_in_page.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/format_quote.svg b/app/javascript/material-icons/400-24px/format_quote.svg index c354385ea93845..17a51085683a7d 100644 --- a/app/javascript/material-icons/400-24px/format_quote.svg +++ b/app/javascript/material-icons/400-24px/format_quote.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/format_quote_off.svg b/app/javascript/material-icons/400-24px/format_quote_off.svg index 8410c3924bd4bc..38a92eca2e6ff8 100644 --- a/app/javascript/material-icons/400-24px/format_quote_off.svg +++ b/app/javascript/material-icons/400-24px/format_quote_off.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/forward_5-fill.svg b/app/javascript/material-icons/400-24px/forward_5-fill.svg index bc0119a640e6b0..d0b8eb42da1562 100644 --- a/app/javascript/material-icons/400-24px/forward_5-fill.svg +++ b/app/javascript/material-icons/400-24px/forward_5-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/forward_5.svg b/app/javascript/material-icons/400-24px/forward_5.svg index bc0119a640e6b0..d0b8eb42da1562 100644 --- a/app/javascript/material-icons/400-24px/forward_5.svg +++ b/app/javascript/material-icons/400-24px/forward_5.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/group-fill.svg b/app/javascript/material-icons/400-24px/group-fill.svg index c0d6cef5c555ad..6ba4667f67f014 100644 --- a/app/javascript/material-icons/400-24px/group-fill.svg +++ b/app/javascript/material-icons/400-24px/group-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/group.svg b/app/javascript/material-icons/400-24px/group.svg index dbc2c937e4c4f0..03590ed6244a8f 100644 --- a/app/javascript/material-icons/400-24px/group.svg +++ b/app/javascript/material-icons/400-24px/group.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/help-fill.svg b/app/javascript/material-icons/400-24px/help-fill.svg index 6fd48ca5dd1f97..afef63fa60d8fc 100644 --- a/app/javascript/material-icons/400-24px/help-fill.svg +++ b/app/javascript/material-icons/400-24px/help-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/help.svg b/app/javascript/material-icons/400-24px/help.svg index 0f10691c552974..8aa823bf06eafb 100644 --- a/app/javascript/material-icons/400-24px/help.svg +++ b/app/javascript/material-icons/400-24px/help.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/inbox-fill.svg b/app/javascript/material-icons/400-24px/inbox-fill.svg index 15ae2d8f3c4068..f9b740f2143e29 100644 --- a/app/javascript/material-icons/400-24px/inbox-fill.svg +++ b/app/javascript/material-icons/400-24px/inbox-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/inbox.svg b/app/javascript/material-icons/400-24px/inbox.svg index 32c727e8104990..042b6ee9e89a1b 100644 --- a/app/javascript/material-icons/400-24px/inbox.svg +++ b/app/javascript/material-icons/400-24px/inbox.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/info-fill.svg b/app/javascript/material-icons/400-24px/info-fill.svg index 0232e17ad0a438..149547220860f0 100644 --- a/app/javascript/material-icons/400-24px/info-fill.svg +++ b/app/javascript/material-icons/400-24px/info-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/info.svg b/app/javascript/material-icons/400-24px/info.svg index 05606f4e59e3de..a0b97f8a5f6551 100644 --- a/app/javascript/material-icons/400-24px/info.svg +++ b/app/javascript/material-icons/400-24px/info.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/list_alt-fill.svg b/app/javascript/material-icons/400-24px/list_alt-fill.svg index 6aa8b508236b31..b84dba8fd68c67 100644 --- a/app/javascript/material-icons/400-24px/list_alt-fill.svg +++ b/app/javascript/material-icons/400-24px/list_alt-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/list_alt.svg b/app/javascript/material-icons/400-24px/list_alt.svg index cca8ab195554a9..b3aad84d73480b 100644 --- a/app/javascript/material-icons/400-24px/list_alt.svg +++ b/app/javascript/material-icons/400-24px/list_alt.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/lock-fill.svg b/app/javascript/material-icons/400-24px/lock-fill.svg index 0815d784181ce1..fd34be4c360897 100644 --- a/app/javascript/material-icons/400-24px/lock-fill.svg +++ b/app/javascript/material-icons/400-24px/lock-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/lock.svg b/app/javascript/material-icons/400-24px/lock.svg index 20b9e3984ec95a..ecc5cd8992ba09 100644 --- a/app/javascript/material-icons/400-24px/lock.svg +++ b/app/javascript/material-icons/400-24px/lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/manufacturing-fill.svg b/app/javascript/material-icons/400-24px/manufacturing-fill.svg index f19180759c0df3..53b05e010fc8fc 100644 --- a/app/javascript/material-icons/400-24px/manufacturing-fill.svg +++ b/app/javascript/material-icons/400-24px/manufacturing-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/manufacturing.svg b/app/javascript/material-icons/400-24px/manufacturing.svg index f19180759c0df3..53b05e010fc8fc 100644 --- a/app/javascript/material-icons/400-24px/manufacturing.svg +++ b/app/javascript/material-icons/400-24px/manufacturing.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/mood-fill.svg b/app/javascript/material-icons/400-24px/mood-fill.svg index 9480d0fb92aaa3..29ac126f1a19f8 100644 --- a/app/javascript/material-icons/400-24px/mood-fill.svg +++ b/app/javascript/material-icons/400-24px/mood-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/mood.svg b/app/javascript/material-icons/400-24px/mood.svg index 46cafa76808008..0ade2b1b6a3378 100644 --- a/app/javascript/material-icons/400-24px/mood.svg +++ b/app/javascript/material-icons/400-24px/mood.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/music_note-fill.svg b/app/javascript/material-icons/400-24px/music_note-fill.svg index b10ad1921ad5b3..6ce5eff0017a71 100644 --- a/app/javascript/material-icons/400-24px/music_note-fill.svg +++ b/app/javascript/material-icons/400-24px/music_note-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/music_note.svg b/app/javascript/material-icons/400-24px/music_note.svg index b10ad1921ad5b3..6ce5eff0017a71 100644 --- a/app/javascript/material-icons/400-24px/music_note.svg +++ b/app/javascript/material-icons/400-24px/music_note.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person-fill.svg b/app/javascript/material-icons/400-24px/person-fill.svg index 73ef1efc1040ac..321281d2cf3b29 100644 --- a/app/javascript/material-icons/400-24px/person-fill.svg +++ b/app/javascript/material-icons/400-24px/person-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person.svg b/app/javascript/material-icons/400-24px/person.svg index a3f6b246c83140..c304462dba412a 100644 --- a/app/javascript/material-icons/400-24px/person.svg +++ b/app/javascript/material-icons/400-24px/person.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person_add-fill.svg b/app/javascript/material-icons/400-24px/person_add-fill.svg index 3fa7f65288b965..d7cba656804972 100644 --- a/app/javascript/material-icons/400-24px/person_add-fill.svg +++ b/app/javascript/material-icons/400-24px/person_add-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person_add.svg b/app/javascript/material-icons/400-24px/person_add.svg index 39b592bf04edea..cfaeac0071c368 100644 --- a/app/javascript/material-icons/400-24px/person_add.svg +++ b/app/javascript/material-icons/400-24px/person_add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person_alert-fill.svg b/app/javascript/material-icons/400-24px/person_alert-fill.svg index ddbecc60537fce..4e88d53dd322f0 100644 --- a/app/javascript/material-icons/400-24px/person_alert-fill.svg +++ b/app/javascript/material-icons/400-24px/person_alert-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person_alert.svg b/app/javascript/material-icons/400-24px/person_alert.svg index 292ea32154768b..2b81be1c70bc80 100644 --- a/app/javascript/material-icons/400-24px/person_alert.svg +++ b/app/javascript/material-icons/400-24px/person_alert.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person_remove-fill.svg b/app/javascript/material-icons/400-24px/person_remove-fill.svg index 239c7a49dcb7c0..e3a9c2f7ad69dd 100644 --- a/app/javascript/material-icons/400-24px/person_remove-fill.svg +++ b/app/javascript/material-icons/400-24px/person_remove-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/person_remove.svg b/app/javascript/material-icons/400-24px/person_remove.svg index 725da3649b62a3..f075b09d61b5ea 100644 --- a/app/javascript/material-icons/400-24px/person_remove.svg +++ b/app/javascript/material-icons/400-24px/person_remove.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/photo_camera.svg b/app/javascript/material-icons/400-24px/photo_camera.svg index 4621435ce097ac..e863b0dfa5cd9a 100644 --- a/app/javascript/material-icons/400-24px/photo_camera.svg +++ b/app/javascript/material-icons/400-24px/photo_camera.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/public-fill.svg b/app/javascript/material-icons/400-24px/public-fill.svg index 104f26e1330579..ccc5efef2ae408 100644 --- a/app/javascript/material-icons/400-24px/public-fill.svg +++ b/app/javascript/material-icons/400-24px/public-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/public.svg b/app/javascript/material-icons/400-24px/public.svg index 104f26e1330579..ccc5efef2ae408 100644 --- a/app/javascript/material-icons/400-24px/public.svg +++ b/app/javascript/material-icons/400-24px/public.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/replay_5-fill.svg b/app/javascript/material-icons/400-24px/replay_5-fill.svg index c0c259829ee6fe..d4dd38b30e1ad3 100644 --- a/app/javascript/material-icons/400-24px/replay_5-fill.svg +++ b/app/javascript/material-icons/400-24px/replay_5-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/replay_5.svg b/app/javascript/material-icons/400-24px/replay_5.svg index c0c259829ee6fe..d4dd38b30e1ad3 100644 --- a/app/javascript/material-icons/400-24px/replay_5.svg +++ b/app/javascript/material-icons/400-24px/replay_5.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/safety_check-fill.svg b/app/javascript/material-icons/400-24px/safety_check-fill.svg index b38091a8ec4f2a..affaa146592b9a 100644 --- a/app/javascript/material-icons/400-24px/safety_check-fill.svg +++ b/app/javascript/material-icons/400-24px/safety_check-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/safety_check.svg b/app/javascript/material-icons/400-24px/safety_check.svg index 87bdba21fe3444..3f5d4726a14e75 100644 --- a/app/javascript/material-icons/400-24px/safety_check.svg +++ b/app/javascript/material-icons/400-24px/safety_check.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/share.svg b/app/javascript/material-icons/400-24px/share.svg index 23e617121cdafa..6791f0ff25c246 100644 --- a/app/javascript/material-icons/400-24px/share.svg +++ b/app/javascript/material-icons/400-24px/share.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/shield_question-fill.svg b/app/javascript/material-icons/400-24px/shield_question-fill.svg index c647567a00d0f7..b0de7067ebc3e4 100644 --- a/app/javascript/material-icons/400-24px/shield_question-fill.svg +++ b/app/javascript/material-icons/400-24px/shield_question-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/shield_question.svg b/app/javascript/material-icons/400-24px/shield_question.svg index 342ac0800e525b..dce64741d82c4d 100644 --- a/app/javascript/material-icons/400-24px/shield_question.svg +++ b/app/javascript/material-icons/400-24px/shield_question.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/smart_toy-fill.svg b/app/javascript/material-icons/400-24px/smart_toy-fill.svg index df417f5ff7c4c3..eb3fe24a765a36 100644 --- a/app/javascript/material-icons/400-24px/smart_toy-fill.svg +++ b/app/javascript/material-icons/400-24px/smart_toy-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/smart_toy.svg b/app/javascript/material-icons/400-24px/smart_toy.svg index b84efc73b18ae4..68190f9ced9b2a 100644 --- a/app/javascript/material-icons/400-24px/smart_toy.svg +++ b/app/javascript/material-icons/400-24px/smart_toy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/speed.svg b/app/javascript/material-icons/400-24px/speed.svg index 0837877f42f2f8..8b28d172b41986 100644 --- a/app/javascript/material-icons/400-24px/speed.svg +++ b/app/javascript/material-icons/400-24px/speed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/visibility-fill.svg b/app/javascript/material-icons/400-24px/visibility-fill.svg index 44b5f4c6061bd3..84b06b82e26dc1 100644 --- a/app/javascript/material-icons/400-24px/visibility-fill.svg +++ b/app/javascript/material-icons/400-24px/visibility-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/visibility.svg b/app/javascript/material-icons/400-24px/visibility.svg index 8fe45d09af6004..546009d226e693 100644 --- a/app/javascript/material-icons/400-24px/visibility.svg +++ b/app/javascript/material-icons/400-24px/visibility.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/warning-fill.svg b/app/javascript/material-icons/400-24px/warning-fill.svg index c3727d3f57a943..c37c0f3abc1717 100644 --- a/app/javascript/material-icons/400-24px/warning-fill.svg +++ b/app/javascript/material-icons/400-24px/warning-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/warning.svg b/app/javascript/material-icons/400-24px/warning.svg index 238299e6064bc5..7fe972ff275df5 100644 --- a/app/javascript/material-icons/400-24px/warning.svg +++ b/app/javascript/material-icons/400-24px/warning.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From bf32befcc21831b31f684795bb211b2f1f4cfb6d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 12 Mar 2026 10:00:41 -0400 Subject: [PATCH 053/203] Run `annotaterb models` after bug fix to re-order model comments (#38172) --- app/models/account_migration.rb | 4 +-- app/models/account_moderation_note.rb | 4 +-- .../account_relationship_severance_event.rb | 8 ++--- app/models/account_stat.rb | 8 ++--- app/models/account_statuses_cleanup_policy.rb | 8 ++--- app/models/account_summary.rb | 2 +- app/models/account_warning.rb | 8 ++--- app/models/account_warning_preset.rb | 2 +- .../statuses_per_account_count.rb | 2 +- app/models/appeal.rb | 10 +++--- app/models/backup.rb | 6 ++-- app/models/block.rb | 2 +- app/models/bookmark.rb | 4 +-- app/models/bulk_import.rb | 14 ++++---- app/models/bulk_import_row.rb | 2 +- app/models/canonical_email_block.rb | 2 +- app/models/conversation_mute.rb | 2 +- app/models/custom_emoji.rb | 14 ++++---- app/models/custom_filter_keyword.rb | 2 +- app/models/custom_filter_status.rb | 4 +-- app/models/domain_block.rb | 12 +++---- app/models/email_domain_block.rb | 2 +- app/models/featured_tag.rb | 8 ++--- app/models/follow.rb | 8 ++--- app/models/follow_recommendation.rb | 2 +- app/models/follow_recommendation_mute.rb | 4 +-- .../follow_recommendation_suppression.rb | 2 +- app/models/follow_request.rb | 8 ++--- app/models/identity.rb | 2 +- app/models/instance.rb | 2 +- app/models/invite.rb | 6 ++-- app/models/ip_block.rb | 6 ++-- app/models/list.rb | 6 ++-- app/models/list_account.rb | 2 +- app/models/login_activity.rb | 6 ++-- app/models/mention.rb | 4 +-- app/models/mute.rb | 4 +-- app/models/notification.rb | 8 ++--- app/models/notification_permission.rb | 4 +-- app/models/notification_policy.rb | 12 +++---- app/models/notification_request.rb | 6 ++-- app/models/preview_card.rb | 36 +++++++++---------- app/models/preview_card_provider.rb | 6 ++-- app/models/preview_card_trend.rb | 6 ++-- app/models/preview_cards_status.rb | 2 +- app/models/relationship_severance_event.rb | 4 +-- app/models/relay.rb | 4 +-- app/models/report.rb | 16 ++++----- app/models/report_note.rb | 4 +-- app/models/rule.rb | 4 +-- app/models/session_activation.rb | 6 ++-- app/models/setting.rb | 2 +- app/models/severed_relationship.rb | 10 +++--- app/models/site_upload.rb | 6 ++-- app/models/software_update.rb | 6 ++-- app/models/status.rb | 34 +++++++++--------- app/models/status_edit.rb | 14 ++++---- app/models/status_pin.rb | 4 +-- app/models/status_trend.rb | 8 ++--- app/models/tag.rb | 18 +++++----- app/models/tag_follow.rb | 4 +-- app/models/user_ip.rb | 2 +- app/models/webauthn_credential.rb | 6 ++-- app/models/webhook.rb | 6 ++-- 64 files changed, 215 insertions(+), 215 deletions(-) diff --git a/app/models/account_migration.rb b/app/models/account_migration.rb index 6cdc7128ef4067..b4ab0d604bdc01 100644 --- a/app/models/account_migration.rb +++ b/app/models/account_migration.rb @@ -5,12 +5,12 @@ # Table name: account_migrations # # id :bigint(8) not null, primary key -# account_id :bigint(8) # acct :string default(""), not null # followers_count :bigint(8) default(0), not null -# target_account_id :bigint(8) # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) +# target_account_id :bigint(8) # class AccountMigration < ApplicationRecord diff --git a/app/models/account_moderation_note.rb b/app/models/account_moderation_note.rb index ca7f8e3d5fef3f..a6d8feffcf53ad 100644 --- a/app/models/account_moderation_note.rb +++ b/app/models/account_moderation_note.rb @@ -6,10 +6,10 @@ # # id :bigint(8) not null, primary key # content :text not null -# account_id :bigint(8) not null -# target_account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# target_account_id :bigint(8) not null # class AccountModerationNote < ApplicationRecord diff --git a/app/models/account_relationship_severance_event.rb b/app/models/account_relationship_severance_event.rb index c1269fad6d9152..115c63c06273b9 100644 --- a/app/models/account_relationship_severance_event.rb +++ b/app/models/account_relationship_severance_event.rb @@ -6,12 +6,12 @@ # Table name: account_relationship_severance_events # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# relationship_severance_event_id :bigint(8) not null -# created_at :datetime not null -# updated_at :datetime not null # followers_count :integer default(0), not null # following_count :integer default(0), not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) not null +# relationship_severance_event_id :bigint(8) not null # class AccountRelationshipSeveranceEvent < ApplicationRecord self.ignored_columns += %w( diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb index 14aa7ef800787f..137211e66b3d45 100644 --- a/app/models/account_stat.rb +++ b/app/models/account_stat.rb @@ -5,13 +5,13 @@ # Table name: account_stats # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# statuses_count :bigint(8) default(0), not null -# following_count :bigint(8) default(0), not null # followers_count :bigint(8) default(0), not null +# following_count :bigint(8) default(0), not null +# last_status_at :datetime +# statuses_count :bigint(8) default(0), not null # created_at :datetime not null # updated_at :datetime not null -# last_status_at :datetime +# account_id :bigint(8) not null # class AccountStat < ApplicationRecord diff --git a/app/models/account_statuses_cleanup_policy.rb b/app/models/account_statuses_cleanup_policy.rb index 0915003c8b7197..94e82ab63f0b1d 100644 --- a/app/models/account_statuses_cleanup_policy.rb +++ b/app/models/account_statuses_cleanup_policy.rb @@ -5,19 +5,19 @@ # Table name: account_statuses_cleanup_policies # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null # enabled :boolean default(TRUE), not null -# min_status_age :integer default(1209600), not null # keep_direct :boolean default(TRUE), not null +# keep_media :boolean default(FALSE), not null # keep_pinned :boolean default(TRUE), not null # keep_polls :boolean default(FALSE), not null -# keep_media :boolean default(FALSE), not null -# keep_self_fav :boolean default(TRUE), not null # keep_self_bookmark :boolean default(TRUE), not null +# keep_self_fav :boolean default(TRUE), not null # min_favs :integer # min_reblogs :integer +# min_status_age :integer default(1209600), not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null # class AccountStatusesCleanupPolicy < ApplicationRecord include Redisable diff --git a/app/models/account_summary.rb b/app/models/account_summary.rb index 7522a7019345f8..b09336ff48d85b 100644 --- a/app/models/account_summary.rb +++ b/app/models/account_summary.rb @@ -4,9 +4,9 @@ # # Table name: account_summaries # -# account_id :bigint(8) primary key # language :string # sensitive :boolean +# account_id :bigint(8) primary key # class AccountSummary < ApplicationRecord diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb index 9058f73fb8f057..0572e8d22d717a 100644 --- a/app/models/account_warning.rb +++ b/app/models/account_warning.rb @@ -5,15 +5,15 @@ # Table name: account_warnings # # id :bigint(8) not null, primary key -# account_id :bigint(8) -# target_account_id :bigint(8) # action :integer default("none"), not null +# overruled_at :datetime +# status_ids :string is an Array # text :text default(""), not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) # report_id :bigint(8) -# status_ids :string is an Array -# overruled_at :datetime +# target_account_id :bigint(8) # class AccountWarning < ApplicationRecord diff --git a/app/models/account_warning_preset.rb b/app/models/account_warning_preset.rb index c20f683cff90b5..d9fc4b4af3a5f1 100644 --- a/app/models/account_warning_preset.rb +++ b/app/models/account_warning_preset.rb @@ -6,9 +6,9 @@ # # id :bigint(8) not null, primary key # text :text default(""), not null +# title :string default(""), not null # created_at :datetime not null # updated_at :datetime not null -# title :string default(""), not null # class AccountWarningPreset < ApplicationRecord diff --git a/app/models/annual_report/statuses_per_account_count.rb b/app/models/annual_report/statuses_per_account_count.rb index 05a2f53c9d5947..4e1e4452818a72 100644 --- a/app/models/annual_report/statuses_per_account_count.rb +++ b/app/models/annual_report/statuses_per_account_count.rb @@ -5,9 +5,9 @@ # Table name: annual_report_statuses_per_account_counts # # id :bigint(8) not null, primary key +# statuses_count :bigint(8) not null # year :integer not null # account_id :bigint(8) not null -# statuses_count :bigint(8) not null # class AnnualReport::StatusesPerAccountCount < ApplicationRecord diff --git a/app/models/appeal.rb b/app/models/appeal.rb index 6a75fec661be06..33811dd3db1140 100644 --- a/app/models/appeal.rb +++ b/app/models/appeal.rb @@ -5,15 +5,15 @@ # Table name: appeals # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# account_warning_id :bigint(8) not null -# text :text default(""), not null # approved_at :datetime -# approved_by_account_id :bigint(8) # rejected_at :datetime -# rejected_by_account_id :bigint(8) +# text :text default(""), not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# account_warning_id :bigint(8) not null +# approved_by_account_id :bigint(8) +# rejected_by_account_id :bigint(8) # class Appeal < ApplicationRecord TEXT_LENGTH_LIMIT = 2_000 diff --git a/app/models/backup.rb b/app/models/backup.rb index 5feb31d7df64cc..310287234a1bd7 100644 --- a/app/models/backup.rb +++ b/app/models/backup.rb @@ -5,14 +5,14 @@ # Table name: backups # # id :bigint(8) not null, primary key -# user_id :bigint(8) -# dump_file_name :string # dump_content_type :string +# dump_file_name :string +# dump_file_size :bigint(8) # dump_updated_at :datetime # processed :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null -# dump_file_size :bigint(8) +# user_id :bigint(8) # class Backup < ApplicationRecord diff --git a/app/models/block.rb b/app/models/block.rb index 5476542a5ab8eb..662cc1ac20e779 100644 --- a/app/models/block.rb +++ b/app/models/block.rb @@ -5,11 +5,11 @@ # Table name: blocks # # id :bigint(8) not null, primary key +# uri :string # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null # target_account_id :bigint(8) not null -# uri :string # class Block < ApplicationRecord diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb index 04b660372e0d5e..147d87b5649755 100644 --- a/app/models/bookmark.rb +++ b/app/models/bookmark.rb @@ -5,10 +5,10 @@ # Table name: bookmarks # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# status_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# status_id :bigint(8) not null # class Bookmark < ApplicationRecord diff --git a/app/models/bulk_import.rb b/app/models/bulk_import.rb index 8435c245a23fcc..e76f29bfc2ab15 100644 --- a/app/models/bulk_import.rb +++ b/app/models/bulk_import.rb @@ -5,18 +5,18 @@ # Table name: bulk_imports # # id :bigint(8) not null, primary key -# type :integer not null -# state :integer not null -# total_items :integer default(0), not null -# imported_items :integer default(0), not null -# processed_items :integer default(0), not null # finished_at :datetime -# overwrite :boolean default(FALSE), not null +# imported_items :integer default(0), not null # likely_mismatched :boolean default(FALSE), not null # original_filename :string default(""), not null -# account_id :bigint(8) not null +# overwrite :boolean default(FALSE), not null +# processed_items :integer default(0), not null +# state :integer not null +# total_items :integer default(0), not null +# type :integer not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null # class BulkImport < ApplicationRecord self.inheritance_column = false diff --git a/app/models/bulk_import_row.rb b/app/models/bulk_import_row.rb index dd7190c970cc51..01aba1ebc21352 100644 --- a/app/models/bulk_import_row.rb +++ b/app/models/bulk_import_row.rb @@ -5,10 +5,10 @@ # Table name: bulk_import_rows # # id :bigint(8) not null, primary key -# bulk_import_id :bigint(8) not null # data :jsonb # created_at :datetime not null # updated_at :datetime not null +# bulk_import_id :bigint(8) not null # class BulkImportRow < ApplicationRecord belongs_to :bulk_import diff --git a/app/models/canonical_email_block.rb b/app/models/canonical_email_block.rb index 4ed160fc262cb7..01e577d3f2a395 100644 --- a/app/models/canonical_email_block.rb +++ b/app/models/canonical_email_block.rb @@ -6,9 +6,9 @@ # # id :bigint(8) not null, primary key # canonical_email_hash :string default(""), not null -# reference_account_id :bigint(8) # created_at :datetime not null # updated_at :datetime not null +# reference_account_id :bigint(8) # class CanonicalEmailBlock < ApplicationRecord diff --git a/app/models/conversation_mute.rb b/app/models/conversation_mute.rb index 31f8e19667ce61..379596415430c3 100644 --- a/app/models/conversation_mute.rb +++ b/app/models/conversation_mute.rb @@ -5,8 +5,8 @@ # Table name: conversation_mutes # # id :bigint(8) not null, primary key -# conversation_id :bigint(8) not null # account_id :bigint(8) not null +# conversation_id :bigint(8) not null # class ConversationMute < ApplicationRecord diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 5c39e053be13a1..fc1437539e154c 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -5,20 +5,20 @@ # Table name: custom_emojis # # id :bigint(8) not null, primary key -# shortcode :string default(""), not null +# disabled :boolean default(FALSE), not null # domain :string -# image_file_name :string # image_content_type :string +# image_file_name :string # image_file_size :integer +# image_remote_url :string +# image_storage_schema_version :integer # image_updated_at :datetime -# created_at :datetime not null -# updated_at :datetime not null -# disabled :boolean default(FALSE), not null +# shortcode :string default(""), not null # uri :string -# image_remote_url :string # visible_in_picker :boolean default(TRUE), not null +# created_at :datetime not null +# updated_at :datetime not null # category_id :bigint(8) -# image_storage_schema_version :integer # class CustomEmoji < ApplicationRecord diff --git a/app/models/custom_filter_keyword.rb b/app/models/custom_filter_keyword.rb index 1abec4ddc4de3a..b417a1dc3e12e4 100644 --- a/app/models/custom_filter_keyword.rb +++ b/app/models/custom_filter_keyword.rb @@ -5,11 +5,11 @@ # Table name: custom_filter_keywords # # id :bigint(8) not null, primary key -# custom_filter_id :bigint(8) not null # keyword :text default(""), not null # whole_word :boolean default(TRUE), not null # created_at :datetime not null # updated_at :datetime not null +# custom_filter_id :bigint(8) not null # class CustomFilterKeyword < ApplicationRecord diff --git a/app/models/custom_filter_status.rb b/app/models/custom_filter_status.rb index c85b811280e4c9..5d50d551cc739e 100644 --- a/app/models/custom_filter_status.rb +++ b/app/models/custom_filter_status.rb @@ -5,10 +5,10 @@ # Table name: custom_filter_statuses # # id :bigint(8) not null, primary key -# custom_filter_id :bigint(8) not null -# status_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# custom_filter_id :bigint(8) not null +# status_id :bigint(8) not null # class CustomFilterStatus < ApplicationRecord diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 8e7d7b6afc5ee0..74a494517ace08 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -6,14 +6,14 @@ # # id :bigint(8) not null, primary key # domain :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# severity :integer default("silence") -# reject_media :boolean default(FALSE), not null -# reject_reports :boolean default(FALSE), not null +# obfuscate :boolean default(FALSE), not null # private_comment :text # public_comment :text -# obfuscate :boolean default(FALSE), not null +# reject_media :boolean default(FALSE), not null +# reject_reports :boolean default(FALSE), not null +# severity :integer default("silence") +# created_at :datetime not null +# updated_at :datetime not null # class DomainBlock < ApplicationRecord diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb index 44d6bc6987a5ca..d52c76b0300644 100644 --- a/app/models/email_domain_block.rb +++ b/app/models/email_domain_block.rb @@ -5,11 +5,11 @@ # Table name: email_domain_blocks # # id :bigint(8) not null, primary key +# allow_with_approval :boolean default(FALSE), not null # domain :string default(""), not null # created_at :datetime not null # updated_at :datetime not null # parent_id :bigint(8) -# allow_with_approval :boolean default(FALSE), not null # class EmailDomainBlock < ApplicationRecord diff --git a/app/models/featured_tag.rb b/app/models/featured_tag.rb index a0938d6c0a4a3c..6f3279ea18c133 100644 --- a/app/models/featured_tag.rb +++ b/app/models/featured_tag.rb @@ -5,13 +5,13 @@ # Table name: featured_tags # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# tag_id :bigint(8) not null -# statuses_count :bigint(8) default(0), not null # last_status_at :datetime +# name :string +# statuses_count :bigint(8) default(0), not null # created_at :datetime not null # updated_at :datetime not null -# name :string +# account_id :bigint(8) not null +# tag_id :bigint(8) not null # class FeaturedTag < ApplicationRecord diff --git a/app/models/follow.rb b/app/models/follow.rb index 4d1598dcad66c7..c8930c2f868b56 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -5,14 +5,14 @@ # Table name: follows # # id :bigint(8) not null, primary key +# languages :string is an Array +# notify :boolean default(FALSE), not null +# show_reblogs :boolean default(TRUE), not null +# uri :string # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null # target_account_id :bigint(8) not null -# show_reblogs :boolean default(TRUE), not null -# uri :string -# notify :boolean default(FALSE), not null -# languages :string is an Array # class Follow < ApplicationRecord diff --git a/app/models/follow_recommendation.rb b/app/models/follow_recommendation.rb index 0435437a814baf..98d6be527edba6 100644 --- a/app/models/follow_recommendation.rb +++ b/app/models/follow_recommendation.rb @@ -4,9 +4,9 @@ # # Table name: global_follow_recommendations # -# account_id :bigint(8) primary key # rank :decimal(, ) # reason :text is an Array +# account_id :bigint(8) primary key # class FollowRecommendation < ApplicationRecord diff --git a/app/models/follow_recommendation_mute.rb b/app/models/follow_recommendation_mute.rb index ef931988e7162a..980f6f15470e6b 100644 --- a/app/models/follow_recommendation_mute.rb +++ b/app/models/follow_recommendation_mute.rb @@ -5,10 +5,10 @@ # Table name: follow_recommendation_mutes # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# target_account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# target_account_id :bigint(8) not null # class FollowRecommendationMute < ApplicationRecord belongs_to :account diff --git a/app/models/follow_recommendation_suppression.rb b/app/models/follow_recommendation_suppression.rb index 59e94dc6b3a464..efbe135a1bfcab 100644 --- a/app/models/follow_recommendation_suppression.rb +++ b/app/models/follow_recommendation_suppression.rb @@ -5,9 +5,9 @@ # Table name: follow_recommendation_suppressions # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null # class FollowRecommendationSuppression < ApplicationRecord diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb index 0b518036b10393..906de95db29020 100644 --- a/app/models/follow_request.rb +++ b/app/models/follow_request.rb @@ -5,14 +5,14 @@ # Table name: follow_requests # # id :bigint(8) not null, primary key +# languages :string is an Array +# notify :boolean default(FALSE), not null +# show_reblogs :boolean default(TRUE), not null +# uri :string # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null # target_account_id :bigint(8) not null -# show_reblogs :boolean default(TRUE), not null -# uri :string -# notify :boolean default(FALSE), not null -# languages :string is an Array # class FollowRequest < ApplicationRecord diff --git a/app/models/identity.rb b/app/models/identity.rb index 77821b78fa2550..c01980c660a482 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -4,11 +4,11 @@ # # Table name: identities # +# id :bigint(8) not null, primary key # provider :string default(""), not null # uid :string default(""), not null # created_at :datetime not null # updated_at :datetime not null -# id :bigint(8) not null, primary key # user_id :bigint(8) # diff --git a/app/models/instance.rb b/app/models/instance.rb index 01d258281adf9b..a49f73f7997f5a 100644 --- a/app/models/instance.rb +++ b/app/models/instance.rb @@ -4,8 +4,8 @@ # # Table name: instances # -# domain :string primary key # accounts_count :bigint(8) +# domain :string primary key # class Instance < ApplicationRecord diff --git a/app/models/invite.rb b/app/models/invite.rb index 9437ebee60b773..2e9371a07419bf 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -5,15 +5,15 @@ # Table name: invites # # id :bigint(8) not null, primary key -# user_id :bigint(8) not null +# autofollow :boolean default(FALSE), not null # code :string default(""), not null +# comment :text # expires_at :datetime # max_uses :integer # uses :integer default(0), not null # created_at :datetime not null # updated_at :datetime not null -# autofollow :boolean default(FALSE), not null -# comment :text +# user_id :bigint(8) not null # class Invite < ApplicationRecord diff --git a/app/models/ip_block.rb b/app/models/ip_block.rb index b3b678a6a16df6..5bbfc1fd246699 100644 --- a/app/models/ip_block.rb +++ b/app/models/ip_block.rb @@ -5,12 +5,12 @@ # Table name: ip_blocks # # id :bigint(8) not null, primary key -# created_at :datetime not null -# updated_at :datetime not null +# comment :text default(""), not null # expires_at :datetime # ip :inet default(#), not null # severity :integer default(NULL), not null -# comment :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null # class IpBlock < ApplicationRecord diff --git a/app/models/list.rb b/app/models/list.rb index 49ead642ac9dee..221c690f07db24 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -5,12 +5,12 @@ # Table name: lists # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null +# exclusive :boolean default(FALSE), not null +# replies_policy :integer default("list"), not null # title :string default(""), not null # created_at :datetime not null # updated_at :datetime not null -# replies_policy :integer default("list"), not null -# exclusive :boolean default(FALSE), not null +# account_id :bigint(8) not null # class List < ApplicationRecord diff --git a/app/models/list_account.rb b/app/models/list_account.rb index 00ecd44c3c353e..2eb3f92064c952 100644 --- a/app/models/list_account.rb +++ b/app/models/list_account.rb @@ -5,10 +5,10 @@ # Table name: list_accounts # # id :bigint(8) not null, primary key -# list_id :bigint(8) not null # account_id :bigint(8) not null # follow_id :bigint(8) # follow_request_id :bigint(8) +# list_id :bigint(8) not null # class ListAccount < ApplicationRecord diff --git a/app/models/login_activity.rb b/app/models/login_activity.rb index 240d571c0edd38..f124b9812b1869 100644 --- a/app/models/login_activity.rb +++ b/app/models/login_activity.rb @@ -5,14 +5,14 @@ # Table name: login_activities # # id :bigint(8) not null, primary key -# user_id :bigint(8) not null # authentication_method :string -# provider :string -# success :boolean # failure_reason :string # ip :inet +# provider :string +# success :boolean # user_agent :string # created_at :datetime +# user_id :bigint(8) not null # class LoginActivity < ApplicationRecord diff --git a/app/models/mention.rb b/app/models/mention.rb index e921d41de357db..35491c0acf9422 100644 --- a/app/models/mention.rb +++ b/app/models/mention.rb @@ -5,11 +5,11 @@ # Table name: mentions # # id :bigint(8) not null, primary key -# status_id :bigint(8) not null +# silent :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null -# silent :boolean default(FALSE), not null +# status_id :bigint(8) not null # class Mention < ApplicationRecord diff --git a/app/models/mute.rb b/app/models/mute.rb index 1d18b30eea9385..b5594e2b7fa6a1 100644 --- a/app/models/mute.rb +++ b/app/models/mute.rb @@ -5,12 +5,12 @@ # Table name: mutes # # id :bigint(8) not null, primary key +# expires_at :datetime +# hide_notifications :boolean default(TRUE), not null # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null # target_account_id :bigint(8) not null -# hide_notifications :boolean default(TRUE), not null -# expires_at :datetime # class Mute < ApplicationRecord diff --git a/app/models/notification.rb b/app/models/notification.rb index 8ee32798ad58fa..9f03539f764f49 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -5,15 +5,15 @@ # Table name: notifications # # id :bigint(8) not null, primary key -# activity_id :bigint(8) not null # activity_type :string not null +# filtered :boolean default(FALSE), not null +# group_key :string +# type :string # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null +# activity_id :bigint(8) not null # from_account_id :bigint(8) not null -# type :string -# filtered :boolean default(FALSE), not null -# group_key :string # class Notification < ApplicationRecord diff --git a/app/models/notification_permission.rb b/app/models/notification_permission.rb index e0001473f815a8..169112c69f64d9 100644 --- a/app/models/notification_permission.rb +++ b/app/models/notification_permission.rb @@ -5,10 +5,10 @@ # Table name: notification_permissions # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# from_account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# from_account_id :bigint(8) not null # class NotificationPermission < ApplicationRecord belongs_to :account diff --git a/app/models/notification_policy.rb b/app/models/notification_policy.rb index d22f871a37b7bb..73a13b92a8744d 100644 --- a/app/models/notification_policy.rb +++ b/app/models/notification_policy.rb @@ -5,14 +5,14 @@ # Table name: notification_policies # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# created_at :datetime not null -# updated_at :datetime not null -# for_not_following :integer default("accept"), not null -# for_not_followers :integer default("accept"), not null +# for_limited_accounts :integer default("filter"), not null # for_new_accounts :integer default("accept"), not null +# for_not_followers :integer default("accept"), not null +# for_not_following :integer default("accept"), not null # for_private_mentions :integer default("filter"), not null -# for_limited_accounts :integer default("filter"), not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) not null # class NotificationPolicy < ApplicationRecord diff --git a/app/models/notification_request.rb b/app/models/notification_request.rb index d95fb58b476c30..e8ed508d87ea82 100644 --- a/app/models/notification_request.rb +++ b/app/models/notification_request.rb @@ -5,12 +5,12 @@ # Table name: notification_requests # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# from_account_id :bigint(8) not null -# last_status_id :bigint(8) # notifications_count :bigint(8) default(0), not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# from_account_id :bigint(8) not null +# last_status_id :bigint(8) # class NotificationRequest < ApplicationRecord diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index 4c8b52a8d57916..86646445705455 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -5,33 +5,33 @@ # Table name: preview_cards # # id :bigint(8) not null, primary key -# url :string default(""), not null -# title :string default(""), not null +# author_name :string default(""), not null +# author_url :string default(""), not null +# blurhash :string # description :string default(""), not null -# image_file_name :string +# embed_url :string default(""), not null +# height :integer default(0), not null +# html :text default(""), not null # image_content_type :string +# image_description :string default(""), not null +# image_file_name :string # image_file_size :integer +# image_storage_schema_version :integer # image_updated_at :datetime -# type :integer default("link"), not null -# html :text default(""), not null -# author_name :string default(""), not null -# author_url :string default(""), not null +# language :string +# link_type :integer +# max_score :float +# max_score_at :datetime # provider_name :string default(""), not null # provider_url :string default(""), not null +# published_at :datetime +# title :string default(""), not null +# trendable :boolean +# type :integer default("link"), not null +# url :string default(""), not null # width :integer default(0), not null -# height :integer default(0), not null # created_at :datetime not null # updated_at :datetime not null -# embed_url :string default(""), not null -# image_storage_schema_version :integer -# blurhash :string -# language :string -# max_score :float -# max_score_at :datetime -# trendable :boolean -# link_type :integer -# published_at :datetime -# image_description :string default(""), not null # author_account_id :bigint(8) # unverified_author_account_id :bigint(8) # diff --git a/app/models/preview_card_provider.rb b/app/models/preview_card_provider.rb index 889176036c7558..9d77d45e22cbbf 100644 --- a/app/models/preview_card_provider.rb +++ b/app/models/preview_card_provider.rb @@ -6,13 +6,13 @@ # # id :bigint(8) not null, primary key # domain :string default(""), not null -# icon_file_name :string # icon_content_type :string +# icon_file_name :string # icon_file_size :bigint(8) # icon_updated_at :datetime -# trendable :boolean -# reviewed_at :datetime # requested_review_at :datetime +# reviewed_at :datetime +# trendable :boolean # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/preview_card_trend.rb b/app/models/preview_card_trend.rb index 58155971a368cd..f3f3bc14363201 100644 --- a/app/models/preview_card_trend.rb +++ b/app/models/preview_card_trend.rb @@ -5,11 +5,11 @@ # Table name: preview_card_trends # # id :bigint(8) not null, primary key -# preview_card_id :bigint(8) not null -# score :float default(0.0), not null -# rank :integer default(0), not null # allowed :boolean default(FALSE), not null # language :string +# rank :integer default(0), not null +# score :float default(0.0), not null +# preview_card_id :bigint(8) not null # class PreviewCardTrend < ApplicationRecord include RankedTrend diff --git a/app/models/preview_cards_status.rb b/app/models/preview_cards_status.rb index 5ff635205548b3..d70958843183b3 100644 --- a/app/models/preview_cards_status.rb +++ b/app/models/preview_cards_status.rb @@ -4,9 +4,9 @@ # # Table name: preview_cards_statuses # +# url :string # preview_card_id :bigint(8) not null, primary key # status_id :bigint(8) not null, primary key -# url :string # class PreviewCardsStatus < ApplicationRecord self.primary_key = [:preview_card_id, :status_id] diff --git a/app/models/relationship_severance_event.rb b/app/models/relationship_severance_event.rb index 30ada2576714d3..864038e980cac2 100644 --- a/app/models/relationship_severance_event.rb +++ b/app/models/relationship_severance_event.rb @@ -5,9 +5,9 @@ # Table name: relationship_severance_events # # id :bigint(8) not null, primary key -# type :integer not null -# target_name :string not null # purged :boolean default(FALSE), not null +# target_name :string not null +# type :integer not null # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/relay.rb b/app/models/relay.rb index 8a9524e9b37173..53221887bdfe22 100644 --- a/app/models/relay.rb +++ b/app/models/relay.rb @@ -6,10 +6,10 @@ # # id :bigint(8) not null, primary key # inbox_url :string default(""), not null -# follow_activity_id :string +# state :integer default("idle"), not null # created_at :datetime not null # updated_at :datetime not null -# state :integer default("idle"), not null +# follow_activity_id :string # class Relay < ApplicationRecord diff --git a/app/models/report.rb b/app/models/report.rb index 86fbda1d2bfac5..282a1f757014bb 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -5,20 +5,20 @@ # Table name: reports # # id :bigint(8) not null, primary key -# status_ids :bigint(8) default([]), not null, is an Array +# action_taken_at :datetime +# category :integer default("other"), not null # comment :text default(""), not null +# forwarded :boolean +# rule_ids :bigint(8) is an Array +# status_ids :bigint(8) default([]), not null, is an Array +# uri :string # created_at :datetime not null # updated_at :datetime not null # account_id :bigint(8) not null # action_taken_by_account_id :bigint(8) -# target_account_id :bigint(8) not null -# assigned_account_id :bigint(8) -# uri :string -# forwarded :boolean -# category :integer default("other"), not null -# action_taken_at :datetime -# rule_ids :bigint(8) is an Array # application_id :bigint(8) +# assigned_account_id :bigint(8) +# target_account_id :bigint(8) not null # class Report < ApplicationRecord diff --git a/app/models/report_note.rb b/app/models/report_note.rb index 9d3be52594f4c4..b2ac3aefe979cb 100644 --- a/app/models/report_note.rb +++ b/app/models/report_note.rb @@ -6,10 +6,10 @@ # # id :bigint(8) not null, primary key # content :text not null -# report_id :bigint(8) not null -# account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# report_id :bigint(8) not null # class ReportNote < ApplicationRecord diff --git a/app/models/rule.rb b/app/models/rule.rb index 672bef7d1ea166..0db6e13d0550db 100644 --- a/app/models/rule.rb +++ b/app/models/rule.rb @@ -5,12 +5,12 @@ # Table name: rules # # id :bigint(8) not null, primary key -# priority :integer default(0), not null # deleted_at :datetime +# hint :text default(""), not null +# priority :integer default(0), not null # text :text default(""), not null # created_at :datetime not null # updated_at :datetime not null -# hint :text default(""), not null # class Rule < ApplicationRecord include Discard::Model diff --git a/app/models/session_activation.rb b/app/models/session_activation.rb index 55b1428be65b60..39cc090d37c0ca 100644 --- a/app/models/session_activation.rb +++ b/app/models/session_activation.rb @@ -5,12 +5,12 @@ # Table name: session_activations # # id :bigint(8) not null, primary key -# session_id :string not null +# ip :inet +# user_agent :string default(""), not null # created_at :datetime not null # updated_at :datetime not null -# user_agent :string default(""), not null -# ip :inet # access_token_id :bigint(8) +# session_id :string not null # user_id :bigint(8) not null # web_push_subscription_id :bigint(8) # diff --git a/app/models/setting.rb b/app/models/setting.rb index 12ff32f00a1703..a5492eebb726d0 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -5,8 +5,8 @@ # Table name: settings # # id :bigint(8) not null, primary key -# var :string not null # value :text +# var :string not null # created_at :datetime # updated_at :datetime # diff --git a/app/models/severed_relationship.rb b/app/models/severed_relationship.rb index 64b5b0001b751a..eb9b0023ed57b4 100644 --- a/app/models/severed_relationship.rb +++ b/app/models/severed_relationship.rb @@ -5,15 +5,15 @@ # Table name: severed_relationships # # id :bigint(8) not null, primary key -# relationship_severance_event_id :bigint(8) not null -# local_account_id :bigint(8) not null -# remote_account_id :bigint(8) not null # direction :integer not null -# show_reblogs :boolean -# notify :boolean # languages :string is an Array +# notify :boolean +# show_reblogs :boolean # created_at :datetime not null # updated_at :datetime not null +# local_account_id :bigint(8) not null +# relationship_severance_event_id :bigint(8) not null +# remote_account_id :bigint(8) not null # class SeveredRelationship < ApplicationRecord belongs_to :relationship_severance_event diff --git a/app/models/site_upload.rb b/app/models/site_upload.rb index 273dd6de9fd22a..62bf03b5e80535 100644 --- a/app/models/site_upload.rb +++ b/app/models/site_upload.rb @@ -5,15 +5,15 @@ # Table name: site_uploads # # id :bigint(8) not null, primary key -# var :string default(""), not null -# file_file_name :string +# blurhash :string # file_content_type :string +# file_file_name :string # file_file_size :integer # file_updated_at :datetime # meta :json +# var :string default(""), not null # created_at :datetime not null # updated_at :datetime not null -# blurhash :string # class SiteUpload < ApplicationRecord diff --git a/app/models/software_update.rb b/app/models/software_update.rb index 4c868f6f597948..266ca1326f05e6 100644 --- a/app/models/software_update.rb +++ b/app/models/software_update.rb @@ -5,10 +5,10 @@ # Table name: software_updates # # id :bigint(8) not null, primary key -# version :string not null -# urgent :boolean default(FALSE), not null -# type :integer default("patch"), not null # release_notes :string default(""), not null +# type :integer default("patch"), not null +# urgent :boolean default(FALSE), not null +# version :string not null # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/status.rb b/app/models/status.rb index d5cfe2df681239..c97465939b64b7 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -5,30 +5,30 @@ # Table name: statuses # # id :bigint(8) not null, primary key -# uri :string +# deleted_at :datetime +# edited_at :datetime +# fetched_replies_at :datetime +# language :string +# local :boolean +# ordered_media_attachment_ids :bigint(8) is an Array +# quote_approval_policy :integer default(0), not null +# reply :boolean default(FALSE), not null +# sensitive :boolean default(FALSE), not null +# spoiler_text :text default(""), not null # text :text default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# in_reply_to_id :bigint(8) -# reblog_of_id :bigint(8) +# trendable :boolean +# uri :string # url :string -# sensitive :boolean default(FALSE), not null # visibility :integer default("public"), not null -# spoiler_text :text default(""), not null -# reply :boolean default(FALSE), not null -# language :string -# conversation_id :bigint(8) -# local :boolean +# created_at :datetime not null +# updated_at :datetime not null # account_id :bigint(8) not null # application_id :bigint(8) +# conversation_id :bigint(8) # in_reply_to_account_id :bigint(8) +# in_reply_to_id :bigint(8) # poll_id :bigint(8) -# deleted_at :datetime -# edited_at :datetime -# trendable :boolean -# ordered_media_attachment_ids :bigint(8) is an Array -# fetched_replies_at :datetime -# quote_approval_policy :integer default(0), not null +# reblog_of_id :bigint(8) # class Status < ApplicationRecord diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb index 060866e50c3518..a83046600d7fed 100644 --- a/app/models/status_edit.rb +++ b/app/models/status_edit.rb @@ -5,17 +5,17 @@ # Table name: status_edits # # id :bigint(8) not null, primary key -# status_id :bigint(8) not null -# account_id :bigint(8) -# text :text default(""), not null -# spoiler_text :text default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# ordered_media_attachment_ids :bigint(8) is an Array # media_descriptions :text is an Array +# ordered_media_attachment_ids :bigint(8) is an Array # poll_options :string is an Array # sensitive :boolean +# spoiler_text :text default(""), not null +# text :text default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) # quote_id :bigint(8) +# status_id :bigint(8) not null # class StatusEdit < ApplicationRecord diff --git a/app/models/status_pin.rb b/app/models/status_pin.rb index 83711dde42da93..190124caed7087 100644 --- a/app/models/status_pin.rb +++ b/app/models/status_pin.rb @@ -5,10 +5,10 @@ # Table name: status_pins # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# status_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# status_id :bigint(8) not null # class StatusPin < ApplicationRecord diff --git a/app/models/status_trend.rb b/app/models/status_trend.rb index 807efec0ff18cf..b76d6a7260b948 100644 --- a/app/models/status_trend.rb +++ b/app/models/status_trend.rb @@ -5,12 +5,12 @@ # Table name: status_trends # # id :bigint(8) not null, primary key -# status_id :bigint(8) not null -# account_id :bigint(8) not null -# score :float default(0.0), not null -# rank :integer default(0), not null # allowed :boolean default(FALSE), not null # language :string +# rank :integer default(0), not null +# score :float default(0.0), not null +# account_id :bigint(8) not null +# status_id :bigint(8) not null # class StatusTrend < ApplicationRecord diff --git a/app/models/tag.rb b/app/models/tag.rb index 97edda879e5026..224ec327366976 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -5,18 +5,18 @@ # Table name: tags # # id :bigint(8) not null, primary key -# name :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# usable :boolean -# trendable :boolean -# listable :boolean -# reviewed_at :datetime -# requested_review_at :datetime +# display_name :string # last_status_at :datetime +# listable :boolean # max_score :float # max_score_at :datetime -# display_name :string +# name :string default(""), not null +# requested_review_at :datetime +# reviewed_at :datetime +# trendable :boolean +# usable :boolean +# created_at :datetime not null +# updated_at :datetime not null # class Tag < ApplicationRecord diff --git a/app/models/tag_follow.rb b/app/models/tag_follow.rb index 528616c4502b98..5f0935345f79b0 100644 --- a/app/models/tag_follow.rb +++ b/app/models/tag_follow.rb @@ -5,10 +5,10 @@ # Table name: tag_follows # # id :bigint(8) not null, primary key -# tag_id :bigint(8) not null -# account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null +# tag_id :bigint(8) not null # class TagFollow < ApplicationRecord diff --git a/app/models/user_ip.rb b/app/models/user_ip.rb index 25aa81ccd42f71..690c383c797738 100644 --- a/app/models/user_ip.rb +++ b/app/models/user_ip.rb @@ -4,9 +4,9 @@ # # Table name: user_ips # -# user_id :bigint(8) primary key # ip :inet # used_at :datetime +# user_id :bigint(8) primary key # class UserIp < ApplicationRecord diff --git a/app/models/webauthn_credential.rb b/app/models/webauthn_credential.rb index 3681ce332c2b7e..0c7f9b26bfa55d 100644 --- a/app/models/webauthn_credential.rb +++ b/app/models/webauthn_credential.rb @@ -5,13 +5,13 @@ # Table name: webauthn_credentials # # id :bigint(8) not null, primary key -# external_id :string not null -# public_key :string not null # nickname :string not null +# public_key :string not null # sign_count :bigint(8) default(0), not null -# user_id :bigint(8) # created_at :datetime not null # updated_at :datetime not null +# external_id :string not null +# user_id :bigint(8) # class WebauthnCredential < ApplicationRecord diff --git a/app/models/webhook.rb b/app/models/webhook.rb index e3dff76365da7d..fea6a972c7315f 100644 --- a/app/models/webhook.rb +++ b/app/models/webhook.rb @@ -5,13 +5,13 @@ # Table name: webhooks # # id :bigint(8) not null, primary key -# url :string not null +# enabled :boolean default(TRUE), not null # events :string default([]), not null, is an Array # secret :string default(""), not null -# enabled :boolean default(TRUE), not null +# template :text +# url :string not null # created_at :datetime not null # updated_at :datetime not null -# template :text # class Webhook < ApplicationRecord From 7778db1418a1ab0b18b230f9a0a6be6b9ccf773b Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 12 Mar 2026 10:04:21 -0400 Subject: [PATCH 054/203] Truncate unsupported/EOL version changelogs (#38173) --- CHANGELOG.md | 1152 +------------------------------------------------- 1 file changed, 1 insertion(+), 1151 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6c0ff20d1aba3..0cdb887424ef1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1245,1154 +1245,4 @@ The following changelog entries focus on changes visible to users, administrator - Fix empty environment variables not using default nil value (#27400 by @renchap) - Fix language sorting in settings (#27158 by @gunchleoc) -## [4.2.11] - 2024-08-16 - -### Added - -- Add support for incoming `` tag ([mediaformat](https://github.com/mastodon/mastodon/pull/31375)) - -### Changed - -- Change logic of block/mute bypass for mentions from moderators to only apply to visible roles with moderation powers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31271)) - -### Fixed - -- Fix incorrect rate limit on PUT requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31356)) -- Fix presence of `ß` in adjacent word preventing mention and hashtag matching ([adamniedzielski](https://github.com/mastodon/mastodon/pull/31122)) -- Fix processing of webfinger responses with multiple `self` links ([adamniedzielski](https://github.com/mastodon/mastodon/pull/31110)) -- Fix duplicate `orderedItems` in user archive's `outbox.json` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31099)) -- Fix click event handling when clicking outside of an open dropdown menu ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31251)) -- Fix status processing failing halfway when a remote post has a malformed `replies` attribute ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31246)) -- Fix `--verbose` option of `tootctl media remove`, which was previously erroneously removed ([mjankowski](https://github.com/mastodon/mastodon/pull/30536)) -- Fix division by zero on some video/GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30600)) -- Fix Web UI trying to save user settings despite being logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30324)) -- Fix hashtag regexp matching some link anchors ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30190)) -- Fix local account search on LDAP login being case-sensitive ([raucao](https://github.com/mastodon/mastodon/pull/30113)) -- Fix development environment admin account not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29958)) -- Fix report reason selector in moderation interface not unselecting rules when changing category ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29026)) -- Fix already-invalid reports failing to resolve ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29027)) -- Fix OCR when using S3/CDN for assets ([vmstan](https://github.com/mastodon/mastodon/pull/28551)) -- Fix error when encountering malformed `Tag` objects from Kbin ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28235)) -- Fix not all allowed image formats showing in file picker when uploading custom emoji ([june128](https://github.com/mastodon/mastodon/pull/28076)) -- Fix search popout listing unusable search options when logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27918)) -- Fix processing of featured collections lacking an `items` attribute ([tribela](https://github.com/mastodon/mastodon/pull/27581)) -- Fix `mastodon:stats` decoration of stats rake task ([mjankowski](https://github.com/mastodon/mastodon/pull/31104)) - -## [4.2.10] - 2024-07-04 - -### Security - -- Fix incorrect permission checking on multiple API endpoints ([GHSA-58x8-3qxw-6hm7](https://github.com/mastodon/mastodon/security/advisories/GHSA-58x8-3qxw-6hm7)) -- Fix incorrect authorship checking when processing some activities (CVE-2024-37903, [GHSA-xjvf-fm67-4qc3](https://github.com/mastodon/mastodon/security/advisories/GHSA-xjvf-fm67-4qc3)) -- Fix ongoing streaming sessions not being invalidated when application tokens get revoked ([GHSA-vp5r-5pgw-jwqx](https://github.com/mastodon/mastodon/security/advisories/GHSA-vp5r-5pgw-jwqx)) -- Update dependencies - -### Added - -- Add yarn version specification to avoid confusion with Yarn 3 and Yarn 4 - -### Changed - -- Change preview cards generation to skip unusually long URLs ([oneiros](https://github.com/mastodon/mastodon/pull/30854)) -- Change search modifiers to be case-insensitive ([Gargron](https://github.com/mastodon/mastodon/pull/30865)) -- Change `STATSD_ADDR` handling to emit a warning rather than crashing if the address is unreachable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30691)) -- Change PWA start URL from `/home` to `/` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27377)) - -### Removed - -- Removed dependency on `posix-spawn` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18559)) - -### Fixed - -- Fix scheduled statuses scheduled in less than 5 minutes being immediately published ([danielmbrasil](https://github.com/mastodon/mastodon/pull/30584)) -- Fix encoding detection for link cards ([oneiros](https://github.com/mastodon/mastodon/pull/30780)) -- Fix `/admin/accounts/:account_id/statuses/:id` for edited posts with media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30819)) -- Fix duplicate `@context` attribute in user archive export ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30653)) - -## [4.2.9] - 2024-05-30 - -### Security - -- Update dependencies -- Fix private mention filtering ([GHSA-5fq7-3p3j-9vrf](https://github.com/mastodon/mastodon/security/advisories/GHSA-5fq7-3p3j-9vrf)) -- Fix password change endpoint not being rate-limited ([GHSA-q3rg-xx5v-4mxh](https://github.com/mastodon/mastodon/security/advisories/GHSA-q3rg-xx5v-4mxh)) -- Add hardening around rate-limit bypass ([GHSA-c2r5-cfqr-c553](https://github.com/mastodon/mastodon/security/advisories/GHSA-c2r5-cfqr-c553)) - -### Added - -- Add rate-limit on OAuth application registration ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316)) -- Add fallback redirection when getting a webfinger query `WEB_DOMAIN@WEB_DOMAIN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28592)) -- Add `digest` attribute to `Admin::DomainBlock` entity in REST API ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29092)) - -### Removed - -- Remove superfluous application-level caching in some controllers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29862)) -- Remove aggressive OAuth application vacuuming ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316)) - -### Fixed - -- Fix leaking Elasticsearch connections in Sidekiq processes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30450)) -- Fix language of remote posts not being recognized when using unusual casing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30403)) -- Fix off-by-one in `tootctl media` commands ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30306)) -- Fix removal of allowed domains (in `LIMITED_FEDERATION_MODE`) not being recorded in the audit log ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30125)) -- Fix not being able to block a subdomain of an already-blocked domain through the API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30119)) -- Fix `Idempotency-Key` being ignored when scheduling a post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30084)) -- Fix crash when supplying the `FFMPEG_BINARY` environment variable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30022)) -- Fix improper email address validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29838)) -- Fix results/query in `api/v1/featured_tags/suggestions` ([mjankowski](https://github.com/mastodon/mastodon/pull/29597)) -- Fix unblocking internationalized domain names under certain conditions ([tribela](https://github.com/mastodon/mastodon/pull/29530)) -- Fix admin account created by `mastodon:setup` not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29379)) -- Fix reference to non-existent var in CLI maintenance command ([mjankowski](https://github.com/mastodon/mastodon/pull/28363)) - -## [4.2.8] - 2024-02-23 - -### Added - -- Add hourly task to automatically require approval for new registrations in the absence of moderators ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29318), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29355)) - In order to prevent future abandoned Mastodon servers from being used for spam, harassment and other malicious activity, Mastodon will now automatically switch new user registrations to require moderator approval whenever they are left open and no activity (including non-moderation actions from apps) from any logged-in user with permission to access moderation reports has been detected in a full week. - When this happens, users with the permission to change server settings will receive an email notification. - This feature is disabled when `EMAIL_DOMAIN_ALLOWLIST` is used, and can also be disabled with `DISABLE_AUTOMATIC_SWITCHING_TO_APPROVED_REGISTRATIONS=true`. - -### Changed - -- Change registrations to be closed by default on new installations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29280)) - If you are running a server and never changed your registrations mode from the default, updating will automatically close your registrations. - Simply re-enable them through the administration interface or using `tootctl settings registrations open` if you want to enable them again. - -### Fixed - -- Fix processing of remote ActivityPub actors making use of `Link` objects as `Image` `url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29335)) -- Fix link verifications when page size exceeds 1MB ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29358)) - -## [4.2.7] - 2024-02-16 - -### Fixed - -- Fix OmniAuth tests and edge cases in error handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29201), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29207)) -- Fix new installs by upgrading to the latest release of the `nsa` gem, instead of a no longer existing commit ([mjankowski](https://github.com/mastodon/mastodon/pull/29065)) - -### Security - -- Fix insufficient checking of remote posts ([GHSA-jhrq-qvrm-qr36](https://github.com/mastodon/mastodon/security/advisories/GHSA-jhrq-qvrm-qr36)) - -## [4.2.6] - 2024-02-14 - -### Security - -- Update the `sidekiq-unique-jobs` dependency (see [GHSA-cmh9-rx85-xj38](https://github.com/mhenrixon/sidekiq-unique-jobs/security/advisories/GHSA-cmh9-rx85-xj38)) - In addition, we have disabled the web interface for `sidekiq-unique-jobs` out of caution. - If you need it, you can re-enable it by setting `ENABLE_SIDEKIQ_UNIQUE_JOBS_UI=true`. - If you only need to clear all locks, you can now use `bundle exec rake sidekiq_unique_jobs:delete_all_locks`. -- Update the `nokogiri` dependency (see [GHSA-xc9x-jj77-9p9j](https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-xc9x-jj77-9p9j)) -- Disable administrative Doorkeeper routes ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29187)) -- Fix ongoing streaming sessions not being invalidated when applications get deleted in some cases ([GHSA-7w3c-p9j8-mq3x](https://github.com/mastodon/mastodon/security/advisories/GHSA-7w3c-p9j8-mq3x)) - In some rare cases, the streaming server was not notified of access tokens revocation on application deletion. -- Change external authentication behavior to never reattach a new identity to an existing user by default ([GHSA-vm39-j3vx-pch3](https://github.com/mastodon/mastodon/security/advisories/GHSA-vm39-j3vx-pch3)) - Up until now, Mastodon has allowed new identities from external authentication providers to attach to an existing local user based on their verified e-mail address. - This allowed upgrading users from a database-stored password to an external authentication provider, or move from one authentication provider to another. - However, this behavior may be unexpected, and means that when multiple authentication providers are configured, the overall security would be that of the least secure authentication provider. - For these reasons, this behavior is now locked under the `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH` environment variable. - In addition, regardless of this environment variable, Mastodon will refuse to attach two identities from the same authentication provider to the same account. - -## [4.2.5] - 2024-02-01 - -### Security - -- Fix insufficient origin validation (CVE-2024-23832, [GHSA-3fjr-858r-92rw](https://github.com/mastodon/mastodon/security/advisories/GHSA-3fjr-858r-92rw)) - -## [4.2.4] - 2024-01-24 - -### Fixed - -- Fix error when processing remote files with unusually long names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28823)) -- Fix processing of compacted single-item JSON-LD collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28816)) -- Retry 401 errors on replies fetching ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28788)) -- Fix `RecordNotUnique` errors in LinkCrawlWorker ([tribela](https://github.com/mastodon/mastodon/pull/28748)) -- Fix Mastodon not correctly processing HTTP Signatures with query strings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28443), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28476)) -- Fix potential redirection loop of streaming endpoint ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28665)) -- Fix streaming API redirection ignoring the port of `streaming_api_base_url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28558)) -- Fix error when processing link preview with an array as `inLanguage` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28252)) -- Fix unsupported time zone or locale preventing sign-up ([Gargron](https://github.com/mastodon/mastodon/pull/28035)) -- Fix "Hide these posts from home" list setting not refreshing when switching lists ([brianholley](https://github.com/mastodon/mastodon/pull/27763)) -- Fix missing background behind dismissable banner in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/27479)) -- Fix line wrapping of language selection button with long locale codes ([gunchleoc](https://github.com/mastodon/mastodon/pull/27100), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27127)) -- Fix `Undo Announce` activity not being sent to non-follower authors ([MitarashiDango](https://github.com/mastodon/mastodon/pull/18482)) -- Fix N+1s because of association preloaders not actually getting called ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28339)) -- Fix empty column explainer getting cropped under certain conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28337)) -- Fix `LinkCrawlWorker` error when encountering empty OEmbed response ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28268)) -- Fix call to inefficient `delete_matched` cache method in domain blocks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28367)) - -### Security - -- Add rate-limit of TOTP authentication attempts at controller level ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28801)) - -## [4.2.3] - 2023-12-05 - -### Fixed - -- Fix dependency on `json-canonicalization` version that has been made unavailable since last release - -## [4.2.2] - 2023-12-04 - -### Changed - -- Change dismissed banners to be stored server-side ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27055)) -- Change GIF max matrix size error to explicitly mention GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27927)) -- Change `Follow` activities delivery to bypass availability check ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/27586)) -- Change single-column navigation notice to be displayed outside of the logo container ([renchap](https://github.com/mastodon/mastodon/pull/27462), [renchap](https://github.com/mastodon/mastodon/pull/27476)) -- Change Content-Security-Policy to be tighter on media paths ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26889)) -- Change post language code to include country code when relevant ([gunchleoc](https://github.com/mastodon/mastodon/pull/27099), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27207)) - -### Fixed - -- Fix upper border radius of onboarding columns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27890)) -- Fix incoming status creation date not being restricted to standard ISO8601 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27655), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28081)) -- Fix some posts from threads received out-of-order sometimes not being inserted into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27653)) -- Fix posts from force-sensitized accounts being able to trend ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27620)) -- Fix error when trying to delete already-deleted file with OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27569)) -- Fix batch attachment deletion when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27554)) -- Fix processing LDSigned activities from actors with unknown public keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27474)) -- Fix error and incorrect URLs in `/api/v1/accounts/:id/featured_tags` for remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27459)) -- Fix report processing notice not mentioning the report number when performing a custom action ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27442)) -- Fix handling of `inLanguage` attribute in preview card processing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27423)) -- Fix own posts being removed from home timeline when unfollowing a used hashtag ([kmycode](https://github.com/mastodon/mastodon/pull/27391)) -- Fix some link anchors being recognized as hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27584)) -- Fix format-dependent redirects being cached regardless of requested format ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27634)) - -## [4.2.1] - 2023-10-10 - -### Added - -- Add redirection on `/deck` URLs for logged-out users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27128)) -- Add support for v4.2.0 migrations to `tootctl maintenance fix-duplicates` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27147)) - -### Changed - -- Change some worker lock TTLs to be shorter-lived ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27246)) -- Change user archive export allowed period from 7 days to 6 days ([suddjian](https://github.com/mastodon/mastodon/pull/27200)) - -### Fixed - -- Fix duplicate reports being sent when reporting some remote posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27355)) -- Fix clicking on already-opened thread post scrolling to the top of the thread ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27331), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27338), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27350)) -- Fix some remote posts getting truncated ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27307)) -- Fix some cases of infinite scroll code trying to fetch inaccessible posts in a loop ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27286)) -- Fix `Vary` headers not being set on some redirects ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27272)) -- Fix mentions being matched in some URL query strings ([mjankowski](https://github.com/mastodon/mastodon/pull/25656)) -- Fix unexpected linebreak in version string in the Web UI ([vmstan](https://github.com/mastodon/mastodon/pull/26986)) -- Fix double scroll bars in some columns in advanced interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27187)) -- Fix boosts of local users being filtered in account timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27204)) -- Fix multiple instances of the trend refresh scheduler sometimes running at once ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27253)) -- Fix importer returning negative row estimates ([jgillich](https://github.com/mastodon/mastodon/pull/27258)) -- Fix incorrectly keeping outdated update notices absent from the API endpoint ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27021)) -- Fix import progress not updating on certain failures ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27247)) -- Fix websocket connections being incorrectly decremented twice on errors ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/27238)) -- Fix explore prompt appearing because of posts being received out of order ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27211)) -- Fix explore prompt sometimes showing up when the home TL is loading ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27062)) -- Fix link handling of mentions in user profiles when logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27185)) -- Fix filtering audit log for entries about disabling 2FA ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27186)) -- Fix notification toasts not respecting reduce-motion ([c960657](https://github.com/mastodon/mastodon/pull/27178)) -- Fix retention dashboard not displaying correct month ([vmstan](https://github.com/mastodon/mastodon/pull/27180)) -- Fix tIME chunk not being properly removed from PNG uploads ([TheEssem](https://github.com/mastodon/mastodon/pull/27111)) -- Fix division by zero in video in bitrate computation code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27129)) -- Fix inefficient queries in “Follows and followers” as well as several admin pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27116), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27306)) -- Fix ActiveRecord using two connection pools when no replica is defined ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27061)) -- Fix the search documentation URL in system checks ([renchap](https://github.com/mastodon/mastodon/pull/27036)) - -## [4.2.0] - 2023-09-21 - -The following changelog entries focus on changes visible to users, administrators, client developers or federated software developers, but there has also been a lot of code modernization, refactoring, and tooling work, in particular by [@danielmbrasil](https://github.com/danielmbrasil), [@mjankowski](https://github.com/mjankowski), [@nschonni](https://github.com/nschonni), [@renchap](https://github.com/renchap), and [@takayamaki](https://github.com/takayamaki). - -### Added - -- **Add full-text search of opted-in public posts and rework search operators** ([Gargron](https://github.com/mastodon/mastodon/pull/26485), [jsgoldstein](https://github.com/mastodon/mastodon/pull/26344), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26657), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26650), [jsgoldstein](https://github.com/mastodon/mastodon/pull/26659), [Gargron](https://github.com/mastodon/mastodon/pull/26660), [Gargron](https://github.com/mastodon/mastodon/pull/26663), [Gargron](https://github.com/mastodon/mastodon/pull/26688), [Gargron](https://github.com/mastodon/mastodon/pull/26689), [Gargron](https://github.com/mastodon/mastodon/pull/26686), [Gargron](https://github.com/mastodon/mastodon/pull/26687), [Gargron](https://github.com/mastodon/mastodon/pull/26692), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26697), [Gargron](https://github.com/mastodon/mastodon/pull/26699), [Gargron](https://github.com/mastodon/mastodon/pull/26701), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26710), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26739), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26754), [Gargron](https://github.com/mastodon/mastodon/pull/26662), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26755), [Gargron](https://github.com/mastodon/mastodon/pull/26781), [Gargron](https://github.com/mastodon/mastodon/pull/26782), [Gargron](https://github.com/mastodon/mastodon/pull/26760), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26756), [Gargron](https://github.com/mastodon/mastodon/pull/26784), [Gargron](https://github.com/mastodon/mastodon/pull/26807), [Gargron](https://github.com/mastodon/mastodon/pull/26835), [Gargron](https://github.com/mastodon/mastodon/pull/26847), [Gargron](https://github.com/mastodon/mastodon/pull/26834), [arbolitoloco1](https://github.com/mastodon/mastodon/pull/26893), [tribela](https://github.com/mastodon/mastodon/pull/26896), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26927), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26959), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27014)) - This introduces a new `public_statuses` Elasticsearch index for public posts by users who have opted in to their posts being searchable (`toot#indexable` flag). - This also revisits the other indexes to provide more useful indexing, and adds new search operators such as `from:me`, `before:2022-11-01`, `after:2022-11-01`, `during:2022-11-01`, `language:fr`, `has:poll`, or `in:library` (for searching only in posts you have written or interacted with). - Results are now ordered chronologically. -- **Add admin notifications for new Mastodon versions** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26582)) - This is done by querying `https://api.joinmastodon.org/update-check` every 30 minutes in a background job. - That URL can be changed using the `UPDATE_CHECK_URL` environment variable, and the feature outright disabled by setting that variable to an empty string (`UPDATE_CHECK_URL=`). -- **Add “Privacy and reach” tab in profile settings** ([Gargron](https://github.com/mastodon/mastodon/pull/26484), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26508)) - This reorganized scattered privacy and reach settings to a single place, as well as improve their wording. -- **Add display of out-of-band hashtags in the web interface** ([Gargron](https://github.com/mastodon/mastodon/pull/26492), [arbolitoloco1](https://github.com/mastodon/mastodon/pull/26497), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26506), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26525), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26606), [Gargron](https://github.com/mastodon/mastodon/pull/26666), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26960)) -- **Add role badges to the web interface** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25649), [Gargron](https://github.com/mastodon/mastodon/pull/26281)) -- **Add ability to pick domains to forward reports to using the `forward_to_domains` parameter in `POST /api/v1/reports`** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25866), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26636)) - The `forward_to_domains` REST API parameter is a list of strings. If it is empty or omitted, the previous behavior is maintained. - The `forward` parameter still needs to be set for `forward_to_domains` to be taken into account. - The forwarded-to domains can only include that of the original author and people being replied to. -- **Add forwarding of reported replies to servers being replied to** ([Gargron](https://github.com/mastodon/mastodon/pull/25341), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26189)) -- Add `ONE_CLICK_SSO_LOGIN` environment variable to directly link to the Single-Sign On provider if there is only one sign up method available ([CSDUMMI](https://github.com/mastodon/mastodon/pull/26083), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26368), [CSDUMMI](https://github.com/mastodon/mastodon/pull/26857), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26901)) -- **Add webhook templating** ([Gargron](https://github.com/mastodon/mastodon/pull/23289)) -- **Add webhooks for local `status.created`, `status.updated`, `account.updated` and `report.updated`** ([VyrCossont](https://github.com/mastodon/mastodon/pull/24133), [VyrCossont](https://github.com/mastodon/mastodon/pull/24243), [VyrCossont](https://github.com/mastodon/mastodon/pull/24211)) -- **Add exclusive lists** ([dariusk, necropolina](https://github.com/mastodon/mastodon/pull/22048), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25324)) -- **Add a confirmation screen when suspending a domain** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25144), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25603)) -- **Add support for importing lists** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25203), [mgmn](https://github.com/mastodon/mastodon/pull/26120), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26372)) -- **Add optional hCaptcha support** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25019), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25057), [Gargron](https://github.com/mastodon/mastodon/pull/25395), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26388)) -- **Add lines to threads in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24549), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24677), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24696), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24711), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24714), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24713), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24715), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24800), [teeerevor](https://github.com/mastodon/mastodon/pull/25706), [renchap](https://github.com/mastodon/mastodon/pull/25807)) -- **Add new onboarding flow to web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24619), [Gargron](https://github.com/mastodon/mastodon/pull/24646), [Gargron](https://github.com/mastodon/mastodon/pull/24705), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24872), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/24883), [Gargron](https://github.com/mastodon/mastodon/pull/24954), [stevenjlm](https://github.com/mastodon/mastodon/pull/24959), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25010), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25275), [Gargron](https://github.com/mastodon/mastodon/pull/25559), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25561)) -- **Add auto-refresh of accounts we get new messages/edits of** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26510)) -- **Add Elasticsearch cluster health check and indexes mismatch check to dashboard** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26448), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26605), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26658)) -- Add `hide_collections`, `discoverable` and `indexable` attributes to credentials API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26998)) -- Add `S3_ENABLE_CHECKSUM_MODE` environment variable to enable checksum verification on compatible S3-providers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26435)) -- Add admin API for managing tags ([rrgeorge](https://github.com/mastodon/mastodon/pull/26872)) -- Add a link to hashtag timelines from the Trending hashtags moderation interface ([gunchleoc](https://github.com/mastodon/mastodon/pull/26724)) -- Add timezone to datetimes in e-mails ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26822)) -- Add `authorized_fetch` server setting in addition to env var ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25798), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26958)) -- Add avatar image to webfinger responses ([tvler](https://github.com/mastodon/mastodon/pull/26558)) -- Add debug logging on signature verification failure ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26637), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26812)) -- Add explicit error messages when DeepL quota is exceeded ([lutoma](https://github.com/mastodon/mastodon/pull/26704)) -- Add Elasticsearch/OpenSearch version to “Software” in admin dashboard ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26652)) -- Add `data-nosnippet` attribute to remote posts and local posts with `noindex` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26648)) -- Add support for federating `memorial` attribute ([rrgeorge](https://github.com/mastodon/mastodon/pull/26583)) -- Add Cherokee and Kalmyk to languages dropdown ([gunchleoc](https://github.com/mastodon/mastodon/pull/26012), [gunchleoc](https://github.com/mastodon/mastodon/pull/26013)) -- Add `DELETE /api/v1/profile/avatar` and `DELETE /api/v1/profile/header` to the REST API ([danielmbrasil](https://github.com/mastodon/mastodon/pull/25124), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26573)) -- Add `ES_PRESET` option to customize numbers of shards and replicas ([Gargron](https://github.com/mastodon/mastodon/pull/26483), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26489)) - This can have a value of `single_node_cluster` (default), `small_cluster` (uses one replica) or `large_cluster` (uses one replica and a higher number of shards). -- Add `CACHE_BUSTER_HTTP_METHOD` environment variable ([renchap](https://github.com/mastodon/mastodon/pull/26528), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26542)) -- Add support for `DB_PASS` when using `DATABASE_URL` ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26295)) -- Add `GET /api/v1/instance/languages` to REST API ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24443)) -- Add primary key to `preview_cards_statuses` join table ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25243), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26384), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26447), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26737), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26979)) -- Add client-side timeout on resend confirmation button ([Gargron](https://github.com/mastodon/mastodon/pull/26300)) -- Add published date and author to news on the explore screen in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26155)) -- Add `lang` attribute to various UI components ([c960657](https://github.com/mastodon/mastodon/pull/23869), [c960657](https://github.com/mastodon/mastodon/pull/23891), [c960657](https://github.com/mastodon/mastodon/pull/26111), [c960657](https://github.com/mastodon/mastodon/pull/26149)) -- Add stricter protocol fields validation for accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25937)) -- Add support for Azure blob storage ([mistydemeo](https://github.com/mastodon/mastodon/pull/23607), [mistydemeo](https://github.com/mastodon/mastodon/pull/26080)) -- Add toast with option to open post after publishing in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25564), [Signez](https://github.com/mastodon/mastodon/pull/25919), [Gargron](https://github.com/mastodon/mastodon/pull/26664)) -- Add canonical link tags in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25715)) -- Add button to see results for polls in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25726)) -- Add at-symbol prepended to mention span title ([forsamori](https://github.com/mastodon/mastodon/pull/25684)) -- Add users index on `unconfirmed_email` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25672), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25702)) -- Add superapp index on `oauth_applications` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25670)) -- Add index to backups on `user_id` column ([mjankowski](https://github.com/mastodon/mastodon/pull/25647)) -- Add onboarding prompt when home feed too slow in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25267), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25556), [Gargron](https://github.com/mastodon/mastodon/pull/25579), [renchap](https://github.com/mastodon/mastodon/pull/25580), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25581), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25617), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25917), [Gargron](https://github.com/mastodon/mastodon/pull/26829), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26935)) -- Add `POST /api/v1/conversations/:id/unread` API endpoint to mark a conversation as unread ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25509)) -- Add `translate="no"` to outgoing mentions and links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25524)) -- Add unsubscribe link and headers to e-mails ([Gargron](https://github.com/mastodon/mastodon/pull/25378), [c960657](https://github.com/mastodon/mastodon/pull/26085)) -- Add logging of websocket send errors ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25280)) -- Add time zone preference ([Gargron](https://github.com/mastodon/mastodon/pull/25342), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26025)) -- Add `legal` as report category ([Gargron](https://github.com/mastodon/mastodon/pull/23941), [renchap](https://github.com/mastodon/mastodon/pull/25400), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26509)) -- Add `data-nosnippet` so Google doesn't use trending posts in snippets for `/` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25279)) -- Add card with who invited you to join when displaying rules on sign-up ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23475)) -- Add missing primary keys to `accounts_tags` and `statuses_tags` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25210)) -- Add support for custom sign-up URLs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25014), [renchap](https://github.com/mastodon/mastodon/pull/25108), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25190), [mgmn](https://github.com/mastodon/mastodon/pull/25531)) - This is set using `SSO_ACCOUNT_SIGN_UP` and reflected in the REST API by adding `registrations.sign_up_url` to the `/api/v2/instance` endpoint. -- Add polling and automatic redirection to `/start` on email confirmation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25013)) -- Add ability to block sign-ups from IP using the CLI ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24870)) -- Add ALT badges to media that has alternative text in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24782), [c960657](https://github.com/mastodon/mastodon/pull/26166) -- Add ability to include accounts with pending follow requests in lists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19727), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24810)) -- Add trend management to admin API ([rrgeorge](https://github.com/mastodon/mastodon/pull/24257)) - - `POST /api/v1/admin/trends/statuses/:id/approve` - - `POST /api/v1/admin/trends/statuses/:id/reject` - - `POST /api/v1/admin/trends/links/:id/approve` - - `POST /api/v1/admin/trends/links/:id/reject` - - `POST /api/v1/admin/trends/tags/:id/approve` - - `POST /api/v1/admin/trends/tags/:id/reject` - - `GET /api/v1/admin/trends/links/publishers` - - `POST /api/v1/admin/trends/links/publishers/:id/approve` - - `POST /api/v1/admin/trends/links/publishers/:id/reject` -- Add user handle to notification mail recipient address ([HeitorMC](https://github.com/mastodon/mastodon/pull/24240)) -- Add progress indicator to sign-up flow ([Gargron](https://github.com/mastodon/mastodon/pull/24545)) -- Add client-side validation for taken username in sign-up form ([Gargron](https://github.com/mastodon/mastodon/pull/24546)) -- Add `--approve` option to `tootctl accounts create` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24533)) -- Add “In Memoriam” banner back to profiles ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23591), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23614)) - This adds the `memorial` attribute to the `Account` REST API entity. -- Add colour to follow button when hashtag is being followed ([c960657](https://github.com/mastodon/mastodon/pull/24361)) -- Add further explanations to the profile link verification instructions ([drzax](https://github.com/mastodon/mastodon/pull/19723)) -- Add a link to Identity provider's account settings from the account settings ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24100), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24628)) -- Add support for streaming server to connect to postgres with self-signed certs through the `sslmode` URL parameter ([ramuuns](https://github.com/mastodon/mastodon/pull/21431)) -- Add support for specifying S3 storage classes through the `S3_STORAGE_CLASS` environment variable ([hyl](https://github.com/mastodon/mastodon/pull/22480)) -- Add support for incoming rich text ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23913)) -- Add support for Ruby 3.2 ([tenderlove](https://github.com/mastodon/mastodon/pull/22928), [casperisfine](https://github.com/mastodon/mastodon/pull/24142), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24202), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26934)) -- Add API parameter to safeguard unexpected mentions in new posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18350)) - -### Changed - -- **Change hashtags to be displayed separately when they are the last line of a post** ([renchap](https://github.com/mastodon/mastodon/pull/26499), [renchap](https://github.com/mastodon/mastodon/pull/26614), [renchap](https://github.com/mastodon/mastodon/pull/26615)) -- **Change reblogs to be excluded from "Posts and replies" tab in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/26302)) -- **Change interaction modal in web interface** ([Gargron, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26075), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26269), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26268), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26267), [mgmn](https://github.com/mastodon/mastodon/pull/26459), [tribela](https://github.com/mastodon/mastodon/pull/26461), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26593), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26795)) -- **Change design of link previews in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/26136), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26151), [Gargron](https://github.com/mastodon/mastodon/pull/26153), [Gargron](https://github.com/mastodon/mastodon/pull/26250), [Gargron](https://github.com/mastodon/mastodon/pull/26287), [Gargron](https://github.com/mastodon/mastodon/pull/26286), [c960657](https://github.com/mastodon/mastodon/pull/26184)) -- **Change "direct message" nomenclature to "private mention" in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24248)) -- **Change translation feature to cover Content Warnings, poll options and media descriptions** ([c960657](https://github.com/mastodon/mastodon/pull/24175), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25251), [c960657](https://github.com/mastodon/mastodon/pull/26168), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26452)) -- **Change account search to match by text when opted-in** ([jsgoldstein](https://github.com/mastodon/mastodon/pull/25599), [Gargron](https://github.com/mastodon/mastodon/pull/26378)) -- **Change import feature to be clearer, less error-prone and more reliable** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21054), [mgmn](https://github.com/mastodon/mastodon/pull/24874)) -- **Change local and federated timelines to be tabs of a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26633)) -- **Change user archive export to be faster and more reliable, and export `.zip` archives instead of `.tar.gz` ones** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23360), [TheEssem](https://github.com/mastodon/mastodon/pull/25034)) -- **Change `mastodon-streaming` systemd unit files to be templated** ([e-nomem](https://github.com/mastodon/mastodon/pull/24751)) -- **Change `statsd` integration to disable sidekiq metrics by default** ([mjankowski](https://github.com/mastodon/mastodon/pull/25265), [mjankowski](https://github.com/mastodon/mastodon/pull/25336), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26310)) - This deprecates `statsd` support and disables the sidekiq integration unless `STATSD_SIDEKIQ` is set to `true`. - This is because the `nsa` gem is unmaintained, and its sidekiq integration is known to add very significant overhead. - Later versions of Mastodon will have other ways to get the same metrics. -- **Change replica support to native Rails adapter** ([krainboltgreene](https://github.com/mastodon/mastodon/pull/25693), [Gargron](https://github.com/mastodon/mastodon/pull/25849), [Gargron](https://github.com/mastodon/mastodon/pull/25874), [Gargron](https://github.com/mastodon/mastodon/pull/25851), [Gargron](https://github.com/mastodon/mastodon/pull/25977), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26074), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26326), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26386), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26856)) - This is a breaking change, dropping `makara` support, and requiring you to update your database configuration if you are using replicas. - To tell Mastodon to use a read replica, you can either set the `REPLICA_DB_NAME` environment variable (along with `REPLICA_DB_USER`, `REPLICA_DB_PASS`, `REPLICA_DB_HOST`, and `REPLICA_DB_PORT`, if they differ from the primary database), or the `REPLICA_DATABASE_URL` environment variable if your configuration is based on `DATABASE_URL`. -- Change DCT method used for JPEG encoding to float ([electroCutie](https://github.com/mastodon/mastodon/pull/26675)) -- Change from `node-redis` to `ioredis` for streaming ([gmemstr](https://github.com/mastodon/mastodon/pull/26581)) -- Change private statuses index to index without crutches ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26713)) -- Change video compression parameters ([Gargron](https://github.com/mastodon/mastodon/pull/26631), [Gargron](https://github.com/mastodon/mastodon/pull/26745), [Gargron](https://github.com/mastodon/mastodon/pull/26766), [Gargron](https://github.com/mastodon/mastodon/pull/26970)) -- Change admin e-mail notification settings to be their own settings group ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26596)) -- Change opacity of the delete icon in the search field to be more visible ([AntoninDelFabbro](https://github.com/mastodon/mastodon/pull/26449)) -- Change Account Search to prioritize username over display name ([jsgoldstein](https://github.com/mastodon/mastodon/pull/26623)) -- Change follow recommendation materialized view to be faster in most cases ([renchap, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26545)) -- Change `robots.txt` to block GPTBot ([Foritus](https://github.com/mastodon/mastodon/pull/26396)) -- Change header of hashtag timelines in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26362), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26416)) -- Change streaming `/metrics` to include additional metrics ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26299), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26945)) -- Change indexing frequency from 5 minutes to 1 minute, add locks to schedulers ([Gargron](https://github.com/mastodon/mastodon/pull/26304)) -- Change column link to add a better keyboard focus indicator ([teeerevor](https://github.com/mastodon/mastodon/pull/26278)) -- Change poll form element colors to fit with the rest of the ui ([teeerevor](https://github.com/mastodon/mastodon/pull/26139), [teeerevor](https://github.com/mastodon/mastodon/pull/26162), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26164)) -- Change 'favourite' to 'favorite' for American English ([marekr](https://github.com/mastodon/mastodon/pull/24667), [gunchleoc](https://github.com/mastodon/mastodon/pull/26009), [nabijaczleweli](https://github.com/mastodon/mastodon/pull/26109)) -- Change ActivityStreams representation of suspended accounts to not use a blank `name` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25276)) -- Change focus UI for keyboard only input ([teeerevor](https://github.com/mastodon/mastodon/pull/25935), [Gargron](https://github.com/mastodon/mastodon/pull/26125), [Gargron](https://github.com/mastodon/mastodon/pull/26767)) -- Change thread view to scroll to the selected post rather than the post being replied to ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24685)) -- Change links in multi-column mode so tabs are open in single-column mode ([Signez](https://github.com/mastodon/mastodon/pull/25893), [Signez](https://github.com/mastodon/mastodon/pull/26070), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25973), [Signez](https://github.com/mastodon/mastodon/pull/26019), [Signez](https://github.com/mastodon/mastodon/pull/26759)) -- Change searching with `#` to include account index ([jsgoldstein](https://github.com/mastodon/mastodon/pull/25638)) -- Change label and design of sensitive and unavailable media in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25712), [Gargron](https://github.com/mastodon/mastodon/pull/26135), [Gargron](https://github.com/mastodon/mastodon/pull/26330)) -- Change button colors to increase hover/focus contrast and consistency ([teeerevor](https://github.com/mastodon/mastodon/pull/25677), [Gargron](https://github.com/mastodon/mastodon/pull/25679)) -- Change dropdown icon above compose form from ellipsis to bars in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25661)) -- Change header backgrounds to use fewer different colors in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25577)) -- Change files to be deleted in batches instead of one-by-one ([Gargron](https://github.com/mastodon/mastodon/pull/23302), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25586), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25587)) -- Change emoji picker icon ([iparr](https://github.com/mastodon/mastodon/pull/25479)) -- Change edit profile page ([Gargron](https://github.com/mastodon/mastodon/pull/25413), [c960657](https://github.com/mastodon/mastodon/pull/26538)) -- Change "bot" label to "automated" ([Gargron](https://github.com/mastodon/mastodon/pull/25356)) -- Change design of dropdowns in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25107)) -- Change wording of “Content cache retention period” setting to highlight destructive implications ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23261)) -- Change autolinking to allow carets in URL search params ([renchap](https://github.com/mastodon/mastodon/pull/25216)) -- Change share action from being in action bar to being in dropdown in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25105)) -- Change sessions to be ordered from most-recent to least-recently updated ([frankieroberto](https://github.com/mastodon/mastodon/pull/25005)) -- Change vacuum scheduler to also delete expired tokens and unused application records ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24868), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24871)) -- Change "Sign in" to "Login" ([Gargron](https://github.com/mastodon/mastodon/pull/24942)) -- Change domain suspensions to also be checked before trying to fetch unknown remote resources ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24535)) -- Change media components to use aspect-ratio rather than compute height themselves ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24686), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24943), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26801)) -- Change logo version in header based on screen size in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24707)) -- Change label from "For you" to "People" on explore screen in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24706)) -- Change logged-out WebUI HTML pages to be cached for a few seconds ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24708)) -- Change unauthenticated responses to be cached in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/24348), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24662), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24665)) -- Change HTTP caching logic ([Gargron](https://github.com/mastodon/mastodon/pull/24347), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24604)) -- Change hashtags and mentions in bios to open in-app in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24643)) -- Change styling of the recommended accounts to allow bio to be more visible ([chike00](https://github.com/mastodon/mastodon/pull/24480)) -- Change account search in moderation interface to allow searching by username including the leading `@` ([HeitorMC](https://github.com/mastodon/mastodon/pull/24242)) -- Change all components to use the same error page in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24512)) -- Change search pop-out in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24305)) -- Change user settings to be stored in a more optimal way ([Gargron](https://github.com/mastodon/mastodon/pull/23630), [c960657](https://github.com/mastodon/mastodon/pull/24321), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24453), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24460), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24558), [Gargron](https://github.com/mastodon/mastodon/pull/24761), [Gargron](https://github.com/mastodon/mastodon/pull/24783), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25508), [jsgoldstein](https://github.com/mastodon/mastodon/pull/25340), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26884), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27012)) -- Change media upload limits and remove client-side resizing ([Gargron](https://github.com/mastodon/mastodon/pull/23726)) -- Change design of account rows in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24247), [Gargron](https://github.com/mastodon/mastodon/pull/24343), [Gargron](https://github.com/mastodon/mastodon/pull/24956), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25131)) -- Change log-out to use Single Logout when using external log-in through OIDC ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24020)) -- Change sidekiq-bulk's batch size from 10,000 to 1,000 jobs in one Redis call ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24034)) -- Change translation to only be offered for supported languages ([c960657](https://github.com/mastodon/mastodon/pull/23879), [c960657](https://github.com/mastodon/mastodon/pull/24037)) - This adds the `/api/v1/instance/translation_languages` REST API endpoint that returns an object with the supported translation language pairs in the form: - ```json - { - "fr": ["en", "de"] - } - ``` - (where `fr` is a supported source language and `en` and `de` or supported output language when translating a `fr` string) -- Change compose form checkbox to native input with `appearance: none` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22949)) -- Change posts' clickable area to be larger ([c960657](https://github.com/mastodon/mastodon/pull/23621)) -- Change `followed_by` link to `location=all` if account is local on /admin/accounts/:id page ([tribela](https://github.com/mastodon/mastodon/pull/23467)) - -### Removed - -- **Remove support for Node.js 14** ([renchap](https://github.com/mastodon/mastodon/pull/25198)) -- **Remove support for Ruby 2.7** ([nschonni](https://github.com/mastodon/mastodon/pull/24237)) -- **Remove clustering from streaming API** ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/24655)) -- **Remove anonymous access to the streaming API** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23989)) -- Remove obfuscation of reply count in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26768)) -- Remove `kmr` from language selection, as it was a duplicate for `ku` ([gunchleoc](https://github.com/mastodon/mastodon/pull/26014), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26787)) -- Remove 16:9 cropping from web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26132)) -- Remove back button from bookmarks, favourites and lists screens in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26126)) -- Remove display name input from sign-up form ([Gargron](https://github.com/mastodon/mastodon/pull/24704)) -- Remove `tai` locale ([c960657](https://github.com/mastodon/mastodon/pull/23880)) -- Remove empty Kushubian (csb) local files ([nschonni](https://github.com/mastodon/mastodon/pull/24151)) -- Remove `Permissions-Policy` header from all responses ([Gargron](https://github.com/mastodon/mastodon/pull/24124)) - -### Fixed - -- **Fix filters not being applying in the explore page** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25887)) -- **Fix being unable to load past a full page of filtered posts in Home timeline** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24930)) -- **Fix log-in flow when involving both OAuth and external authentication** ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24073)) -- **Fix broken links in account gallery** ([c960657](https://github.com/mastodon/mastodon/pull/24218)) -- **Fix migration handler not updating lists** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24808)) -- Fix crash when viewing a moderation appeal and the moderator account has been deleted ([xrobau](https://github.com/mastodon/mastodon/pull/25900)) -- Fix error in Web UI when server rules cannot be fetched ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26957)) -- Fix paragraph margins resulting in irregular read-more cut-off in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26828)) -- Fix notification permissions being requested immediately after login ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26472)) -- Fix performances of profile directory ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26840), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26842)) -- Fix mute button and volume slider feeling disconnected in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26827), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26860)) -- Fix “Scoped order is ignored, it's forced to be batch order.” warnings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26793)) -- Fix blocked domain appearing in account feeds ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26823)) -- Fix invalid `Content-Type` header for WebP images ([c960657](https://github.com/mastodon/mastodon/pull/26773)) -- Fix minor inefficiencies in `tootctl search deploy` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26721)) -- Fix filter form in profiles directory overflowing instead of wrapping ([arbolitoloco1](https://github.com/mastodon/mastodon/pull/26682)) -- Fix sign up steps progress layout in right-to-left locales ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26728)) -- Fix bug with “favorited by” and “reblogged by“ view on posts only showing up to 40 items ([timothyjrogers](https://github.com/mastodon/mastodon/pull/26577), [timothyjrogers](https://github.com/mastodon/mastodon/pull/26574)) -- Fix bad search type heuristic ([Gargron](https://github.com/mastodon/mastodon/pull/26673)) -- Fix not being able to negate prefix clauses in search ([Gargron](https://github.com/mastodon/mastodon/pull/26672)) -- Fix timeout on invalid set of exclusionary parameters in `/api/v1/timelines/public` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/26239)) -- Fix adding column with default value taking longer on Postgres >= 11 ([Gargron](https://github.com/mastodon/mastodon/pull/26375)) -- Fix light theme select option for hashtags ([teeerevor](https://github.com/mastodon/mastodon/pull/26311)) -- Fix AVIF attachments ([c960657](https://github.com/mastodon/mastodon/pull/26264)) -- Fix incorrect URL normalization when fetching remote resources ([c960657](https://github.com/mastodon/mastodon/pull/26219), [c960657](https://github.com/mastodon/mastodon/pull/26285)) -- Fix being unable to filter posts for individual Chinese languages ([gunchleoc](https://github.com/mastodon/mastodon/pull/26066)) -- Fix preview card sometimes linking to 4xx error pages ([c960657](https://github.com/mastodon/mastodon/pull/26200)) -- Fix emoji picker button scrolling with textarea content in single-column view ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25304)) -- Fix missing border on error screen in light theme in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26152)) -- Fix UI overlap with the loupe icon in the Explore Tab ([gol-cha](https://github.com/mastodon/mastodon/pull/26113)) -- Fix unexpected redirection to `/explore` after sign-in ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26143)) -- Fix `/api/v1/statuses/:id/unfavourite` and `/api/v1/statuses/:id/unreblog` returning non-updated counts ([c960657](https://github.com/mastodon/mastodon/pull/24365)) -- Fix clicking the “Back” button sometimes leading out of Mastodon ([c960657](https://github.com/mastodon/mastodon/pull/23953), [CSFlorin](https://github.com/mastodon/mastodon/pull/24835), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/24867), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25281)) -- Fix processing of `null` ActivityPub activities ([tribela](https://github.com/mastodon/mastodon/pull/26021)) -- Fix hashtag posts not being removed from home feed on hashtag unfollow ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26028)) -- Fix for "follows you" indicator in light web UI not readable ([vmstan](https://github.com/mastodon/mastodon/pull/25993)) -- Fix incorrect line break between icon and number of reposts & favourites ([edent](https://github.com/mastodon/mastodon/pull/26004)) -- Fix sounds not being loaded from assets host ([Signez](https://github.com/mastodon/mastodon/pull/25931)) -- Fix buttons showing inconsistent styles ([teeerevor](https://github.com/mastodon/mastodon/pull/25903), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25965), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26341), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26482)) -- Fix trend calculation working on too many items at a time ([Gargron](https://github.com/mastodon/mastodon/pull/25835)) -- Fix dropdowns being disabled for logged out users in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25714), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25964)) -- Fix explore page being inaccessible when opted-out of trends in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25716)) -- Fix re-activated accounts possibly getting deleted by `AccountDeletionWorker` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25711)) -- Fix `/api/v2/search` not working with following query param ([danielmbrasil](https://github.com/mastodon/mastodon/pull/25681)) -- Fix inefficient query when requesting a new confirmation email from a logged-in account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25669)) -- Fix unnecessary concurrent calls to `/api/*/instance` in web UI ([mgmn](https://github.com/mastodon/mastodon/pull/25663)) -- Fix resolving local URL for remote content ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25637)) -- Fix search not being easily findable on smaller screens in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25576), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25631)) -- Fix j/k keyboard shortcuts on some status lists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25554)) -- Fix missing validation on `default_privacy` setting ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25513)) -- Fix incorrect pagination headers in `/api/v2/admin/accounts` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/25477)) -- Fix non-interactive upload container being given a `button` role and tabIndex ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25462)) -- Fix always redirecting to onboarding in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25396)) -- Fix inconsistent use of middle dot (·) instead of bullet (•) to separate items ([j-f1](https://github.com/mastodon/mastodon/pull/25248)) -- Fix spacing of middle dots in the detailed status meta section ([j-f1](https://github.com/mastodon/mastodon/pull/25247)) -- Fix prev/next buttons color in media viewer ([renchap](https://github.com/mastodon/mastodon/pull/25231)) -- Fix email addresses not being properly updated in `tootctl maintenance fix-duplicates` ([mjankowski](https://github.com/mastodon/mastodon/pull/25118)) -- Fix unicode surrogate pairs sometimes being broken in page title ([eai04191](https://github.com/mastodon/mastodon/pull/25148)) -- Fix various inefficient queries against account domains ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25126)) -- Fix video player offering to expand in a lightbox when it's in an `iframe` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25067)) -- Fix post embed previews ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25071)) -- Fix inadequate error handling in several API controllers when given invalid parameters ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24947), [danielmbrasil](https://github.com/mastodon/mastodon/pull/24958), [danielmbrasil](https://github.com/mastodon/mastodon/pull/25063), [danielmbrasil](https://github.com/mastodon/mastodon/pull/25072), [danielmbrasil](https://github.com/mastodon/mastodon/pull/25386), [danielmbrasil](https://github.com/mastodon/mastodon/pull/25595)) -- Fix uncaught `ActiveRecord::StatementInvalid` in Mastodon::IpBlocksCLI ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24861)) -- Fix various edge cases with local moves ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24812)) -- Fix `tootctl accounts cull` crashing when encountering a domain resolving to a private address ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23378)) -- Fix `tootctl accounts approve --number N` not aproving the N earliest registrations ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24605)) -- Fix being unable to clear media description when editing posts ([c960657](https://github.com/mastodon/mastodon/pull/24720)) -- Fix unavailable translations not falling back to English ([mgmn](https://github.com/mastodon/mastodon/pull/24727)) -- Fix anonymous visitors getting a session cookie on first visit ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24584), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24650), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24664)) -- Fix cutting off first letter of hashtag links sometimes in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/24623)) -- Fix crash in `tootctl accounts create --reattach --force` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24557), [danielmbrasil](https://github.com/mastodon/mastodon/pull/24680)) -- Fix characters being emojified even when using Variation Selector 15 (text) ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20949), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24615)) -- Fix uncaught ActiveRecord::StatementInvalid exception in `Mastodon::AccountsCLI#approve` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24590)) -- Fix email confirmation skip option in `tootctl accounts modify USERNAME --email EMAIL --confirm` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24578)) -- Fix tooltip for dates without time ([c960657](https://github.com/mastodon/mastodon/pull/24244)) -- Fix missing loading spinner and loading more on scroll in Private Mentions column ([c960657](https://github.com/mastodon/mastodon/pull/24446)) -- Fix account header image missing from `/settings/profile` on narrow screens ([c960657](https://github.com/mastodon/mastodon/pull/24433)) -- Fix height of announcements not being updated when using reduced animations ([c960657](https://github.com/mastodon/mastodon/pull/24354)) -- Fix inconsistent radius in advanced interface drawer ([thislight](https://github.com/mastodon/mastodon/pull/24407)) -- Fix loading more trending posts on scroll in the advanced interface ([OmmyZhang](https://github.com/mastodon/mastodon/pull/24314)) -- Fix poll ending notification for edited polls ([c960657](https://github.com/mastodon/mastodon/pull/24311)) -- Fix max width of media in `/about` and `/privacy-policy` ([mgmn](https://github.com/mastodon/mastodon/pull/24180)) -- Fix streaming API not being usable without `DATABASE_URL` ([Gargron](https://github.com/mastodon/mastodon/pull/23960)) -- Fix external authentication not running onboarding code for new users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23458)) - -## [4.1.8] - 2023-09-19 - -### Fixed - -- Fix post edits not being forwarded as expected ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26936)) -- Fix moderator rights inconsistencies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26729)) -- Fix crash when encountering invalid URL ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26814)) -- Fix cached posts including stale stats ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26409)) -- Fix uploading of video files for which `ffprobe` reports `0/0` average framerate ([NicolaiSoeborg](https://github.com/mastodon/mastodon/pull/26500)) -- Fix unexpected audio stream transcoding when uploaded video is eligible to passthrough ([yufushiro](https://github.com/mastodon/mastodon/pull/26608)) - -### Security - -- Fix missing HTML sanitization in translation API (CVE-2023-42452, [GHSA-2693-xr3m-jhqr](https://github.com/mastodon/mastodon/security/advisories/GHSA-2693-xr3m-jhqr)) -- Fix incorrect domain name normalization (CVE-2023-42451, [GHSA-v3xf-c9qf-j667](https://github.com/mastodon/mastodon/security/advisories/GHSA-v3xf-c9qf-j667)) - -## [4.1.7] - 2023-09-05 - -### Changed - -- Change remote report processing to accept reports with long comments, but truncate them ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25028)) - -### Fixed - -- **Fix blocking subdomains of an already-blocked domain** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26392)) -- Fix `/api/v1/timelines/tag/:hashtag` allowing for unauthenticated access when public preview is disabled ([danielmbrasil](https://github.com/mastodon/mastodon/pull/26237)) -- Fix inefficiencies in `PlainTextFormatter` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26727)) - -## [4.1.6] - 2023-07-31 - -### Fixed - -- Fix memory leak in streaming server ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26228)) -- Fix wrong filters sometimes applying in streaming ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26159), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26213), [renchap](https://github.com/mastodon/mastodon/pull/26233)) -- Fix incorrect connect timeout in outgoing requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26116)) - -## [4.1.5] - 2023-07-21 - -### Added - -- Add check preventing Sidekiq workers from running with Makara configured ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25850)) - -### Changed - -- Change request timeout handling to use a longer deadline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26055)) - -### Fixed - -- Fix moderation interface for remote instances with a .zip TLD ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25885)) -- Fix remote accounts being possibly persisted to database with incomplete protocol values ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25886)) -- Fix trending publishers table not rendering correctly on narrow screens ([vmstan](https://github.com/mastodon/mastodon/pull/25945)) - -### Security - -- Fix CSP headers being unintentionally wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26105)) - -## [4.1.4] - 2023-07-07 - -### Fixed - -- Fix branding:generate_app_icons failing because of disallowed ICO coder ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25794)) -- Fix crash in admin interface when viewing a remote user with verified links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25796)) -- Fix processing of media files with unusual names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25788)) - -## [4.1.3] - 2023-07-06 - -### Added - -- Add fallback redirection when getting a webfinger query `LOCAL_DOMAIN@LOCAL_DOMAIN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23600)) - -### Changed - -- Change OpenGraph-based embeds to allow fullscreen ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25058)) -- Change AccessTokensVacuum to also delete expired tokens ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24868)) -- Change profile updates to be sent to recently-mentioned servers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24852)) -- Change automatic post deletion thresholds and load detection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24614)) -- Change `/api/v1/statuses/:id/history` to always return at least one item ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25510)) -- Change auto-linking to allow carets in URL query params ([renchap](https://github.com/mastodon/mastodon/pull/25216)) - -### Removed - -- Remove invalid `X-Frame-Options: ALLOWALL` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25070)) - -### Fixed - -- Fix wrong view being displayed when a webhook fails validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25464)) -- Fix soft-deleted post cleanup scheduler overwhelming the streaming server ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25519)) -- Fix incorrect pagination headers in `/api/v2/admin/accounts` ([danielmbrasil](https://github.com/mastodon/mastodon/pull/25477)) -- Fix multiple inefficiencies in automatic post cleanup worker ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24607), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24785), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24840)) -- Fix performance of streaming by parsing message JSON once ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25278), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25361)) -- Fix CSP headers when `S3_ALIAS_HOST` includes a path component ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25273)) -- Fix `tootctl accounts approve --number N` not approving N earliest registrations ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24605)) -- Fix reports not being closed when performing batch suspensions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24988)) -- Fix being able to vote on your own polls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25015)) -- Fix race condition when reblogging a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25016)) -- Fix “Authorized applications” inefficiently and incorrectly getting last use date ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25060)) -- Fix “Authorized applications” crashing when listing apps with certain admin API scopes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25713)) -- Fix multiple N+1s in ConversationsController ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25134), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25399), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25499)) -- Fix user archive takeouts when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24431)) -- Fix searching for remote content by URL not working under certain conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25637)) -- Fix inefficiencies in indexing content for search ([VyrCossont](https://github.com/mastodon/mastodon/pull/24285), [VyrCossont](https://github.com/mastodon/mastodon/pull/24342)) - -### Security - -- Add finer permission requirements for managing webhooks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25463)) -- Update dependencies -- Add hardening headers for user-uploaded files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25756)) -- Fix verified links possibly hiding important parts of the URL (CVE-2023-36462) -- Fix timeout handling of outbound HTTP requests (CVE-2023-36461) -- Fix arbitrary file creation through media processing (CVE-2023-36460) -- Fix possible XSS in preview cards (CVE-2023-36459) - -## [4.1.2] - 2023-04-04 - -### Fixed - -- Fix crash in `tootctl` commands making use of parallelization when Elasticsearch is enabled ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24182), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24377)) -- Fix crash in `db:setup` when Elasticsearch is enabled ([rrgeorge](https://github.com/mastodon/mastodon/pull/24302)) -- Fix user archive takeout when using OpenStack Swift or S3 providers with no ACL support ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24200)) -- Fix invalid/expired invites being processed on sign-up ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24337)) - -### Security - -- Update Ruby to 3.0.6 due to ReDoS vulnerabilities ([saizai](https://github.com/mastodon/mastodon/pull/24334)) -- Fix unescaped user input in LDAP query ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24379)) - -## [4.1.1] - 2023-03-16 - -### Added - -- Add redirection from paths with url-encoded `@` to their decoded form ([thijskh](https://github.com/mastodon/mastodon/pull/23593)) -- Add `lang` attribute to native language names in language picker in Web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23749)) -- Add headers to outgoing mails to avoid auto-replies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23597)) -- Add support for refreshing many accounts at once with `tootctl accounts refresh` ([9p4](https://github.com/mastodon/mastodon/pull/23304)) -- Add confirmation modal when clicking to edit a post with a non-empty compose form ([PauloVilarinho](https://github.com/mastodon/mastodon/pull/23936)) -- Add support for the HAproxy PROXY protocol through the `PROXY_PROTO_V1` environment variable ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24064)) -- Add `SENDFILE_HEADER` environment variable ([Gargron](https://github.com/mastodon/mastodon/pull/24123)) -- Add cache headers to static files served through Rails ([Gargron](https://github.com/mastodon/mastodon/pull/24120)) - -### Changed - -- Increase contrast of upload progress bar background ([toolmantim](https://github.com/mastodon/mastodon/pull/23836)) -- Change post auto-deletion throttling constants to better scale with server size ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23320)) -- Change order of bookmark and favourite sidebar entries in single-column UI for consistency ([TerryGarcia](https://github.com/mastodon/mastodon/pull/23701)) -- Change `ActivityPub::DeliveryWorker` retries to be spread out more ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21956)) - -### Fixed - -- Fix “Remove all followers from the selected domains” also removing follows and notifications ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23805)) -- Fix streaming metrics format ([emilweth](https://github.com/mastodon/mastodon/pull/23519), [emilweth](https://github.com/mastodon/mastodon/pull/23520)) -- Fix case-sensitive check for previously used hashtags in hashtag autocompletion ([deanveloper](https://github.com/mastodon/mastodon/pull/23526)) -- Fix focus point of already-attached media not saving after edit ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23566)) -- Fix sidebar behavior in settings/admin UI on mobile ([wxt2005](https://github.com/mastodon/mastodon/pull/23764)) -- Fix inefficiency when searching accounts per username in admin interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23801)) -- Fix duplicate “Publish” button on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23804)) -- Fix server error when failing to follow back followers from `/relationships` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23787)) -- Fix server error when attempting to display the edit history of a trendable post in the admin interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23574)) -- Fix `tootctl accounts migrate` crashing because of a typo ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23567)) -- Fix original account being unfollowed on migration before the follow request to the new account could be sent ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21957)) -- Fix the “Back” button in column headers sometimes leaving Mastodon ([c960657](https://github.com/mastodon/mastodon/pull/23953)) -- Fix pgBouncer resetting application name on every transaction ([Gargron](https://github.com/mastodon/mastodon/pull/23958)) -- Fix unconfirmed accounts being counted as active users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23803)) -- Fix `/api/v1/streaming` sub-paths not being redirected ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23988)) -- Fix drag'n'drop upload area text that spans multiple lines not being centered ([vintprox](https://github.com/mastodon/mastodon/pull/24029)) -- Fix sidekiq jobs not triggering Elasticsearch index updates ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24046)) -- Fix tags being unnecessarily stripped from plain-text short site description ([c960657](https://github.com/mastodon/mastodon/pull/23975)) -- Fix HTML entities not being un-escaped in extracted plain-text from remote posts ([c960657](https://github.com/mastodon/mastodon/pull/24019)) -- Fix dashboard crash on ElasticSearch server error ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23751)) -- Fix incorrect post links in strikes when the account is remote ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23611)) -- Fix misleading error code when receiving invalid WebAuthn credentials ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23568)) -- Fix duplicate mails being sent when the SMTP server is too slow to close the connection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23750)) - -### Security - -- Change user backups to use expiring URLs for download when possible ([Gargron](https://github.com/mastodon/mastodon/pull/24136)) -- Add warning for object storage misconfiguration ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24137)) - -## [4.1.0] - 2023-02-10 - -### Added - -- **Add support for importing/exporting server-wide domain blocks** ([enbylenore](https://github.com/mastodon/mastodon/pull/20597), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21471), [dariusk](https://github.com/mastodon/mastodon/pull/22803), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21470)) -- **Add listing of followed hashtags** ([connorshea](https://github.com/mastodon/mastodon/pull/21773)) -- **Add support for editing media description and focus point of already-sent posts** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20878)) - - Previously, you could add and remove attachments, but not edit media description of already-attached media - - REST API changes: - - `PUT /api/v1/statuses/:id` now takes an extra `media_attributes[]` array parameter with the `id` of the updated media and their updated `description`, `focus`, and `thumbnail` -- **Add follow request banner on account header** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20785)) - - REST API changes: - - `Relationship` entities have an extra `requested_by` boolean attribute representing whether the represented user has requested to follow you -- **Add confirmation screen when handling reports** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22375), [Gargron](https://github.com/mastodon/mastodon/pull/23156), [tribela](https://github.com/mastodon/mastodon/pull/23178)) -- Add option to make the landing page be `/about` even when trends are enabled ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20808)) -- Add `noindex` setting back to the admin interface ([prplecake](https://github.com/mastodon/mastodon/pull/22205)) -- Add instance peers API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22810)) -- Add instance activity API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22833)) -- Add setting for status page URL ([Gargron](https://github.com/mastodon/mastodon/pull/23390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23499)) - - REST API changes: - - Add `configuration.urls.status` attribute to the object returned by `GET /api/v2/instance` -- Add `account.approved` webhook ([Saiv46](https://github.com/mastodon/mastodon/pull/22938)) -- Add 12 hours option to polls ([Pleclown](https://github.com/mastodon/mastodon/pull/21131)) -- Add dropdown menu item to open admin interface for remote domains ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21895)) -- Add `--remove-headers`, `--prune-profiles` and `--include-follows` flags to `tootctl media remove` ([evanphilip](https://github.com/mastodon/mastodon/pull/22149)) -- Add `--email` and `--dry-run` options to `tootctl accounts delete` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22328)) -- Add `tootctl accounts migrate` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22330)) -- Add `tootctl accounts prune` ([tribela](https://github.com/mastodon/mastodon/pull/18397)) -- Add `tootctl domains purge` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22063)) -- Add `SIDEKIQ_CONCURRENCY` environment variable ([muffinista](https://github.com/mastodon/mastodon/pull/19589)) -- Add `DB_POOL` environment variable support for streaming server ([Gargron](https://github.com/mastodon/mastodon/pull/23470)) -- Add `MIN_THREADS` environment variable to set minimum Puma threads ([jimeh](https://github.com/mastodon/mastodon/pull/21048)) -- Add explanation text to log-in page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20946)) -- Add user profile OpenGraph tag on post pages ([bramus](https://github.com/mastodon/mastodon/pull/21423)) -- Add maskable icon support for Android ([workeffortwaste](https://github.com/mastodon/mastodon/pull/20904)) -- Add Belarusian to supported languages ([Mixaill](https://github.com/mastodon/mastodon/pull/22022)) -- Add Western Frisian to supported languages ([ykzts](https://github.com/mastodon/mastodon/pull/18602)) -- Add Montenegrin to the language picker ([ayefries](https://github.com/mastodon/mastodon/pull/21013)) -- Add Southern Sami and Lule Sami to the language picker ([Jullan-M](https://github.com/mastodon/mastodon/pull/21262)) -- Add logging for Rails cache timeouts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21667)) -- Add color highlight for active hashtag “follow” button ([MFTabriz](https://github.com/mastodon/mastodon/pull/21629)) -- Add brotli compression to `assets:precompile` ([Izorkin](https://github.com/mastodon/mastodon/pull/19025)) -- Add “disabled” account filter to the `/admin/accounts` UI ([tribela](https://github.com/mastodon/mastodon/pull/21282)) -- Add transparency to modal background for accessibility ([edent](https://github.com/mastodon/mastodon/pull/18081)) -- Add `lang` attribute to image description textarea and poll option field ([c960657](https://github.com/mastodon/mastodon/pull/23293)) -- Add `spellcheck` attribute to Content Warning and poll option input fields ([c960657](https://github.com/mastodon/mastodon/pull/23395)) -- Add `title` attribute to video elements in media attachments ([bramus](https://github.com/mastodon/mastodon/pull/21420)) -- Add left and right margins to emojis ([dsblank](https://github.com/mastodon/mastodon/pull/20464)) -- Add `roles` attribute to `Account` entities in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23255), [tribela](https://github.com/mastodon/mastodon/pull/23428)) -- Add `reading:autoplay:gifs` to `/api/v1/preferences` ([j-f1](https://github.com/mastodon/mastodon/pull/22706)) -- Add `hide_collections` parameter to `/api/v1/accounts/credentials` ([CarlSchwan](https://github.com/mastodon/mastodon/pull/22790)) -- Add `policy` attribute to web push subscription objects in REST API at `/api/v1/push/subscriptions` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23210)) -- Add metrics endpoint to streaming API ([Gargron](https://github.com/mastodon/mastodon/pull/23388), [Gargron](https://github.com/mastodon/mastodon/pull/23469)) -- Add more specific error messages to HTTP signature verification ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21617)) -- Add Storj DCS to cloud object storage options in the `mastodon:setup` rake task ([jtolio](https://github.com/mastodon/mastodon/pull/21929)) -- Add checkmark symbol in the checkbox for sensitive media ([sidp](https://github.com/mastodon/mastodon/pull/22795)) -- Add missing accessibility attributes to logout link in modals ([kytta](https://github.com/mastodon/mastodon/pull/22549)) -- Add missing accessibility attributes to “Hide image” button in `MediaGallery` ([hs4man21](https://github.com/mastodon/mastodon/pull/22513)) -- Add missing accessibility attributes to hide content warning field when disabled ([hs4man21](https://github.com/mastodon/mastodon/pull/22568)) -- Add `aria-hidden` to footer circle dividers to improve accessibility ([hs4man21](https://github.com/mastodon/mastodon/pull/22576)) -- Add `lang` attribute to compose form inputs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23240)) - -### Changed - -- **Ensure exact match is the first result in hashtag searches** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21315)) -- Change account search to return followed accounts first ([dariusk](https://github.com/mastodon/mastodon/pull/22956)) -- Change batch account suspension to create a strike ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20897)) -- Change default reply language to match the default language when replying to a translated post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22272)) -- Change misleading wording about waitlists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20850)) -- Increase width of the unread notification border ([connorshea](https://github.com/mastodon/mastodon/pull/21692)) -- Change new post notification button on profiles to make it more apparent when it is enabled ([tribela](https://github.com/mastodon/mastodon/pull/22541)) -- Change trending tags admin interface to always show batch action controls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23013)) -- Change wording of some OAuth scope descriptions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22491)) -- Change wording of admin report handling actions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18388)) -- Change confirm prompts for relationships management ([tribela](https://github.com/mastodon/mastodon/pull/19411)) -- Change language surrounding disability in prompts for media descriptions ([hs4man21](https://github.com/mastodon/mastodon/pull/20923)) -- Change confusing wording in the sign in banner ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22490)) -- Change `POST /settings/applications/:id` to regenerate token on scopes change ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23359)) -- Change account moderation notes to make links clickable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22553)) -- Change link previews for statuses to never use avatar as fallback ([Gargron](https://github.com/mastodon/mastodon/pull/23376)) -- Change email address input to be read-only for logged-in users when requesting a new confirmation e-mail ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23247)) -- Change notifications per page from 15 to 40 in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/23348)) -- Change number of stored items in home feed from 400 to 800 ([Gargron](https://github.com/mastodon/mastodon/pull/23349)) -- Change API rate limits from 300/5min per user to 1500/5min per user, 300/5min per app ([Gargron](https://github.com/mastodon/mastodon/pull/23347)) -- Save avatar or header correctly even if the other one fails ([tribela](https://github.com/mastodon/mastodon/pull/18465)) -- Change `referrer-policy` to `same-origin` application-wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23014), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23037)) -- Add 'private' to `Cache-Control`, match Rails expectations ([daxtens](https://github.com/mastodon/mastodon/pull/20608)) -- Make the button that expands the compose form differentiable from the button that publishes a post ([Tak](https://github.com/mastodon/mastodon/pull/20864)) -- Change automatic post deletion configuration to be accessible to moved users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20774)) -- Make tag following idempotent ([trwnh](https://github.com/mastodon/mastodon/pull/20860), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21285)) -- Use buildx functions for faster builds ([inductor](https://github.com/mastodon/mastodon/pull/20692)) -- Split off Dockerfile components for faster builds ([moritzheiber](https://github.com/mastodon/mastodon/pull/20933), [ineffyble](https://github.com/mastodon/mastodon/pull/20948), [BtbN](https://github.com/mastodon/mastodon/pull/21028)) -- Change last occurrence of “silence” to “limit” in UI text ([cincodenada](https://github.com/mastodon/mastodon/pull/20637)) -- Change “hide toot” to “hide post” ([seanthegeek](https://github.com/mastodon/mastodon/pull/22385)) -- Don't allow URLs that contain non-normalized paths to be verified ([dgl](https://github.com/mastodon/mastodon/pull/20999)) -- Change the “Trending now” header to be a link to the Explore page ([connorshea](https://github.com/mastodon/mastodon/pull/21759)) -- Change PostgreSQL connection timeout from 2 minutes to 15 seconds ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21790)) -- Make handle more easily selectable on profile page ([cadars](https://github.com/mastodon/mastodon/pull/21479)) -- Allow admins to refresh remotely-suspended accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22327)) -- Change dropdown menu to contain “Copy link to post” even for non-public posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21316)) -- Allow adding relays in secure mode and limited federation mode ([ineffyble](https://github.com/mastodon/mastodon/pull/22324)) -- Change timestamps to be displayed using the user's timezone throughout the moderation interface ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/21878), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22555)) -- Change CSP directives on API to be tight and concise ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20960)) -- Change web UI to not autofocus the compose form ([raboof](https://github.com/mastodon/mastodon/pull/16517), [Akkiesoft](https://github.com/mastodon/mastodon/pull/23094)) -- Change idempotency key handling for posting when database access is slow ([lambda](https://github.com/mastodon/mastodon/pull/21840)) -- Change remote media files to be downloaded outside of transactions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21796)) -- Improve contrast of charts in “poll has ended” notifications ([j-f1](https://github.com/mastodon/mastodon/pull/22575)) -- Change OEmbed detection and validation to be somewhat more lenient ([ineffyble](https://github.com/mastodon/mastodon/pull/22533)) -- Widen ElasticSearch version detection to not display a warning for OpenSearch ([VyrCossont](https://github.com/mastodon/mastodon/pull/22422), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23064)) -- Change link verification to allow pages larger than 1MB as long as the link is in the first 1MB ([untitaker](https://github.com/mastodon/mastodon/pull/22879)) -- Update default Node.js version to Node.js 16 ([ineffyble](https://github.com/mastodon/mastodon/pull/22223), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22342)) - -### Removed - -- Officially remove support for Ruby 2.6 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21477)) -- Remove `object-fit` polyfill used for old versions of Microsoft Edge ([shuuji3](https://github.com/mastodon/mastodon/pull/22693)) -- Remove `intersection-observer` polyfill for old Safari support ([shuuji3](https://github.com/mastodon/mastodon/pull/23284)) -- Remove empty `title` tag from mailer layout ([nametoolong](https://github.com/mastodon/mastodon/pull/23078)) -- Remove post count and last posts from ActivityPub representation of hashtag collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23460)) - -### Fixed - -- **Fix changing domain block severity not undoing individual account effects** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22135)) -- Fix suspension worker crashing on S3-compatible setups without ACL support ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22487)) -- Fix possible race conditions when suspending/unsuspending accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22363)) -- Fix being stuck in edit mode when deleting the edited posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22126)) -- Fix attached media uploads not being cleared when replying to a post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23504)) -- Fix filters not being applied to some notification types ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23211)) -- Fix incorrect link in push notifications for some event types ([elizabeth-dev](https://github.com/mastodon/mastodon/pull/23286)) -- Fix some performance issues with `/admin/instances` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21907)) -- Fix some pre-4.0 admin audit logs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22091)) -- Fix moderation audit log items for warnings having incorrect links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23242)) -- Fix account activation being sometimes triggered before email confirmation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23245)) -- Fix missing OAuth scopes for admin APIs ([trwnh](https://github.com/mastodon/mastodon/pull/20918), [trwnh](https://github.com/mastodon/mastodon/pull/20979)) -- Fix voter count not being cleared when a poll is reset ([afontenot](https://github.com/mastodon/mastodon/pull/21700)) -- Fix attachments of edited posts not being fetched ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21565)) -- Fix irreversible and whole_word parameters handling in `/api/v1/filters` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21988)) -- Fix 500 error when marking posts as sensitive while some of them are deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22134)) -- Fix expanded posts not always being scrolled into view ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21797)) -- Fix not being able to scroll the remote interaction modal on small screens ([xendke](https://github.com/mastodon/mastodon/pull/21763)) -- Fix not being able to scroll in post history modal ([cadars](https://github.com/mastodon/mastodon/pull/23396)) -- Fix audio player volume control on Safari ([minacle](https://github.com/mastodon/mastodon/pull/23187)) -- Fix disappearing “Explore” tabs on Safari ([nyura](https://github.com/mastodon/mastodon/pull/20917), [ykzts](https://github.com/mastodon/mastodon/pull/20982)) -- Fix wrong padding in RTL layout ([Gargron](https://github.com/mastodon/mastodon/pull/23157)) -- Fix drag & drop upload area display in single-column mode ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23217)) -- Fix being unable to get a single EmailDomainBlock from the admin API ([trwnh](https://github.com/mastodon/mastodon/pull/20846)) -- Fix admin-set follow recommandations being case-sensitive ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23500)) -- Fix unserialized `role` on account entities in admin API ([Gargron](https://github.com/mastodon/mastodon/pull/23290)) -- Fix pagination of followed tags ([trwnh](https://github.com/mastodon/mastodon/pull/20861)) -- Fix dropdown menu positions when scrolling ([sidp](https://github.com/mastodon/mastodon/pull/22916), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23062)) -- Fix email with empty domain name labels passing validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23246)) -- Fix mysterious registration failure when “Require a reason to join” is set with open registrations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22127)) -- Fix attachment rendering of edited posts in OpenGraph ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22270)) -- Fix invalid/empty RSS feed link on account pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20772)) -- Fix error in `VerifyLinkService` when processing links with no href ([joshuap](https://github.com/mastodon/mastodon/pull/20741)) -- Fix error in `VerifyLinkService` when processing links with invalid URLs ([untitaker](https://github.com/mastodon/mastodon/pull/23204)) -- Fix media uploads with FFmpeg 5 ([dead10ck](https://github.com/mastodon/mastodon/pull/21191)) -- Fix sensitive flag not being set when replying to a post with a content warning under certain conditions ([kedamaDQ](https://github.com/mastodon/mastodon/pull/21724)) -- Fix misleading message briefly showing up when loading follow requests under some conditions ([c960657](https://github.com/mastodon/mastodon/pull/23386)) -- Fix “Share @:user's profile” profile menu item not working ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21490)) -- Fix crash and incorrect behavior in `tootctl domains crawl` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19004)) -- Fix autoplay on iOS ([jamesadney](https://github.com/mastodon/mastodon/pull/21422)) -- Fix user clean-up scheduler crash when an unconfirmed account has a moderation note ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23318)) -- Fix spaces not being stripped in admin account search ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21324)) -- Fix spaces not being stripped when adding relays ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22655)) -- Fix infinite loading spinner instead of soft 404 for non-existing remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21303)) -- Fix minor visual issue with the top border of verified account fields ([j-f1](https://github.com/mastodon/mastodon/pull/22006)) -- Fix pending account approval and rejection not being recorded in the admin audit log ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/22088)) -- Fix “Sign up” button with closed registrations not opening modal on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22060)) -- Fix UI header overflowing on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21783)) -- Fix 500 error when trying to migrate to an invalid address ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21462)) -- Fix crash when trying to fetch unobtainable avatar of user using external authentication ([lochiiconnectivity](https://github.com/mastodon/mastodon/pull/22462)) -- Fix processing error on incoming malformed JSON-LD under some situations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23416)) -- Fix potential duplicate posts in Explore tab ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22121)) -- Fix deprecation warning in `tootctl accounts rotate` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22120)) -- Fix styling of featured tags in light theme ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23252)) -- Fix missing style in warning and strike cards ([AtelierSnek](https://github.com/mastodon/mastodon/pull/22177), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22302)) -- Fix wasteful request to `/api/v1/custom_emojis` when not logged in ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22326)) -- Fix replies sometimes being delivered to user-blocked domains ([tribela](https://github.com/mastodon/mastodon/pull/22117)) -- Fix admin dashboard crash when using some ElasticSearch replacements ([cortices](https://github.com/mastodon/mastodon/pull/21006)) -- Fix profile avatar being slightly offset into left border ([RiedleroD](https://github.com/mastodon/mastodon/pull/20994)) -- Fix N+1 queries in `NotificationsController` ([nametoolong](https://github.com/mastodon/mastodon/pull/21202)) -- Fix being unable to react to announcements with the keycap number sign emoji ([kescherCode](https://github.com/mastodon/mastodon/pull/22231)) -- Fix height computation of post embeds ([hodgesmr](https://github.com/mastodon/mastodon/pull/22141)) -- Fix accessibility issue of the search bar due to hidden placeholder ([alexstine](https://github.com/mastodon/mastodon/pull/21275)) -- Fix layout change handler not being removed due to a typo ([nschonni](https://github.com/mastodon/mastodon/pull/21829)) -- Fix typo in the default `S3_HOSTNAME` used in the `mastodon:setup` rake task ([danp](https://github.com/mastodon/mastodon/pull/19932)) -- Fix the top action bar appearing in the multi-column layout ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20943)) -- Fix inability to use local LibreTranslate without setting `ALLOWED_PRIVATE_ADDRESSES` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21926)) -- Fix punycoded local domains not being prettified in initial state ([Tritlo](https://github.com/mastodon/mastodon/pull/21440)) -- Fix CSP violation warning by removing inline CSS from SVG logo ([luxiaba](https://github.com/mastodon/mastodon/pull/20814)) -- Fix margin for search field on medium window size ([minacle](https://github.com/mastodon/mastodon/pull/21606)) -- Fix search popout scrolling with the page in single-column mode ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/16463)) -- Fix minor post cache hydration discrepancy ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19879)) -- Fix `・` detection in hashtags ([parthoghosh24](https://github.com/mastodon/mastodon/pull/22888)) -- Fix hashtag follows bypassing user blocks ([tribela](https://github.com/mastodon/mastodon/pull/22849)) -- Fix moved accounts being incorrectly redirected to account settings when trying to view a remote profile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22497)) -- Fix site upload validations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22479)) -- Fix “Add new domain block” button using last submitted search value instead of the current one ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22485)) -- Fix misleading hashtag warning when posting with “Followers only” or “Mentioned people only” visibility ([n0toose](https://github.com/mastodon/mastodon/pull/22827)) -- Fix embedded posts with videos grabbing focus ([Akkiesoft](https://github.com/mastodon/mastodon/pull/22778)) -- Fix `$` not being escaped in `.env.production` files generated by the `mastodon:setup` rake task ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23012), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23072)) -- Fix sanitizer parsing link text as HTML when stripping unsupported links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22558)) -- Fix `scheduled_at` input not using `datetime-local` when editing announcements ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21896)) -- Fix REST API serializer for `Account` not including `moved` when the moved account has itself moved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22483)) -- Fix `/api/v1/admin/trends/tags` using wrong serializer ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18943)) -- Fix situations in which instance actor can be set to a Mastodon-incompatible name ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22307)) - -### Security - -- Add `form-action` CSP directive ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20781), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20958), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20962)) -- Fix unbounded recursion in account discovery ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22025)) -- Revoke all authorized applications on password reset ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/21325)) -- Fix unbounded recursion in post discovery ([ClearlyClaire,nametoolong](https://github.com/mastodon/mastodon/pull/23506)) - -## [4.0.2] - 2022-11-15 - -### Fixed - -- Fix wrong color on mentions hidden behind content warning in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/20724)) -- Fix filters from other users being used in the streaming service ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20719)) -- Fix `unsafe-eval` being used when `wasm-unsafe-eval` is enough in Content Security Policy ([Gargron](https://github.com/mastodon/mastodon/pull/20729), [prplecake](https://github.com/mastodon/mastodon/pull/20606)) - -## [4.0.1] - 2022-11-14 - -### Fixed - -- Fix nodes order being sometimes mangled when rewriting emoji ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20677)) - -## [4.0.0] - 2022-11-14 - -Some of the features in this release have been funded through the [NGI0 Discovery](https://nlnet.nl/discovery) Fund, a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 825322. - -### Added - -- Add ability to filter followed accounts' posts by language ([Gargron](https://github.com/mastodon/mastodon/pull/19095), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19268)) -- **Add ability to follow hashtags** ([Gargron](https://github.com/mastodon/mastodon/pull/18809), [Gargron](https://github.com/mastodon/mastodon/pull/18862), [Gargron](https://github.com/mastodon/mastodon/pull/19472), [noellabo](https://github.com/mastodon/mastodon/pull/18924)) -- Add ability to filter individual posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18945)) -- **Add ability to translate posts** ([Gargron](https://github.com/mastodon/mastodon/pull/19218), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19433), [Gargron](https://github.com/mastodon/mastodon/pull/19453), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19434), [Gargron](https://github.com/mastodon/mastodon/pull/19388), [ykzts](https://github.com/mastodon/mastodon/pull/19244), [Gargron](https://github.com/mastodon/mastodon/pull/19245)) -- Add featured tags to web UI ([noellabo](https://github.com/mastodon/mastodon/pull/19408), [noellabo](https://github.com/mastodon/mastodon/pull/19380), [noellabo](https://github.com/mastodon/mastodon/pull/19358), [noellabo](https://github.com/mastodon/mastodon/pull/19409), [Gargron](https://github.com/mastodon/mastodon/pull/19382), [ykzts](https://github.com/mastodon/mastodon/pull/19418), [noellabo](https://github.com/mastodon/mastodon/pull/19403), [noellabo](https://github.com/mastodon/mastodon/pull/19404), [Gargron](https://github.com/mastodon/mastodon/pull/19398), [Gargron](https://github.com/mastodon/mastodon/pull/19712), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20018)) -- **Add support for language preferences for trending statuses and links** ([Gargron](https://github.com/mastodon/mastodon/pull/18288), [Gargron](https://github.com/mastodon/mastodon/pull/19349), [ykzts](https://github.com/mastodon/mastodon/pull/19335)) - - Previously, you could only see trends in your current language - - For less popular languages, that meant empty trends - - Now, trends in your preferred languages' are shown on top, with others beneath -- Add server rules to sign-up flow ([Gargron](https://github.com/mastodon/mastodon/pull/19296)) -- Add privacy icons to report modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19190)) -- Add `noopener` to links to remote profiles in web UI ([shleeable](https://github.com/mastodon/mastodon/pull/19014)) -- Add option to open original page in dropdowns of remote content in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/20299)) -- Add warning for sensitive audio posts in web UI ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/17885)) -- Add language attribute to posts in web UI ([tribela](https://github.com/mastodon/mastodon/pull/18544)) -- Add support for uploading WebP files ([Saiv46](https://github.com/mastodon/mastodon/pull/18506)) -- Add support for uploading `audio/vnd.wave` files ([tribela](https://github.com/mastodon/mastodon/pull/18737)) -- Add support for uploading AVIF files ([txt-file](https://github.com/mastodon/mastodon/pull/19647)) -- Add support for uploading HEIC files ([Gargron](https://github.com/mastodon/mastodon/pull/19618)) -- Add more debug information when processing remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15605), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19209)) -- **Add retention policy for cached content and media** ([Gargron](https://github.com/mastodon/mastodon/pull/19232), [zunda](https://github.com/mastodon/mastodon/pull/19478), [Gargron](https://github.com/mastodon/mastodon/pull/19458), [Gargron](https://github.com/mastodon/mastodon/pull/19248)) - - Set for how long remote posts or media should be cached on your server - - Hands-off alternative to `tootctl` commands -- **Add customizable user roles** ([Gargron](https://github.com/mastodon/mastodon/pull/18641), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18812), [Gargron](https://github.com/mastodon/mastodon/pull/19040), [tribela](https://github.com/mastodon/mastodon/pull/18825), [tribela](https://github.com/mastodon/mastodon/pull/18826), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18776), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18777), [unextro](https://github.com/mastodon/mastodon/pull/18786), [tribela](https://github.com/mastodon/mastodon/pull/18824), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19436)) - - Previously, there were 3 hard-coded roles, user, moderator, and admin - - Create your own roles and decide which permissions they should have -- Add notifications for new reports ([Gargron](https://github.com/mastodon/mastodon/pull/18697), [Gargron](https://github.com/mastodon/mastodon/pull/19475)) -- Add ability to select all accounts matching search for batch actions in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/19053), [Gargron](https://github.com/mastodon/mastodon/pull/19054)) -- Add ability to view previous edits of a status in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/19462)) -- Add ability to block sign-ups from IP ([Gargron](https://github.com/mastodon/mastodon/pull/19037)) -- **Add webhooks to admin UI** ([Gargron](https://github.com/mastodon/mastodon/pull/18510)) -- Add admin API for managing domain allows ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18668)) -- Add admin API for managing domain blocks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18247)) -- Add admin API for managing e-mail domain blocks ([Gargron](https://github.com/mastodon/mastodon/pull/19066)) -- Add admin API for managing canonical e-mail blocks ([Gargron](https://github.com/mastodon/mastodon/pull/19067)) -- Add admin API for managing IP blocks ([Gargron](https://github.com/mastodon/mastodon/pull/19065), [trwnh](https://github.com/mastodon/mastodon/pull/20207)) -- Add `sensitized` attribute to accounts in admin REST API ([trwnh](https://github.com/mastodon/mastodon/pull/20094)) -- Add `services` and `metadata` to the NodeInfo endpoint ([MFTabriz](https://github.com/mastodon/mastodon/pull/18563)) -- Add `--remove-role` option to `tootctl accounts modify` ([Gargron](https://github.com/mastodon/mastodon/pull/19477)) -- Add `--days` option to `tootctl media refresh` ([tribela](https://github.com/mastodon/mastodon/pull/18425)) -- Add `EMAIL_DOMAIN_LISTS_APPLY_AFTER_CONFIRMATION` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18642)) -- Add `IP_RETENTION_PERIOD` and `SESSION_RETENTION_PERIOD` environment variables ([kescherCode](https://github.com/mastodon/mastodon/pull/18757)) -- Add `http_hidden_proxy` environment variable ([tribela](https://github.com/mastodon/mastodon/pull/18427)) -- Add `ENABLE_STARTTLS` environment variable ([erbridge](https://github.com/mastodon/mastodon/pull/20321)) -- Add caching for payload serialization during fan-out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19637), [Gargron](https://github.com/mastodon/mastodon/pull/19642), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19746), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19747), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19963)) -- Add assets from Twemoji 14.0 ([Gargron](https://github.com/mastodon/mastodon/pull/19733)) -- Add reputation and followers score boost to SQL-only account search ([Gargron](https://github.com/mastodon/mastodon/pull/19251)) -- Add Scots, Balaibalan, Láadan, Lingua Franca Nova, Lojban, Toki Pona to languages list ([VyrCossont](https://github.com/mastodon/mastodon/pull/20168)) -- Set autocomplete hints for e-mail, password and OTP fields ([rcombs](https://github.com/mastodon/mastodon/pull/19833), [offbyone](https://github.com/mastodon/mastodon/pull/19946), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20071)) -- Add support for DigitalOcean Spaces in setup wizard ([v-aisac](https://github.com/mastodon/mastodon/pull/20573)) - -### Changed - -- **Change brand color and logotypes** ([Gargron](https://github.com/mastodon/mastodon/pull/18592), [Gargron](https://github.com/mastodon/mastodon/pull/18639), [Gargron](https://github.com/mastodon/mastodon/pull/18691), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18634), [Gargron](https://github.com/mastodon/mastodon/pull/19254), [mayaeh](https://github.com/mastodon/mastodon/pull/18710)) -- **Change post editing to be enabled in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/19103)) -- **Change web UI to work for logged-out users** ([Gargron](https://github.com/mastodon/mastodon/pull/18961), [Gargron](https://github.com/mastodon/mastodon/pull/19250), [Gargron](https://github.com/mastodon/mastodon/pull/19294), [Gargron](https://github.com/mastodon/mastodon/pull/19306), [Gargron](https://github.com/mastodon/mastodon/pull/19315), [ykzts](https://github.com/mastodon/mastodon/pull/19322), [Gargron](https://github.com/mastodon/mastodon/pull/19412), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19437), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19415), [Gargron](https://github.com/mastodon/mastodon/pull/19348), [Gargron](https://github.com/mastodon/mastodon/pull/19295), [Gargron](https://github.com/mastodon/mastodon/pull/19422), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19414), [Gargron](https://github.com/mastodon/mastodon/pull/19319), [Gargron](https://github.com/mastodon/mastodon/pull/19345), [Gargron](https://github.com/mastodon/mastodon/pull/19310), [Gargron](https://github.com/mastodon/mastodon/pull/19301), [Gargron](https://github.com/mastodon/mastodon/pull/19423), [ykzts](https://github.com/mastodon/mastodon/pull/19471), [ykzts](https://github.com/mastodon/mastodon/pull/19333), [ykzts](https://github.com/mastodon/mastodon/pull/19337), [ykzts](https://github.com/mastodon/mastodon/pull/19272), [ykzts](https://github.com/mastodon/mastodon/pull/19468), [Gargron](https://github.com/mastodon/mastodon/pull/19466), [Gargron](https://github.com/mastodon/mastodon/pull/19457), [Gargron](https://github.com/mastodon/mastodon/pull/19426), [Gargron](https://github.com/mastodon/mastodon/pull/19427), [Gargron](https://github.com/mastodon/mastodon/pull/19421), [Gargron](https://github.com/mastodon/mastodon/pull/19417), [Gargron](https://github.com/mastodon/mastodon/pull/19413), [Gargron](https://github.com/mastodon/mastodon/pull/19397), [Gargron](https://github.com/mastodon/mastodon/pull/19387), [Gargron](https://github.com/mastodon/mastodon/pull/19396), [Gargron](https://github.com/mastodon/mastodon/pull/19385), [ykzts](https://github.com/mastodon/mastodon/pull/19334), [ykzts](https://github.com/mastodon/mastodon/pull/19329), [Gargron](https://github.com/mastodon/mastodon/pull/19324), [Gargron](https://github.com/mastodon/mastodon/pull/19318), [Gargron](https://github.com/mastodon/mastodon/pull/19316), [Gargron](https://github.com/mastodon/mastodon/pull/19263), [trwnh](https://github.com/mastodon/mastodon/pull/19305), [ykzts](https://github.com/mastodon/mastodon/pull/19273), [Gargron](https://github.com/mastodon/mastodon/pull/19801), [Gargron](https://github.com/mastodon/mastodon/pull/19790), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19773), [Gargron](https://github.com/mastodon/mastodon/pull/19798), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19724), [Gargron](https://github.com/mastodon/mastodon/pull/19709), [Gargron](https://github.com/mastodon/mastodon/pull/19514), [Gargron](https://github.com/mastodon/mastodon/pull/19562), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19981), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19978), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20148), [Gargron](https://github.com/mastodon/mastodon/pull/20302), [cutls](https://github.com/mastodon/mastodon/pull/20400)) - - The web app can now be accessed without being logged in - - No more `/web` prefix on web app paths - - Profiles, posts, and other public pages now use the same interface for logged in and logged out users - - The web app displays a server information banner - - Pop-up windows for remote interaction have been replaced with a modal window - - No need to type in your username for remote interaction, copy-paste-to-search method explained - - Various hints throughout the app explain what the different timelines are - - New about page design - - New privacy policy page design shows when the policy was last updated - - All sections of the web app now have appropriate window titles - - The layout of the interface has been streamlined between different screen sizes - - Posts now use more horizontal space -- Change label of publish button to be "Publish" again in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/18583)) -- Change language to be carried over on reply in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18557)) -- Change "Unfollow" to "Cancel follow request" when request still pending in web UI ([prplecake](https://github.com/mastodon/mastodon/pull/19363)) -- **Change post filtering system** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18058), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19050), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18894), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19051), [noellabo](https://github.com/mastodon/mastodon/pull/18923), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18956), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18744), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/19878), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20567)) - - Filtered keywords and phrases can now be grouped into named categories - - Filtered posts show which exact filter was hit - - Individual posts can be added to a filter - - You can peek inside filtered posts anyway -- Change path of privacy policy page from `/terms` to `/privacy-policy` ([Gargron](https://github.com/mastodon/mastodon/pull/19249)) -- Change how hashtags are normalized ([Gargron](https://github.com/mastodon/mastodon/pull/18795), [Gargron](https://github.com/mastodon/mastodon/pull/18863), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18854)) -- Change settings area to be separated into categories in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/19407), [Gargron](https://github.com/mastodon/mastodon/pull/19533)) -- Change "No accounts selected" errors to use the appropriate noun in admin UI ([prplecake](https://github.com/mastodon/mastodon/pull/19356)) -- Change e-mail domain blocks to match subdomains of blocked domains ([Gargron](https://github.com/mastodon/mastodon/pull/18979)) -- Change custom emoji file size limit from 50 KB to 256 KB ([Gargron](https://github.com/mastodon/mastodon/pull/18788)) -- Change "Allow trends without prior review" setting to also work for trending posts ([Gargron](https://github.com/mastodon/mastodon/pull/17977)) -- Change admin announcements form to use single inputs for date and time in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18321)) -- Change search API to be accessible without being logged in ([Gargron](https://github.com/mastodon/mastodon/pull/18963), [Gargron](https://github.com/mastodon/mastodon/pull/19326)) -- Change following and followers API to be accessible without being logged in ([Gargron](https://github.com/mastodon/mastodon/pull/18964)) -- Change `AUTHORIZED_FETCH` to not block unauthenticated REST API access ([Gargron](https://github.com/mastodon/mastodon/pull/19803)) -- Change Helm configuration ([deepy](https://github.com/mastodon/mastodon/pull/18997), [jgsmith](https://github.com/mastodon/mastodon/pull/18415), [deepy](https://github.com/mastodon/mastodon/pull/18941)) -- Change mentions of blocked users to not be processed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19725)) -- Change max. thumbnail dimensions to 640x360px (360p) ([Gargron](https://github.com/mastodon/mastodon/pull/19619)) -- Change post-processing to be deferred only for large media types ([Gargron](https://github.com/mastodon/mastodon/pull/19617)) -- Change link verification to only work for https links without unicode ([Gargron](https://github.com/mastodon/mastodon/pull/20304), [Gargron](https://github.com/mastodon/mastodon/pull/20295)) -- Change account deletion requests to spread out over time ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20222)) -- Change larger reblogs/favourites numbers to be shortened in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/20303)) -- Change incoming activity processing to happen in `ingress` queue ([Gargron](https://github.com/mastodon/mastodon/pull/20264)) -- Change notifications to not link show preview cards in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20335)) -- Change amount of replies returned for logged out users in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20355)) -- Change in-app links to keep you in-app in web UI ([trwnh](https://github.com/mastodon/mastodon/pull/20540), [Gargron](https://github.com/mastodon/mastodon/pull/20628)) -- Change table header to be sticky in admin UI ([sk22](https://github.com/mastodon/mastodon/pull/20442)) - -### Removed - -- Remove setting that disables account deletes ([Gargron](https://github.com/mastodon/mastodon/pull/17683)) -- Remove digest e-mails ([Gargron](https://github.com/mastodon/mastodon/pull/17985)) -- Remove unnecessary sections from welcome e-mail ([Gargron](https://github.com/mastodon/mastodon/pull/19299)) -- Remove item titles from RSS feeds ([Gargron](https://github.com/mastodon/mastodon/pull/18640)) -- Remove volume number from hashtags in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/19253)) -- Remove Nanobox configuration ([tonyjiang](https://github.com/mastodon/mastodon/pull/17881)) - -### Fixed - -- Fix rules with same priority being sorted non-deterministically ([Gargron](https://github.com/mastodon/mastodon/pull/20623)) -- Fix error when invalid domain name is submitted ([Gargron](https://github.com/mastodon/mastodon/pull/19474)) -- Fix icons having an image role ([Gargron](https://github.com/mastodon/mastodon/pull/20600)) -- Fix connections to IPv6-only servers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20108)) -- Fix unnecessary service worker registration and preloading when logged out in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20341)) -- Fix unnecessary and slow regex construction ([raggi](https://github.com/mastodon/mastodon/pull/20215)) -- Fix `mailers` queue not being used for mailers ([Gargron](https://github.com/mastodon/mastodon/pull/20274)) -- Fix error in webfinger redirect handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20260)) -- Fix report category not being set to `violation` if rule IDs are provided ([trwnh](https://github.com/mastodon/mastodon/pull/20137)) -- Fix nodeinfo metadata attribute being an array instead of an object ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20114)) -- Fix account endorsements not being idempotent ([trwnh](https://github.com/mastodon/mastodon/pull/20118)) -- Fix status and rule IDs not being strings in admin reports REST API ([trwnh](https://github.com/mastodon/mastodon/pull/20122)) -- Fix error on invalid `replies_policy` in REST API ([trwnh](https://github.com/mastodon/mastodon/pull/20126)) -- Fix redrafting a currently-editing post not leaving edit mode in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20023)) -- Fix performance by avoiding method cache busts ([raggi](https://github.com/mastodon/mastodon/pull/19957)) -- Fix opening the language picker scrolling the single-column view to the top in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19983)) -- Fix content warning button missing `aria-expanded` attribute in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19975)) -- Fix redundant `aria-pressed` attributes in web UI ([Brawaru](https://github.com/mastodon/mastodon/pull/19912)) -- Fix crash when external auth provider has no display name set ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19962)) -- Fix followers count not being updated when migrating follows ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19998)) -- Fix double button to clear emoji search input in web UI ([sunny](https://github.com/mastodon/mastodon/pull/19888)) -- Fix missing null check on applications on strike disputes ([kescherCode](https://github.com/mastodon/mastodon/pull/19851)) -- Fix featured tags not saving preferred casing ([Gargron](https://github.com/mastodon/mastodon/pull/19732)) -- Fix language not being saved when editing status ([Gargron](https://github.com/mastodon/mastodon/pull/19543)) -- Fix not being able to input featured tag with hash symbol ([Gargron](https://github.com/mastodon/mastodon/pull/19535)) -- Fix user clean-up scheduler crash when an unconfirmed account has a moderation note ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19629)) -- Fix being unable to withdraw follow request when confirmation modal is disabled in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19687)) -- Fix inaccurate admin log entry for re-sending confirmation e-mails ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19674)) -- Fix edits not being immediately reflected ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19673)) -- Fix bookmark import stopping at the first failure ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19669)) -- Fix account action type validation ([Gargron](https://github.com/mastodon/mastodon/pull/19476)) -- Fix upload progress not communicating processing phase in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/19530)) -- Fix wrong host being used for custom.css when asset host configured ([Gargron](https://github.com/mastodon/mastodon/pull/19521)) -- Fix account migration form ever using outdated account data ([Gargron](https://github.com/mastodon/mastodon/pull/18429), [nightpool](https://github.com/mastodon/mastodon/pull/19883)) -- Fix error when uploading malformed CSV import ([Gargron](https://github.com/mastodon/mastodon/pull/19509)) -- Fix avatars not using image tags in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/19488)) -- Fix handling of duplicate and out-of-order notifications in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19693)) -- Fix reblogs being discarded after the reblogged status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19731)) -- Fix indexing scheduler trying to index when Elasticsearch is disabled ([Gargron](https://github.com/mastodon/mastodon/pull/19805)) -- Fix n+1 queries when rendering initial state JSON ([Gargron](https://github.com/mastodon/mastodon/pull/19795)) -- Fix n+1 query during status removal ([Gargron](https://github.com/mastodon/mastodon/pull/19753)) -- Fix OCR not working due to Content Security Policy in web UI ([prplecake](https://github.com/mastodon/mastodon/pull/18817)) -- Fix `nofollow` rel being removed in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/19455)) -- Fix language dropdown causing zoom on mobile devices in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/19428)) -- Fix button to dismiss suggestions not showing up in search results in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19325)) -- Fix language dropdown sometimes not appearing in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/19246)) -- Fix quickly switching notification filters resulting in empty or incorrect list in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19052), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/18960)) -- Fix media modal link button in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18877)) -- Fix error upon successful account migration ([Gargron](https://github.com/mastodon/mastodon/pull/19386)) -- Fix negatives values in search index causing queries to fail ([Gargron](https://github.com/mastodon/mastodon/pull/19464), [Gargron](https://github.com/mastodon/mastodon/pull/19481)) -- Fix error when searching for invalid URL ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18580)) -- Fix IP blocks not having a unique index ([Gargron](https://github.com/mastodon/mastodon/pull/19456)) -- Fix remote account in contact account setting not being used ([Gargron](https://github.com/mastodon/mastodon/pull/19351)) -- Fix swallowing mentions of unconfirmed/unapproved users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19191)) -- Fix incorrect and slow cache invalidation when blocking domain and removing media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19062)) -- Fix HTTPs redirect behaviour when running as I2P service ([gi-yt](https://github.com/mastodon/mastodon/pull/18929)) -- Fix deleted pinned posts potentially counting towards the pinned posts limit ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19005)) -- Fix compatibility with OpenSSL 3.0 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18449)) -- Fix error when a remote report includes a private post the server has no access to ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18760)) -- Fix suspicious sign-in mails never being sent ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18599)) -- Fix fallback locale when somehow user's locale is an empty string ([tribela](https://github.com/mastodon/mastodon/pull/18543)) -- Fix avatar/header not being deleted locally when deleted on remote account ([tribela](https://github.com/mastodon/mastodon/pull/18973)) -- Fix missing `,` in Blurhash validation ([noellabo](https://github.com/mastodon/mastodon/pull/18660)) -- Fix order by most recent not working for relationships page in admin UI ([tribela](https://github.com/mastodon/mastodon/pull/18996)) -- Fix uncaught error when invalid date is supplied to API ([Gargron](https://github.com/mastodon/mastodon/pull/19480)) -- Fix REST API sometimes returning HTML on error ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19135)) -- Fix ambiguous column names in `tootctl media refresh` ([tribela](https://github.com/mastodon/mastodon/pull/19206)) -- Fix ambiguous column names in `tootctl search deploy` ([mashirozx](https://github.com/mastodon/mastodon/pull/18993)) -- Fix `CDN_HOST` not being used in some asset URLs ([tribela](https://github.com/mastodon/mastodon/pull/18662)) -- Fix `CAS_DISPLAY_NAME`, `SAML_DISPLAY_NAME` and `OIDC_DISPLAY_NAME` being ignored ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18568)) -- Fix various typos in comments throughout the codebase ([luzpaz](https://github.com/mastodon/mastodon/pull/18604)) -- Fix CSV import error when rows include unicode characters ([HamptonMakes](https://github.com/mastodon/mastodon/pull/20592)) - -### Security - -- Fix being able to spoof link verification ([Gargron](https://github.com/mastodon/mastodon/pull/20217)) -- Fix emoji substitution not applying only to text nodes in backend code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20641)) -- Fix emoji substitution not applying only to text nodes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20640)) -- Fix rate limiting for paths with formats ([Gargron](https://github.com/mastodon/mastodon/pull/20675)) -- Fix out-of-bound reads in blurhash transcoder ([delroth](https://github.com/mastodon/mastodon/pull/20388)) - -_For previous changes, review the [stable-3.5 branch](https://github.com/mastodon/mastodon/blob/stable-3.5/CHANGELOG.md)_ +_For previous changes, review the [stable-4.2 branch](https://github.com/mastodon/mastodon/blob/stable-4.2/CHANGELOG.md)_ From 21a8ff535111d69ad34abc6cecebf4419d624c93 Mon Sep 17 00:00:00 2001 From: Nicholas La Roux Date: Thu, 12 Mar 2026 10:04:35 -0400 Subject: [PATCH 055/203] Upgrade from Ruby 3.4.8 to 3.4.9 (#38154) --- .github/ISSUE_TEMPLATE/2.server_bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/3.troubleshooting.yml | 2 +- .github/actions/setup-ruby/action.yml | 2 +- .github/workflows/bundler-audit.yml | 2 +- .github/workflows/lint-haml.yml | 2 +- .github/workflows/lint-ruby.yml | 2 +- .ruby-version | 2 +- Dockerfile | 2 +- Gemfile.lock | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/2.server_bug_report.yml b/.github/ISSUE_TEMPLATE/2.server_bug_report.yml index 99ec9cf146fc3a..ec3a52689d47bf 100644 --- a/.github/ISSUE_TEMPLATE/2.server_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/2.server_bug_report.yml @@ -59,7 +59,7 @@ body: Any additional technical details you may have, like logs or error traces value: | If this is happening on your own Mastodon server, please fill out those: - - Ruby version: (from `ruby --version`, eg. v3.4.4) + - Ruby version: (from `ruby --version`, eg. v3.4.9) - Node.js version: (from `node --version`, eg. v22.16.0) validations: required: false diff --git a/.github/ISSUE_TEMPLATE/3.troubleshooting.yml b/.github/ISSUE_TEMPLATE/3.troubleshooting.yml index 004e74bf42eb67..5278933ecefed9 100644 --- a/.github/ISSUE_TEMPLATE/3.troubleshooting.yml +++ b/.github/ISSUE_TEMPLATE/3.troubleshooting.yml @@ -61,7 +61,7 @@ body: value: | Please at least include those informations: - Operating system: (eg. Ubuntu 24.04.2) - - Ruby version: (from `ruby --version`, eg. v3.4.4) + - Ruby version: (from `ruby --version`, eg. v3.4.9) - Node.js version: (from `node --version`, eg. v22.16.0) validations: required: false diff --git a/.github/actions/setup-ruby/action.yml b/.github/actions/setup-ruby/action.yml index 0085aa875cb7d8..fed79add6d8ba6 100644 --- a/.github/actions/setup-ruby/action.yml +++ b/.github/actions/setup-ruby/action.yml @@ -17,7 +17,7 @@ runs: sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }} - name: Set up Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1 with: ruby-version: ${{ inputs.ruby-version }} bundler-cache: true diff --git a/.github/workflows/bundler-audit.yml b/.github/workflows/bundler-audit.yml index f3a97f8a2ea77c..b1e8d406e2543e 100644 --- a/.github/workflows/bundler-audit.yml +++ b/.github/workflows/bundler-audit.yml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1 with: bundler-cache: true diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml index 46cf1c25c10626..6cc212180a8f61 100644 --- a/.github/workflows/lint-haml.yml +++ b/.github/workflows/lint-haml.yml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1 with: bundler-cache: true diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml index f99362895a5072..216618f4e56993 100644 --- a/.github/workflows/lint-ruby.yml +++ b/.github/workflows/lint-ruby.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1 with: bundler-cache: true diff --git a/.ruby-version b/.ruby-version index 7921bd0c892723..7bcbb3808b5089 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.8 +3.4.9 diff --git a/Dockerfile b/Dockerfile index c06bc84a3395dc..6a191b7e85126e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ ARG BASE_REGISTRY="docker.io" # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"] # renovate: datasource=docker depName=docker.io/ruby -ARG RUBY_VERSION="3.4.8" +ARG RUBY_VERSION="3.4.9" # # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="22"] # renovate: datasource=node-version depName=node ARG NODE_MAJOR_VERSION="24" diff --git a/Gemfile.lock b/Gemfile.lock index 5ab80abb8c5bb0..6ecf05eff105a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1097,7 +1097,7 @@ DEPENDENCIES xorcist (~> 1.1) RUBY VERSION - ruby 3.4.8 + ruby 3.4.9 BUNDLED WITH 4.0.8 From 7511357ec6de2af992356da2af262b59428f344c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:09:17 +0100 Subject: [PATCH 056/203] Update opentelemetry-ruby (non-major) (#38138) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile | 4 ++-- Gemfile.lock | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 1ff1ebf7de56df..5cdc2aa0e58a9e 100644 --- a/Gemfile +++ b/Gemfile @@ -102,10 +102,10 @@ gem 'rdf-normalize', '~> 0.5' gem 'prometheus_exporter', '~> 2.2', require: false -gem 'opentelemetry-api', '~> 1.7.0' +gem 'opentelemetry-api', '~> 1.8.0' group :opentelemetry do - gem 'opentelemetry-exporter-otlp', '~> 0.31.0', require: false + gem 'opentelemetry-exporter-otlp', '~> 0.32.0', require: false gem 'opentelemetry-instrumentation-active_job', '~> 0.10.0', require: false gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.24.0', require: false gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.24.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 6ecf05eff105a4..1c1f0aac1b89c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -510,10 +510,11 @@ GEM openssl (4.0.1) openssl-signature_algorithm (1.3.0) openssl (> 2.0) - opentelemetry-api (1.7.0) + opentelemetry-api (1.8.0) + logger opentelemetry-common (0.23.0) opentelemetry-api (~> 1.0) - opentelemetry-exporter-otlp (0.31.1) + opentelemetry-exporter-otlp (0.32.0) google-protobuf (>= 3.18) googleapis-common-protos-types (~> 1.3) opentelemetry-api (~> 1.1) @@ -1020,8 +1021,8 @@ DEPENDENCIES omniauth-rails_csrf_protection (~> 2.0) omniauth-saml (~> 2.0) omniauth_openid_connect (~> 0.8.0) - opentelemetry-api (~> 1.7.0) - opentelemetry-exporter-otlp (~> 0.31.0) + opentelemetry-api (~> 1.8.0) + opentelemetry-exporter-otlp (~> 0.32.0) opentelemetry-instrumentation-active_job (~> 0.10.0) opentelemetry-instrumentation-active_model_serializers (~> 0.24.0) opentelemetry-instrumentation-concurrent_ruby (~> 0.24.0) From 377952703cf70f14008cc7938479558d8cca9903 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Thu, 12 Mar 2026 16:46:36 +0100 Subject: [PATCH 057/203] Add serializers for `Accept`+`Reject` of feature requests (#38177) --- .../accept_feature_request_serializer.rb | 33 +++++++++++++++++++ .../reject_feature_request_serializer.rb | 29 ++++++++++++++++ .../accept_feature_request_serializer_spec.rb | 33 +++++++++++++++++++ .../reject_feature_request_serializer_spec.rb | 32 ++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 app/serializers/activitypub/accept_feature_request_serializer.rb create mode 100644 app/serializers/activitypub/reject_feature_request_serializer.rb create mode 100644 spec/serializers/activitypub/accept_feature_request_serializer_spec.rb create mode 100644 spec/serializers/activitypub/reject_feature_request_serializer_spec.rb diff --git a/app/serializers/activitypub/accept_feature_request_serializer.rb b/app/serializers/activitypub/accept_feature_request_serializer.rb new file mode 100644 index 00000000000000..99393dac91225c --- /dev/null +++ b/app/serializers/activitypub/accept_feature_request_serializer.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class ActivityPub::AcceptFeatureRequestSerializer < ActivityPub::Serializer + include RoutingHelper + + attributes :id, :type, :actor, :to, :result + + has_one :virtual_object, key: :object + + def id + [ActivityPub::TagManager.instance.uri_for(object.account), '#accepts/feature_requests/', object.id].join + end + + def type + 'Accept' + end + + def actor + ActivityPub::TagManager.instance.uri_for(object.account) + end + + def to + ActivityPub::TagManager.instance.uri_for(object.collection.account) + end + + def virtual_object + object.activity_uri + end + + def result + ap_account_feature_authorization_url(object.account_id, object) + end +end diff --git a/app/serializers/activitypub/reject_feature_request_serializer.rb b/app/serializers/activitypub/reject_feature_request_serializer.rb new file mode 100644 index 00000000000000..4dbc2cdf11b62c --- /dev/null +++ b/app/serializers/activitypub/reject_feature_request_serializer.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class ActivityPub::RejectFeatureRequestSerializer < ActivityPub::Serializer + include RoutingHelper + + attributes :id, :type, :actor, :to + + has_one :virtual_object, key: :object + + def id + [ActivityPub::TagManager.instance.uri_for(object.account), '#rejects/feature_requests/', object.id].join + end + + def type + 'Reject' + end + + def actor + ActivityPub::TagManager.instance.uri_for(object.account) + end + + def to + ActivityPub::TagManager.instance.uri_for(object.collection.account) + end + + def virtual_object + object.activity_uri + end +end diff --git a/spec/serializers/activitypub/accept_feature_request_serializer_spec.rb b/spec/serializers/activitypub/accept_feature_request_serializer_spec.rb new file mode 100644 index 00000000000000..6fc277f2734564 --- /dev/null +++ b/spec/serializers/activitypub/accept_feature_request_serializer_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::AcceptFeatureRequestSerializer do + include RoutingHelper + + subject { serialized_record_json(record, described_class, adapter: ActivityPub::Adapter) } + + describe 'serializing an object' do + let(:collection) { Fabricate(:remote_collection) } + let(:record) do + Fabricate(:collection_item, + collection:, + uri: 'https://example.com/featured_items/1', + activity_uri: 'https://example.com/feature_requests/1', + state: :accepted) + end + let(:tag_manager) { ActivityPub::TagManager.instance } + + it 'returns expected attributes' do + expect(subject) + .to include( + 'id' => match("#accepts/feature_requests/#{record.id}"), + 'type' => 'Accept', + 'actor' => tag_manager.uri_for(record.account), + 'to' => tag_manager.uri_for(collection.account), + 'object' => 'https://example.com/feature_requests/1', + 'result' => ap_account_feature_authorization_url(record.account_id, record) + ) + end + end +end diff --git a/spec/serializers/activitypub/reject_feature_request_serializer_spec.rb b/spec/serializers/activitypub/reject_feature_request_serializer_spec.rb new file mode 100644 index 00000000000000..1e7aa0bcded9de --- /dev/null +++ b/spec/serializers/activitypub/reject_feature_request_serializer_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::RejectFeatureRequestSerializer do + include RoutingHelper + + subject { serialized_record_json(record, described_class, adapter: ActivityPub::Adapter) } + + describe 'serializing an object' do + let(:collection) { Fabricate(:remote_collection) } + let(:record) do + Fabricate(:collection_item, + collection:, + uri: 'https://example.com/featured_items/1', + activity_uri: 'https://example.com/feature_requests/1', + state: :rejected) + end + let(:tag_manager) { ActivityPub::TagManager.instance } + + it 'returns expected attributes' do + expect(subject) + .to include( + 'id' => match("#rejects/feature_requests/#{record.id}"), + 'type' => 'Reject', + 'actor' => tag_manager.uri_for(record.account), + 'to' => tag_manager.uri_for(collection.account), + 'object' => 'https://example.com/feature_requests/1' + ) + end + end +end From 6416724f75cb5b2ff76e280cb43b063c8b77b477 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 13 Mar 2026 05:33:02 -0400 Subject: [PATCH 058/203] Normalize `current_username` on account migration (#38183) --- app/models/account_migration.rb | 5 ++- spec/models/account_migration_spec.rb | 4 +++ spec/system/settings/migrations_spec.rb | 42 ++++++++++++++++++++----- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/app/models/account_migration.rb b/app/models/account_migration.rb index b4ab0d604bdc01..c2c6355a1869a8 100644 --- a/app/models/account_migration.rb +++ b/app/models/account_migration.rb @@ -25,7 +25,10 @@ class AccountMigration < ApplicationRecord before_validation :set_target_account before_validation :set_followers_count + attribute :current_username, :string + normalizes :acct, with: ->(acct) { acct.strip.delete_prefix('@') } + normalizes :current_username, with: ->(value) { value.strip.delete_prefix('@') } validates :acct, presence: true, domain: { acct: true } validate :validate_migration_cooldown @@ -33,7 +36,7 @@ class AccountMigration < ApplicationRecord scope :within_cooldown, -> { where(created_at: cooldown_duration_ago..) } - attr_accessor :current_password, :current_username + attr_accessor :current_password def self.cooldown_duration_ago Time.current - COOLDOWN_PERIOD diff --git a/spec/models/account_migration_spec.rb b/spec/models/account_migration_spec.rb index b92771e8f5c737..1bb238f7ef8796 100644 --- a/spec/models/account_migration_spec.rb +++ b/spec/models/account_migration_spec.rb @@ -7,6 +7,10 @@ describe 'acct' do it { is_expected.to normalize(:acct).from(' @username@domain ').to('username@domain') } end + + describe 'current_username' do + it { is_expected.to normalize(:current_username).from(' @username ').to('username') } + end end describe 'Validations' do diff --git a/spec/system/settings/migrations_spec.rb b/spec/system/settings/migrations_spec.rb index fecde36f35fd0c..d95636a6091f23 100644 --- a/spec/system/settings/migrations_spec.rb +++ b/spec/system/settings/migrations_spec.rb @@ -33,20 +33,36 @@ end describe 'Creating migrations' do - let(:user) { Fabricate(:user, password: '12345678') } + let(:user) { Fabricate(:user, password:) } + let(:password) { '12345678' } before { sign_in(user) } context 'when migration account is changed' do let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) } - it 'updates moved to account' do - visit settings_migration_path + context 'when user has encrypted password' do + it 'updates moved to account' do + visit settings_migration_path - expect { fill_in_and_submit } - .to(change { user.account.reload.moved_to_account_id }.to(acct.id)) - expect(page) - .to have_content(I18n.t('settings.migrate')) + expect { fill_in_and_submit } + .to(change { user.account.reload.moved_to_account_id }.to(acct.id)) + expect(page) + .to have_content(I18n.t('settings.migrate')) + end + end + + context 'when user has blank encrypted password value' do + before { user.update! encrypted_password: '' } + + it 'updates moved to account using at-username value' do + visit settings_migration_path + + expect { fill_in_and_submit_via_username("@#{user.account.username}") } + .to(change { user.account.reload.moved_to_account_id }.to(acct.id)) + expect(page) + .to have_content(I18n.t('settings.migrate')) + end end end @@ -92,8 +108,18 @@ def fill_in_and_submit fill_in 'account_migration_acct', with: acct.username - fill_in 'account_migration_current_password', with: '12345678' + if block_given? + yield + else + fill_in 'account_migration_current_password', with: password + end click_on I18n.t('migrations.proceed_with_move') end + + def fill_in_and_submit_via_username(username) + fill_in_and_submit do + fill_in 'account_migration_current_username', with: username + end + end end end From 3a2613a3e88d3c10d770e8274451d8dee4c59933 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 10:33:06 +0100 Subject: [PATCH 059/203] New Crowdin Translations (automated) (#38185) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/da.json | 5 +++++ app/javascript/mastodon/locales/de.json | 7 ++++++- app/javascript/mastodon/locales/el.json | 5 +++++ app/javascript/mastodon/locales/en-GB.json | 5 +++++ app/javascript/mastodon/locales/es-AR.json | 7 +++++++ app/javascript/mastodon/locales/es-MX.json | 5 +++++ app/javascript/mastodon/locales/es.json | 23 +++++++++++++++------- app/javascript/mastodon/locales/fi.json | 17 ++++++++++------ app/javascript/mastodon/locales/fr-CA.json | 7 +++++++ app/javascript/mastodon/locales/fr.json | 7 +++++++ app/javascript/mastodon/locales/ga.json | 9 +++++++++ app/javascript/mastodon/locales/gl.json | 5 +++++ app/javascript/mastodon/locales/he.json | 9 +++++++++ app/javascript/mastodon/locales/hu.json | 7 +++++++ app/javascript/mastodon/locales/is.json | 7 +++++++ app/javascript/mastodon/locales/it.json | 5 +++++ app/javascript/mastodon/locales/pt-PT.json | 8 ++++++++ app/javascript/mastodon/locales/sq.json | 7 +++++++ app/javascript/mastodon/locales/sv.json | 5 +++++ app/javascript/mastodon/locales/tr.json | 7 +++++++ app/javascript/mastodon/locales/vi.json | 5 +++++ app/javascript/mastodon/locales/zh-CN.json | 9 +++++++++ app/javascript/mastodon/locales/zh-TW.json | 5 +++++ config/locales/simple_form.fi.yml | 2 +- 24 files changed, 163 insertions(+), 15 deletions(-) diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 3dde4254e740a3..7911a597b2390c 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Samlede feltet \"{item}\" op.", "account_edit.field_reorder_modal.handle_label": "Træk feltet \"{item}\"", "account_edit.field_reorder_modal.title": "Omarrangér felter", + "account_edit.image_edit.add_button": "Tilføj billede", + "account_edit.image_edit.alt_add_button": "Tilføj alt-tekst", + "account_edit.image_edit.alt_edit_button": "Rediger alt-tekst", + "account_edit.image_edit.remove_button": "Fjern billede", + "account_edit.image_edit.replace_button": "Erstat billede", "account_edit.name_modal.add_title": "Tilføj visningsnavn", "account_edit.name_modal.edit_title": "Rediger visningsnavn", "account_edit.profile_tab.button_label": "Tilpas", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 3b2d249828913c..cc9217dfddf837 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -174,7 +174,7 @@ "account_edit.field_edit_modal.name_hint": "z. B. „Meine Website“", "account_edit.field_edit_modal.name_label": "Beschriftung", "account_edit.field_edit_modal.url_warning": "Um einen Link hinzuzufügen, füge {protocol} an den Anfang ein.", - "account_edit.field_edit_modal.value_hint": "z. B. „https://example.me“", + "account_edit.field_edit_modal.value_hint": "z. B. „https://beispiel.tld“", "account_edit.field_edit_modal.value_label": "Inhalt", "account_edit.field_reorder_modal.drag_cancel": "Das Ziehen wurde abgebrochen und das Feld „{item}“ wurde abgelegt.", "account_edit.field_reorder_modal.drag_end": "Das Feld „{item}“ wurde abgelegt.", @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Das Feld „{item}“ wurde ausgewählt.", "account_edit.field_reorder_modal.handle_label": "Das Feld „{item}“ verschieben", "account_edit.field_reorder_modal.title": "Felder neu anordnen", + "account_edit.image_edit.add_button": "Bild hinzufügen", + "account_edit.image_edit.alt_add_button": "Bildbeschreibung hinzufügen", + "account_edit.image_edit.alt_edit_button": "Bildbeschreibung bearbeiten", + "account_edit.image_edit.remove_button": "Bild entfernen", + "account_edit.image_edit.replace_button": "Bild ersetzen", "account_edit.name_modal.add_title": "Anzeigenamen hinzufügen", "account_edit.name_modal.edit_title": "Anzeigenamen bearbeiten", "account_edit.profile_tab.button_label": "Anpassen", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 100fce7e9a7245..4b33a257fd0a55 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Το πεδίο \"{item}\" σηκώθηκε.", "account_edit.field_reorder_modal.handle_label": "Μετακίνηση πεδίου \"{item}\"", "account_edit.field_reorder_modal.title": "Αναδιάταξη πεδίων", + "account_edit.image_edit.add_button": "Προσθήκη εικόνας", + "account_edit.image_edit.alt_add_button": "Προσθήκη εναλλακτικού κειμένου", + "account_edit.image_edit.alt_edit_button": "Επεξεργασία εναλλακτικού κειμένου", + "account_edit.image_edit.remove_button": "Αφαίρεση εικόνας", + "account_edit.image_edit.replace_button": "Αντικατάσταση εικόνας", "account_edit.name_modal.add_title": "Προσθήκη εμφανιζόμενου ονόματος", "account_edit.name_modal.edit_title": "Επεξεργασία εμφανιζόμενου ονόματος", "account_edit.profile_tab.button_label": "Προσαρμογή", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 25cb548c19e31d..abb0bcef2da58d 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Picked up field \"{item}\".", "account_edit.field_reorder_modal.handle_label": "Drag field \"{item}\"", "account_edit.field_reorder_modal.title": "Rearrange fields", + "account_edit.image_edit.add_button": "Add image", + "account_edit.image_edit.alt_add_button": "Add alt text", + "account_edit.image_edit.alt_edit_button": "Edit alt text", + "account_edit.image_edit.remove_button": "Remove image", + "account_edit.image_edit.replace_button": "Replace image", "account_edit.name_modal.add_title": "Add display name", "account_edit.name_modal.edit_title": "Edit display name", "account_edit.profile_tab.button_label": "Customise", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 51e30303e05503..d2edfca143db7c 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Campo elegido «{item}».", "account_edit.field_reorder_modal.handle_label": "Arrastrá el campo «{item}»", "account_edit.field_reorder_modal.title": "Reordená los campos", + "account_edit.image_edit.add_button": "Agregar imagen", + "account_edit.image_edit.alt_add_button": "Agregar texto alternativo", + "account_edit.image_edit.alt_edit_button": "Editar texto alternativo", + "account_edit.image_edit.remove_button": "Quitar imagen", + "account_edit.image_edit.replace_button": "Reemplazar imagen", "account_edit.name_modal.add_title": "Agregar nombre a mostrar", "account_edit.name_modal.edit_title": "Editar nombre a mostrar", "account_edit.profile_tab.button_label": "Personalizar", @@ -961,12 +966,14 @@ "notifications_permission_banner.title": "No te pierdas nada", "onboarding.follows.back": "Volver", "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Podés intentar usar la búsqueda o navegar por la página de exploración para encontrar cuentas a las que seguir, o intentarlo de nuevo más tarde.", + "onboarding.follows.next": "Siguiente: Configurá tu perfil", "onboarding.follows.search": "Buscar", "onboarding.follows.title": "Para comenzar, empezá a seguir cuentas", "onboarding.profile.discoverable": "Hacer que mi perfil sea detectable", "onboarding.profile.discoverable_hint": "Cuando optás por ser detectable en Mastodon, tus mensajes pueden aparecer en los resultados de búsqueda y de tendencia, y tu perfil puede ser sugerido a personas con intereses similares a los tuyos.", "onboarding.profile.display_name": "Nombre para mostrar", "onboarding.profile.display_name_hint": "Tu nombre completo o tu pseudónimo…", + "onboarding.profile.finish": "Finalizar", "onboarding.profile.note": "Biografía", "onboarding.profile.note_hint": "Podés @mencionar otras cuentas o usar #etiquetas…", "onboarding.profile.title": "Configuración del perfil", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 6d99e2dbf51fd7..9c6bd2e3700b82 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Recogido el campo «{item}».", "account_edit.field_reorder_modal.handle_label": "Arrastra el campo «{item}»", "account_edit.field_reorder_modal.title": "Reorganizar campos", + "account_edit.image_edit.add_button": "Añadir imagen", + "account_edit.image_edit.alt_add_button": "Añadir texto alternativo", + "account_edit.image_edit.alt_edit_button": "Editar texto alternativo", + "account_edit.image_edit.remove_button": "Eliminar imagen", + "account_edit.image_edit.replace_button": "Reemplazar imagen", "account_edit.name_modal.add_title": "Añadir nombre para mostrar", "account_edit.name_modal.edit_title": "Editar nombre para mostrar", "account_edit.profile_tab.button_label": "Personalizar", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 3bf85dc81608ce..abf1791b0f5ddd 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -173,6 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Recomendamos no usar emojis personalizados combinados con enlaces. Los campos personalizados que contengan ambos solo se mostrarán como texto en vez de un enlace, para evitar confusiones.", "account_edit.field_edit_modal.name_hint": "Ej. \"Web personal\"", "account_edit.field_edit_modal.name_label": "Etiqueta", + "account_edit.field_edit_modal.url_warning": "Para añadir un enlace, incluye {protocol} al principio.", + "account_edit.field_edit_modal.value_hint": "Ejemplo: “https://example.me”", "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "El arrastre se ha cancelado. El campo \"{item}\" se ha soltado.", "account_edit.field_reorder_modal.drag_end": "El campo \"{item}\" se ha soltado.", @@ -182,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Campo \"{item}\" seleccionado.", "account_edit.field_reorder_modal.handle_label": "Arrastra el campo \"{item}\"", "account_edit.field_reorder_modal.title": "Reorganizar campos", + "account_edit.image_edit.add_button": "Añadir imagen", + "account_edit.image_edit.alt_add_button": "Añadir texto alternativo", + "account_edit.image_edit.alt_edit_button": "Editar texto alternativo", + "account_edit.image_edit.remove_button": "Quitar imagen", + "account_edit.image_edit.replace_button": "Sustituir imagen", "account_edit.name_modal.add_title": "Añadir nombre para mostrar", "account_edit.name_modal.edit_title": "Editar nombre para mostrar", "account_edit.profile_tab.button_label": "Personalizar", @@ -337,14 +344,14 @@ "collections.create_collection": "Crear colección", "collections.delete_collection": "Eliminar colección", "collections.description_length_hint": "Limitado a 100 caracteres", - "collections.detail.accept_inclusion": "Aceptar", + "collections.detail.accept_inclusion": "De acuerdo", "collections.detail.accounts_heading": "Cuentas", "collections.detail.author_added_you": "{author} te añadió a esta colección", "collections.detail.curated_by_author": "Seleccionado por {author}", "collections.detail.curated_by_you": "Seleccionado por ti", "collections.detail.loading": "Cargando colección…", "collections.detail.other_accounts_in_collection": "Otros en esta colección:", - "collections.detail.revoke_inclusion": "Eliminar", + "collections.detail.revoke_inclusion": "Sácame de aquí", "collections.detail.sensitive_note": "Esta colección contiene cuentas y contenido que puede ser sensible para algunos usuarios.", "collections.detail.share": "Compartir esta colección", "collections.edit_details": "Editar detalles", @@ -360,9 +367,9 @@ "collections.old_last_post_note": "Última publicación hace más de una semana", "collections.remove_account": "Quitar esta cuenta", "collections.report_collection": "Informar de esta colección", - "collections.revoke_collection_inclusion": "Eliminarme de esta colección", - "collections.revoke_inclusion.confirmation": "Ha sido eliminado de\"{collection}\"", - "collections.revoke_inclusion.error": "Se ha producido un error. Inténtelo de nuevo más tarde.", + "collections.revoke_collection_inclusion": "Sácame de esta colección", + "collections.revoke_inclusion.confirmation": "Has salido de la \"{collection}\"", + "collections.revoke_inclusion.error": "Se ha producido un error, inténtalo de nuevo más tarde.", "collections.search_accounts_label": "Buscar cuentas para añadir…", "collections.search_accounts_max_reached": "Has añadido el número máximo de cuentas", "collections.sensitive": "Sensible", @@ -486,9 +493,9 @@ "confirmations.remove_from_followers.confirm": "Eliminar seguidor", "confirmations.remove_from_followers.message": "{name} dejará de seguirte. ¿Estás seguro de que quieres continuar?", "confirmations.remove_from_followers.title": "¿Eliminar seguidor?", - "confirmations.revoke_collection_inclusion.confirm": "Eliminar", + "confirmations.revoke_collection_inclusion.confirm": "Sácame", "confirmations.revoke_collection_inclusion.message": "Esta acción es permanente, y el curador no podrá volver a añadirle a la colección más adelante.", - "confirmations.revoke_collection_inclusion.title": "¿Eliminarse de esta colección?", + "confirmations.revoke_collection_inclusion.title": "¿Salirse de esta colección?", "confirmations.revoke_quote.confirm": "Eliminar publicación", "confirmations.revoke_quote.message": "Esta acción no tiene vuelta atrás.", "confirmations.revoke_quote.title": "¿Eliminar la publicación?", @@ -959,12 +966,14 @@ "notifications_permission_banner.title": "Nunca te pierdas nada", "onboarding.follows.back": "Atrás", "onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar personas a las que seguir, o inténtalo de nuevo más tarde.", + "onboarding.follows.next": "Siguiente: Configura tu perfil", "onboarding.follows.search": "Buscar", "onboarding.follows.title": "Sigue personas para comenzar", "onboarding.profile.discoverable": "Hacer que mi perfil aparezca en búsquedas", "onboarding.profile.discoverable_hint": "Cuando permites que tu perfil aparezca en búsquedas en Mastodon, tus publicaciones podrán aparecer en los resultados de búsqueda y en tendencias, y tu perfil podrá recomendarse a gente con intereses similares a los tuyos.", "onboarding.profile.display_name": "Nombre para mostrar", "onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…", + "onboarding.profile.finish": "Terminar", "onboarding.profile.note": "Biografía", "onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #etiquetas…", "onboarding.profile.title": "Configuración del perfil", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index f7f529a63921ed..04be6b3223356a 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Valittu kenttä ”{item}”.", "account_edit.field_reorder_modal.handle_label": "Siirrä kenttää ”{item}”", "account_edit.field_reorder_modal.title": "Järjestele kenttiä", + "account_edit.image_edit.add_button": "Lisää kuva", + "account_edit.image_edit.alt_add_button": "Lisää tekstivastine", + "account_edit.image_edit.alt_edit_button": "Muokkaa tekstivastinetta", + "account_edit.image_edit.remove_button": "Poista kuva", + "account_edit.image_edit.replace_button": "Korvaa kuva", "account_edit.name_modal.add_title": "Lisää näyttönimi", "account_edit.name_modal.edit_title": "Muokkaa näyttönimeä", "account_edit.profile_tab.button_label": "Mukauta", @@ -225,8 +230,8 @@ "alert.rate_limited.title": "Pyyntömäärää rajoitettu", "alert.unexpected.message": "Tapahtui odottamaton virhe.", "alert.unexpected.title": "Hups!", - "alt_text_badge.title": "Vaihtoehtoinen teksti", - "alt_text_modal.add_alt_text": "Lisää vaihtoehtoinen teksti", + "alt_text_badge.title": "Tekstivastine", + "alt_text_modal.add_alt_text": "Lisää tekstivastine", "alt_text_modal.add_text_from_image": "Lisää teksti kuvasta", "alt_text_modal.cancel": "Peruuta", "alt_text_modal.change_thumbnail": "Vaihda pikkukuva", @@ -468,10 +473,10 @@ "confirmations.logout.confirm": "Kirjaudu ulos", "confirmations.logout.message": "Haluatko varmasti kirjautua ulos?", "confirmations.logout.title": "Kirjaudutaanko ulos?", - "confirmations.missing_alt_text.confirm": "Lisää vaihtoehtoinen teksti", - "confirmations.missing_alt_text.message": "Julkaisussasi on mediaa ilman vaihtoehtoista tekstiä. Kuvausten lisääminen auttaa tekemään sisällöstäsi saavutettavamman useammille ihmisille.", + "confirmations.missing_alt_text.confirm": "Lisää tekstivastine", + "confirmations.missing_alt_text.message": "Julkaisussasi on mediaa ilman tekstivastinetta. Kuvausten lisääminen auttaa tekemään sisällöstäsi saavutettavamman useammille ihmisille.", "confirmations.missing_alt_text.secondary": "Julkaise silti", - "confirmations.missing_alt_text.title": "Lisätäänkö vaihtoehtoinen teksti?", + "confirmations.missing_alt_text.title": "Lisätäänkö tekstivastine?", "confirmations.mute.confirm": "Mykistä", "confirmations.private_quote_notify.cancel": "Takaisin muokkaukseen", "confirmations.private_quote_notify.confirm": "Julkaise", @@ -706,7 +711,7 @@ "ignore_notifications_modal.not_following_title": "Sivuutetaanko ilmoitukset käyttäjiltä, joita et seuraa?", "ignore_notifications_modal.private_mentions_title": "Sivuutetaanko ilmoitukset pyytämättömistä yksityismaininnoista?", "info_button.label": "Ohje", - "info_button.what_is_alt_text": "

Mikä vaihtoehtoinen teksti on?

Vaihtoehtoinen teksti tarjoaa kuvauksen kuvista ihmisille, joilla on näkövamma tai matalan kaistanleveyden yhteys tai jotka kaipaavat lisäkontekstia.

Voit parantaa saavutettavuutta ja ymmärrettävyyttä kaikkien näkökulmasta kirjoittamalla selkeän, tiiviin ja objektiivisen vaihtoehtoisen tekstin.

  • Ota mukaan tärkeät elementit
  • Tiivistä kuvissa oleva teksti
  • Käytä tavallisia lauserakenteita
  • Vältä turhaa tietoa
  • Keskity trendeihin ja keskeisiin tuloksiin monimutkaisissa visuaalisissa esityksissä (kuten kaavioissa tai kartoissa)
", + "info_button.what_is_alt_text": "

Mikä tekstivastine on?

Tekstivastine tarjoaa kuvauksen kuvista ihmisille, joilla on näkövamma tai matalan kaistanleveyden yhteys tai jotka kaipaavat lisäkontekstia.

Voit parantaa saavutettavuutta ja ymmärrettävyyttä kaikkien näkökulmasta kirjoittamalla selkeän, tiiviin ja objektiivisen tekstivastineen.

  • Ota mukaan tärkeät elementit
  • Tiivistä kuvissa oleva teksti
  • Käytä tavallisia lauserakenteita
  • Vältä turhaa tietoa
  • Keskity trendeihin ja keskeisiin tuloksiin monimutkaisissa visuaalisissa esityksissä (kuten kaavioissa tai kartoissa)
", "interaction_modal.action": "Jotta voit olla vuorovaikutuksessa käyttäjän {name} julkaisun kanssa, sinun on kirjauduttava sisään tilillesi käyttämälläsi Mastodon-palvelimella.", "interaction_modal.go": "Siirry", "interaction_modal.no_account_yet": "Eikö sinulla ole vielä tiliä?", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 007baa530c28cd..ffbd596f251a29 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Champ « {item} » sélectionné.", "account_edit.field_reorder_modal.handle_label": "Faites glisser le champ « {item} »", "account_edit.field_reorder_modal.title": "Réorganiser les champs", + "account_edit.image_edit.add_button": "Ajouter une image", + "account_edit.image_edit.alt_add_button": "Ajouter un texte alternatif", + "account_edit.image_edit.alt_edit_button": "Modifier le texte alternatif", + "account_edit.image_edit.remove_button": "Supprimer l’image", + "account_edit.image_edit.replace_button": "Remplacer l'image", "account_edit.name_modal.add_title": "Ajouter un nom public", "account_edit.name_modal.edit_title": "Modifier le nom public", "account_edit.profile_tab.button_label": "Personnaliser", @@ -961,12 +966,14 @@ "notifications_permission_banner.title": "Ne rien rater", "onboarding.follows.back": "Retour", "onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer de rechercher ou de parcourir la page \"Explorer\" pour trouver des personnes à suivre, ou réessayer plus tard.", + "onboarding.follows.next": "Suivant : configurer votre profil", "onboarding.follows.search": "Recherche", "onboarding.follows.title": "Suivre des personnes pour commencer", "onboarding.profile.discoverable": "Permettre de découvrir mon profil", "onboarding.profile.discoverable_hint": "Lorsque vous acceptez d'être découvert sur Mastodon, vos messages peuvent apparaître dans les résultats de recherche et les tendances, et votre profil peut être suggéré à des personnes ayant des intérêts similaires aux vôtres.", "onboarding.profile.display_name": "Nom affiché", "onboarding.profile.display_name_hint": "Votre nom complet ou votre nom rigolo…", + "onboarding.profile.finish": "Terminer", "onboarding.profile.note": "Bio", "onboarding.profile.note_hint": "Vous pouvez @mentionner d'autres personnes ou #hashtags…", "onboarding.profile.title": "Configuration du profil", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 6eb2e0a216c6f2..f1a8f511bbf3d1 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Champ « {item} » sélectionné.", "account_edit.field_reorder_modal.handle_label": "Faites glisser le champ « {item} »", "account_edit.field_reorder_modal.title": "Réorganiser les champs", + "account_edit.image_edit.add_button": "Ajouter une image", + "account_edit.image_edit.alt_add_button": "Ajouter un texte alternatif", + "account_edit.image_edit.alt_edit_button": "Modifier le texte alternatif", + "account_edit.image_edit.remove_button": "Supprimer l’image", + "account_edit.image_edit.replace_button": "Remplacer l'image", "account_edit.name_modal.add_title": "Ajouter un nom public", "account_edit.name_modal.edit_title": "Modifier le nom public", "account_edit.profile_tab.button_label": "Personnaliser", @@ -961,12 +966,14 @@ "notifications_permission_banner.title": "Toujours au courant", "onboarding.follows.back": "Retour", "onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer d'utiliser la recherche ou parcourir la page de découverte pour trouver des personnes à suivre, ou réessayez plus tard.", + "onboarding.follows.next": "Suivant : configurer votre profil", "onboarding.follows.search": "Recherche", "onboarding.follows.title": "Suivre des personnes pour commencer", "onboarding.profile.discoverable": "Permettre de découvrir mon profil", "onboarding.profile.discoverable_hint": "Lorsque vous acceptez d'être découvert sur Mastodon, vos messages peuvent apparaître dans les résultats de recherche et les tendances, et votre profil peut être suggéré à des personnes ayant des intérêts similaires aux vôtres.", "onboarding.profile.display_name": "Nom affiché", "onboarding.profile.display_name_hint": "Votre nom complet ou votre nom rigolo…", + "onboarding.profile.finish": "Terminer", "onboarding.profile.note": "Biographie", "onboarding.profile.note_hint": "Vous pouvez @mentionner d'autres personnes ou #hashtags…", "onboarding.profile.title": "Configuration du profil", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 3374b69c41af37..ca1155df87e867 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -173,6 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Molaimid gan emoji saincheaptha a úsáid i gcomhar le Urlanna. Taispeánfar réimsí saincheaptha ina bhfuil an dá cheann mar théacs amháin seachas mar nasc, chun mearbhall úsáideoirí a sheachaint.", "account_edit.field_edit_modal.name_hint": "M.sh. “Suíomh Gréasáin pearsanta”", "account_edit.field_edit_modal.name_label": "Lipéad", + "account_edit.field_edit_modal.url_warning": "Chun nasc a chur leis, cuir {protocol} ag an tús le do thoil.", + "account_edit.field_edit_modal.value_hint": "M.sh. “https://example.me”", "account_edit.field_edit_modal.value_label": "Luach", "account_edit.field_reorder_modal.drag_cancel": "Cuireadh an tarraingt ar ceal. Baineadh an réimse \"{item}\".", "account_edit.field_reorder_modal.drag_end": "Baineadh an réimse \"{item}\".", @@ -182,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Réimse \"{item}\" bailithe.", "account_edit.field_reorder_modal.handle_label": "Tarraing réimse \"{item}\"", "account_edit.field_reorder_modal.title": "Athshocraigh réimsí", + "account_edit.image_edit.add_button": "Cuir íomhá leis", + "account_edit.image_edit.alt_add_button": "Cuir téacs alt leis", + "account_edit.image_edit.alt_edit_button": "Cuir téacs alt in eagar", + "account_edit.image_edit.remove_button": "Bain íomhá", + "account_edit.image_edit.replace_button": "Athsholáthair íomhá", "account_edit.name_modal.add_title": "Cuir ainm taispeána leis", "account_edit.name_modal.edit_title": "Cuir ainm taispeána in eagar", "account_edit.profile_tab.button_label": "Saincheap", @@ -959,12 +966,14 @@ "notifications_permission_banner.title": "Ná caill aon rud go deo", "onboarding.follows.back": "Ar ais", "onboarding.follows.empty": "Ar an drochuair, ní féidir aon torthaí a thaispeáint faoi láthair. Is féidir leat triail a bhaint as cuardach nó brabhsáil ar an leathanach taiscéalaíochta chun teacht ar dhaoine le leanúint, nó bain triail eile as níos déanaí.", + "onboarding.follows.next": "Ar Aghaidh: Socraigh do phróifíl", "onboarding.follows.search": "Cuardach", "onboarding.follows.title": "Lean daoine le tosú", "onboarding.profile.discoverable": "Déan mo phróifíl a fháil amach", "onboarding.profile.discoverable_hint": "Nuair a roghnaíonn tú infhionnachtana ar Mastodon, d’fhéadfadh do phoist a bheith le feiceáil i dtorthaí cuardaigh agus treochtaí, agus d’fhéadfaí do phróifíl a mholadh do dhaoine a bhfuil na leasanna céanna acu leat.", "onboarding.profile.display_name": "Ainm taispeána", "onboarding.profile.display_name_hint": "D’ainm iomlán nó d’ainm spraíúil…", + "onboarding.profile.finish": "Críochnaigh", "onboarding.profile.note": "Bith", "onboarding.profile.note_hint": "Is féidir leat @ daoine eile a lua nó #hashtags…", "onboarding.profile.title": "Socrú próifíle", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 4a7780ff848504..975c89e75a5e76 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Seleccionado o campo \"{item}\".", "account_edit.field_reorder_modal.handle_label": "Arrastra o campo \"{item}\"", "account_edit.field_reorder_modal.title": "Ordear campos", + "account_edit.image_edit.add_button": "Engadir imaxe", + "account_edit.image_edit.alt_add_button": "Engadir descrición", + "account_edit.image_edit.alt_edit_button": "Editar descrición", + "account_edit.image_edit.remove_button": "Retirar a imaxe", + "account_edit.image_edit.replace_button": "Substituír a imaxe", "account_edit.name_modal.add_title": "Engadir nome público", "account_edit.name_modal.edit_title": "Editar o nome público", "account_edit.profile_tab.button_label": "Personalizar", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index c3f7f818187908..12adfb5ce23acd 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -173,6 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "אנו ממליצים נגד שימוש באמוג'י ייחודיים ביחד עם URL. שדות מיוחדים שמכילים את שניהם יופיעו כמלל בלבד ולא כקישור, כדי למנוע בלבול משתמשים.", "account_edit.field_edit_modal.name_hint": "למשל \"אתר אישי\"", "account_edit.field_edit_modal.name_label": "תווית", + "account_edit.field_edit_modal.url_warning": "כדי להוסיף קישור, אנא הכלילו {protocol} בהתחלה.", + "account_edit.field_edit_modal.value_hint": "למשל “https://example.me”", "account_edit.field_edit_modal.value_label": "ערך", "account_edit.field_reorder_modal.drag_cancel": "הגרירה בוטלה. השדה \"{item}\" נעזב.", "account_edit.field_reorder_modal.drag_end": "השדה \"{item}\" נעזב.", @@ -182,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "השדה \"{item}\" נבחר.", "account_edit.field_reorder_modal.handle_label": "הזזת השדה \"{item}\"", "account_edit.field_reorder_modal.title": "סידור שדות", + "account_edit.image_edit.add_button": "הוספת תמונה", + "account_edit.image_edit.alt_add_button": "הוספת מלל חלופי", + "account_edit.image_edit.alt_edit_button": "עריכת מלל חלופי", + "account_edit.image_edit.remove_button": "הסרת תמונה", + "account_edit.image_edit.replace_button": "החלפת תמונה", "account_edit.name_modal.add_title": "הוספת שם תצוגה", "account_edit.name_modal.edit_title": "עריכת שם תצוגה", "account_edit.profile_tab.button_label": "התאמה אישית", @@ -959,12 +966,14 @@ "notifications_permission_banner.title": "לעולם אל תחמיץ דבר", "onboarding.follows.back": "בחזרה", "onboarding.follows.empty": "למצער, תוצאות לחיפושך אינן בנמצא. ניתן להשתמש בחיפוש או בדף החקירות לשם מציאת אנשים ולעקבם. אפשר גם לנסות שוב אחר כך.", + "onboarding.follows.next": "להמשיך ליצירת הפרופיל שלך", "onboarding.follows.search": "חיפוש", "onboarding.follows.title": "כדי להתחיל, יש לעקוב אחרי אנשים", "onboarding.profile.discoverable": "כלול את הפרופיל שלי בעמודת התגליות", "onboarding.profile.discoverable_hint": "כשתבחרו להכלל ב\"תגליות\" על מסטודון, ההודעות שלכם עשויות להופיע בתוצאות חיפוש ועמודות \"נושאים חמים\", והפרופיל יוצע לאחרים עם תחומי עניין משותפים לכם.", "onboarding.profile.display_name": "שם להצגה", "onboarding.profile.display_name_hint": "שמך המלא או כינוי הכיף שלך…", + "onboarding.profile.finish": "סיום", "onboarding.profile.note": "אודות", "onboarding.profile.note_hint": "ניתן @לאזכר משתמשים אחרים או #תגיות…", "onboarding.profile.title": "הגדרת פרופיל", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index f58a33925dcf8f..7fcc82d1dabf27 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "A(z) „{item}” mező áthelyezéshez felvéve.", "account_edit.field_reorder_modal.handle_label": "A(z) „{item}” mező húzása", "account_edit.field_reorder_modal.title": "Mezők átrendezése", + "account_edit.image_edit.add_button": "Kép hozzáadása", + "account_edit.image_edit.alt_add_button": "Helyettesítő szöveg hozzáadása", + "account_edit.image_edit.alt_edit_button": "Helyettesítő szöveg szerkesztése", + "account_edit.image_edit.remove_button": "Kép eltávolítása", + "account_edit.image_edit.replace_button": "Kép cseréje", "account_edit.name_modal.add_title": "Megjelenítendő név hozzáadása", "account_edit.name_modal.edit_title": "Megjelenítendő név szerkesztése", "account_edit.profile_tab.button_label": "Testreszabás", @@ -961,12 +966,14 @@ "notifications_permission_banner.title": "Soha ne mulassz el semmit", "onboarding.follows.back": "Vissza", "onboarding.follows.empty": "Sajnos jelenleg nem jeleníthető meg eredmény. Kipróbálhatod a keresést vagy böngészheted a felfedező oldalon a követni kívánt személyeket, vagy próbáld meg később.", + "onboarding.follows.next": "Következik: A profil beállítása", "onboarding.follows.search": "Keresés", "onboarding.follows.title": "A kezdéshez kezdj el embereket követni", "onboarding.profile.discoverable": "Saját profil beállítása felfedezhetőként", "onboarding.profile.discoverable_hint": "A Mastodonon a felfedezhetőség választása esetén a saját bejegyzéseid megjelenhetnek a keresési eredmények és a felkapott tartalmak között, valamint a profilod a hozzád hasonló érdeklődési körrel rendelkező embereknél is ajánlásra kerülhet.", "onboarding.profile.display_name": "Megjelenített név", "onboarding.profile.display_name_hint": "Teljes neved vagy vicces neved…", + "onboarding.profile.finish": "Befejezés", "onboarding.profile.note": "Bemutatkozás", "onboarding.profile.note_hint": "Megemlíthetsz @másokat vagy #hashtag-eket…", "onboarding.profile.title": "Profilbeállítás", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 603cf573cca140..60ae1b6db699b6 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Náði reitnum \"{item}\".", "account_edit.field_reorder_modal.handle_label": "Dragðu reitinn \"{item}\"", "account_edit.field_reorder_modal.title": "Endurraða gagnasviðum", + "account_edit.image_edit.add_button": "Bæta við mynd", + "account_edit.image_edit.alt_add_button": "Bæta við hjálpartexta", + "account_edit.image_edit.alt_edit_button": "Breyta hjálpartexta", + "account_edit.image_edit.remove_button": "Fjarlægja mynd", + "account_edit.image_edit.replace_button": "Skipta um mynd", "account_edit.name_modal.add_title": "Bættu við birtingarnafni", "account_edit.name_modal.edit_title": "Breyta birtingarnafni", "account_edit.profile_tab.button_label": "Sérsníða", @@ -961,12 +966,14 @@ "notifications_permission_banner.title": "Aldrei missa af neinu", "onboarding.follows.back": "Til baka", "onboarding.follows.empty": "Því miður er ekki hægt að birta neinar niðurstöður í augnablikinu. Þú getur reynt að nota leitina eða skoðað könnunarsíðuna til að finna fólk til að fylgjast með, nú eða prófað aftur síðar.", + "onboarding.follows.next": "Næsta: Settu upp notandasniðið þitt", "onboarding.follows.search": "Leita", "onboarding.follows.title": "Þú ættir að fylgjast með fólki til að komast í gang", "onboarding.profile.discoverable": "Gera notandasniðið mitt uppgötvanlegt", "onboarding.profile.discoverable_hint": "Þegar þú velur að hægt sé að uppgötva þig á Mastodon, munu færslurnar þínar birtast í leitarniðurstöðum og vinsældalistum, auk þess sem stungið verður upp á notandasniðinu þínu við fólk sem er með svipuð áhugamál og þú.", "onboarding.profile.display_name": "Birtingarnafn", "onboarding.profile.display_name_hint": "Fullt nafn þitt eða eitthvað til gamans…", + "onboarding.profile.finish": "Ljúka", "onboarding.profile.note": "Æviágrip", "onboarding.profile.note_hint": "Þú getur @minnst á annað fólk eða #myllumerki…", "onboarding.profile.title": "Uppsetning notandasniðs", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 384723db6d3b29..8a675829d8ca9e 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Campo \"{item}\" selezionato.", "account_edit.field_reorder_modal.handle_label": "Trascina il campo \"{item}\"", "account_edit.field_reorder_modal.title": "Riorganizza i campi", + "account_edit.image_edit.add_button": "Aggiungi un'immagine", + "account_edit.image_edit.alt_add_button": "Aggiungi il testo alternativo", + "account_edit.image_edit.alt_edit_button": "Modifica il testo alternativo", + "account_edit.image_edit.remove_button": "Rimuovi l'immagine", + "account_edit.image_edit.replace_button": "Sostituisci l'immagine", "account_edit.name_modal.add_title": "Aggiungi il nome mostrato", "account_edit.name_modal.edit_title": "Modifica il nome mostrato", "account_edit.profile_tab.button_label": "Personalizza", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 7bca941543889a..62f51fc30c3049 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -173,6 +173,7 @@ "account_edit.field_edit_modal.link_emoji_warning": "Não recomendamos o uso de emojis personalizados em combinação com URLs. Campos personalizados que contenham ambos serão exibidos apenas como texto, em vez de como hiperligação, para evitar confusão aos utilizadores.", "account_edit.field_edit_modal.name_hint": "Ex.: \"Site pessoal\"", "account_edit.field_edit_modal.name_label": "Rótulo", + "account_edit.field_edit_modal.value_hint": "Ex.: \"https://exemplo.eu\"", "account_edit.field_edit_modal.value_label": "Valor", "account_edit.field_reorder_modal.drag_cancel": "O arrastamento foi cancelado. O campo \"{item}\" foi largado.", "account_edit.field_reorder_modal.drag_end": "O campo \"{item}\" foi largado.", @@ -182,6 +183,11 @@ "account_edit.field_reorder_modal.drag_start": "Apanhou o campo \"{item}\".", "account_edit.field_reorder_modal.handle_label": "Arrastar o campo \"{item}\"", "account_edit.field_reorder_modal.title": "Reordenar campos", + "account_edit.image_edit.add_button": "Adicionar imagem", + "account_edit.image_edit.alt_add_button": "Adicionar texto alternativo", + "account_edit.image_edit.alt_edit_button": "Editar texto alternativo", + "account_edit.image_edit.remove_button": "Remover imagem", + "account_edit.image_edit.replace_button": "Substituir imagem", "account_edit.name_modal.add_title": "Adicionar nome a mostrar", "account_edit.name_modal.edit_title": "Editar o nome a mostrar", "account_edit.profile_tab.button_label": "Personalizar", @@ -958,12 +964,14 @@ "notifications_permission_banner.title": "Nunca percas nada", "onboarding.follows.back": "Voltar", "onboarding.follows.empty": "Infelizmente não é possível mostrar resultados neste momento. Podes tentar pesquisar ou navegar na página \"Explorar\" para encontrares pessoas para seguires ou tentar novamente mais tarde.", + "onboarding.follows.next": "A seguir: configure o seu perfil", "onboarding.follows.search": "Pesquisar", "onboarding.follows.title": "Segue pessoas para começar", "onboarding.profile.discoverable": "Permitir que o meu perfil seja descoberto", "onboarding.profile.discoverable_hint": "Quando opta pela possibilidade de ser descoberto no Mastodon, as suas mensagens podem aparecer nos resultados de pesquisa e nos destaques, e o seu perfil pode ser sugerido a pessoas com interesses semelhantes aos seus.", "onboarding.profile.display_name": "Nome a apresentar", "onboarding.profile.display_name_hint": "O teu nome completo ou o teu nome divertido…", + "onboarding.profile.finish": "Terminar", "onboarding.profile.note": "Biografia", "onboarding.profile.note_hint": "Podes @mencionar outras pessoas e usar #etiquetas…", "onboarding.profile.title": "Configuração do perfil", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 55d52f66c25ed9..ad4471483e542f 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -183,6 +183,11 @@ "account_edit.field_reorder_modal.drag_start": "U mor fusha “{item}”.", "account_edit.field_reorder_modal.handle_label": "Tërhiqni fushën “{item}”", "account_edit.field_reorder_modal.title": "Risistemoni fusha", + "account_edit.image_edit.add_button": "Shtoni figurë", + "account_edit.image_edit.alt_add_button": "Shtoni tekst alternativ", + "account_edit.image_edit.alt_edit_button": "Përpunoni tekst alternativ", + "account_edit.image_edit.remove_button": "Hiqe figurën", + "account_edit.image_edit.replace_button": "Zëvendësoje figurën", "account_edit.name_modal.add_title": "Shtoni emër në ekran", "account_edit.name_modal.edit_title": "Përpunoni emër në ekran", "account_edit.profile_tab.button_label": "Përshtateni", @@ -955,12 +960,14 @@ "notifications_permission_banner.title": "Mos t’ju shpëtojë gjë", "onboarding.follows.back": "Mbrapsht", "onboarding.follows.empty": "Mjerisht, s’mund të shfaqen përfundime tani. Mund të provoni të përdorni kërkimin, ose të shfletoni faqen e eksplorimit, që të gjeni persona për ndjekje, ose të riprovoni më vonë.", + "onboarding.follows.next": "Pasuesi: Ujdisni profilin tuaj", "onboarding.follows.search": "Kërkoni", "onboarding.follows.title": "Që t’ia filloni, ndiqni persona", "onboarding.profile.discoverable": "Bëje profilin tim të zbulueshëm", "onboarding.profile.discoverable_hint": "Kur zgjidhni të jeni i zbulueshëm në Mastodon, postimet tuaja mund të shfaqen në përfundime kërkimesh dhe gjëra në modë dhe profili juaj mund t’u sugjerohet njerëzve me interesa të ngjashme me ju.", "onboarding.profile.display_name": "Emër në ekran", "onboarding.profile.display_name_hint": "Emri juaj i plotë, ose ç’të doni…", + "onboarding.profile.finish": "Përfundoje", "onboarding.profile.note": "Jetëshkrim", "onboarding.profile.note_hint": "Mund të @përmendni persona të tjerë, ose #hashtagë…", "onboarding.profile.title": "Udjisje profili", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 158feb561ca109..7997992887ac3b 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -167,6 +167,11 @@ "account_edit.field_edit_modal.name_label": "Etikett", "account_edit.field_edit_modal.url_warning": "För att lägga till en länk, vänligen inkludera {protocol} i början.", "account_edit.field_edit_modal.value_hint": "T.ex. \"https://example.me”", + "account_edit.image_edit.add_button": "Lägg till bild", + "account_edit.image_edit.alt_add_button": "Lägg till alternativtext", + "account_edit.image_edit.alt_edit_button": "Redigera alternativtext", + "account_edit.image_edit.remove_button": "Ta bort bild", + "account_edit.image_edit.replace_button": "Ersätt bild", "account_edit.profile_tab.button_label": "Anpassa", "account_note.placeholder": "Klicka för att lägga till anteckning", "admin.dashboard.daily_retention": "Användarlojalitet per dag efter registrering", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index e84d8a5462fe61..4494be38e97662 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "\"{item}\" alanı seçildi.", "account_edit.field_reorder_modal.handle_label": "\"{item}\" alanını sürükle", "account_edit.field_reorder_modal.title": "Alanları yeniden düzenle", + "account_edit.image_edit.add_button": "Görsel ekle", + "account_edit.image_edit.alt_add_button": "Alternatif metin ekle", + "account_edit.image_edit.alt_edit_button": "Alternatif metni düzenle", + "account_edit.image_edit.remove_button": "Görseli kaldır", + "account_edit.image_edit.replace_button": "Görseli değiştir", "account_edit.name_modal.add_title": "Görünen ad ekle", "account_edit.name_modal.edit_title": "Görünen adı düzenle", "account_edit.profile_tab.button_label": "Özelleştir", @@ -961,12 +966,14 @@ "notifications_permission_banner.title": "Hiçbir şeyi kaçırmayın", "onboarding.follows.back": "Geri", "onboarding.follows.empty": "Maalesef şu an bir sonuç gösterilemiyor. Takip edilecek kişileri bulmak için arama veya keşfet sayfasına gözatmayı kullanabilirsiniz veya daha sonra tekrar deneyin.", + "onboarding.follows.next": "Sonraki: Profilinizi ayarlayın", "onboarding.follows.search": "Ara", "onboarding.follows.title": "Başlamak için insanları takip edin", "onboarding.profile.discoverable": "Profilimi keşfedilebilir yap", "onboarding.profile.discoverable_hint": "Mastodon'da keşfedilebilirliği etkinleştirdiğinizde, gönderileriniz arama sonuçlarında ve trendlerde görünebilir aynı zamanda profiliniz sizinle benzer ilgi alanlarına sahip kişilere önerilebilir.", "onboarding.profile.display_name": "Görünen isim", "onboarding.profile.display_name_hint": "Tam adınız veya kullanıcı adınız…", + "onboarding.profile.finish": "Tamamla", "onboarding.profile.note": "Kişisel bilgiler", "onboarding.profile.note_hint": "Diğer insanlara @değinebilir veya #etiketler kullanabilirsiniz…", "onboarding.profile.title": "Profilini ayarla", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 911d1c0fcbe400..34b8770b81129e 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Đã chọn trường \"{item}\".", "account_edit.field_reorder_modal.handle_label": "Kéo trường \"{item}\"", "account_edit.field_reorder_modal.title": "Sắp xếp lại trường", + "account_edit.image_edit.add_button": "Thêm ảnh", + "account_edit.image_edit.alt_add_button": "Thêm văn bản thay thế", + "account_edit.image_edit.alt_edit_button": "Sửa văn bản thay thế", + "account_edit.image_edit.remove_button": "Gỡ ảnh", + "account_edit.image_edit.replace_button": "Thay thế ảnh", "account_edit.name_modal.add_title": "Thêm tên gọi", "account_edit.name_modal.edit_title": "Sửa tên gọi", "account_edit.profile_tab.button_label": "Tùy chỉnh", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 9002fd1b0156ba..7e3282fc196674 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -173,6 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "我们建议不要同时使用自定义表情和网址。同时包含两者的自定义字段将会显示为纯文本而不是链接形式,以避免用户混淆。", "account_edit.field_edit_modal.name_hint": "例如:“个人网站”", "account_edit.field_edit_modal.name_label": "标签", + "account_edit.field_edit_modal.url_warning": "要添加链接,请在开头加上 {protocol}。", + "account_edit.field_edit_modal.value_hint": "例如:“https://example.me”", "account_edit.field_edit_modal.value_label": "值", "account_edit.field_reorder_modal.drag_cancel": "拖拽已终止。字段“{item}”已被丢弃。", "account_edit.field_reorder_modal.drag_end": "字段“{item}”已被丢弃。", @@ -182,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "已选中字段“{item}”。", "account_edit.field_reorder_modal.handle_label": "拖拽字段“{item}”", "account_edit.field_reorder_modal.title": "重新排列字段", + "account_edit.image_edit.add_button": "添加图片", + "account_edit.image_edit.alt_add_button": "添加替代文本", + "account_edit.image_edit.alt_edit_button": "编辑替代文本", + "account_edit.image_edit.remove_button": "移除图片", + "account_edit.image_edit.replace_button": "替换图片", "account_edit.name_modal.add_title": "添加显示名称", "account_edit.name_modal.edit_title": "编辑显示名称", "account_edit.profile_tab.button_label": "自定义", @@ -959,12 +966,14 @@ "notifications_permission_banner.title": "精彩不容错过", "onboarding.follows.back": "返回", "onboarding.follows.empty": "很抱歉,现在无法显示任何结果。你可以尝试使用搜索或浏览探索页面来查找要关注的人,或稍后再试。", + "onboarding.follows.next": "下一步:设置你的个人资料", "onboarding.follows.search": "搜索", "onboarding.follows.title": "关注用户,玩转 Mastodon", "onboarding.profile.discoverable": "让我的账号可被他人发现", "onboarding.profile.discoverable_hint": "当你在 Mastodon 上启用发现功能时,你的嘟文可能会出现在搜索结果与热门中,你的账号可能会被推荐给与你兴趣相似的人。", "onboarding.profile.display_name": "昵称", "onboarding.profile.display_name_hint": "你的全名或昵称…", + "onboarding.profile.finish": "完成", "onboarding.profile.note": "简介", "onboarding.profile.note_hint": "你可以提及 @其他人 或使用 #话题…", "onboarding.profile.title": "设置个人资料", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 7dffcfc992294e..4258450790f162 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -184,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "已選取欄位「{item}」。", "account_edit.field_reorder_modal.handle_label": "拖放欄位「{item}」", "account_edit.field_reorder_modal.title": "重新整理欄位", + "account_edit.image_edit.add_button": "新增圖片", + "account_edit.image_edit.alt_add_button": "新增 ALT 說明文字", + "account_edit.image_edit.alt_edit_button": "編輯 ALT 說明文字", + "account_edit.image_edit.remove_button": "移除圖片", + "account_edit.image_edit.replace_button": "替換圖片", "account_edit.name_modal.add_title": "新增顯示名稱", "account_edit.name_modal.edit_title": "編輯顯示名稱", "account_edit.profile_tab.button_label": "自訂", diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 9854ef31a77c82..1187ffdf0593bb 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -257,7 +257,7 @@ fi: setting_emoji_style: Emojityyli setting_expand_spoilers: Laajenna aina sisältövaroituksilla merkityt julkaisut setting_hide_network: Piilota verkostotietosi - setting_missing_alt_text_modal: Varoita ennen kuin julkaisen mediaa ilman vaihtoehtoista tekstiä + setting_missing_alt_text_modal: Varoita ennen kuin julkaisen mediaa ilman tekstivastinetta setting_quick_boosting: Ota nopea tehostus käyttöön setting_reduce_motion: Vähennä animaatioiden liikettä setting_system_font_ui: Käytä järjestelmän oletusfonttia From fdbc3c8a16d4c77a85aac26a742809a58d8d34b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:21:45 +0100 Subject: [PATCH 060/203] Update dependency sass to v1.98.0 (#38142) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2da7b00cf35f74..29c8511bb5311d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8578,10 +8578,10 @@ __metadata: languageName: node linkType: hard -"immutable@npm:^5.0.2": - version: 5.0.3 - resolution: "immutable@npm:5.0.3" - checksum: 10c0/3269827789e1026cd25c2ea97f0b2c19be852ffd49eda1b674b20178f73d84fa8d945ad6f5ac5bc4545c2b4170af9f6e1f77129bc1cae7974a4bf9b04a9cdfb9 +"immutable@npm:^5.1.5": + version: 5.1.5 + resolution: "immutable@npm:5.1.5" + checksum: 10c0/8017ece1578e3c5939ba3305176aee059def1b8a90c7fa2a347ef583ebbd38cbe77ce1bbd786a5fab57e2da00bbcb0493b92e4332cdc4e1fe5cfb09a4688df31 languageName: node linkType: hard @@ -12552,19 +12552,19 @@ __metadata: linkType: hard "sass@npm:^1.62.1, sass@npm:^1.70.0": - version: 1.97.3 - resolution: "sass@npm:1.97.3" + version: 1.98.0 + resolution: "sass@npm:1.98.0" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" - immutable: "npm:^5.0.2" + immutable: "npm:^5.1.5" source-map-js: "npm:>=0.6.2 <2.0.0" dependenciesMeta: "@parcel/watcher": optional: true bin: sass: sass.js - checksum: 10c0/67f6b5d220f20c1c23a8b16dda5fd1c5d119ad5caf8195b185d553b5b239fb188a3787f04fc00171c62515f2c4e5e0eb5ad4992a80f8543428556883c1240ba3 + checksum: 10c0/9e91daa20f970fefb364ac31289f070636da7aa7eaeb43e371ea98fa98085a6dbc2d3d058504226a02d07717faf0a4ce8d41b579ecb428c4a9d96b4dc1944a95 languageName: node linkType: hard From 890b2673fcb8416ec62d71f993d57b47e257b2f8 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 13 Mar 2026 14:55:17 +0100 Subject: [PATCH 061/203] Allow displaying field status (error, warning, info) under form fields (#38175) --- .../a11y_live_region.stories.tsx | 25 ++++++++++ .../components/a11y_live_region/index.tsx | 28 +++++++++++ .../callout_inline/callout_inline.stories.tsx | 39 +++++++++++++++ .../components/callout_inline/index.tsx | 39 +++++++++++++++ .../callout_inline/styles.module.css | 29 ++++++++++++ .../form_fields/checkbox_field.stories.tsx | 2 +- .../components/form_fields/checkbox_field.tsx | 4 +- .../components/form_fields/combobox_field.tsx | 4 +- .../form_fields/copy_link_field.tsx | 4 +- .../form_fields/emoji_text_field.tsx | 8 ++-- .../form_fields/fieldset.module.scss | 12 ++++- .../components/form_fields/fieldset.tsx | 29 ++++++++++-- .../form_field_wrapper.module.scss | 8 ++++ .../form_fields/form_field_wrapper.tsx | 47 +++++++++++++++---- .../radio_button_field.stories.tsx | 2 +- .../form_fields/radio_button_field.tsx | 4 +- .../form_fields/select_field.stories.tsx | 2 +- .../components/form_fields/select_field.tsx | 4 +- .../form_fields/text_area_field.stories.tsx | 12 ++++- .../form_fields/text_area_field.tsx | 4 +- .../form_fields/text_input.module.scss | 8 ++-- .../form_fields/text_input_field.stories.tsx | 12 ++++- .../form_fields/text_input_field.tsx | 4 +- .../form_fields/toggle_field.stories.tsx | 2 +- .../components/form_fields/toggle_field.tsx | 4 +- .../account_timeline/modals/note_modal.tsx | 7 ++- .../features/collections/editor/details.tsx | 27 +++++++++-- .../mastodon/features/onboarding/profile.tsx | 4 +- app/javascript/mastodon/locales/en.json | 1 + .../mastodon/reducers/slices/collections.ts | 3 +- app/javascript/mastodon/utils/hashtags.ts | 5 ++ 31 files changed, 331 insertions(+), 52 deletions(-) create mode 100644 app/javascript/mastodon/components/a11y_live_region/a11y_live_region.stories.tsx create mode 100644 app/javascript/mastodon/components/a11y_live_region/index.tsx create mode 100644 app/javascript/mastodon/components/callout_inline/callout_inline.stories.tsx create mode 100644 app/javascript/mastodon/components/callout_inline/index.tsx create mode 100644 app/javascript/mastodon/components/callout_inline/styles.module.css diff --git a/app/javascript/mastodon/components/a11y_live_region/a11y_live_region.stories.tsx b/app/javascript/mastodon/components/a11y_live_region/a11y_live_region.stories.tsx new file mode 100644 index 00000000000000..00804d685ba2ad --- /dev/null +++ b/app/javascript/mastodon/components/a11y_live_region/a11y_live_region.stories.tsx @@ -0,0 +1,25 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { A11yLiveRegion } from '.'; + +const meta = { + title: 'Components/A11yLiveRegion', + component: A11yLiveRegion, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Polite: Story = { + args: { + children: "This field can't be empty.", + }, +}; + +export const Assertive: Story = { + args: { + ...Polite.args, + role: 'alert', + }, +}; diff --git a/app/javascript/mastodon/components/a11y_live_region/index.tsx b/app/javascript/mastodon/components/a11y_live_region/index.tsx new file mode 100644 index 00000000000000..51fee5e4b93fee --- /dev/null +++ b/app/javascript/mastodon/components/a11y_live_region/index.tsx @@ -0,0 +1,28 @@ +import { polymorphicForwardRef } from '@/types/polymorphic'; + +/** + * A live region is a content region that announces changes of its contents + * to users of assistive technology like screen readers. + * + * Dynamically added warnings, errors, or live status updates should be wrapped + * in a live region to ensure they are not missed when they appear. + * + * **Important:** + * Live regions must be present in the DOM _before_ + * the to-be announced content is rendered into it. + */ + +export const A11yLiveRegion = polymorphicForwardRef<'div'>( + ({ role = 'status', as: Component = 'div', children, ...props }, ref) => { + return ( + + {children} + + ); + }, +); diff --git a/app/javascript/mastodon/components/callout_inline/callout_inline.stories.tsx b/app/javascript/mastodon/components/callout_inline/callout_inline.stories.tsx new file mode 100644 index 00000000000000..f18af41dc0d1e5 --- /dev/null +++ b/app/javascript/mastodon/components/callout_inline/callout_inline.stories.tsx @@ -0,0 +1,39 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { CalloutInline } from '.'; + +const meta = { + title: 'Components/CalloutInline', + args: { + children: 'Contents here', + }, + component: CalloutInline, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Error: Story = { + args: { + variant: 'error', + }, +}; + +export const Warning: Story = { + args: { + variant: 'warning', + }, +}; + +export const Success: Story = { + args: { + variant: 'success', + }, +}; + +export const Info: Story = { + args: { + variant: 'info', + }, +}; diff --git a/app/javascript/mastodon/components/callout_inline/index.tsx b/app/javascript/mastodon/components/callout_inline/index.tsx new file mode 100644 index 00000000000000..e2e6791963285a --- /dev/null +++ b/app/javascript/mastodon/components/callout_inline/index.tsx @@ -0,0 +1,39 @@ +import type { FC } from 'react'; + +import classNames from 'classnames'; + +import CheckIcon from '@/material-icons/400-24px/check.svg?react'; +import ErrorIcon from '@/material-icons/400-24px/error.svg?react'; +import InfoIcon from '@/material-icons/400-24px/info.svg?react'; +import WarningIcon from '@/material-icons/400-24px/warning.svg?react'; + +import { Icon } from '../icon'; + +import classes from './styles.module.css'; + +export interface FieldStatus { + variant: 'error' | 'warning' | 'info' | 'success'; + message?: string; +} + +const iconMap: Record = { + error: ErrorIcon, + warning: WarningIcon, + info: InfoIcon, + success: CheckIcon, +}; + +export const CalloutInline: FC< + Partial & React.ComponentPropsWithoutRef<'div'> +> = ({ variant = 'error', message, className, children, ...props }) => { + return ( +
+ + {message ?? children} +
+ ); +}; diff --git a/app/javascript/mastodon/components/callout_inline/styles.module.css b/app/javascript/mastodon/components/callout_inline/styles.module.css new file mode 100644 index 00000000000000..8d32f7df9b4742 --- /dev/null +++ b/app/javascript/mastodon/components/callout_inline/styles.module.css @@ -0,0 +1,29 @@ +.wrapper { + display: flex; + align-items: start; + gap: 4px; + font-size: 13px; + font-weight: 500; + + &[data-variant='success'] { + color: var(--color-text-success); + } + + &[data-variant='warning'] { + color: var(--color-text-warning); + } + + &[data-variant='error'] { + color: var(--color-text-error); + } + + &[data-variant='info'] { + color: var(--color-text-primary); + } +} + +.icon { + width: 16px; + height: 16px; + margin-top: 1px; +} diff --git a/app/javascript/mastodon/components/form_fields/checkbox_field.stories.tsx b/app/javascript/mastodon/components/form_fields/checkbox_field.stories.tsx index 4d208cf21b6d4e..16b3a53f0baff2 100644 --- a/app/javascript/mastodon/components/form_fields/checkbox_field.stories.tsx +++ b/app/javascript/mastodon/components/form_fields/checkbox_field.stories.tsx @@ -76,7 +76,7 @@ export const Optional: Story = { export const WithError: Story = { args: { required: false, - hasError: true, + status: 'error', }, }; diff --git a/app/javascript/mastodon/components/form_fields/checkbox_field.tsx b/app/javascript/mastodon/components/form_fields/checkbox_field.tsx index 2b6933c8473146..c08b81ca36d7f5 100644 --- a/app/javascript/mastodon/components/form_fields/checkbox_field.tsx +++ b/app/javascript/mastodon/components/form_fields/checkbox_field.tsx @@ -13,12 +13,12 @@ type Props = Omit, 'type'> & { export const CheckboxField = forwardRef< HTMLInputElement, Props & CommonFieldWrapperProps ->(({ id, label, hint, hasError, required, ...otherProps }, ref) => ( +>(({ id, label, hint, status, required, ...otherProps }, ref) => ( diff --git a/app/javascript/mastodon/components/form_fields/combobox_field.tsx b/app/javascript/mastodon/components/form_fields/combobox_field.tsx index 89193ed9d57ca7..8ce7161657bc92 100644 --- a/app/javascript/mastodon/components/form_fields/combobox_field.tsx +++ b/app/javascript/mastodon/components/form_fields/combobox_field.tsx @@ -86,14 +86,14 @@ interface Props */ export const ComboboxFieldWithRef = ( - { id, label, hint, hasError, required, ...otherProps }: Props, + { id, label, hint, status, required, ...otherProps }: Props, ref: React.ForwardedRef, ) => ( {(inputProps) => } diff --git a/app/javascript/mastodon/components/form_fields/copy_link_field.tsx b/app/javascript/mastodon/components/form_fields/copy_link_field.tsx index ad93e3a065d66f..d772315adeb5ea 100644 --- a/app/javascript/mastodon/components/form_fields/copy_link_field.tsx +++ b/app/javascript/mastodon/components/form_fields/copy_link_field.tsx @@ -22,7 +22,7 @@ interface CopyLinkFieldProps extends CommonFieldWrapperProps, TextInputProps { export const CopyLinkField = forwardRef( ( - { id, label, hint, hasError, value, required, className, ...otherProps }, + { id, label, hint, status, value, required, className, ...otherProps }, ref, ) => { const intl = useIntl(); @@ -48,7 +48,7 @@ export const CopyLinkField = forwardRef( label={label} hint={hint} required={required} - hasError={hasError} + status={status} inputId={id} > {(inputProps) => ( diff --git a/app/javascript/mastodon/components/form_fields/emoji_text_field.tsx b/app/javascript/mastodon/components/form_fields/emoji_text_field.tsx index a57c4d1dd4861b..af9e3d5280f8c4 100644 --- a/app/javascript/mastodon/components/form_fields/emoji_text_field.tsx +++ b/app/javascript/mastodon/components/form_fields/emoji_text_field.tsx @@ -37,7 +37,7 @@ export const EmojiTextInputField: FC< value, label, hint, - hasError, + status, maxLength, counterMax = maxLength, recommended, @@ -49,7 +49,7 @@ export const EmojiTextInputField: FC< const wrapperProps = { label, hint, - hasError, + status, counterMax, recommended, disabled, @@ -84,7 +84,7 @@ export const EmojiTextAreaField: FC< recommended, disabled, hint, - hasError, + status, ...otherProps }) => { const textareaRef = useRef(null); @@ -92,7 +92,7 @@ export const EmojiTextAreaField: FC< const wrapperProps = { label, hint, - hasError, + status, counterMax, recommended, disabled, diff --git a/app/javascript/mastodon/components/form_fields/fieldset.module.scss b/app/javascript/mastodon/components/form_fields/fieldset.module.scss index f222762af51c3f..2751b3c8a01c0e 100644 --- a/app/javascript/mastodon/components/form_fields/fieldset.module.scss +++ b/app/javascript/mastodon/components/form_fields/fieldset.module.scss @@ -1,7 +1,9 @@ .fieldset { + --container-gap: 12px; + display: flex; flex-direction: column; - gap: 12px; + gap: var(--container-gap); color: var(--color-text-primary); font-size: 15px; } @@ -17,3 +19,11 @@ column-gap: 24px; } } + +.status { + // If there's no content, we need to compensate for the parent's + // flex gap to avoid extra spacing below the field. + &:empty { + margin-top: calc(-1 * var(--container-gap)); + } +} diff --git a/app/javascript/mastodon/components/form_fields/fieldset.tsx b/app/javascript/mastodon/components/form_fields/fieldset.tsx index d52a95130b13ef..26381ca834ca0a 100644 --- a/app/javascript/mastodon/components/form_fields/fieldset.tsx +++ b/app/javascript/mastodon/components/form_fields/fieldset.tsx @@ -3,14 +3,19 @@ import type { ReactNode, FC } from 'react'; import { createContext, useId } from 'react'; +import { A11yLiveRegion } from 'mastodon/components/a11y_live_region'; +import type { FieldStatus } from 'mastodon/components/callout_inline'; +import { CalloutInline } from 'mastodon/components/callout_inline'; + import classes from './fieldset.module.scss'; +import { getFieldStatus } from './form_field_wrapper'; import formFieldWrapperClasses from './form_field_wrapper.module.scss'; interface FieldsetProps { legend: ReactNode; hint?: ReactNode; name?: string; - hasError?: boolean; + status?: FieldStatus | FieldStatus['variant']; layout?: 'vertical' | 'horizontal'; children: ReactNode; } @@ -26,22 +31,33 @@ export const Fieldset: FC = ({ legend, hint, name, - hasError, + status, layout, children, }) => { const uniqueId = useId(); const labelId = `${uniqueId}-label`; const hintId = `${uniqueId}-hint`; + const statusId = `${uniqueId}-status`; const fieldsetName = name || `${uniqueId}-fieldset-name`; const hasHint = !!hint; + const fieldStatus = getFieldStatus(status); + const hasStatusMessage = !!fieldStatus?.message; + + const descriptionIds = [ + hasHint ? hintId : '', + hasStatusMessage ? statusId : '', + ] + .filter((id) => !!id) + .join(' '); + return (
@@ -59,6 +75,11 @@ export const Fieldset: FC = ({ {children}
+ + {/* Live region must be rendered even when empty */} + + {hasStatusMessage && } +
); }; diff --git a/app/javascript/mastodon/components/form_fields/form_field_wrapper.module.scss b/app/javascript/mastodon/components/form_fields/form_field_wrapper.module.scss index faeb48aae4f62b..cff93be8a69fcd 100644 --- a/app/javascript/mastodon/components/form_fields/form_field_wrapper.module.scss +++ b/app/javascript/mastodon/components/form_fields/form_field_wrapper.module.scss @@ -46,6 +46,14 @@ font-size: 13px; } +.status { + // If there's no content, we need to compensate for the parent's + // flex gap to avoid extra spacing below the field. + &:empty { + margin-top: calc(-1 * var(--form-field-label-gap)); + } +} + .inputWrapper { display: block; } diff --git a/app/javascript/mastodon/components/form_fields/form_field_wrapper.tsx b/app/javascript/mastodon/components/form_fields/form_field_wrapper.tsx index 6454153ab88c90..7cd6d676142f74 100644 --- a/app/javascript/mastodon/components/form_fields/form_field_wrapper.tsx +++ b/app/javascript/mastodon/components/form_fields/form_field_wrapper.tsx @@ -7,6 +7,10 @@ import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; +import { A11yLiveRegion } from 'mastodon/components/a11y_live_region'; +import type { FieldStatus } from 'mastodon/components/callout_inline'; +import { CalloutInline } from 'mastodon/components/callout_inline'; + import { FieldsetNameContext } from './fieldset'; import classes from './form_field_wrapper.module.scss'; @@ -20,7 +24,7 @@ interface FieldWrapperProps { label: ReactNode; hint?: ReactNode; required?: boolean; - hasError?: boolean; + status?: FieldStatus['variant'] | FieldStatus; inputId?: string; describedById?: string; inputPlacement?: 'inline-start' | 'inline-end'; @@ -33,7 +37,7 @@ interface FieldWrapperProps { */ export type CommonFieldWrapperProps = Pick< FieldWrapperProps, - 'label' | 'hint' | 'hasError' + 'label' | 'hint' | 'status' > & { wrapperClassName?: string }; /** @@ -48,27 +52,31 @@ export const FormFieldWrapper: FC = ({ hint, describedById, required, - hasError, + status, inputPlacement, children, className, }) => { const uniqueId = useId(); const inputId = inputIdProp || `${uniqueId}-input`; + const statusId = `${inputIdProp || uniqueId}-status`; const hintId = `${inputIdProp || uniqueId}-hint`; const hasHint = !!hint; + const fieldStatus = getFieldStatus(status); + const hasStatusMessage = !!fieldStatus?.message; const hasParentFieldset = !!useContext(FieldsetNameContext); + const descriptionIds = + [hasHint ? hintId : '', hasStatusMessage ? statusId : '', describedById] + .filter((id) => !!id) + .join(' ') || undefined; + const inputProps: InputProps = { required, id: inputId, + 'aria-describedby': descriptionIds, }; - if (hasHint) { - inputProps['aria-describedby'] = describedById - ? `${describedById} ${hintId}` - : hintId; - } const input = (
{children(inputProps)}
@@ -77,7 +85,7 @@ export const FormFieldWrapper: FC = ({ return (
{inputPlacement === 'inline-start' && input} @@ -100,6 +108,11 @@ export const FormFieldWrapper: FC = ({
{inputPlacement !== 'inline-start' && input} + + {/* Live region must be rendered even when empty */} + + {hasStatusMessage && } +
); }; @@ -121,3 +134,19 @@ const RequiredMark: FC<{ required?: boolean }> = ({ required }) => ); + +export function getFieldStatus(status: FieldWrapperProps['status']) { + if (!status) { + return null; + } + + if (typeof status === 'string') { + const fieldStatus: FieldStatus = { + variant: status, + message: '', + }; + return fieldStatus; + } + + return status; +} diff --git a/app/javascript/mastodon/components/form_fields/radio_button_field.stories.tsx b/app/javascript/mastodon/components/form_fields/radio_button_field.stories.tsx index 95687abff324c7..1292b85724d589 100644 --- a/app/javascript/mastodon/components/form_fields/radio_button_field.stories.tsx +++ b/app/javascript/mastodon/components/form_fields/radio_button_field.stories.tsx @@ -71,7 +71,7 @@ export const Optional: Story = { export const WithError: Story = { args: { required: false, - hasError: true, + status: 'error', }, }; diff --git a/app/javascript/mastodon/components/form_fields/radio_button_field.tsx b/app/javascript/mastodon/components/form_fields/radio_button_field.tsx index 51f52168e06ec3..cbc9020ca7fe17 100644 --- a/app/javascript/mastodon/components/form_fields/radio_button_field.tsx +++ b/app/javascript/mastodon/components/form_fields/radio_button_field.tsx @@ -15,7 +15,7 @@ type Props = Omit, 'type'> & { export const RadioButtonField = forwardRef< HTMLInputElement, Props & CommonFieldWrapperProps ->(({ id, label, hint, hasError, required, ...otherProps }, ref) => { +>(({ id, label, hint, status, required, ...otherProps }, ref) => { const fieldsetName = useContext(FieldsetNameContext); return ( @@ -23,7 +23,7 @@ export const RadioButtonField = forwardRef< label={label} hint={hint} required={required} - hasError={hasError} + status={status} inputId={id} inputPlacement='inline-start' > diff --git a/app/javascript/mastodon/components/form_fields/select_field.stories.tsx b/app/javascript/mastodon/components/form_fields/select_field.stories.tsx index 469238dd44d9f4..c215a6e04ae832 100644 --- a/app/javascript/mastodon/components/form_fields/select_field.stories.tsx +++ b/app/javascript/mastodon/components/form_fields/select_field.stories.tsx @@ -51,7 +51,7 @@ export const Optional: Story = { export const WithError: Story = { args: { required: false, - hasError: true, + status: 'error', }, }; diff --git a/app/javascript/mastodon/components/form_fields/select_field.tsx b/app/javascript/mastodon/components/form_fields/select_field.tsx index 59854b578e0aee..7c1bfdf47d93db 100644 --- a/app/javascript/mastodon/components/form_fields/select_field.tsx +++ b/app/javascript/mastodon/components/form_fields/select_field.tsx @@ -19,12 +19,12 @@ interface Props */ export const SelectField = forwardRef( - ({ id, label, hint, required, hasError, children, ...otherProps }, ref) => ( + ({ id, label, hint, required, status, children, ...otherProps }, ref) => ( {(inputProps) => ( diff --git a/app/javascript/mastodon/components/form_fields/text_area_field.stories.tsx b/app/javascript/mastodon/components/form_fields/text_area_field.stories.tsx index 190239aee2a752..f06d7bbdcf799d 100644 --- a/app/javascript/mastodon/components/form_fields/text_area_field.stories.tsx +++ b/app/javascript/mastodon/components/form_fields/text_area_field.stories.tsx @@ -38,7 +38,17 @@ export const Optional: Story = { export const WithError: Story = { args: { required: false, - hasError: true, + status: { variant: 'error', message: "This field can't be empty" }, + }, +}; + +export const WithWarning: Story = { + args: { + required: false, + status: { + variant: 'warning', + message: 'Special characters are not allowed', + }, }, }; diff --git a/app/javascript/mastodon/components/form_fields/text_area_field.tsx b/app/javascript/mastodon/components/form_fields/text_area_field.tsx index 1e4bacc041931e..1284aa9276a0d7 100644 --- a/app/javascript/mastodon/components/form_fields/text_area_field.tsx +++ b/app/javascript/mastodon/components/form_fields/text_area_field.tsx @@ -26,14 +26,14 @@ export const TextAreaField = forwardRef< TextAreaProps & CommonFieldWrapperProps >( ( - { id, label, hint, required, hasError, wrapperClassName, ...otherProps }, + { id, label, hint, required, status, wrapperClassName, ...otherProps }, ref, ) => ( diff --git a/app/javascript/mastodon/components/form_fields/text_input.module.scss b/app/javascript/mastodon/components/form_fields/text_input.module.scss index 289ff1333aecaa..f432f57055d39c 100644 --- a/app/javascript/mastodon/components/form_fields/text_input.module.scss +++ b/app/javascript/mastodon/components/form_fields/text_input.module.scss @@ -29,16 +29,16 @@ color: var(--color-text-secondary); } - &:focus { - outline-color: var(--color-text-brand); - } - &:focus:user-invalid, &:required:user-invalid, [data-has-error='true'] & { outline-color: var(--color-text-error); } + &:focus { + outline-color: var(--color-text-brand); + } + &:required:user-valid { outline-color: var(--color-text-success); } diff --git a/app/javascript/mastodon/components/form_fields/text_input_field.stories.tsx b/app/javascript/mastodon/components/form_fields/text_input_field.stories.tsx index 8e8d7e99230f99..702597a0c1de76 100644 --- a/app/javascript/mastodon/components/form_fields/text_input_field.stories.tsx +++ b/app/javascript/mastodon/components/form_fields/text_input_field.stories.tsx @@ -40,7 +40,17 @@ export const Optional: Story = { export const WithError: Story = { args: { required: false, - hasError: true, + status: 'error', + }, +}; + +export const WithWarning: Story = { + args: { + required: false, + status: { + variant: 'warning', + message: 'Special characters are not allowed', + }, }, }; diff --git a/app/javascript/mastodon/components/form_fields/text_input_field.tsx b/app/javascript/mastodon/components/form_fields/text_input_field.tsx index f23a5da62f0e9f..d7d07833d360ae 100644 --- a/app/javascript/mastodon/components/form_fields/text_input_field.tsx +++ b/app/javascript/mastodon/components/form_fields/text_input_field.tsx @@ -25,14 +25,14 @@ interface Props extends TextInputProps, CommonFieldWrapperProps {} export const TextInputField = forwardRef( ( - { id, label, hint, hasError, required, wrapperClassName, ...otherProps }, + { id, label, hint, status, required, wrapperClassName, ...otherProps }, ref, ) => ( diff --git a/app/javascript/mastodon/components/form_fields/toggle_field.stories.tsx b/app/javascript/mastodon/components/form_fields/toggle_field.stories.tsx index 924c18aa74c91e..295600a3fde62d 100644 --- a/app/javascript/mastodon/components/form_fields/toggle_field.stories.tsx +++ b/app/javascript/mastodon/components/form_fields/toggle_field.stories.tsx @@ -45,7 +45,7 @@ export const Optional: Story = { export const WithError: Story = { args: { required: false, - hasError: true, + status: 'error', }, }; diff --git a/app/javascript/mastodon/components/form_fields/toggle_field.tsx b/app/javascript/mastodon/components/form_fields/toggle_field.tsx index 6cafbcdc3602d1..75fdb8f21bd770 100644 --- a/app/javascript/mastodon/components/form_fields/toggle_field.tsx +++ b/app/javascript/mastodon/components/form_fields/toggle_field.tsx @@ -14,12 +14,12 @@ type Props = Omit, 'type'> & { export const ToggleField = forwardRef< HTMLInputElement, Props & CommonFieldWrapperProps ->(({ id, label, hint, hasError, required, ...otherProps }, ref) => ( +>(({ id, label, hint, status, required, ...otherProps }, ref) => ( diff --git a/app/javascript/mastodon/features/account_timeline/modals/note_modal.tsx b/app/javascript/mastodon/features/account_timeline/modals/note_modal.tsx index 45fe4d7105d59e..d108a14fd695a4 100644 --- a/app/javascript/mastodon/features/account_timeline/modals/note_modal.tsx +++ b/app/javascript/mastodon/features/account_timeline/modals/note_modal.tsx @@ -141,8 +141,11 @@ const InnerNodeModal: FC<{ onChange={handleChange} label={intl.formatMessage(messages.fieldLabel)} className={classes.noteInput} - hasError={state === 'error'} - hint={errorText} + status={ + state === 'error' + ? { variant: 'error', message: errorText } + : undefined + } // eslint-disable-next-line jsx-a11y/no-autofocus -- We want to focus here as it's a modal. autoFocus /> diff --git a/app/javascript/mastodon/features/collections/editor/details.tsx b/app/javascript/mastodon/features/collections/editor/details.tsx index f59bd4de51b0ca..f4891cad7011dc 100644 --- a/app/javascript/mastodon/features/collections/editor/details.tsx +++ b/app/javascript/mastodon/features/collections/editor/details.tsx @@ -1,12 +1,15 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; import { isFulfilled } from '@reduxjs/toolkit'; -import { inputToHashtag } from '@/mastodon/utils/hashtags'; +import { + hasSpecialCharacters, + inputToHashtag, +} from '@/mastodon/utils/hashtags'; import type { ApiCreateCollectionPayload, ApiUpdateCollectionPayload, @@ -31,6 +34,7 @@ import classes from './styles.module.scss'; import { WizardStepHeader } from './wizard_step_header'; export const CollectionDetails: React.FC = () => { + const intl = useIntl(); const dispatch = useAppDispatch(); const history = useHistory(); const { id, name, description, topic, discoverable, sensitive, accountIds } = @@ -152,6 +156,11 @@ export const CollectionDetails: React.FC = () => { ], ); + const topicHasSpecialCharacters = useMemo( + () => hasSpecialCharacters(topic), + [topic], + ); + return (
@@ -224,6 +233,18 @@ export const CollectionDetails: React.FC = () => { autoCorrect='off' spellCheck='false' maxLength={40} + status={ + topicHasSpecialCharacters + ? { + variant: 'warning', + message: intl.formatMessage({ + id: 'collections.topic_special_chars_hint', + defaultMessage: + 'Special characters will be removed when saving', + }), + } + : undefined + } />
@@ -252,7 +252,7 @@ export const Profile: React.FC<{ } value={note} onChange={handleNoteChange} - hasError={!!errors?.note} + status={errors?.note ? 'error' : undefined} id='note' />
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 7567c489c260cd..db5c1313a549ef 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "You have added the maximum number of accounts", "collections.sensitive": "Sensitive", "collections.topic_hint": "Add a hashtag that helps others understand the main topic of this collection.", + "collections.topic_special_chars_hint": "Special characters will be removed when saving", "collections.view_collection": "View collection", "collections.view_other_collections_by_user": "View other collections by this user", "collections.visibility_public": "Public", diff --git a/app/javascript/mastodon/reducers/slices/collections.ts b/app/javascript/mastodon/reducers/slices/collections.ts index a534a134404811..47bbd5e6ba88de 100644 --- a/app/javascript/mastodon/reducers/slices/collections.ts +++ b/app/javascript/mastodon/reducers/slices/collections.ts @@ -23,6 +23,7 @@ import { createAppSelector, createDataLoadingThunk, } from '@/mastodon/store/typed_functions'; +import { inputToHashtag } from '@/mastodon/utils/hashtags'; type QueryStatus = 'idle' | 'loading' | 'error'; @@ -82,7 +83,7 @@ const collectionSlice = createSlice({ id: collection?.id ?? null, name: collection?.name ?? '', description: collection?.description ?? '', - topic: collection?.tag?.name ?? '', + topic: inputToHashtag(collection?.tag?.name ?? ''), language: collection?.language ?? '', discoverable: collection?.discoverable ?? true, sensitive: collection?.sensitive ?? false, diff --git a/app/javascript/mastodon/utils/hashtags.ts b/app/javascript/mastodon/utils/hashtags.ts index d14efe5db33e63..ff90a884653762 100644 --- a/app/javascript/mastodon/utils/hashtags.ts +++ b/app/javascript/mastodon/utils/hashtags.ts @@ -59,3 +59,8 @@ export const inputToHashtag = (input: string): string => { return `#${words.join('')}${trailingSpace}`; }; + +export const hasSpecialCharacters = (input: string) => { + // Regex matches any character NOT a letter/digit, except the # + return /[^a-zA-Z0-9# ]/.test(input); +}; From 91407ecc150eec9400f8685ec46fadd866cb68fb Mon Sep 17 00:00:00 2001 From: Echo Date: Fri, 13 Mar 2026 15:21:39 +0100 Subject: [PATCH 062/203] Fix incorrect SASS addition (#38194) --- .../features/account_timeline/components/redesign.module.scss | 4 ++-- app/javascript/styles/mastodon/components.scss | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss index 0d9036265a257d..e761c761d9dbd6 100644 --- a/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss +++ b/app/javascript/mastodon/features/account_timeline/components/redesign.module.scss @@ -107,7 +107,7 @@ } $button-breakpoint: 420px; -$button-fallback-breakpoint: #{$button-breakpoint} + 55px; +$button-fallback-breakpoint: $button-breakpoint + 55px; .buttonsDesktop { @container (width < #{$button-breakpoint}) { @@ -132,7 +132,7 @@ $button-fallback-breakpoint: #{$button-breakpoint} + 55px; } @supports (not (container-type: inline-size)) { - @media (min-width: (#{$button-fallback-breakpoint} + 1px)) { + @media (min-width: ($button-fallback-breakpoint + 1px)) { display: none; } } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index a89c9cca5cb2b0..765bf4dcf62ea5 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -8478,7 +8478,7 @@ noscript { gap: 8px; $button-breakpoint: 420px; - $button-fallback-breakpoint: #{$button-breakpoint} + 55px; + $button-fallback-breakpoint: $button-breakpoint + 55px; &--desktop { margin-top: 55px; @@ -8502,7 +8502,7 @@ noscript { } @supports (not (container-type: inline-size)) { - @media (min-width: (#{$button-fallback-breakpoint} + 1px)) { + @media (min-width: ($button-fallback-breakpoint + 1px)) { display: none; } } From d26269d68b0110ddb05a3f9ff221d2619db937e6 Mon Sep 17 00:00:00 2001 From: Echo Date: Fri, 13 Mar 2026 15:28:39 +0100 Subject: [PATCH 063/203] Adds a range selector component (#38191) --- .../form_fields/range_input.module.scss | 128 ++++++++++++++++++ .../form_fields/range_input_field.stories.tsx | 32 +++++ .../form_fields/range_input_field.tsx | 86 ++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 app/javascript/mastodon/components/form_fields/range_input.module.scss create mode 100644 app/javascript/mastodon/components/form_fields/range_input_field.stories.tsx create mode 100644 app/javascript/mastodon/components/form_fields/range_input_field.tsx diff --git a/app/javascript/mastodon/components/form_fields/range_input.module.scss b/app/javascript/mastodon/components/form_fields/range_input.module.scss new file mode 100644 index 00000000000000..cbace07dcc4a88 --- /dev/null +++ b/app/javascript/mastodon/components/form_fields/range_input.module.scss @@ -0,0 +1,128 @@ +/* + Inspired by: + https://danielstern.ca/range.css + https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/ +*/ + +.input { + --color-bg-thumb: var(--color-bg-brand-base); + --color-bg-thumb-hover: var(--color-bg-brand-base-hover); + --color-bg-track: var(--color-bg-secondary); + + width: 100%; + margin: 6px 0; + background-color: transparent; + appearance: none; + + &:focus { + outline: none; + } + + // Thumb + + &::-webkit-slider-thumb { + margin-top: -6px; + width: 16px; + height: 16px; + background: var(--color-bg-thumb); + border: 0; + border-radius: 50px; + cursor: pointer; + -webkit-appearance: none; + } + + &::-moz-range-thumb { + width: 16px; + height: 16px; + background: var(--color-bg-thumb); + border: 0; + border-radius: 50px; + cursor: pointer; + } + + &::-ms-thumb { + width: 16px; + height: 16px; + background: var(--color-bg-thumb); + border: 0; + border-radius: 50px; + cursor: pointer; + margin-top: 0; // Needed to keep the Edge thumb centred + } + + &:focus, + &:hover { + &::-webkit-slider-thumb { + background: var(--color-bg-thumb-hover); + } + + &::-moz-range-thumb { + background: var(--color-bg-thumb-hover); + } + + &::-ms-thumb { + background: var(--color-bg-thumb-hover); + } + } + + &:focus-visible { + &::-webkit-slider-thumb { + outline: var(--outline-focus-default); + outline-offset: 2px; + } + + &::-moz-range-thumb { + outline: var(--outline-focus-default); + outline-offset: 2px; + } + + &::-ms-thumb { + outline: var(--outline-focus-default); + outline-offset: 2px; + } + } + + // Track + + &::-webkit-slider-runnable-track { + background: var(--color-bg-track); + border: 0; + border-radius: 1.3px; + width: 100%; + height: 4px; + cursor: pointer; + } + + &::-moz-range-track { + background: var(--color-bg-track); + border: 0; + border-radius: 1.3px; + width: 100%; + height: 4px; + cursor: pointer; + } + + &::-ms-track { + background: var(--color-bg-track); + border: 0; + color: transparent; + width: 100%; + height: 4px; + cursor: pointer; + } +} + +.markers { + display: flex; + flex-direction: column; + justify-content: space-between; + writing-mode: vertical-lr; + width: 100%; + font-size: 11px; + color: var(--color-text-secondary); + user-select: none; + + option { + padding: 0; + } +} diff --git a/app/javascript/mastodon/components/form_fields/range_input_field.stories.tsx b/app/javascript/mastodon/components/form_fields/range_input_field.stories.tsx new file mode 100644 index 00000000000000..672228ab8c0795 --- /dev/null +++ b/app/javascript/mastodon/components/form_fields/range_input_field.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { RangeInputField } from './range_input_field'; + +const meta = { + title: 'Components/Form Fields/RangeInputField', + component: RangeInputField, + args: { + label: 'Label', + hint: 'This is a description of this form field', + checked: false, + disabled: false, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Simple: Story = {}; + +export const Markers: Story = { + args: { + markers: [ + { value: 0, label: 'None' }, + { value: 25, label: 'Some' }, + { value: 50, label: 'Half' }, + { value: 75, label: 'Most' }, + { value: 100, label: 'All' }, + ], + }, +}; diff --git a/app/javascript/mastodon/components/form_fields/range_input_field.tsx b/app/javascript/mastodon/components/form_fields/range_input_field.tsx new file mode 100644 index 00000000000000..8fb2620339da69 --- /dev/null +++ b/app/javascript/mastodon/components/form_fields/range_input_field.tsx @@ -0,0 +1,86 @@ +import type { ComponentPropsWithoutRef } from 'react'; +import { forwardRef, useId } from 'react'; + +import classNames from 'classnames'; + +import { FormFieldWrapper } from './form_field_wrapper'; +import type { CommonFieldWrapperProps } from './form_field_wrapper'; +import classes from './range_input.module.scss'; + +export type RangeInputProps = Omit< + ComponentPropsWithoutRef<'input'>, + 'type' | 'list' +> & { + markers?: { value: number; label: string }[] | number[]; +}; + +interface Props extends RangeInputProps, CommonFieldWrapperProps {} + +/** + * A simple form field for single-line text. + * + * Accepts an optional `hint` and can be marked as required + * or optional (by explicitly setting `required={false}`) + */ + +export const RangeInputField = forwardRef( + ( + { id, label, hint, status, required, wrapperClassName, ...otherProps }, + ref, + ) => ( + + {(inputProps) => } + + ), +); + +RangeInputField.displayName = 'RangeInputField'; + +export const RangeInput = forwardRef( + ({ className, markers, id, ...otherProps }, ref) => { + const markersId = useId(); + + if (!markers) { + return ( + + ); + } + return ( + <> + + + {markers.map((marker) => { + const value = typeof marker === 'number' ? marker : marker.value; + return ( + + + ); + }, +); + +RangeInput.displayName = 'RangeInput'; From ec0e78f128a12c5980bf04c870a3ca43a7b5cf5d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 13 Mar 2026 12:54:57 -0400 Subject: [PATCH 064/203] Add coverage for rewrite with vary username paths (#38197) --- spec/requests/username_rewrites_spec.rb | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 spec/requests/username_rewrites_spec.rb diff --git a/spec/requests/username_rewrites_spec.rb b/spec/requests/username_rewrites_spec.rb new file mode 100644 index 00000000000000..9e025a5f0dbafe --- /dev/null +++ b/spec/requests/username_rewrites_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Username URL rewrites' do + describe 'GET /users/:username' do + it 'redirects to at-username page variation' do + get '/users/username' + + expect(response) + .to have_http_status(301) + .and redirect_to('/@username') + expect(response.headers) + .to include('Vary' => 'Origin, Accept') + end + end + + describe 'GET /users/:username/following' do + it 'redirects to at-username page variation' do + get '/users/username/following' + + expect(response) + .to have_http_status(301) + .and redirect_to('/@username/following') + expect(response.headers) + .to include('Vary' => 'Origin, Accept') + end + end + + describe 'GET /users/:username/followers' do + it 'redirects to at-username page variation' do + get '/users/username/followers' + + expect(response) + .to have_http_status(301) + .and redirect_to('/@username/followers') + expect(response.headers) + .to include('Vary' => 'Origin, Accept') + end + end + + describe 'GET /users/:username/statuses/:id' do + it 'redirects to at-username page variation' do + get '/users/username/statuses/123456' + + expect(response) + .to have_http_status(301) + .and redirect_to('/@username/123456') + expect(response.headers) + .to include('Vary' => 'Origin, Accept') + end + end +end From 89b7a3d7fb73ba79d33cab9755424b4aa2050b1b Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 13 Mar 2026 18:14:04 +0100 Subject: [PATCH 065/203] A11y improvements on login & settings pages (#38188) --- app/helpers/branding_helper.rb | 7 ++- .../styles/mastodon/containers.scss | 34 +++++++------- app/javascript/styles/mastodon/forms.scss | 46 +++++++++++++++---- app/views/auth/sessions/new.html.haml | 3 ++ app/views/layouts/auth.html.haml | 9 ++-- 5 files changed, 66 insertions(+), 33 deletions(-) diff --git a/app/helpers/branding_helper.rb b/app/helpers/branding_helper.rb index 8201f36e3c2d1b..ef6d33ae5ce3d4 100644 --- a/app/helpers/branding_helper.rb +++ b/app/helpers/branding_helper.rb @@ -11,11 +11,14 @@ def logo_as_symbol(version = :icon) end def _logo_as_symbol_wordmark - content_tag(:svg, tag.use(href: '#logo-symbol-wordmark'), viewBox: '0 0 261 66', class: 'logo logo--wordmark') + tag.svg(viewBox: '0 0 261 66', class: 'logo logo--wordmark') do + tag.title('Mastodon') + + tag.use(href: '#logo-symbol-wordmark') + end end def _logo_as_symbol_icon - content_tag(:svg, tag.use(href: '#logo-symbol-icon'), viewBox: '0 0 79 79', class: 'logo logo--icon') + tag.svg(tag.use(href: '#logo-symbol-icon'), viewBox: '0 0 79 79', class: 'logo logo--icon') end def render_logo diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index 57c62a29e3579b..5e199273e03b78 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -13,28 +13,28 @@ .logo-container { margin: 50px auto; - h1 { + a { display: flex; justify-content: center; align-items: center; - - .logo { - height: 42px; - margin-inline-end: 10px; + width: min-content; + margin: 0 auto; + padding: 12px 16px; + color: var(--color-text-primary); + text-decoration: none; + outline: 0; + line-height: 32px; + font-weight: 500; + font-size: 14px; + + &:focus-visible { + outline: var(--outline-focus-default); } + } - a { - display: flex; - justify-content: center; - align-items: center; - color: var(--color-text-primary); - text-decoration: none; - outline: 0; - padding: 12px 16px; - line-height: 32px; - font-weight: 500; - font-size: 14px; - } + .logo { + height: 42px; + margin-inline-end: 10px; } } diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 3305d1c3cd0c95..c25bdfc8206718 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -77,7 +77,6 @@ code { .input { margin-bottom: 16px; - overflow: hidden; &:last-child { margin-bottom: 0; @@ -471,13 +470,19 @@ code { } } - .input.radio_buttons .radio label { - margin-bottom: 5px; - font-family: inherit; - font-size: 14px; - color: var(--color-text-primary); - display: block; - width: auto; + .input.radio_buttons .radio { + label { + margin-bottom: 5px; + font-family: inherit; + font-size: 14px; + color: var(--color-text-primary); + display: block; + width: auto; + } + + input[type='radio'] { + accent-color: var(--color-text-brand); + } } .check_boxes { @@ -503,6 +508,12 @@ code { } } + label.checkbox { + input[type='checkbox'] { + accent-color: var(--color-text-brand); + } + } + .input.static .label_input__wrapper { font-size: 14px; padding: 10px; @@ -523,13 +534,20 @@ code { color: var(--color-text-primary); display: block; width: 100%; - outline: 0; font-family: inherit; resize: vertical; background: var(--color-bg-secondary); border: 1px solid var(--color-border-primary); border-radius: 4px; padding: 10px 16px; + outline: var(--outline-focus-default); + outline-offset: -2px; + outline-color: transparent; + transition: outline-color 0.15s ease-out; + + &:focus { + outline: var(--outline-focus-default); + } &:invalid { box-shadow: none; @@ -613,6 +631,11 @@ code { margin-inline-end: 0; } + &:focus-visible { + outline: var(--outline-focus-default); + outline-offset: 2px; + } + &:active, &:focus, &:hover { @@ -653,6 +676,11 @@ code { padding-inline-end: 30px; height: 41px; + &:focus-visible { + outline: var(--outline-focus-default); + outline-offset: 2px; + } + @media screen and (width <= 600px) { font-size: 16px; } diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml index 67b958d38c167e..91505820d433fa 100644 --- a/app/views/auth/sessions/new.html.haml +++ b/app/views/auth/sessions/new.html.haml @@ -12,6 +12,7 @@ - if use_seamless_external_login? = f.input :email, autofocus: true, + required: true, hint: false, input_html: { 'aria-label': t('simple_form.labels.defaults.username_or_email') }, label: t('simple_form.labels.defaults.username_or_email'), @@ -19,12 +20,14 @@ - else = f.input :email, autofocus: true, + required: true, hint: false, input_html: { 'aria-label': t('simple_form.labels.defaults.email') }, label: t('simple_form.labels.defaults.email'), wrapper: :with_label .fields-group = f.input :password, + required: true, hint: false, input_html: { 'aria-label': t('simple_form.labels.defaults.password'), autocomplete: 'current-password' }, label: t('simple_form.labels.defaults.password'), diff --git a/app/views/layouts/auth.html.haml b/app/views/layouts/auth.html.haml index bdbcc84e1f3d21..c989e6ccd86a44 100644 --- a/app/views/layouts/auth.html.haml +++ b/app/views/layouts/auth.html.haml @@ -4,12 +4,11 @@ - content_for :content do .container-alt .logo-container - %h1 - - if within_authorization_flow? + - if within_authorization_flow? + = logo_as_symbol(:wordmark) + - else + = link_to root_path do = logo_as_symbol(:wordmark) - - else - = link_to root_path do - = logo_as_symbol(:wordmark) .form-container = render 'flashes' From 10624ecce6abe9d7e82a03599aabdf8ba622d3b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:11:06 +0100 Subject: [PATCH 066/203] Update dependency oj to v3.16.16 (#38186) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1c1f0aac1b89c5..1ed47d38fff534 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -473,7 +473,7 @@ GEM nokogiri (1.19.1) mini_portile2 (~> 2.8.2) racc (~> 1.4) - oj (3.16.15) + oj (3.16.16) bigdecimal (>= 3.0) ostruct (>= 0.2) omniauth (2.1.4) From e7cb3af15f37395ecee0a9bd88fe55051b63aeab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:11:11 +0100 Subject: [PATCH 067/203] Update dependency aws-sdk-s3 to v1.216.0 (#38187) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1ed47d38fff534..f4ab61acf73b95 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,7 @@ GEM ast (2.4.3) attr_required (1.0.2) aws-eventstream (1.4.0) - aws-partitions (1.1223.0) + aws-partitions (1.1225.0) aws-sdk-core (3.243.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -111,7 +111,7 @@ GEM aws-sdk-kms (1.122.0) aws-sdk-core (~> 3, >= 3.241.4) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.215.0) + aws-sdk-s3 (1.216.0) aws-sdk-core (~> 3, >= 3.243.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) From a9c248f14f50853b90cb99c9f2e6de42bd142a37 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 04:11:32 -0400 Subject: [PATCH 068/203] Limit email domain block variant query to unique values (#38196) --- app/models/email_domain_block.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb index d52c76b0300644..c4ab8b9ed27643 100644 --- a/app/models/email_domain_block.rb +++ b/app/models/email_domain_block.rb @@ -70,7 +70,7 @@ def domains_with_variants segments = uri.normalized_host.split('.') segments.map.with_index { |_, i| segments[i..].join('.') } - end + end.uniq end def extract_uris(domain_or_domains) From 5b6b86c97cea9f512376f9baa16aa7a1aae5edb1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 08:26:09 +0000 Subject: [PATCH 069/203] New Crowdin Translations (automated) (#38201) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/cy.json | 29 ++++ app/javascript/mastodon/locales/da.json | 1 + app/javascript/mastodon/locales/de.json | 1 + app/javascript/mastodon/locales/el.json | 1 + app/javascript/mastodon/locales/en-GB.json | 1 + app/javascript/mastodon/locales/es-AR.json | 1 + app/javascript/mastodon/locales/es-MX.json | 1 + app/javascript/mastodon/locales/es.json | 1 + app/javascript/mastodon/locales/fi.json | 1 + app/javascript/mastodon/locales/fr-CA.json | 1 + app/javascript/mastodon/locales/fr.json | 1 + app/javascript/mastodon/locales/gl.json | 1 + app/javascript/mastodon/locales/he.json | 1 + app/javascript/mastodon/locales/is.json | 1 + app/javascript/mastodon/locales/it.json | 1 + app/javascript/mastodon/locales/ko.json | 1 + app/javascript/mastodon/locales/nan-TW.json | 26 +++- app/javascript/mastodon/locales/nl.json | 10 ++ app/javascript/mastodon/locales/nn.json | 9 ++ app/javascript/mastodon/locales/no.json | 141 +++++++++++++++++++- app/javascript/mastodon/locales/oc.json | 37 ++++- app/javascript/mastodon/locales/sq.json | 1 + app/javascript/mastodon/locales/th.json | 28 ++++ app/javascript/mastodon/locales/vi.json | 1 + app/javascript/mastodon/locales/zh-CN.json | 1 + app/javascript/mastodon/locales/zh-TW.json | 1 + config/locales/devise.no.yml | 1 + config/locales/doorkeeper.no.yml | 2 + config/locales/nan-TW.yml | 42 ++++++ config/locales/no.yml | 8 ++ config/locales/th.yml | 5 + 31 files changed, 346 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index dbbe7ae181083e..48bda093f97f26 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -153,6 +153,7 @@ "account_edit.column_title": "Golygu Proffil", "account_edit.custom_fields.name": "maes", "account_edit.custom_fields.placeholder": "Ychwanegwch eich rhagenwau, dolenni allanol, neu unrhyw beth arall hoffech ei rannu.", + "account_edit.custom_fields.reorder_button": "Ail-drefnu meysydd", "account_edit.custom_fields.tip_content": "Gallwch chi ychwanegu hygrededd at eich cyfrif Mastodon yn hawdd trwy wirio dolenni i unrhyw wefannau rydych chi'n berchen arnyn nhw.", "account_edit.custom_fields.tip_title": "Awgrym: Ychwanegu dolenni wedi'u gwirio", "account_edit.custom_fields.title": "Meysydd cyfaddas", @@ -167,8 +168,26 @@ "account_edit.field_delete_modal.title": "Dileu maes cyfaddas?", "account_edit.field_edit_modal.add_title": "Ychwanegu maes cyfaddas", "account_edit.field_edit_modal.edit_title": "Golygu maes cyfaddas", + "account_edit.field_edit_modal.limit_header": "Wedi mynd dros y terfyn nodau sy'n cael eu hargymell", + "account_edit.field_edit_modal.limit_message": "Efallai na fydd defnyddwyr symudol yn gweld eich maes yn llawn.", + "account_edit.field_edit_modal.link_emoji_warning": "Rydym yn argymell yn erbyn defnyddio emoji personol ar y cyd ag URLau. Bydd meysydd personol sy'n cynnwys y ddau yn cael eu harddangos fel testun yn unig yn hytrach nag fel dolen, er mwyn atal dryswch ymhlith defnyddwyr.", "account_edit.field_edit_modal.name_hint": "e.e. “Gwefan bersonol”", + "account_edit.field_edit_modal.url_warning": "I ychwanegu dolen, cofiwch gynnwys {protocol} ar y dechrau.", + "account_edit.field_edit_modal.value_hint": "E.e. “https://example.me”", "account_edit.field_edit_modal.value_label": "Gwerth", + "account_edit.field_reorder_modal.drag_cancel": "Cafodd y llusgo ei ddiddymu. Cafodd y maes \"{item}\" ei ollwng.", + "account_edit.field_reorder_modal.drag_end": "Cafodd y maes \"{item}\" ei ollwng.", + "account_edit.field_reorder_modal.drag_instructions": "I aildrefnu meysydd cyfaddas, pwyswch y bylchwr neu enter. Wrth lusgo, defnyddiwch y bysellau saeth i symud y maes i fyny neu i lawr. Pwyswch y bylchwr neu enter eto i ollwng y maes yn ei safle newydd, neu pwyswch escape i'w ddiddymu.", + "account_edit.field_reorder_modal.drag_move": "Symudwyd y maes \"{item}\".", + "account_edit.field_reorder_modal.drag_over": "Symudwyd y maes \"{item}\" dros \"{over}\".", + "account_edit.field_reorder_modal.drag_start": "Wedi codi maes \"{item}\".", + "account_edit.field_reorder_modal.handle_label": "Llusgo'r maes \"{item}\"", + "account_edit.field_reorder_modal.title": "Aildrefnu meysydd", + "account_edit.image_edit.add_button": "Ychwanegu Delwedd", + "account_edit.image_edit.alt_add_button": "Ychwanegu testun amgen", + "account_edit.image_edit.alt_edit_button": "Golygu testun amgen", + "account_edit.image_edit.remove_button": "Tynnu delwedd", + "account_edit.image_edit.replace_button": "Amnewid delwedd", "account_edit.name_modal.add_title": "Ychwanegu enw dangos", "account_edit.name_modal.edit_title": "Golygu enw dangos", "account_edit.profile_tab.button_label": "Cyfaddasu", @@ -324,12 +343,14 @@ "collections.create_collection": "Creu casgliad", "collections.delete_collection": "Dileu casgliad", "collections.description_length_hint": "Terfyn o 100 nod", + "collections.detail.accept_inclusion": "Iawn", "collections.detail.accounts_heading": "Cyfrifon", "collections.detail.author_added_you": "Ychwanegodd {author} chi at y casgliad hwn", "collections.detail.curated_by_author": "Wedi'i guradu gan {author}", "collections.detail.curated_by_you": "Wedi'i guradu gennych chi", "collections.detail.loading": "Yn llwytho casgliad…", "collections.detail.other_accounts_in_collection": "Eraill yn y casgliad hwn:", + "collections.detail.revoke_inclusion": "Tynnu fi", "collections.detail.sensitive_note": "Mae'r casgliad hwn yn cynnwys cyfrifon a chynnwys a allai fod yn sensitif i rai defnyddwyr.", "collections.detail.share": "Rhannu'r casgliad hwn", "collections.edit_details": "Golygu manylion", @@ -345,6 +366,9 @@ "collections.old_last_post_note": "Postiwyd ddiwethaf dros wythnos yn ôl", "collections.remove_account": "Dileu'r cyfrif hwn", "collections.report_collection": "Adroddwch am y casgliad hwn", + "collections.revoke_collection_inclusion": "Tynnu fy hun o'r casgliad hwn", + "collections.revoke_inclusion.confirmation": "Rydych chi wedi cael eich tynnu o \"{collection}\"", + "collections.revoke_inclusion.error": "Bu gwall, ceisiwch eto yn nes ymlaen.", "collections.search_accounts_label": "Chwiliwch am gyfrifon i'w hychwanegu…", "collections.search_accounts_max_reached": "Rydych chi wedi ychwanegu'r nifer mwyaf o gyfrifon", "collections.sensitive": "Sensitif", @@ -468,6 +492,9 @@ "confirmations.remove_from_followers.confirm": "Dileu dilynwr", "confirmations.remove_from_followers.message": "Bydd {name} yn rhoi'r gorau i'ch dilyn. A ydych yn siŵr eich bod am fwrw ymlaen?", "confirmations.remove_from_followers.title": "Tynnu dilynwr?", + "confirmations.revoke_collection_inclusion.confirm": "Tynnu fi", + "confirmations.revoke_collection_inclusion.message": "Mae'r weithred hon yn barhaol, a fydd y curadur ddim yn gallu eich ail-ychwanegu at y casgliad yn ddiweddarach.", + "confirmations.revoke_collection_inclusion.title": "Tynnu eich hun o'r casgliad hwn?", "confirmations.revoke_quote.confirm": "Dileu'r postiad", "confirmations.revoke_quote.message": "Does dim modd dadwneud y weithred hon.", "confirmations.revoke_quote.title": "Dileu'r postiad?", @@ -938,12 +965,14 @@ "notifications_permission_banner.title": "Peidiwch â cholli dim", "onboarding.follows.back": "Nôl", "onboarding.follows.empty": "Yn anffodus, nid oes modd dangos unrhyw ganlyniadau ar hyn o bryd. Gallwch geisio defnyddio chwilio neu bori'r dudalen archwilio i ddod o hyd i bobl i'w dilyn, neu ceisio eto yn nes ymlaen.", + "onboarding.follows.next": "Nesaf: Gosodwch eich proffil", "onboarding.follows.search": "Chwilio", "onboarding.follows.title": "Dilynwch bobl i gychwyn arni", "onboarding.profile.discoverable": "Gwnewch fy mhroffil yn un y gellir ei ddarganfod", "onboarding.profile.discoverable_hint": "Pan fyddwch yn dewis ymuno â darganfod ar Mastodon, gall eich postiadau ymddangos mewn canlyniadau chwilio a threndiau, ac efallai y bydd eich proffil yn cael ei awgrymu i bobl sydd â diddordebau tebyg i chi.", "onboarding.profile.display_name": "Enw dangos", "onboarding.profile.display_name_hint": "Eich enw llawn neu'ch enw hwyl…", + "onboarding.profile.finish": "Gorffen", "onboarding.profile.note": "Bywgraffiad", "onboarding.profile.note_hint": "Gallwch @grybwyll pobl eraill neu #hashnodau…", "onboarding.profile.title": "Gosodiad proffil", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 7911a597b2390c..241873eea2197f 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Du har tilføjet det maksimale antal konti", "collections.sensitive": "Sensitivt", "collections.topic_hint": "Tilføj et hashtag, der hjælper andre med at forstå det overordnede emne for denne samling.", + "collections.topic_special_chars_hint": "Specialtegn fjernes, når der gemmes", "collections.view_collection": "Vis samling", "collections.view_other_collections_by_user": "Se andre samlinger af denne bruger", "collections.visibility_public": "Offentlig", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index cc9217dfddf837..a85d9e2d52d833 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Du hast die Höchstzahl an Konten hinzugefügt", "collections.sensitive": "Inhaltswarnung", "collections.topic_hint": "Ein Hashtag für diese Sammlung kann anderen dabei helfen, dein Anliegen besser einordnen zu können.", + "collections.topic_special_chars_hint": "Sonderzeichen werden beim Speichern entfernt", "collections.view_collection": "Sammlungen anzeigen", "collections.view_other_collections_by_user": "Andere Sammlungen dieses Kontos ansehen", "collections.visibility_public": "Öffentlich", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 4b33a257fd0a55..498efe3170b037 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Έχετε προσθέσει τον μέγιστο αριθμό λογαριασμών", "collections.sensitive": "Ευαίσθητο", "collections.topic_hint": "Προσθέστε μια ετικέτα που βοηθά άλλους να κατανοήσουν το κύριο θέμα αυτής της συλλογής.", + "collections.topic_special_chars_hint": "Οι ειδικοί χαρακτήρες θα αφαιρεθούν κατά την αποθήκευση", "collections.view_collection": "Προβολή συλλογής", "collections.view_other_collections_by_user": "Δείτε άλλες συλλογές από αυτόν τον χρήστη", "collections.visibility_public": "Δημόσια", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index abb0bcef2da58d..8e56d07168c3b5 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "You have added the maximum number of accounts", "collections.sensitive": "Sensitive", "collections.topic_hint": "Add a hashtag that helps others understand the main topic of this collection.", + "collections.topic_special_chars_hint": "Special characters will be removed when saving", "collections.view_collection": "View collection", "collections.view_other_collections_by_user": "View other collections by this user", "collections.visibility_public": "Public", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index d2edfca143db7c..495f4a5a0d6f56 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Agregaste el número máximo de cuentas", "collections.sensitive": "Sensible", "collections.topic_hint": "Agregá una etiqueta que ayude a otros usuarios a entender el tema principal de esta colección.", + "collections.topic_special_chars_hint": "Los símbolos se eliminarán al guardar", "collections.view_collection": "Abrir colección", "collections.view_other_collections_by_user": "Ver otras colecciones de este usuario", "collections.visibility_public": "Pública", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 9c6bd2e3700b82..eefc2e01db8061 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Has añadido el número máximo de cuentas", "collections.sensitive": "Sensible", "collections.topic_hint": "Añade una etiqueta que ayude a los demás a comprender el tema principal de esta colección.", + "collections.topic_special_chars_hint": "Los caracteres especiales se eliminarán al guardar", "collections.view_collection": "Ver colección", "collections.view_other_collections_by_user": "Ver otras colecciones de este usuario", "collections.visibility_public": "Pública", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index abf1791b0f5ddd..d0c59b2acf3961 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Has añadido el número máximo de cuentas", "collections.sensitive": "Sensible", "collections.topic_hint": "Añadir una etiqueta que ayude a otros a entender el tema principal de esta colección.", + "collections.topic_special_chars_hint": "Los caracteres especiales se eliminarán al guardar", "collections.view_collection": "Ver colección", "collections.view_other_collections_by_user": "Ver otras colecciones de este usuario", "collections.visibility_public": "Pública", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 04be6b3223356a..dfbe5087bd6e3e 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Olet lisännyt enimmäismäärän tilejä", "collections.sensitive": "Arkaluonteinen", "collections.topic_hint": "Lisää aihetunniste, joka auttaa muita ymmärtämään tämän kokoelman pääaiheen.", + "collections.topic_special_chars_hint": "Erikoismerkit poistetaan tallennettaessa", "collections.view_collection": "Näytä kokoelma", "collections.view_other_collections_by_user": "Näytä muut tämän käyttäjän kokoelmat", "collections.visibility_public": "Julkinen", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index ffbd596f251a29..ba2db8c8f3755a 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Vous avez ajouté le nombre maximum de comptes", "collections.sensitive": "Sensible", "collections.topic_hint": "Ajouter un hashtag pour aider les autres personnes à comprendre le sujet de la collection.", + "collections.topic_special_chars_hint": "Les caractères spéciaux seront supprimés lors de l'enregistrement", "collections.view_collection": "Voir la collection", "collections.view_other_collections_by_user": "Voir les autres collections par ce compte", "collections.visibility_public": "Publique", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index f1a8f511bbf3d1..a43f69d1924d31 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Vous avez ajouté le nombre maximum de comptes", "collections.sensitive": "Sensible", "collections.topic_hint": "Ajouter un hashtag pour aider les autres personnes à comprendre le sujet de la collection.", + "collections.topic_special_chars_hint": "Les caractères spéciaux seront supprimés lors de l'enregistrement", "collections.view_collection": "Voir la collection", "collections.view_other_collections_by_user": "Voir les autres collections par ce compte", "collections.visibility_public": "Publique", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 975c89e75a5e76..9e8889c53f824b 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Acadaches o máximo de contas permitidas", "collections.sensitive": "Sensible", "collections.topic_hint": "Engadir un cancelo para que axudar a que outras persoas coñezan a temática desta colección.", + "collections.topic_special_chars_hint": "Vanse eliminar os caracteres especiais ao gardar", "collections.view_collection": "Ver colección", "collections.view_other_collections_by_user": "Ver outras coleccións desta usuaria", "collections.visibility_public": "Pública", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 12adfb5ce23acd..34cfae819aabed 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "הגעת למספר החשבונות המירבי", "collections.sensitive": "רגיש", "collections.topic_hint": "הוספת תגית שמסייעת לאחרים להבין את הנושא הראשי של האוסף.", + "collections.topic_special_chars_hint": "תווים מיוחדים יוסרו בעת השמירה", "collections.view_collection": "צפיה באוסף", "collections.view_other_collections_by_user": "צפייה באוספים אחרים של משתמש.ת אלו", "collections.visibility_public": "פומבי", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 60ae1b6db699b6..d98649cf907256 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Þú hefur þegar bætt við leyfilegum hámarksfjölda aðganga", "collections.sensitive": "Viðkvæmt", "collections.topic_hint": "Bættu við myllumerki sem hjálpar öðrum að skilja aðalefni þessa safns.", + "collections.topic_special_chars_hint": "Sérstafir verða fjarlægðir við vistun", "collections.view_collection": "Skoða safn", "collections.view_other_collections_by_user": "Skoða önnur söfn frá þessum notanda", "collections.visibility_public": "Opinbert", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 8a675829d8ca9e..35ad6eaf105450 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Hai aggiunto il numero massimo di account", "collections.sensitive": "Sensibile", "collections.topic_hint": "Aggiungi un hashtag che aiuti gli altri a comprendere l'argomento principale di questa collezione.", + "collections.topic_special_chars_hint": "I caratteri speciali verranno rimossi durante il salvataggio", "collections.view_collection": "Visualizza la collezione", "collections.view_other_collections_by_user": "Visualizza altre collezioni da questo utente", "collections.visibility_public": "Pubblica", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 6737c4b2eed04e..05b75de2f9c684 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -85,6 +85,7 @@ "account.menu.copied": "계정 링크를 복사했습니다", "account.menu.copy": "링크 복사하기", "account.menu.mention": "멘션", + "account.menu.mute": "계정 뮤트", "account.menu.note.description": "나에게만 보입니다", "account.menu.open_original_page": "{domain}에서 보기", "account.menu.remove_follower": "팔로워 제거", diff --git a/app/javascript/mastodon/locales/nan-TW.json b/app/javascript/mastodon/locales/nan-TW.json index 3d108b00746dc9..ce52b991e3624e 100644 --- a/app/javascript/mastodon/locales/nan-TW.json +++ b/app/javascript/mastodon/locales/nan-TW.json @@ -173,6 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "Lán無建議佇URL內底用自訂ê emoji。為著避免用者舞花去,自訂ê框á若包含自訂emoji kap URL,kan-ta ē顯示做文字。", "account_edit.field_edit_modal.name_hint": "例:「個人網站」", "account_edit.field_edit_modal.name_label": "標簽", + "account_edit.field_edit_modal.url_warning": "若beh加連結,請佇起頭包含 {protocol}。", + "account_edit.field_edit_modal.value_hint": "例:「https://example.me」", "account_edit.field_edit_modal.value_label": "值", "account_edit.field_reorder_modal.drag_cancel": "Giú ê動作取消ah。框á「{item}」予lâng khǹg ah。", "account_edit.field_reorder_modal.drag_end": "框á「{item}」予lâng khǹg ah。", @@ -182,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "有揀框á「{item}」。", "account_edit.field_reorder_modal.handle_label": "Giú框á「{item}」", "account_edit.field_reorder_modal.title": "重整理框á", + "account_edit.image_edit.add_button": "加圖片", + "account_edit.image_edit.alt_add_button": "加添說明文字", + "account_edit.image_edit.alt_edit_button": "編說明文字", + "account_edit.image_edit.remove_button": "Suá掉圖片", + "account_edit.image_edit.replace_button": "取代圖片", "account_edit.name_modal.add_title": "加添顯示ê名", "account_edit.name_modal.edit_title": "編顯示ê名", "account_edit.profile_tab.button_label": "自訂", @@ -337,12 +344,14 @@ "collections.create_collection": "建立收藏", "collections.delete_collection": "Thâi掉收藏", "collections.description_length_hint": "限制 100 字", + "collections.detail.accept_inclusion": "OK", "collections.detail.accounts_heading": "口座", "collections.detail.author_added_you": "{author} kā lí加kàu tsit ê收藏", "collections.detail.curated_by_author": "{author} 揀ê", "collections.detail.curated_by_you": "Lí揀ê", "collections.detail.loading": "載入收藏……", "collections.detail.other_accounts_in_collection": "Tsit ê收藏內ê別lâng:", + "collections.detail.revoke_inclusion": "Kā我suá掉", "collections.detail.sensitive_note": "Tsit ê收藏包含對一寡用者敏感ê口座kap內容。", "collections.detail.share": "分享tsit ê收藏", "collections.edit_details": "編輯詳細", @@ -358,10 +367,14 @@ "collections.old_last_post_note": "頂改佇超過一禮拜進前PO文", "collections.remove_account": "Suá掉tsit ê口座", "collections.report_collection": "檢舉tsit ê收藏", + "collections.revoke_collection_inclusion": "Kā我對收藏內底suá掉", + "collections.revoke_inclusion.confirmation": "Lí已經對「{collection}」hőng suá掉", + "collections.revoke_inclusion.error": "出tshê ah,請小等leh koh試。", "collections.search_accounts_label": "Tshuē口座來加添……", "collections.search_accounts_max_reached": "Lí已經加kàu口座數ê盡磅ah。", "collections.sensitive": "敏感ê", "collections.topic_hint": "加 hashtag,幫tsān別lâng了解tsit ê收藏ê主題。", + "collections.topic_special_chars_hint": "儲存ê時陣ê suá掉特殊字元", "collections.view_collection": "看收藏", "collections.view_other_collections_by_user": "看tsit ê用者ê別ê收藏", "collections.visibility_public": "公共ê", @@ -481,6 +494,9 @@ "confirmations.remove_from_followers.confirm": "Suá掉跟tuè lí ê", "confirmations.remove_from_followers.message": "{name} ē停止跟tuè lí。Lí kám確定beh繼續?", "confirmations.remove_from_followers.title": "Kám beh suá掉跟tuè lí ê?", + "confirmations.revoke_collection_inclusion.confirm": "Kā我suá掉", + "confirmations.revoke_collection_inclusion.message": "Tsit ê行動永永有效,了後建立收藏ê bē當kā lí重加kàu收藏內底。", + "confirmations.revoke_collection_inclusion.title": "敢beh kā家kī對收藏內底suá掉?", "confirmations.revoke_quote.confirm": "Thâi掉PO文", "confirmations.revoke_quote.message": "Tsit ê動作bē當復原。", "confirmations.revoke_quote.title": "Kám beh thâi掉PO文?", @@ -840,8 +856,8 @@ "notification.annual_report.view": "Kā #Wrapstodon 看māi。", "notification.favourite": "{name} kah意lí ê PO文", "notification.favourite.name_and_others_with_link": "{name} kap{count, plural, other {另外 # ê lâng}}kah意lí ê PO文", - "notification.favourite_pm": "{name} kah意lí ê私人提起", - "notification.favourite_pm.name_and_others_with_link": "{name} kap{count, plural, other {另外 # ê lâng}}kah意lí ê私人提起", + "notification.favourite_pm": "{name} kah意lí ê私人ê提起", + "notification.favourite_pm.name_and_others_with_link": "{name} kap{count, plural, other {另外 # ê lâng}}kah意lí ê私人ê提起", "notification.follow": "{name}跟tuè lí", "notification.follow.name_and_others": "{name} kap{count, plural, other {另外 # ê lâng}}跟tuè lí", "notification.follow_request": "{name} 請求跟tuè lí", @@ -944,19 +960,21 @@ "notifications.policy.filter_not_following_hint": "直到lí手動允准in", "notifications.policy.filter_not_following_title": "Lí無跟tuè ê lâng", "notifications.policy.filter_private_mentions_hint": "通知ē受過濾,除非是tī lí ê提起ê回應內底,á是lí跟tuè送PO文ê lâng", - "notifications.policy.filter_private_mentions_title": "家kī直接送來ê私人提起", + "notifications.policy.filter_private_mentions_title": "家kī直接送來ê私人ê提起", "notifications.policy.title": "管理通知tuì……", "notifications_permission_banner.enable": "啟用桌面ê通知", "notifications_permission_banner.how_to_control": "Nā beh佇Mastodon關起來ê時陣收通知,請啟用桌面通知。若準啟用,Lí ē當通過面頂ê {icon} 鈕á,準準控制siánn物互動ê類型ē生桌面通知。", "notifications_permission_banner.title": "逐ê著看", "onboarding.follows.back": "轉去", "onboarding.follows.empty": "可惜,tsit-má無半條結果通顯示。Lí ē當試用tshiau-tshuē á是瀏覽探索ê頁,來tshuē beh跟tuè ê lâng,或者是sió等leh koh試。", + "onboarding.follows.next": "後一步:設定lí ê個人資料", "onboarding.follows.search": "Tshiau-tshuē", "onboarding.follows.title": "請跟tuè lâng來開始。", "onboarding.profile.discoverable": "Hōo我ê個人資料通tshuē著", "onboarding.profile.discoverable_hint": "Nā lí揀beh佇Mastodon開hōo lâng發現ê功能,lí ê PO文通顯示佇tshiau-tshuē結果kap趨勢,而且你ê個人資料可能ē推薦hōo kap lí有相siâng興趣ê別lâng。", "onboarding.profile.display_name": "顯示ê名", "onboarding.profile.display_name_hint": "Lí ê全名á是別號……", + "onboarding.profile.finish": "完成", "onboarding.profile.note": "個人紹介", "onboarding.profile.note_hint": "Lí ē當 @mention 別lâng á是用 #hashtag……", "onboarding.profile.title": "個人資料ê設定", @@ -1158,7 +1176,7 @@ "status.quote_error.limited_account_hint.title": "Tsit ê口座予 {domain} ê管理員tshàng起來ah。", "status.quote_error.muted_account_hint.title": "因為lí有消音 @{name},tsit篇PO文受khàm掉。", "status.quote_error.not_available": "PO文bē當看", - "status.quote_error.pending_approval": "PO文當咧送", + "status.quote_error.pending_approval": "PO文當leh送", "status.quote_error.pending_approval_popout.body": "佇Mastodon,lí ē當控制PO文kám beh hōo lâng引用。Tsit篇PO文teh等原文作者允准。", "status.quote_error.revoked": "PO文已經hōo作者thâi掉", "status.quote_followers_only": "Kan-ta tuè我ê ē當引用PO文", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index c45babcc0d0326..08469f32d26162 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -173,6 +173,8 @@ "account_edit.field_edit_modal.link_emoji_warning": "We raden aan om aangepaste emoji in combinatie met urls te gebruiken. Aangepaste velden die beide bevatten worden alleen als tekst weergegeven in plaats van als een link, om verwarring van de gebruiker te voorkomen.", "account_edit.field_edit_modal.name_hint": "Bijv. \"Persoonlijke website\"", "account_edit.field_edit_modal.name_label": "Label", + "account_edit.field_edit_modal.url_warning": "Voeg {protocol} toe aan het begin om een link toe te voegen.", + "account_edit.field_edit_modal.value_hint": "Bijv. \"https://example.me\"", "account_edit.field_edit_modal.value_label": "Waarde", "account_edit.field_reorder_modal.drag_cancel": "Slepen is geannuleerd. Veld \"{item}\" is weggevallen.", "account_edit.field_reorder_modal.drag_end": "Veld \"{item}\" was weggevallen.", @@ -182,6 +184,11 @@ "account_edit.field_reorder_modal.drag_start": "Opgepakt veld \"{item}\".", "account_edit.field_reorder_modal.handle_label": "Veld \"{item}\" slepen", "account_edit.field_reorder_modal.title": "Velden herschikken", + "account_edit.image_edit.add_button": "Afbeelding toevoegen", + "account_edit.image_edit.alt_add_button": "Alt-tekst toevoegen", + "account_edit.image_edit.alt_edit_button": "Alt-tekst bewerken", + "account_edit.image_edit.remove_button": "Afbeelding verwijderen", + "account_edit.image_edit.replace_button": "Afbeelding vervangen", "account_edit.name_modal.add_title": "Weergavenaam toevoegen", "account_edit.name_modal.edit_title": "Weergavenaam bewerken", "account_edit.profile_tab.button_label": "Aanpassen", @@ -367,6 +374,7 @@ "collections.search_accounts_max_reached": "Je hebt het maximum aantal accounts toegevoegd", "collections.sensitive": "Gevoelig", "collections.topic_hint": "Voeg een hashtag toe die anderen helpt het hoofdonderwerp van deze verzameling te begrijpen.", + "collections.topic_special_chars_hint": "Speciale tekens worden bij het opslaan verwijderd", "collections.view_collection": "Verzameling bekijken", "collections.view_other_collections_by_user": "Bekijk andere verzamelingen van deze gebruiker", "collections.visibility_public": "Openbaar", @@ -959,12 +967,14 @@ "notifications_permission_banner.title": "Mis nooit meer iets", "onboarding.follows.back": "Terug", "onboarding.follows.empty": "Helaas kunnen op dit moment geen resultaten worden getoond. Je kunt proberen te zoeken of op de verkenningspagina te bladeren om mensen te vinden die je kunt volgen, of probeer het later opnieuw.", + "onboarding.follows.next": "Volgende: stel je profiel in", "onboarding.follows.search": "Zoeken", "onboarding.follows.title": "Volg mensen om te beginnen", "onboarding.profile.discoverable": "Maak mijn profiel vindbaar", "onboarding.profile.discoverable_hint": "Wanneer je akkoord gaat met het vindbaar zijn op Mastodon, verschijnen je berichten in zoekresultaten en kunnen ze trending worden, en je profiel kan aan andere mensen worden aanbevolen wanneer ze vergelijkbare interesses hebben.", "onboarding.profile.display_name": "Weergavenaam", "onboarding.profile.display_name_hint": "Jouw volledige naam of een leuke bijnaam…", + "onboarding.profile.finish": "Voltooien", "onboarding.profile.note": "Biografie", "onboarding.profile.note_hint": "Je kunt andere mensen @vermelden of #hashtags gebruiken…", "onboarding.profile.title": "Profiel instellen", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index e44f7e52b936c1..9736f790cbcbd0 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -151,8 +151,13 @@ "account_edit.button.edit": "Rediger {item}", "account_edit.column_button": "Ferdig", "account_edit.column_title": "Rediger profil", + "account_edit.custom_fields.name": "felt", "account_edit.custom_fields.placeholder": "Legg til pronomen, lenkjer eller kva du elles vil dela.", + "account_edit.custom_fields.reorder_button": "Omorganiser felter", + "account_edit.custom_fields.tip_content": "Du kan enkelt øke troverdighet til Mastodon-kontoen din ved å verifisere koblinger til nettsider du eier.", + "account_edit.custom_fields.tip_title": "Legg til bekreftede lenker", "account_edit.custom_fields.title": "Eigne felt", + "account_edit.custom_fields.verified_hint": "Hvordan legger jeg til en verifisert lenke?", "account_edit.display_name.placeholder": "Det synlege namnet ditt er det som syner på profilen din og i tidsliner.", "account_edit.display_name.title": "Synleg namn", "account_edit.featured_hashtags.item": "emneknaggar", @@ -275,6 +280,8 @@ "callout.dismiss": "Avvis", "carousel.current": "Side {current, number} / {max, number}", "carousel.slide": "Side {current, number} av {max, number}", + "character_counter.recommended": "{currentLength}/{maxLength} anbefalte tegn", + "character_counter.required": "{currentLength}/{maxLength} tegn", "closed_registrations.other_server_instructions": "Sidan Mastodon er desentralisert kan du lage ein brukar på ein anna tenar og framleis interagere med denne.", "closed_registrations_modal.description": "Det er ikkje mogleg å opprette ein konto på {domain} nett no, men hugs at du ikkje treng ein konto på akkurat {domain} for å nytte Mastodon.", "closed_registrations_modal.find_another_server": "Finn ein annan tenar", @@ -445,6 +452,8 @@ "confirmations.remove_from_followers.confirm": "Fjern fylgjar", "confirmations.remove_from_followers.message": "{name} vil ikkje fylgja deg meir. Vil du halda fram?", "confirmations.remove_from_followers.title": "Fjern fylgjar?", + "confirmations.revoke_collection_inclusion.confirm": "Fjern meg", + "confirmations.revoke_collection_inclusion.title": "Fjern deg selv fra denne samlingen?", "confirmations.revoke_quote.confirm": "Fjern innlegget", "confirmations.revoke_quote.message": "Du kan ikkje angra denne handlinga.", "confirmations.revoke_quote.title": "Fjern innlegget?", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 2cd2341d8bc3d6..b0a6256b057e0b 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -4,19 +4,26 @@ "about.default_locale": "Standard", "about.disclaimer": "Mastodon er gratis, åpen kildekode-programvare og et varemerke fra Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Årsak ikke tilgjengelig", - "about.domain_blocks.preamble": "Mastodon lar deg normalt sett se innholdet fra og samhandle med brukere fra enhver annen tjener i fødiverset. Dette er unntakene som har blitt lagt inn på denne tjeneren.", - "about.domain_blocks.silenced.explanation": "Du vil vanligvis ikke se profiler og innhold fra denne tjeneren, med mindre du eksplisitt søker dem opp eller velger å følge dem.", + "about.domain_blocks.preamble": "Mastodon lar deg normalt sett se innholdet fra og samhandle med brukere fra enhver annen server i fødiverset. Dette er unntakene som har blitt lagt inn på denne serveren.", + "about.domain_blocks.silenced.explanation": "Du vil vanligvis ikke se profiler og innhold fra denne serveren, med mindre du eksplisitt søker dem opp eller velger å følge dem.", "about.domain_blocks.silenced.title": "Begrenset", - "about.domain_blocks.suspended.explanation": "Ikke noe innhold fra denne tjeneren vil bli behandlet, lagret eller utvekslet. Det gjør det umulig å samhandle eller kommunisere med brukere fra denne tjeneren.", + "about.domain_blocks.suspended.explanation": "Ikke noe innhold fra denne serveren vil bli behandlet, lagret eller utvekslet. Det gjør det umulig å samhandle eller kommunisere med brukere fra denne serveren.", "about.domain_blocks.suspended.title": "Suspendert", "about.language_label": "Språk", - "about.not_available": "Denne informasjonen er ikke gjort tilgjengelig på denne tjeneren.", - "about.powered_by": "Desentraliserte sosiale medier drevet av {mastodon}", + "about.not_available": "Denne informasjonen er ikke gjort tilgjengelig på denne serveren.", + "about.powered_by": "Desentralisert sosialt medie drevet av {mastodon}", "about.rules": "Regler for serveren", "account.account_note_header": "Personlig notat", + "account.activity": "Aktivitet", + "account.add_note": "Legg til et personlig notat", "account.add_or_remove_from_list": "Legg til eller fjern fra lister", + "account.badges.admin": "Administrator", + "account.badges.blocked": "Blokkert", "account.badges.bot": "Automatisert", + "account.badges.domain_blocked": "Blokkert domene", "account.badges.group": "Gruppe", + "account.badges.muted": "Dempet", + "account.badges.muted_until": "Dempet til {until}", "account.block": "Blokker @{name}", "account.block_domain": "Blokker domenet {domain}", "account.block_short": "Blokker", @@ -27,7 +34,9 @@ "account.direct": "Nevn @{name} privat", "account.disable_notifications": "Slutt å varsle meg når @{name} legger ut innlegg", "account.domain_blocking": "Blokkerer domene", + "account.edit_note": "Rediger personlig notat", "account.edit_profile": "Rediger profil", + "account.edit_profile_short": "Rediger", "account.enable_notifications": "Varsle meg når @{name} legger ut innlegg", "account.endorse": "Vis frem på profilen", "account.familiar_followers_many": "Fulgt av {name1}, {name2}, og {othersCount, plural, one {en annen du kjenner} other {# andre du kjenner}}", @@ -35,11 +44,22 @@ "account.familiar_followers_two": "Fulgt av {name1} og {name2}", "account.featured": "Utvalgt", "account.featured.accounts": "Profiler", + "account.featured.collections": "Samlinger", "account.featured.hashtags": "Emneknagger", "account.featured_tags.last_status_at": "Siste innlegg {date}", "account.featured_tags.last_status_never": "Ingen Innlegg", + "account.field_overflow": "Vis fullt innhold", + "account.filters.all": "All aktivitet", + "account.filters.boosts_toggle": "Vis fremhevinger", + "account.filters.posts_boosts": "Innlegg og fremhevinger", + "account.filters.posts_only": "Innlegg", + "account.filters.posts_replies": "Innlegg og svar", + "account.filters.replies_toggle": "Vis svar", "account.follow": "Følg", "account.follow_back": "Følg tilbake", + "account.follow_back_short": "Følg tilbake", + "account.follow_request_cancel": "Avbryt forespørsel", + "account.follow_request_cancel_short": "Avbryt", "account.followers": "Følgere", "account.followers.empty": "Ingen følger denne brukeren ennå.", "account.followers_counter": "{count, plural, one {{counter} følger} other {{counter} følgere}}", @@ -51,12 +71,31 @@ "account.go_to_profile": "Gå til profil", "account.hide_reblogs": "Skjul fremhevinger fra @{name}", "account.in_memoriam": "Til minne om.", + "account.joined_long": "Ble med den {date}", "account.joined_short": "Ble med", "account.languages": "Endre hvilke språk du abonnerer på", "account.link_verified_on": "Eierskap av denne lenken ble sjekket {date}", "account.locked_info": "Denne kontoens personvernstatus er satt til låst. Eieren vurderer manuelt hvem som kan følge dem.", "account.media": "Media", "account.mention": "Nevn @{name}", + "account.menu.add_to_list": "Legg til i liste…", + "account.menu.block": "Blokker kontor", + "account.menu.block_domain": "Blokker {domain}", + "account.menu.copied": "Kopierte kontolenke til utklippstavle", + "account.menu.copy": "Kopier lenke", + "account.menu.direct": "Nevn privat", + "account.menu.hide_reblogs": "Skjul fremhevinger i tidslinje", + "account.menu.mention": "Nevn", + "account.menu.mute": "Demp konto", + "account.menu.note.description": "Synlig bare for deg", + "account.menu.open_original_page": "Se på {domain}", + "account.menu.remove_follower": "Fjern følger", + "account.menu.report": "Rapporter konto", + "account.menu.share": "Del…", + "account.menu.show_reblogs": "Vis fremhevinger i tidslinje", + "account.menu.unblock": "Opphev blokkering av konto", + "account.menu.unblock_domain": "Opphev blokkering av {domain}", + "account.menu.unmute": "Opphev demping av konto", "account.moved_to": "{name} har angitt at deres nye konto nå er:", "account.mute": "Demp @{name}", "account.mute_notifications_short": "Demp varsler", @@ -64,7 +103,21 @@ "account.muted": "Dempet", "account.muting": "Demper", "account.mutual": "Dere følger hverandre", + "account.name.help.domain": "{domain} er serveren som inneholder profilen og innleggene for brukeren.", + "account.name.help.domain_self": "{domain} er serveren som inneholder profilen og innleggene dine.", + "account.name.help.header": "Et håndtak er som en e-postadresse", + "account.name.help.username": "{username} er denne kontoens brukernavn på deres server. Noen på en annen server kan ha det samme brukernavnet.", + "account.name.help.username_self": "{username} er ditt brukernavn på denne serveren. Noen på en annen server kan ha det samme brukernavnet.", + "account.name_info": "Hva betyr dette?", "account.no_bio": "Ingen beskrivelse oppgitt.", + "account.node_modal.callout": "Personlige notater er bare synlige for deg.", + "account.node_modal.edit_title": "Rediger personlig notat", + "account.node_modal.error_unknown": "Klarte ikke å lagre notatet", + "account.node_modal.field_label": "Personlig notat", + "account.node_modal.save": "Lagre", + "account.node_modal.title": "Legg til et personlig notat", + "account.note.edit_button": "Rediger", + "account.note.title": "Personlig notat (bare synlig for deg)", "account.open_original_page": "Gå til originalsiden", "account.posts": "Innlegg", "account.posts_with_replies": "Innlegg med svar", @@ -75,6 +128,8 @@ "account.share": "Del @{name} sin profil", "account.show_reblogs": "Vis fremhevinger fra @{name}", "account.statuses_counter": "{count, plural, one {{counter} innlegg} other {{counter} innlegg}}", + "account.timeline.pinned": "Festet", + "account.timeline.pinned.view_all": "Vis alle festede innlegg", "account.unblock": "Opphev blokkering av @{name}", "account.unblock_domain": "Opphev blokkering av {domain}", "account.unblock_domain_short": "Opphev blokkering", @@ -84,6 +139,19 @@ "account.unmute": "Opphev demping av @{name}", "account.unmute_notifications_short": "Opphev demping av varsler", "account.unmute_short": "Opphev demping", + "account_edit.bio.placeholder": "Legg til en kort introduksjon for å hjelpe andre med å identifisere deg.", + "account_edit.button.add": "Legg til {item}", + "account_edit.button.delete": "Slett {item}", + "account_edit.button.edit": "Rediger {item}", + "account_edit.column_button": "Ferdig", + "account_edit.column_title": "Rediger profil", + "account_edit.custom_fields.name": "felt", + "account_edit.custom_fields.reorder_button": "Omorganiser felter", + "account_edit.custom_fields.tip_content": "Du kan enkelt øke troverdighet til Mastodon-kontoen din ved å verifisere koblinger til nettsider du eier.", + "account_edit.custom_fields.tip_title": "Legg til bekreftede lenker", + "account_edit.custom_fields.title": "Egendefinerte felter", + "account_edit.custom_fields.verified_hint": "Hvordan legger jeg til en verifisert lenke?", + "account_edit.save": "Lagre", "account_note.placeholder": "Klikk for å legge til et notat", "admin.dashboard.daily_retention": "Andel brukere som er aktive, per dag etter registrering", "admin.dashboard.monthly_retention": "Andel brukere som er aktive, per måned etter registrering", @@ -107,6 +175,11 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Beskriv dette for folk med synsproblemer…", "alt_text_modal.done": "Ferdig", "announcement.announcement": "Kunngjøring", + "annual_report.announcement.action_build": "Lag min Wrapstodon", + "annual_report.announcement.action_dismiss": "Nei takk", + "annual_report.announcement.action_view": "Se Wrapstodon", + "annual_report.summary.close": "Lukk", + "annual_report.summary.copy_link": "Kopier lenke", "annual_report.summary.most_used_app.most_used_app": "mest brukte applikasjoner", "annual_report.summary.most_used_hashtag.most_used_hashtag": "mest brukte evne knagg", "annual_report.summary.new_posts.new_posts": "nye innlegg", @@ -137,11 +210,20 @@ "bundle_modal_error.close": "Lukk", "bundle_modal_error.message": "Noe gikk galt mens denne komponenten ble lastet inn.", "bundle_modal_error.retry": "Prøv igjen", + "carousel.current": "Side {current, number} / {max, number}", + "carousel.slide": "Side {current, number} av {max, number}", + "character_counter.recommended": "{currentLength}/{maxLength} anbefalte tegn", + "character_counter.required": "{currentLength}/{maxLength} tegn", "closed_registrations.other_server_instructions": "Siden Mastodon er desentralizert, kan du opprette en konto på en annen server og fortsatt kommunisere med denne.", "closed_registrations_modal.description": "Opprettelse av en konto på {domain} er for tiden ikke mulig, men vær oppmerksom på at du ikke trenger en konto spesifikt på {domain} for å kunne bruke Mastodon.", "closed_registrations_modal.find_another_server": "Finn en annen server", "closed_registrations_modal.preamble": "Mastodon er desentralisert, så uansett hvor du oppretter kontoen din, vil du kunne følge og samhandle med alle på denne serveren. Du kan til og med kjøre serveren selv!", "closed_registrations_modal.title": "Registrerer deg på Mastodon", + "collection.share_modal.share_link_label": "Invitasjonslenke for deling", + "collection.share_modal.share_via_post": "Publiser på Mastodon", + "collection.share_modal.share_via_system": "Del med…", + "collection.share_modal.title": "Del samling", + "collection.share_modal.title_new": "Del din nye samling!", "column.about": "Om", "column.blocks": "Blokkerte brukere", "column.bookmarks": "Bokmerker", @@ -206,6 +288,7 @@ "confirmations.delete_list.title": "Slett liste?", "confirmations.discard_edit_media.confirm": "Forkast", "confirmations.discard_edit_media.message": "Du har ulagrede endringer i mediebeskrivelsen eller i forhåndsvisning, forkast dem likevel?", + "confirmations.follow_to_collection.title": "Følg konto?", "confirmations.follow_to_list.confirm": "Følg og legg til i liste", "confirmations.follow_to_list.message": "Du må følge {name} for å kunne legge vedkommende til i en liste.", "confirmations.follow_to_list.title": "Følg bruker?", @@ -217,13 +300,29 @@ "confirmations.missing_alt_text.secondary": "Legg ut likevel", "confirmations.missing_alt_text.title": "Legg til bildebeskrivelse?", "confirmations.mute.confirm": "Demp", + "confirmations.private_quote_notify.cancel": "Tilbake til redigering", + "confirmations.private_quote_notify.confirm": "Publiser innlegg", + "confirmations.private_quote_notify.do_not_show_again": "Ikke vis meg denne meldingen igjen", + "confirmations.private_quote_notify.message": "Personen du siterer og andre som er nevnt vil bli varslet og vil kunne se innlegget ditt, selv om de ikke følger deg.", + "confirmations.private_quote_notify.title": "Del med følgere og nevnte brukere?", + "confirmations.quiet_post_quote_info.got_it": "Forstått", "confirmations.redraft.confirm": "Slett og skriv på nytt", "confirmations.redraft.message": "Er du sikker på at du vil slette dette innlegget og lagre det på nytt? Favoritter og fremhevinger vil gå tapt, og svar til det originale innlegget vil bli foreldreløse.", "confirmations.redraft.title": "Slett og skriv på nytt?", "confirmations.remove_from_followers.confirm": "Fjern følger", "confirmations.remove_from_followers.message": "{name} vil ikke lenger følge deg. Er du sikker på at du vil fortsette?", "confirmations.remove_from_followers.title": "Fjern følger?", + "confirmations.revoke_collection_inclusion.confirm": "Fjern meg", + "confirmations.revoke_collection_inclusion.title": "Fjern deg selv fra denne samlingen?", + "confirmations.revoke_quote.confirm": "Fjern innlegg", + "confirmations.revoke_quote.message": "Denne handlingen kan ikke angres.", + "confirmations.revoke_quote.title": "Fjern innlegg?", + "confirmations.unblock.confirm": "Opphev blokkering", + "confirmations.unblock.title": "Opphev blokkering av {name}?", "confirmations.unfollow.confirm": "Slutt å følge", + "confirmations.unfollow.title": "Slutt å følge {name}?", + "confirmations.withdraw_request.confirm": "Trekk tilbake forespørsel", + "confirmations.withdraw_request.title": "Trekk tilbake forespørsel om å følge {name}?", "content_warning.hide": "Skjul innlegg", "content_warning.show": "Vis likevel", "content_warning.show_more": "Vis mer", @@ -740,7 +839,9 @@ "report_notification.categories.spam": "Søppelpost", "report_notification.categories.spam_sentence": "spam", "report_notification.categories.violation": "Regelbrudd", + "report_notification.categories.violation_sentence": "regel brudd", "report_notification.open": "Åpne rapport", + "search.clear": "Tøm søk", "search.no_recent_searches": "Ingen søk nylig", "search.placeholder": "Søk", "search.quick_action.account_search": "Profiler som samsvarer med {x}", @@ -761,6 +862,7 @@ "search_results.all": "Alle", "search_results.hashtags": "Emneknagger", "search_results.no_results": "Ingen resultater.", + "search_results.no_search_yet": "Søk etter innlegg, profiler eller hashtags.", "search_results.see_all": "Se alle", "search_results.statuses": "Innlegg", "search_results.title": "Søk etter \"{q}\"", @@ -776,13 +878,23 @@ "status.admin_account": "Åpne moderatorgrensesnittet for @{name}", "status.admin_domain": "Åpne moderatorgrensesnittet for {domain}", "status.admin_status": "Åpne dette innlegget i moderatorgrensesnittet", + "status.all_disabled": "Fremheving og sitering er deaktivert", "status.block": "Blokker @{name}", "status.bookmark": "Bokmerke", "status.cancel_reblog_private": "Fjern fremheving", + "status.cannot_quote": "Du har ikke tilgang til å sitere dette innlegget", "status.cannot_reblog": "Denne posten kan ikke fremheves", + "status.contains_quote": "Inneholder sitat", + "status.context.loading": "Laster inn flere svar", + "status.context.loading_error": "Klarte ikke å laste inn nye svar", + "status.context.loading_success": "Nye svar er lastet inn", + "status.context.more_replies_found": "Flere svar funnet", + "status.context.retry": "Prøv igjen", + "status.context.show": "Vis", "status.continued_thread": "Fortsettelse av samtale", "status.copy": "Kopier lenken til innlegget", "status.delete": "Slett", + "status.delete.success": "Innlegget er slettet", "status.detailed_status": "Detaljert samtalevisning", "status.direct": "Nevn @{name} privat", "status.direct_indicator": "Privat omtale", @@ -803,17 +915,29 @@ "status.mute_conversation": "Demp samtale", "status.open": "Utvid dette innlegget", "status.pin": "Fest på profilen", + "status.quote_error.blocked_account_hint.title": "Dette innlegget er skjult fordi du har blokkert @{name}.", + "status.quote_error.blocked_domain_hint.title": "Dette innlegget er skjult fordi du har blokkert {domain}.", "status.quote_error.filtered": "Skjult på grunn av et av filterne dine", + "status.quote_error.limited_account_hint.action": "Vis likevel", + "status.quote_error.limited_account_hint.title": "Denne kontoen har blitt skjult av moderatorene til {domain}.", + "status.quote_error.muted_account_hint.title": "Dette innlegget er skjult fordi du har dempet @{name}.", + "status.quote_error.not_available": "Innlegg utilgjengelig", + "status.quote_error.pending_approval": "Ventende innlegg", "status.read_more": "Les mer", "status.reblog": "Fremhev", "status.reblogged_by": "Fremhevet av {name}", "status.reblogs.empty": "Ingen har fremhevet dette innlegget enda. Når noen gjør det, vil de dukke opp her.", "status.redraft": "Slett og skriv på nytt", "status.remove_bookmark": "Fjern bokmerke", + "status.remove_favourite": "Fjern fra favoritter", + "status.remove_quote": "Fjern", + "status.replied_in_thread": "Svarte i tråd", "status.replied_to": "Som svar til {name}", "status.reply": "Svar", "status.replyAll": "Svar til samtale", "status.report": "Rapporter @{name}", + "status.request_quote": "Send forespørsel om å sitere", + "status.revoke_quote": "Fjern innlegget mitt fra @{name}' innlegg", "status.sensitive_warning": "Følsomt innhold", "status.share": "Del", "status.show_less_all": "Vis mindre for alle", @@ -829,8 +953,13 @@ "subscribed_languages.save": "Lagre endringer", "subscribed_languages.target": "Endre abonnerte språk for {target}", "tabs_bar.home": "Hjem", + "tabs_bar.menu": "Meny", "tabs_bar.notifications": "Varslinger", + "tabs_bar.publish": "Nytt Innlegg", + "tabs_bar.search": "Søk", + "tag.remove": "Fjern", "terms_of_service.title": "Bruksvilkår", + "terms_of_service.upcoming_changes_on": "Kommende endringer på {date}", "time_remaining.days": "{number, plural,one {# dag} other {# dager}} igjen", "time_remaining.hours": "{number, plural, one {# time} other {# timer}} igjen", "time_remaining.minutes": "{number, plural, one {# minutt} other {# minutter}} igjen", @@ -846,6 +975,8 @@ "upload_button.label": "Legg til media", "upload_error.limit": "Filopplastingsgrensen er oversteget.", "upload_error.poll": "Filopplasting er ikke tillatt for avstemninger.", + "upload_error.quote": "Filopplasting er ikke tillatt med sitat.", + "upload_form.drag_and_drop.instructions": "For å plukke opp et medievedlegg, trykk på mellomrom eller enter. Bruk piltastene for å flytte medievedlegget i ønsket retning. Trykk mellomrom eller enter igjen for å slippe vedlegget på nytt sted, eller trykk på esc for å avbryte.", "upload_form.edit": "Rediger", "upload_progress.label": "Laster opp...", "upload_progress.processing": "Behandler…", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 9abbc0324d34a7..a00a2ab6c11404 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -11,6 +11,8 @@ "about.powered_by": "Malhum social descentralizat propulsat per {mastodon}", "about.rules": "Règlas del servidor", "account.account_note_header": "Nòta personala", + "account.activity": "Activitat", + "account.add_note": "Apondre una nòta personala", "account.add_or_remove_from_list": "Ajustar o tirar de las listas", "account.badges.bot": "Robòt", "account.badges.group": "Grop", @@ -51,6 +53,12 @@ "account.locked_info": "L’estatut de privacitat del compte es configurat sus clavat. Lo proprietari causís qual pòt sègre son compte.", "account.media": "Mèdias", "account.mention": "Mencionar @{name}", + "account.menu.mention": "Mencionar", + "account.menu.mute": "Silenciar lo compte", + "account.menu.note.description": "Sonque visibla per vos", + "account.menu.remove_follower": "Tirar lo seguidor", + "account.menu.report": "Senhalar lo compte", + "account.menu.share": "Partejar…", "account.moved_to": "{name} indiquèt que son nòu compte es ara :", "account.mute": "Rescondre @{name}", "account.mute_notifications_short": "Amudir las notificacions", @@ -58,6 +66,10 @@ "account.muted": "Mes en silenci", "account.mutual": "Vos seguissètz", "account.no_bio": "Cap de descripcion pas fornida.", + "account.node_modal.field_label": "Nòta personala", + "account.node_modal.save": "Enregistrar", + "account.node_modal.title": "Apondre una nòta personala", + "account.note.edit_button": "Modificar", "account.open_original_page": "Dobrir la pagina d’origina", "account.posts": "Tuts", "account.posts_with_replies": "Tuts e responsas", @@ -67,6 +79,7 @@ "account.requests_to_follow_you": "Demanda a vos sègre", "account.share": "Partejar lo perfil a @{name}", "account.show_reblogs": "Mostrar los partatges de @{name}", + "account.timeline.pinned": "Penjat", "account.unblock": "Desblocar @{name}", "account.unblock_domain": "Desblocar {domain}", "account.unblock_domain_short": "Desblocar", @@ -76,6 +89,10 @@ "account.unmute": "Quitar de rescondre @{name}", "account.unmute_notifications_short": "Restablir las notificacions", "account.unmute_short": "Tornar afichar", + "account_edit.column_title": "Modificar lo perfil", + "account_edit.custom_fields.name": "camp", + "account_edit.field_delete_modal.delete_button": "Suprimir", + "account_edit.field_edit_modal.value_label": "Valor", "account_note.placeholder": "Clicar per ajustar una nòta", "admin.dashboard.retention.average": "Mejana", "admin.dashboard.retention.cohort": "Mes d’inscripcion", @@ -91,6 +108,8 @@ "alt_text_modal.change_thumbnail": "Cambiar de miniatura", "alt_text_modal.done": "Acabat", "announcement.announcement": "Anóncia", + "annual_report.summary.close": "Tampar", + "annual_report.summary.copy_link": "Copiar lo ligam", "attachments_list.unprocessed": "(pas tractat)", "audio.hide": "Amagar àudio", "block_modal.show_less": "Ne veire mens", @@ -106,6 +125,9 @@ "bundle_modal_error.retry": "Tornar ensajar", "closed_registrations_modal.find_another_server": "Trobar un autre servidor", "closed_registrations_modal.title": "S’inscriure a Mastodon", + "collections.visibility_public": "Publica", + "collections.visibility_title": "Visibilitat", + "collections.visibility_unlisted": "Non listada", "column.about": "A prepaus", "column.blocks": "Personas blocadas", "column.bookmarks": "Marcadors", @@ -387,7 +409,7 @@ "notification.reblog": "{name} a partejat vòstre estatut", "notification.relationships_severance_event.learn_more": "Ne saber mai", "notification.status": "{name} ven de publicar", - "notification.update": "{name} modiquè sa publicacion", + "notification.update": "{name} modifiquèt sa publicacion", "notifications.clear": "Escafar", "notifications.clear_confirmation": "Volètz vertadièrament escafar totas vòstras las notificacions ?", "notifications.column_settings.admin.report": "Senhalaments novèls :", @@ -440,7 +462,9 @@ "privacy.direct.short": "Mencion privada", "privacy.private.long": "Mostrar pas qu’als seguidors", "privacy.private.short": "Seguidors", + "privacy.public.long": "Tot lo monde de e defòra de Mastodon", "privacy.public.short": "Public", + "privacy.unlisted.long": "Rescondut dels resultats de recèrca de Mastodon, las tendéncias e las cronologias publicas", "privacy.unlisted.short": "Public silenciós", "privacy_policy.last_updated": "Darrièra actualizacion {date}", "privacy_policy.title": "Politica de confidencialitat", @@ -593,5 +617,14 @@ "video.fullscreen": "Ecran complèt", "video.hide": "Amagar la vidèo", "video.pause": "Pausa", - "video.play": "Lectura" + "video.play": "Lectura", + "visibility_modal.button_title": "Definir la visibilitat", + "visibility_modal.header": "Visibilitat e interaccion", + "visibility_modal.instructions": "Contrarotlatz qui pòt interagir amb aquesta publicacion. Podètz tanben aplicar de paramètres a totas las publicacions futuras en anant a Preferéncias > Paramètres per defaut de publicacion.", + "visibility_modal.privacy_label": "Visibilitat", + "visibility_modal.quote_followers": "Sonque pels seguidors", + "visibility_modal.quote_label": "Qual pòt citar", + "visibility_modal.quote_nobody": "Sonque ieu", + "visibility_modal.quote_public": "Tot lo monde", + "visibility_modal.save": "Enregistrar" } diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index ad4471483e542f..b900fc650adea6 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -368,6 +368,7 @@ "collections.search_accounts_max_reached": "Keni shtuar numrin maksimum të llogarive", "collections.sensitive": "Rezervat", "collections.topic_hint": "Shtoni një hashtag që ndihmon të tjerët të kuptojnë temën kryesore të këtij koleksion.", + "collections.topic_special_chars_hint": "Shenjat e posaçme do të hiqen, kur ruhet", "collections.view_collection": "Shiheni koleksionin", "collections.view_other_collections_by_user": "Shihni koleksione të tjera nga ky përdorues", "collections.visibility_public": "Publik", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 6a7aaeb4585f77..0377c38240798a 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -14,9 +14,15 @@ "about.powered_by": "สื่อสังคมแบบกระจายศูนย์ที่ขับเคลื่อนโดย {mastodon}", "about.rules": "กฎของเซิร์ฟเวอร์", "account.account_note_header": "หมายเหตุส่วนบุคคล", + "account.add_note": "เพิ่มโน้ตส่วนบุคคล", "account.add_or_remove_from_list": "เพิ่มหรือเอาออกจากรายการ", + "account.badges.admin": "แอดมิน", + "account.badges.blocked": "บล็อกอยู่", "account.badges.bot": "อัตโนมัติ", + "account.badges.domain_blocked": "โดเมนที่ถูกบล็อก", "account.badges.group": "กลุ่ม", + "account.badges.muted": "ปิดเสียงบัญชีแล้ว", + "account.badges.muted_until": "ปิดเสียงบัญชีจนถึง {until}", "account.block": "ปิดกั้น @{name}", "account.block_domain": "ปิดกั้นโดเมน {domain}", "account.block_short": "ปิดกั้น", @@ -27,10 +33,12 @@ "account.direct": "กล่าวถึง @{name} แบบส่วนตัว", "account.disable_notifications": "หยุดแจ้งเตือนฉันเมื่อ @{name} โพสต์", "account.domain_blocking": "กำลังปิดกั้นโดเมน", + "account.edit_note": "แก้ไขโน้ตส่วนบุคคล", "account.edit_profile": "แก้ไขโปรไฟล์", "account.edit_profile_short": "แก้ไข", "account.enable_notifications": "แจ้งเตือนฉันเมื่อ @{name} โพสต์", "account.endorse": "แสดงในโปรไฟล์", + "account.familiar_followers_many": "ติดตามโดย {name1}, {name2}, และ {othersCount, plural, one {อีกหนึ่งผู้ใช้ที่คุณรู้จัก} other {# ผู้ใช้อื่น ๆ ที่คุณรู้จัก}}", "account.familiar_followers_one": "ติดตามโดย {name1}", "account.familiar_followers_two": "ติดตามโดย {name1} และ {name2}", "account.featured": "น่าสนใจ", @@ -48,6 +56,7 @@ "account.followers": "ผู้ติดตาม", "account.followers.empty": "ยังไม่มีใครติดตามผู้ใช้นี้", "account.followers_counter": "{count, plural, other {{counter} ผู้ติดตาม}}", + "account.followers_you_know_counter": "{counter} ที่คุณรู้จัก", "account.following": "กำลังติดตาม", "account.following_counter": "{count, plural, other {{counter} กำลังติดตาม}}", "account.follows.empty": "ผู้ใช้นี้ยังไม่ได้ติดตามใคร", @@ -61,6 +70,7 @@ "account.locked_info": "มีการตั้งสถานะความเป็นส่วนตัวของบัญชีนี้เป็นล็อคอยู่ เจ้าของตรวจทานผู้ที่สามารถติดตามเขาด้วยตนเอง", "account.media": "สื่อ", "account.mention": "กล่าวถึง @{name}", + "account.menu.remove_follower": "ลบผู้ติดตาม", "account.moved_to": "{name} ได้ระบุว่าบัญชีใหม่ของเขาในตอนนี้คือ:", "account.mute": "ซ่อน @{name}", "account.mute_notifications_short": "ซ่อนการแจ้งเตือน", @@ -75,6 +85,7 @@ "account.remove_from_followers": "เอา {name} ออกจากผู้ติดตาม", "account.report": "รายงาน @{name}", "account.requested_follow": "{name} ได้ขอติดตามคุณ", + "account.requests_to_follow_you": "ส่งคำขอติดตามคุณ", "account.share": "แชร์โปรไฟล์ของ @{name}", "account.show_reblogs": "แสดงการดันจาก @{name}", "account.statuses_counter": "{count, plural, other {{counter} โพสต์}}", @@ -110,6 +121,7 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "อธิบายสิ่งนี้สำหรับผู้คนที่มีความบกพร่องทางการมองเห็น…", "alt_text_modal.done": "เสร็จสิ้น", "announcement.announcement": "ประกาศ", + "annual_report.summary.followers.new_followers": "{count, plural, other {ผู้ติดตามใหม่}}", "annual_report.summary.most_used_app.most_used_app": "แอปที่ใช้มากที่สุด", "annual_report.summary.most_used_hashtag.most_used_hashtag": "แฮชแท็กที่ใช้มากที่สุด", "annual_report.summary.new_posts.new_posts": "โพสต์ใหม่", @@ -144,6 +156,7 @@ "closed_registrations_modal.find_another_server": "ค้นหาเซิร์ฟเวอร์อื่น", "closed_registrations_modal.preamble": "Mastodon เป็นแบบกระจายศูนย์ ดังนั้นไม่ว่าคุณจะสร้างบัญชีของคุณที่ใด คุณจะสามารถติดตามและโต้ตอบกับใครก็ตามในเซิร์ฟเวอร์นี้ คุณยังสามารถโฮสต์บัญชีด้วยตนเองได้อีกด้วย!", "closed_registrations_modal.title": "การลงทะเบียนใน Mastodon", + "collections.accounts.empty_description": "สามารถเพิ่มได้สูงสุด {count} บัญชีที่คุณติดตาม", "column.about": "เกี่ยวกับ", "column.blocks": "ผู้ใช้ที่ปิดกั้นอยู่", "column.bookmarks": "ที่คั่นหน้า", @@ -212,7 +225,11 @@ "confirmations.discard_draft.post.title": "ละทิ้งโพสต์แบบร่างของคุณ?", "confirmations.discard_edit_media.confirm": "ละทิ้ง", "confirmations.discard_edit_media.message": "คุณมีการเปลี่ยนแปลงคำอธิบายหรือตัวอย่างสื่อที่ยังไม่ได้บันทึก ละทิ้งการเปลี่ยนแปลงเหล่านั้นต่อไป?", + "confirmations.follow_to_collection.confirm": "ติดตามและเพิ่มไปยังคอลเล็กชัน", + "confirmations.follow_to_collection.message": "คุณต้องติดตาม {name} ถึงจะสามารถเพิ่มพวกเขาลงคอลเล็กชันได้", + "confirmations.follow_to_collection.title": "ติดตามบัญชีนี้ไหม?", "confirmations.follow_to_list.confirm": "ติดตามและเพิ่มไปยังรายการ", + "confirmations.follow_to_list.message": "คุณต้องติดตาม {name} ถึงจะสามารถเพิ่มพวกเขาลงลิสต์ได้", "confirmations.follow_to_list.title": "ติดตามผู้ใช้?", "confirmations.logout.confirm": "ออกจากระบบ", "confirmations.logout.message": "คุณแน่ใจหรือไม่ว่าต้องการออกจากระบบ?", @@ -223,12 +240,15 @@ "confirmations.missing_alt_text.title": "เพิ่มข้อความแสดงแทน?", "confirmations.mute.confirm": "ซ่อน", "confirmations.private_quote_notify.confirm": "เผยแพร่โพสต์", + "confirmations.private_quote_notify.message": "คนที่คุณอ้างอิงและผู้ที่ถูกกล่าวถึงคนอื่น ๆ จะถูกแจ้งให้ทราบและพวกเขาจะสามารถดูโพสต์ของคุณได้ ถึงแม้พวกเขาจะไม่ได้ติดตามคุณอยู่", + "confirmations.private_quote_notify.title": "แชร์ให้กับผู้ติดตามและผู้ใช้ที่ถูกกล่าวถึงไหม?", "confirmations.quiet_post_quote_info.dismiss": "ไม่ต้องเตือนฉันอีก", "confirmations.quiet_post_quote_info.got_it": "เข้าใจแล้ว", "confirmations.redraft.confirm": "ลบแล้วร่างใหม่", "confirmations.redraft.message": "คุณแน่ใจหรือไม่ว่าต้องการลบโพสต์นี้แล้วร่างโพสต์ใหม่? รายการโปรดและการดันจะสูญหาย และการตอบกลับโพสต์ดั้งเดิมจะไม่มีความเกี่ยวพัน", "confirmations.redraft.title": "ลบแล้วร่างโพสต์ใหม่?", "confirmations.remove_from_followers.confirm": "เอาผู้ติดตามออก", + "confirmations.remove_from_followers.message": "{name} จะหยุดติดตามคุณ คุณแน่ใจไหมว่าจะดำเนินการต่อ", "confirmations.remove_from_followers.title": "เอาผู้ติดตามออก?", "confirmations.revoke_quote.confirm": "เอาโพสต์ออก", "confirmations.revoke_quote.title": "เอาโพสต์ออก?", @@ -316,6 +336,7 @@ "empty_column.notification_requests": "โล่งทั้งหมด! ไม่มีสิ่งใดที่นี่ เมื่อคุณได้รับการแจ้งเตือนใหม่ การแจ้งเตือนจะปรากฏที่นี่ตามการตั้งค่าของคุณ", "empty_column.notifications": "คุณยังไม่มีการแจ้งเตือนใด ๆ เมื่อผู้คนอื่น ๆ โต้ตอบกับคุณ คุณจะเห็นการแจ้งเตือนที่นี่", "empty_column.public": "ไม่มีสิ่งใดที่นี่! เขียนบางอย่างเป็นสาธารณะ หรือติดตามผู้ใช้จากเซิร์ฟเวอร์อื่น ๆ ด้วยตนเองเพื่อเติมเส้นเวลาให้เต็ม", + "error.no_hashtag_feed_access": "ลงทะเบียนหรือลงชื่อเข้าใช้เพื่อดูและติดตามแฮชแท็กนี้", "error.unexpected_crash.explanation": "เนื่องจากข้อบกพร่องในโค้ดของเราหรือปัญหาความเข้ากันได้ของเบราว์เซอร์ จึงไม่สามารถแสดงหน้านี้ได้อย่างถูกต้อง", "error.unexpected_crash.explanation_addons": "ไม่สามารถแสดงหน้านี้ได้อย่างถูกต้อง ข้อผิดพลาดนี้มีแนวโน้มว่าเกิดจากส่วนเสริมของเบราว์เซอร์หรือเครื่องมือการแปลอัตโนมัติ", "error.unexpected_crash.next_steps": "ลองรีเฟรชหน้า หากนั่นไม่ช่วย คุณอาจยังสามารถใช้ Mastodon ได้ผ่านเบราว์เซอร์อื่นหรือแอปเนทีฟ", @@ -371,6 +392,8 @@ "follow_suggestions.view_all": "ดูทั้งหมด", "follow_suggestions.who_to_follow": "ติดตามใครดี", "followed_tags": "แฮชแท็กที่ติดตาม", + "followers.hide_other_followers": "ผู้ใช้นี้เลือกที่จะไม่แสดงรายชื่อผู้ติดตามคนอื่น ๆ ของพวกเขา", + "following.hide_other_following": "ผู้ใช้นี้เลือกที่จะไม่แสดงรายชื่อคนอื่น ๆ ที่พวกเขาติดตาม", "footer.about": "เกี่ยวกับ", "footer.about_this_server": "เกี่ยวกับ", "footer.directory": "ไดเรกทอรีโปรไฟล์", @@ -551,7 +574,9 @@ "navigation_bar.privacy_and_reach": "ความเป็นส่วนตัวและการเข้าถึง", "navigation_bar.search": "ค้นหา", "navigation_bar.search_trends": "ค้นหา / กำลังนิยม", + "navigation_panel.collapse_followed_tags": "ยุบเมนูแฮชแท็กที่ติดตามอยู่", "navigation_panel.collapse_lists": "ยุบเมนูรายการ", + "navigation_panel.expand_followed_tags": "ขยายเมนูแฮชแท็กที่ติดตามอยู่", "navigation_panel.expand_lists": "ขยายเมนูรายการ", "not_signed_in_indicator.not_signed_in": "คุณจำเป็นต้องเข้าสู่ระบบเพื่อเข้าถึงทรัพยากรนี้", "notification.admin.report": "{name} ได้รายงาน {target}", @@ -675,6 +700,7 @@ "notifications_permission_banner.title": "ไม่พลาดสิ่งใด", "onboarding.follows.back": "ย้อนกลับ", "onboarding.follows.empty": "น่าเสียดาย ไม่สามารถแสดงผลลัพธ์ได้ในตอนนี้ คุณสามารถลองใช้การค้นหาหรือเรียกดูหน้าสำรวจเพื่อค้นหาผู้คนที่จะติดตาม หรือลองอีกครั้งในภายหลัง", + "onboarding.follows.next": "ต่อไป: ตั้งค่าโปรไฟล์ของคุณ", "onboarding.follows.search": "ค้นหา", "onboarding.follows.title": "ติดตามผู้คนเพื่อเริ่มต้นใช้งาน", "onboarding.profile.discoverable": "ทำให้โปรไฟล์ของฉันสามารถค้นพบได้", @@ -851,9 +877,11 @@ "status.open": "ขยายโพสต์นี้", "status.pin": "ปักหมุดในโปรไฟล์", "status.quote_error.limited_account_hint.action": "แสดงต่อไป", + "status.quote_followers_only": "เฉพาะผู้ติดตามเท่านั้นที่สามารถอ้างอิงโพสต์นี้ได้", "status.quote_post_author": "อ้างอิงโพสต์โดย @{name}", "status.read_more": "อ่านเพิ่มเติม", "status.reblog": "ดัน", + "status.reblog_private": "แชร์อีกครั้งกับผู้ติดตามของคุณ", "status.reblogged_by": "{name} ได้ดัน", "status.reblogs.empty": "ยังไม่มีใครดันโพสต์นี้ เมื่อใครสักคนดัน เขาจะปรากฏที่นี่", "status.redraft": "ลบแล้วร่างใหม่", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 34b8770b81129e..7cf66595ac4d59 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "Bạn đã đạt đến số lượng tài khoản tối đa", "collections.sensitive": "Nhạy cảm", "collections.topic_hint": "Thêm hashtag giúp người khác hiểu chủ đề chính của gói khởi đầu này.", + "collections.topic_special_chars_hint": "Những ký tự đặc biệt sẽ bị xóa khi lưu", "collections.view_collection": "Xem gói khởi đầu", "collections.view_other_collections_by_user": "Xem những gói khởi đầu khác từ tài khoản này", "collections.visibility_public": "Công khai", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 7e3282fc196674..bb2400c775242c 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "你添加的账号数量已达上限", "collections.sensitive": "敏感内容", "collections.topic_hint": "添加话题标签,帮助他人了解此收藏列表的主题。", + "collections.topic_special_chars_hint": "保存时将自动移除特殊字符", "collections.view_collection": "查看收藏列表", "collections.view_other_collections_by_user": "查看此用户的其他收藏列表", "collections.visibility_public": "公开", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 4258450790f162..36e6d517a0899a 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -374,6 +374,7 @@ "collections.search_accounts_max_reached": "您新增之帳號數已達上限", "collections.sensitive": "敏感內容", "collections.topic_hint": "新增主題標籤以協助其他人瞭解此收藏名單之主題。", + "collections.topic_special_chars_hint": "特殊字元將於儲存時被移除", "collections.view_collection": "檢視收藏名單", "collections.view_other_collections_by_user": "檢視此使用者之其他收藏名單", "collections.visibility_public": "公開", diff --git a/config/locales/devise.no.yml b/config/locales/devise.no.yml index b92e7e8aa35aa0..116705d63d672b 100644 --- a/config/locales/devise.no.yml +++ b/config/locales/devise.no.yml @@ -7,6 +7,7 @@ send_paranoid_instructions: Hvis e-postadressen din finnes i databasen vår vil du om noen få minutter motta en e-post med instruksjoner for bekreftelse. Sjekk spam-mappen din hvis du ikke mottok e-posten. failure: already_authenticated: Du er allerede innlogget. + closed_registrations: Registreringsforsøket ditt har blitt blokkert på grunn av en nettverkspolicy. Hvis du mener dette er feil, kontakt %{email}. inactive: Kontoen din er ikke blitt aktivert ennå. invalid: Ugyldig %{authentication_keys} eller passord. last_attempt: Du har ett forsøk igjen før kontoen din låses. diff --git a/config/locales/doorkeeper.no.yml b/config/locales/doorkeeper.no.yml index 7b7b9d65342689..59874904b2a05a 100644 --- a/config/locales/doorkeeper.no.yml +++ b/config/locales/doorkeeper.no.yml @@ -60,6 +60,7 @@ error: title: En feil oppstod new: + prompt_html: "%{client_name} ønsker tilgang til kontoen din. Godkjenn denne forespørselen kun hvis du kjenner igjen og stoler på denne kilden." review_permissions: Gå gjennom tillatelser title: Autorisasjon påkrevd show: @@ -134,6 +135,7 @@ media: Mediavedlegg mutes: Dempinger notifications: Varslinger + profile: Din Mastodon-profil push: Push-varslinger reports: Rapporteringer search: Søk diff --git a/config/locales/nan-TW.yml b/config/locales/nan-TW.yml index 06b133d83225b5..e4fd51a2a8010e 100644 --- a/config/locales/nan-TW.yml +++ b/config/locales/nan-TW.yml @@ -1893,9 +1893,49 @@ nan-TW: user_domain_block: Lí kā %{target_name} 封鎖ah lost_followers: 失去ê 跟tuè lí ê lost_follows: 失去ê lí 跟tuè ê + preamble: Lí封鎖域名,á是lí ê管理員決定beh kā別ê服侍器停止權限ê時,Lí可能會失去跟tuè lí ê,á是lí跟tuè ê。若發生,lí會當kā中止聯絡ê ê列單載落去,來檢視或者有可能佇別站kā輸入。 + purged: 關係tsit台服侍器有予lí ê服侍器管理員清掉ê資訊。 + type: 事件 statuses: + attached: + audio: + other: "%{count} 段聲音" + description: 附件: %{attached} + image: + other: "%{count} 幅圖" + video: + other: "%{count} 段影片" + boosted_from_html: 對 %{acct_link} 轉送 + content_warning: 內容警告: %{warning} + content_warnings: + hide: Am-khàm PO文 + show: 顯示其他ê內容 default_language: Kap界面ê語言sio kâng + disallowed_hashtags: + other: 包含bē用得ê標籤: %{tags} + edited_at_html: 佇 %{date} 編輯 + errors: + in_reply_not_found: Lí試應ê PO文假若無佇leh。 + quoted_status_not_found: Lí試引用ê PO文假若無佇leh。 + quoted_user_not_mentioned: Bē當佇私人ê提起引用無提起ê用者。 + over_character_limit: 超過 %{max} 字ê限制ah + pin_errors: + direct: Kan-ta受提起ê用者tsiah通看ê PO文bē當釘 + limit: Lí已經kā PO文釘kàu盡磅ah。 + ownership: Bē當釘別lâng êPO文 + reblog: 轉送ê PO文 bē當釘 + quote_error: + not_available: PO文bē當看 + pending_approval: PO文當leh等審查 + revoked: PO文已經hōo作者thâi掉 + quote_policies: + followers: Kan-ta hōo跟tuè ê lâng + nobody: Kan-ta我 + public: Ta̍k ê lâng + quote_post_author: 引用 %{acct} ê PO文ah + title: "%{name}:「%{quote}」" visibilities: + direct: 私人ê提起 private: Kan-ta hōo跟tuè ê lâng public: 公開ê public_long: 逐ê lâng(無論佇Mastodon以內á是以外) @@ -1911,6 +1951,8 @@ nan-TW: interaction_exceptions: Tshāi佇互動ê特例 interaction_exceptions_explanation: 超過收藏kap轉送ê底限ê PO文可能會受保留,就算in後來降落。 keep_direct: 保留私人ê短phue + keep_direct_hint: Bē thâi掉任何lí ê私人ê提起 + keep_media: 保留有媒體附件ê PO文 terms_of_service: title: 服務規定 themes: diff --git a/config/locales/no.yml b/config/locales/no.yml index a143940462a2a0..f6dd1e9c8de88e 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -44,9 +44,11 @@ title: Endre E-postadressen til %{username} change_role: changed_msg: Rollen ble endret! + edit_roles: Administrer brukerroller label: Endre rolle no_role: Ingen rolle title: Endre rolle for %{username} + collections: Samlinger confirm: Bekreft confirmed: Bekreftet confirming: Bekrefte @@ -128,6 +130,7 @@ resubscribe: Abonner på nytt role: Rolle search: Søk + search_same_email_domain: Andre brukere med det samme epost domenet search_same_ip: Andre brukere med den samme IP-en security: Sikkerhet security_measures: @@ -168,6 +171,7 @@ approve_appeal: Godkjenn anke approve_user: Godkjenn bruker assigned_to_self_report: Tilordne rapport + change_email_user: Endre brukerens e-postadresse change_role_user: Endre rolle for brukeren confirm_user: Bekreft brukeren create_account_warning: Opprett en advarsel @@ -275,6 +279,7 @@ filter_by_user: Sorter etter bruker title: Revisionslogg announcements: + back: Tilbake til kunngjøringer destroyed_msg: Kunngjøringen er slettet! edit: title: Rediger kunngjøring @@ -291,6 +296,9 @@ unpublish: Avpubliser unpublished_msg: Kunngjøring upublisert! updated_msg: Kunngjøringen er oppdatert! + collections: + accounts: Kontoer + collection_title: Samling av %{name} critical_update_pending: Kritisk oppdatering avventer custom_emojis: assign_category: Tilegn kategori diff --git a/config/locales/th.yml b/config/locales/th.yml index e91c7c8fce3b36..b3e24132355747 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -9,6 +9,8 @@ th: accounts: followers: other: ผู้ติดตาม + following: + other: กำลังติดตาม instance_actor_flash: บัญชีนี้เป็นตัวดำเนินการเสมือนที่ใช้เพื่อเป็นตัวแทนของเซิร์ฟเวอร์เองและไม่ใช่ผู้ใช้รายบุคคลใด ๆ มีการใช้บัญชีสำหรับวัตถุประสงค์ในการติดต่อกับภายนอกและไม่ควรได้รับการระงับ last_active: ใช้งานล่าสุด link_verified_on: ตรวจสอบความเป็นเจ้าของของลิงก์นี้เมื่อ %{date} @@ -315,6 +317,8 @@ th: new: create: สร้างประกาศ title: ประกาศใหม่ + preview: + explanation_html: 'อีเมลจะถูกส่งไปยังผู้ใช้ %{display_count} คน. โดยจะมีข้อความต่อไปนี้อยู่ในอีเมล:' publish: เผยแพร่ published_msg: เผยแพร่ประกาศสำเร็จ! scheduled_for: จัดกำหนดการไว้สำหรับ %{time} @@ -950,6 +954,7 @@ th: live: สด notify_users: แจ้งเตือนผู้ใช้ preview: + explanation_html: 'อีเมลจะถูกส่งไปยังผู้ใช้ %{display_count} คน. ที่ลงทะเบียนก่อนวันที่ %{date} โดยจะมีข้อความต่อไปนี้อยู่ในอีเมล:' send_preview: ส่งตัวอย่างไปยัง %{email} send_to_all: other: ส่ง %{display_count} อีเมล From f561014aa84428df4ea2e348a445aab8e8061657 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 04:34:18 -0400 Subject: [PATCH 070/203] Make use of `email_domain` in sign up approval check (#38199) --- app/models/user.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index f1ac710e8e96ae..dd029d8e08257c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -463,18 +463,15 @@ def sign_up_from_ip_requires_approval? end def sign_up_email_requires_approval? - return false if email.blank? - - _, domain = email.split('@', 2) - return false if domain.blank? + return false if email_domain.blank? records = [] # Doing this conditionally is not very satisfying, but this is consistent # with the MX records validations we do and keeps the specs tractable. - records = DomainResource.new(domain).mx unless self.class.skip_mx_check? + records = DomainResource.new(email_domain).mx unless self.class.skip_mx_check? - EmailDomainBlock.requires_approval?(records + [domain], attempt_ip: sign_up_ip) + EmailDomainBlock.requires_approval?(records + [email_domain], attempt_ip: sign_up_ip) end def sign_up_username_requires_approval? From 2f989c780a3db1feb9f6a5254c1490307baf610c Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 16 Mar 2026 09:42:06 +0100 Subject: [PATCH 071/203] Reinstate action logging (#38211) --- app/models/admin/account_action.rb | 7 +++++++ spec/models/admin/account_action_spec.rb | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index eed17c9a87e5f3..e132571da02045 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -46,6 +46,7 @@ def process_action! ApplicationRecord.transaction do handle_type! process_strike! + create_log! process_reports! end @@ -105,6 +106,12 @@ def handle_suspend! target_account.suspend!(origin: :local) end + def create_log! + # A log entry is only interesting if the warning contains + # custom text from someone. Otherwise it's just noise. + log_action(:create, @warning) if @warning&.text.present? && type == 'none' + end + def text_for_warning [warning_preset&.text, text].compact.join("\n\n") end diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb index 6032594850d346..f25ddd6fe4c63f 100644 --- a/spec/models/admin/account_action_spec.rb +++ b/spec/models/admin/account_action_spec.rb @@ -11,12 +11,14 @@ let(:account) { Fabricate(:admin_user).account } let(:target_account) { Fabricate(:account) } let(:type) { 'disable' } + let(:text) { nil } before do account_action.assign_attributes( type: type, current_account: account, - target_account: target_account + target_account: target_account, + text: ) end @@ -53,6 +55,20 @@ end end + context 'when type is `none`' do + let(:type) { 'none' } + + context 'when a custom text is given' do + let(:text) { 'custom' } + + it 'logs the action' do + expect { subject }.to change(Admin::ActionLog, :count).by(1) + + expect(Admin::ActionLog.last.target.text).to eq 'custom' + end + end + end + context 'when type is invalid' do let(:type) { 'whatever' } From e2bf09d57d057d3deb59e31900023d901f3bbf74 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 04:48:10 -0400 Subject: [PATCH 072/203] Add timestamp spec for markers API response (#38206) --- app/controllers/api/v1/markers_controller.rb | 8 +------- spec/requests/api/v1/markers_spec.rb | 11 +++++++++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/v1/markers_controller.rb b/app/controllers/api/v1/markers_controller.rb index 8eaf7767df87e0..cfb708ff3082d6 100644 --- a/app/controllers/api/v1/markers_controller.rb +++ b/app/controllers/api/v1/markers_controller.rb @@ -32,13 +32,7 @@ def create private def serialize_map(map) - serialized = {} - - map.each_pair do |key, value| - serialized[key] = ActiveModelSerializers::SerializableResource.new(value, serializer: REST::MarkerSerializer).as_json - end - - Oj.dump(serialized) + map.transform_values { |value| ActiveModelSerializers::SerializableResource.new(value, serializer: REST::MarkerSerializer) } end def resource_params diff --git a/spec/requests/api/v1/markers_spec.rb b/spec/requests/api/v1/markers_spec.rb index 0e6ecc56855b23..17f19872f73670 100644 --- a/spec/requests/api/v1/markers_spec.rb +++ b/spec/requests/api/v1/markers_spec.rb @@ -7,8 +7,10 @@ describe 'GET /api/v1/markers' do before do - Fabricate(:marker, timeline: 'home', last_read_id: 123, user: user) - Fabricate(:marker, timeline: 'notifications', last_read_id: 456, user: user) + travel_to DateTime.parse('2026-03-15T12:34:56.789Z'), with_usec: true do + Fabricate(:marker, timeline: 'home', last_read_id: 123, user: user) + Fabricate(:marker, timeline: 'notifications', last_read_id: 456, user: user) + end get '/api/v1/markers', headers: headers, params: { timeline: %w(home notifications) } end @@ -23,6 +25,11 @@ notifications: include(last_read_id: '456') ) end + + it 'uses a specific style of IS08601 timestamps' do + expect(response.parsed_body) + .to include(home: include(updated_at: eq('2026-03-15T12:34:56.789Z'))) + end end describe 'POST /api/v1/markers' do From 8d5d66ecfc21a5d88a139f2e7e19a2ba879659ae Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 05:02:21 -0400 Subject: [PATCH 073/203] Remove unused react component helper method (#38207) --- app/helpers/react_component_helper.rb | 29 ++++++--------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/app/helpers/react_component_helper.rb b/app/helpers/react_component_helper.rb index 821a6f1e2d451d..a9a7a8d7c9db56 100644 --- a/app/helpers/react_component_helper.rb +++ b/app/helpers/react_component_helper.rb @@ -2,33 +2,16 @@ module ReactComponentHelper def react_component(name, props = {}, &block) - data = { component: name.to_s.camelcase, props: Oj.dump(props) } - if block.nil? - div_tag_with_data(data) + data = { component: name.to_s.camelcase, props: } + if block_given? + tag.div data:, &block else - content_tag(:div, data: data, &block) + tag.div nil, data: end end def react_admin_component(name, props = {}) - data = { 'admin-component': name.to_s.camelcase, props: Oj.dump(props) } - div_tag_with_data(data) - end - - def serialized_media_attachments(media_attachments) - media_attachments.map { |attachment| serialized_attachment(attachment) } - end - - private - - def div_tag_with_data(data) - content_tag(:div, nil, data: data) - end - - def serialized_attachment(attachment) - ActiveModelSerializers::SerializableResource.new( - attachment, - serializer: REST::MediaAttachmentSerializer - ).as_json + data = { 'admin-component': name.to_s.camelcase, props: } + tag.div nil, data: end end From c993daa3470fa4aa59b716cafa8280f7fca1efc9 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 16 Mar 2026 10:04:28 +0100 Subject: [PATCH 074/203] Start of handling `FeatureRequest` activities (#38193) --- app/lib/activitypub/activity.rb | 2 + .../activitypub/activity/feature_request.rb | 46 ++++++++++++++++ app/models/collection_item.rb | 6 ++- .../activity/feature_request_spec.rb | 54 +++++++++++++++++++ spec/models/collection_item_spec.rb | 9 +++- 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 app/lib/activitypub/activity/feature_request.rb create mode 100644 spec/lib/activitypub/activity/feature_request_spec.rb diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index eab345ce457f79..29316008311758 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -59,6 +59,8 @@ def klass_for(json) ActivityPub::Activity::Move when 'QuoteRequest' ActivityPub::Activity::QuoteRequest + when 'FeatureRequest' + ActivityPub::Activity::FeatureRequest end end end diff --git a/app/lib/activitypub/activity/feature_request.rb b/app/lib/activitypub/activity/feature_request.rb new file mode 100644 index 00000000000000..9e69fa2b99754c --- /dev/null +++ b/app/lib/activitypub/activity/feature_request.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +class ActivityPub::Activity::FeatureRequest < ActivityPub::Activity + include Payloadable + + def perform + return unless Mastodon::Feature.collections_federation_enabled? + return if non_matching_uri_hosts?(@account.uri, @json['id']) + + @collection = @account.collections.find_by(uri: value_or_id(@json['instrument'])) + @featured_account = ActivityPub::TagManager.instance.uris_to_local_accounts([value_or_id(@json['object'])]).first + + return if @collection.nil? || @featured_account.nil? + + if AccountPolicy.new(@account, @featured_account).feature? + accept_request! + else + reject_request! + end + end + + private + + def accept_request! + collection_item = @collection.collection_items.create!( + account: @featured_account, + state: :accepted + ) + + queue_delivery!(collection_item, ActivityPub::AcceptFeatureRequestSerializer) + end + + def reject_request! + collection_item = @collection.collection_items.build( + account: @featured_account, + state: :rejected + ) + + queue_delivery!(collection_item, ActivityPub::RejectFeatureRequestSerializer) + end + + def queue_delivery!(collection_item, serializer) + json = Oj.dump(serialize_payload(collection_item, serializer)) + ActivityPub::DeliveryWorker.perform_async(json, @featured_account.id, @account.inbox_url) + end +end diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb index 1cc8d80e625ecc..69f4f08bbd3bd9 100644 --- a/app/models/collection_item.rb +++ b/app/models/collection_item.rb @@ -32,7 +32,7 @@ class CollectionItem < ApplicationRecord validates :approval_uri, presence: true, unless: -> { local? || account&.local? } validates :account, presence: true, if: :accepted? validates :object_uri, presence: true, if: -> { account.nil? } - validates :uri, presence: true, if: :remote? + validates :uri, presence: true, if: :remote_item_with_remote_account? before_validation :set_position, on: :create before_validation :set_activity_uri, only: :create, if: :local_item_with_remote_account? @@ -50,6 +50,10 @@ def local_item_with_remote_account? local? && account&.remote? end + def remote_item_with_remote_account? + remote? && account&.remote? + end + def object_type :featured_item end diff --git a/spec/lib/activitypub/activity/feature_request_spec.rb b/spec/lib/activitypub/activity/feature_request_spec.rb new file mode 100644 index 00000000000000..ac3e42b27213d8 --- /dev/null +++ b/spec/lib/activitypub/activity/feature_request_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::Activity::FeatureRequest do + let(:sender) { Fabricate(:remote_account) } + let(:recipient) { Fabricate(:account, discoverable:) } + let(:collection) { Fabricate(:remote_collection, account: sender) } + + let(:json) do + { + '@context' => [ + 'https://www.w3.org/ns/activitystreams', + ], + 'id' => 'https://example.com/feature_requests/1', + 'type' => 'FeatureRequest', + 'actor' => sender.uri, + 'object' => ActivityPub::TagManager.instance.uri_for(recipient), + 'instrument' => collection.uri, + } + end + + describe '#perform', feature: :collections_federation do + subject { described_class.new(json, sender) } + + context 'when recipient is discoverable' do + let(:discoverable) { true } + + it 'schedules a job to send an `Accept` activity' do + expect { subject.perform } + .to enqueue_sidekiq_job(ActivityPub::DeliveryWorker) + .with(satisfying do |body| + response_json = JSON.parse(body) + response_json['type'] == 'Accept' && + response_json['to'] == sender.uri + end, recipient.id, sender.inbox_url) + end + end + + context 'when recipient is not discoverable' do + let(:discoverable) { false } + + it 'schedules a job to send a `Reject` activity' do + expect { subject.perform } + .to enqueue_sidekiq_job(ActivityPub::DeliveryWorker) + .with(satisfying do |body| + response_json = JSON.parse(body) + response_json['type'] == 'Reject' && + response_json['to'] == sender.uri + end, recipient.id, sender.inbox_url) + end + end + end +end diff --git a/spec/models/collection_item_spec.rb b/spec/models/collection_item_spec.rb index e8be8c260b0316..8960d434392055 100644 --- a/spec/models/collection_item_spec.rb +++ b/spec/models/collection_item_spec.rb @@ -17,8 +17,9 @@ end context 'when item is not local' do - subject { Fabricate.build(:collection_item, collection: remote_collection) } + subject { Fabricate.build(:collection_item, collection: remote_collection, account:) } + let(:account) { Fabricate.build(:remote_account) } let(:remote_collection) { Fabricate.build(:collection, local: false) } it { is_expected.to validate_presence_of(:uri) } @@ -28,6 +29,12 @@ it { is_expected.to validate_presence_of(:approval_uri) } end + + context 'when account is local' do + let(:account) { Fabricate.build(:account) } + + it { is_expected.to_not validate_presence_of(:uri) } + end end context 'when account is not present' do From 5521aac4abfcdae755953905cb1aca0eaae8d713 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 16 Mar 2026 10:39:18 +0100 Subject: [PATCH 075/203] Fix hashtags preceded by non-break spaces not being processed as such (#38212) --- app/models/tag.rb | 2 +- spec/models/tag_spec.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/tag.rb b/app/models/tag.rb index 224ec327366976..b14dfce76366e8 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -41,7 +41,7 @@ class Tag < ApplicationRecord HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)' HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}".freeze - HASHTAG_RE = /(?<=^|\s)[##](#{HASHTAG_NAME_PAT})/ + HASHTAG_RE = /(?<=^|[[:space:]])[##](#{HASHTAG_NAME_PAT})/ HASHTAG_NAME_RE = /\A(#{HASHTAG_NAME_PAT})\z/i HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]\u0E47-\u0E4E#{HASHTAG_SEPARATORS}]/ diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index 9fe723b3ba6296..61ef531fe188e2 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -103,6 +103,10 @@ def previous_name_error_message expect(subject.match('https://en.wikipedia.org/wiki/Google_LLC_v._Oracle_America,_Inc.#Decision')).to be_nil end + it 'matches a hashtag preceded by a non-break space' do + expect(subject.match('test #foo').to_s).to eq '#foo' + end + it 'matches #aesthetic' do expect(subject.match('this is #aesthetic').to_s).to eq '#aesthetic' end From 62b90d73d77bd8fca406056727f42b8d5190c938 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 16 Mar 2026 11:00:08 +0100 Subject: [PATCH 076/203] Fixes some model definitions in `tootctl maintenance fix-duplicates` (#38214) --- lib/mastodon/cli/maintenance.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index 32ee35c7c7a803..e5db9d6ed23aca 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -26,7 +26,6 @@ class AccountPin < ApplicationRecord; end class ListAccount < ApplicationRecord; end class PollVote < ApplicationRecord; end class Mention < ApplicationRecord; end - class Notification < ApplicationRecord; end class NotificationPermission < ApplicationRecord; end class NotificationRequest < ApplicationRecord; end class AccountDomainBlock < ApplicationRecord; end @@ -40,8 +39,6 @@ class FollowRecommendationSuppression < ApplicationRecord; end class CanonicalEmailBlock < ApplicationRecord; end class Appeal < ApplicationRecord; end class Webhook < ApplicationRecord; end - class BulkImport < ApplicationRecord; end - class SoftwareUpdate < ApplicationRecord; end class SeveredRelationship < ApplicationRecord; end class TagFollow < ApplicationRecord; end @@ -58,6 +55,18 @@ class MediaAttachment < ApplicationRecord self.inheritance_column = nil end + class Notification < ApplicationRecord + self.inheritance_column = nil + end + + class BulkImport < ApplicationRecord + self.inheritance_column = nil + end + + class SoftwareUpdate < ApplicationRecord + self.inheritance_column = nil + end + class AccountStat < ApplicationRecord belongs_to :account, inverse_of: :account_stat end From 12c63a382cffb34729adabf26316122e0239522a Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 16 Mar 2026 11:35:07 +0100 Subject: [PATCH 077/203] Handle adding already approved collection items (#38213) --- app/models/collection_item.rb | 1 + .../process_featured_item_service.rb | 22 +++++++++++++------ .../process_featured_item_service_spec.rb | 21 ++++++++++++++++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb index 69f4f08bbd3bd9..78186a89fcf64d 100644 --- a/app/models/collection_item.rb +++ b/app/models/collection_item.rb @@ -41,6 +41,7 @@ class CollectionItem < ApplicationRecord scope :with_accounts, -> { includes(account: [:account_stat, :user]) } scope :not_blocked_by, ->(account) { where.not(accounts: { id: account.blocking }) } scope :local, -> { joins(:collection).merge(Collection.local) } + scope :accepted_partial, ->(account) { joins(:account).merge(Account.local).accepted.where(uri: nil, account_id: account.id) } def revoke! update!(state: :revoked) diff --git a/app/services/activitypub/process_featured_item_service.rb b/app/services/activitypub/process_featured_item_service.rb index 24f3bbfaed15c4..56edf14c799ba1 100644 --- a/app/services/activitypub/process_featured_item_service.rb +++ b/app/services/activitypub/process_featured_item_service.rb @@ -12,13 +12,21 @@ def call(collection, uri_or_object) with_redis_lock("collection_item:#{item_json['id']}") do return if collection.collection_items.exists?(uri: item_json['id']) - @collection_item = collection.collection_items.create!( - uri: item_json['id'], - object_uri: item_json['featuredObject'], - approval_uri: item_json['featureAuthorization'] - ) - - verify_authorization! + local_account = ActivityPub::TagManager.instance.uris_to_local_accounts([item_json['featuredObject']]).first + + if local_account.present? + # This is a local account that has authorized this item already + @collection_item = collection.collection_items.accepted_partial(local_account).first + @collection_item&.update!(uri: item_json['id']) + else + @collection_item = collection.collection_items.create!( + uri: item_json['id'], + object_uri: item_json['featuredObject'], + approval_uri: item_json['featureAuthorization'] + ) + + verify_authorization! + end @collection_item end diff --git a/spec/services/activitypub/process_featured_item_service_spec.rb b/spec/services/activitypub/process_featured_item_service_spec.rb index dab26f846bb433..dc04ce5d08ffd9 100644 --- a/spec/services/activitypub/process_featured_item_service_spec.rb +++ b/spec/services/activitypub/process_featured_item_service_spec.rb @@ -3,17 +3,21 @@ require 'rails_helper' RSpec.describe ActivityPub::ProcessFeaturedItemService do + include RoutingHelper + subject { described_class.new } let(:collection) { Fabricate(:remote_collection, uri: 'https://other.example.com/collection/1') } + let(:featured_object_uri) { 'https://example.com/actor/1' } + let(:feature_authorization_uri) { 'https://example.com/auth/1' } let(:featured_item_json) do { '@context' => 'https://www.w3.org/ns/activitystreams', 'id' => 'https://other.example.com/featured_item/1', 'type' => 'FeaturedItem', - 'featuredObject' => 'https://example.com/actor/1', + 'featuredObject' => featured_object_uri, 'featuredObjectType' => 'Person', - 'featureAuthorization' => 'https://example.com/auth/1', + 'featureAuthorization' => feature_authorization_uri, } end let(:stubbed_service) do @@ -50,6 +54,19 @@ expect(new_item.object_uri).to eq 'https://example.com/actor/1' expect(new_item.approval_uri).to eq 'https://example.com/auth/1' end + + context 'when an item exists for a local featured account' do + let!(:collection_item) do + Fabricate(:collection_item, collection:, state: :accepted) + end + let(:featured_object_uri) { ActivityPub::TagManager.instance.uri_for(collection_item.account) } + let(:feature_authorization_uri) { ap_account_feature_authorization_url(collection_item.account_id, collection_item) } + + it 'updates the URI of the existing record' do + expect { subject.call(collection, object) }.to_not change(collection.collection_items, :count) + expect(collection_item.reload.uri).to eq 'https://other.example.com/featured_item/1' + end + end end context 'when only the id of the collection item is given' do From 9c8be1e72146af247e2b6cdae6e1d37371506569 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 12:03:13 +0100 Subject: [PATCH 078/203] Update dependency immutable to v4.3.8 [SECURITY] (#38073) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 29c8511bb5311d..d99c4b2aa54e7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8565,16 +8565,16 @@ __metadata: linkType: hard "immutable@npm:^3.8.2": - version: 3.8.2 - resolution: "immutable@npm:3.8.2" - checksum: 10c0/fb6a2999ad3bda9e51741721e42547076dd492635ee4df9241224055fe953ec843583a700088cc4915f23dc326e5084f4e17f1bbd7388c3e872ef5a242e0ac5e + version: 3.8.3 + resolution: "immutable@npm:3.8.3" + checksum: 10c0/bafa7b8371b7622bc3d128cd9e6bba3a654b968f09a237929629f43ac26f7e974a5879cd38baad0c26f6f0628753968611bf832add7bf0c44d647bf4306a2988 languageName: node linkType: hard "immutable@npm:^4.0.0-rc.1, immutable@npm:^4.3.0": - version: 4.3.7 - resolution: "immutable@npm:4.3.7" - checksum: 10c0/9b099197081b22f6433003e34929da8ecddbbdc1474cdc8aa3b7669dee4adda349c06143de22def36016d1b6de5322b043eccd7a11db1dad2ca85dad4fff5435 + version: 4.3.8 + resolution: "immutable@npm:4.3.8" + checksum: 10c0/3de58996305a0faf6ef3fc0685f996c42653ad757760214f5aec7d4a6b59ea7abb882522c5f9a61776fae88c0b45e08eb77cbded5a4f57745ec7c63f9642e44b languageName: node linkType: hard From 21c27eb3affc486767334cf1b1431e4398fcafc0 Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 16 Mar 2026 12:39:52 +0100 Subject: [PATCH 079/203] Profile editing: Uploading avatar and header images (#38189) --- .../mastodon/actions/compose_typed.ts | 4 + app/javascript/mastodon/api/accounts.ts | 2 +- .../components/character_counter/index.tsx | 2 + .../account_edit/components/image_edit.tsx | 35 +- .../account_edit/modals/image_alt.tsx | 3 +- .../account_edit/modals/image_delete.tsx | 3 +- .../account_edit/modals/image_upload.tsx | 484 +++++++++++++++++- .../account_edit/modals/styles.module.scss | 49 ++ app/javascript/mastodon/features/ui/index.jsx | 17 +- app/javascript/mastodon/locales/en.json | 14 + app/javascript/mastodon/reducers/compose.js | 5 + .../mastodon/reducers/slices/profile_edit.ts | 46 ++ package.json | 1 + yarn.lock | 21 + 14 files changed, 647 insertions(+), 39 deletions(-) diff --git a/app/javascript/mastodon/actions/compose_typed.ts b/app/javascript/mastodon/actions/compose_typed.ts index 6b38b25c25cb1a..6bf193ba92370a 100644 --- a/app/javascript/mastodon/actions/compose_typed.ts +++ b/app/javascript/mastodon/actions/compose_typed.ts @@ -273,3 +273,7 @@ export const quoteComposeCancel = createAction('compose/quoteComposeCancel'); export const setComposeQuotePolicy = createAction( 'compose/setQuotePolicy', ); + +export const setDragUploadEnabled = createAction( + 'compose/setDragUploadEnabled', +); diff --git a/app/javascript/mastodon/api/accounts.ts b/app/javascript/mastodon/api/accounts.ts index da4b0e94f8637c..15156e156c9fd5 100644 --- a/app/javascript/mastodon/api/accounts.ts +++ b/app/javascript/mastodon/api/accounts.ts @@ -67,5 +67,5 @@ export const apiGetFamiliarFollowers = (id: string) => export const apiGetProfile = () => apiRequestGet('v1/profile'); -export const apiPatchProfile = (params: ApiProfileUpdateParams) => +export const apiPatchProfile = (params: ApiProfileUpdateParams | FormData) => apiRequestPatch('v1/profile', params); diff --git a/app/javascript/mastodon/components/character_counter/index.tsx b/app/javascript/mastodon/components/character_counter/index.tsx index dce410a7c1336f..6ffe4d02f48a9a 100644 --- a/app/javascript/mastodon/components/character_counter/index.tsx +++ b/app/javascript/mastodon/components/character_counter/index.tsx @@ -26,6 +26,7 @@ export const CharacterCounter = polymorphicForwardRef< maxLength, as: Component = 'span', recommended = false, + className, ...props }, ref, @@ -39,6 +40,7 @@ export const CharacterCounter = polymorphicForwardRef< {...props} ref={ref} className={classNames( + className, classes.counter, currentLength > maxLength && !recommended && classes.counterError, )} diff --git a/app/javascript/mastodon/features/account_edit/components/image_edit.tsx b/app/javascript/mastodon/features/account_edit/components/image_edit.tsx index b99b424aecfc42..340b8156eb67fd 100644 --- a/app/javascript/mastodon/features/account_edit/components/image_edit.tsx +++ b/app/javascript/mastodon/features/account_edit/components/image_edit.tsx @@ -12,11 +12,9 @@ import { openModal } from '@/mastodon/actions/modal'; import { Dropdown } from '@/mastodon/components/dropdown_menu'; import { IconButton } from '@/mastodon/components/icon_button'; import type { MenuItem } from '@/mastodon/models/dropdown_menu'; -import { - createAppSelector, - useAppDispatch, - useAppSelector, -} from '@/mastodon/store'; +import type { ImageLocation } from '@/mastodon/reducers/slices/profile_edit'; +import { selectImageInfo } from '@/mastodon/reducers/slices/profile_edit'; +import { useAppDispatch, useAppSelector } from '@/mastodon/store'; import AddIcon from '@/material-icons/400-24px/add.svg?react'; import DeleteIcon from '@/material-icons/400-24px/delete.svg?react'; import EditIcon from '@/material-icons/400-24px/edit.svg?react'; @@ -50,36 +48,15 @@ const messages = defineMessages({ }, }); -export type ImageLocation = 'avatar' | 'header'; - -const selectImageInfo = createAppSelector( - [ - (state) => state.profileEdit.profile, - (_, location: ImageLocation) => location, - ], - (profile, location) => { - if (!profile) { - return { - hasImage: false, - hasAlt: false, - }; - } - - return { - hasImage: !!profile[`${location}Static`], - hasAlt: !!profile[`${location}Description`], - }; - }, -); - export const AccountImageEdit: FC<{ className?: string; location: ImageLocation; }> = ({ className, location }) => { const intl = useIntl(); - const { hasAlt, hasImage } = useAppSelector((state) => + const { alt, src } = useAppSelector((state) => selectImageInfo(state, location), ); + const hasAlt = !!alt; const dispatch = useAppDispatch(); const handleModal = useCallback( @@ -125,7 +102,7 @@ export const AccountImageEdit: FC<{ const iconClassName = classNames(classes.imageButton, className); - if (!hasImage) { + if (!src) { return ( = ({ onClose }) => { - return ; +> = ({ onClose, location }) => { + const { src: oldSrc } = useAppSelector((state) => + selectImageInfo(state, location), + ); + const hasImage = !!oldSrc; + const [step, setStep] = useState<'select' | 'crop' | 'alt'>('select'); + + // State for individual steps. + const [imageSrc, setImageSrc] = useState(null); + const [imageBlob, setImageBlob] = useState(null); + + const handleFile = useCallback((file: File) => { + const reader = new FileReader(); + reader.addEventListener('load', () => { + const result = reader.result; + if (typeof result === 'string' && result.length > 0) { + setImageSrc(result); + setStep('crop'); + } + }); + reader.readAsDataURL(file); + }, []); + + const handleCrop = useCallback( + (crop: Area) => { + if (!imageSrc) { + setStep('select'); + return; + } + void calculateCroppedImage(imageSrc, crop).then((blob) => { + setImageBlob(blob); + setStep('alt'); + }); + }, + [imageSrc], + ); + + const dispatch = useAppDispatch(); + const handleSave = useCallback( + (altText: string) => { + if (!imageBlob) { + setStep('crop'); + return; + } + void dispatch(uploadImage({ location, imageBlob, altText })).then( + onClose, + ); + }, + [dispatch, imageBlob, location, onClose], + ); + + const handleCancel = useCallback(() => { + switch (step) { + case 'crop': + setImageSrc(null); + setStep('select'); + break; + case 'alt': + setImageBlob(null); + setStep('crop'); + break; + default: + onClose(); + } + }, [onClose, step]); + + return ( + + ) : ( + + ) + } + onClose={onClose} + wrapperClassName={classes.uploadWrapper} + noCancelButton + > + {step === 'select' && ( + + )} + {step === 'crop' && imageSrc && ( + + )} + {step === 'alt' && imageBlob && ( + + )} + + ); }; + +// Taken from app/models/concerns/account/header.rb and app/models/concerns/account/avatar.rb +const ALLOWED_MIME_TYPES = [ + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/webp', +]; + +const StepUpload: FC<{ + location: ImageLocation; + onFile: (file: File) => void; +}> = ({ location, onFile }) => { + const inputRef = useRef(null); + const handleUploadClick = useCallback(() => { + inputRef.current?.click(); + }, []); + + const handleFileChange: ChangeEventHandler = useCallback( + (event) => { + const file = event.currentTarget.files?.[0]; + if (!file || !ALLOWED_MIME_TYPES.includes(file.type)) { + return; + } + onFile(file); + }, + [onFile], + ); + + // Handle drag and drop + const [isDragging, setDragging] = useState(false); + + const handleDragOver = useCallback((event: DragEvent) => { + event.preventDefault(); + if (!event.dataTransfer?.types.includes('Files')) { + return; + } + + const items = Array.from(event.dataTransfer.items); + if ( + !items.some( + (item) => + item.kind === 'file' && ALLOWED_MIME_TYPES.includes(item.type), + ) + ) { + return; + } + + setDragging(true); + }, []); + const handleDragDrop = useCallback( + (event: DragEvent) => { + event.preventDefault(); + setDragging(false); + + if (!event.dataTransfer?.files) { + return; + } + + const file = Array.from(event.dataTransfer.files).find((f) => + ALLOWED_MIME_TYPES.includes(f.type), + ); + if (!file) { + return; + } + + onFile(file); + }, + [onFile], + ); + const handleDragLeave = useCallback((event: DragEvent) => { + event.preventDefault(); + setDragging(false); + }, []); + + const dispatch = useAppDispatch(); + useEffect(() => { + dispatch(setDragUploadEnabled(false)); + document.addEventListener('dragover', handleDragOver); + document.addEventListener('drop', handleDragDrop); + document.addEventListener('dragleave', handleDragLeave); + + return () => { + document.removeEventListener('dragover', handleDragOver); + document.removeEventListener('drop', handleDragDrop); + document.removeEventListener('dragleave', handleDragLeave); + dispatch(setDragUploadEnabled(true)); + }; + }, [handleDragLeave, handleDragDrop, handleDragOver, dispatch]); + + if (isDragging) { + return ( +
+ +
+ ); + } + + return ( +
+ + , + limit: 8, + width: location === 'avatar' ? 400 : 1500, + height: location === 'avatar' ? 400 : 500, + }} + tagName='p' + /> + + + +
+ ); +}; + +const zoomLabel = defineMessage({ + id: 'account_edit.upload_modal.step_crop.zoom', + defaultMessage: 'Zoom', +}); + +const StepCrop: FC<{ + src: string; + location: ImageLocation; + onCancel: () => void; + onComplete: (crop: Area) => void; +}> = ({ src, location, onCancel, onComplete }) => { + const [crop, setCrop] = useState({ x: 0, y: 0 }); + const [croppedArea, setCroppedArea] = useState(null); + const [zoom, setZoom] = useState(1); + const intl = useIntl(); + + const handleZoomChange: ChangeEventHandler = useCallback( + (event) => { + setZoom(event.currentTarget.valueAsNumber); + }, + [], + ); + const handleCropComplete = useCallback((_: Area, croppedAreaPixels: Area) => { + setCroppedArea(croppedAreaPixels); + }, []); + + const handleNext = useCallback(() => { + if (croppedArea) { + onComplete(croppedArea); + } + }, [croppedArea, onComplete]); + + return ( + <> +
+ +
+ +
+ + + +
+ + ); +}; + +const StepAlt: FC<{ + imageBlob: Blob; + onCancel: () => void; + onComplete: (altText: string) => void; +}> = ({ imageBlob, onCancel, onComplete }) => { + const [altText, setAltText] = useState(''); + + const handleChange: ChangeEventHandler = useCallback( + (event) => { + setAltText(event.currentTarget.value); + }, + [], + ); + + const handleComplete = useCallback(() => { + onComplete(altText); + }, [altText, onComplete]); + + const imageSrc = useMemo(() => URL.createObjectURL(imageBlob), [imageBlob]); + const altLimit = useAppSelector( + (state) => + state.server.getIn( + ['server', 'configuration', 'media_attachments', 'description_limit'], + 150, + ) as number, + ); + + return ( + <> + + +
+ + } + hint={ + + } + onChange={handleChange} + /> + +
+ + + } + > + + + +
+ + + +
+ + ); +}; + +async function calculateCroppedImage( + imageSrc: string, + crop: Area, +): Promise { + const image = await dataUriToImage(imageSrc); + const canvas = new OffscreenCanvas(crop.width, crop.height); + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Failed to get canvas context'); + } + + ctx.imageSmoothingQuality = 'high'; + + // Draw the image + ctx.drawImage( + image, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height, + ); + + return canvas.convertToBlob({ + quality: 0.7, + type: 'image/jpeg', + }); +} + +function dataUriToImage(dataUri: string) { + return new Promise((resolve, reject) => { + const image = new Image(); + image.addEventListener('load', () => { + resolve(image); + }); + image.addEventListener('error', (event) => { + if (event.error instanceof Error) { + reject(event.error); + } else { + reject(new Error('Failed to load image')); + } + }); + image.src = dataUri; + }); +} diff --git a/app/javascript/mastodon/features/account_edit/modals/styles.module.scss b/app/javascript/mastodon/features/account_edit/modals/styles.module.scss index 0bd4c07a1503aa..0a0a956eb5adca 100644 --- a/app/javascript/mastodon/features/account_edit/modals/styles.module.scss +++ b/app/javascript/mastodon/features/account_edit/modals/styles.module.scss @@ -80,6 +80,55 @@ } } +.uploadWrapper { + min-height: min(400px, 70vh); + justify-content: center; +} + +.uploadStepSelect { + text-align: center; + + h2 { + color: var(--color-text-primary); + font-size: 15px; + font-weight: 600; + margin-bottom: 4px; + } + + button { + margin-top: 16px; + } +} + +.cropContainer { + position: relative; + width: 100%; + height: 300px; + overflow: hidden; +} + +.cropActions { + margin-top: 8px; // 16px gap from DialogModal, plus 8px = 24px to look like normal action buttons. + display: flex; + gap: 8px; + align-items: center; + justify-content: flex-end; + + .zoomControl { + width: min(100%, 200px); + margin-right: auto; + } +} + +.altImage { + max-height: 300px; + object-fit: contain; +} + +.altCounter { + color: var(--color-text-secondary); +} + .verifiedSteps { font-size: 15px; diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 3e14b016e97a70..eae6d35a5f74c7 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -103,7 +103,11 @@ const mapStateToProps = state => ({ layout: state.getIn(['meta', 'layout']), isComposing: state.getIn(['compose', 'is_composing']), hasComposingContents: state.getIn(['compose', 'text']).trim().length !== 0 || state.getIn(['compose', 'media_attachments']).size > 0 || state.getIn(['compose', 'poll']) !== null || state.getIn(['compose', 'quoted_status_id']) !== null, - canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']), + canUploadMore: + !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) + && state.getIn(['compose', 'media_attachments']).size < state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']), + isUploadEnabled: + state.getIn(['compose', 'isDragDisabled']) !== true, firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION, newAccount: !state.getIn(['accounts', me, 'note']) && !state.getIn(['accounts', me, 'bot']) && state.getIn(['accounts', me, 'following_count'], 0) === 0 && state.getIn(['accounts', me, 'statuses_count'], 0) === 0, username: state.getIn(['accounts', me, 'username']), @@ -324,6 +328,9 @@ class UI extends PureComponent { }; handleDragEnter = (e) => { + if (!this.props.isUploadEnabled) { + return; + } e.preventDefault(); if (!this.dragTargets) { @@ -340,6 +347,9 @@ class UI extends PureComponent { }; handleDragOver = (e) => { + if (!this.props.isUploadEnabled) { + return; + } if (this.dataTransferIsText(e.dataTransfer)) return false; e.preventDefault(); @@ -355,6 +365,9 @@ class UI extends PureComponent { }; handleDrop = (e) => { + if (!this.props.isUploadEnabled) { + return; + } if (this.dataTransferIsText(e.dataTransfer)) return; e.preventDefault(); @@ -429,7 +442,6 @@ class UI extends PureComponent { document.addEventListener('dragover', this.handleDragOver, false); document.addEventListener('drop', this.handleDrop, false); document.addEventListener('dragleave', this.handleDragLeave, false); - document.addEventListener('dragend', this.handleDragEnd, false); if ('serviceWorker' in navigator) { navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage); @@ -456,7 +468,6 @@ class UI extends PureComponent { document.removeEventListener('dragover', this.handleDragOver); document.removeEventListener('drop', this.handleDrop); document.removeEventListener('dragleave', this.handleDragLeave); - document.removeEventListener('dragend', this.handleDragEnd); } setRef = c => { diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index db5c1313a549ef..26ce99c3dae1a9 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -203,6 +203,20 @@ "account_edit.profile_tab.subtitle": "Customize the tabs on your profile and what they display.", "account_edit.profile_tab.title": "Profile tab settings", "account_edit.save": "Save", + "account_edit.upload_modal.back": "Back", + "account_edit.upload_modal.done": "Done", + "account_edit.upload_modal.next": "Next", + "account_edit.upload_modal.step_alt.callout_text": "Adding alt text to media helps people using screen readers to understand your content.", + "account_edit.upload_modal.step_alt.callout_title": "Let’s make Mastodon accessible for all", + "account_edit.upload_modal.step_alt.text_hint": "E.g. “Close-up photo of me wearing glasses and a blue shirt”", + "account_edit.upload_modal.step_alt.text_label": "Alt text", + "account_edit.upload_modal.step_crop.zoom": "Zoom", + "account_edit.upload_modal.step_upload.button": "Browse files", + "account_edit.upload_modal.step_upload.dragging": "Drop to upload", + "account_edit.upload_modal.step_upload.header": "Choose an image", + "account_edit.upload_modal.step_upload.hint": "WEBP, PNG, GIF or JPG format, up to {limit}MB.{br}Image will be scaled to {width}x{height}px.", + "account_edit.upload_modal.title_add": "Add profile photo", + "account_edit.upload_modal.title_replace": "Replace profile photo", "account_edit.verified_modal.details": "Add credibility to your Mastodon profile by verifying links to personal websites. Here’s how it works:", "account_edit.verified_modal.invisible_link.details": "Add the link to your header. The important part is rel=\"me\" which prevents impersonation on websites with user-generated content. You can even use a link tag in the header of the page instead of {tag}, but the HTML must be accessible without executing JavaScript.", "account_edit.verified_modal.invisible_link.summary": "How do I make the link invisible?", diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 51508c777d8f8a..705b3186ba225d 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -8,6 +8,7 @@ import { setComposeQuotePolicy, pasteLinkCompose, cancelPasteLinkCompose, + setDragUploadEnabled, } from '@/mastodon/actions/compose_typed'; import { timelineDelete } from 'mastodon/actions/timelines_typed'; @@ -75,6 +76,7 @@ const initialState = ImmutableMap({ is_submitting: false, is_changing_upload: false, is_uploading: false, + isDragDisabled: false, should_redirect_to_compose_page: false, progress: 0, isUploadingThumbnail: false, @@ -132,6 +134,7 @@ function clearAll(state) { map.set('idempotencyKey', uuid()); map.set('quoted_status_id', null); map.set('quote_policy', state.get('default_quote_policy')); + map.set('isDragDisabled', false); }); } @@ -359,6 +362,8 @@ export const composeReducer = (state = initialState, action) => { return action.meta.requestId === state.get('fetching_link') ? state.set('fetching_link', null) : state; } else if (cancelPasteLinkCompose.match(action)) { return state.set('fetching_link', null); + } else if (setDragUploadEnabled.match(action)) { + return state.set('isDragDisabled', !action.payload); } switch(action.type) { diff --git a/app/javascript/mastodon/reducers/slices/profile_edit.ts b/app/javascript/mastodon/reducers/slices/profile_edit.ts index 62a908e5b12f34..7efd71eb3ea086 100644 --- a/app/javascript/mastodon/reducers/slices/profile_edit.ts +++ b/app/javascript/mastodon/reducers/slices/profile_edit.ts @@ -109,6 +109,17 @@ const profileEditSlice = createSlice({ state.isPending = false; }); + builder.addCase(uploadImage.pending, (state) => { + state.isPending = true; + }); + builder.addCase(uploadImage.rejected, (state) => { + state.isPending = false; + }); + builder.addCase(uploadImage.fulfilled, (state, action) => { + state.profile = action.payload; + state.isPending = false; + }); + builder.addCase(addFeaturedTag.pending, (state) => { state.isPending = true; }); @@ -229,6 +240,41 @@ export const patchProfile = createDataLoadingThunk( }, ); +export type ImageLocation = 'avatar' | 'header'; + +export const selectImageInfo = createAppSelector( + [ + (state) => state.profileEdit.profile, + (_, location: ImageLocation) => location, + ], + (profile, location) => { + if (!profile) { + return {}; + } + + return { + src: profile[location], + static: profile[`${location}Static`], + alt: profile[`${location}Description`], + }; + }, +); + +export const uploadImage = createDataLoadingThunk( + `${profileEditSlice.name}/uploadImage`, + (arg: { location: ImageLocation; imageBlob: Blob; altText: string }) => { + // Note: Alt text is not actually supported by the API yet. + const formData = new FormData(); + formData.append(arg.location, arg.imageBlob); + + return apiPatchProfile(formData); + }, + transformProfile, + { + useLoadingBar: false, + }, +); + export const selectFieldById = createAppSelector( [(state) => state.profileEdit.profile?.fields, (_, id?: string) => id], (fields, fieldId) => { diff --git a/package.json b/package.json index e196a7c596bdb2..9ab025be45bcc0 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "punycode": "^2.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-easy-crop": "^5.5.6", "react-helmet": "^6.1.0", "react-immutable-proptypes": "^2.2.0", "react-immutable-pure-component": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index d99c4b2aa54e7c..2795e9bd97ef6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2919,6 +2919,7 @@ __metadata: punycode: "npm:^2.3.0" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" + react-easy-crop: "npm:^5.5.6" react-helmet: "npm:^6.1.0" react-immutable-proptypes: "npm:^2.2.0" react-immutable-pure-component: "npm:^2.2.2" @@ -10244,6 +10245,13 @@ __metadata: languageName: node linkType: hard +"normalize-wheel@npm:^1.0.1": + version: 1.0.1 + resolution: "normalize-wheel@npm:1.0.1" + checksum: 10c0/5daf4c97e39f36658a5263a6499bbc148676ae2bd85f12c8d03c46ffe7bc3c68d44564c00413d88d0457ac0d94450559bb1c24c2ce7ae0c107031f82d093ac06 + languageName: node + linkType: hard + "object-assign@npm:^4, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -11703,6 +11711,19 @@ __metadata: languageName: node linkType: hard +"react-easy-crop@npm:^5.5.6": + version: 5.5.6 + resolution: "react-easy-crop@npm:5.5.6" + dependencies: + normalize-wheel: "npm:^1.0.1" + tslib: "npm:^2.0.1" + peerDependencies: + react: ">=16.4.0" + react-dom: ">=16.4.0" + checksum: 10c0/ce623791d31559fc46f210ece7b22c0f659710d5de219ef9fb05650940f50445d5e6573ed229b66fad06dfda9651ae458c0f5efb8e1cabdf01511dc32942cdc8 + languageName: node + linkType: hard + "react-fast-compare@npm:^3.1.1": version: 3.2.2 resolution: "react-fast-compare@npm:3.2.2" From f9b2dffaa8bb7d6451eb9b3c3766299094dbad86 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 09:55:53 -0400 Subject: [PATCH 080/203] Use `JSON.generate` call in push update worker (#38208) --- app/workers/push_update_worker.rb | 6 +++--- spec/workers/push_update_worker_spec.rb | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/workers/push_update_worker.rb b/app/workers/push_update_worker.rb index c32975a986c3c5..63240d50b0bfc5 100644 --- a/app/workers/push_update_worker.rb +++ b/app/workers/push_update_worker.rb @@ -23,10 +23,10 @@ def render_payload! end def message - Oj.dump( + JSON.generate({ event: update? ? :'status.update' : :update, - payload: @payload - ) + payload: @payload, + }.as_json) end def publish! diff --git a/spec/workers/push_update_worker_spec.rb b/spec/workers/push_update_worker_spec.rb index f3e0a128df55b8..a423031fb75aac 100644 --- a/spec/workers/push_update_worker_spec.rb +++ b/spec/workers/push_update_worker_spec.rb @@ -15,7 +15,7 @@ context 'with valid records' do let(:account) { Fabricate :account } - let(:status) { Fabricate :status } + let(:status) { Fabricate :status, text: 'Test Post' } before { allow(redis).to receive(:publish) } @@ -25,7 +25,16 @@ expect(redis) .to have_received(:publish) - .with(redis_key, anything) + .with( + redis_key, + match_json_values( + event: 'update', + payload: include( + created_at: status.created_at.iso8601(3), + content: eq('

Test Post

') + ) + ) + ) end def redis_key From 60442197469edc61bc9b6ecb96cd5e7dbc47b576 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 09:55:58 -0400 Subject: [PATCH 081/203] Use `to_json` call for raw event strings (#38215) --- app/lib/access_token_extension.rb | 2 +- app/lib/application_extension.rb | 2 +- app/lib/feed_manager.rb | 4 ++-- app/models/concerns/account/suspensions.rb | 2 +- app/models/custom_filter.rb | 4 ++-- app/models/user.rb | 4 ++-- app/services/batched_remove_status_service.rb | 2 +- app/services/notify_service.rb | 2 +- app/services/remove_status_service.rb | 2 +- app/workers/publish_announcement_reaction_worker.rb | 2 +- app/workers/publish_scheduled_announcement_worker.rb | 2 +- app/workers/push_conversation_worker.rb | 2 +- app/workers/unfilter_notifications_worker.rb | 2 +- app/workers/unpublish_announcement_worker.rb | 2 +- spec/lib/feed_manager_spec.rb | 2 +- spec/models/user_spec.rb | 4 ++-- spec/requests/api/v2/filters_spec.rb | 2 +- spec/services/remove_status_service_spec.rb | 2 +- 18 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/lib/access_token_extension.rb b/app/lib/access_token_extension.rb index 6e06f988a5e3db..268232a436ecdc 100644 --- a/app/lib/access_token_extension.rb +++ b/app/lib/access_token_extension.rb @@ -24,6 +24,6 @@ def update_last_used(request, clock = Time) end def push_to_streaming_api - redis.publish("timeline:access_token:#{id}", Oj.dump(event: :kill)) if revoked? || destroyed? + redis.publish("timeline:access_token:#{id}", { event: :kill }.to_json) if revoked? || destroyed? end end diff --git a/app/lib/application_extension.rb b/app/lib/application_extension.rb index bc6c7561cca464..b8906d339bfe16 100644 --- a/app/lib/application_extension.rb +++ b/app/lib/application_extension.rb @@ -35,7 +35,7 @@ def redirect_uris def close_streaming_sessions(resource_owner = nil) # TODO: #28793 Combine into a single topic - payload = Oj.dump(event: :kill) + payload = { event: :kill }.to_json scope = access_tokens scope = scope.where(resource_owner_id: resource_owner.id) unless resource_owner.nil? scope.in_batches do |tokens| diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 18a58156c39d0c..444b96c7ce5bb1 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -90,7 +90,7 @@ def push_to_home(account, status, update: false) def unpush_from_home(account, status, update: false) return false unless remove_from_feed(:home, account.id, status, aggregate_reblogs: account.user&.aggregates_reblogs?) - redis.publish("timeline:#{account.id}", Oj.dump(event: :delete, payload: status.id.to_s)) unless update + redis.publish("timeline:#{account.id}", { event: :delete, payload: status.id.to_s }.to_json) unless update true end @@ -117,7 +117,7 @@ def push_to_list(list, status, update: false) def unpush_from_list(list, status, update: false) return false unless remove_from_feed(:list, list.id, status, aggregate_reblogs: list.account.user&.aggregates_reblogs?) - redis.publish("timeline:list:#{list.id}", Oj.dump(event: :delete, payload: status.id.to_s)) unless update + redis.publish("timeline:list:#{list.id}", { event: :delete, payload: status.id.to_s }.to_json) unless update true end diff --git a/app/models/concerns/account/suspensions.rb b/app/models/concerns/account/suspensions.rb index 4c9ca593ad036d..28c6bb8c660823 100644 --- a/app/models/concerns/account/suspensions.rb +++ b/app/models/concerns/account/suspensions.rb @@ -35,7 +35,7 @@ def suspend!(date: Time.now.utc, origin: :local, block_email: true) # This terminates all connections for the given account with the streaming # server: - redis.publish("timeline:system:#{id}", Oj.dump(event: :kill)) if local? + redis.publish("timeline:system:#{id}", { event: :kill }.to_json) if local? end def unsuspend! diff --git a/app/models/custom_filter.rb b/app/models/custom_filter.rb index 1151c7de985927..a5d8e937e38691 100644 --- a/app/models/custom_filter.rb +++ b/app/models/custom_filter.rb @@ -115,8 +115,8 @@ def invalidate_cache! @should_invalidate_cache = false Rails.cache.delete("filters:v3:#{account_id}") - redis.publish("timeline:#{account_id}", Oj.dump(event: :filters_changed)) - redis.publish("timeline:system:#{account_id}", Oj.dump(event: :filters_changed)) + redis.publish("timeline:#{account_id}", { event: :filters_changed }.to_json) + redis.publish("timeline:system:#{account_id}", { event: :filters_changed }.to_json) end private diff --git a/app/models/user.rb b/app/models/user.rb index dd029d8e08257c..a774d8953a1efe 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -173,7 +173,7 @@ def disable! # This terminates all connections for the given account with the streaming # server: - redis.publish("timeline:system:#{account.id}", Oj.dump(event: :kill)) + redis.publish("timeline:system:#{account.id}", { event: :kill }.to_json) end def enable! @@ -347,7 +347,7 @@ def revoke_access! # Revoke each access token for the Streaming API, since `update_all`` # doesn't trigger ActiveRecord Callbacks: # TODO: #28793 Combine into a single topic - payload = Oj.dump(event: :kill) + payload = { event: :kill }.to_json redis.pipelined do |pipeline| batch.ids.each do |id| pipeline.publish("timeline:access_token:#{id}", payload) diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index 826dbcc720e212..4dad80fc11b29c 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -82,7 +82,7 @@ def unpush_from_list_timelines(account, statuses) def unpush_from_public_timelines(status, pipeline) return unless status.public_visibility? && status.id > @status_id_cutoff - payload = Oj.dump(event: :delete, payload: status.id.to_s) + payload = { event: :delete, payload: status.id.to_s }.to_json pipeline.publish('timeline:public', payload) pipeline.publish(status.local? ? 'timeline:public:local' : 'timeline:public:remote', payload) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 2f009d5a23ba9c..ed292736d84c13 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -259,7 +259,7 @@ def push_notification! end def push_to_streaming_api! - redis.publish("timeline:#{@recipient.id}:notifications", Oj.dump(event: :notification, payload: InlineRenderer.render(@notification, @recipient, :notification))) + redis.publish("timeline:#{@recipient.id}:notifications", { event: :notification, payload: InlineRenderer.render(@notification, @recipient, :notification) }.to_json) end def subscribed_to_streaming_api? diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index caa22b4729146b..042a20ec790ffb 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -14,7 +14,7 @@ class RemoveStatusService < BaseService # @option [Boolean] :original_removed # @option [Boolean] :skip_streaming def call(status, **options) - @payload = Oj.dump(event: :delete, payload: status.id.to_s) + @payload = { event: :delete, payload: status.id.to_s }.to_json @status = status @account = status.account @options = options diff --git a/app/workers/publish_announcement_reaction_worker.rb b/app/workers/publish_announcement_reaction_worker.rb index 03da56550aadb8..7cf7393c1d13bc 100644 --- a/app/workers/publish_announcement_reaction_worker.rb +++ b/app/workers/publish_announcement_reaction_worker.rb @@ -11,7 +11,7 @@ def perform(announcement_id, name) reaction ||= announcement.announcement_reactions.new(name: name) payload = InlineRenderer.render(reaction, nil, :reaction).tap { |h| h[:announcement_id] = announcement_id.to_s } - payload = Oj.dump(event: :'announcement.reaction', payload: payload) + payload = { event: :'announcement.reaction', payload: payload } FeedManager.instance.with_active_accounts do |account| redis.publish("timeline:#{account.id}", payload) if redis.exists?("subscribed:timeline:#{account.id}") diff --git a/app/workers/publish_scheduled_announcement_worker.rb b/app/workers/publish_scheduled_announcement_worker.rb index c23eae6af7c4a4..63f1600d341226 100644 --- a/app/workers/publish_scheduled_announcement_worker.rb +++ b/app/workers/publish_scheduled_announcement_worker.rb @@ -12,7 +12,7 @@ def perform(announcement_id) @announcement.publish! unless @announcement.published? payload = InlineRenderer.render(@announcement, nil, :announcement) - payload = Oj.dump(event: :announcement, payload: payload) + payload = { event: :announcement, payload: payload }.to_json FeedManager.instance.with_active_accounts do |account| redis.publish("timeline:#{account.id}", payload) if redis.exists?("subscribed:timeline:#{account.id}") diff --git a/app/workers/push_conversation_worker.rb b/app/workers/push_conversation_worker.rb index 23b1469f1110cf..b3990c14797490 100644 --- a/app/workers/push_conversation_worker.rb +++ b/app/workers/push_conversation_worker.rb @@ -9,7 +9,7 @@ def perform(conversation_account_id) message = InlineRenderer.render(conversation, conversation.account, :conversation) timeline_id = "timeline:direct:#{conversation.account_id}" - redis.publish(timeline_id, Oj.dump(event: :conversation, payload: message)) + redis.publish(timeline_id, { event: :conversation, payload: message }.to_json) rescue ActiveRecord::RecordNotFound true end diff --git a/app/workers/unfilter_notifications_worker.rb b/app/workers/unfilter_notifications_worker.rb index cb8a46b8f4e835..7b57a2db136eaa 100644 --- a/app/workers/unfilter_notifications_worker.rb +++ b/app/workers/unfilter_notifications_worker.rb @@ -39,7 +39,7 @@ def decrement_worker_count! end def push_streaming_event! - redis.publish("timeline:#{@recipient.id}:notifications", Oj.dump(event: :notifications_merged, payload: '1')) + redis.publish("timeline:#{@recipient.id}:notifications", { event: :notifications_merged, payload: '1' }.to_json) end def subscribed_to_streaming_api? diff --git a/app/workers/unpublish_announcement_worker.rb b/app/workers/unpublish_announcement_worker.rb index e58c07554a75e5..1b61bacb24ac2f 100644 --- a/app/workers/unpublish_announcement_worker.rb +++ b/app/workers/unpublish_announcement_worker.rb @@ -5,7 +5,7 @@ class UnpublishAnnouncementWorker include Redisable def perform(announcement_id) - payload = Oj.dump(event: :'announcement.delete', payload: announcement_id.to_s) + payload = { event: :'announcement.delete', payload: announcement_id.to_s }.to_json FeedManager.instance.with_active_accounts do |account| redis.publish("timeline:#{account.id}", payload) if redis.exists?("subscribed:timeline:#{account.id}") diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb index 0d0c817b6c9514..c8e44190bdafa2 100644 --- a/spec/lib/feed_manager_spec.rb +++ b/spec/lib/feed_manager_spec.rb @@ -546,7 +546,7 @@ allow(redis).to receive_messages(publish: nil) subject.unpush_from_home(receiver, status) - deletion = Oj.dump(event: :delete, payload: status.id.to_s) + deletion = { event: :delete, payload: status.id.to_s }.to_json expect(redis).to have_received(:publish).with("timeline:#{receiver.id}", deletion) end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 187f05f02e85c4..a7ac034f0a8cd5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -403,7 +403,7 @@ expect(user).to have_attributes(disabled: true) expect(redis) - .to have_received(:publish).with("timeline:system:#{user.account.id}", Oj.dump(event: :kill)).once + .to have_received(:publish).with("timeline:system:#{user.account.id}", { event: :kill }.to_json).once end end @@ -445,7 +445,7 @@ expect { web_push_subscription.reload } .to raise_error(ActiveRecord::RecordNotFound) expect(redis_pipeline_stub) - .to have_received(:publish).with("timeline:access_token:#{access_token.id}", Oj.dump(event: :kill)).once + .to have_received(:publish).with("timeline:access_token:#{access_token.id}", { event: :kill }.to_json).once end def remove_activated_sessions diff --git a/spec/requests/api/v2/filters_spec.rb b/spec/requests/api/v2/filters_spec.rb index cfa607cff06cfb..4613d4f7b4ad4d 100644 --- a/spec/requests/api/v2/filters_spec.rb +++ b/spec/requests/api/v2/filters_spec.rb @@ -222,7 +222,7 @@ expect(keyword.reload.keyword).to eq 'updated' - expect(redis).to have_received(:publish).with("timeline:#{user.account.id}", Oj.dump(event: :filters_changed)).once + expect(redis).to have_received(:publish).with("timeline:#{user.account.id}", { event: :filters_changed }.to_json).once end end diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb index 3cb2eceec5966a..91a902b7334fa3 100644 --- a/spec/services/remove_status_service_spec.rb +++ b/spec/services/remove_status_service_spec.rb @@ -40,7 +40,7 @@ .to_not include(status.id) expect(redis) - .to have_received(:publish).with('timeline:public:media', Oj.dump(event: :delete, payload: status.id.to_s)) + .to have_received(:publish).with('timeline:public:media', { event: :delete, payload: status.id.to_s }.to_json) expect(delete_delivery(hank, status)) .to have_been_made.once From 638429037f72d5b88ba85a069451f785af51549d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 09:58:39 -0400 Subject: [PATCH 082/203] Use `to_json` call for libre translate api (#38216) --- app/lib/translation_service/libre_translate.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/translation_service/libre_translate.rb b/app/lib/translation_service/libre_translate.rb index 0df8590f870939..a63cc723f194a5 100644 --- a/app/lib/translation_service/libre_translate.rb +++ b/app/lib/translation_service/libre_translate.rb @@ -9,7 +9,7 @@ def initialize(base_url, api_key) end def translate(texts, source_language, target_language) - body = Oj.dump(q: texts, source: source_language.presence || 'auto', target: target_language, format: 'html', api_key: @api_key) + body = { q: texts, source: source_language.presence || 'auto', target: target_language, format: 'html', api_key: @api_key }.to_json request(:post, '/translate', body: body) do |res| transform_response(res.body_with_limit, source_language) end From 330357507de2a0f7fd30ca0ac21011ffec7b0088 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:01:37 -0400 Subject: [PATCH 083/203] Use `to_json` call for webhook service (#38217) --- app/services/webhook_service.rb | 4 +++- spec/services/webhook_service_spec.rb | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/services/webhook_service.rb b/app/services/webhook_service.rb index aafa38318173bb..5441c754f323c4 100644 --- a/app/services/webhook_service.rb +++ b/app/services/webhook_service.rb @@ -17,6 +17,8 @@ def webhooks_for_event end def serialize_event - Oj.dump(ActiveModelSerializers::SerializableResource.new(@event, serializer: REST::Admin::WebhookEventSerializer, scope: nil, scope_name: :current_user).as_json) + ActiveModelSerializers::SerializableResource + .new(@event, serializer: REST::Admin::WebhookEventSerializer, scope: nil, scope_name: :current_user) + .to_json end end diff --git a/spec/services/webhook_service_spec.rb b/spec/services/webhook_service_spec.rb index 22a60db9f59efa..8c515423668320 100644 --- a/spec/services/webhook_service_spec.rb +++ b/spec/services/webhook_service_spec.rb @@ -8,12 +8,14 @@ let!(:report) { Fabricate(:report) } let!(:webhook) { Fabricate(:webhook, events: ['report.created']) } + before { freeze_time Time.current } + it 'finds and delivers webhook payloads' do expect { subject.call('report.created', report) } .to enqueue_sidekiq_job(Webhooks::DeliveryWorker) .with( webhook.id, - anything + match_json_values(event: 'report.created', created_at: Time.current.iso8601(3)) ) end end From 1a464bc5ededfad97e9a97d0c7f89f5a0a0ae493 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:02:52 -0400 Subject: [PATCH 084/203] Use `to_json` in simple view hash data-props build locations (#38218) --- app/views/shared/_web_app.html.haml | 2 +- app/views/shares/show.html.haml | 2 +- app/views/statuses/embed.html.haml | 2 +- spec/requests/statuses/embed_spec.rb | 2 ++ spec/system/home_spec.rb | 2 ++ spec/system/share_entrypoint_spec.rb | 2 ++ 6 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/views/shared/_web_app.html.haml b/app/views/shared/_web_app.html.haml index 5e6815165f4457..367d1c52d2a8ac 100644 --- a/app/views/shared/_web_app.html.haml +++ b/app/views/shared/_web_app.html.haml @@ -8,7 +8,7 @@ = render_initial_state = vite_typescript_tag 'application.ts', crossorigin: 'anonymous' -.notranslate.app-holder#mastodon{ data: { props: Oj.dump(default_props) } } +.notranslate.app-holder#mastodon{ data: { props: default_props.to_json } } %noscript = image_tag frontend_asset_path('images/logo.svg'), alt: 'Mastodon' diff --git a/app/views/shares/show.html.haml b/app/views/shares/show.html.haml index c12b43031e5980..1d9717f1f89d7f 100644 --- a/app/views/shares/show.html.haml +++ b/app/views/shares/show.html.haml @@ -2,4 +2,4 @@ = render_initial_state = vite_typescript_tag 'share.tsx', crossorigin: 'anonymous' -#mastodon-compose{ data: { props: Oj.dump(default_props) } } +#mastodon-compose{ data: { props: default_props.to_json } } diff --git a/app/views/statuses/embed.html.haml b/app/views/statuses/embed.html.haml index 09d0792ea2549e..74c807a89ed50e 100644 --- a/app/views/statuses/embed.html.haml +++ b/app/views/statuses/embed.html.haml @@ -1 +1 @@ -#mastodon-status{ data: { props: Oj.dump(default_props.merge(id: @status.id.to_s)) } } +#mastodon-status{ data: { props: default_props.merge(id: @status.id.to_s).to_json } } diff --git a/spec/requests/statuses/embed_spec.rb b/spec/requests/statuses/embed_spec.rb index 33c7ea192c80a3..7fc1b0125c8199 100644 --- a/spec/requests/statuses/embed_spec.rb +++ b/spec/requests/statuses/embed_spec.rb @@ -41,6 +41,8 @@ .to have_http_status(200) expect(response.parsed_body.at('body.embed')) .to be_present + expect(response.parsed_body.at('#mastodon-status')['data-props']) + .to eq({ locale: 'en', id: status.id.to_s }.to_json) expect(response.headers).to include( 'Vary' => 'Accept, Accept-Language, Cookie', 'Cache-Control' => include('public'), diff --git a/spec/system/home_spec.rb b/spec/system/home_spec.rb index aafa9323c0b07e..e839ae160be430 100644 --- a/spec/system/home_spec.rb +++ b/spec/system/home_spec.rb @@ -12,6 +12,8 @@ expect(page) .to have_css('noscript', text: /Mastodon/) .and have_css('body', class: 'app-body') + expect(find('.app-holder#mastodon')['data-props']) + .to eq('{"locale":"en"}') end end diff --git a/spec/system/share_entrypoint_spec.rb b/spec/system/share_entrypoint_spec.rb index 0f07d96efebd78..8f91d28a12d6e3 100644 --- a/spec/system/share_entrypoint_spec.rb +++ b/spec/system/share_entrypoint_spec.rb @@ -19,6 +19,8 @@ .to have_css('.modal-layout__mastodon') .and have_css('div#mastodon-compose') .and have_css('.compose-form__submit') + expect(find_by_id('mastodon-compose')['data-props']) + .to eq('{"locale":"en"}') fill_in_form From 8124f1581aa3770d7e61e920dfc8567192beae00 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:03:20 -0400 Subject: [PATCH 085/203] Use `to_json` call in cli/domains (#38219) --- lib/mastodon/cli/domains.rb | 2 +- spec/lib/mastodon/cli/domains_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mastodon/cli/domains.rb b/lib/mastodon/cli/domains.rb index c247463af52b13..cbd3465e04c9e2 100644 --- a/lib/mastodon/cli/domains.rb +++ b/lib/mastodon/cli/domains.rb @@ -214,7 +214,7 @@ def stats_to_domains(stats) def stats_to_json(stats) stats.compact! - say(Oj.dump(stats)) + say(stats.to_json) end end end diff --git a/spec/lib/mastodon/cli/domains_spec.rb b/spec/lib/mastodon/cli/domains_spec.rb index d1c26546f04dd9..0d2c7f70a870c1 100644 --- a/spec/lib/mastodon/cli/domains_spec.rb +++ b/spec/lib/mastodon/cli/domains_spec.rb @@ -87,7 +87,7 @@ end def json_summary - Oj.dump('host.example': { activity: {} }) + JSON.generate('host.example': { activity: {} }) end end end From b7246518bfc9953b2c507c2da0580d375bdfbc88 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 16 Mar 2026 15:04:25 +0100 Subject: [PATCH 086/203] Add `avatar_description` and `header_description` parameters to `PATCH /api/v1/profile` (#38221) --- app/controllers/api/v1/profiles_controller.rb | 2 ++ lib/mastodon/version.rb | 2 +- spec/requests/api/v1/profiles_spec.rb | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/profiles_controller.rb b/app/controllers/api/v1/profiles_controller.rb index 196d0ef3a7012a..02907f4fb44461 100644 --- a/app/controllers/api/v1/profiles_controller.rb +++ b/app/controllers/api/v1/profiles_controller.rb @@ -25,7 +25,9 @@ def account_params :display_name, :note, :avatar, + :avatar_description, :header, + :header_description, :locked, :bot, :discoverable, diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 76c849e8f6da59..e71f4d2d725950 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -45,7 +45,7 @@ def gem_version def api_versions { - mastodon: 8, + mastodon: 9, } end diff --git a/spec/requests/api/v1/profiles_spec.rb b/spec/requests/api/v1/profiles_spec.rb index b2c74b0191954d..7d0d1a3622465d 100644 --- a/spec/requests/api/v1/profiles_spec.rb +++ b/spec/requests/api/v1/profiles_spec.rb @@ -62,6 +62,7 @@ let(:params) do { avatar: fixture_file_upload('avatar.gif', 'image/gif'), + avatar_description: 'animated walking round cat', discoverable: true, display_name: "Alice Isn't Dead", header: fixture_file_upload('attachment.jpg', 'image/jpeg'), @@ -110,6 +111,7 @@ display_name: eq("Alice Isn't Dead"), note: 'Hello!', avatar: exist, + avatar_description: 'animated walking round cat', header: exist, attribution_domains: ['example.com'], fields: contain_exactly( From c05492ed5a549e483bb15f968402a2af98178477 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:31:58 -0400 Subject: [PATCH 087/203] Use `JSON.generate` call for fan out service (#38222) --- app/services/fan_out_on_write_service.rb | 6 +++--- spec/services/fan_out_on_write_service_spec.rb | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 727a6a63c4bc7d..d97e3e0fb2877c 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -171,10 +171,10 @@ def warm_payload_cache! end def anonymous_payload - @anonymous_payload ||= Oj.dump( + @anonymous_payload ||= JSON.generate({ event: update? ? :'status.update' : :update, - payload: rendered_status - ) + payload: rendered_status, + }.as_json) end def rendered_status diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb index 79ecd06c8d960c..efc70d84c61a85 100644 --- a/spec/services/fan_out_on_write_service_spec.rb +++ b/spec/services/fan_out_on_write_service_spec.rb @@ -54,11 +54,18 @@ def home_feed_of(account) .and be_in(home_feed_of(bob)) .and be_in(home_feed_of(tom)) - expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything) - expect(redis).to have_received(:publish).with('timeline:hashtag:hoge:local', anything) - expect(redis).to have_received(:publish).with('timeline:public', anything) - expect(redis).to have_received(:publish).with('timeline:public:local', anything) - expect(redis).to have_received(:publish).with('timeline:public:media', anything) + expected_payload = { event: 'update', payload: include(id: status.id.to_s, created_at: status.created_at.iso8601(3), content: /

Hello/) } + + expect(redis) + .to have_received(:publish).with('timeline:hashtag:hoge', match_json_values(expected_payload)) + expect(redis) + .to have_received(:publish).with('timeline:hashtag:hoge:local', match_json_values(expected_payload)) + expect(redis) + .to have_received(:publish).with('timeline:public', match_json_values(expected_payload)) + expect(redis) + .to have_received(:publish).with('timeline:public:local', match_json_values(expected_payload)) + expect(redis) + .to have_received(:publish).with('timeline:public:media', match_json_values(expected_payload)) end context 'with silenced_account_ids' do From 7933fa4f94fdfd5f09f1400313c223c93a73ac5c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:32:05 -0400 Subject: [PATCH 088/203] Use `to_json` call in donation campaigns (#38223) --- app/controllers/api/v1/donation_campaigns_controller.rb | 2 +- spec/requests/api/v1/donation_campaigns_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/donation_campaigns_controller.rb b/app/controllers/api/v1/donation_campaigns_controller.rb index cdd7503b304659..e1368e72f10444 100644 --- a/app/controllers/api/v1/donation_campaigns_controller.rb +++ b/app/controllers/api/v1/donation_campaigns_controller.rb @@ -44,7 +44,7 @@ def save_to_cache!(campaign) Rails.cache.write_multi( { request_key => campaign_key(campaign), - "donation_campaign:#{campaign_key(campaign)}" => Oj.dump(campaign), + "donation_campaign:#{campaign_key(campaign)}" => campaign.to_json, }, expires_in: 1.hour, raw: true diff --git a/spec/requests/api/v1/donation_campaigns_spec.rb b/spec/requests/api/v1/donation_campaigns_spec.rb index 2ab3fb8e8a68e8..5df360b728b384 100644 --- a/spec/requests/api/v1/donation_campaigns_spec.rb +++ b/spec/requests/api/v1/donation_campaigns_spec.rb @@ -78,7 +78,7 @@ end before do - stub_request(:get, "#{api_url}?platform=web&seed=#{seed}&locale=en").to_return(body: Oj.dump(campaign_json), status: 200) + stub_request(:get, "#{api_url}?platform=web&seed=#{seed}&locale=en").to_return(body: JSON.generate(campaign_json), status: 200) end it 'returns the expected campaign' do From 8ed13bc6f7aa886058105066560dfa5143dfffeb Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:40:03 -0400 Subject: [PATCH 089/203] Use `to_json` call for accounts API (#38226) --- app/controllers/api/v1/accounts_controller.rb | 2 +- spec/requests/api/v1/accounts_spec.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 936cd56eb8ef12..8738da3c941a9c 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -38,7 +38,7 @@ def create headers.merge!(response.headers) - self.response_body = Oj.dump(response.body) + self.response_body = response.body.to_json self.status = response.status rescue ActiveRecord::RecordInvalid => e render json: ValidationErrorFormatter.new(e, 'account.username': :username, 'invite_request.text': :reason).as_json, status: 422 diff --git a/spec/requests/api/v1/accounts_spec.rb b/spec/requests/api/v1/accounts_spec.rb index e3416fc3376e3a..0ea5c3921eff0a 100644 --- a/spec/requests/api/v1/accounts_spec.rb +++ b/spec/requests/api/v1/accounts_spec.rb @@ -185,7 +185,12 @@ expect(response).to have_http_status(200) expect(response.content_type) .to start_with('application/json') - expect(response.parsed_body[:access_token]).to_not be_blank + expect(response.parsed_body) + .to include( + access_token: be_present, + created_at: be_a(Integer), + token_type: 'Bearer' + ) user = User.find_by(email: 'hello@world.tld') expect(user).to_not be_nil From 70230c632cdb74ede2fa141d7bf8323be8d387cd Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:40:51 -0400 Subject: [PATCH 090/203] Use `to_json` call for AP::Follow reject path (#38227) --- app/lib/activitypub/activity/follow.rb | 2 +- spec/lib/activitypub/activity/follow_spec.rb | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index 97e41ab78950a9..083b03cb7b1833 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -39,7 +39,7 @@ def perform end def reject_follow_request!(target_account) - json = Oj.dump(serialize_payload(FollowRequest.new(account: @account, target_account: target_account, uri: @json['id']), ActivityPub::RejectFollowSerializer)) + json = serialize_payload(FollowRequest.new(account: @account, target_account: target_account, uri: @json['id']), ActivityPub::RejectFollowSerializer).to_json ActivityPub::DeliveryWorker.perform_async(json, target_account.id, @account.inbox_url) end end diff --git a/spec/lib/activitypub/activity/follow_spec.rb b/spec/lib/activitypub/activity/follow_spec.rb index c1829cb8d717d3..3660a3914a109f 100644 --- a/spec/lib/activitypub/activity/follow_spec.rb +++ b/spec/lib/activitypub/activity/follow_spec.rb @@ -84,6 +84,23 @@ end end + context 'when recipient blocks sender' do + before { Fabricate :block, account: recipient, target_account: sender } + + it 'sends a reject and does not follow' do + subject.perform + + expect(sender.requested?(recipient)) + .to be false + expect(ActivityPub::DeliveryWorker) + .to have_enqueued_sidekiq_job( + match_json_values(type: 'Reject', object: include(type: 'Follow')), + recipient.id, + anything + ) + end + end + context 'when a follow relationship already exists' do before do sender.active_relationships.create!(target_account: recipient, uri: 'bar') From d9cd65f039ba078d7347f2d598f96dc5f2c543f7 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:49:09 -0400 Subject: [PATCH 091/203] Use `to_json` call for AP::QuoteRequest accept/reject paths (#38229) --- app/lib/activitypub/activity/quote_request.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/activitypub/activity/quote_request.rb b/app/lib/activitypub/activity/quote_request.rb index 46c45cde276117..9e41bdec654d70 100644 --- a/app/lib/activitypub/activity/quote_request.rb +++ b/app/lib/activitypub/activity/quote_request.rb @@ -31,7 +31,7 @@ def accept_quote_request!(quoted_status) status.quote.update!(activity_uri: @json['id']) status.quote.accept! - json = Oj.dump(serialize_payload(status.quote, ActivityPub::AcceptQuoteRequestSerializer)) + json = serialize_payload(status.quote, ActivityPub::AcceptQuoteRequestSerializer).to_json ActivityPub::DeliveryWorker.perform_async(json, quoted_status.account_id, @account.inbox_url) # Ensure the user is notified @@ -60,7 +60,7 @@ def reject_quote_request!(quoted_status) account: @account, activity_uri: @json['id'] ) - json = Oj.dump(serialize_payload(quote, ActivityPub::RejectQuoteRequestSerializer)) + json = serialize_payload(quote, ActivityPub::RejectQuoteRequestSerializer).to_json ActivityPub::DeliveryWorker.perform_async(json, quoted_status.account_id, @account.inbox_url) end From 968ce25c39d0d7c7f7f5836f28443d5c3f600b7d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 10:49:21 -0400 Subject: [PATCH 092/203] Use `to_json` call for worker payloads (#38228) --- app/workers/activitypub/distribute_poll_update_worker.rb | 2 +- app/workers/activitypub/distribution_worker.rb | 2 +- app/workers/activitypub/feature_request_worker.rb | 2 +- app/workers/activitypub/move_distribution_worker.rb | 2 +- app/workers/activitypub/quote_request_worker.rb | 2 +- app/workers/activitypub/update_distribution_worker.rb | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/workers/activitypub/distribute_poll_update_worker.rb b/app/workers/activitypub/distribute_poll_update_worker.rb index 8c1eefd93d5bd4..a2535a0ba2a167 100644 --- a/app/workers/activitypub/distribute_poll_update_worker.rb +++ b/app/workers/activitypub/distribute_poll_update_worker.rb @@ -43,7 +43,7 @@ def inboxes end def payload - @payload ||= Oj.dump(serialize_payload(@status, ActivityPub::UpdatePollSerializer, signer: @account)) + @payload ||= serialize_payload(@status, ActivityPub::UpdatePollSerializer, signer: @account).to_json end def relay! diff --git a/app/workers/activitypub/distribution_worker.rb b/app/workers/activitypub/distribution_worker.rb index 63013bdc69a9c6..a95de45183d65f 100644 --- a/app/workers/activitypub/distribution_worker.rb +++ b/app/workers/activitypub/distribution_worker.rb @@ -24,7 +24,7 @@ def inboxes end def payload - @payload ||= Oj.dump(serialize_payload(@status, activity_serializer, serializer_options.merge(signer: @account))) + @payload ||= serialize_payload(@status, activity_serializer, serializer_options.merge(signer: @account)).to_json end def activity_serializer diff --git a/app/workers/activitypub/feature_request_worker.rb b/app/workers/activitypub/feature_request_worker.rb index fa895a546d772c..61bc041f503a43 100644 --- a/app/workers/activitypub/feature_request_worker.rb +++ b/app/workers/activitypub/feature_request_worker.rb @@ -17,6 +17,6 @@ def inboxes end def payload - @payload ||= Oj.dump(serialize_payload(@collection_item, ActivityPub::FeatureRequestSerializer, signer: @account)) + @payload ||= serialize_payload(@collection_item, ActivityPub::FeatureRequestSerializer, signer: @account).to_json end end diff --git a/app/workers/activitypub/move_distribution_worker.rb b/app/workers/activitypub/move_distribution_worker.rb index 1680fcc76ec906..255a59cef0cac2 100644 --- a/app/workers/activitypub/move_distribution_worker.rb +++ b/app/workers/activitypub/move_distribution_worker.rb @@ -28,6 +28,6 @@ def inboxes end def signed_payload - @signed_payload ||= Oj.dump(serialize_payload(@migration, ActivityPub::MoveSerializer, signer: @account)) + @signed_payload ||= serialize_payload(@migration, ActivityPub::MoveSerializer, signer: @account).to_json end end diff --git a/app/workers/activitypub/quote_request_worker.rb b/app/workers/activitypub/quote_request_worker.rb index 0540492f863768..45e328bb804437 100644 --- a/app/workers/activitypub/quote_request_worker.rb +++ b/app/workers/activitypub/quote_request_worker.rb @@ -17,6 +17,6 @@ def inboxes end def payload - @payload ||= Oj.dump(serialize_payload(@quote, ActivityPub::QuoteRequestSerializer, signer: @account, allow_post_inlining: true)) + @payload ||= serialize_payload(@quote, ActivityPub::QuoteRequestSerializer, signer: @account, allow_post_inlining: true).to_json end end diff --git a/app/workers/activitypub/update_distribution_worker.rb b/app/workers/activitypub/update_distribution_worker.rb index 976f516498dc28..6b6c9056327712 100644 --- a/app/workers/activitypub/update_distribution_worker.rb +++ b/app/workers/activitypub/update_distribution_worker.rb @@ -23,6 +23,6 @@ def inboxes end def payload - @payload ||= Oj.dump(serialize_payload(@account, ActivityPub::UpdateActorSerializer, signer: @account, sign_with: @options[:sign_with])) + @payload ||= serialize_payload(@account, ActivityPub::UpdateActorSerializer, signer: @account, sign_with: @options[:sign_with]).to_json end end From 092acbd47b2da58defb8ac834d00dd861ce4855e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 11:06:22 -0400 Subject: [PATCH 093/203] Use `to_json` call for pins API (#38231) --- app/controllers/api/v1/statuses/pins_controller.rb | 4 ++-- spec/requests/api/v1/statuses/pins_spec.rb | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/statuses/pins_controller.rb b/app/controllers/api/v1/statuses/pins_controller.rb index 32a5f71293d537..39662eff733188 100644 --- a/app/controllers/api/v1/statuses/pins_controller.rb +++ b/app/controllers/api/v1/statuses/pins_controller.rb @@ -30,7 +30,7 @@ def distribute_add_activity! adapter: ActivityPub::Adapter ).as_json - ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id) + ActivityPub::RawDistributionWorker.perform_async(json.to_json, current_account.id) end def distribute_remove_activity! @@ -40,6 +40,6 @@ def distribute_remove_activity! adapter: ActivityPub::Adapter ).as_json - ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id) + ActivityPub::RawDistributionWorker.perform_async(json.to_json, current_account.id) end end diff --git a/spec/requests/api/v1/statuses/pins_spec.rb b/spec/requests/api/v1/statuses/pins_spec.rb index 66ed1510a48727..26e939cd516599 100644 --- a/spec/requests/api/v1/statuses/pins_spec.rb +++ b/spec/requests/api/v1/statuses/pins_spec.rb @@ -29,6 +29,8 @@ expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, pinned: true) ) + expect(ActivityPub::RawDistributionWorker) + .to have_enqueued_sidekiq_job(match_json_values(type: 'Add'), user.account.id) end end @@ -118,6 +120,8 @@ expect(response.parsed_body).to match( a_hash_including(id: status.id.to_s, pinned: false) ) + expect(ActivityPub::RawDistributionWorker) + .to have_enqueued_sidekiq_job(match_json_values(type: 'Remove'), user.account.id) end end From 6b1eac8865309d20336038575ab556a80c897b16 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 11:09:28 -0400 Subject: [PATCH 094/203] Use `to_json` call for Relay enable/disable (#38232) --- app/models/relay.rb | 4 ++-- spec/models/relay_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/relay.rb b/app/models/relay.rb index 53221887bdfe22..41a0a2a1aefa32 100644 --- a/app/models/relay.rb +++ b/app/models/relay.rb @@ -31,7 +31,7 @@ def to_log_human_identifier def enable! activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) - payload = Oj.dump(follow_activity(activity_id)) + payload = follow_activity(activity_id).to_json update!(state: :pending, follow_activity_id: activity_id) reset_delivery_tracker @@ -40,7 +40,7 @@ def enable! def disable! activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil) - payload = Oj.dump(unfollow_activity(activity_id)) + payload = unfollow_activity(activity_id).to_json update!(state: :idle, follow_activity_id: nil) reset_delivery_tracker diff --git a/spec/models/relay_spec.rb b/spec/models/relay_spec.rb index 03758ca6a8fecf..e1fed60b81b0cf 100644 --- a/spec/models/relay_spec.rb +++ b/spec/models/relay_spec.rb @@ -78,7 +78,7 @@ .to change { relay.reload.state }.to('idle') .and change { relay.reload.follow_activity_id }.to(be_nil) expect(ActivityPub::DeliveryWorker) - .to have_received(:perform_async).with(match('Undo'), Account.representative.id, relay.inbox_url) + .to have_received(:perform_async).with(match_json_values(type: 'Undo'), Account.representative.id, relay.inbox_url) expect(DeliveryFailureTracker) .to have_received(:reset!).with(relay.inbox_url) end @@ -94,7 +94,7 @@ .to change { relay.reload.state }.to('pending') .and change { relay.reload.follow_activity_id }.to(be_present) expect(ActivityPub::DeliveryWorker) - .to have_received(:perform_async).with(match('Follow'), Account.representative.id, relay.inbox_url) + .to have_received(:perform_async).with(match_json_values(type: 'Follow'), Account.representative.id, relay.inbox_url) expect(DeliveryFailureTracker) .to have_received(:reset!).with(relay.inbox_url) end From 0c75e97345aff96689bcd5960ce48e459b47f318 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 11:10:10 -0400 Subject: [PATCH 095/203] Use `JSON.generate` in backup service (#38234) --- app/services/backup_service.rb | 14 +++++++------- spec/services/backup_service_spec.rb | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb index 299b5df234e783..bbe655b71a44cd 100644 --- a/app/services/backup_service.rb +++ b/app/services/backup_service.rb @@ -23,7 +23,7 @@ def build_outbox_json!(file) skeleton = serialize(collection_presenter, ActivityPub::CollectionSerializer) skeleton[:@context] = full_context skeleton[:orderedItems] = ['!PLACEHOLDER!'] - skeleton = Oj.dump(skeleton) + skeleton = JSON.generate(skeleton) prepend, append = skeleton.split('"!PLACEHOLDER!"') add_comma = false @@ -44,7 +44,7 @@ def build_outbox_json!(file) end end - Oj.dump(item) + JSON.generate(item) end.join(',')) GC.start @@ -107,7 +107,7 @@ def dump_actor!(zipfile) download_to_zip(zipfile, account.avatar, "avatar#{File.extname(account.avatar.path)}") if account.avatar.exists? download_to_zip(zipfile, account.header, "header#{File.extname(account.header.path)}") if account.header.exists? - json = Oj.dump(actor) + json = JSON.generate(actor) zipfile.get_output_stream('actor.json') do |io| io.write(json) @@ -118,7 +118,7 @@ def dump_likes!(zipfile) skeleton = serialize(ActivityPub::CollectionPresenter.new(id: 'likes.json', type: :ordered, size: 0, items: []), ActivityPub::CollectionSerializer) skeleton.delete(:totalItems) skeleton[:orderedItems] = ['!PLACEHOLDER!'] - skeleton = Oj.dump(skeleton) + skeleton = JSON.generate(skeleton) prepend, append = skeleton.split('"!PLACEHOLDER!"') zipfile.get_output_stream('likes.json') do |io| @@ -131,7 +131,7 @@ def dump_likes!(zipfile) add_comma = true io.write(statuses.map do |status| - Oj.dump(ActivityPub::TagManager.instance.uri_for(status)) + JSON.generate(ActivityPub::TagManager.instance.uri_for(status)) end.join(',')) GC.start @@ -145,7 +145,7 @@ def dump_bookmarks!(zipfile) skeleton = serialize(ActivityPub::CollectionPresenter.new(id: 'bookmarks.json', type: :ordered, size: 0, items: []), ActivityPub::CollectionSerializer) skeleton.delete(:totalItems) skeleton[:orderedItems] = ['!PLACEHOLDER!'] - skeleton = Oj.dump(skeleton) + skeleton = JSON.generate(skeleton) prepend, append = skeleton.split('"!PLACEHOLDER!"') zipfile.get_output_stream('bookmarks.json') do |io| @@ -157,7 +157,7 @@ def dump_bookmarks!(zipfile) add_comma = true io.write(statuses.map do |status| - Oj.dump(ActivityPub::TagManager.instance.uri_for(status)) + JSON.generate(ActivityPub::TagManager.instance.uri_for(status)) end.join(',')) GC.start diff --git a/spec/services/backup_service_spec.rb b/spec/services/backup_service_spec.rb index 878405a0fec9dc..1dcebc24d2806f 100644 --- a/spec/services/backup_service_spec.rb +++ b/spec/services/backup_service_spec.rb @@ -56,7 +56,7 @@ def process_backup def expect_outbox_export body = export_json_raw(:outbox) - json = Oj.load(body) + json = JSON.parse(body) aggregate_failures do expect(body.scan('@context').count).to eq 1 @@ -93,7 +93,7 @@ def export_json_raw(type) end def export_json(type) - Oj.load(export_json_raw(type)) + JSON.parse(export_json_raw(type)) end def include_create_item(status) From f460ad611ac27af1252b9260b7b8a9c370ee9d61 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 11:12:38 -0400 Subject: [PATCH 096/203] Use `to_json` call in web/push notification worker (#38233) --- app/workers/web/push_notification_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/web/push_notification_worker.rb b/app/workers/web/push_notification_worker.rb index a1f4e46690c23c..0277aadfd8f344 100644 --- a/app/workers/web/push_notification_worker.rb +++ b/app/workers/web/push_notification_worker.rb @@ -101,7 +101,7 @@ def web_push_request def push_notification_json I18n.with_locale(@subscription.locale.presence || I18n.default_locale) do - Oj.dump(serialized_notification.as_json) + serialized_notification.to_json end end From 8792d6f8408d93295930ea967632f4b404785382 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 11:16:43 -0400 Subject: [PATCH 097/203] Use `JSON.generate` in trivial string/hash conversion in specs (#38224) --- .../lib/activitypub/activity/announce_spec.rb | 4 +-- spec/lib/activitypub/activity/create_spec.rb | 6 ++-- .../activity/quote_request_spec.rb | 2 +- spec/lib/activitypub/activity_spec.rb | 2 +- spec/lib/activitypub/dereferencer_spec.rb | 2 +- spec/lib/activitypub/forwarder_spec.rb | 4 +-- spec/lib/webfinger_spec.rb | 8 ++--- spec/lib/webhooks/payload_renderer_spec.rb | 2 +- .../fetch_featured_collection_service_spec.rb | 20 ++++++------- ...h_featured_tags_collection_service_spec.rb | 8 ++--- .../fetch_remote_account_service_spec.rb | 26 ++++++++-------- .../fetch_remote_actor_service_spec.rb | 26 ++++++++-------- .../fetch_remote_key_service_spec.rb | 10 +++---- .../fetch_remote_status_service_spec.rb | 6 ++-- .../activitypub/fetch_replies_service_spec.rb | 6 ++-- .../process_collection_service_spec.rb | 2 +- .../process_status_update_service_spec.rb | 14 ++++----- .../synchronize_followers_service_spec.rb | 30 +++++++++---------- .../activitypub/verify_quote_service_spec.rb | 4 +-- .../fetch_remote_status_service_spec.rb | 2 +- spec/services/resolve_account_service_spec.rb | 10 +++---- .../software_update_check_service_spec.rb | 2 +- spec/support/streaming_client.rb | 2 +- .../fetch_all_replies_worker_spec.rb | 14 ++++----- .../activitypub/fetch_replies_worker_spec.rb | 2 +- 25 files changed, 107 insertions(+), 107 deletions(-) diff --git a/spec/lib/activitypub/activity/announce_spec.rb b/spec/lib/activitypub/activity/announce_spec.rb index b556bfd6c2ece3..4becf2320d7feb 100644 --- a/spec/lib/activitypub/activity/announce_spec.rb +++ b/spec/lib/activitypub/activity/announce_spec.rb @@ -35,7 +35,7 @@ context 'when sender is followed by a local account' do before do Fabricate(:account).follow!(sender) - stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: JSON.generate(unknown_object_json), headers: { 'Content-Type': 'application/activity+json' }) subject.perform end @@ -120,7 +120,7 @@ let(:object_json) { 'https://example.com/actor/hello-world' } before do - stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: JSON.generate(unknown_object_json), headers: { 'Content-Type': 'application/activity+json' }) end context 'when the relay is enabled' do diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index 19b6014af158cd..3e376db3dd91e5 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -1029,7 +1029,7 @@ def activity_for_object(json) end before do - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { @@ -1085,7 +1085,7 @@ def activity_for_object(json) end before do - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { @@ -1217,7 +1217,7 @@ def activity_for_object(json) before do stub_request(:get, object_json[:id]) .with(headers: { Authorization: "Bearer #{token}" }) - .to_return(body: Oj.dump(object_json), headers: { 'Content-Type': 'application/activity+json' }) + .to_return(body: JSON.generate(object_json), headers: { 'Content-Type': 'application/activity+json' }) subject.perform end diff --git a/spec/lib/activitypub/activity/quote_request_spec.rb b/spec/lib/activitypub/activity/quote_request_spec.rb index db80448a80b6a4..d68f01211d5bec 100644 --- a/spec/lib/activitypub/activity/quote_request_spec.rb +++ b/spec/lib/activitypub/activity/quote_request_spec.rb @@ -86,7 +86,7 @@ context 'when trying to quote a quotable local status' do before do - stub_request(:get, 'https://example.com/unknown-status').to_return(status: 200, body: Oj.dump(status_json), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/unknown-status').to_return(status: 200, body: JSON.generate(status_json), headers: { 'Content-Type': 'application/activity+json' }) quoted_post.update(quote_approval_policy: InteractionPolicy::POLICY_FLAGS[:public] << 16) end diff --git a/spec/lib/activitypub/activity_spec.rb b/spec/lib/activitypub/activity_spec.rb index d7d0700dc65c0b..a4976eb8672aff 100644 --- a/spec/lib/activitypub/activity_spec.rb +++ b/spec/lib/activitypub/activity_spec.rb @@ -89,7 +89,7 @@ before do sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender)) - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump(approval_payload)) + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate(approval_payload)) end context 'when getting them in order' do diff --git a/spec/lib/activitypub/dereferencer_spec.rb b/spec/lib/activitypub/dereferencer_spec.rb index 11078de866e1e3..13eedf518fdea0 100644 --- a/spec/lib/activitypub/dereferencer_spec.rb +++ b/spec/lib/activitypub/dereferencer_spec.rb @@ -12,7 +12,7 @@ let(:uri) { nil } before do - stub_request(:get, 'https://example.com/foo').to_return(body: Oj.dump(object), headers: { 'Content-Type' => 'application/activity+json' }) + stub_request(:get, 'https://example.com/foo').to_return(body: JSON.generate(object), headers: { 'Content-Type' => 'application/activity+json' }) end context 'with a URI' do diff --git a/spec/lib/activitypub/forwarder_spec.rb b/spec/lib/activitypub/forwarder_spec.rb index f72e3342183e83..7276511a7dd18b 100644 --- a/spec/lib/activitypub/forwarder_spec.rb +++ b/spec/lib/activitypub/forwarder_spec.rb @@ -54,8 +54,8 @@ it 'correctly forwards to expected remote followers' do expect { subject.forward! } - .to enqueue_sidekiq_job(ActivityPub::LowPriorityDeliveryWorker).with(Oj.dump(payload), anything, eve.preferred_inbox_url) - .and enqueue_sidekiq_job(ActivityPub::LowPriorityDeliveryWorker).with(Oj.dump(payload), anything, mallory.preferred_inbox_url) + .to enqueue_sidekiq_job(ActivityPub::LowPriorityDeliveryWorker).with(JSON.generate(payload), anything, eve.preferred_inbox_url) + .and enqueue_sidekiq_job(ActivityPub::LowPriorityDeliveryWorker).with(JSON.generate(payload), anything, mallory.preferred_inbox_url) end end end diff --git a/spec/lib/webfinger_spec.rb b/spec/lib/webfinger_spec.rb index e214a03536bf17..7f2251b858f2c9 100644 --- a/spec/lib/webfinger_spec.rb +++ b/spec/lib/webfinger_spec.rb @@ -10,7 +10,7 @@ let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } it 'correctly parses the response' do - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) expect(subject.self_link_href).to eq 'https://example.com/alice' end @@ -20,7 +20,7 @@ let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' }] } } it 'correctly parses the response' do - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) expect(subject.self_link_href).to eq 'https://example.com/alice' end @@ -30,7 +30,7 @@ let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/json"' }] } } it 'raises an error' do - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) expect { subject } .to raise_error(Webfinger::Error) @@ -53,7 +53,7 @@ before do stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(body: host_meta, headers: { 'Content-Type': 'application/jrd+json' }) - stub_request(:get, 'https://example.com/.well-known/nonStandardWebfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/.well-known/nonStandardWebfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'uses host meta details' do diff --git a/spec/lib/webhooks/payload_renderer_spec.rb b/spec/lib/webhooks/payload_renderer_spec.rb index 0623edd25475d4..d4a330ea1cd690 100644 --- a/spec/lib/webhooks/payload_renderer_spec.rb +++ b/spec/lib/webhooks/payload_renderer_spec.rb @@ -7,7 +7,7 @@ let(:event) { Webhooks::EventPresenter.new(type, object) } let(:payload) { ActiveModelSerializers::SerializableResource.new(event, serializer: REST::Admin::WebhookEventSerializer, scope: nil, scope_name: :current_user).as_json } - let(:json) { Oj.dump(payload) } + let(:json) { JSON.generate(payload) } describe '#render' do context 'when event is account.approved' do diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb index f0002bc388a9c7..24c5fa0a066a86 100644 --- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb +++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb @@ -75,11 +75,11 @@ shared_examples 'sets pinned posts' do before do - stub_request(:get, 'https://example.com/account/pinned/known').to_return(status: 200, body: Oj.dump(status_json_pinned_known), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/account/pinned/unknown-inlined').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_inlined), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/account/pinned/known').to_return(status: 200, body: JSON.generate(status_json_pinned_known), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/account/pinned/unknown-inlined').to_return(status: 200, body: JSON.generate(status_json_pinned_unknown_inlined), headers: { 'Content-Type': 'application/activity+json' }) stub_request(:get, 'https://example.com/account/pinned/unknown-unreachable').to_return(status: 404) - stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/account/collections/featured').to_return(status: 200, body: Oj.dump(featured_with_null), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: JSON.generate(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/account/collections/featured').to_return(status: 200, body: JSON.generate(featured_with_null), headers: { 'Content-Type': 'application/activity+json' }) subject end @@ -101,7 +101,7 @@ let(:collection_or_uri) { actor.featured_collection_url } before do - stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'sets pinned posts' @@ -122,7 +122,7 @@ context 'when the endpoint is a Collection' do before do - stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'sets pinned posts' @@ -139,7 +139,7 @@ end before do - stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'sets pinned posts' @@ -148,7 +148,7 @@ let(:items) { 'https://example.com/account/pinned/unknown-reachable' } before do - stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: JSON.generate(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' }) subject end @@ -175,7 +175,7 @@ end before do - stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'sets pinned posts' @@ -184,7 +184,7 @@ let(:items) { 'https://example.com/account/pinned/unknown-reachable' } before do - stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: JSON.generate(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' }) subject end diff --git a/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb index 59367b1e32c5e8..91b5267bdf2181 100644 --- a/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb +++ b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb @@ -38,7 +38,7 @@ describe '#call' do context 'when the endpoint is a Collection' do before do - stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'sets featured tags' @@ -46,7 +46,7 @@ context 'when the account already has featured tags' do before do - stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) actor.featured_tags.create!(name: 'FoO') actor.featured_tags.create!(name: 'baz') @@ -67,7 +67,7 @@ end before do - stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'sets featured tags' @@ -88,7 +88,7 @@ end before do - stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_url).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'sets featured tags' diff --git a/spec/services/activitypub/fetch_remote_account_service_spec.rb b/spec/services/activitypub/fetch_remote_account_service_spec.rb index 7ebd3cdc7041e7..30d693fa4b66f6 100644 --- a/spec/services/activitypub/fetch_remote_account_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_account_service_spec.rb @@ -38,8 +38,8 @@ before do actor[:inbox] = nil - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and returns nil' do @@ -54,8 +54,8 @@ let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and sets attributes' do @@ -75,9 +75,9 @@ let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) - stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and follows redirection and sets attributes' do @@ -98,8 +98,8 @@ let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and does not create account' do @@ -114,9 +114,9 @@ let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) - stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and follows redirect and does not create account' do @@ -130,7 +130,7 @@ context 'with wrong id' do it 'does not create account' do - expect(subject.call('https://fake.address/@foo', prefetched_body: Oj.dump(actor))).to be_nil + expect(subject.call('https://fake.address/@foo', prefetched_body: JSON.generate(actor))).to be_nil end end end diff --git a/spec/services/activitypub/fetch_remote_actor_service_spec.rb b/spec/services/activitypub/fetch_remote_actor_service_spec.rb index 975e0799ddd165..36457c207c0375 100644 --- a/spec/services/activitypub/fetch_remote_actor_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_actor_service_spec.rb @@ -38,8 +38,8 @@ before do actor[:inbox] = nil - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and returns nil' do @@ -54,8 +54,8 @@ let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and sets values' do @@ -75,9 +75,9 @@ let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) - stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and follows redirect and sets values' do @@ -98,8 +98,8 @@ let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and does not create account' do @@ -114,9 +114,9 @@ let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob', type: 'application/activity+json' }] } } before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) - stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'fetches resource and looks up webfinger and follows redirect and does not create account' do @@ -130,7 +130,7 @@ context 'with wrong id' do it 'does not create account' do - expect(subject.call('https://fake.address/@foo', prefetched_body: Oj.dump(actor))).to be_nil + expect(subject.call('https://fake.address/@foo', prefetched_body: JSON.generate(actor))).to be_nil end end end diff --git a/spec/services/activitypub/fetch_remote_key_service_spec.rb b/spec/services/activitypub/fetch_remote_key_service_spec.rb index ddd1a8067e821a..635a07c26bc454 100644 --- a/spec/services/activitypub/fetch_remote_key_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_key_service_spec.rb @@ -50,8 +50,8 @@ end before do - stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://example.com/alice').to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end describe '#call' do @@ -59,7 +59,7 @@ context 'when the key is a sub-object from the actor' do before do - stub_request(:get, public_key_id).to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, public_key_id).to_return(body: JSON.generate(actor), headers: { 'Content-Type': 'application/activity+json' }) end it 'returns the expected account' do @@ -71,7 +71,7 @@ let(:public_key_id) { 'https://example.com/alice-public-key.json' } before do - stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, public_key_id).to_return(body: JSON.generate(key_json.merge({ '@context': ['https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' }) end it 'returns the expected account' do @@ -84,7 +84,7 @@ let(:actor_public_key) { 'https://example.com/alice-public-key.json' } before do - stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, public_key_id).to_return(body: JSON.generate(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' }) end it 'returns the nil' do diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index 6afee5f25ef263..e37fcca7a09a4d 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -11,7 +11,7 @@ let(:follower) { Fabricate(:account, username: 'alice') } let(:follow) { nil } - let(:response) { { body: Oj.dump(object), headers: { 'content-type': 'application/activity+json' } } } + let(:response) { { body: JSON.generate(object), headers: { 'content-type': 'application/activity+json' } } } let(:existing_status) { nil } let(:note) do @@ -369,7 +369,7 @@ end it 'creates statuses but not more than limit allows' do - expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) } + expect { subject.call(object[:id], prefetched_body: JSON.generate(object)) } .to change { sender.statuses.count }.by_at_least(2) .and change { sender.statuses.count }.by_at_most(3) end @@ -419,7 +419,7 @@ end it 'creates statuses but not more than limit allows' do - expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) } + expect { subject.call(object[:id], prefetched_body: JSON.generate(object)) } .to change { sender.statuses.count }.by_at_least(2) .and change { sender.statuses.count }.by_at_most(3) end diff --git a/spec/services/activitypub/fetch_replies_service_spec.rb b/spec/services/activitypub/fetch_replies_service_spec.rb index 36159309f1de6a..1442755c53cfcc 100644 --- a/spec/services/activitypub/fetch_replies_service_spec.rb +++ b/spec/services/activitypub/fetch_replies_service_spec.rb @@ -58,7 +58,7 @@ context 'when passing the URL to the collection' do before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it 'spawns workers for up to 5 replies on the same server' do @@ -93,7 +93,7 @@ context 'when passing the URL to the collection' do before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it 'spawns workers for up to 5 replies on the same server' do @@ -132,7 +132,7 @@ context 'when passing the URL to the collection' do before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it 'spawns workers for up to 5 replies on the same server' do diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb index 74df0f91063f53..cee4272c2222d7 100644 --- a/spec/services/activitypub/process_collection_service_spec.rb +++ b/spec/services/activitypub/process_collection_service_spec.rb @@ -21,7 +21,7 @@ } end - let(:json) { Oj.dump(payload) } + let(:json) { JSON.generate(payload) } describe '#call' do context 'when actor is suspended' do diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index aca2feb0087b6e..8279411a33eca2 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -23,7 +23,7 @@ ], } end - let(:json) { Oj.load(Oj.dump(payload)) } + let(:json) { Oj.load(JSON.generate(payload)) } let(:alice) { Fabricate(:account) } let(:bob) { Fabricate(:account) } @@ -545,7 +545,7 @@ end before do - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { @@ -610,7 +610,7 @@ end before do - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { @@ -819,7 +819,7 @@ end before do - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { @@ -884,7 +884,7 @@ end before do - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { @@ -1127,7 +1127,7 @@ end before do - stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { @@ -1235,7 +1235,7 @@ end before do - stub_request(:get, second_approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + stub_request(:get, second_approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': [ 'https://www.w3.org/ns/activitystreams', { diff --git a/spec/services/activitypub/synchronize_followers_service_spec.rb b/spec/services/activitypub/synchronize_followers_service_spec.rb index 813658d149b889..c41783f7c4d1cd 100644 --- a/spec/services/activitypub/synchronize_followers_service_spec.rb +++ b/spec/services/activitypub/synchronize_followers_service_spec.rb @@ -55,7 +55,7 @@ context 'when the endpoint is a Collection of actor URIs' do before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'synchronizes followers' @@ -72,7 +72,7 @@ end before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'synchronizes followers' @@ -93,7 +93,7 @@ end before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'synchronizes followers' @@ -102,7 +102,7 @@ context 'when the endpoint is a paginated Collection of actor URIs split across multiple pages' do before do stub_request(:get, 'https://example.com/partial-followers') - .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': 'https://www.w3.org/ns/activitystreams', type: 'Collection', id: 'https://example.com/partial-followers', @@ -110,7 +110,7 @@ })) stub_request(:get, 'https://example.com/partial-followers/1') - .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': 'https://www.w3.org/ns/activitystreams', type: 'CollectionPage', id: 'https://example.com/partial-followers/1', @@ -120,7 +120,7 @@ })) stub_request(:get, 'https://example.com/partial-followers/2') - .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': 'https://www.w3.org/ns/activitystreams', type: 'CollectionPage', id: 'https://example.com/partial-followers/2', @@ -135,7 +135,7 @@ context 'when the endpoint is a paginated Collection of actor URIs split across, but one page errors out' do before do stub_request(:get, 'https://example.com/partial-followers') - .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': 'https://www.w3.org/ns/activitystreams', type: 'Collection', id: 'https://example.com/partial-followers', @@ -143,7 +143,7 @@ })) stub_request(:get, 'https://example.com/partial-followers/1') - .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({ + .to_return(status: 200, headers: { 'Content-Type': 'application/activity+json' }, body: JSON.generate({ '@context': 'https://www.w3.org/ns/activitystreams', type: 'CollectionPage', id: 'https://example.com/partial-followers/1', @@ -185,7 +185,7 @@ before do stub_const('ActivityPub::SynchronizeFollowersService::MAX_COLLECTION_PAGES', 1) - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it 'confirms pending follow request but does not remove extra followers' do @@ -213,7 +213,7 @@ context 'when the endpoint is a Collection of actor URIs' do before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'synchronizes followers' @@ -230,7 +230,7 @@ end before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'synchronizes followers' @@ -251,7 +251,7 @@ end before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'synchronizes followers' @@ -263,7 +263,7 @@ context 'when the endpoint is a Collection of actor URIs' do before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it 'does not remove followers' do @@ -286,7 +286,7 @@ end before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it 'does not remove followers' do @@ -313,7 +313,7 @@ end before do - stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, collection_uri).to_return(status: 200, body: JSON.generate(payload), headers: { 'Content-Type': 'application/activity+json' }) end it 'does not remove followers' do diff --git a/spec/services/activitypub/verify_quote_service_spec.rb b/spec/services/activitypub/verify_quote_service_spec.rb index 94b9e33ed3b1a0..5c9248dbb932af 100644 --- a/spec/services/activitypub/verify_quote_service_spec.rb +++ b/spec/services/activitypub/verify_quote_service_spec.rb @@ -76,7 +76,7 @@ before do stub_request(:get, approval_uri) - .to_return(status: 200, body: Oj.dump(json), headers: { 'Content-Type': 'application/activity+json' }) + .to_return(status: 200, body: JSON.generate(json), headers: { 'Content-Type': 'application/activity+json' }) end context 'with a valid activity for already-fetched posts' do @@ -179,7 +179,7 @@ context 'with a valid activity for already-fetched posts, with a pre-fetched approval' do it 'updates the status without fetching the activity' do - expect { subject.call(quote, prefetched_approval: Oj.dump(json)) } + expect { subject.call(quote, prefetched_approval: JSON.generate(json)) } .to change(quote, :state).to('accepted') expect(a_request(:get, approval_uri)) diff --git a/spec/services/fetch_remote_status_service_spec.rb b/spec/services/fetch_remote_status_service_spec.rb index a9c61e7b4e54e5..2b7ff9cbdcbf77 100644 --- a/spec/services/fetch_remote_status_service_spec.rb +++ b/spec/services/fetch_remote_status_service_spec.rb @@ -19,7 +19,7 @@ context 'when protocol is :activitypub' do subject { described_class.new.call(note[:id], prefetched_body: prefetched_body) } - let(:prefetched_body) { Oj.dump(note) } + let(:prefetched_body) { JSON.generate(note) } before do subject diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb index 1bd4e9a8e3bfcc..897f7cf44f83d1 100644 --- a/spec/services/resolve_account_service_spec.rb +++ b/spec/services/resolve_account_service_spec.rb @@ -103,7 +103,7 @@ def webfinger_discovery_request context 'with a legitimate webfinger redirection' do before do webfinger = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } - stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'returns new remote account' do @@ -121,7 +121,7 @@ def webfinger_discovery_request context 'with a misconfigured redirection' do before do webfinger = { subject: 'acct:Foo@redirected.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } - stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) end it 'returns new remote account' do @@ -139,9 +139,9 @@ def webfinger_discovery_request context 'with too many webfinger redirections' do before do webfinger = { subject: 'acct:foo@evil.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } - stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: JSON.generate(webfinger), headers: { 'Content-Type': 'application/jrd+json' }) webfinger2 = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] } - stub_request(:get, 'https://evil.example.com/.well-known/webfinger?resource=acct:foo@evil.example.com').to_return(body: Oj.dump(webfinger2), headers: { 'Content-Type': 'application/jrd+json' }) + stub_request(:get, 'https://evil.example.com/.well-known/webfinger?resource=acct:foo@evil.example.com').to_return(body: JSON.generate(webfinger2), headers: { 'Content-Type': 'application/jrd+json' }) end it 'does not return a new remote account' do @@ -150,7 +150,7 @@ def webfinger_discovery_request end context 'with webfinger response subject missing a host value' do - let(:body) { Oj.dump({ subject: 'user@' }) } + let(:body) { JSON.generate({ subject: 'user@' }) } let(:url) { 'https://host.example/.well-known/webfinger?resource=acct:user@host.example' } before do diff --git a/spec/services/software_update_check_service_spec.rb b/spec/services/software_update_check_service_spec.rb index 73ffe6b89947e2..ac342dce94f8c0 100644 --- a/spec/services/software_update_check_service_spec.rb +++ b/spec/services/software_update_check_service_spec.rb @@ -82,7 +82,7 @@ end before do - stub_request(:get, full_update_check_url).to_return(body: Oj.dump(server_json)) + stub_request(:get, full_update_check_url).to_return(body: JSON.generate(server_json)) end it 'updates the list of known updates' do diff --git a/spec/support/streaming_client.rb b/spec/support/streaming_client.rb index 02186e781c7d3e..c9a97ab060c85f 100644 --- a/spec/support/streaming_client.rb +++ b/spec/support/streaming_client.rb @@ -152,7 +152,7 @@ def connect end def subscribe(channel, **params) - send(Oj.dump({ type: 'subscribe', stream: channel }.merge(params))) + send(JSON.generate({ type: 'subscribe', stream: channel }.merge(params))) end def wait_for(event = nil) diff --git a/spec/workers/activitypub/fetch_all_replies_worker_spec.rb b/spec/workers/activitypub/fetch_all_replies_worker_spec.rb index 9795c4619a1d67..c7b107ac817db3 100644 --- a/spec/workers/activitypub/fetch_all_replies_worker_spec.rb +++ b/spec/workers/activitypub/fetch_all_replies_worker_spec.rb @@ -126,11 +126,11 @@ all_items.each do |item| next if [top_note_uri, reply_note_uri].include? item - stub_request(:get, item).to_return(status: 200, body: Oj.dump(empty_object), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, item).to_return(status: 200, body: JSON.generate(empty_object), headers: { 'Content-Type': 'application/activity+json' }) end - stub_request(:get, top_note_uri).to_return(status: 200, body: Oj.dump(top_object), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, reply_note_uri).to_return(status: 200, body: Oj.dump(reply_object), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, top_note_uri).to_return(status: 200, body: JSON.generate(top_object), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, reply_note_uri).to_return(status: 200, body: JSON.generate(reply_object), headers: { 'Content-Type': 'application/activity+json' }) end shared_examples 'fetches all replies' do @@ -180,8 +180,8 @@ end before do - stub_request(:get, top_collection_uri).to_return(status: 200, body: Oj.dump(replies_top), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, reply_collection_uri).to_return(status: 200, body: Oj.dump(replies_nested), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, top_collection_uri).to_return(status: 200, body: JSON.generate(replies_top), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, reply_collection_uri).to_return(status: 200, body: JSON.generate(replies_nested), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'fetches all replies' @@ -254,8 +254,8 @@ end before do - stub_request(:get, top_page_2_uri).to_return(status: 200, body: Oj.dump(top_page_two), headers: { 'Content-Type': 'application/activity+json' }) - stub_request(:get, reply_page_2_uri).to_return(status: 200, body: Oj.dump(reply_page_two), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, top_page_2_uri).to_return(status: 200, body: JSON.generate(top_page_two), headers: { 'Content-Type': 'application/activity+json' }) + stub_request(:get, reply_page_2_uri).to_return(status: 200, body: JSON.generate(reply_page_two), headers: { 'Content-Type': 'application/activity+json' }) end it_behaves_like 'fetches all replies' diff --git a/spec/workers/activitypub/fetch_replies_worker_spec.rb b/spec/workers/activitypub/fetch_replies_worker_spec.rb index 56d19705a4d034..5eea3fcbcf57b6 100644 --- a/spec/workers/activitypub/fetch_replies_worker_spec.rb +++ b/spec/workers/activitypub/fetch_replies_worker_spec.rb @@ -17,7 +17,7 @@ } end - let(:json) { Oj.dump(payload) } + let(:json) { JSON.generate(payload) } describe 'perform' do it 'performs a request if the collection URI is from the same host' do From 703f2d0263f8ce858eaa924cbc5b00b19ee1b2e3 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Mar 2026 11:17:57 -0400 Subject: [PATCH 098/203] Use implicit `to_json` call in app/services (#38225) --- app/services/activitypub/synchronize_followers_service.rb | 2 +- app/services/after_block_domain_from_account_service.rb | 2 +- app/services/authorize_follow_service.rb | 2 +- app/services/block_service.rb | 2 +- app/services/create_featured_tag_service.rb | 2 +- app/services/delete_account_service.rb | 6 +++--- app/services/favourite_service.rb | 2 +- app/services/follow_service.rb | 2 +- app/services/reject_follow_service.rb | 2 +- app/services/remove_domains_from_followers_service.rb | 2 +- app/services/remove_featured_tag_service.rb | 2 +- app/services/remove_from_followers_service.rb | 2 +- app/services/remove_status_service.rb | 2 +- app/services/report_service.rb | 2 +- app/services/revoke_collection_item_service.rb | 2 +- app/services/revoke_quote_service.rb | 2 +- app/services/suspend_account_service.rb | 4 ++-- app/services/unblock_service.rb | 2 +- app/services/unfavourite_service.rb | 2 +- app/services/unfollow_service.rb | 4 ++-- app/services/unsuspend_account_service.rb | 2 +- app/services/vote_service.rb | 2 +- 22 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/services/activitypub/synchronize_followers_service.rb b/app/services/activitypub/synchronize_followers_service.rb index 9e0b452929dc98..30f475ed07e1c0 100644 --- a/app/services/activitypub/synchronize_followers_service.rb +++ b/app/services/activitypub/synchronize_followers_service.rb @@ -62,7 +62,7 @@ def handle_unexpected_outgoing_follows!(expected_followers) end def build_undo_follow_json(follow) - Oj.dump(serialize_payload(follow, ActivityPub::UndoFollowSerializer)) + serialize_payload(follow, ActivityPub::UndoFollowSerializer).to_json end # Only returns true if the whole collection has been processed diff --git a/app/services/after_block_domain_from_account_service.rb b/app/services/after_block_domain_from_account_service.rb index fc5dc6568103e1..549d5086745844 100644 --- a/app/services/after_block_domain_from_account_service.rb +++ b/app/services/after_block_domain_from_account_service.rb @@ -54,7 +54,7 @@ def reject_follow!(follow) return unless follow.account.activitypub? - ActivityPub::DeliveryWorker.perform_async(Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), @account.id, follow.account.inbox_url) + ActivityPub::DeliveryWorker.perform_async(serialize_payload(follow, ActivityPub::RejectFollowSerializer).to_json, @account.id, follow.account.inbox_url) end def notify_of_severed_relationships! diff --git a/app/services/authorize_follow_service.rb b/app/services/authorize_follow_service.rb index 49bef727e6a8c9..1c2c6da3ceaf9d 100644 --- a/app/services/authorize_follow_service.rb +++ b/app/services/authorize_follow_service.rb @@ -22,6 +22,6 @@ def create_notification(follow_request) end def build_json(follow_request) - Oj.dump(serialize_payload(follow_request, ActivityPub::AcceptFollowSerializer)) + serialize_payload(follow_request, ActivityPub::AcceptFollowSerializer).to_json end end diff --git a/app/services/block_service.rb b/app/services/block_service.rb index 98229d98c0eaaf..cbd0e8e75b50df 100644 --- a/app/services/block_service.rb +++ b/app/services/block_service.rb @@ -26,6 +26,6 @@ def create_notification(block) end def build_json(block) - Oj.dump(serialize_payload(block, ActivityPub::BlockSerializer)) + serialize_payload(block, ActivityPub::BlockSerializer).to_json end end diff --git a/app/services/create_featured_tag_service.rb b/app/services/create_featured_tag_service.rb index 298bdb5928cb72..b8cb5903aa63ff 100644 --- a/app/services/create_featured_tag_service.rb +++ b/app/services/create_featured_tag_service.rb @@ -26,6 +26,6 @@ def call(account, name_or_tag, raise_error: true) private def build_json(featured_tag) - Oj.dump(serialize_payload(featured_tag, ActivityPub::AddHashtagSerializer, signer: @account)) + serialize_payload(featured_tag, ActivityPub::AddHashtagSerializer, signer: @account).to_json end end diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb index 6557dda48fb604..fa19d6ee50a8f9 100644 --- a/app/services/delete_account_service.rb +++ b/app/services/delete_account_service.rb @@ -114,7 +114,7 @@ def reject_follows! # we have to force it to unfollow them. ActivityPub::DeliveryWorker.push_bulk(Follow.where(account: @account)) do |follow| - [Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url] + [serialize_payload(follow, ActivityPub::RejectFollowSerializer).to_json, follow.target_account_id, @account.inbox_url] end end @@ -126,7 +126,7 @@ def undo_follows! # if the remote account gets un-suspended. ActivityPub::DeliveryWorker.push_bulk(Follow.where(target_account: @account)) do |follow| - [Oj.dump(serialize_payload(follow, ActivityPub::UndoFollowSerializer)), follow.account_id, @account.inbox_url] + [serialize_payload(follow, ActivityPub::UndoFollowSerializer).to_json, follow.account_id, @account.inbox_url] end end @@ -285,7 +285,7 @@ def record_severed_relationships! end def delete_actor_json - @delete_actor_json ||= Oj.dump(serialize_payload(@account, ActivityPub::DeleteActorSerializer, signer: @account, always_sign: true)) + @delete_actor_json ||= serialize_payload(@account, ActivityPub::DeleteActorSerializer, signer: @account, always_sign: true).to_json end def delivery_inboxes diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index ded50187f77485..7638d7f257de91 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -42,6 +42,6 @@ def increment_statistics end def build_json(favourite) - Oj.dump(serialize_payload(favourite, ActivityPub::LikeSerializer)) + serialize_payload(favourite, ActivityPub::LikeSerializer).to_json end end diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index 5ff1b63503c977..c2d2956a985e0a 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -90,7 +90,7 @@ def direct_follow! end def build_json(follow_request) - Oj.dump(serialize_payload(follow_request, ActivityPub::FollowSerializer)) + serialize_payload(follow_request, ActivityPub::FollowSerializer).to_json end def follow_options diff --git a/app/services/reject_follow_service.rb b/app/services/reject_follow_service.rb index bc0000c8c886b0..4ced57bdf27d15 100644 --- a/app/services/reject_follow_service.rb +++ b/app/services/reject_follow_service.rb @@ -17,6 +17,6 @@ def create_notification(follow_request) end def build_json(follow_request) - Oj.dump(serialize_payload(follow_request, ActivityPub::RejectFollowSerializer)) + serialize_payload(follow_request, ActivityPub::RejectFollowSerializer).to_json end end diff --git a/app/services/remove_domains_from_followers_service.rb b/app/services/remove_domains_from_followers_service.rb index d76763409d3b0d..ed01b26e16a83c 100644 --- a/app/services/remove_domains_from_followers_service.rb +++ b/app/services/remove_domains_from_followers_service.rb @@ -18,6 +18,6 @@ def create_notification(follow) end def build_json(follow) - Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)) + serialize_payload(follow, ActivityPub::RejectFollowSerializer).to_json end end diff --git a/app/services/remove_featured_tag_service.rb b/app/services/remove_featured_tag_service.rb index 4fdd43eb6a6b92..69e8a47bf477a6 100644 --- a/app/services/remove_featured_tag_service.rb +++ b/app/services/remove_featured_tag_service.rb @@ -26,6 +26,6 @@ def call(account, featured_tag_or_tag) private def build_json(featured_tag) - Oj.dump(serialize_payload(featured_tag, ActivityPub::RemoveHashtagSerializer, signer: @account)) + serialize_payload(featured_tag, ActivityPub::RemoveHashtagSerializer, signer: @account).to_json end end diff --git a/app/services/remove_from_followers_service.rb b/app/services/remove_from_followers_service.rb index 007d5b1fdd4b00..22fb72cd649cd7 100644 --- a/app/services/remove_from_followers_service.rb +++ b/app/services/remove_from_followers_service.rb @@ -18,6 +18,6 @@ def create_notification(follow) end def build_json(follow) - Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)) + serialize_payload(follow, ActivityPub::RejectFollowSerializer).to_json end end diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 042a20ec790ffb..bb86f346acd649 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -103,7 +103,7 @@ def remove_from_remote_reach end def signed_activity_json - @signed_activity_json ||= Oj.dump(serialize_payload(@status, @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteNoteSerializer, signer: @account, always_sign: true)) + @signed_activity_json ||= serialize_payload(@status, @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteNoteSerializer, signer: @account, always_sign: true).to_json end def remove_reblogs diff --git a/app/services/report_service.rb b/app/services/report_service.rb index a666450af0a277..3e418dc85a8cf2 100644 --- a/app/services/report_service.rb +++ b/app/services/report_service.rb @@ -98,7 +98,7 @@ def reported_collection_ids end def payload - Oj.dump(serialize_payload(@report, ActivityPub::FlagSerializer, account: some_local_account)) + serialize_payload(@report, ActivityPub::FlagSerializer, account: some_local_account).to_json end def some_local_account diff --git a/app/services/revoke_collection_item_service.rb b/app/services/revoke_collection_item_service.rb index d299b567f225b5..c0dc70e952cf92 100644 --- a/app/services/revoke_collection_item_service.rb +++ b/app/services/revoke_collection_item_service.rb @@ -19,6 +19,6 @@ def distribute_stamp_deletion! end def signed_activity_json - @signed_activity_json ||= Oj.dump(serialize_payload(@collection_item, ActivityPub::DeleteFeatureAuthorizationSerializer, signer: @account, always_sign: true)) + @signed_activity_json ||= serialize_payload(@collection_item, ActivityPub::DeleteFeatureAuthorizationSerializer, signer: @account, always_sign: true).to_json end end diff --git a/app/services/revoke_quote_service.rb b/app/services/revoke_quote_service.rb index 346fba89709ed8..1bc69c1f51cb57 100644 --- a/app/services/revoke_quote_service.rb +++ b/app/services/revoke_quote_service.rb @@ -39,6 +39,6 @@ def inboxes end def signed_activity_json - @signed_activity_json ||= Oj.dump(serialize_payload(@quote, ActivityPub::DeleteQuoteAuthorizationSerializer, signer: @account, always_sign: true, force_approval_id: true)) + @signed_activity_json ||= serialize_payload(@quote, ActivityPub::DeleteQuoteAuthorizationSerializer, signer: @account, always_sign: true, force_approval_id: true).to_json end end diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index 666b64cacfb17b..1ec868a961eae9 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -34,7 +34,7 @@ def reject_remote_follows! Follow.where(account: @account).find_in_batches do |follows| ActivityPub::DeliveryWorker.push_bulk(follows) do |follow| - [Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url] + [serialize_payload(follow, ActivityPub::RejectFollowSerializer).to_json, follow.target_account_id, @account.inbox_url] end follows.each(&:destroy) @@ -72,6 +72,6 @@ def remove_from_trends! end def signed_activity_json - @signed_activity_json ||= Oj.dump(serialize_payload(@account, ActivityPub::UpdateActorSerializer, signer: @account)) + @signed_activity_json ||= serialize_payload(@account, ActivityPub::UpdateActorSerializer, signer: @account).to_json end end diff --git a/app/services/unblock_service.rb b/app/services/unblock_service.rb index c263ac8afe0548..31067618a98201 100644 --- a/app/services/unblock_service.rb +++ b/app/services/unblock_service.rb @@ -18,6 +18,6 @@ def create_notification(unblock) end def build_json(unblock) - Oj.dump(serialize_payload(unblock, ActivityPub::UndoBlockSerializer)) + serialize_payload(unblock, ActivityPub::UndoBlockSerializer).to_json end end diff --git a/app/services/unfavourite_service.rb b/app/services/unfavourite_service.rb index 37917a64f1bc52..2f422c42513a4d 100644 --- a/app/services/unfavourite_service.rb +++ b/app/services/unfavourite_service.rb @@ -18,6 +18,6 @@ def create_notification(favourite) end def build_json(favourite) - Oj.dump(serialize_payload(favourite, ActivityPub::UndoLikeSerializer)) + serialize_payload(favourite, ActivityPub::UndoLikeSerializer).to_json end end diff --git a/app/services/unfollow_service.rb b/app/services/unfollow_service.rb index 385aa0c7b17dba..a77f8c012d0472 100644 --- a/app/services/unfollow_service.rb +++ b/app/services/unfollow_service.rb @@ -68,10 +68,10 @@ def send_reject_follow(follow) end def build_json(follow) - Oj.dump(serialize_payload(follow, ActivityPub::UndoFollowSerializer)) + serialize_payload(follow, ActivityPub::UndoFollowSerializer).to_json end def build_reject_json(follow) - Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)) + serialize_payload(follow, ActivityPub::RejectFollowSerializer).to_json end end diff --git a/app/services/unsuspend_account_service.rb b/app/services/unsuspend_account_service.rb index 1a52e80d246642..95cb18606fbf0f 100644 --- a/app/services/unsuspend_account_service.rb +++ b/app/services/unsuspend_account_service.rb @@ -63,6 +63,6 @@ def publish_media_attachments! end def signed_activity_json - @signed_activity_json ||= Oj.dump(serialize_payload(@account, ActivityPub::UpdateActorSerializer, signer: @account)) + @signed_activity_json ||= serialize_payload(@account, ActivityPub::UpdateActorSerializer, signer: @account).to_json end end diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb index 878350388b8bae..f30748faed871b 100644 --- a/app/services/vote_service.rb +++ b/app/services/vote_service.rb @@ -65,7 +65,7 @@ def deliver_votes! end def build_json(vote) - Oj.dump(serialize_payload(vote, ActivityPub::VoteSerializer)) + serialize_payload(vote, ActivityPub::VoteSerializer).to_json end def increment_voters_count! From 4328807f28f28eed7afbd650aef45f5bcd436d38 Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 16 Mar 2026 16:56:30 +0100 Subject: [PATCH 099/203] Profile editing: Finish image editing (#38235) --- app/javascript/mastodon/api/accounts.ts | 6 + app/javascript/mastodon/api_types/profile.ts | 2 + .../components/details/details.stories.tsx | 29 ++++ .../mastodon/components/details/index.tsx | 35 +++++ .../components/details/styles.module.scss | 25 +++ .../mastodon/features/account_edit/index.tsx | 7 +- .../account_edit/modals/image_alt.tsx | 143 +++++++++++++++++- .../account_edit/modals/image_delete.tsx | 40 ++++- .../account_edit/modals/image_upload.tsx | 61 +------- .../features/account_edit/modals/index.ts | 2 +- .../account_edit/modals/styles.module.scss | 39 ++--- .../account_edit/modals/verified_modal.tsx | 13 +- .../features/account_edit/styles.module.scss | 13 ++ .../components/account_header.tsx | 1 + app/javascript/mastodon/locales/en.json | 13 +- .../mastodon/reducers/slices/profile_edit.ts | 48 +++++- 16 files changed, 373 insertions(+), 104 deletions(-) create mode 100644 app/javascript/mastodon/components/details/details.stories.tsx create mode 100644 app/javascript/mastodon/components/details/index.tsx create mode 100644 app/javascript/mastodon/components/details/styles.module.scss diff --git a/app/javascript/mastodon/api/accounts.ts b/app/javascript/mastodon/api/accounts.ts index 15156e156c9fd5..fc6e38fbc8d47a 100644 --- a/app/javascript/mastodon/api/accounts.ts +++ b/app/javascript/mastodon/api/accounts.ts @@ -69,3 +69,9 @@ export const apiGetProfile = () => apiRequestGet('v1/profile'); export const apiPatchProfile = (params: ApiProfileUpdateParams | FormData) => apiRequestPatch('v1/profile', params); + +export const apiDeleteProfileAvatar = () => + apiRequestDelete('v1/profile/avatar'); + +export const apiDeleteProfileHeader = () => + apiRequestDelete('v1/profile/header'); diff --git a/app/javascript/mastodon/api_types/profile.ts b/app/javascript/mastodon/api_types/profile.ts index 9814bddde96006..acc3b46787d5b1 100644 --- a/app/javascript/mastodon/api_types/profile.ts +++ b/app/javascript/mastodon/api_types/profile.ts @@ -27,6 +27,8 @@ export interface ApiProfileJSON { export type ApiProfileUpdateParams = Partial< Pick< ApiProfileJSON, + | 'avatar_description' + | 'header_description' | 'display_name' | 'note' | 'locked' diff --git a/app/javascript/mastodon/components/details/details.stories.tsx b/app/javascript/mastodon/components/details/details.stories.tsx new file mode 100644 index 00000000000000..3bc833f313b8d8 --- /dev/null +++ b/app/javascript/mastodon/components/details/details.stories.tsx @@ -0,0 +1,29 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { Details } from './index'; + +const meta = { + component: Details, + title: 'Components/Details', + args: { + summary: 'Here is the summary title', + children: ( +

+ And here are the details that are hidden until you click the summary. +

+ ), + }, + render(props) { + return ( +
+
+
+ ); + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Plain: Story = {}; diff --git a/app/javascript/mastodon/components/details/index.tsx b/app/javascript/mastodon/components/details/index.tsx new file mode 100644 index 00000000000000..aac92e8f77b986 --- /dev/null +++ b/app/javascript/mastodon/components/details/index.tsx @@ -0,0 +1,35 @@ +import { forwardRef } from 'react'; +import type { ComponentPropsWithoutRef, ReactNode } from 'react'; + +import classNames from 'classnames'; + +import ExpandArrowIcon from '@/material-icons/400-24px/expand_more.svg?react'; + +import { Icon } from '../icon'; + +import classes from './styles.module.scss'; + +export const Details = forwardRef< + HTMLDetailsElement, + { + summary: ReactNode; + children: ReactNode; + className?: string; + } & ComponentPropsWithoutRef<'details'> +>(({ summary, children, className, ...rest }, ref) => { + return ( +
+ + {summary} + + + + {children} +
+ ); +}); +Details.displayName = 'Details'; diff --git a/app/javascript/mastodon/components/details/styles.module.scss b/app/javascript/mastodon/components/details/styles.module.scss new file mode 100644 index 00000000000000..03aace8a626b45 --- /dev/null +++ b/app/javascript/mastodon/components/details/styles.module.scss @@ -0,0 +1,25 @@ +.details { + color: var(--color-text-secondary); + font-size: 13px; + margin-top: 8px; + + summary { + cursor: pointer; + font-weight: 600; + list-style: none; + margin-bottom: 8px; + text-decoration: underline; + text-decoration-style: dotted; + } + + :global(.icon) { + width: 1.4em; + height: 1.4em; + vertical-align: middle; + transition: transform 0.2s ease-in-out; + } + + &[open] :global(.icon) { + transform: rotate(-180deg); + } +} diff --git a/app/javascript/mastodon/features/account_edit/index.tsx b/app/javascript/mastodon/features/account_edit/index.tsx index a621de4c5de0da..015768175383b6 100644 --- a/app/javascript/mastodon/features/account_edit/index.tsx +++ b/app/javascript/mastodon/features/account_edit/index.tsx @@ -7,6 +7,7 @@ import { useHistory } from 'react-router-dom'; import type { ModalType } from '@/mastodon/actions/modal'; import { openModal } from '@/mastodon/actions/modal'; +import { AccountBio } from '@/mastodon/components/account_bio'; import { Avatar } from '@/mastodon/components/avatar'; import { Button } from '@/mastodon/components/button'; import { DismissibleCallout } from '@/mastodon/components/callout/dismissible'; @@ -201,7 +202,11 @@ export const AccountEdit: FC = () => { /> } > - + = ({ onClose }) => { - return ; +> = ({ onClose, location }) => { + const { profile, isPending } = useAppSelector((state) => state.profileEdit); + + const initialAlt = profile?.[`${location}Description`]; + const imageSrc = profile?.[`${location}Static`]; + + const [altText, setAltText] = useState(initialAlt ?? ''); + + const dispatch = useAppDispatch(); + const handleSave = useCallback(() => { + void dispatch( + patchProfile({ + [`${location}_description`]: altText, + }), + ).then(onClose); + }, [altText, dispatch, location, onClose]); + + if (!imageSrc) { + return ; + } + + return ( + + ) : ( + + ) + } + onClose={onClose} + onConfirm={handleSave} + confirm={ + + } + updating={isPending} + > +
+ +
+
+ ); +}; + +export const ImageAltTextField: FC<{ + imageSrc: string; + altText: string; + onChange: (altText: string) => void; +}> = ({ imageSrc, altText, onChange }) => { + const altLimit = useAppSelector( + (state) => + state.server.getIn( + ['server', 'configuration', 'media_attachments', 'description_limit'], + 150, + ) as number, + ); + + const handleChange: ChangeEventHandler = useCallback( + (event) => { + onChange(event.currentTarget.value); + }, + [onChange], + ); + + return ( + <> + + +
+ + } + hint={ + + } + onChange={handleChange} + value={altText} + /> + +
+ +
+ } + className={classes.altHint} + > +
    {chunks}
, + li: (chunks) =>
  • {chunks}
  • , + }} + tagName='div' + /> +
    + + ); }; diff --git a/app/javascript/mastodon/features/account_edit/modals/image_delete.tsx b/app/javascript/mastodon/features/account_edit/modals/image_delete.tsx index 211dd35bed4084..50bcf3d8a1f6f6 100644 --- a/app/javascript/mastodon/features/account_edit/modals/image_delete.tsx +++ b/app/javascript/mastodon/features/account_edit/modals/image_delete.tsx @@ -1,12 +1,48 @@ +import { useCallback } from 'react'; import type { FC } from 'react'; +import { FormattedMessage } from 'react-intl'; + +import { Button } from '@/mastodon/components/button'; +import { deleteImage } from '@/mastodon/reducers/slices/profile_edit'; import type { ImageLocation } from '@/mastodon/reducers/slices/profile_edit'; +import { useAppDispatch, useAppSelector } from '@/mastodon/store'; import { DialogModal } from '../../ui/components/dialog_modal'; import type { DialogModalProps } from '../../ui/components/dialog_modal'; export const ImageDeleteModal: FC< DialogModalProps & { location: ImageLocation } -> = ({ onClose }) => { - return ; +> = ({ onClose, location }) => { + const isPending = useAppSelector((state) => state.profileEdit.isPending); + const dispatch = useAppDispatch(); + const handleDelete = useCallback(() => { + void dispatch(deleteImage({ location })).then(onClose); + }, [dispatch, location, onClose]); + + return ( + + } + buttons={ + + } + > + + + ); }; diff --git a/app/javascript/mastodon/features/account_edit/modals/image_upload.tsx b/app/javascript/mastodon/features/account_edit/modals/image_upload.tsx index bf6473a9a0c029..ccf65cceed549b 100644 --- a/app/javascript/mastodon/features/account_edit/modals/image_upload.tsx +++ b/app/javascript/mastodon/features/account_edit/modals/image_upload.tsx @@ -8,9 +8,6 @@ import Cropper from 'react-easy-crop'; import { setDragUploadEnabled } from '@/mastodon/actions/compose_typed'; import { Button } from '@/mastodon/components/button'; -import { Callout } from '@/mastodon/components/callout'; -import { CharacterCounter } from '@/mastodon/components/character_counter'; -import { TextAreaField } from '@/mastodon/components/form_fields'; import { RangeInput } from '@/mastodon/components/form_fields/range_input_field'; import { selectImageInfo, @@ -22,6 +19,7 @@ import { useAppDispatch, useAppSelector } from '@/mastodon/store'; import { DialogModal } from '../../ui/components/dialog_modal'; import type { DialogModalProps } from '../../ui/components/dialog_modal'; +import { ImageAltTextField } from './image_alt'; import classes from './styles.module.scss'; import 'react-easy-crop/react-easy-crop.css'; @@ -357,66 +355,19 @@ const StepAlt: FC<{ }> = ({ imageBlob, onCancel, onComplete }) => { const [altText, setAltText] = useState(''); - const handleChange: ChangeEventHandler = useCallback( - (event) => { - setAltText(event.currentTarget.value); - }, - [], - ); - const handleComplete = useCallback(() => { onComplete(altText); }, [altText, onComplete]); const imageSrc = useMemo(() => URL.createObjectURL(imageBlob), [imageBlob]); - const altLimit = useAppSelector( - (state) => - state.server.getIn( - ['server', 'configuration', 'media_attachments', 'description_limit'], - 150, - ) as number, - ); return ( <> - - -
    - - } - hint={ - - } - onChange={handleChange} - /> - -
    - - - } - > - - +