From ac04e62a0ea7467abc6c6552cac2113489bf6aee Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Wed, 20 Dec 2023 10:44:01 +0100 Subject: [PATCH 01/22] Remove unused route definitions (#28430) --- config/routes/api.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config/routes/api.rb b/config/routes/api.rb index ebfa60a69..0fe9f69ab 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -301,10 +301,6 @@ namespace :api, format: false do resources :statuses, only: [:show, :destroy] end - namespace :accounts do - resources :relationships, only: :index - end - namespace :admin do resources :accounts, only: [:index] end From d29b1cca2e1dd9f38a12086ca8010fafbb0cac33 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Wed, 20 Dec 2023 15:35:59 +0100 Subject: [PATCH 02/22] Fix `friends_of_friends` sometimes suggesting already-followed accounts (#28433) --- app/models/account_suggestions/friends_of_friends_source.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/account_suggestions/friends_of_friends_source.rb b/app/models/account_suggestions/friends_of_friends_source.rb index 77d4f635a..28d0ab99b 100644 --- a/app/models/account_suggestions/friends_of_friends_source.rb +++ b/app/models/account_suggestions/friends_of_friends_source.rb @@ -16,7 +16,7 @@ class AccountSuggestions::FriendsOfFriendsSource < AccountSuggestions::Source JOIN account_stats ON account_stats.account_id = accounts.id LEFT OUTER JOIN follow_recommendation_mutes ON follow_recommendation_mutes.target_account_id = accounts.id AND follow_recommendation_mutes.account_id = :id WHERE follows.account_id IN (SELECT * FROM first_degree) - AND follows.target_account_id NOT IN (SELECT * FROM first_degree) + AND NOT EXISTS (SELECT 1 FROM follows f WHERE f.target_account_id = follows.target_account_id AND f.account_id = :id) AND follows.target_account_id <> :id AND accounts.discoverable AND accounts.suspended_at IS NULL From 01f0a6ca4fbb4d53c01f8be353a9ebfeb98f1739 Mon Sep 17 00:00:00 2001 From: Eugen Rochko <eugen@zeonfederated.com> Date: Thu, 21 Dec 2023 09:32:25 +0100 Subject: [PATCH 03/22] Fix profile setup showing default avatar in web UI (#28453) --- app/javascript/mastodon/features/onboarding/profile.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/onboarding/profile.jsx b/app/javascript/mastodon/features/onboarding/profile.jsx index 09e6b2c6c..daaef6065 100644 --- a/app/javascript/mastodon/features/onboarding/profile.jsx +++ b/app/javascript/mastodon/features/onboarding/profile.jsx @@ -26,6 +26,8 @@ const messages = defineMessages({ uploadAvatar: { id: 'onboarding.profile.upload_avatar', defaultMessage: 'Upload profile picture' }, }); +const nullIfMissing = path => path.endsWith('missing.png') ? null : path; + export const Profile = () => { const account = useAppSelector(state => state.getIn(['accounts', me])); const [displayName, setDisplayName] = useState(account.get('display_name')); @@ -61,8 +63,8 @@ export const Profile = () => { setHeader(e.target?.files?.[0]); }, [setHeader]); - const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : account.get('avatar'), [avatar, account]); - const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : account.get('header'), [header, account]); + const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : nullIfMissing(account.get('avatar')), [avatar, account]); + const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : nullIfMissing(account.get('header')), [header, account]); const handleSubmit = useCallback(() => { setIsSaving(true); From 2463b53363a621c1e18223bda5c47a663707a22c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 21 Dec 2023 03:51:03 -0500 Subject: [PATCH 04/22] More duplicates in cli maintenance spec, misc bug fixes (#28449) --- lib/mastodon/cli/maintenance.rb | 40 +- spec/lib/mastodon/cli/maintenance_spec.rb | 426 +++++++++++++++++++++- 2 files changed, 443 insertions(+), 23 deletions(-) diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index 98067c6e3..7b3a9852a 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -40,6 +40,10 @@ module Mastodon::CLI class BulkImport < ApplicationRecord; end class SoftwareUpdate < ApplicationRecord; end + class DomainBlock < ApplicationRecord + scope :by_severity, -> { order(Arel.sql('(CASE severity WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END), domain')) } + end + class PreviewCard < ApplicationRecord self.inheritance_column = false end @@ -249,19 +253,7 @@ module Mastodon::CLI say 'Deduplicating user records…' - # Deduplicating email - ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row| - users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse - ref_user = users.shift - say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow - say "e-mail will be disabled for the following accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow - say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow - - users.each_with_index do |user, index| - user.update!(email: "#{index} " + user.email) - end - end - + deduplicate_users_process_email deduplicate_users_process_confirmation_token deduplicate_users_process_remember_token deduplicate_users_process_password_token @@ -280,6 +272,20 @@ module Mastodon::CLI ActiveRecord::Base.connection.execute('REINDEX INDEX index_users_on_unconfirmed_email;') if migrator_version >= 2023_07_02_151753 end + def deduplicate_users_process_email + ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row| + users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse + ref_user = users.shift + say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow + say "e-mail will be disabled for the following accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow + say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow + + users.each_with_index do |user, index| + user.update!(email: "#{index} " + user.email) + end + end + end + def deduplicate_users_process_confirmation_token ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE confirmation_token IS NOT NULL GROUP BY confirmation_token HAVING count(*) > 1").each do |row| users = User.where(id: row['ids'].split(',')).sort_by(&:created_at).reverse.drop(1) @@ -571,7 +577,7 @@ module Mastodon::CLI say 'Deduplicating webhooks…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webhooks GROUP BY url HAVING count(*) > 1").each do |row| - Webhooks.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) + Webhook.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end say 'Restoring webhooks indexes…' @@ -604,11 +610,7 @@ module Mastodon::CLI say 'Please chose the one to keep unchanged, other ones will be automatically renamed.' - ref_id = ask('Account to keep unchanged:') do |q| - q.required true - q.default 0 - q.convert :int - end + ref_id = ask('Account to keep unchanged:', required: true, default: 0).to_i accounts.delete_at(ref_id) diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb index 353bf08b6..ca492bbf6 100644 --- a/spec/lib/mastodon/cli/maintenance_spec.rb +++ b/spec/lib/mastodon/cli/maintenance_spec.rb @@ -62,6 +62,7 @@ describe Mastodon::CLI::Maintenance do context 'with duplicate accounts' do before do prepare_duplicate_data + choose_local_account_to_keep end let(:duplicate_account_username) { 'username' } @@ -71,21 +72,37 @@ describe Mastodon::CLI::Maintenance do expect { subject } .to output_results( 'Deduplicating accounts', + 'Multiple local accounts were found for', 'Restoring index_accounts_on_username_and_domain_lower', 'Reindexing textual indexes on accounts…', 'Finished!' ) - .and change(duplicate_accounts, :count).from(2).to(1) + .and change(duplicate_remote_accounts, :count).from(2).to(1) + .and change(duplicate_local_accounts, :count).from(2).to(1) end - def duplicate_accounts + def duplicate_remote_accounts Account.where(username: duplicate_account_username, domain: duplicate_account_domain) end + def duplicate_local_accounts + Account.where(username: duplicate_account_username, domain: nil) + end + def prepare_duplicate_data ActiveRecord::Base.connection.remove_index :accounts, name: :index_accounts_on_username_and_domain_lower - Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain) - Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false) + _remote_account = Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain) + _remote_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false) + _local_account = Fabricate(:account, username: duplicate_account_username, domain: nil) + _local_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: nil).save(validate: false) + end + + def choose_local_account_to_keep + allow(cli.shell) + .to receive(:ask) + .with(/Account to keep unchanged/, anything) + .and_return('0') + .once end end @@ -175,6 +192,407 @@ describe Mastodon::CLI::Maintenance do end end + context 'with duplicate account_domain_blocks' do + before do + prepare_duplicate_data + end + + let(:duplicate_domain) { 'example.host' } + let(:account) { Fabricate(:account) } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Removing duplicate account domain blocks', + 'Restoring account domain blocks indexes', + 'Finished!' + ) + .and change(duplicate_account_domain_blocks, :count).from(2).to(1) + end + + def duplicate_account_domain_blocks + AccountDomainBlock.where(account: account, domain: duplicate_domain) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :account_domain_blocks, [:account_id, :domain] + Fabricate(:account_domain_block, account: account, domain: duplicate_domain) + Fabricate.build(:account_domain_block, account: account, domain: duplicate_domain).save(validate: false) + end + end + + context 'with duplicate announcement_reactions' do + before do + prepare_duplicate_data + end + + let(:account) { Fabricate(:account) } + let(:announcement) { Fabricate(:announcement) } + let(:name) { Fabricate(:custom_emoji).shortcode } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Removing duplicate announcement reactions', + 'Restoring announcement_reactions indexes', + 'Finished!' + ) + .and change(duplicate_announcement_reactions, :count).from(2).to(1) + end + + def duplicate_announcement_reactions + AnnouncementReaction.where(account: account, announcement: announcement, name: name) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :announcement_reactions, [:account_id, :announcement_id, :name] + Fabricate(:announcement_reaction, account: account, announcement: announcement, name: name) + Fabricate.build(:announcement_reaction, account: account, announcement: announcement, name: name).save(validate: false) + end + end + + context 'with duplicate conversations' do + before do + prepare_duplicate_data + end + + let(:uri) { 'https://example.host/path' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating conversations', + 'Restoring conversations indexes', + 'Finished!' + ) + .and change(duplicate_conversations, :count).from(2).to(1) + end + + def duplicate_conversations + Conversation.where(uri: uri) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :conversations, :uri + Fabricate(:conversation, uri: uri) + Fabricate.build(:conversation, uri: uri).save(validate: false) + end + end + + context 'with duplicate custom_emojis' do + before do + prepare_duplicate_data + end + + let(:duplicate_shortcode) { 'wowzers' } + let(:duplicate_domain) { 'example.host' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating custom_emojis', + 'Restoring custom_emojis indexes', + 'Finished!' + ) + .and change(duplicate_custom_emojis, :count).from(2).to(1) + end + + def duplicate_custom_emojis + CustomEmoji.where(shortcode: duplicate_shortcode, domain: duplicate_domain) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :custom_emojis, [:shortcode, :domain] + Fabricate(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain) + Fabricate.build(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain).save(validate: false) + end + end + + context 'with duplicate custom_emoji_categories' do + before do + prepare_duplicate_data + end + + let(:duplicate_name) { 'name_value' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating custom_emoji_categories', + 'Restoring custom_emoji_categories indexes', + 'Finished!' + ) + .and change(duplicate_custom_emoji_categories, :count).from(2).to(1) + end + + def duplicate_custom_emoji_categories + CustomEmojiCategory.where(name: duplicate_name) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :custom_emoji_categories, :name + Fabricate(:custom_emoji_category, name: duplicate_name) + Fabricate.build(:custom_emoji_category, name: duplicate_name).save(validate: false) + end + end + + context 'with duplicate domain_allows' do + before do + prepare_duplicate_data + end + + let(:domain) { 'example.host' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating domain_allows', + 'Restoring domain_allows indexes', + 'Finished!' + ) + .and change(duplicate_domain_allows, :count).from(2).to(1) + end + + def duplicate_domain_allows + DomainAllow.where(domain: domain) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :domain_allows, :domain + Fabricate(:domain_allow, domain: domain) + Fabricate.build(:domain_allow, domain: domain).save(validate: false) + end + end + + context 'with duplicate domain_blocks' do + before do + prepare_duplicate_data + end + + let(:domain) { 'example.host' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating domain_blocks', + 'Restoring domain_blocks indexes', + 'Finished!' + ) + .and change(duplicate_domain_blocks, :count).from(2).to(1) + end + + def duplicate_domain_blocks + DomainBlock.where(domain: domain) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :domain_blocks, :domain + Fabricate(:domain_block, domain: domain) + Fabricate.build(:domain_block, domain: domain).save(validate: false) + end + end + + context 'with duplicate email_domain_blocks' do + before do + prepare_duplicate_data + end + + let(:domain) { 'example.host' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating email_domain_blocks', + 'Restoring email_domain_blocks indexes', + 'Finished!' + ) + .and change(duplicate_email_domain_blocks, :count).from(2).to(1) + end + + def duplicate_email_domain_blocks + EmailDomainBlock.where(domain: domain) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :email_domain_blocks, :domain + Fabricate(:email_domain_block, domain: domain) + Fabricate.build(:email_domain_block, domain: domain).save(validate: false) + end + end + + context 'with duplicate media_attachments' do + before do + prepare_duplicate_data + end + + let(:shortcode) { 'codenam' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating media_attachments', + 'Restoring media_attachments indexes', + 'Finished!' + ) + .and change(duplicate_media_attachments, :count).from(2).to(1) + end + + def duplicate_media_attachments + MediaAttachment.where(shortcode: shortcode) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :media_attachments, :shortcode + Fabricate(:media_attachment, shortcode: shortcode) + Fabricate.build(:media_attachment, shortcode: shortcode).save(validate: false) + end + end + + context 'with duplicate preview_cards' do + before do + prepare_duplicate_data + end + + let(:url) { 'https://example.host/path' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating preview_cards', + 'Restoring preview_cards indexes', + 'Finished!' + ) + .and change(duplicate_preview_cards, :count).from(2).to(1) + end + + def duplicate_preview_cards + PreviewCard.where(url: url) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :preview_cards, :url + Fabricate(:preview_card, url: url) + Fabricate.build(:preview_card, url: url).save(validate: false) + end + end + + context 'with duplicate statuses' do + before do + prepare_duplicate_data + end + + let(:uri) { 'https://example.host/path' } + let(:account) { Fabricate(:account) } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating statuses', + 'Restoring statuses indexes', + 'Finished!' + ) + .and change(duplicate_statuses, :count).from(2).to(1) + end + + def duplicate_statuses + Status.where(uri: uri) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :statuses, :uri + Fabricate(:status, account: account, uri: uri) + duplicate = Fabricate.build(:status, account: account, uri: uri) + duplicate.save(validate: false) + Fabricate(:status_pin, account: account, status: duplicate) + Fabricate(:status, in_reply_to_id: duplicate.id) + Fabricate(:status, reblog_of_id: duplicate.id) + end + end + + context 'with duplicate tags' do + before do + prepare_duplicate_data + end + + let(:name) { 'tagname' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating tags', + 'Restoring tags indexes', + 'Finished!' + ) + .and change(duplicate_tags, :count).from(2).to(1) + end + + def duplicate_tags + Tag.where(name: name) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :tags, name: 'index_tags_on_name_lower_btree' + Fabricate(:tag, name: name) + Fabricate.build(:tag, name: name).save(validate: false) + end + end + + context 'with duplicate webauthn_credentials' do + before do + prepare_duplicate_data + end + + let(:external_id) { '123_123_123' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating webauthn_credentials', + 'Restoring webauthn_credentials indexes', + 'Finished!' + ) + .and change(duplicate_webauthn_credentials, :count).from(2).to(1) + end + + def duplicate_webauthn_credentials + WebauthnCredential.where(external_id: external_id) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :webauthn_credentials, :external_id + Fabricate(:webauthn_credential, external_id: external_id) + Fabricate.build(:webauthn_credential, external_id: external_id).save(validate: false) + end + end + + context 'with duplicate webhooks' do + before do + prepare_duplicate_data + end + + let(:url) { 'https://example.host/path' } + + it 'runs the deduplication process' do + expect { subject } + .to output_results( + 'Deduplicating webhooks', + 'Restoring webhooks indexes', + 'Finished!' + ) + .and change(duplicate_webhooks, :count).from(2).to(1) + end + + def duplicate_webhooks + Webhook.where(url: url) + end + + def prepare_duplicate_data + ActiveRecord::Base.connection.remove_index :webhooks, :url + Fabricate(:webhook, url: url) + Fabricate.build(:webhook, url: url).save(validate: false) + end + end + def agree_to_backup_warning allow(cli.shell) .to receive(:yes?) From 9e5ddc1745bf729888dc2b11300955c715e95008 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 10:09:00 +0100 Subject: [PATCH 05/22] New Crowdin Translations (automated) (#28451) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/lad.json | 28 +++++ app/javascript/mastodon/locales/tr.json | 4 +- config/locales/devise.ie.yml | 10 ++ config/locales/ie.yml | 38 +++++++ config/locales/lad.yml | 135 +++++++++++++++++++++++ config/locales/simple_form.ie.yml | 18 +++ config/locales/simple_form.lad.yml | 9 ++ config/locales/tr.yml | 2 +- 8 files changed, 241 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index 1b501c710..c776a91ee 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -77,6 +77,10 @@ "admin.dashboard.retention.average": "Media", "admin.dashboard.retention.cohort": "Mez de enrejistrasyon", "admin.dashboard.retention.cohort_size": "Muevos utilizadores", + "admin.impact_report.instance_accounts": "Profiles de kuentos esto efasaria", + "admin.impact_report.instance_followers": "Suivantes a los kualos nuestros utilizadores perderian", + "admin.impact_report.instance_follows": "Suivantes a los kualos sus utilizadores perderian", + "admin.impact_report.title": "Rezumen de impakto", "alert.rate_limited.message": "Por favor aprova dempues de {retry_time, time, medium}.", "alert.rate_limited.title": "Trafiko limitado", "alert.unexpected.message": "Afito un yerro no asperado.", @@ -220,6 +224,7 @@ "emoji_button.search_results": "Rizultados de bushkeda", "emoji_button.symbols": "Simbolos", "emoji_button.travel": "Viajes i lugares", + "empty_column.account_hides_collections": "Este utilizador desidio no mostrar esta enformasyon", "empty_column.account_suspended": "Kuento suspendido", "empty_column.account_timeline": "No ay publikasyones aki!", "empty_column.account_unavailable": "Profil no desponivle", @@ -294,6 +299,8 @@ "hashtag.column_settings.tag_mode.any": "Kualsekera de estos", "hashtag.column_settings.tag_mode.none": "Dinguno de estos", "hashtag.column_settings.tag_toggle": "Inkluir etiketas adisionalas en esta kolumna", + "hashtag.counter_by_accounts": "{count, plural, one {{counter} partisipante} other {{counter} partisipantes}}", + "hashtag.counter_by_uses": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}} oy", "hashtag.follow": "Segir etiketa", "hashtag.unfollow": "Desegir etiketa", @@ -303,18 +310,23 @@ "home.column_settings.basic": "Opsyones bazikas", "home.column_settings.show_reblogs": "Amostrar repartajasyones", "home.column_settings.show_replies": "Amostrar repuestas", + "home.explore_prompt.body": "Tu linya prinsipala es una mikstura de publikasyones kon etiketas a las kualas eskojites a segir, la djente a la kuala eskojites a segir i las publikasyones ke eyos repartajan. Si esta demaziado trankila, puedes:", + "home.explore_prompt.title": "Esta es tu baza prinsipala en Mastodon.", "home.hide_announcements": "Eskonde pregones", "home.pending_critical_update.body": "Por favor aktualiza tu sirvidor de Mastodon pishin!", "home.pending_critical_update.link": "Ve aktualizasyones", + "home.pending_critical_update.title": "Aktualizasyon de seguridad kritika esta desponivle!", "home.show_announcements": "Amostra pregones", "interaction_modal.description.favourite": "Kon un kuento en Mastodon, puedes markar esta publikasyon komo favorita para ke el autor sepa ke te plaze i para guadrarla para dempues.", "interaction_modal.description.follow": "Kon un kuento en Mastodon, puedes segir a {name} para risivir sus publikasyones en tu linya temporal prinsipala.", "interaction_modal.description.reblog": "Kon un kuento en Mastodon, puedes repartajar esta publikasyon para amostrarla a tus suivantes.", "interaction_modal.description.reply": "Kon un kuento en Mastodon, puedes arispondir a esta publikasyon.", + "interaction_modal.login.action": "Va a tu sirvidor", "interaction_modal.login.prompt": "Domeno del sirvidor de tu kuento, por enshemplo mastodon.social", "interaction_modal.no_account_yet": "No tyenes kuento de Mastodon?", "interaction_modal.on_another_server": "En otro sirvidor", "interaction_modal.on_this_server": "En este sirvidor", + "interaction_modal.sign_in": "No estas konektado kon este sirvidor. Ande tyenes tu kuento?", "interaction_modal.title.favourite": "Endika ke te plaze publikasyon de {name}", "interaction_modal.title.follow": "Sige a {name}", "interaction_modal.title.reblog": "Repartaja publikasyon de {name}", @@ -369,6 +381,7 @@ "lists.delete": "Efasa lista", "lists.edit": "Edita lista", "lists.edit.submit": "Troka titolo", + "lists.exclusive": "Eskonder estas publikasyones de linya prinsipala", "lists.new.create": "Adjusta lista", "lists.new.title_placeholder": "Titolo de mueva lista", "lists.replies_policy.followed": "Kualseker utilizardo segido", @@ -403,6 +416,7 @@ "navigation_bar.lists": "Listas", "navigation_bar.logout": "Salir", "navigation_bar.mutes": "Utilizadores silensiados", + "navigation_bar.opened_in_classic_interface": "Publikasyones, kuentos i otras pajinas espesifikas se avren kon preferensyas predeterminadas en la enterfaz web klasika.", "navigation_bar.personal": "Personal", "navigation_bar.pins": "Publikasyones fiksadas", "navigation_bar.preferences": "Preferensyas", @@ -460,17 +474,24 @@ "notifications_permission_banner.title": "Nunka te piedres niente", "onboarding.action.back": "Va atras", "onboarding.actions.back": "Va atras", + "onboarding.actions.go_to_explore": "Va a los trendes", + "onboarding.actions.go_to_home": "Va a tu linya prinsipala", "onboarding.compose.template": "Ke haber, #Mastodon?", "onboarding.follows.title": "Personaliza tu linya prinsipala", "onboarding.profile.display_name": "Nombre amostrado", "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", + "onboarding.share.message": "Soy {username} en #Mastodon! Segidme en {url}", + "onboarding.share.next_steps": "Posivles sigientes pasos:", "onboarding.share.title": "Partaja tu profil", "onboarding.start.skip": "No nesesitas ayudo para ampesar?", + "onboarding.start.title": "Lo logrates!", + "onboarding.steps.follow_people.body": "El buto de Mastodon es segir a djente interesante.", "onboarding.steps.follow_people.title": "Personaliza tu linya prinsipala", "onboarding.steps.publish_status.title": "Eskrive tu primera publikasyon", "onboarding.steps.setup_profile.title": "Personaliza tu profil", @@ -515,6 +536,7 @@ "reply_indicator.cancel": "Anula", "report.block": "Bloka", "report.block_explanation": "No veras sus publikasyones. No podra ver tus publikasyones ni segirte. Podra saver ke le blokates.", + "report.categories.legal": "Legal", "report.categories.other": "Otros", "report.categories.spam": "Spam", "report.categories.violation": "El kontenido viola una o mas reglas del sirvidor", @@ -552,6 +574,7 @@ "report.unfollow": "Desegir a @{name}", "report.unfollow_explanation": "Estas sigiendo este kuento. Para no ver sus publikasyones en tu linya de tiempo, puedes deshar de segirlo.", "report_notification.attached_statuses": "{count, plural, one {{count} publikasyon} other {{count} publikasyones}} atadas", + "report_notification.categories.legal": "Legal", "report_notification.categories.other": "Otros", "report_notification.categories.spam": "Spam", "report_notification.categories.violation": "Violasion de reglas", @@ -570,6 +593,7 @@ "search_popout.options": "Opsyones de bushkeda", "search_popout.quick_actions": "Aksiones rapidas", "search_popout.recent": "Bushkedas resientes", + "search_popout.specific_date": "dato espesifiko", "search_popout.user": "utilizador", "search_results.accounts": "Profiles", "search_results.all": "Todos", @@ -596,10 +620,12 @@ "status.cannot_reblog": "Esta publikasyon no se puede repartajar", "status.copy": "Kopia atadijo de publikasyon", "status.delete": "Efasa", + "status.detailed_status": "Vista de konversasyon detalyada", "status.direct": "Enmenta a @{name} en privado", "status.direct_indicator": "Enmentadura privada", "status.edit": "Edita", "status.edited": "Editado {date}", + "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} vezes}}", "status.embed": "Inkrusta", "status.favourite": "Te plaze", "status.filter": "Filtra esta publikasyon", @@ -637,6 +663,7 @@ "status.show_more": "Amostra mas", "status.show_more_all": "Amostra mas para todo", "status.show_original": "Amostra orijinal", + "status.title.with_attachments": "{user} publiko {attachmentCount, plural, one {un anekso} other {{attachmentCount} aneksos}}", "status.translate": "Trezlada", "status.translated_from_with": "Trezladado dizde {lang} kon {provider}", "status.uncached_media_warning": "Vista previa no desponivle", @@ -685,6 +712,7 @@ "upload_modal.preview_label": "Vista previa ({ratio})", "upload_progress.label": "Kargando...", "upload_progress.processing": "Prosesando…", + "username.taken": "Akel nombre de utilizador ya esta en uzo. Aprova otruno", "video.close": "Serra video", "video.download": "Abasha dosya", "video.exit_fullscreen": "Sal de ekran kompleto", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 8eb09bb7c..e6389b05a 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -45,7 +45,7 @@ "account.joined_short": "Katıldı", "account.languages": "Abone olunan dilleri değiştir", "account.link_verified_on": "Bu bağlantının sahipliği {date} tarihinde denetlendi", - "account.locked_info": "Bu hesabın gizlilik durumu gizli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini manuel olarak onaylıyor.", + "account.locked_info": "Bu hesabın gizlilik durumu gizli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini elle onaylıyor.", "account.media": "Medya", "account.mention": "@{name} kişisinden bahset", "account.moved_to": "{name} yeni hesabının artık şu olduğunu belirtti:", @@ -345,7 +345,7 @@ "keyboard_shortcuts.down": "Listede aşağıya inmek için", "keyboard_shortcuts.enter": "gönderiyi aç", "keyboard_shortcuts.favourite": "Gönderiyi favorilerine ekle", - "keyboard_shortcuts.favourites": "Favoriler listeni aç", + "keyboard_shortcuts.favourites": "Gözde listeni aç", "keyboard_shortcuts.federated": "Federe akışı aç", "keyboard_shortcuts.heading": "Klavye kısayolları", "keyboard_shortcuts.home": "Ana akışı aç", diff --git a/config/locales/devise.ie.yml b/config/locales/devise.ie.yml index cee5357c2..0cf0fbe1f 100644 --- a/config/locales/devise.ie.yml +++ b/config/locales/devise.ie.yml @@ -2,8 +2,16 @@ ie: devise: failure: + already_authenticated: Tu ha ja intrat. + inactive: Tui conto ancor ne ha esset activat. invalid: Ínvalid %{authentication_keys} o passa-parol. + last_attempt: Hay solmen un prova ante que tui conto deveni serrat. + locked: Tui conto es serrat. not_found_in_database: Ínvalid %{authentication_keys} o passa-parol. + pending: Tui conto es ancor sub revision. + timeout: Tui session ha expirat. Ples reintrar denov por continuar. + unauthenticated: Tu deve intrar o registrar te ante continuar. + unconfirmed: Tu deve confirmar tui e-posta ante continuar. mailer: email_changed: extra: Si tu ne changeat tui email-adresse, it es probabil que alqui ha ganiat accesse a tui conto. Ples changear tui passa-parol strax o contacter li administrator del servitor si tu ne posse intrar tui conto. @@ -20,6 +28,8 @@ ie: title: Reiniciar passa-parol two_factor_disabled: explanation: 2-factor autentication por tui conto ha esset desactivisat. Aperter session nu es possibil solmen per email-adresse e passa-parol. + omniauth_callbacks: + failure: Ne posset autenticar te de %{kind} pro "%{reason}". passwords: no_token: Tu ne posse accessar ti-ci págine sin venir de un email pri reiniciar li passa-parol. Si tu ha venit de un email pri reiniciar li passa-parol, ples far cert que tu usat li complet URL providet. send_instructions: Si tui email-adresse existe in nor database, tu va reciver un ligament por recuperar li passa-parol a tui email-adresse in quelc minutes. Ples vider tui spam-emails si tu ne recivet ti email. diff --git a/config/locales/ie.yml b/config/locales/ie.yml index 94df7d145..c9eef7520 100644 --- a/config/locales/ie.yml +++ b/config/locales/ie.yml @@ -329,6 +329,7 @@ ie: title: Adjunter nov customisat emoji no_emoji_selected: Null emoji esset changeat pro que null esset selectet not_permitted: Tu ne es permisset far ti action + overwrite: Remplazzar shortcode_hint: Adminim 2 carácteres, solmen lítteres, ciffres e sublineas title: Customisat emoji uncategorized: Íncategorisat @@ -433,6 +434,7 @@ ie: title: Bloccar nov email-dominia no_email_domain_block_selected: Null email-dominia-bloccas esset changeat pro que null esset selectet not_permitted: Ne permisset + resolved_through_html: Resoluet per %{domain} title: Bloccat email-dominias export_domain_allows: new: @@ -459,6 +461,7 @@ ie: unsuppress: Restaurar seque-recomandation instances: availability: + no_failures_recorded: Null fallimentes registrat. title: Disponibilitá warning: Li ultim prova conexer a ti servitor ha esset ínsuccessosi back_to_all: Omni @@ -601,15 +604,36 @@ ie: status: Statu statuses: Contenete raportat summary: + action_preambles: + delete_html: 'Tu va <strong>remover</strong> alcun postas de <strong>@%{acct}</strong>''. To va:' + mark_as_sensitive_html: 'Tu va <strong>marcar</strong> alcun postas de <strong>@%{acct}</strong> quam <strong>sensitiv</strong>. To va:' + silence_html: 'Tu va <strong>limitar</strong> li conto de <strong>@%{acct}</strong>. To va:' + suspend_html: 'Tu va <strong>suspender</strong> li conto de <strong>@%{acct}</strong>. To va:' close_report: 'Marcar raporte #%{id} quam resoluet' title: Raportes + unassign: Ínassignar + unknown_action_msg: 'Ínconosset action: %{action}' + unresolved: Ínresoluet + updated_at: Actualisat + view_profile: Vider profil roles: + add_new: Adjunter un rol + assigned_users: + one: "%{count} usator" + other: "%{count} usatores" categories: + administration: Administration devops: DevOps invites: Invitationes moderation: Moderation special: Special delete: Deleter + description_html: Con <strong>roles por usatores</strong>, tu posse customisar li functiones e locs de Mastodon in queles tui usatores posse accesser. + edit: Modificar rol '%{name}' + everyone: Permissiones predefinit + permissions_count: + one: "%{count} permission" + other: "%{count} permissiones" privileges: administrator: Administrator delete_user_data: Deleter Data de Usator @@ -618,26 +642,39 @@ ie: manage_announcements: Tractar proclamationes manage_announcements_description: Permisse usatores tractar proclamationes sur li servitor manage_appeals: Gerer Apelles + manage_blocks: Gerer Bloccas + manage_custom_emojis: Gerer Customisat Emojis manage_federation: Gerer Federation manage_invites: Gerer Invitationes manage_reports: Gerer Raportes manage_roles: Gerer Roles + manage_rules: Gerer Regules + manage_settings: Gerer Parametres manage_taxonomies: Gerer Taxonomies manage_user_access_description: Permisse usatores desactivisar li 2-factor autentication de altri usatores, changear lor email-adresses, e reiniciar lor passa-paroles manage_users: Gerer usatores + manage_webhooks: Gerer Web-crocs + view_devops: DevOps title: Roles rules: add_new: Adjunter un regule + delete: Deleter + edit: Redacter regul + empty: Ancor null regules de servitor ha esset definit. title: Regules del servitor settings: about: + manage_rules: Gerer regules de servitor title: Pri appearance: title: Aspecte discovery: profile_directory: Profilarium public_timelines: Public témpor-lineas + title: Decovriment domain_blocks: + all: Ad omnes + disabled: A necun users: A local usatores qui ha initiat session registrations: title: Registrationes @@ -645,6 +682,7 @@ ie: modes: none: Nequi posse registrar se open: Quicunc posse registrar se + title: Parametres del servitor site_uploads: delete: Deleter cargat file destroyed_msg: Cargat file successosimen deletet! diff --git a/config/locales/lad.yml b/config/locales/lad.yml index afa6d2930..36364feba 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -306,6 +306,7 @@ lad: unpublish: Retirar publikasyon unpublished_msg: Pregon retirado kon sukseso! updated_msg: Pregon aktualizado kon sukseso! + critical_update_pending: Aktualizasyon kritika esta asperando custom_emojis: assign_category: Asinyar kategoria by_domain: Domeno @@ -331,6 +332,7 @@ lad: no_emoji_selected: No se troko dingun emoji porke no eskojites dinguno not_permitted: No tienes permiso para realizar esta aksyon overwrite: Sobreskrive + shortcode: Kodiche kurto title: Emojis personalizados uncategorized: No kategorizado unlist: No lista @@ -380,6 +382,7 @@ lad: confirm_suspension: cancel: Anula confirm: Suspende + title: Konfirma bloko de domeno para %{domain} created_msg: El bloko de domeno esta siendo prosesado destroyed_msg: El bloko de domeno se dezizo domain: Domeno @@ -702,6 +705,7 @@ lad: manage_users: Administra utilizadores manage_users_description: Permete a los utilizadores ver los peratim de otros utilizadores i realizar aksyones de moderasyon kontra eyos manage_webhooks: Administrar webhooks + view_dashboard: Ve pano view_devops: DevOps title: Rolos rules: @@ -715,10 +719,17 @@ lad: title: Sovre esto appearance: title: Aparensya + branding: + title: Marka + content_retention: + title: Retensyon de kontenido discovery: follow_recommendations: Rekomendasyones de kuentos profile_directory: Katalogo de profiles public_timelines: Linyas de tiempo publikas + publish_discovered_servers: Publika sirvidores diskuviertos + publish_statistics: Publika estatistikas + title: Diskuvrimiento trends: Trendes domain_blocks: all: A todos @@ -737,7 +748,9 @@ lad: delete: Efasa dosya kargada destroyed_msg: Dosya supremida kon sukseso! software_updates: + critical_update: Kritiko – por favor aktualiza pishin documentation_link: Ambezate mas + release_notes: Notas sovre la versyon title: Aktualizasyones desponivles type: Tipo version: Versyon @@ -795,6 +808,10 @@ lad: message_html: No tienes definido dinguna regla del sirvidor. sidekiq_process_check: message_html: No ay dingun prosedura Sidekiq en egzekusion para la(s) kola(s) %{value}. Por favor, reviza tu konfigurasyon de Sidekiq + software_version_critical_check: + action: Amostra aktualizasyones desponivles + software_version_patch_check: + action: Amostra aktualizasyones desponivles upload_check_privacy_error: message_html: "<strong>Tu sirvidor de web es mal konfigurado. La privasita de tus utilizadores esta en riziko.</strong>" upload_check_privacy_error_object_storage: @@ -852,7 +869,9 @@ lad: listable: Pueden ser rekomendadas no_tag_selected: No se troko dinguna etiketa al no eskojer dinguna not_listable: No seran rekomendadas + not_trendable: No aperesera en trendes not_usable: No se pueden uzar + title: Etiketas en trend trendable: Pueden apareser en trendes trending_rank: Trend n.º %{rank} usable: Pueden uzarse @@ -901,6 +920,8 @@ lad: body: "%{target} esta apelando a una solisitasyon de moderasyon de %{action_taken_by} el %{date}, del tipo %{type}. Eyos eskrivieron:" next_steps: Puedes achetar la apelasyon para dezazer la dechizyon de moderasyon, o ignorarla. subject: "%{username} esta apelando a una dechizyon de moderasyon en %{instance}" + new_critical_software_updates: + subject: Ay aktualizasyones kritikas de Mastodon desponivles para %{instance}! new_pending_account: body: Los peratim del muevo kuento estan abashos. Puedes achetar o refuzar esta aplikasyon. subject: Muevo kuento para revizion en %{instance} (%{username}) @@ -908,6 +929,9 @@ lad: body: "%{reporter} tiene raportado a %{target}" body_remote: Alguno de %{domain} a raportado a %{target} subject: Muevo raporto para la %{instance} (#%{id}) + new_software_updates: + body: Ay mueva versyon de Mastodon, kizas keras aktualizar! + subject: Ay muevas versyones de Mastodon desponivles para %{instance}! new_trends: body: 'Los sigientes elementos nesesitan una revizion antes de ke se puedan amostrar publikamente:' new_trending_links: @@ -916,10 +940,20 @@ lad: title: Publikasyones en trend new_trending_tags: title: Etiketas en trend + aliases: + add_new: Kriya un alias + empty: No tienes aliases. + remove: Dezata alias appearance: advanced_web_interface: Enterfaz web avanzada + animations_and_accessibility: Animasyones i aksesivilita + confirmation_dialogs: Dialogos de konfirmasyon + discovery: Diskuvrimiento + localization: + guide_link_text: Todos pueden kontribuir. sensitive_content: Kontenido sensivle application_mailer: + notification_preferences: Troka preferensyas de posta salutation: "%{name}," settings: 'Troka preferensyas de posta: %{link}' unsubscribe: Dezabona @@ -936,7 +970,11 @@ lad: your_token: Tu token de akseso auth: apply_for_account: Solisita un kuento + captcha_confirmation: + title: Kontrolo de sigurita confirmations: + awaiting_review_title: Estamos revizando tu enrejistramiento + clicking_this_link: klikando en este atadijo login_link: konektate kon kuento welcome_title: Bienvenido, %{name}! wrong_email_hint: Si este adreso de posta es inkorekto, puedes trokarlo en las preferensyas del kuento. @@ -962,6 +1000,7 @@ lad: progress: confirm: Konfirma posta details: Tus detalyos + review: Muestra revizyon rules: Acheta reglas providers: cas: CAS @@ -975,10 +1014,12 @@ lad: back: Atras preamble: Estas son establesidas i aplikadas por los moderadores de %{domain}. title: Algunas reglas bazikas. + title_invited: Fuites envitado. security: Sigurita set_new_password: Establese muevo kod setup: email_below_hint_html: Mira en tu kuti de spam o solisita de muevo. Si el adreso de posta elektronika ke aparese aki es yerrado, puedes trokarlo aki. + link_not_received: No risivites un atadijo? sign_in: preamble_html: Konektate kon tus kredensiales de <strong>%{domain}</strong>. Si tu kuento esta balabayado en otruno servidor, no puedras konektarte aki. title: Konektate kon %{domain} @@ -1023,6 +1064,8 @@ lad: x_seconds: "%{count} s" deletes: proceed: Efasa kuento + warning: + username_unavailable: Tu nombre de utilizador no estara desponivle disputes: strikes: action_taken: Aksyon tomada @@ -1054,6 +1097,7 @@ lad: domain_validator: invalid_domain: no es un nombre de domeno valido edit_profile: + basic_information: Enformasyon bazika other: Otros errors: '400': La solisitasyon ke enviates no fue valida o fue malformada. @@ -1093,14 +1137,27 @@ lad: filters: contexts: account: Profiles + home: Prinsipyo i listas notifications: Avizos public: Linyas de tiempo publikas + thread: Konversasyones edit: add_keyword: Adjusta biervo yave keywords: Biervos yaves statuses: Publikasyones individualas + title: Edita filtro index: delete: Efasa + empty: No tyenes filtros. + keywords: + one: "%{count} biervo yave" + other: "%{count} biervos yave" + statuses: + one: "%{count} publikasyon" + other: "%{count} publikasyones" + statuses_long: + one: "%{count} publikasyon individuala eskondida" + other: "%{count} publikasyones individualas eskondidas" title: Filtros new: save: Guadra muevo filtro @@ -1109,17 +1166,22 @@ lad: back_to_filter: Retorna al filtro batch: remove: Kita del filtro + index: + title: Publikasyones filtradas generic: all: Todos cancel: Anula + changes_saved_msg: Trokamientos guadrados kon reusho! confirm: Konfirma copy: Kopia delete: Efasa + deselect: Deseleksyonar todo none: Dinguno save_changes: Guadra trokamientos today: oy imports: errors: + empty: Dosya CSV vaziya invalid_csv_file: 'Dosya CSV no valida. Yerro: %{error}' over_rows_processing_limit: kontiene mas de %{count} filas too_large: Dosya es mas grande @@ -1130,19 +1192,35 @@ lad: overwrite: Sobreskrive overwrite_long: Mete muevos rejistros en vez de los aktuales preface: Puedes importar siertos datos, komo todas las personas a las kualas estas sigiendo o blokando en tu kuento en esta instansya, dizde dosyas eksportadas de otra instansya. + recent_imports: Importasyones resyentes states: finished: Finalizado scheduled: Programado unconfirmed: Sin konfirmasyon status: Estado success: Tus datos se tienen kargado djustamente i seran prosesados pishin + time_started: Ampesado el + titles: + bookmarks: Importando markadores + domain_blocking: Importando domenos blokados + following: Importando kuentos segidos + lists: Importando listas + muting: Importando kuentos silensyados + type: Tipo de importasyon + type_groups: + constructive: Segidores i markadores + destructive: Blokos i silensyos types: blocking: Lista de blokos bookmarks: Markadores + domain_blocking: Lista de domenos blokados + following: Lista de segidos lists: Listas + muting: Lista de silensyados upload: Karga invites: delete: Dezaktiva + expired: Kadukado expires_in: '1800': 30 minutos '21600': 6 oras @@ -1151,6 +1229,8 @@ lad: '604800': 1 semana '86400': 1 diya expires_in_prompt: Nunkua + invalid: Esta envitasyon no es valida + invited_by: 'Fuites envitado por:' max_uses: one: 1 uzo other: "%{count} uzos" @@ -1214,12 +1294,31 @@ lad: moderation: title: Moderasyon notification_mailer: + admin: + sign_up: + subject: "%{name} se enrejistro" + favourite: + title: Muevo favorito follow: + body: "%{name} te esta sigiendo!" + subject: "%{name} te esta sigiendo" title: Muevo suivante follow_request: + subject: 'Segidor esta asperando: %{name}' title: Mueva solisitud de segimiento mention: action: Arisponde + body: 'Fuites enmentado por %{name} en:' + subject: Fuites enmentado por %{name} + title: Mueva enmentadura + reblog: + title: Mueva repartajasyon + status: + subject: "%{name} publiko algo" + update: + subject: "%{name} edito una publikasyon" + notifications: + other_settings: Otras preferensyas de avizos number: human: decimal_units: @@ -1227,7 +1326,9 @@ lad: units: billion: MM million: M + quadrillion: Ku thousand: K + trillion: T otp_authentication: enable: Kapasita instructions_html: "<strong>Eskanea este kodiche QR dizde Google Authenticator o una aplikasyon similar en tu telefon</strong>. A partir de agora, esta aplikasyon djenerara kodiches ke tendras ke ingresar kuando keras konektarte kon tu kuento." @@ -1249,6 +1350,7 @@ lad: expired: La anketa ya tiene eskapado invalid_choice: La opsyon de voto eskojida no egziste over_character_limit: no puede trespasar %{max} karakteres kada uno + self_vote: No puedes votar en tus propias anketas too_few_options: deve tener mas de un elemento too_many_options: no puede kontener mas de %{max} elementos preferences: @@ -1286,6 +1388,7 @@ lad: content_warning: 'Avertensya de kontenido:' sessions: activity: Ultima aktivita + browser: Navigador browsers: alipay: Alipay blackberry: BlackBerry @@ -1293,6 +1396,7 @@ lad: edge: Microsoft Edge electron: Electron firefox: Firefox + generic: Navigador deskonosido huawei_browser: Huawei Browser ie: Internet Explorer micro_messenger: MicroMessenger @@ -1303,6 +1407,7 @@ lad: qq: QQ Browser safari: Safari uc_browser: UC Browser + unknown_browser: Navigador deskonosido weibo: Weibo current_session: Sesyon aktuala ip: IP @@ -1374,9 +1479,30 @@ lad: limit: Ya tienes fiksado el numero maksimo de publikasyones ownership: La publikasyon de otra persona no puede fiksarse poll: + total_people: + one: "%{count} persona" + other: "%{count} personas" + total_votes: + one: "%{count} voto" + other: "%{count} votos" vote: Vota show_more: Amostra mas + show_newer: Amostra mas muevos + show_older: Amostra mas viejos + show_thread: Amostra diskusyon + title: '%{name}: "%{quote}"' + visibilities: + direct: Direkto + private: Solo suivantes + private_long: Solo amostra a tus segidores + public: Publiko + public_long: Todos pueden ver + unlisted: No listado statuses_cleanup: + enabled: Otomatikamente efasa publikasyones viejas + exceptions: Eksepsiones + ignore_favs: Ignora favoritos + ignore_reblogs: Ignora repartajasyones min_age: '1209600': 2 semanas '15778476': 6 mezes @@ -1451,14 +1577,23 @@ lad: mark_statuses_as_sensitive: Algunas de tus publikasyones an sido markados komo sensivles por los moderadores de %{instance}. Esto sinyifika ke la djente tendra ke pulsar los dosyas multimedia en las publikasyones antes de ke se amostre una vista previa. Puedes markar los dosyas multimedia komo sensivles tu mezmo kuando publikes en el avenir. sensitive: A partir de agora todas los dosyas multimedia ke subas seran markados komo sensivles i eskondidos tras una avertensya. reason: 'Razon:' + subject: + none: Avertensya para %{acct} title: + delete_statuses: Publikasyones efasadas + mark_statuses_as_sensitive: Publikasyones markadas komo sensivles none: Avertensya silence: Kuento limitado suspend: Kuento suspendido + welcome: + final_action: Ampesa a publikar + subject: Bienvenido a Mastodon users: + invalid_otp_token: Kodiche de dos pasos no valido signed_in_as: 'Konektado komo:' verification: verification: Verifikasyon + verified_links: Tus atadijos verifikados webauthn_credentials: add: Adjusta mueva yave de sigurita delete: Efasa diff --git a/config/locales/simple_form.ie.yml b/config/locales/simple_form.ie.yml index a94903b66..ff1587be8 100644 --- a/config/locales/simple_form.ie.yml +++ b/config/locales/simple_form.ie.yml @@ -47,6 +47,7 @@ ie: setting_display_media_show_all: Sempre monstrar medie form_admin_settings: bootstrap_timeline_accounts: Ti-ci contos va esser pinglat al parte superiori del recomandationes por nov usatores. + site_contact_username: Qualmen li gente posse atinger te sur Mastodon. theme: Li dessine quel ínregistrat visitantes e nov usatores vide. timeline_preview: Ínregistrat visitantes va posser vider li max recent public postas disponibil che li servitor. trends_as_landing_page: Monstrar populari contenete a ínregistrat visitantes vice un description del servitor. Besona que tendenties es activisat. @@ -67,15 +68,32 @@ ie: starts_at: Comense del eveniment text: Proclamation defaults: + chosen_languages: Filtrar lingues confirm_new_password: Confirmar nov passa-parol confirm_password: Confirmar passa-parol + context: Filtrar contextus current_password: Actual passa-parol + data: Data + display_name: Nómine a monstrar + email: E-posta + expires_in: Expirar pos honeypot: "%{label} (ne plenar)" locale: Lingue del interfacie new_password: Nov passa-parol note: Biografie password: Passa-parol + setting_default_language: Lingue in quel postar + setting_default_privacy: Privatie de postada + setting_default_sensitive: Sempre marcar medie quam sensitiv + setting_display_media_default: Predefinitiones + setting_display_media_hide_all: Celar omno + setting_display_media_show_all: Monstrar omno + setting_theme: Tema de situ + setting_trends: Monstrar li hodial tendenties + setting_unfollow_modal: Monstrar dialog de confirmation ante dessequer alquem + setting_use_pending_items: Mode lent form_admin_settings: registrations_mode: Qui posse registrar se + theme: Predefenit tema notification_emails: follow_request: Alqui petit sequer te diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml index 33fe877db..e0b8eab87 100644 --- a/config/locales/simple_form.lad.yml +++ b/config/locales/simple_form.lad.yml @@ -168,14 +168,18 @@ lad: confirm_password: Konfirma kod context: Filtra kontekstos current_password: Kod aktual + data: Datos display_name: Nombre amostrado email: Adreso de posta elektronika expires_in: Kaduka dempues de + fields: Datos adisyonales header: Imaje de kavesera + locale: Lingua de enterfaz new_password: Muevo kod note: Deskripsyon otp_attempt: Kodiche de dos pasos password: Kod + phrase: Biervo yave o fraza setting_default_language: Lingua de publikasyones setting_default_privacy: Privasita de publikasyones setting_display_media_default: Predeterminado @@ -247,6 +251,8 @@ lad: follow_request: Alguno tiene solisitado segirte mention: Alguno te enmento reblog: Alguno repartajo tu publikasyon + software_updates: + label: Mueva version de Mastodon esta desponivle rule: text: Regla tag: @@ -259,8 +265,11 @@ lad: position: Priorita webhook: events: Evenimientos kapasitados + url: URL de Endpoint 'no': 'No' + not_recommended: No rekomendado recommended: Rekomendado required: mark: "*" + text: rekerido 'yes': Si diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 098719eb7..568607e70 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1272,7 +1272,7 @@ tr: remove: Filtreden kaldır index: hint: Bu filtre diğer ölçütlerden bağımsız olarak tekil gönderileri seçmek için uygulanıyor. Web arayüzünü kullanarak bu filtreye daha fazla gönderi ekleyebilirsiniz. - title: Filtrelenmiş gönderiler + title: Süzgeçlenmiş gönderiler generic: all: Tümü all_items_on_page_selected_html: From 87e2bd02ac3accd198f43d30723bc3027fb4bffd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 10:13:10 +0100 Subject: [PATCH 06/22] Update dependency irb to v1.11.0 (#28442) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 15bdb9b4f..cc262f8e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -376,8 +376,8 @@ GEM rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) idn-ruby (0.1.5) - io-console (0.6.0) - irb (1.10.1) + io-console (0.7.1) + irb (1.11.0) rdoc reline (>= 0.3.8) jmespath (1.6.2) @@ -540,7 +540,7 @@ GEM activesupport (>= 7.0.0) rack railties (>= 7.0.0) - psych (5.1.1.1) + psych (5.1.2) stringio public_suffix (5.0.4) puma (6.4.0) @@ -614,7 +614,7 @@ GEM link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.6.1) rdf (~> 3.2) - rdoc (6.6.1) + rdoc (6.6.2) psych (>= 4.0.0) redcarpet (3.6.0) redis (4.8.1) From c753b1ad35ae84c21c0474bdb052833b2bd4463a Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 21 Dec 2023 04:18:38 -0500 Subject: [PATCH 07/22] Clean up of `RSpec/LetSetup` within `spec/models` (#28444) --- .rubocop_todo.yml | 3 --- spec/models/account_spec.rb | 1 + spec/models/canonical_email_block_spec.rb | 2 +- spec/models/user_spec.rb | 2 ++ 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 030f31110..bb2715f96 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -63,11 +63,8 @@ RSpec/LetSetup: - 'spec/lib/activitypub/activity/delete_spec.rb' - 'spec/lib/vacuum/applications_vacuum_spec.rb' - 'spec/lib/vacuum/preview_cards_vacuum_spec.rb' - - 'spec/models/account_spec.rb' - 'spec/models/account_statuses_cleanup_policy_spec.rb' - - 'spec/models/canonical_email_block_spec.rb' - 'spec/models/status_spec.rb' - - 'spec/models/user_spec.rb' - 'spec/services/account_statuses_cleanup_service_spec.rb' - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb' - 'spec/services/activitypub/fetch_remote_status_service_spec.rb' diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 522549125..87aa8bc75 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -954,6 +954,7 @@ RSpec.describe Account do it 'returns every usable non-suspended account' do expect(described_class.searchable).to contain_exactly(silenced_local, silenced_remote, local_account, remote_account) + expect(described_class.searchable).to_not include(suspended_local, suspended_remote, unconfirmed, unapproved) end it 'does not mess with previously-applied scopes' do diff --git a/spec/models/canonical_email_block_spec.rb b/spec/models/canonical_email_block_spec.rb index 0acff8237..c63483f96 100644 --- a/spec/models/canonical_email_block_spec.rb +++ b/spec/models/canonical_email_block_spec.rb @@ -28,7 +28,7 @@ RSpec.describe CanonicalEmailBlock do end describe '.block?' do - let!(:canonical_email_block) { Fabricate(:canonical_email_block, email: 'foo@bar.com') } + before { Fabricate(:canonical_email_block, email: 'foo@bar.com') } it 'returns true for the same email' do expect(described_class.block?('foo@bar.com')).to be true diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 9111fd7c7..ab5bd39b7 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -422,6 +422,7 @@ RSpec.describe User do it 'deactivates all sessions' do expect(user.session_activations.count).to eq 0 + expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound) end it 'revokes all access tokens' do @@ -430,6 +431,7 @@ RSpec.describe User do it 'removes push subscriptions' do expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0 + expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) end end From f32d672d2ffc33fc4b07a6e36f5d9dcbc3f26e4c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 21 Dec 2023 04:28:41 -0500 Subject: [PATCH 08/22] Clean up of `RSpec/LetSetup` within `spec/controllers` (#28446) --- .rubocop_todo.yml | 5 ---- .../follower_accounts_controller_spec.rb | 7 +++++ .../following_accounts_controller_spec.rb | 7 +++++ ...authorized_applications_controller_spec.rb | 4 +++ .../oauth/tokens_controller_spec.rb | 4 +++ .../settings/imports_controller_spec.rb | 26 ++++++++++--------- 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index bb2715f96..f834c5562 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -55,11 +55,6 @@ RSpec/LetSetup: - 'spec/controllers/auth/confirmations_controller_spec.rb' - 'spec/controllers/auth/passwords_controller_spec.rb' - 'spec/controllers/auth/sessions_controller_spec.rb' - - 'spec/controllers/follower_accounts_controller_spec.rb' - - 'spec/controllers/following_accounts_controller_spec.rb' - - 'spec/controllers/oauth/authorized_applications_controller_spec.rb' - - 'spec/controllers/oauth/tokens_controller_spec.rb' - - 'spec/controllers/settings/imports_controller_spec.rb' - 'spec/lib/activitypub/activity/delete_spec.rb' - 'spec/lib/vacuum/applications_vacuum_spec.rb' - 'spec/lib/vacuum/preview_cards_vacuum_spec.rb' diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb index cb8c2a0e5..dd78c96c0 100644 --- a/spec/controllers/follower_accounts_controller_spec.rb +++ b/spec/controllers/follower_accounts_controller_spec.rb @@ -48,6 +48,13 @@ describe FollowerAccountsController do it 'returns followers' do expect(response).to have_http_status(200) + expect(body_as_json) + .to include( + orderedItems: contain_exactly( + include(follow_from_bob.account.username), + include(follow_from_chris.account.username) + ) + ) expect(body['totalItems']).to eq 2 expect(body['partOf']).to be_present end diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb index 095528ed0..7bb78fb42 100644 --- a/spec/controllers/following_accounts_controller_spec.rb +++ b/spec/controllers/following_accounts_controller_spec.rb @@ -48,6 +48,13 @@ describe FollowingAccountsController do it 'returns followers' do expect(response).to have_http_status(200) + expect(body_as_json) + .to include( + orderedItems: contain_exactly( + include(follow_of_bob.target_account.username), + include(follow_of_chris.target_account.username) + ) + ) expect(body['totalItems']).to eq 2 expect(body['partOf']).to be_present end diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb index b54610604..b46b944d0 100644 --- a/spec/controllers/oauth/authorized_applications_controller_spec.rb +++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb @@ -63,5 +63,9 @@ describe Oauth::AuthorizedApplicationsController do it 'removes subscriptions for the application\'s access tokens' do expect(Web::PushSubscription.where(user: user).count).to eq 0 end + + it 'removes the web_push_subscription' do + expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) + end end end diff --git a/spec/controllers/oauth/tokens_controller_spec.rb b/spec/controllers/oauth/tokens_controller_spec.rb index 973393bcf..dd2d8ca70 100644 --- a/spec/controllers/oauth/tokens_controller_spec.rb +++ b/spec/controllers/oauth/tokens_controller_spec.rb @@ -20,5 +20,9 @@ RSpec.describe Oauth::TokensController do it 'removes web push subscription for token' do expect(Web::PushSubscription.where(access_token: access_token).count).to eq 0 end + + it 'removes the web_push_subscription' do + expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) + end end end diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb index 1e7b75893..89ec39e54 100644 --- a/spec/controllers/settings/imports_controller_spec.rb +++ b/spec/controllers/settings/imports_controller_spec.rb @@ -22,6 +22,7 @@ RSpec.describe Settings::ImportsController do it 'assigns the expected imports', :aggregate_failures do expect(response).to have_http_status(200) expect(assigns(:recent_imports)).to eq [import] + expect(assigns(:recent_imports)).to_not include(other_import) expect(response.headers['Cache-Control']).to include('private, no-store') end end @@ -138,6 +139,7 @@ RSpec.describe Settings::ImportsController do let(:bulk_import) { Fabricate(:bulk_import, account: user.account, type: import_type, state: :finished) } before do + rows.each { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) } bulk_import.update(total_items: bulk_import.rows.count, processed_items: bulk_import.rows.count, imported_items: 0) end @@ -152,11 +154,11 @@ RSpec.describe Settings::ImportsController do context 'with follows' do let(:import_type) { 'following' } - let!(:rows) do + let(:rows) do [ { 'acct' => 'foo@bar' }, { 'acct' => 'user@bar', 'show_reblogs' => false, 'notify' => true, 'languages' => %w(fr de) }, - ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) } + ] end include_examples 'export failed rows', "Account address,Show boosts,Notify on new posts,Languages\nfoo@bar,true,false,\nuser@bar,false,true,\"fr, de\"\n" @@ -165,11 +167,11 @@ RSpec.describe Settings::ImportsController do context 'with blocks' do let(:import_type) { 'blocking' } - let!(:rows) do + let(:rows) do [ { 'acct' => 'foo@bar' }, { 'acct' => 'user@bar' }, - ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) } + ] end include_examples 'export failed rows', "foo@bar\nuser@bar\n" @@ -178,11 +180,11 @@ RSpec.describe Settings::ImportsController do context 'with mutes' do let(:import_type) { 'muting' } - let!(:rows) do + let(:rows) do [ { 'acct' => 'foo@bar' }, { 'acct' => 'user@bar', 'hide_notifications' => false }, - ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) } + ] end include_examples 'export failed rows', "Account address,Hide notifications\nfoo@bar,true\nuser@bar,false\n" @@ -191,11 +193,11 @@ RSpec.describe Settings::ImportsController do context 'with domain blocks' do let(:import_type) { 'domain_blocking' } - let!(:rows) do + let(:rows) do [ { 'domain' => 'bad.domain' }, { 'domain' => 'evil.domain' }, - ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) } + ] end include_examples 'export failed rows', "bad.domain\nevil.domain\n" @@ -204,11 +206,11 @@ RSpec.describe Settings::ImportsController do context 'with bookmarks' do let(:import_type) { 'bookmarks' } - let!(:rows) do + let(:rows) do [ { 'uri' => 'https://foo.com/1' }, { 'uri' => 'https://foo.com/2' }, - ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) } + ] end include_examples 'export failed rows', "https://foo.com/1\nhttps://foo.com/2\n" @@ -217,11 +219,11 @@ RSpec.describe Settings::ImportsController do context 'with lists' do let(:import_type) { 'lists' } - let!(:rows) do + let(:rows) do [ { 'list_name' => 'Amigos', 'acct' => 'user@example.com' }, { 'list_name' => 'Frenemies', 'acct' => 'user@org.org' }, - ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) } + ] end include_examples 'export failed rows', "Amigos,user@example.com\nFrenemies,user@org.org\n" From 5976d3702ff067fb0ed972fbc5f01a0f46b01bd7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko <eugen@zeonfederated.com> Date: Thu, 21 Dec 2023 10:44:09 +0100 Subject: [PATCH 09/22] Change "Follow" to "Follow back" and "Mutual" when appropriate in web UI (#28452) --- .../features/account/components/header.jsx | 20 +++++++++++++++---- app/javascript/mastodon/locales/en.json | 3 ++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx index 29b46cb43..ab9526f66 100644 --- a/app/javascript/mastodon/features/account/components/header.jsx +++ b/app/javascript/mastodon/features/account/components/header.jsx @@ -35,6 +35,8 @@ import FollowRequestNoteContainer from '../containers/follow_request_note_contai const messages = defineMessages({ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, follow: { id: 'account.follow', defaultMessage: 'Follow' }, + followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' }, + mutual: { id: 'account.mutual', defaultMessage: 'Mutual' }, cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Withdraw follow request' }, requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, @@ -82,6 +84,18 @@ const titleFromAccount = account => { return `${prefix} (@${acct})`; }; +const messageForFollowButton = relationship => { + if (relationship.get('following') && relationship.get('followed_by')) { + return messages.mutual; + } else if (!relationship.get('following') && relationship.get('followed_by')) { + return messages.followBack; + } else if (relationship.get('following')) { + return messages.unfollow; + } else { + return messages.follow; + } +}; + const dateFormatOptions = { month: 'short', day: 'numeric', @@ -253,9 +267,7 @@ class Header extends ImmutablePureComponent { let info = []; let menu = []; - if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) { - info.push(<span key='followed_by' className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>); - } else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) { + if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) { info.push(<span key='blocked' className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>); } @@ -281,7 +293,7 @@ class Header extends ImmutablePureComponent { } else if (account.getIn(['relationship', 'requested'])) { actionBtn = <Button text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />; } else if (!account.getIn(['relationship', 'blocking'])) { - actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames({ 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={signedIn ? this.props.onFollow : this.props.onInteractionModal} />; + actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames({ 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(messageForFollowButton(account.get('relationship')))} onClick={signedIn ? this.props.onFollow : this.props.onInteractionModal} />; } else if (account.getIn(['relationship', 'blocking'])) { actionBtn = <Button text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />; } diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 05d7d1656..be7209d04 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "No posts", "account.featured_tags.title": "{name}'s featured hashtags", "account.follow": "Follow", + "account.follow_back": "Follow back", "account.followers": "Followers", "account.followers.empty": "No one follows this user yet.", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.following": "Following", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}", "account.follows.empty": "This user doesn't follow anyone yet.", - "account.follows_you": "Follows you", "account.go_to_profile": "Go to profile", "account.hide_reblogs": "Hide boosts from @{name}", "account.in_memoriam": "In Memoriam.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Mute notifications", "account.mute_short": "Mute", "account.muted": "Muted", + "account.mutual": "Mutual", "account.no_bio": "No description provided.", "account.open_original_page": "Open original page", "account.posts": "Posts", From cd64a5b2ec93416437850481b0ec8fc1ba43871f Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 21 Dec 2023 05:10:18 -0500 Subject: [PATCH 10/22] Clean up of `RSpec/LetSetup` within `api/` (#28448) --- .rubocop_todo.yml | 5 -- .../v1/accounts/statuses_controller_spec.rb | 3 +- .../api/v1/filters_controller_spec.rb | 5 ++ .../api/v2/admin/accounts_controller_spec.rb | 64 +++++++++++++------ .../v2/filters/keywords_controller_spec.rb | 4 ++ .../v2/filters/statuses_controller_spec.rb | 4 ++ 6 files changed, 60 insertions(+), 25 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f834c5562..8bee220d7 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -47,11 +47,6 @@ RSpec/ExampleLength: RSpec/LetSetup: Exclude: - - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb' - - 'spec/controllers/api/v1/filters_controller_spec.rb' - - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb' - - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb' - - 'spec/controllers/api/v2/filters/statuses_controller_spec.rb' - 'spec/controllers/auth/confirmations_controller_spec.rb' - 'spec/controllers/auth/passwords_controller_spec.rb' - 'spec/controllers/auth/sessions_controller_spec.rb' diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb index 0e4fa9301..df71e94ac 100644 --- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb @@ -10,11 +10,11 @@ describe Api::V1::Accounts::StatusesController do before do allow(controller).to receive(:doorkeeper_token) { token } - Fabricate(:status, account: user.account) end describe 'GET #index' do it 'returns expected headers', :aggregate_failures do + Fabricate(:status, account: user.account) get :index, params: { account_id: user.account.id, limit: 1 } expect(response).to have_http_status(200) @@ -30,7 +30,6 @@ describe Api::V1::Accounts::StatusesController do end context 'with exclude replies' do - let!(:older_statuses) { user.account.statuses.destroy_all } let!(:status) { Fabricate(:status, account: user.account) } let!(:status_self_reply) { Fabricate(:status, account: user.account, thread: status) } diff --git a/spec/controllers/api/v1/filters_controller_spec.rb b/spec/controllers/api/v1/filters_controller_spec.rb index 8d5408cf5..b0f64ccf4 100644 --- a/spec/controllers/api/v1/filters_controller_spec.rb +++ b/spec/controllers/api/v1/filters_controller_spec.rb @@ -15,10 +15,15 @@ RSpec.describe Api::V1::FiltersController do describe 'GET #index' do let(:scopes) { 'read:filters' } let!(:filter) { Fabricate(:custom_filter, account: user.account) } + let!(:custom_filter_keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) } it 'returns http success' do get :index expect(response).to have_http_status(200) + expect(body_as_json) + .to contain_exactly( + include(id: custom_filter_keyword.id.to_s) + ) end end diff --git a/spec/controllers/api/v2/admin/accounts_controller_spec.rb b/spec/controllers/api/v2/admin/accounts_controller_spec.rb index 18b395014..349f4d252 100644 --- a/spec/controllers/api/v2/admin/accounts_controller_spec.rb +++ b/spec/controllers/api/v2/admin/accounts_controller_spec.rb @@ -34,28 +34,56 @@ RSpec.describe Api::V2::Admin::AccountsController do it_behaves_like 'forbidden for wrong scope', 'write:statuses' it_behaves_like 'forbidden for wrong role', '' - [ - [{ status: 'active', origin: 'local', permissions: 'staff' }, [:admin_account]], - [{ by_domain: 'example.org', origin: 'remote' }, [:remote_account]], - [{ status: 'suspended' }, [:suspended_remote, :suspended_account]], - [{ status: 'disabled' }, [:disabled_account]], - [{ status: 'pending' }, [:pending_account]], - ].each do |params, expected_results| - context "when called with #{params.inspect}" do - let(:params) { params } + context 'when called with status active and origin local and permissions staff' do + let(:params) { { status: 'active', origin: 'local', permissions: 'staff' } } - it "returns the correct accounts (#{expected_results.inspect})" do - expect(response).to have_http_status(200) - - expect(body_json_ids).to eq(expected_results.map { |symbol| send(symbol).id }) - end - - def body_json_ids - body_as_json.map { |a| a[:id].to_i } - end + it 'returns the correct accounts' do + expect(response).to have_http_status(200) + expect(body_json_ids).to eq([admin_account.id]) end end + context 'when called with by_domain value and origin remote' do + let(:params) { { by_domain: 'example.org', origin: 'remote' } } + + it 'returns the correct accounts' do + expect(response).to have_http_status(200) + expect(body_json_ids).to include(remote_account.id) + expect(body_json_ids).to_not include(other_remote_account.id) + end + end + + context 'when called with status suspended' do + let(:params) { { status: 'suspended' } } + + it 'returns the correct accounts' do + expect(response).to have_http_status(200) + expect(body_json_ids).to include(suspended_remote.id, suspended_account.id) + end + end + + context 'when called with status disabled' do + let(:params) { { status: 'disabled' } } + + it 'returns the correct accounts' do + expect(response).to have_http_status(200) + expect(body_json_ids).to include(disabled_account.id) + end + end + + context 'when called with status pending' do + let(:params) { { status: 'pending' } } + + it 'returns the correct accounts' do + expect(response).to have_http_status(200) + expect(body_json_ids).to include(pending_account.id) + end + end + + def body_json_ids + body_as_json.map { |a| a[:id].to_i } + end + context 'with limit param' do let(:params) { { limit: 1 } } diff --git a/spec/controllers/api/v2/filters/keywords_controller_spec.rb b/spec/controllers/api/v2/filters/keywords_controller_spec.rb index 5321f787a..9f25237bc 100644 --- a/spec/controllers/api/v2/filters/keywords_controller_spec.rb +++ b/spec/controllers/api/v2/filters/keywords_controller_spec.rb @@ -22,6 +22,10 @@ RSpec.describe Api::V2::Filters::KeywordsController do it 'returns http success' do get :index, params: { filter_id: filter.id } expect(response).to have_http_status(200) + expect(body_as_json) + .to contain_exactly( + include(id: keyword.id.to_s) + ) end context "when trying to access another's user filters" do diff --git a/spec/controllers/api/v2/filters/statuses_controller_spec.rb b/spec/controllers/api/v2/filters/statuses_controller_spec.rb index 5c2a62395..d9df80397 100644 --- a/spec/controllers/api/v2/filters/statuses_controller_spec.rb +++ b/spec/controllers/api/v2/filters/statuses_controller_spec.rb @@ -22,6 +22,10 @@ RSpec.describe Api::V2::Filters::StatusesController do it 'returns http success' do get :index, params: { filter_id: filter.id } expect(response).to have_http_status(200) + expect(body_as_json) + .to contain_exactly( + include(id: status_filter.id.to_s) + ) end context "when trying to access another's user filters" do From c99f88e1a8ac1bbd4967e65c12717862a7dd00fd Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 21 Dec 2023 06:19:56 -0500 Subject: [PATCH 11/22] Clean up of `RSpec/LetSetup` within `spec/lib` (#28447) --- .rubocop_todo.yml | 3 --- spec/lib/activitypub/activity/delete_spec.rb | 4 ++++ spec/lib/vacuum/applications_vacuum_spec.rb | 8 ++++---- spec/lib/vacuum/preview_cards_vacuum_spec.rb | 4 ++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8bee220d7..645faa604 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -50,9 +50,6 @@ RSpec/LetSetup: - 'spec/controllers/auth/confirmations_controller_spec.rb' - 'spec/controllers/auth/passwords_controller_spec.rb' - 'spec/controllers/auth/sessions_controller_spec.rb' - - 'spec/lib/activitypub/activity/delete_spec.rb' - - 'spec/lib/vacuum/applications_vacuum_spec.rb' - - 'spec/lib/vacuum/preview_cards_vacuum_spec.rb' - 'spec/models/account_statuses_cleanup_policy_spec.rb' - 'spec/models/status_spec.rb' - 'spec/services/account_statuses_cleanup_service_spec.rb' diff --git a/spec/lib/activitypub/activity/delete_spec.rb b/spec/lib/activitypub/activity/delete_spec.rb index 3a73b3726..aec2c71e5 100644 --- a/spec/lib/activitypub/activity/delete_spec.rb +++ b/spec/lib/activitypub/activity/delete_spec.rb @@ -50,6 +50,10 @@ RSpec.describe ActivityPub::Activity::Delete do it 'sends delete activity to followers of rebloggers' do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once end + + it 'deletes the reblog' do + expect { reblog.reload }.to raise_error(ActiveRecord::RecordNotFound) + end end end diff --git a/spec/lib/vacuum/applications_vacuum_spec.rb b/spec/lib/vacuum/applications_vacuum_spec.rb index 57a222aaf..df5c86060 100644 --- a/spec/lib/vacuum/applications_vacuum_spec.rb +++ b/spec/lib/vacuum/applications_vacuum_spec.rb @@ -13,11 +13,11 @@ RSpec.describe Vacuum::ApplicationsVacuum do let!(:unused_app) { Fabricate(:application, created_at: 1.month.ago) } let!(:recent_app) { Fabricate(:application, created_at: 1.hour.ago) } - let!(:active_access_token) { Fabricate(:access_token, application: app_with_token) } - let!(:active_access_grant) { Fabricate(:access_grant, application: app_with_grant) } - let!(:user) { Fabricate(:user, created_by_application: app_with_signup) } - before do + Fabricate(:access_token, application: app_with_token) + Fabricate(:access_grant, application: app_with_grant) + Fabricate(:user, created_by_application: app_with_signup) + subject.perform end diff --git a/spec/lib/vacuum/preview_cards_vacuum_spec.rb b/spec/lib/vacuum/preview_cards_vacuum_spec.rb index c1b7f7e9c..9dbdf0bc2 100644 --- a/spec/lib/vacuum/preview_cards_vacuum_spec.rb +++ b/spec/lib/vacuum/preview_cards_vacuum_spec.rb @@ -30,5 +30,9 @@ RSpec.describe Vacuum::PreviewCardsVacuum do it 'does not delete attached preview cards' do expect(new_preview_card.reload).to be_persisted end + + it 'does not delete orphaned preview cards in the retention period' do + expect(orphaned_preview_card.reload).to be_persisted + end end end From efd16f3c2cf12c41bb39b6378007ef8f72e60652 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 21 Dec 2023 06:20:12 -0500 Subject: [PATCH 12/22] Clean up of `RSpec/LetSetup` within `spec/services/activitypub` (#28445) --- .rubocop_todo.yml | 4 ---- spec/services/account_statuses_cleanup_service_spec.rb | 7 +++++++ .../activitypub/fetch_remote_status_service_spec.rb | 1 - spec/services/activitypub/process_account_service_spec.rb | 2 +- .../activitypub/process_collection_service_spec.rb | 3 ++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 645faa604..6b6a4a89e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -52,11 +52,7 @@ RSpec/LetSetup: - 'spec/controllers/auth/sessions_controller_spec.rb' - 'spec/models/account_statuses_cleanup_policy_spec.rb' - 'spec/models/status_spec.rb' - - 'spec/services/account_statuses_cleanup_service_spec.rb' - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb' - - 'spec/services/activitypub/fetch_remote_status_service_spec.rb' - - 'spec/services/activitypub/process_account_service_spec.rb' - - 'spec/services/activitypub/process_collection_service_spec.rb' - 'spec/services/batched_remove_status_service_spec.rb' - 'spec/services/block_domain_service_spec.rb' - 'spec/services/bulk_import_service_spec.rb' diff --git a/spec/services/account_statuses_cleanup_service_spec.rb b/spec/services/account_statuses_cleanup_service_spec.rb index f7a88a917..0ac113f10 100644 --- a/spec/services/account_statuses_cleanup_service_spec.rb +++ b/spec/services/account_statuses_cleanup_service_spec.rb @@ -39,6 +39,13 @@ describe AccountStatusesCleanupService, type: :service do it 'actually deletes the statuses' do subject.call(account_policy, 10) expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id])).to be_nil + expect { recent_status.reload }.to_not raise_error + end + + it 'preserves recent and unrelated statuses' do + subject.call(account_policy, 10) + expect { unrelated_status.reload }.to_not raise_error + expect { recent_status.reload }.to_not raise_error end end diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index 826b67d88..3c64feaee 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -8,7 +8,6 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do subject { described_class.new } let!(:sender) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar') } - let!(:recipient) { Fabricate(:account) } let(:existing_status) { nil } diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 09eb5ddee..58dd2badb 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -33,7 +33,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do end context 'when account is not suspended' do - subject { described_class.new.call('alice', 'example.com', payload) } + subject { described_class.new.call(account.username, account.domain, payload) } let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') } diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb index df526daf3..f4a2b8fec 100644 --- a/spec/services/activitypub/process_collection_service_spec.rb +++ b/spec/services/activitypub/process_collection_service_spec.rb @@ -242,7 +242,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do it 'does not process forged payload' do allow(ActivityPub::Activity).to receive(:factory) - subject.call(json, forwarder) + expect { subject.call(json, forwarder) } + .to_not change(actor.reload.statuses, :count) expect(ActivityPub::Activity).to_not have_received(:factory).with( hash_including( From 9251779d755c62dca9326df82687c4b62e2abb8a Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 21 Dec 2023 09:23:53 -0500 Subject: [PATCH 13/22] Fix `RSpec/LetSetup` cop in spec/services (#28459) --- .rubocop_todo.yml | 13 ---- .../batched_remove_status_service_spec.rb | 2 +- spec/services/block_domain_service_spec.rb | 16 ++-- spec/services/bulk_import_service_spec.rb | 10 ++- spec/services/delete_account_service_spec.rb | 73 +++++++++---------- spec/services/import_service_spec.rb | 2 +- spec/services/notify_service_spec.rb | 9 ++- spec/services/remove_status_service_spec.rb | 14 ++-- spec/services/report_service_spec.rb | 3 +- spec/services/resolve_account_service_spec.rb | 3 +- spec/services/suspend_account_service_spec.rb | 2 +- spec/services/unallow_domain_service_spec.rb | 1 + .../unsuspend_account_service_spec.rb | 2 +- .../scheduler/user_cleanup_scheduler_spec.rb | 11 ++- 14 files changed, 74 insertions(+), 87 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6b6a4a89e..afd530530 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -53,19 +53,6 @@ RSpec/LetSetup: - 'spec/models/account_statuses_cleanup_policy_spec.rb' - 'spec/models/status_spec.rb' - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb' - - 'spec/services/batched_remove_status_service_spec.rb' - - 'spec/services/block_domain_service_spec.rb' - - 'spec/services/bulk_import_service_spec.rb' - - 'spec/services/delete_account_service_spec.rb' - - 'spec/services/import_service_spec.rb' - - 'spec/services/notify_service_spec.rb' - - 'spec/services/remove_status_service_spec.rb' - - 'spec/services/report_service_spec.rb' - - 'spec/services/resolve_account_service_spec.rb' - - 'spec/services/suspend_account_service_spec.rb' - - 'spec/services/unallow_domain_service_spec.rb' - - 'spec/services/unsuspend_account_service_spec.rb' - - 'spec/workers/scheduler/user_cleanup_scheduler_spec.rb' RSpec/MultipleExpectations: Max: 8 diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb index 8201c9d51..991a852f0 100644 --- a/spec/services/batched_remove_status_service_spec.rb +++ b/spec/services/batched_remove_status_service_spec.rb @@ -10,7 +10,7 @@ RSpec.describe BatchedRemoveStatusService, type: :service do let!(:jeff) { Fabricate(:account) } let!(:hank) { Fabricate(:account, username: 'hank', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } - let(:status_alice_hello) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com') } + let(:status_alice_hello) { PostStatusService.new.call(alice, text: "Hello @#{bob.pretty_acct}") } let(:status_alice_other) { PostStatusService.new.call(alice, text: 'Another status') } before do diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb index 36dce9d19..fc3a1397d 100644 --- a/spec/services/block_domain_service_spec.rb +++ b/spec/services/block_domain_service_spec.rb @@ -21,19 +21,19 @@ RSpec.describe BlockDomainService, type: :service do end it 'removes remote accounts from that domain' do - expect(Account.find_remote('badguy666', 'evil.org').suspended?).to be true + expect(bad_account.reload.suspended?).to be true end it 'records suspension date appropriately' do - expect(Account.find_remote('badguy666', 'evil.org').suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at + expect(bad_account.reload.suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at end it 'keeps already-banned accounts banned' do - expect(Account.find_remote('badguy', 'evil.org').suspended?).to be true + expect(already_banned_account.reload.suspended?).to be true end it 'does not overwrite suspension date of already-banned accounts' do - expect(Account.find_remote('badguy', 'evil.org').suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at + expect(already_banned_account.reload.suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at end it 'removes the remote accounts\'s statuses and media attachments' do @@ -53,19 +53,19 @@ RSpec.describe BlockDomainService, type: :service do end it 'silences remote accounts from that domain' do - expect(Account.find_remote('badguy666', 'evil.org').silenced?).to be true + expect(bad_account.reload.silenced?).to be true end it 'records suspension date appropriately' do - expect(Account.find_remote('badguy666', 'evil.org').silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at + expect(bad_account.reload.silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at end it 'keeps already-banned accounts banned' do - expect(Account.find_remote('badguy', 'evil.org').silenced?).to be true + expect(already_banned_account.reload.silenced?).to be true end it 'does not overwrite suspension date of already-banned accounts' do - expect(Account.find_remote('badguy', 'evil.org').silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at + expect(already_banned_account.reload.silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at end it 'leaves the domains status and attachments, but clears media' do diff --git a/spec/services/bulk_import_service_spec.rb b/spec/services/bulk_import_service_spec.rb index 3a3f95ccc..f703650c3 100644 --- a/spec/services/bulk_import_service_spec.rb +++ b/spec/services/bulk_import_service_spec.rb @@ -271,14 +271,15 @@ RSpec.describe BulkImportService do let(:import_type) { 'domain_blocking' } let(:overwrite) { false } - let!(:rows) do + let(:rows) do [ { 'domain' => 'blocked.com' }, { 'domain' => 'to_block.com' }, - ].map { |data| import.rows.create!(data: data) } + ] end before do + rows.each { |data| import.rows.create!(data: data) } account.block_domain!('alreadyblocked.com') account.block_domain!('blocked.com') end @@ -298,14 +299,15 @@ RSpec.describe BulkImportService do let(:import_type) { 'domain_blocking' } let(:overwrite) { true } - let!(:rows) do + let(:rows) do [ { 'domain' => 'blocked.com' }, { 'domain' => 'to_block.com' }, - ].map { |data| import.rows.create!(data: data) } + ] end before do + rows.each { |data| import.rows.create!(data: data) } account.block_domain!('alreadyblocked.com') account.block_domain!('blocked.com') end diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb index a2c57f1c1..f04ba9cf1 100644 --- a/spec/services/delete_account_service_spec.rb +++ b/spec/services/delete_account_service_spec.rb @@ -28,74 +28,69 @@ RSpec.describe DeleteAccountService, type: :service do let!(:account_note) { Fabricate(:account_note, account: account) } it 'deletes associated owned and target records and target notifications' do - expect { subject } - .to delete_associated_owned_records - .and delete_associated_target_records - .and delete_associated_target_notifications + subject + + expect_deletion_of_associated_owned_records + expect_deletion_of_associated_target_records + expect_deletion_of_associated_target_notifications end - def delete_associated_owned_records - change do - [ - account.statuses, - account.media_attachments, - account.notifications, - account.favourites, - account.active_relationships, - account.passive_relationships, - account.polls, - account.account_notes, - ].map(&:count) - end.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) + def expect_deletion_of_associated_owned_records + expect { status.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { status_with_mention.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { mention.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { media_attachment.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { favourite.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { active_relationship.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { passive_relationship.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { poll.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { poll_vote.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { account_note.reload }.to raise_error(ActiveRecord::RecordNotFound) end - def delete_associated_target_records - change(account_pins_for_account, :count).from(1).to(0) + def expect_deletion_of_associated_target_records + expect { endorsement.reload }.to raise_error(ActiveRecord::RecordNotFound) end - def account_pins_for_account - AccountPin.where(target_account: account) - end - - def delete_associated_target_notifications - change do - %w( - poll favourite status mention follow - ).map { |type| Notification.where(type: type).count } - end.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0]) + def expect_deletion_of_associated_target_notifications + expect { favourite_notification.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { follow_notification.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { mention_notification.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { poll_notification.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { status_notification.reload }.to raise_error(ActiveRecord::RecordNotFound) end end describe '#call on local account' do before do - stub_request(:post, 'https://alice.com/inbox').to_return(status: 201) - stub_request(:post, 'https://bob.com/inbox').to_return(status: 201) + stub_request(:post, remote_alice.inbox_url).to_return(status: 201) + stub_request(:post, remote_bob.inbox_url).to_return(status: 201) end let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', domain: 'alice.com', protocol: :activitypub) } let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', domain: 'bob.com', protocol: :activitypub) } include_examples 'common behavior' do - let!(:account) { Fabricate(:account) } - let!(:local_follower) { Fabricate(:account) } + let(:account) { Fabricate(:account) } + let(:local_follower) { Fabricate(:account) } it 'sends a delete actor activity to all known inboxes' do subject - expect(a_request(:post, 'https://alice.com/inbox')).to have_been_made.once - expect(a_request(:post, 'https://bob.com/inbox')).to have_been_made.once + expect(a_request(:post, remote_alice.inbox_url)).to have_been_made.once + expect(a_request(:post, remote_bob.inbox_url)).to have_been_made.once end end end describe '#call on remote account' do before do - stub_request(:post, 'https://alice.com/inbox').to_return(status: 201) - stub_request(:post, 'https://bob.com/inbox').to_return(status: 201) + stub_request(:post, account.inbox_url).to_return(status: 201) end include_examples 'common behavior' do - let!(:account) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') } - let!(:local_follower) { Fabricate(:account) } + let(:account) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') } + let(:local_follower) { Fabricate(:account) } it 'sends expected activities to followed and follower inboxes' do subject diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb index 3936b2363..d67da66ca 100644 --- a/spec/services/import_service_spec.rb +++ b/spec/services/import_service_spec.rb @@ -190,7 +190,7 @@ RSpec.describe ImportService, type: :service do # Make sure to not actually go to the remote server before do - stub_request(:post, 'https://թութ.հայ/inbox').to_return(status: 200) + stub_request(:post, nare.inbox_url).to_return(status: 200) end it 'follows the listed account' do diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index 8fcb58658..50055dafc 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -67,9 +67,10 @@ RSpec.describe NotifyService, type: :service do context 'when the message chain is initiated by recipient, but is not direct message' do let(:reply_to) { Fabricate(:status, account: recipient) } - let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } + before { Fabricate(:mention, account: sender, status: reply_to) } + it 'does not notify' do expect { subject }.to_not change(Notification, :count) end @@ -77,10 +78,11 @@ RSpec.describe NotifyService, type: :service do context 'when the message chain is initiated by recipient, but without a mention to the sender, even if the sender sends multiple messages in a row' do let(:reply_to) { Fabricate(:status, account: recipient) } - let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:dummy_reply) { Fabricate(:status, account: sender, visibility: :direct, thread: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: dummy_reply)) } + before { Fabricate(:mention, account: sender, status: reply_to) } + it 'does not notify' do expect { subject }.to_not change(Notification, :count) end @@ -88,9 +90,10 @@ RSpec.describe NotifyService, type: :service do context 'when the message chain is initiated by the recipient with a mention to the sender' do let(:reply_to) { Fabricate(:status, account: recipient, visibility: :direct) } - let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } + before { Fabricate(:mention, account: sender, status: reply_to) } + it 'does notify' do expect { subject }.to change(Notification, :count) end diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb index 7754ae800..08ce0b045 100644 --- a/spec/services/remove_status_service_spec.rb +++ b/spec/services/remove_status_service_spec.rb @@ -12,15 +12,15 @@ RSpec.describe RemoveStatusService, type: :service do let!(:bill) { Fabricate(:account, username: 'bill', protocol: :activitypub, domain: 'example2.com', inbox_url: 'http://example2.com/inbox') } before do - stub_request(:post, 'http://example.com/inbox').to_return(status: 200) - stub_request(:post, 'http://example2.com/inbox').to_return(status: 200) + stub_request(:post, hank.inbox_url).to_return(status: 200) + stub_request(:post, bill.inbox_url).to_return(status: 200) jeff.follow!(alice) hank.follow!(alice) end context 'when removed status is not a reblog' do - let!(:status) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com ThisIsASecret') } + let!(:status) { PostStatusService.new.call(alice, text: "Hello @#{bob.pretty_acct} ThisIsASecret") } before do FavouriteService.new.call(jeff, status) @@ -39,7 +39,7 @@ RSpec.describe RemoveStatusService, type: :service do it 'sends Delete activity to followers' do subject.call(status) - expect(a_request(:post, 'http://example.com/inbox').with( + expect(a_request(:post, hank.inbox_url).with( body: hash_including({ 'type' => 'Delete', 'object' => { @@ -53,7 +53,7 @@ RSpec.describe RemoveStatusService, type: :service do it 'sends Delete activity to rebloggers' do subject.call(status) - expect(a_request(:post, 'http://example2.com/inbox').with( + expect(a_request(:post, bill.inbox_url).with( body: hash_including({ 'type' => 'Delete', 'object' => { @@ -78,7 +78,7 @@ RSpec.describe RemoveStatusService, type: :service do it 'sends Undo activity to followers' do subject.call(status) - expect(a_request(:post, 'http://example.com/inbox').with( + expect(a_request(:post, hank.inbox_url).with( body: hash_including({ 'type' => 'Undo', 'object' => hash_including({ @@ -96,7 +96,7 @@ RSpec.describe RemoveStatusService, type: :service do it 'sends Undo activity to followers' do subject.call(status) - expect(a_request(:post, 'http://example.com/inbox').with( + expect(a_request(:post, hank.inbox_url).with( body: hash_including({ 'type' => 'Undo', 'object' => hash_including({ diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb index d3bcd5d31..2a919f640 100644 --- a/spec/services/report_service_spec.rb +++ b/spec/services/report_service_spec.rb @@ -156,9 +156,8 @@ RSpec.describe ReportService, type: :service do -> { described_class.new.call(source_account, target_account) } end - let!(:other_report) { Fabricate(:report, target_account: target_account) } - before do + Fabricate(:report, target_account: target_account) ActionMailer::Base.deliveries.clear source_account.user.settings['notification_emails.report'] = true source_account.user.save diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb index 9f5fdca29..3c4b182e2 100644 --- a/spec/services/resolve_account_service_spec.rb +++ b/spec/services/resolve_account_service_spec.rb @@ -20,7 +20,7 @@ RSpec.describe ResolveAccountService, type: :service do let!(:remote_account) { Fabricate(:account, username: 'foo', domain: 'ap.example.com', protocol: 'activitypub') } context 'when domain is banned' do - let!(:domain_block) { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) } + before { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) } it 'does not return an account' do expect(subject.call('foo@ap.example.com', skip_webfinger: true)).to be_nil @@ -214,6 +214,7 @@ RSpec.describe ResolveAccountService, type: :service do expect(account.domain).to eq 'ap.example.com' expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' expect(account.uri).to eq 'https://ap.example.com/users/foo' + expect(status.reload.account).to eq(account) end end diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index c258995b7..b309ce511 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -46,9 +46,9 @@ RSpec.describe SuspendAccountService, type: :service do let!(:account) { Fabricate(:account) } let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') } let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') } - let!(:report) { Fabricate(:report, account: remote_reporter, target_account: account) } before do + Fabricate(:report, account: remote_reporter, target_account: account) remote_follower.follow!(account) end diff --git a/spec/services/unallow_domain_service_spec.rb b/spec/services/unallow_domain_service_spec.rb index 19d40e7e8..d96e35418 100644 --- a/spec/services/unallow_domain_service_spec.rb +++ b/spec/services/unallow_domain_service_spec.rb @@ -27,6 +27,7 @@ RSpec.describe UnallowDomainService, type: :service do end it 'removes remote accounts from that domain' do + expect { already_banned_account.reload }.to raise_error(ActiveRecord::RecordNotFound) expect(Account.where(domain: 'evil.org').exists?).to be false end diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb index 2f737c621..211c39e6c 100644 --- a/spec/services/unsuspend_account_service_spec.rb +++ b/spec/services/unsuspend_account_service_spec.rb @@ -39,9 +39,9 @@ RSpec.describe UnsuspendAccountService, type: :service do let!(:account) { Fabricate(:account) } let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') } let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') } - let!(:report) { Fabricate(:report, account: remote_reporter, target_account: account) } before do + Fabricate(:report, account: remote_reporter, target_account: account) remote_follower.follow!(account) end diff --git a/spec/workers/scheduler/user_cleanup_scheduler_spec.rb b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb index 990979500..8fda246ba 100644 --- a/spec/workers/scheduler/user_cleanup_scheduler_spec.rb +++ b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb @@ -18,12 +18,11 @@ describe Scheduler::UserCleanupScheduler do confirmed_user.update!(confirmed_at: 1.day.ago) end - it 'deletes the old unconfirmed user' do - expect { subject.perform }.to change { User.exists?(old_unconfirmed_user.id) }.from(true).to(false) - end - - it "deletes the old unconfirmed user's account" do - expect { subject.perform }.to change { Account.exists?(old_unconfirmed_user.account_id) }.from(true).to(false) + it 'deletes the old unconfirmed user, their account, and the moderation note' do + expect { subject.perform } + .to change { User.exists?(old_unconfirmed_user.id) }.from(true).to(false) + .and change { Account.exists?(old_unconfirmed_user.account_id) }.from(true).to(false) + expect { moderation_note.reload }.to raise_error(ActiveRecord::RecordNotFound) end it 'does not delete the new unconfirmed user or their account' do From 1c041356a1c264347a787b0370163df7753ef3da Mon Sep 17 00:00:00 2001 From: Renaud Chaput <renchap@gmail.com> Date: Thu, 21 Dec 2023 21:19:18 +0100 Subject: [PATCH 14/22] Fix error on viewing a profile when unlogged (#28465) --- app/javascript/mastodon/features/account/components/header.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx index ab9526f66..97f68c221 100644 --- a/app/javascript/mastodon/features/account/components/header.jsx +++ b/app/javascript/mastodon/features/account/components/header.jsx @@ -85,6 +85,8 @@ const titleFromAccount = account => { }; const messageForFollowButton = relationship => { + if(!relationship) return messages.follow; + if (relationship.get('following') && relationship.get('followed_by')) { return messages.mutual; } else if (!relationship.get('following') && relationship.get('followed_by')) { From 43547cd21000101c0fc1486cdd431ce164741de7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 08:41:21 +0100 Subject: [PATCH 15/22] Update dependency debug to v1.9.1 (#28466) 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 cc262f8e1..9b78ca733 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -220,7 +220,7 @@ GEM database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) date (3.3.4) - debug (1.9.0) + debug (1.9.1) irb (~> 1.10) reline (>= 0.3.8) debug_inspector (1.1.0) From e70a65761ad3bc0931ab9c30d541895bc70df9ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 08:58:20 +0100 Subject: [PATCH 16/22] New Crowdin Translations (automated) (#28467) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/af.json | 1 - app/javascript/mastodon/locales/an.json | 1 - app/javascript/mastodon/locales/ar.json | 1 - app/javascript/mastodon/locales/ast.json | 1 - app/javascript/mastodon/locales/be.json | 3 +- app/javascript/mastodon/locales/bg.json | 1 - app/javascript/mastodon/locales/bn.json | 1 - app/javascript/mastodon/locales/br.json | 1 - app/javascript/mastodon/locales/ca.json | 3 +- app/javascript/mastodon/locales/ckb.json | 1 - app/javascript/mastodon/locales/co.json | 1 - app/javascript/mastodon/locales/cs.json | 1 - app/javascript/mastodon/locales/cy.json | 1 - app/javascript/mastodon/locales/da.json | 3 +- app/javascript/mastodon/locales/de.json | 3 +- app/javascript/mastodon/locales/el.json | 1 - app/javascript/mastodon/locales/en-GB.json | 1 - app/javascript/mastodon/locales/eo.json | 1 - app/javascript/mastodon/locales/es-AR.json | 3 +- app/javascript/mastodon/locales/es-MX.json | 3 +- app/javascript/mastodon/locales/es.json | 3 +- app/javascript/mastodon/locales/et.json | 1 - app/javascript/mastodon/locales/eu.json | 3 +- app/javascript/mastodon/locales/fa.json | 1 - app/javascript/mastodon/locales/fi.json | 3 +- app/javascript/mastodon/locales/fil.json | 1 - app/javascript/mastodon/locales/fo.json | 3 +- app/javascript/mastodon/locales/fr-QC.json | 3 +- app/javascript/mastodon/locales/fr.json | 3 +- app/javascript/mastodon/locales/fy.json | 3 +- app/javascript/mastodon/locales/ga.json | 1 - app/javascript/mastodon/locales/gd.json | 1 - app/javascript/mastodon/locales/gl.json | 1 - app/javascript/mastodon/locales/he.json | 3 +- app/javascript/mastodon/locales/hi.json | 1 - app/javascript/mastodon/locales/hr.json | 1 - app/javascript/mastodon/locales/hu.json | 3 +- app/javascript/mastodon/locales/hy.json | 1 - app/javascript/mastodon/locales/id.json | 1 - app/javascript/mastodon/locales/ie.json | 1 - app/javascript/mastodon/locales/ig.json | 1 - app/javascript/mastodon/locales/io.json | 1 - app/javascript/mastodon/locales/is.json | 1 - app/javascript/mastodon/locales/it.json | 3 +- app/javascript/mastodon/locales/ja.json | 1 - app/javascript/mastodon/locales/ka.json | 1 - app/javascript/mastodon/locales/kab.json | 1 - app/javascript/mastodon/locales/kk.json | 1 - app/javascript/mastodon/locales/ko.json | 3 +- app/javascript/mastodon/locales/ku.json | 1 - app/javascript/mastodon/locales/kw.json | 1 - app/javascript/mastodon/locales/lad.json | 6 +- app/javascript/mastodon/locales/lt.json | 3 +- app/javascript/mastodon/locales/lv.json | 1 - app/javascript/mastodon/locales/mk.json | 1 - app/javascript/mastodon/locales/ml.json | 1 - app/javascript/mastodon/locales/mr.json | 1 - app/javascript/mastodon/locales/ms.json | 1 - app/javascript/mastodon/locales/my.json | 1 - app/javascript/mastodon/locales/nl.json | 3 +- app/javascript/mastodon/locales/nn.json | 3 +- app/javascript/mastodon/locales/no.json | 1 - app/javascript/mastodon/locales/oc.json | 1 - app/javascript/mastodon/locales/pa.json | 1 - app/javascript/mastodon/locales/pl.json | 3 +- app/javascript/mastodon/locales/pt-BR.json | 1 - app/javascript/mastodon/locales/pt-PT.json | 3 +- app/javascript/mastodon/locales/ro.json | 1 - app/javascript/mastodon/locales/ru.json | 1 - app/javascript/mastodon/locales/sa.json | 1 - app/javascript/mastodon/locales/sc.json | 1 - app/javascript/mastodon/locales/sco.json | 1 - app/javascript/mastodon/locales/si.json | 1 - app/javascript/mastodon/locales/sk.json | 1 - app/javascript/mastodon/locales/sl.json | 3 +- app/javascript/mastodon/locales/sq.json | 1 - app/javascript/mastodon/locales/sr-Latn.json | 1 - app/javascript/mastodon/locales/sr.json | 2 +- app/javascript/mastodon/locales/sv.json | 3 +- app/javascript/mastodon/locales/ta.json | 1 - app/javascript/mastodon/locales/te.json | 1 - app/javascript/mastodon/locales/th.json | 2 +- app/javascript/mastodon/locales/tr.json | 3 +- app/javascript/mastodon/locales/tt.json | 1 - app/javascript/mastodon/locales/uk.json | 2 +- app/javascript/mastodon/locales/ur.json | 1 - app/javascript/mastodon/locales/uz.json | 1 - app/javascript/mastodon/locales/vi.json | 1 - app/javascript/mastodon/locales/zgh.json | 1 - app/javascript/mastodon/locales/zh-CN.json | 3 +- app/javascript/mastodon/locales/zh-HK.json | 3 +- app/javascript/mastodon/locales/zh-TW.json | 3 +- config/locales/ie.yml | 170 +++++++++++++++++++ config/locales/lad.yml | 76 +++++++++ config/locales/simple_form.ie.yml | 19 +++ config/locales/simple_form.it.yml | 4 +- config/locales/simple_form.lad.yml | 25 +++ 97 files changed, 356 insertions(+), 94 deletions(-) diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json index d3cc40c60..6c37cdf5c 100644 --- a/app/javascript/mastodon/locales/af.json +++ b/app/javascript/mastodon/locales/af.json @@ -30,7 +30,6 @@ "account.followers.empty": "Hierdie gebruiker het nog nie volgers nie.", "account.following": "Volg", "account.follows.empty": "Die gebruiker volg nog niemand.", - "account.follows_you": "Volg jou", "account.go_to_profile": "Gaan na profiel", "account.hide_reblogs": "Versteek plasings wat deur @{name} aangestuur is", "account.joined_short": "Aangesluit", diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json index b2134551b..23899b100 100644 --- a/app/javascript/mastodon/locales/an.json +++ b/app/javascript/mastodon/locales/an.json @@ -35,7 +35,6 @@ "account.following": "Seguindo", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Seguindo}}", "account.follows.empty": "Este usuario encara no sigue a dengún.", - "account.follows_you": "Te sigue", "account.go_to_profile": "Ir ta lo perfil", "account.hide_reblogs": "Amagar retutz de @{name}", "account.joined_short": "S'unió", diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 979bc9a70..c0d07cc64 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -37,7 +37,6 @@ "account.following": "الاشتراكات", "account.following_counter": "{count, plural, zero{لا يُتابِع أحدًا} one {يُتابِعُ واحد} two{يُتابِعُ اِثنان} few{يُتابِعُ {counter}} many{يُتابِعُ {counter}} other {يُتابِعُ {counter}}}", "account.follows.empty": "لا يُتابع هذا المُستخدمُ أيَّ أحدٍ حتى الآن.", - "account.follows_you": "يُتابِعُك", "account.go_to_profile": "اذهب إلى الملف الشخصي", "account.hide_reblogs": "إخفاء المعاد نشرها مِن @{name}", "account.in_memoriam": "في الذكرى.", diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json index 98f622c29..c4238edcd 100644 --- a/app/javascript/mastodon/locales/ast.json +++ b/app/javascript/mastodon/locales/ast.json @@ -38,7 +38,6 @@ "account.following": "Siguiendo", "account.following_counter": "{count, plural,one {Sigue a {counter}} other {Sigue a {counter}}}", "account.follows.empty": "Esti perfil nun sigue a naide.", - "account.follows_you": "Síguete", "account.go_to_profile": "Dir al perfil", "account.hide_reblogs": "Anubrir los artículos compartíos de @{name}", "account.in_memoriam": "N'alcordanza.", diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index f94a8b79f..773af4034 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Няма допісаў", "account.featured_tags.title": "Тэгі, выбраныя {name}", "account.follow": "Падпісацца", + "account.follow_back": "Падпісацца ў адказ", "account.followers": "Падпісчыкі", "account.followers.empty": "Ніхто пакуль не падпісаны на гэтага карыстальніка.", "account.followers_counter": "{count, plural, one {{counter} падпісчык} few {{counter} падпісчыкі} many {{counter} падпісчыкаў} other {{counter} падпісчыка}}", "account.following": "Падпіскі", "account.following_counter": "{count, plural, one {{counter} падпіска} few {{counter} падпіскі} many {{counter} падпісак} other {{counter} падпіскі}}", "account.follows.empty": "Карыстальнік ні на каго не падпісаны.", - "account.follows_you": "Падпісаны на вас", "account.go_to_profile": "Перайсці да профілю", "account.hide_reblogs": "Схаваць пашырэнні ад @{name}", "account.in_memoriam": "У памяць.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Не апавяшчаць", "account.mute_short": "Ігнараваць", "account.muted": "Ігнаруецца", + "account.mutual": "Узаемныя", "account.no_bio": "Апісанне адсутнічае.", "account.open_original_page": "Адкрыць арыгінальную старонку", "account.posts": "Допісы", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index f57d868c7..8a6d6040d 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -38,7 +38,6 @@ "account.following": "Последвано", "account.following_counter": "{count, plural, one {{counter} последван} other {{counter} последвани}}", "account.follows.empty": "Потребителят още никого не следва.", - "account.follows_you": "Следва ви", "account.go_to_profile": "Към профила", "account.hide_reblogs": "Скриване на подсилвания от @{name}", "account.in_memoriam": "В памет на.", diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json index b6e4fbb96..fe3d2a627 100644 --- a/app/javascript/mastodon/locales/bn.json +++ b/app/javascript/mastodon/locales/bn.json @@ -37,7 +37,6 @@ "account.following": "অনুসরণ করা হচ্ছে", "account.following_counter": "{count, plural,one {{counter} জনকে অনুসরণ} other {{counter} জনকে অনুসরণ}}", "account.follows.empty": "এই সদস্য কাউকে এখনো ফলো করেন না.", - "account.follows_you": "আপনাকে ফলো করে", "account.go_to_profile": "প্রোফাইলে যান", "account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন", "account.in_memoriam": "স্মৃতিতে.", diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json index 39cd73241..bea8b27b7 100644 --- a/app/javascript/mastodon/locales/br.json +++ b/app/javascript/mastodon/locales/br.json @@ -36,7 +36,6 @@ "account.following": "Koumanantoù", "account.following_counter": "{count, plural, one{{counter} C'houmanant} two{{counter} Goumanant} other {{counter} a Goumanant}}", "account.follows.empty": "An implijer·ez-mañ na heul den ebet.", - "account.follows_you": "Ho heuilh", "account.go_to_profile": "Gwelet ar profil", "account.hide_reblogs": "Kuzh skignadennoù gant @{name}", "account.joined_short": "Amañ abaoe", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 5ae49325f..86f1fb476 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "No hi ha tuts", "account.featured_tags.title": "etiquetes destacades de {name}", "account.follow": "Segueix", + "account.follow_back": "Segueix", "account.followers": "Seguidors", "account.followers.empty": "A aquest usuari encara no el segueix ningú.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} Seguidors}}", "account.following": "Seguint", "account.following_counter": "{count, plural, other {{counter} Seguint-ne}}", "account.follows.empty": "Aquest usuari encara no segueix ningú.", - "account.follows_you": "Et segueix", "account.go_to_profile": "Vés al perfil", "account.hide_reblogs": "Amaga els impulsos de @{name}", "account.in_memoriam": "En Memòria.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Silencia les notificacions", "account.mute_short": "Silencia", "account.muted": "Silenciat", + "account.mutual": "Mutu", "account.no_bio": "No s'ha proporcionat cap descripció.", "account.open_original_page": "Obre la pàgina original", "account.posts": "Tuts", diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json index 7e9641832..f1cafd1ac 100644 --- a/app/javascript/mastodon/locales/ckb.json +++ b/app/javascript/mastodon/locales/ckb.json @@ -36,7 +36,6 @@ "account.following": "بەدوادا", "account.following_counter": "{count, plural, one {{counter} شوێنکەوتوو} other {{counter} شوێنکەوتوو}}", "account.follows.empty": "ئەم بەکارهێنەرە تا ئێستا شوێن کەس نەکەوتووە.", - "account.follows_you": "شوێنت دەکەوێت", "account.go_to_profile": "بڕۆ بۆ پڕۆفایلی", "account.hide_reblogs": "داشاردنی بووستەکان لە @{name}", "account.joined_short": "بەشداری کردووە", diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json index d4bb2f82b..9f90c3d21 100644 --- a/app/javascript/mastodon/locales/co.json +++ b/app/javascript/mastodon/locales/co.json @@ -19,7 +19,6 @@ "account.followers_counter": "{count, plural, one {{counter} Abbunatu} other {{counter} Abbunati}}", "account.following_counter": "{count, plural, one {{counter} Abbunamentu} other {{counter} Abbunamenti}}", "account.follows.empty": "St'utilizatore ùn seguita nisunu.", - "account.follows_you": "Vi seguita", "account.hide_reblogs": "Piattà spartere da @{name}", "account.link_verified_on": "A prupietà di stu ligame hè stata verificata u {date}", "account.locked_info": "U statutu di vita privata di u contu hè chjosu. U pruprietariu esamina manualmente e dumande d'abbunamentu.", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 16bf5020c..e18cabcec 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -38,7 +38,6 @@ "account.following": "Sledujete", "account.following_counter": "{count, plural, one {{counter} Sledovaný} few {{counter} Sledovaní} many {{counter} Sledovaných} other {{counter} Sledovaných}}", "account.follows.empty": "Tento uživatel zatím nikoho nesleduje.", - "account.follows_you": "Sleduje vás", "account.go_to_profile": "Přejít na profil", "account.hide_reblogs": "Skrýt boosty od @{name}", "account.in_memoriam": "In Memoriam.", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 4ecf48735..17133631a 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -37,7 +37,6 @@ "account.following": "Yn dilyn", "account.following_counter": "{count, plural, one {Yn dilyn: {counter}} other {Yn dilyn: {counter}}}", "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.", - "account.follows_you": "Yn eich dilyn chi", "account.go_to_profile": "Mynd i'r proffil", "account.hide_reblogs": "Cuddio hybiau gan @{name}", "account.in_memoriam": "Er Cof.", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 04fc43734..3bc830ad2 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Ingen indlæg", "account.featured_tags.title": "{name}s fremhævede hashtags", "account.follow": "Følg", + "account.follow_back": "Følg tilbage", "account.followers": "Følgere", "account.followers.empty": "Ingen følger denne bruger endnu.", "account.followers_counter": "{count, plural, one {{counter} Følger} other {{counter} Følgere}}", "account.following": "Følger", "account.following_counter": "{count, plural, one {{counter} Følges} other {{counter} Følges}}", "account.follows.empty": "Denne bruger følger ikke nogen endnu.", - "account.follows_you": "Følger dig", "account.go_to_profile": "Gå til profil", "account.hide_reblogs": "Skjul boosts fra @{name}", "account.in_memoriam": "Til minde om.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Slå lyden fra for notifikationer", "account.mute_short": "Skjul (mute)", "account.muted": "Skjult (muted)", + "account.mutual": "Fælles", "account.no_bio": "Ingen beskrivelse til rådighed.", "account.open_original_page": "Åbn oprindelig side", "account.posts": "Indlæg", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index cf545e48c..ddcbc58ce 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Keine Beiträge", "account.featured_tags.title": "Von {name} vorgestellte Hashtags", "account.follow": "Folgen", + "account.follow_back": "Ebenfalls folgen", "account.followers": "Follower", "account.followers.empty": "Diesem Profil folgt noch niemand.", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}", "account.following": "Folge ich", "account.following_counter": "{count, plural, one {{counter} Folge ich} other {{counter} Folge ich}}", "account.follows.empty": "Dieses Profil folgt noch niemandem.", - "account.follows_you": "Folgt dir", "account.go_to_profile": "Profil aufrufen", "account.hide_reblogs": "Geteilte Beiträge von @{name} ausblenden", "account.in_memoriam": "Zum Andenken.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Benachrichtigungen stummschalten", "account.mute_short": "Stummschalten", "account.muted": "Stummgeschaltet", + "account.mutual": "Gegenseitig", "account.no_bio": "Keine Beschreibung verfügbar.", "account.open_original_page": "Ursprüngliche Seite öffnen", "account.posts": "Beiträge", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 02ce7120a..83a986ba4 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -36,7 +36,6 @@ "account.following": "Ακολουθείτε", "account.following_counter": "{count, plural, one {{counter} Ακολουθεί} other {{counter} Ακολουθούν}}", "account.follows.empty": "Αυτός ο χρήστης δεν ακολουθεί κανέναν ακόμα.", - "account.follows_you": "Σε ακολουθεί", "account.go_to_profile": "Μετάβαση στο προφίλ", "account.hide_reblogs": "Απόκρυψη ενισχύσεων από @{name}", "account.in_memoriam": "Εις μνήμην.", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 1a67fecb6..37e5efa5b 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -38,7 +38,6 @@ "account.following": "Following", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}", "account.follows.empty": "This user doesn't follow anyone yet.", - "account.follows_you": "Follows you", "account.go_to_profile": "Go to profile", "account.hide_reblogs": "Hide boosts from @{name}", "account.in_memoriam": "In Memoriam.", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 2678d83a5..4daa699dc 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -38,7 +38,6 @@ "account.following": "Sekvatoj", "account.following_counter": "{count, plural, one {{counter} Sekvato} other {{counter} Sekvatoj}}", "account.follows.empty": "La uzanto ankoraŭ ne sekvas iun ajn.", - "account.follows_you": "Sekvas vin", "account.go_to_profile": "Iri al profilo", "account.hide_reblogs": "Kaŝi diskonigojn de @{name}", "account.in_memoriam": "Memore.", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 4573f4ab9..eb6c0f3e4 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Sin mensajes", "account.featured_tags.title": "Etiquetas destacadas de {name}", "account.follow": "Seguir", + "account.follow_back": "Seguir también", "account.followers": "Seguidores", "account.followers.empty": "Todavía nadie sigue a este usuario.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", "account.following": "Siguiendo", "account.following_counter": "{count, plural, other {Siguiendo a {counter}}}", "account.follows.empty": "Todavía este usuario no sigue a nadie.", - "account.follows_you": "Te sigue", "account.go_to_profile": "Ir al perfil", "account.hide_reblogs": "Ocultar adhesiones de @{name}", "account.in_memoriam": "Cuenta conmemorativa.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Silenciar notificaciones", "account.mute_short": "Silenciar", "account.muted": "Silenciado", + "account.mutual": "Mutuo", "account.no_bio": "Sin descripción provista.", "account.open_original_page": "Abrir página original", "account.posts": "Mensajes", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 0d26afef2..fab5f6ec9 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Sin publicaciones", "account.featured_tags.title": "Etiquetas destacadas de {name}", "account.follow": "Seguir", + "account.follow_back": "Seguir también", "account.followers": "Seguidores", "account.followers.empty": "Todavía nadie sigue a este usuario.", "account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidores}}", "account.following": "Siguiendo", "account.following_counter": "{count, plural, other {{counter} Siguiendo}}", "account.follows.empty": "Este usuario todavía no sigue a nadie.", - "account.follows_you": "Te sigue", "account.go_to_profile": "Ir al perfil", "account.hide_reblogs": "Ocultar retoots de @{name}", "account.in_memoriam": "En memoria.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Silenciar notificaciones", "account.mute_short": "Silenciar", "account.muted": "Silenciado", + "account.mutual": "Mutuo", "account.no_bio": "Sin biografía.", "account.open_original_page": "Abrir página original", "account.posts": "Publicaciones", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index bbc8bcc75..0d6149f5f 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Sin publicaciones", "account.featured_tags.title": "Etiquetas destacadas de {name}", "account.follow": "Seguir", + "account.follow_back": "Seguir también", "account.followers": "Seguidores", "account.followers.empty": "Todavía nadie sigue a este usuario.", "account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidores}}", "account.following": "Siguiendo", "account.following_counter": "{count, plural, other {Siguiendo a {counter}}}", "account.follows.empty": "Este usuario todavía no sigue a nadie.", - "account.follows_you": "Te sigue", "account.go_to_profile": "Ir al perfil", "account.hide_reblogs": "Ocultar impulsos de @{name}", "account.in_memoriam": "Cuenta conmemorativa.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Silenciar notificaciones", "account.mute_short": "Silenciar", "account.muted": "Silenciado", + "account.mutual": "Mutuo", "account.no_bio": "Sin biografía.", "account.open_original_page": "Abrir página original", "account.posts": "Publicaciones", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index a41aa02f8..c39733410 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -38,7 +38,6 @@ "account.following": "Jälgib", "account.following_counter": "{count, plural, one {{counter} jälgitav} other {{counter} jälgitavat}}", "account.follows.empty": "See kasutaja ei jälgi veel kedagi.", - "account.follows_you": "Jälgib sind", "account.go_to_profile": "Mine profiilile", "account.hide_reblogs": "Peida @{name} jagamised", "account.in_memoriam": "In Memoriam.", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 26ed7add1..c75f697e4 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Bidalketarik ez", "account.featured_tags.title": "{name} erabiltzailearen nabarmendutako traolak", "account.follow": "Jarraitu", + "account.follow_back": "Jarraitu bueltan", "account.followers": "Jarraitzaileak", "account.followers.empty": "Ez du inork erabiltzaile hau jarraitzen oraindik.", "account.followers_counter": "{count, plural, one {Jarraitzaile {counter}} other {{counter} jarraitzaile}}", "account.following": "Jarraitzen", "account.following_counter": "{count, plural, one {{counter} jarraitzen} other {{counter} jarraitzen}}", "account.follows.empty": "Erabiltzaile honek ez du inor jarraitzen oraindik.", - "account.follows_you": "Jarraitzen dizu", "account.go_to_profile": "Joan profilera", "account.hide_reblogs": "Ezkutatu @{name} erabiltzailearen bultzadak", "account.in_memoriam": "Oroimenezkoa.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Mututu jakinarazpenak", "account.mute_short": "Mututu", "account.muted": "Mutututa", + "account.mutual": "Elkarrekikoa", "account.no_bio": "Ez da deskribapenik eman.", "account.open_original_page": "Ireki jatorrizko orria", "account.posts": "Bidalketa", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 8e8930bfe..a93d0f661 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -38,7 +38,6 @@ "account.following": "پی میگیرید", "account.following_counter": "{count, plural, one {{counter} پیگرفته} other {{counter} پیگرفته}}", "account.follows.empty": "این کاربر هنوز پیگیر کسی نیست.", - "account.follows_you": "پیگیرتان است", "account.go_to_profile": "رفتن به نمایه", "account.hide_reblogs": "نهفتن تقویتهای @{name}", "account.in_memoriam": "به یادبود.", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index e99de4d03..dab7eac1e 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Ei julkaisuja", "account.featured_tags.title": "Käyttäjän {name} esillä pidettävät aihetunnisteet", "account.follow": "Seuraa", + "account.follow_back": "Seuraa takaisin", "account.followers": "Seuraajat", "account.followers.empty": "Kukaan ei seuraa tätä käyttäjää vielä.", "account.followers_counter": "{count, plural, one {{counter} seuraaja} other {{counter} seuraajaa}}", "account.following": "Seuratut", "account.following_counter": "{count, plural, one {{counter} seurattu} other {{counter} seurattua}}", "account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.", - "account.follows_you": "Seuraa sinua", "account.go_to_profile": "Avaa profiili", "account.hide_reblogs": "Piilota käyttäjän @{name} tehostukset", "account.in_memoriam": "Muistoissamme.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Mykistä ilmoitukset", "account.mute_short": "Mykistä", "account.muted": "Mykistetty", + "account.mutual": "Molemmat", "account.no_bio": "Kuvausta ei ole annettu.", "account.open_original_page": "Avaa alkuperäinen sivu", "account.posts": "Julkaisut", diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json index bc7635887..ec27c8f60 100644 --- a/app/javascript/mastodon/locales/fil.json +++ b/app/javascript/mastodon/locales/fil.json @@ -31,7 +31,6 @@ "account.followers.empty": "Wala pang sumusunod sa tagagamit na ito.", "account.following": "Sinusundan", "account.follows.empty": "Wala pang sinusundan ang tagagamit na ito.", - "account.follows_you": "Sinusunod ka", "account.go_to_profile": "Pumunta sa profile", "account.hide_reblogs": "Itago ang mga pagpapalakas mula sa {name}", "account.in_memoriam": "Sa Alaala Ni.", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 45fafd15d..9259f2015 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Einki uppslag", "account.featured_tags.title": "Tvíkrossar hjá {name}", "account.follow": "Fylg", + "account.follow_back": "Fylg aftur", "account.followers": "Fylgjarar", "account.followers.empty": "Ongar fylgjarar enn.", "account.followers_counter": "{count, plural, one {{counter} Fylgjari} other {{counter} Fylgjarar}}", "account.following": "Fylgir", "account.following_counter": "{count, plural, one {{counter} fylgir} other {{counter} fylgja}}", "account.follows.empty": "Hesin brúkari fylgir ongum enn.", - "account.follows_you": "Fylgir tær", "account.go_to_profile": "Far til vanga", "account.hide_reblogs": "Fjal lyft frá @{name}", "account.in_memoriam": "In memoriam.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Sløkk fráboðanir", "account.mute_short": "Doyv", "account.muted": "Sløkt/ur", + "account.mutual": "Sínamillum", "account.no_bio": "Lýsing vantar.", "account.open_original_page": "Opna upprunasíðuna", "account.posts": "Uppsløg", diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json index e2067cc46..f2d99412d 100644 --- a/app/javascript/mastodon/locales/fr-QC.json +++ b/app/javascript/mastodon/locales/fr-QC.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Aucune publication", "account.featured_tags.title": "Hashtags inclus de {name}", "account.follow": "Suivre", + "account.follow_back": "S'abonner en retour", "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.following": "Abonné·e", "account.following_counter": "{count, plural, one {{counter} Abonnement} other {{counter} Abonnements}}", "account.follows.empty": "Ce compte ne suit personne présentement.", - "account.follows_you": "Vous suit", "account.go_to_profile": "Voir ce profil", "account.hide_reblogs": "Masquer les boosts de @{name}", "account.in_memoriam": "En souvenir de", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Rendre les notifications muettes", "account.mute_short": "Rendre muet", "account.muted": "Masqué·e", + "account.mutual": "Mutuel", "account.no_bio": "Description manquante.", "account.open_original_page": "Ouvrir la page d'origine", "account.posts": "Publications", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 7db4bf7bc..774702f98 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Aucun message", "account.featured_tags.title": "Les hashtags en vedette de {name}", "account.follow": "Suivre", + "account.follow_back": "S'abonner en retour", "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.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": "Aller au profil", "account.hide_reblogs": "Masquer les partages de @{name}", "account.in_memoriam": "En mémoire de.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Désactiver les alertes", "account.mute_short": "Mettre en sourdine", "account.muted": "Masqué·e", + "account.mutual": "Mutuel", "account.no_bio": "Aucune description fournie.", "account.open_original_page": "Ouvrir la page d'origine", "account.posts": "Messages", diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json index 2ec4a53bf..ea42ef91c 100644 --- a/app/javascript/mastodon/locales/fy.json +++ b/app/javascript/mastodon/locales/fy.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Gjin berjochten", "account.featured_tags.title": "Utljochte hashtags fan {name}", "account.follow": "Folgje", + "account.follow_back": "Weromfolgje", "account.followers": "Folgers", "account.followers.empty": "Noch net ien folget dizze brûker.", "account.followers_counter": "{count, plural, one {{counter} folger} other {{counter} folgers}}", "account.following": "Folgjend", "account.following_counter": "{count, plural, one {{counter} folgjend} other {{counter} folgjend}}", "account.follows.empty": "Dizze brûker folget noch net ien.", - "account.follows_you": "Folget jo", "account.go_to_profile": "Gean nei profyl", "account.hide_reblogs": "Boosts fan @{name} ferstopje", "account.in_memoriam": "Yn memoriam.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Meldingen negearje", "account.mute_short": "Negearje", "account.muted": "Negearre", + "account.mutual": "Jimme folgje inoar", "account.no_bio": "Gjin omskriuwing opjûn.", "account.open_original_page": "Orizjinele side iepenje", "account.posts": "Berjochten", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index ee6b44c88..f0f9c5554 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -35,7 +35,6 @@ "account.following": "Ag leanúint", "account.following_counter": "{count, plural, one {Ag leanúint cúntas amháin} other {Ag leanúint {counter} cúntas}}", "account.follows.empty": "Ní leanann an t-úsáideoir seo duine ar bith fós.", - "account.follows_you": "Do do leanúint", "account.go_to_profile": "Téigh go dtí próifíl", "account.hide_reblogs": "Folaigh moltaí ó @{name}", "account.in_memoriam": "Cuimhneachán.", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index 91333c1a0..11d83d6ce 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -37,7 +37,6 @@ "account.following": "A’ leantainn", "account.following_counter": "{count, plural, one {A’ leantainn {counter}} two {A’ leantainn {counter}} few {A’ leantainn {counter}} other {A’ leantainn {counter}}}", "account.follows.empty": "Chan eil an cleachdaiche seo a’ leantainn neach sam bith fhathast.", - "account.follows_you": "Gad leantainn", "account.go_to_profile": "Tadhail air a’ phròifil", "account.hide_reblogs": "Falaich na brosnachaidhean o @{name}", "account.in_memoriam": "Mar chuimhneachan.", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 08d7d4977..bfd20a467 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -38,7 +38,6 @@ "account.following": "Seguindo", "account.following_counter": "{count, plural, one {{counter} Seguindo} other {{counter} Seguindo}}", "account.follows.empty": "Esta usuaria aínda non segue a ninguén.", - "account.follows_you": "Séguete", "account.go_to_profile": "Ir ao perfil", "account.hide_reblogs": "Agochar promocións de @{name}", "account.in_memoriam": "Lembranzas.", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index dda92c5c9..658e16b85 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "אין חצרוצים", "account.featured_tags.title": "התגיות המועדפות של {name}", "account.follow": "לעקוב", + "account.follow_back": "החזרת עוקב", "account.followers": "עוקבים", "account.followers.empty": "אף אחד לא עוקב אחר המשתמש הזה עדיין.", "account.followers_counter": "{count, plural,one {עוקב אחד} other {{counter} עוקבים}}", "account.following": "נעקבים", "account.following_counter": "{count, plural,one {עוקב אחרי {counter}}other {עוקב אחרי {counter}}}", "account.follows.empty": "משתמש זה עדיין לא עוקב אחרי אף אחד.", - "account.follows_you": "במעקב אחריך", "account.go_to_profile": "מעבר לפרופיל", "account.hide_reblogs": "להסתיר הידהודים מאת @{name}", "account.in_memoriam": "פרופיל זכרון.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "השתקת התראות", "account.mute_short": "השתקה", "account.muted": "מושתק", + "account.mutual": "הדדיים", "account.no_bio": "לא סופק תיאור.", "account.open_original_page": "לפתיחת העמוד המקורי", "account.posts": "פוסטים", diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json index 387941b5e..76d7c1d38 100644 --- a/app/javascript/mastodon/locales/hi.json +++ b/app/javascript/mastodon/locales/hi.json @@ -37,7 +37,6 @@ "account.following": "फॉलोइंग", "account.following_counter": "{count, plural, one {{counter} निम्नलिखित} other {{counter} निम्नलिखित}}", "account.follows.empty": "यह यूज़र् अभी तक किसी को फॉलो नहीं करता है।", - "account.follows_you": "आपको फॉलो करता है", "account.go_to_profile": "प्रोफाइल में जाएँ", "account.hide_reblogs": "@{name} के बूस्ट छुपाएं", "account.in_memoriam": "याद में", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index 6da7d6cd8..654991a0f 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -28,7 +28,6 @@ "account.following": "Pratim", "account.following_counter": "{count, plural, one {{counter} praćeni} few{{counter} praćena} other {{counter} praćenih}}", "account.follows.empty": "Korisnik/ca još ne prati nikoga.", - "account.follows_you": "Prati te", "account.go_to_profile": "Idi na profil", "account.hide_reblogs": "Sakrij boostove od @{name}", "account.in_memoriam": "U sjećanje.", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 386b15811..0d50e36fe 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Nincs bejegyzés", "account.featured_tags.title": "{name} kiemelt hashtagjei", "account.follow": "Követés", + "account.follow_back": "Viszontkövetés", "account.followers": "Követő", "account.followers.empty": "Ezt a felhasználót még senki sem követi.", "account.followers_counter": "{count, plural, one {{counter} Követő} other {{counter} Követő}}", "account.following": "Követve", "account.following_counter": "{count, plural, one {{counter} Követett} other {{counter} Követett}}", "account.follows.empty": "Ez a felhasználó még senkit sem követ.", - "account.follows_you": "Követ téged", "account.go_to_profile": "Ugrás a profilhoz", "account.hide_reblogs": "@{name} megtolásainak elrejtése", "account.in_memoriam": "Emlékünkben.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Értesítések némítása", "account.mute_short": "Némítás", "account.muted": "Némítva", + "account.mutual": "Kölcsönös", "account.no_bio": "Leírás nincs megadva.", "account.open_original_page": "Eredeti oldal megnyitása", "account.posts": "Bejegyzések", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index f2548c7d3..835105218 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -32,7 +32,6 @@ "account.following": "Հետեւած", "account.following_counter": "{count, plural, one {{counter} Հետեւած} other {{counter} Հետեւած}}", "account.follows.empty": "Այս օգտատէրը դեռ ոչ մէկի չի հետեւում։", - "account.follows_you": "Հետեւում է քեզ", "account.go_to_profile": "Գնալ անձնական հաշիւ", "account.hide_reblogs": "Թաքցնել @{name}֊ի տարածածները", "account.joined_short": "Միացել է", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 8ecf36125..5af20a97f 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -37,7 +37,6 @@ "account.following": "Mengikuti", "account.following_counter": "{count, plural, other {{counter} Mengikuti}}", "account.follows.empty": "Pengguna ini belum mengikuti siapa pun.", - "account.follows_you": "Mengikuti Anda", "account.go_to_profile": "Buka profil", "account.hide_reblogs": "Sembunyikan boosts dari @{name}", "account.in_memoriam": "Mengenang.", diff --git a/app/javascript/mastodon/locales/ie.json b/app/javascript/mastodon/locales/ie.json index 9f91bf2ce..a7cf1caab 100644 --- a/app/javascript/mastodon/locales/ie.json +++ b/app/javascript/mastodon/locales/ie.json @@ -38,7 +38,6 @@ "account.following": "Sequent", "account.following_counter": "{count, plural, one {{counter} Sequent} other {{counter} Sequent}}", "account.follows.empty": "Ti-ci usator ancor ne seque quemcunc.", - "account.follows_you": "Seque te", "account.go_to_profile": "Ear a profil", "account.hide_reblogs": "Celar boosts de @{name}", "account.in_memoriam": "In Memoriam.", diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json index c24d28eea..f163567f1 100644 --- a/app/javascript/mastodon/locales/ig.json +++ b/app/javascript/mastodon/locales/ig.json @@ -6,7 +6,6 @@ "account.follow": "Soro", "account.followers": "Ndị na-eso", "account.following": "Na-eso", - "account.follows_you": "Na-eso gị", "account.mute": "Mee ogbi @{name}", "account.unfollow": "Kwụsị iso", "account_note.placeholder": "Click to add a note", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index ba4440893..233b76845 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -37,7 +37,6 @@ "account.following": "Sequata", "account.following_counter": "{count, plural, one {{counter} Sequas} other {{counter} Sequanti}}", "account.follows.empty": "Ca uzanto ne sequa irgu til nun.", - "account.follows_you": "Sequas tu", "account.go_to_profile": "Irez al profilo", "account.hide_reblogs": "Celez repeti de @{name}", "account.in_memoriam": "Memorige.", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 0df1a5e75..46e83b4db 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -38,7 +38,6 @@ "account.following": "Fylgist með", "account.following_counter": "{count, plural, one {Fylgist með: {counter}} other {Fylgist með: {counter}}}", "account.follows.empty": "Þessi notandi fylgist ennþá ekki með neinum.", - "account.follows_you": "Fylgir þér", "account.go_to_profile": "Fara í notandasnið", "account.hide_reblogs": "Fela endurbirtingar fyrir @{name}", "account.in_memoriam": "Minning.", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index fa659506a..4fb4d88cb 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Nessun post", "account.featured_tags.title": "Hashtag in evidenza di {name}", "account.follow": "Segui", + "account.follow_back": "Segui a tua volta", "account.followers": "Follower", "account.followers.empty": "Ancora nessuno segue questo utente.", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}", "account.following": "Seguiti", "account.following_counter": "{count, plural, one {{counter} Seguiti} other {{counter} Seguiti}}", "account.follows.empty": "Questo utente non segue ancora nessuno.", - "account.follows_you": "Ti segue", "account.go_to_profile": "Vai al profilo", "account.hide_reblogs": "Nascondi potenziamenti da @{name}", "account.in_memoriam": "In memoria.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Silenzia notifiche", "account.mute_short": "Silenzia", "account.muted": "Mutato", + "account.mutual": "Reciproco", "account.no_bio": "Nessuna descrizione fornita.", "account.open_original_page": "Apri la pagina originale", "account.posts": "Post", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 5ad9d6dc9..324641575 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -38,7 +38,6 @@ "account.following": "フォロー中", "account.following_counter": "{counter} フォロー", "account.follows.empty": "まだ誰もフォローしていません。", - "account.follows_you": "フォローされています", "account.go_to_profile": "プロフィールページへ", "account.hide_reblogs": "@{name}さんからのブーストを非表示", "account.in_memoriam": "故人を偲んで。", diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json index 875ac3c19..9d977e933 100644 --- a/app/javascript/mastodon/locales/ka.json +++ b/app/javascript/mastodon/locales/ka.json @@ -15,7 +15,6 @@ "account.featured_tags.last_status_never": "პოსტები არ არის", "account.follow": "გაყოლა", "account.followers": "მიმდევრები", - "account.follows_you": "მოგყვებათ", "account.hide_reblogs": "დაიმალოს ბუსტები @{name}-სგან", "account.media": "მედია", "account.mention": "ასახელეთ @{name}", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index e9d4b57de..08c70a940 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -22,7 +22,6 @@ "account.followers_counter": "{count, plural, one {{count} n umeḍfar} other {{count} n imeḍfaren}}", "account.following_counter": "{count, plural, one {{counter} yettwaḍfaren} other {{counter} yettwaḍfaren}}", "account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.", - "account.follows_you": "Yeṭṭafaṛ-ik", "account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}", "account.link_verified_on": "Taɣara n useɣwen-a tettwasenqed ass n {date}", "account.locked_info": "Amiḍan-agi uslig isekweṛ. D bab-is kan i izemren ad yeǧǧ, s ufus-is, win ara t-iḍefṛen.", diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json index 189d792e3..e0e047ba1 100644 --- a/app/javascript/mastodon/locales/kk.json +++ b/app/javascript/mastodon/locales/kk.json @@ -34,7 +34,6 @@ "account.following": "Жазылым", "account.following_counter": "{count, plural, one {{counter} жазылым} other {{counter} жазылым}}", "account.follows.empty": "Бұл қолданушы әлі ешкімге жазылмаған.", - "account.follows_you": "Сізге жазылған", "account.go_to_profile": "Профиліне өту", "account.hide_reblogs": "@{name} бустарын жасыру", "account.joined_short": "Қосылған", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 4606916c1..5746ab67a 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "게시물 없음", "account.featured_tags.title": "{name} 님의 추천 해시태그", "account.follow": "팔로우", + "account.follow_back": "맞팔로우", "account.followers": "팔로워", "account.followers.empty": "아직 아무도 이 사용자를 팔로우하고 있지 않습니다.", "account.followers_counter": "{counter} 팔로워", "account.following": "팔로잉", "account.following_counter": "{counter} 팔로잉", "account.follows.empty": "이 사용자는 아직 아무도 팔로우하고 있지 않습니다.", - "account.follows_you": "나를 팔로우합니다", "account.go_to_profile": "프로필로 이동", "account.hide_reblogs": "@{name}의 부스트를 숨기기", "account.in_memoriam": "고인의 계정입니다.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "알림 뮤트", "account.mute_short": "뮤트", "account.muted": "뮤트됨", + "account.mutual": "상호 팔로우", "account.no_bio": "제공된 설명이 없습니다.", "account.open_original_page": "원본 페이지 열기", "account.posts": "게시물", diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json index b94054267..7d8603cae 100644 --- a/app/javascript/mastodon/locales/ku.json +++ b/app/javascript/mastodon/locales/ku.json @@ -36,7 +36,6 @@ "account.following": "Dişopîne", "account.following_counter": "{count, plural, one {{counter} Dişopîne} other {{counter} Dişopîne}}", "account.follows.empty": "Ev bikarhêner hin kesekî heya niha neşopandiye.", - "account.follows_you": "Te dişopîne", "account.go_to_profile": "Biçe bo profîlê", "account.hide_reblogs": "Bilindkirinên ji @{name} veşêre", "account.in_memoriam": "Di bîranînê de.", diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json index ca08ca836..8f384fe12 100644 --- a/app/javascript/mastodon/locales/kw.json +++ b/app/javascript/mastodon/locales/kw.json @@ -20,7 +20,6 @@ "account.followers_counter": "{count, plural, one {{counter} Holyer} other {{counter} Holyer}}", "account.following_counter": "{count, plural, one {Ow holya {counter}} other {Ow holya {counter}}}", "account.follows.empty": "Ny wra'n devnydhyer ma holya nagonan hwath.", - "account.follows_you": "Y'th hol", "account.hide_reblogs": "Kudha kenerthow a @{name}", "account.link_verified_on": "Perghenogeth an kolm ma a veu checkys dhe {date}", "account.locked_info": "Studh privetter an akont ma yw alhwedhys. An perghen a wra dasweles dre leuv piw a yll aga holya.", diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index c776a91ee..c54ed2a7e 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "\"No ay publikasyones", "account.featured_tags.title": "Etiketas avaliadas de {name}", "account.follow": "Sige", + "account.follow_back": "Sige tamyen", "account.followers": "Suivantes", "account.followers.empty": "Por agora dingun no sige a este utilizador.", "account.followers_counter": "{count, plural, one {{counter} suivante} other {{counter} suivantes}}", "account.following": "Sigiendo", "account.following_counter": "{count, plural, other {Sigiendo a {counter}}}", "account.follows.empty": "Este utilizador ainda no sige a ningun.", - "account.follows_you": "Te sige", "account.go_to_profile": "Va al profil", "account.hide_reblogs": "Eskonde repartajasyones de @{name}", "account.in_memoriam": "De bendicha memoria.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Silensia avizos de @{name}", "account.mute_short": "Silensia", "account.muted": "Silensiado", + "account.mutual": "Mutual", "account.no_bio": "No ay deskripsion.", "account.open_original_page": "Avre pajina orijnala", "account.posts": "Publikasyones", @@ -478,6 +479,7 @@ "onboarding.actions.go_to_home": "Va a tu linya prinsipala", "onboarding.compose.template": "Ke haber, #Mastodon?", "onboarding.follows.title": "Personaliza tu linya prinsipala", + "onboarding.profile.discoverable": "Faz ke mi profil apareska en bushkedas", "onboarding.profile.display_name": "Nombre amostrado", "onboarding.profile.display_name_hint": "Tu nombre para amostrar.", "onboarding.profile.note": "Tu deskripsyon", @@ -555,6 +557,7 @@ "report.reasons.dislike": "No me plaze", "report.reasons.dislike_description": "\"No es algo ke kero ver", "report.reasons.legal": "Es ilegal", + "report.reasons.legal_description": "Kreyes ke esta violando la ley de tu paiz o el paiz del sirvidor", "report.reasons.other": "Es otra koza", "report.reasons.other_description": "El problem no es de las otras kategorias", "report.reasons.spam": "Es spam", @@ -611,6 +614,7 @@ "sign_in_banner.create_account": "Kriya kuento", "sign_in_banner.sign_in": "Konektate", "sign_in_banner.sso_redirect": "Konektate o enrejistrate", + "sign_in_banner.text": "Konektate para segir prefiles o etiketas, partajar publikasyones, arispondir a eyas i markar ke te plazen. Puedes tambyen enteraktuar dizde tu kuento en un sirvidor desferente.", "status.admin_account": "Avre la enterfaz de moderasyon para @{name}", "status.admin_domain": "Avre la enterfaz de moderasyon para @{domain}", "status.admin_status": "Avre esto en la enterfaz de moderasyon", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 58c80e411..82f1669b1 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Nėra įrašų", "account.featured_tags.title": "{name} rekomenduojamos grotažymės", "account.follow": "Sekti", + "account.follow_back": "Sekti atgal", "account.followers": "Sekėjai", "account.followers.empty": "Šio naudotojo dar niekas neseka.", "account.followers_counter": "{count, plural, one {{counter} sekėjas (-a)} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}", "account.following": "Seka", "account.following_counter": "{count, plural, one {{counter} Seka} few {{counter} Seka} many {{counter} Seka} other {{counter} Seka}}", "account.follows.empty": "Šis (-i) naudotojas (-a) dar nieko neseka.", - "account.follows_you": "Seka tave", "account.go_to_profile": "Eiti į profilį", "account.hide_reblogs": "Slėpti pakėlimus iš @{name}", "account.in_memoriam": "Atminimui.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Nutildyti pranešimus", "account.mute_short": "Nutildyti", "account.muted": "Nutildytas", + "account.mutual": "Abipusis", "account.no_bio": "Nėra pateikto aprašymo.", "account.open_original_page": "Atidaryti originalinį puslapį", "account.posts": "Įrašai", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 63ec6275b..c06a1d936 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -37,7 +37,6 @@ "account.following": "Seko", "account.following_counter": "{count, plural, one {{counter} sekojamais} other {{counter} sekojamie}}", "account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.", - "account.follows_you": "Seko tev", "account.go_to_profile": "Doties uz profilu", "account.hide_reblogs": "Slēpt @{name} izceltas ziņas", "account.in_memoriam": "Piemiņai.", diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json index 371218bbf..bdef3f4a5 100644 --- a/app/javascript/mastodon/locales/mk.json +++ b/app/javascript/mastodon/locales/mk.json @@ -25,7 +25,6 @@ "account.followers": "Следбеници", "account.followers.empty": "Никој не го следи овој корисник сеуште.", "account.follows.empty": "Корисникот не следи никој сеуште.", - "account.follows_you": "Те следи тебе", "account.hide_reblogs": "Сокриј буст од @{name}", "account.link_verified_on": "Сопстевноста на овај линк беше проверен на {date}", "account.locked_info": "Статусот на приватност на овај корисник е сетиран како заклучен. Корисникот одлучува кој можи да го следи него.", diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json index b00cedc6f..11636646b 100644 --- a/app/javascript/mastodon/locales/ml.json +++ b/app/javascript/mastodon/locales/ml.json @@ -26,7 +26,6 @@ "account.following": "പിന്തുടരുന്നു", "account.following_counter": "{count, plural, one {{counter} പിന്തുടരുന്നു} other {{counter} പിന്തുടരുന്നു}}", "account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.", - "account.follows_you": "നിങ്ങളെ പിന്തുടരുന്നു", "account.go_to_profile": "പ്രൊഫൈലിലേക്ക് പോകാം", "account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക", "account.joined_short": "ജോയിൻ ചെയ്തിരിക്കുന്നു", diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json index 75b75375b..7f5b7d652 100644 --- a/app/javascript/mastodon/locales/mr.json +++ b/app/javascript/mastodon/locales/mr.json @@ -35,7 +35,6 @@ "account.following": "अनुसरण", "account.following_counter": "{count, plural, one {{counter} following} other {{counter} following}}", "account.follows.empty": "हा वापरकर्ता अजूनपर्यंत कोणाचा अनुयायी नाही.", - "account.follows_you": "तुमचा अनुयायी आहे", "account.go_to_profile": "प्रोफाइल वर जा", "account.hide_reblogs": "@{name} पासून सर्व बूस्ट लपवा", "account.joined_short": "सामील झाले", diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index 724e07ae7..50a48db1e 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -37,7 +37,6 @@ "account.following": "Mengikuti", "account.following_counter": "{count, plural, one {{counter} Diikuti} other {{counter} Diikuti}}", "account.follows.empty": "Pengguna ini belum mengikuti sesiapa.", - "account.follows_you": "Mengikuti anda", "account.go_to_profile": "Pergi ke profil", "account.hide_reblogs": "Sembunyikan galakan daripada @{name}", "account.in_memoriam": "Dalam Memoriam.", diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json index 917419d17..3ca03b616 100644 --- a/app/javascript/mastodon/locales/my.json +++ b/app/javascript/mastodon/locales/my.json @@ -38,7 +38,6 @@ "account.following": "စောင့်ကြည့်နေသည်", "account.following_counter": "{count, plural, one {စောင့်ကြည့်ထားသူ {counter}} other {စောင့်ကြည့်ထားသူများ {counter}}}", "account.follows.empty": "ဤသူသည် မည်သူ့ကိုမျှ စောင့်ကြည့်ခြင်း မရှိသေးပါ။", - "account.follows_you": "သင့်ကို စောင့်ကြည့်နေသည်", "account.go_to_profile": "ပရိုဖိုင်းသို့ သွားရန်", "account.hide_reblogs": "@{name} ၏ မျှဝေမှုကို ဝှက်ထားရန်", "account.in_memoriam": "အမှတ်တရ", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 295b420fd..86617d4a5 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Geen berichten", "account.featured_tags.title": "Uitgelichte hashtags van {name}", "account.follow": "Volgen", + "account.follow_back": "Terugvolgen", "account.followers": "Volgers", "account.followers.empty": "Deze gebruiker heeft nog geen volgers of heeft deze verborgen.", "account.followers_counter": "{count, plural, one {{counter} volger} other {{counter} volgers}}", "account.following": "Volgend", "account.following_counter": "{count, plural, one {{counter} volgend} other {{counter} volgend}}", "account.follows.empty": "Deze gebruiker volgt nog niemand of heeft deze verborgen.", - "account.follows_you": "Volgt jou", "account.go_to_profile": "Ga naar profiel", "account.hide_reblogs": "Boosts van @{name} verbergen", "account.in_memoriam": "In memoriam.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Meldingen negeren", "account.mute_short": "Negeren", "account.muted": "Genegeerd", + "account.mutual": "Jullie volgen elkaar", "account.no_bio": "Geen beschrijving opgegeven.", "account.open_original_page": "Originele pagina openen", "account.posts": "Berichten", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 4750b1adc..3ef2f80ea 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Ingen innlegg", "account.featured_tags.title": "{name} sine framheva emneknaggar", "account.follow": "Fylg", + "account.follow_back": "Følg tilbake", "account.followers": "Fylgjarar", "account.followers.empty": "Ingen fylgjer denne brukaren enno.", "account.followers_counter": "{count, plural, one {{counter} fylgjar} other {{counter} fylgjarar}}", "account.following": "Fylgjer", "account.following_counter": "{count, plural, one {Fylgjer {counter}} other {Fylgjer {counter}}}", "account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.", - "account.follows_you": "Fylgjer deg", "account.go_to_profile": "Gå til profil", "account.hide_reblogs": "Skjul framhevingar frå @{name}", "account.in_memoriam": "Til minne om.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Demp varslingar", "account.mute_short": "Demp", "account.muted": "Målbunden", + "account.mutual": "Felles", "account.no_bio": "Inga skildring er gjeven.", "account.open_original_page": "Opne originalsida", "account.posts": "Tut", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 1739d4aa3..29eaeddff 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -38,7 +38,6 @@ "account.following": "Følger", "account.following_counter": "{count, plural, one {{counter} som følges} other {{counter} som følges}}", "account.follows.empty": "Denne brukeren følger ikke noen enda.", - "account.follows_you": "Følger deg", "account.go_to_profile": "Gå til profil", "account.hide_reblogs": "Skjul fremhevinger fra @{name}", "account.in_memoriam": "Til minne om.", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 3812057fb..833bfe6ac 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -34,7 +34,6 @@ "account.following": "Abonat", "account.following_counter": "{count, plural, one {{counter} Abonaments} other {{counter} Abonaments}}", "account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.", - "account.follows_you": "Vos sèc", "account.go_to_profile": "Anar al perfil", "account.hide_reblogs": "Rescondre los partatges de @{name}", "account.in_memoriam": "En Memòria.", diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json index 708d8c3e9..132b695cd 100644 --- a/app/javascript/mastodon/locales/pa.json +++ b/app/javascript/mastodon/locales/pa.json @@ -18,7 +18,6 @@ "account.followers.empty": "ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਹਾਲੇ ਕੋਈ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।", "account.following": "ਫ਼ਾਲੋ ਕੀਤਾ", "account.follows.empty": "ਇਹ ਵਰਤੋਂਕਾਰ ਹਾਲੇ ਕਿਸੇ ਨੂੰ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।", - "account.follows_you": "ਤੁਹਾਨੂੰ ਫ਼ਾਲੋ ਕਰੋ", "account.media": "ਮੀਡੀਆ", "account.muted": "ਮੌਨ ਕੀਤੀਆਂ", "account.posts": "ਪੋਸਟਾਂ", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index dc77f8f4e..9a3710da7 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Brak postów", "account.featured_tags.title": "Polecane hasztagi {name}", "account.follow": "Obserwuj", + "account.follow_back": "Obserwuj wzajemnie", "account.followers": "Obserwujący", "account.followers.empty": "Nikt jeszcze nie obserwuje tego użytkownika.", "account.followers_counter": "{count, plural, one {{counter} obserwujący} few {{counter} obserwujących} many {{counter} obserwujących} other {{counter} obserwujących}}", "account.following": "Obserwowani", "account.following_counter": "{count, plural, one {{counter} obserwowany} few {{counter} obserwowanych} many {{counter} obserwowanych} other {{counter} obserwowanych}}", "account.follows.empty": "Ten użytkownik nie obserwuje jeszcze nikogo.", - "account.follows_you": "Obserwuje Cię", "account.go_to_profile": "Przejdź do profilu", "account.hide_reblogs": "Ukryj podbicia od @{name}", "account.in_memoriam": "Ku pamięci.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Wycisz powiadomienia", "account.mute_short": "Wycisz", "account.muted": "Wyciszony", + "account.mutual": "Przyjaciele", "account.no_bio": "Brak opisu.", "account.open_original_page": "Otwórz stronę oryginalną", "account.posts": "Wpisy", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index e7975dd76..482cc8ee7 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -38,7 +38,6 @@ "account.following": "Seguindo", "account.following_counter": "{count, plural, one {segue {counter}} other {segue {counter}}}", "account.follows.empty": "Nada aqui.", - "account.follows_you": "te segue", "account.go_to_profile": "Ir ao perfil", "account.hide_reblogs": "Ocultar boosts de @{name}", "account.in_memoriam": "Em memória.", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index d5055b0dc..a6d0ffee9 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Sem publicações", "account.featured_tags.title": "#Etiquetas destacadas por {name}", "account.follow": "Seguir", + "account.follow_back": "Seguir de volta", "account.followers": "Seguidores", "account.followers.empty": "Ainda ninguém segue este utilizador.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", "account.following": "A seguir", "account.following_counter": "{count, plural, other {A seguir {counter}}}", "account.follows.empty": "Este utilizador ainda não segue ninguém.", - "account.follows_you": "Segue-te", "account.go_to_profile": "Ir para o perfil", "account.hide_reblogs": "Esconder partilhas de @{name}", "account.in_memoriam": "Em Memória.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Silenciar notificações", "account.mute_short": "Silenciar", "account.muted": "Silenciada", + "account.mutual": "Mútuo", "account.no_bio": "Nenhuma descrição fornecida.", "account.open_original_page": "Abrir a página original", "account.posts": "Publicações", diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json index 5355f9935..88dbfa4b8 100644 --- a/app/javascript/mastodon/locales/ro.json +++ b/app/javascript/mastodon/locales/ro.json @@ -36,7 +36,6 @@ "account.following": "Urmăriți", "account.following_counter": "{count, plural, one {Un abonament} few {{counter} abonamente} other {{counter} de abonamente}}", "account.follows.empty": "Momentan acest utilizator nu are niciun abonament.", - "account.follows_you": "Este abonat la tine", "account.go_to_profile": "Mergi la profil", "account.hide_reblogs": "Ascunde distribuirile de la @{name}", "account.joined_short": "Înscris", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index f0c48236b..37a8328c9 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -38,7 +38,6 @@ "account.following": "Подписки", "account.following_counter": "{count, plural, one {{counter} подписка} many {{counter} подписок} other {{counter} подписки}}", "account.follows.empty": "Этот пользователь пока ни на кого не подписался.", - "account.follows_you": "Подписан(а) на вас", "account.go_to_profile": "Перейти к профилю", "account.hide_reblogs": "Скрыть продвижения от @{name}", "account.in_memoriam": "В Памяти.", diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json index 59379343b..051ec5d6f 100644 --- a/app/javascript/mastodon/locales/sa.json +++ b/app/javascript/mastodon/locales/sa.json @@ -36,7 +36,6 @@ "account.following": "अनुसरति", "account.following_counter": "{count, plural, one {{counter} अनुसृतः} two {{counter} अनुसृतौ} other {{counter} अनुसृताः}}", "account.follows.empty": "न कोऽप्यनुसृतो वर्तते", - "account.follows_you": "त्वामनुसरति", "account.go_to_profile": "प्रोफायिलं गच्छ", "account.hide_reblogs": "@{name} मित्रस्य प्रकाशनानि छिद्यन्ताम्", "account.in_memoriam": "स्मृत्याम्", diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index 7f29525e7..89e951f77 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -30,7 +30,6 @@ "account.following": "Sighende", "account.following_counter": "{count, plural, one {Sighende a {counter}} other {Sighende a {counter}}}", "account.follows.empty": "Custa persone non sighit ancora a nemos.", - "account.follows_you": "Ti sighit", "account.hide_reblogs": "Cua is cumpartziduras de @{name}", "account.in_memoriam": "In memoriam.", "account.joined_short": "At aderidu", diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json index 28dac9c2a..0378cd292 100644 --- a/app/javascript/mastodon/locales/sco.json +++ b/app/javascript/mastodon/locales/sco.json @@ -35,7 +35,6 @@ "account.following": "Follaein", "account.following_counter": "{count, plural, one {{counter} Follaein} other {{counter} Follaein}}", "account.follows.empty": "This uiser disnae follae oniebody yit.", - "account.follows_you": "Follaes ye", "account.go_to_profile": "Gang tae profile", "account.hide_reblogs": "Dinnae shaw heezes fae @{name}", "account.joined_short": "Jynt", diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json index 835f699b8..c2d2a41cc 100644 --- a/app/javascript/mastodon/locales/si.json +++ b/app/javascript/mastodon/locales/si.json @@ -26,7 +26,6 @@ "account.following": "අනුගමන", "account.following_counter": "{count, plural, one {අනුගමන {counter}} other {අනුගමන {counter}}}", "account.follows.empty": "තවමත් කිසිවෙක් අනුගමනය නොකරයි.", - "account.follows_you": "ඔබව අනුගමනය කරයි", "account.go_to_profile": "පැතිකඩට යන්න", "account.joined_short": "එක් වූ දිනය", "account.link_verified_on": "මෙම සබැඳියේ අයිතිය {date} දී පරීක්ෂා කෙරිණි", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 0eb404198..02425d73e 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -38,7 +38,6 @@ "account.following": "Sledujem", "account.following_counter": "{count, plural, one {{counter} Sledovaných} other {{counter} Sledujúcich}}", "account.follows.empty": "Tento používateľ ešte nikoho nesleduje.", - "account.follows_you": "Sleduje ťa", "account.go_to_profile": "Prejdi na profil", "account.hide_reblogs": "Skry zdieľania od @{name}", "account.in_memoriam": "In Memoriam.", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index 4ef2681f9..b3998b911 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Ni objav", "account.featured_tags.title": "Izpostavljeni ključniki {name}", "account.follow": "Sledi", + "account.follow_back": "Sledi nazaj", "account.followers": "Sledilci", "account.followers.empty": "Nihče ne sledi temu uporabniku.", "account.followers_counter": "{count, plural, one {ima {counter} sledilca} two {ima {counter} sledilca} few {ima {counter} sledilce} other {ima {counter} sledilcev}}", "account.following": "Sledim", "account.following_counter": "{count, plural, one {sledi {count} osebi} two {sledi {count} osebama} few {sledi {count} osebam} other {sledi {count} osebam}}", "account.follows.empty": "Ta uporabnik še ne sledi nikomur.", - "account.follows_you": "Vam sledi", "account.go_to_profile": "Pojdi na profil", "account.hide_reblogs": "Skrij izpostavitve od @{name}", "account.in_memoriam": "V spomin.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Utišaj obvestila", "account.mute_short": "Utišaj", "account.muted": "Utišan", + "account.mutual": "Vzajemno", "account.no_bio": "Ni opisa.", "account.open_original_page": "Odpri izvirno stran", "account.posts": "Objave", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 710224e1c..48eac1487 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -38,7 +38,6 @@ "account.following": "Ndjekje", "account.following_counter": "{count, plural, one {{counter} i Ndjekur} other {{counter} të Ndjekur}}", "account.follows.empty": "Ky përdorues ende s’ndjek kënd.", - "account.follows_you": "Ju ndjek", "account.go_to_profile": "Kalo te profili", "account.hide_reblogs": "Fshih përforcime nga @{name}", "account.in_memoriam": "In Memoriam.", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index 35bb8f992..59ad0ae84 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -38,7 +38,6 @@ "account.following": "Prati", "account.following_counter": "{count, plural, one {{counter} prati} few {{counter} prati} other {{counter} prati}}", "account.follows.empty": "Ovaj korisnik još uvek nikog ne prati.", - "account.follows_you": "Prati vas", "account.go_to_profile": "Idi na profil", "account.hide_reblogs": "Sakrij podržavanja @{name}", "account.in_memoriam": "U znak sećanja na.", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index 552c04d13..79786b8d4 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -38,7 +38,6 @@ "account.following": "Прати", "account.following_counter": "{count, plural, one {{counter} прати} few {{counter} прати} other {{counter} прати}}", "account.follows.empty": "Овај корисник још увек никог не прати.", - "account.follows_you": "Прати вас", "account.go_to_profile": "Иди на профил", "account.hide_reblogs": "Сакриј подржавања од @{name}", "account.in_memoriam": "У знак сећања на.", @@ -53,6 +52,7 @@ "account.mute_notifications_short": "Искључи обавештења", "account.mute_short": "Искључи", "account.muted": "Игнорисан", + "account.mutual": "Заједнички", "account.no_bio": "Нема описа.", "account.open_original_page": "Отвори оригиналну страницу", "account.posts": "Објаве", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 8a07da72d..d3ca776bd 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Inga inlägg", "account.featured_tags.title": "{name}s utvalda hashtaggar", "account.follow": "Följ", + "account.follow_back": "Följ tillbaka", "account.followers": "Följare", "account.followers.empty": "Ingen följer denna användare än.", "account.followers_counter": "{count, plural, one {{counter} följare} other {{counter} följare}}", "account.following": "Följer", "account.following_counter": "{count, plural, one {{counter} följd} other {{counter} följda}}", "account.follows.empty": "Denna användare följer inte någon än.", - "account.follows_you": "Följer dig", "account.go_to_profile": "Gå till profilen", "account.hide_reblogs": "Dölj boostar från @{name}", "account.in_memoriam": "Till minne av.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Stäng av aviseringsljud", "account.mute_short": "Tysta", "account.muted": "Tystad", + "account.mutual": "Ömsesidig", "account.no_bio": "Ingen beskrivning angiven.", "account.open_original_page": "Öppna den ursprungliga sidan", "account.posts": "Inlägg", diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json index ce9042e62..6b2332d5b 100644 --- a/app/javascript/mastodon/locales/ta.json +++ b/app/javascript/mastodon/locales/ta.json @@ -25,7 +25,6 @@ "account.following": "பின்தொடரும்", "account.following_counter": "{count, plural,one {{counter} சந்தா} other {{counter} சந்தாக்கள்}}", "account.follows.empty": "இந்த பயனர் இதுவரை யாரையும் பின்தொடரவில்லை.", - "account.follows_you": "உங்களைப் பின்தொடர்கிறார்", "account.hide_reblogs": "இருந்து ஊக்கியாக மறை @{name}", "account.link_verified_on": "இந்த இணைப்பை உரிமையாளர் சரிபார்க்கப்பட்டது {date}", "account.locked_info": "இந்தக் கணக்கு தனியுரிமை நிலை பூட்டப்பட்டுள்ளது. அவர்களைப் பின்தொடர்பவர் யார் என்பதை உரிமையாளர் கைமுறையாக மதிப்பாய்வு செய்கிறார்.", diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json index f21c0ef57..3c231871f 100644 --- a/app/javascript/mastodon/locales/te.json +++ b/app/javascript/mastodon/locales/te.json @@ -12,7 +12,6 @@ "account.followers": "అనుచరులు", "account.followers.empty": "ఈ వినియోగదారుడిని ఇంకా ఎవరూ అనుసరించడంలేదు.", "account.follows.empty": "ఈ వినియోగదారి ఇంకా ఎవరినీ అనుసరించడంలేదు.", - "account.follows_you": "మిమ్మల్ని అనుసరిస్తున్నారు", "account.hide_reblogs": "@{name} నుంచి బూస్ట్ లను దాచిపెట్టు", "account.link_verified_on": "ఈ లంకె యొక్క యాజమాన్యం {date}న పరీక్షించబడింది", "account.locked_info": "ఈ ఖాతా యొక్క గోప్యత స్థితి లాక్ చేయబడి వుంది. ఈ ఖాతాను ఎవరు అనుసరించవచ్చో యజమానే నిర్ణయం తీసుకుంటారు.", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 67b920393..d14e37517 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "ไม่มีโพสต์", "account.featured_tags.title": "แฮชแท็กที่น่าสนใจของ {name}", "account.follow": "ติดตาม", + "account.follow_back": "ติดตามกลับ", "account.followers": "ผู้ติดตาม", "account.followers.empty": "ยังไม่มีใครติดตามผู้ใช้นี้", "account.followers_counter": "{count, plural, other {{counter} ผู้ติดตาม}}", "account.following": "กำลังติดตาม", "account.following_counter": "{count, plural, other {{counter} กำลังติดตาม}}", "account.follows.empty": "ผู้ใช้นี้ยังไม่ได้ติดตามใคร", - "account.follows_you": "ติดตามคุณ", "account.go_to_profile": "ไปยังโปรไฟล์", "account.hide_reblogs": "ซ่อนการดันจาก @{name}", "account.in_memoriam": "เพื่อระลึกถึง", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index e6389b05a..e85db817b 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "Gönderi yok", "account.featured_tags.title": "{name} kişisinin öne çıkan etiketleri", "account.follow": "Takip et", + "account.follow_back": "Geri takip et", "account.followers": "Takipçi", "account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.", "account.followers_counter": "{count, plural, one {{counter} Takipçi} other {{counter} Takipçi}}", "account.following": "Takip Ediliyor", "account.following_counter": "{count, plural, one {{counter} Takip Edilen} other {{counter} Takip Edilen}}", "account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.", - "account.follows_you": "Seni takip ediyor", "account.go_to_profile": "Profile git", "account.hide_reblogs": "@{name} kişisinin boostlarını gizle", "account.in_memoriam": "Hatırasına.", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "Bildirimleri sessize al", "account.mute_short": "Sessize al", "account.muted": "Susturuldu", + "account.mutual": "Karşılıklı", "account.no_bio": "Herhangi bir açıklama belirtilmedi.", "account.open_original_page": "Asıl sayfayı aç", "account.posts": "Gönderiler", diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json index 6727f3e59..47fe60bd2 100644 --- a/app/javascript/mastodon/locales/tt.json +++ b/app/javascript/mastodon/locales/tt.json @@ -35,7 +35,6 @@ "account.following": "Язылулар", "account.following_counter": "{count, plural, one {{counter} язылу} other {{counter} язылу}}", "account.follows.empty": "Беркемгә дә язылмаган әле.", - "account.follows_you": "Сезгә язылган", "account.go_to_profile": "Профильгә күчү", "account.hide_reblogs": "Скрывать көчен нче @{name}", "account.in_memoriam": "Истәлегенә.", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 58a14c0ed..92eacaad1 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -38,7 +38,6 @@ "account.following": "Ви стежите", "account.following_counter": "{count, plural, one {{counter} підписка} few {{counter} підписки} many {{counter} підписок} other {{counter} підписки}}", "account.follows.empty": "Цей користувач ще ні на кого не підписався.", - "account.follows_you": "Підписується на вас", "account.go_to_profile": "Перейти до профілю", "account.hide_reblogs": "Сховати поширення від @{name}", "account.in_memoriam": "Пам'ятник.", @@ -53,6 +52,7 @@ "account.mute_notifications_short": "Не сповіщати", "account.mute_short": "Ігнорувати", "account.muted": "Приховується", + "account.mutual": "Взаємно", "account.no_bio": "Немає опису.", "account.open_original_page": "Відкрити оригінальну сторінку", "account.posts": "Дописи", diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json index 8fc3aff03..563b2dedf 100644 --- a/app/javascript/mastodon/locales/ur.json +++ b/app/javascript/mastodon/locales/ur.json @@ -32,7 +32,6 @@ "account.following": "فالو کر رہے ہیں", "account.following_counter": "{count, plural, one {{counter} پیروی کر رہے ہیں} other {{counter} پیروی کر رہے ہیں}}", "account.follows.empty": "\"یہ صارف ہنوز کسی کی پیروی نہیں کرتا ہے\".", - "account.follows_you": "آپ کا پیروکار ہے", "account.go_to_profile": "پروفائل پر جائیں", "account.hide_reblogs": "@{name} سے فروغ چھپائیں", "account.in_memoriam": "یادگار میں۔", diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json index 026cc115c..8eeee42a5 100644 --- a/app/javascript/mastodon/locales/uz.json +++ b/app/javascript/mastodon/locales/uz.json @@ -35,7 +35,6 @@ "account.following": "Kuzatish", "account.following_counter": "{count, plural, one {{counter} ga Muxlis} other {{counter} larga muxlis}}", "account.follows.empty": "Bu foydalanuvchi hali hech kimni kuzatmagan.", - "account.follows_you": "Sizga obuna", "account.go_to_profile": "Profilga o'tish", "account.hide_reblogs": "@{name} dan boostlarni yashirish", "account.joined_short": "Qo'shilgan", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 9ac90b407..721c7cd4a 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -38,7 +38,6 @@ "account.following": "Đang theo dõi", "account.following_counter": "{count, plural, one {{counter} Theo dõi} other {{counter} Theo dõi}}", "account.follows.empty": "Người này chưa theo dõi ai.", - "account.follows_you": "Đang theo dõi bạn", "account.go_to_profile": "Xem hồ sơ", "account.hide_reblogs": "Ẩn tút @{name} đăng lại", "account.in_memoriam": "Tưởng Niệm.", diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json index 5896a25b0..008a9636d 100644 --- a/app/javascript/mastodon/locales/zgh.json +++ b/app/javascript/mastodon/locales/zgh.json @@ -12,7 +12,6 @@ "account.edit_profile": "ⵙⵏⴼⵍ ⵉⴼⵔⵙ", "account.follow": "ⴹⴼⵕ", "account.followers": "ⵉⵎⴹⴼⴰⵕⵏ", - "account.follows_you": "ⴹⴼⵕⵏ ⴽⵯⵏ", "account.media": "ⴰⵙⵏⵖⵎⵉⵙ", "account.mute": "ⵥⵥⵉⵥⵏ @{name}", "account.muted": "ⵉⵜⵜⵓⵥⵉⵥⵏ", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 1678fd729..575e0c7ae 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "暂无嘟文", "account.featured_tags.title": "{name} 的精选标签", "account.follow": "关注", + "account.follow_back": "回关", "account.followers": "关注者", "account.followers.empty": "目前无人关注此用户。", "account.followers_counter": "被 {counter} 人关注", "account.following": "正在关注", "account.following_counter": "正在关注 {counter} 人", "account.follows.empty": "此用户目前未关注任何人。", - "account.follows_you": "关注了你", "account.go_to_profile": "前往个人资料页", "account.hide_reblogs": "隐藏来自 @{name} 的转嘟", "account.in_memoriam": "谨此悼念。", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "关闭通知", "account.mute_short": "隐藏", "account.muted": "已隐藏", + "account.mutual": "互相关注", "account.no_bio": "未提供描述。", "account.open_original_page": "打开原始页面", "account.posts": "嘟文", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 263c70799..cd0845b6e 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "暫無文章", "account.featured_tags.title": "{name} 的精選標籤", "account.follow": "關注", + "account.follow_back": "追蹤對方", "account.followers": "追蹤者", "account.followers.empty": "尚未有人追蹤這位使用者。", "account.followers_counter": "有 {count, plural,one {{counter} 個} other {{counter} 個}}追蹤者", "account.following": "正在追蹤", "account.following_counter": "正在追蹤 {count, plural,one {{counter}}other {{counter} 人}}", "account.follows.empty": "這位使用者尚未追蹤任何人。", - "account.follows_you": "追蹤你", "account.go_to_profile": "前往個人檔案", "account.hide_reblogs": "隱藏 @{name} 的轉推", "account.in_memoriam": "謹此悼念。", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "靜音通知", "account.mute_short": "靜音", "account.muted": "靜音", + "account.mutual": "互相追蹤", "account.no_bio": "未提供描述。", "account.open_original_page": "打開原始頁面", "account.posts": "帖文", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 8484e00c8..998393695 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -32,13 +32,13 @@ "account.featured_tags.last_status_never": "沒有嘟文", "account.featured_tags.title": "{name} 的推薦主題標籤", "account.follow": "跟隨", + "account.follow_back": "跟隨回去", "account.followers": "跟隨者", "account.followers.empty": "尚未有人跟隨這位使用者。", "account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨", "account.following": "跟隨中", "account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}", "account.follows.empty": "這位使用者尚未跟隨任何人。", - "account.follows_you": "跟隨了您", "account.go_to_profile": "前往個人檔案", "account.hide_reblogs": "隱藏來自 @{name} 的轉嘟", "account.in_memoriam": "謹此悼念。", @@ -53,6 +53,7 @@ "account.mute_notifications_short": "靜音推播通知", "account.mute_short": "靜音", "account.muted": "已靜音", + "account.mutual": "互相跟隨", "account.no_bio": "無個人檔案描述", "account.open_original_page": "檢視原始頁面", "account.posts": "嘟文", diff --git a/config/locales/ie.yml b/config/locales/ie.yml index c9eef7520..6013923e3 100644 --- a/config/locales/ie.yml +++ b/config/locales/ie.yml @@ -485,8 +485,16 @@ ie: instance_followers_measure: nor sequitores ta instance_follows_measure: lor sequitores ci instance_languages_dimension: Max grand lingues + instance_statuses_measure: salvat postas delivery: + all: Omni + clear: Aclarar errores de liveration + failing: Fallint + restart: Recomensar liveration + stop: Haltar liveration unavailable: Índisponibil + delivery_available: Liveration es disponibil + delivery_error_days: Dies de errores de liveration delivery_error_hint: Si liveration ne es possibil durant %{count} dies, it va esser marcat automaticmen quam ínliverabil. destroyed_msg: Data de %{domain} es nu in li linea por iminent deletion. empty: Null dominias trovat. @@ -564,6 +572,9 @@ ie: other_description_html: Vider plu optiones por controlar li conduida del conto e customisar comunication al raportat conto. resolve_description_html: Null action va esser fat contra li raportat conto, null admoniment registrat, e li raporte va esser cludet. silence_description_html: Li conto va esser visibil nur a tis qui ja seque it o qui sercha it manualmen, limitante severimen su atingement. Ti sempre posse esser revertet. Ti clude omni raportes contra ti conto. + suspend_description_html: Ti-ci conto e omni contenete va esser ínaccessibil e finalmen deletet, e interacter con it va esser ínpossibil. Reversibil til que 30 dies ha passat. Clude omni raportes contra ti conto. + actions_description_html: Decider quel action a far por soluer ti raporte. Si tu fa un punitiv action contra li raportat conto, li usator va reciver un notification per email, except in li casu que li <strong>Spam</strong> categorie es selectet. + actions_description_remote_html: Decider quel action a far por soluer ti raporte. It va afecter solmen qualmen <strong>vor</strong> servitor comunica con ti lontan conto e tractar su contenete. add_to_report: Adjunter plu al raporte are_you_sure: Es tu cert? assign_to_self: Assignar it a me @@ -579,6 +590,9 @@ ie: confirm_action: Confirmar moderatori action contra @%{acct} created_at: Raportat delete_and_resolve: Deleter postas + forwarded: Reinviat + forwarded_replies_explanation: Ti-ci raporte es de un lontan usator e pri lontan contenete. It ha esset reinviat a vos pro que li raportat contenete es un response a un de tui usatores. + forwarded_to: Misset anc a %{domain} mark_as_resolved: Marcar quam soluet mark_as_sensitive: Marcar quam sensitiv mark_as_unresolved: Marcar quam ínsoluet @@ -603,13 +617,26 @@ ie: skip_to_actions: Ear rect al actiones status: Statu statuses: Contenete raportat + statuses_description_html: Ofensiv contenete va esser citat in comunication con li raportat conto summary: action_preambles: delete_html: 'Tu va <strong>remover</strong> alcun postas de <strong>@%{acct}</strong>''. To va:' mark_as_sensitive_html: 'Tu va <strong>marcar</strong> alcun postas de <strong>@%{acct}</strong> quam <strong>sensitiv</strong>. To va:' silence_html: 'Tu va <strong>limitar</strong> li conto de <strong>@%{acct}</strong>. To va:' suspend_html: 'Tu va <strong>suspender</strong> li conto de <strong>@%{acct}</strong>. To va:' + actions: + delete_html: Remover li ofendent postas + mark_as_sensitive_html: Marcar li medie del ofendent postas quam sensitiv + silence_html: Severimen limitar li atingement de <strong>@%{acct}</strong> per far su profil e contenete visibil solmen a persones qui ja seque ilu o qui sercha su profil manualmen + suspend_html: Suspender <strong>@%{acct}</strong>, fante su profil e contenete ínaccessibil e ínpossibil con quel interacter close_report: 'Marcar raporte #%{id} quam resoluet' + close_reports_html: Marcar <strong>omni</strong> raportes contra <strong>@%{acct}</strong> quam soluet + delete_data_html: Deleter li profil e contenete de <strong>@%{acct}</strong> 30 dies pos nu, except si ilu es dessuspendet ante tande + preview_preamble_html: "<strong>@%{acct}</strong> va reciver un admoniment con li sequent contenete:" + record_strike_html: Registrar un admoniment contra <strong>@%{acct}</strong> por auxiliar vos tractar futur violationes de ti-ci conto + send_email_html: Misser un admoniment-email a <strong>@%{acct}</strong> + warning_placeholder: Ínobligatori additional rason por li moderatori action. + target_origin: Orígine del conto raportat title: Raportes unassign: Ínassignar unknown_action_msg: 'Ínconosset action: %{action}' @@ -639,21 +666,29 @@ ie: delete_user_data: Deleter Data de Usator delete_user_data_description: Possibilisa que usatores mey deleter li data de altri usatores strax invite_users: Invitar Usatores + invite_users_description: Permisse que usatores invita novones al servitor manage_announcements: Tractar proclamationes manage_announcements_description: Permisse usatores tractar proclamationes sur li servitor manage_appeals: Gerer Apelles manage_blocks: Gerer Bloccas + manage_blocks_description: Permisse que usatores blocca provisores de e-posta e IP-adresses manage_custom_emojis: Gerer Customisat Emojis + manage_custom_emojis_description: Permisse que usatores gere customisat emojis sur li servitor manage_federation: Gerer Federation + manage_federation_description: Permisse que usatores sive blocca sive permisse federation con altri domenes, e controla liverabilitá manage_invites: Gerer Invitationes manage_reports: Gerer Raportes manage_roles: Gerer Roles manage_rules: Gerer Regules + manage_rules_description: Permisse que usatores changea regules del servitor manage_settings: Gerer Parametres + manage_settings_description: Permisse que usatores changea parametres del situ manage_taxonomies: Gerer Taxonomies + manage_user_access: Gerer Usator-Accesse manage_user_access_description: Permisse usatores desactivisar li 2-factor autentication de altri usatores, changear lor email-adresses, e reiniciar lor passa-paroles manage_users: Gerer usatores manage_webhooks: Gerer Web-crocs + view_audit_log_description: Permisse que usatores vide li historie de administrativ actiones sur li servitor view_devops: DevOps title: Roles rules: @@ -667,43 +702,76 @@ ie: manage_rules: Gerer regules de servitor title: Pri appearance: + preamble: Customisar li interfacie web de Mastodon. title: Aspecte discovery: + follow_recommendations: Seque-recomandationes + preamble: Exposir interessant contenete es importantissim por incorporar nov usatores qui fórsan conosse nequi che Mastodon. Decider qualmen diferent utensiles de decovrition functiona che vor servitor. profile_directory: Profilarium public_timelines: Public témpor-lineas + publish_discovered_servers: Publicar decovrit servitores + publish_statistics: Publicar statisticas title: Decovriment + trends: Tendenties domain_blocks: all: Ad omnes disabled: A necun users: A local usatores qui ha initiat session registrations: + preamble: Decider qui posse crear un conto che vor servitor. title: Registrationes registrations_mode: modes: + approved: Aprobation besonat por adhesion none: Nequi posse registrar se open: Quicunc posse registrar se + security: + authorized_fetch: Postular autentication de federat servitores + authorized_fetch_hint: Postular autentication de federat servitores possibilisa plu strict infortiament de ambi usatori e servitori bloccas. Támen, ti fórsan va limitar li potentie de vor servitor, reducter li atingement de vor responses, e possibilmen introducter problemas de compatibilitá con quelc federat servicies. Additionalmen, ti ne va preventer dedicat actores de accesser vor public postas e contos. + authorized_fetch_overridden_hint: Tu actualmen ne posse changear ti parametre pro que it es controlat de un environmental variabile. + federation_authentication: Infortiation de federational autentication title: Parametres del servitor site_uploads: delete: Deleter cargat file destroyed_msg: Cargat file successosimen deletet! software_updates: + critical_update: Critic — ples actualisar rapidmen + description: On recomanda que vu actualisa vor Mastodon-servitor regularimen por profiter del max recent fixes e facultates. In plu, quelcvez it es critic actualisar Mastodon promptmen por evitar problemas de securitá. Pro ti rasones, Mastodon questiona chascun 30 minutes ca hay actualisationes, e va notificar vos secun vor parametres pri email-notificationes. + documentation_link: Aprender plu + title: Actualisationes disponibil type: Specie version: Version statuses: account: Autor + application: Aplication + back_to_account: Retornar al págine del conto + back_to_report: Retornar al págine del raporte batch: remove_from_report: Remover de raporte report: Raportar deleted: Deletet favourites: Favorites + history: Historie de versiones in_reply_to: Respondent a language: Lingue + media: + title: Medie metadata: Metadata + no_status_selected: Null postas esset changeat pro que null esset selectet open: Aperter posta original_status: Original posta + reblogs: Boosts + status_changed: Posta modificat + title: Postas del conto + trending: Populari visibility: Visibilitá + with_media: Con medie strikes: actions: + delete_statuses: "%{name} deletet li postas de %{target}" + disable: "%{name} gelat li conto de %{target}" + mark_statuses_as_sensitive: "%{name} marcat li postas de %{target} quam sensitiv" + none: "%{name} misset un admoniment a %{target}" sensitive: "%{name} marcat li conto de %{target} quam sensitiv" silence: "%{name} limitat li conto de %{target}" suspend: "%{name} suspendet li conto de %{target}" @@ -717,20 +785,81 @@ ie: action: Vider li documentation elasticsearch_preset_single_node: action: Vider li documentation + rules_check: + action: Gerer regules de servitor + software_version_critical_check: + message_html: Un critical actualisation por Mastodon es disposibil, ples actualisar tam rapidmen possibil. + software_version_patch_check: + action: Vider actualisationes disponibil title: Administration trends: + allow: Permisser approved: Aprobat + disallow: Despermisser + links: + allow: Permisser ligament + allow_provider: Permisser editor + description_html: Ci hay ligamentes actualmen partit per mult contos sur queles tui servitor vide postas. It posse auxiliar tui usatores saver to quo eveni in li munde. Null ligamentes es monstrat publicmen ante que tu aproba li publicator. Tu posse anc aprobar o rejecter índividual ligamentes. + disallow: Despermisser ligament + disallow_provider: Despermisser editor + no_link_selected: Null ligamentes esset changeat pro que null esset selectet + publishers: + no_publisher_selected: Null editores esset changeat pro que necun esset selectet + title: Ligamentes in tendentie + usage_comparison: Partit %{today} vezes hodie, in comparation a %{yesterday} yer + not_allowed_to_trend: Ne permisset esser in tendentie + pending_review: Sub inspection + preview_card_providers: + allowed: Ligamentes de ti-ci editor posse esser in tendentie + description_html: Tis es dominias de queles ligamentes es sovente distribuet che vor servitor. Ligamentes ne va intrar li tendenties publicmen except si li dominia del ligament es aprobat. Vor aprobation (o rejection) aplica anc a subdominias. + rejected: Ligamentes de ti publicator ne va intrar li tendenties + title: Editores + rejected: Rejectet statuses: + allow: Permisser posta + allow_account: Permisser autor + description_html: Tis es postas queles vor servitor conosse queles on actualmen distribue e favoritisa mult. It posse auxiliar vor nov e retornant usatores por trovar plu persones por sequer. Null postas es monstrat publicmen til que vu aproba li autor, e li autor permisse que su conto es suggestet a altres. Vu anc posse permisser o rejecter individual postas. + disallow: Despermisser posta + disallow_account: Despermisser autor + no_status_selected: Null populari postas esset changeat pro que null esset selectet + not_discoverable: Autor ne ha consentit esser decovribil shared_by: one: Partit o favoritisat un vez other: Partit e favoritisat %{friendly_count} vezes + title: Populari postas tags: + current_score: Actual puntes %{score} + dashboard: + tag_accounts_measure: unic usationes + tag_languages_dimension: Max usat lingues + tag_servers_dimension: Max populari servitores + tag_servers_measure: diferent servitores + tag_uses_measure: total usationes + description_html: Vi hashtags actualmen aparient in mult postas queles vor servitor vide. It posse auxiliar vor usatores decovrir pri quel gente parla nu. Null hashtags es monstrat til que vu aproba les. listable: Suggestibil + no_tag_selected: Null hashtags esset changeat pro que null esset selectet not_listable: Ne suggestibil + not_trendable: Ne va aparir in tendenties not_usable: Prohibit + peaked_on_and_decaying: Atinget su cime ye %{date}, nu deperient + title: Populari hashtags + trendable: Posse aparir in tendenties + trending_rank: 'Tendentie #%{rank}' usable: Permisset usage_comparison: 'Usat hodie: %{today} vez(es), yer: %{yesterday}' + used_by_over_week: + one: Usat de un person durant li ultim semane + other: Usat de %{count} persones durant li ultim semane + title: Tendenties + trending: Populari + warning_presets: + add_new: Adjunter un nov + delete: Deleter + edit_preset: Modificar prefiguration de avise + empty: Vu ancor ha definit null prefigurationes de avise. + title: Modificar prefigurationes de avise webhooks: + enabled: Activ events: Evenimentes status: Statu admin_mailer: @@ -741,6 +870,7 @@ ie: title: Populari postas new_trending_tags: title: Populari hashtags + subject: Nov tendenties a inspecter sur %{instance} aliases: add_new: Crear alias created_msg: Successosimen creat un nov alias. Tu nu posse initiar li movement del antiqui conto. @@ -1153,6 +1283,7 @@ ie: validations: images_and_video: On ne posse atachar un video a un posta quel ja contene images migrations: + acct: Translocat a set_redirect: Configurar un redirection move_handler: carry_mutes_over_text: Ti-ci usator movet se de %{acct}, quel tu hat silentiat. @@ -1180,6 +1311,13 @@ ie: email_events: Evenimentes por email-notificationes email_events_hint: 'Selecte li evenimentes pri queles tu vole reciver notificationes:' other_settings: Parametres pri altri notificationes + number: + human: + decimal_units: + units: + billion: B + million: M + thousand: m otp_authentication: code_hint: Inmetter li code generat de tui aplication de autentication por confirmar description_html: Si tu activisa <strong>2-factor autentication</strong> per un aplication de autentication, aperter un session va postular que tu have possession de tui telefon, quel va generar codes por que tu mey inmetter les. @@ -1340,6 +1478,7 @@ ie: boosted_from_html: Boostat de %{acct_link} default_language: Sam quam li lingue del interfacie edited_at_html: Modificat ye %{date} + open_in_web: Aperter in web pin_errors: direct: On ne posse pinglar postas queles es visibil solmen a mentionat usatores limit: Tu ja ha pinglat li maxim númere de postas @@ -1353,16 +1492,30 @@ ie: one: "%{count} vote" other: "%{count} votes" vote: Votar + show_more: Monstrar plu + show_newer: Monstrar plu nov + show_older: Monstrar plu old title: "%{name}: «%{quote}»" visibilities: + private_long: Monstrar solmen a sequitores public: Public + public_long: Omnes posse vider unlisted: Delistat + unlisted_long: Omnes posse vider, ma ne listat sur public témpor-lineas statuses_cleanup: + enabled: Automaticmen deleter old postas + enabled_hint: Deleter automaticmen tui postas quande ili atinge un cert etá, except si ili concorda con un del exceptiones ci infra exceptions: Exceptiones + explanation: Deletion de postas es un operation expensiv, e pro to es efectuat lentmen quande li servitor ne es ocupat. Pro to, on posse deleter tui postas un cert témpor pos atinger un cert etá. + ignore_favs: Ignorar favorites + interaction_exceptions: Exceptiones basat sur interactiones + keep_direct: Retener missages direct + keep_direct_hint: Ne delete quelcunc de tui direct missages keep_pinned: Conservar pinglat postas keep_pinned_hint: Delete null de tui pinglat postas keep_polls: Conservar balotationes keep_polls_hint: Delete null de tui balotationes + keep_self_fav: Retener postas favorit de te min_age: '1209600': 2 semanes '31556952': 1 annu @@ -1442,11 +1595,28 @@ ie: mark_statuses_as_sensitive: Tui postas che %{acct} ha esset marcat quam sensitiv none: Admoniment por %{acct} sensitive: Tui postas che %{acct} ve esser marcat quam sensitiv pos nu + title: + delete_statuses: Postas efaciat + sensitive: Conto marcat quam sensitiv + silence: Conto limitat + suspend: Conto suspendet welcome: + edit_profile_action: Configuration de profil explanation: Vi quelc suggestiones por que tu mey comensar + final_action: Comensar postar subject: Benevenit a Mastodon + title: Benevenit, %{name}! users: seamless_external_login: Tu ha intrat per un servicie external, dunc parametres pri tui passa-parol e email-adresse ne es disponibil. verification: extra_instructions_html: '<strong>Nota</strong>: Li ligament in tui websitu posse esser ínvisibil. Li important parte es <code>rel="me"</code> quel prevente fals self-identification in websitus con contenete generat de usatores. Tu posse mem usar un <code>link</code> element in li cap-section del págine vice <code>a</code>, ma li HTML code deve esser accessibil sin executer JavaScript.' + here_is_how: Vide qualmen verification: Verification + verified_links: Tui verificat ligamentes + webauthn_credentials: + add: Adjunter nov clave de securitá + create: + error: Un problema evenit durant li adjuntion de tui clave de securitá. Ples provar denov. + success: Tui clave de securitá esset adjuntet con successe. + delete: Deleter + delete_confirmation: Vole tu vermen deleter ti-ci clave de securitá? diff --git a/config/locales/lad.yml b/config/locales/lad.yml index 36364feba..471b830a3 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -718,6 +718,7 @@ lad: manage_rules: Administra reglas del sirvidor title: Sovre esto appearance: + preamble: Personaliza la enterfaz web de Mastodon. title: Aparensya branding: title: Marka @@ -753,6 +754,8 @@ lad: release_notes: Notas sovre la versyon title: Aktualizasyones desponivles type: Tipo + types: + major: Versyon prinsipala version: Versyon statuses: account: Autor @@ -976,6 +979,7 @@ lad: awaiting_review_title: Estamos revizando tu enrejistramiento clicking_this_link: klikando en este atadijo login_link: konektate kon kuento + proceed_to_login_html: Agora puedes ir a %{login_link}. welcome_title: Bienvenido, %{name}! wrong_email_hint: Si este adreso de posta es inkorekto, puedes trokarlo en las preferensyas del kuento. delete_account: Efasa kuento @@ -1064,12 +1068,15 @@ lad: x_seconds: "%{count} s" deletes: proceed: Efasa kuento + success_msg: Tu kuento fue efasado kon reusho warning: + before: 'Antes de kontinuar, por favor melda kon atensyon las sigientes notas:' username_unavailable: Tu nombre de utilizador no estara desponivle disputes: strikes: action_taken: Aksyon tomada appeal: Apela + appeal_rejected: La apelasyon fue refuzada appeal_submitted_at: Apelasyon embiada appealed_msg: Tu apelasyon fue embiada. Si la achetamos, se te avizara. appeals: @@ -1134,6 +1141,8 @@ lad: storage: Magazinaje de multimedia featured_tags: add_new: Adjusta muevo + errors: + limit: Tienes alkansado el karar maksimo de etiketas filters: contexts: account: Profiles @@ -1147,8 +1156,11 @@ lad: statuses: Publikasyones individualas title: Edita filtro index: + contexts: Filtros en %{contexts} delete: Efasa empty: No tyenes filtros. + expires_in: Kaduka en %{distance} + expires_on: Kaduka en %{date} keywords: one: "%{count} biervo yave" other: "%{count} biervos yave" @@ -1177,6 +1189,7 @@ lad: delete: Efasa deselect: Deseleksyonar todo none: Dinguno + order_by: Ordena por save_changes: Guadra trokamientos today: oy imports: @@ -1201,6 +1214,7 @@ lad: success: Tus datos se tienen kargado djustamente i seran prosesados pishin time_started: Ampesado el titles: + blocking: Importando kuentos blokados bookmarks: Importando markadores domain_blocking: Importando domenos blokados following: Importando kuentos segidos @@ -1229,6 +1243,7 @@ lad: '604800': 1 semana '86400': 1 diya expires_in_prompt: Nunkua + generate: Djenera un atadijo de envitasyon invalid: Esta envitasyon no es valida invited_by: 'Fuites envitado por:' max_uses: @@ -1255,7 +1270,15 @@ lad: title: Estoria de autentifikasyon mail_subscriptions: unsubscribe: + action: Si, dezabona complete: Dezabonado + emails: + notification_emails: + favourite: avizos de favoritos por posta + follow: avizos de segidores por posta + follow_request: avizos de solisitasyones de segimyento por posta + mention: avizos de enmentaduras por posta + reblog: avizos de repartajasyones por posta title: Dezabona media_attachments: validations: @@ -1295,15 +1318,21 @@ lad: title: Moderasyon notification_mailer: admin: + report: + subject: "%{name} embio un raporto" sign_up: subject: "%{name} se enrejistro" favourite: + body: 'Tu publikasyon fue favoritada por %{name}:' + subject: A %{name} le plaze tu publikasyon title: Muevo favorito follow: body: "%{name} te esta sigiendo!" subject: "%{name} te esta sigiendo" title: Muevo suivante follow_request: + action: Administra solisitudes de segimiento + body: "%{name} tiene solisitado segirte" subject: 'Segidor esta asperando: %{name}' title: Mueva solisitud de segimiento mention: @@ -1311,13 +1340,19 @@ lad: body: 'Fuites enmentado por %{name} en:' subject: Fuites enmentado por %{name} title: Mueva enmentadura + poll: + subject: Una anketa de %{name} eskapo reblog: + body: 'Tu publikasyon fue repartajada por %{name}:' + subject: "%{name} repartajo tu publikasyon" title: Mueva repartajasyon status: subject: "%{name} publiko algo" update: subject: "%{name} edito una publikasyon" notifications: + administration_emails: Avizos de administrasyon por posta + email_events: Evenimyentos para avizos por posta other_settings: Otras preferensyas de avizos number: human: @@ -1383,9 +1418,16 @@ lad: mutual: Mutual primary: Prinsipal relationship: Relasyon + remove_selected_followers: Kita a los suivantes eskojidos + remove_selected_follows: Desige a los utilizadores eskojidos status: Estado del kuento rss: content_warning: 'Avertensya de kontenido:' + descriptions: + account: Publikasyones publikas de @%{acct} + tag: 'Publikasyones publikas etiketadas kon #%{hashtag}' + self_destruct: + title: Este sirvidor esta serrando sessions: activity: Ultima aktivita browser: Navigador @@ -1410,6 +1452,7 @@ lad: unknown_browser: Navigador deskonosido weibo: Weibo current_session: Sesyon aktuala + description: "%{browser} en %{platform}" ip: IP platforms: adobe_air: Adobe Air @@ -1421,6 +1464,7 @@ lad: kai_os: KaiOS linux: Linux mac: macOS + unknown_platform: Platforma deskonesida windows: Windows windows_mobile: Windows Mobile windows_phone: Windows Phone @@ -1478,6 +1522,7 @@ lad: direct: Las publikasyones ke son vizivles solo para los utilizadores enmentados no pueden fiksarse limit: Ya tienes fiksado el numero maksimo de publikasyones ownership: La publikasyon de otra persona no puede fiksarse + reblog: No se puede fixar una repartajasyon poll: total_people: one: "%{count} persona" @@ -1503,6 +1548,18 @@ lad: exceptions: Eksepsiones ignore_favs: Ignora favoritos ignore_reblogs: Ignora repartajasyones + keep_direct: Manten enmentaduras privadas + keep_direct_hint: No efasa dingunos mesajes privados + keep_media: Manten publikasyones kon atamientos + keep_media_hint: No efasa tus publikasyones kon atamientos de multimedia + keep_pinned: Manten publikasyones fiksadas + keep_pinned_hint: No efasa tus publikasyones fiksadas + keep_polls: Manten anketas + keep_polls_hint: No efasa tus anketas + keep_self_bookmark: Manten publikasyones kon markadores + keep_self_bookmark_hint: No efasa tus publikasyones kon tus markadores + keep_self_fav: Manten publikasyones ke te plazen + keep_self_fav_hint: No efasa las tus publikasyones kualas markates komo favoritas min_age: '1209600': 2 semanas '15778476': 6 mezes @@ -1512,6 +1569,8 @@ lad: '604800': 1 semana '63113904': 2 anyos '7889238': 3 mezes + min_favs: Manten publikasyones favoritadas a lo manko + min_reblogs: Manten publikasyones repartajadas a lo manko stream_entries: sensitive_content: Kontenido sensivle strikes: @@ -1577,18 +1636,30 @@ lad: mark_statuses_as_sensitive: Algunas de tus publikasyones an sido markados komo sensivles por los moderadores de %{instance}. Esto sinyifika ke la djente tendra ke pulsar los dosyas multimedia en las publikasyones antes de ke se amostre una vista previa. Puedes markar los dosyas multimedia komo sensivles tu mezmo kuando publikes en el avenir. sensitive: A partir de agora todas los dosyas multimedia ke subas seran markados komo sensivles i eskondidos tras una avertensya. reason: 'Razon:' + statuses: 'Publikasyones relevantes:' subject: + delete_statuses: Tus publikasyones en %{acct} tienen sido efasadas + disable: Tu kuento %{acct} tiene sido konjelado + mark_statuses_as_sensitive: Tus publikasyones en %{acct} tienen sido markadas komo sensivles none: Avertensya para %{acct} + sensitive: Tus publikasyones en %{acct} seran markadas komo sensivles dizde agora + silence: Tu kuento %{acct} tiene sido limitado + suspend: Tu kuento %{acct} tiene sido suspendido title: delete_statuses: Publikasyones efasadas + disable: Kuento konjelado mark_statuses_as_sensitive: Publikasyones markadas komo sensivles none: Avertensya + sensitive: Kuento markado komo sensivle silence: Kuento limitado suspend: Kuento suspendido welcome: + edit_profile_action: Konfigurasyon de profil final_action: Ampesa a publikar subject: Bienvenido a Mastodon + title: Bienvenido, %{name}! users: + follow_limit_reached: No puedes segir a mas de %{limit} personas invalid_otp_token: Kodiche de dos pasos no valido signed_in_as: 'Konektado komo:' verification: @@ -1596,7 +1667,12 @@ lad: verified_links: Tus atadijos verifikados webauthn_credentials: add: Adjusta mueva yave de sigurita + create: + success: Tu yave de sigurita fue adjustada kon sukseso. delete: Efasa + delete_confirmation: Estas siguro ke keres efasar esta yave de sigurita? + destroy: + success: Tu yave de sigurita fue efasada kon sukseso. invalid_credential: Yave de sigurita no valida nickname_hint: Introduska el sovrenombre de tu mueva yave de sigurita not_enabled: Ainda no tienes aktivado WebAuthn diff --git a/config/locales/simple_form.ie.yml b/config/locales/simple_form.ie.yml index ff1587be8..672ff9d00 100644 --- a/config/locales/simple_form.ie.yml +++ b/config/locales/simple_form.ie.yml @@ -4,6 +4,9 @@ ie: hints: account: unlocked: Persones va posser sequer te sin petir aprobation. Desselecte si tu vole manualmen tractar petitiones de sequer e decider ca acceptar o rejecter nov sequitores. + account_warning_preset: + text: Tu posse usar posta-sintaxe, quam URLs, hashtags e mentiones + title: Ínobligatori. Ne visibil al recipiente admin_account_action: send_email_notification: Li usator va reciver un explication de ti quel evenit con su conto text_html: Ínobligatori. Tu posse usar posta-sintaxe. Tu posse <a href="%{path}">adjunter preconfigurationes de avises</a> por sparar témpor @@ -53,14 +56,25 @@ ie: trends_as_landing_page: Monstrar populari contenete a ínregistrat visitantes vice un description del servitor. Besona que tendenties es activisat. form_challenge: current_password: Tu nu intra un area secur + ip_block: + severities: + sign_up_block: Nov registrationes ne va esser possibil + sign_up_requires_approval: Nov registrationes va besonar tui aprobation + webhook: + events: Selecter evenimentes a misser + url: Ad u misser evenimentes labels: account: fields: value: Contenete + indexable: Includer public postas in resultates de sercha account_warning_preset: + text: Textu prefigurat title: Titul admin_account_action: + send_email_notification: Notificar li usator per e-posta type: Action + warning_preset_id: Usar un prefiguration de avise announcement: all_day: Eveniment del tot die ends_at: Fine del eveniment @@ -95,5 +109,10 @@ ie: form_admin_settings: registrations_mode: Qui posse registrar se theme: Predefenit tema + trends: Possibilisar tendenties + trends_as_landing_page: Usar tendenties quam frontispicie notification_emails: follow_request: Alqui petit sequer te + trending_tag: Nov tendentie besonant inspection + tag: + trendable: Permisse que ti-ci hashtag apari sub tendenties diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index 9ad432350..731379835 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -6,7 +6,7 @@ it: discoverable: I tuoi post pubblici e il tuo profilo potrebbero essere presenti o consigliati in varie aree di Mastodon e il tuo profilo potrebbe essere suggerito ad altri utenti. display_name: Il tuo nome completo o il tuo soprannome. fields: La tua homepage, i pronomi, l'età, tutto quello che vuoi. - indexable: I tuoi post pubblici potrebbero apparire nei risultati di ricerca su Mastodon. Le persone che hanno interagito con i tuoi post potrebbero essere in grado di cercarli indipendentemente. + indexable: I tuoi post pubblici potrebbero apparire nei risultati di ricerca su Mastodon. Le persone che hanno interagito con i tuoi post potrebbero essere in grado di cercarli anche se non hai attivato questa impostazione. note: 'Puoi @menzionare altre persone o usare gli #hashtags.' show_collections: Le persone saranno in grado di navigare attraverso i tuoi seguaci e seguaci. Le persone che segui vedranno che li seguirai indipendentemente dalle tue impostazioni. unlocked: Le persone saranno in grado di seguirti senza richiedere l'approvazione. Deseleziona se vuoi controllare le richieste di seguirti e scegli se accettare o rifiutare nuovi follower. @@ -122,7 +122,7 @@ it: webauthn: Se si tratta di una chiavetta USB assicurati di inserirla e, se necessario, toccarla. settings: indexable: La pagina del tuo profilo potrebbe apparire nei risultati di ricerca su Google, Bing e altri. - show_application: Sarai sempre in grado di vedere quale app ha pubblicato il tuo post indipendentemente. + show_application: Tu sarai sempre in grado di vedere quale app ha pubblicato il tuo post anche se hai attivato questa impostazione. tag: name: Puoi cambiare solo il minuscolo/maiuscolo delle lettere, ad esempio, per renderlo più leggibile user: diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml index e0b8eab87..aeac5f1c5 100644 --- a/config/locales/simple_form.lad.yml +++ b/config/locales/simple_form.lad.yml @@ -4,6 +4,9 @@ lad: hints: account: display_name: Tu nombre para amostrar. + fields: Tu deskripsyon, pronombres, edad, todo lo ke keras. + note: 'Puedes @enmentar a otra djente o #etiketas.' + show_collections: Otra djente podra ver tus segidos i suivantes. Personas a las kualas siges siempre podran ver que las estas sigiendo. account_alias: acct: Espesifika tu nombre de utilizador@domeno del kuento de ande keres migrar account_migration: @@ -55,6 +58,7 @@ lad: setting_display_media_show_all: Amostra siempre el kontenido de multimedia setting_use_blurhash: Los gradientes se bazan en los kolores de las imajes eskondidas pero faziendo velados los peratim setting_use_pending_items: Eskonde muevas publikasyones detras de un klik en lugar de desplazar otomatikamente la linya + username: Solo puedes uzar letras, shifras i sulinyados whole_word: Kuando el biervo yave o fraza es solo alfanumerika, solo sera aplikado si konkorda kon todo el biervo domain_allow: domain: Este domeno podra obtener datos de este sirvidor i los datos entrantes seran prosesados i archivados @@ -113,6 +117,8 @@ lad: sessions: otp: 'Introduse el kodiche de autentifikasyon de dos pasos djenerado por tu aplikasyon de telefon o uza uno de tus kodiches de recuperasyon:' webauthn: Si es una yave USB, asigurete de insertarla y, si es necesario, pulsala. + settings: + show_application: Tu siempre podras ver dizde kuala aplikasyon publikates tu publikasyon. tag: name: Solo se puede trokar la kapitalizasyon de las letras, por enshemplo, para ke sea mas meldable user: @@ -132,6 +138,9 @@ lad: fields: name: Etiketa value: Kontenido + indexable: Inkluye publikasyones publikas en los rezultados de bushkeda + show_collections: Amostra tus segidos i suivantes en el profil + unlocked: Otomatikamente acheta muevos suivantes account_alias: acct: Alias del kuento viejo account_migration: @@ -175,16 +184,23 @@ lad: fields: Datos adisyonales header: Imaje de kavesera locale: Lingua de enterfaz + max_uses: Maksimo numero de uzos new_password: Muevo kod note: Deskripsyon otp_attempt: Kodiche de dos pasos password: Kod phrase: Biervo yave o fraza + setting_aggregate_reblogs: Agrupa repartajasyones en linyas + setting_always_send_emails: Siempre embia avizos por posta + setting_auto_play_gif: Siempre reproduse los GIFs animados setting_default_language: Lingua de publikasyones setting_default_privacy: Privasita de publikasyones setting_display_media_default: Predeterminado setting_display_media_hide_all: Eskonde todo setting_display_media_show_all: Amostra todo + setting_reduce_motion: Reduse el movimyento en animasyones + setting_system_font_ui: Uza el font predeterminado del sistem + setting_trends: Amostra los trendes de oy setting_use_blurhash: Amostra gradientes koloridos para kontenido multimedia eskondido setting_use_pending_items: Modo lento severity: Severita @@ -247,14 +263,23 @@ lad: sign_up_requires_approval: Limita enrejistrasyones severity: Regla notification_emails: + favourite: A alguno le plaze tu publikasyon follow: Alguno te ampeso a segir follow_request: Alguno tiene solisitado segirte mention: Alguno te enmento + pending_account: Muevo kuento nesesita revizyon reblog: Alguno repartajo tu publikasyon + report: Muevo raporto fue embiado software_updates: + all: Avizame de kada aktualizasyon + critical: Avizame solo de aktualizasyones kritikas label: Mueva version de Mastodon esta desponivle + none: Nunkua avizame de aktualizasyones (no rekomendado) + patch: Avizame de aktualizasyones de yerros rule: text: Regla + settings: + indexable: Inkluye la pajina de profil en los bushkadores tag: name: Etiketa user: From 513d35969ef8d11ea494fb049094868b833bcff8 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 22 Dec 2023 03:03:59 -0500 Subject: [PATCH 17/22] Fix `RSpec/LetSetup` cop in auth controller specs (#28464) --- .rubocop_todo.yml | 3 --- .../auth/confirmations_controller_spec.rb | 6 ++++-- spec/controllers/auth/passwords_controller_spec.rb | 2 ++ spec/controllers/auth/sessions_controller_spec.rb | 14 ++++++-------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index afd530530..384f80923 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -47,9 +47,6 @@ RSpec/ExampleLength: RSpec/LetSetup: Exclude: - - 'spec/controllers/auth/confirmations_controller_spec.rb' - - 'spec/controllers/auth/passwords_controller_spec.rb' - - 'spec/controllers/auth/sessions_controller_spec.rb' - 'spec/models/account_statuses_cleanup_policy_spec.rb' - 'spec/models/status_spec.rb' - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb' diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb index 58bc38f54..15403e8ea 100644 --- a/spec/controllers/auth/confirmations_controller_spec.rb +++ b/spec/controllers/auth/confirmations_controller_spec.rb @@ -41,8 +41,9 @@ describe Auth::ConfirmationsController do get :show, params: { confirmation_token: 'foobar' } end - it 'redirects to login' do + it 'redirects to login and confirms user' do expect(response).to redirect_to(new_user_session_path) + expect(user.reload.confirmed_at).to_not be_nil end end @@ -87,8 +88,9 @@ describe Auth::ConfirmationsController do get :show, params: { confirmation_token: 'foobar' } end - it 'redirects to login' do + it 'redirects to login and confirms email' do expect(response).to redirect_to(new_user_session_path) + expect(user.reload.unconfirmed_email).to be_nil end it 'does not queue up bootstrapping of home timeline' do diff --git a/spec/controllers/auth/passwords_controller_spec.rb b/spec/controllers/auth/passwords_controller_spec.rb index e7f7ab467..d70490abc 100644 --- a/spec/controllers/auth/passwords_controller_spec.rb +++ b/spec/controllers/auth/passwords_controller_spec.rb @@ -70,6 +70,7 @@ describe Auth::PasswordsController do it 'deactivates all sessions' do expect(user.session_activations.count).to eq 0 + expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound) end it 'revokes all access tokens' do @@ -78,6 +79,7 @@ describe Auth::PasswordsController do it 'removes push subscriptions' do expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0 + expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) end end diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index f341d75b7..212cc4d5e 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -123,9 +123,8 @@ RSpec.describe Auth::SessionsController do let(:previous_ip) { '1.2.3.4' } let(:current_ip) { '4.3.2.1' } - let!(:previous_login) { Fabricate(:login_activity, user: user, ip: previous_ip) } - before do + Fabricate(:login_activity, user: user, ip: previous_ip) allow(controller.request).to receive(:remote_ip).and_return(current_ip) user.update(current_sign_in_at: 1.month.ago) post :create, params: { user: { email: user.email, password: user.password } } @@ -328,12 +327,6 @@ RSpec.describe Auth::SessionsController do Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) end - let!(:recovery_codes) do - codes = user.generate_otp_backup_codes! - user.save - return codes - end - let!(:webauthn_credential) do user.update(webauthn_id: WebAuthn.generate_user_id) public_key_credential = WebAuthn::Credential.from_create(fake_client.create) @@ -356,6 +349,11 @@ RSpec.describe Auth::SessionsController do let(:fake_credential) { fake_client.get(challenge: challenge, sign_count: sign_count) } + before do + user.generate_otp_backup_codes! + user.save + end + context 'when using email and password' do before do post :create, params: { user: { email: user.email, password: user.password } } From e6e217fedd13251db166010814a6ae0cdba2ad96 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 22 Dec 2023 03:32:27 -0500 Subject: [PATCH 18/22] Clean up `tagged_with_*` Status specs, fix `RSpec/LetSetup` cop (#28462) --- .rubocop_todo.yml | 1 - spec/models/status_spec.rb | 69 ++++++++++++++++++++++++++++---------- spec/rails_helper.rb | 1 + 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 384f80923..11bacb3b1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -48,7 +48,6 @@ RSpec/ExampleLength: RSpec/LetSetup: Exclude: - 'spec/models/account_statuses_cleanup_policy_spec.rb' - - 'spec/models/status_spec.rb' - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb' RSpec/MultipleExpectations: diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 938d0546d..284576ced 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -265,17 +265,29 @@ RSpec.describe Status do context 'when given one tag' do it 'returns the expected statuses' do - expect(described_class.tagged_with([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_all_tags.id) - expect(described_class.tagged_with([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_with_all_tags.id) - expect(described_class.tagged_with([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id, status_with_all_tags.id) + expect(described_class.tagged_with([tag_cats.id])) + .to include(status_with_tag_cats, status_with_all_tags) + .and not_include(status_without_tags) + expect(described_class.tagged_with([tag_dogs.id])) + .to include(status_with_tag_dogs, status_with_all_tags) + .and not_include(status_without_tags) + expect(described_class.tagged_with([tag_zebras.id])) + .to include(status_tagged_with_zebras, status_with_all_tags) + .and not_include(status_without_tags) end end context 'when given multiple tags' do it 'returns the expected statuses' do - expect(described_class.tagged_with([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_tag_dogs.id, status_with_all_tags.id) - expect(described_class.tagged_with([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_tagged_with_zebras.id, status_with_all_tags.id) - expect(described_class.tagged_with([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_tagged_with_zebras.id, status_with_all_tags.id) + expect(described_class.tagged_with([tag_cats.id, tag_dogs.id])) + .to include(status_with_tag_cats, status_with_tag_dogs, status_with_all_tags) + .and not_include(status_without_tags) + expect(described_class.tagged_with([tag_cats.id, tag_zebras.id])) + .to include(status_with_tag_cats, status_tagged_with_zebras, status_with_all_tags) + .and not_include(status_without_tags) + expect(described_class.tagged_with([tag_dogs.id, tag_zebras.id])) + .to include(status_with_tag_dogs, status_tagged_with_zebras, status_with_all_tags) + .and not_include(status_without_tags) end end end @@ -292,17 +304,26 @@ RSpec.describe Status do context 'when given one tag' do it 'returns the expected statuses' do - expect(described_class.tagged_with_all([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_all_tags.id) - expect(described_class.tagged_with_all([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_with_all_tags.id) - expect(described_class.tagged_with_all([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id) + expect(described_class.tagged_with_all([tag_cats.id])) + .to include(status_with_tag_cats, status_with_all_tags) + .and not_include(status_without_tags) + expect(described_class.tagged_with_all([tag_dogs.id])) + .to include(status_with_tag_dogs, status_with_all_tags) + .and not_include(status_without_tags) + expect(described_class.tagged_with_all([tag_zebras.id])) + .to include(status_tagged_with_zebras) + .and not_include(status_without_tags) end end context 'when given multiple tags' do it 'returns the expected statuses' do - expect(described_class.tagged_with_all([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_all_tags.id) - expect(described_class.tagged_with_all([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to eq [] - expect(described_class.tagged_with_all([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to eq [] + expect(described_class.tagged_with_all([tag_cats.id, tag_dogs.id])) + .to include(status_with_all_tags) + expect(described_class.tagged_with_all([tag_cats.id, tag_zebras.id])) + .to eq [] + expect(described_class.tagged_with_all([tag_dogs.id, tag_zebras.id])) + .to eq [] end end end @@ -319,17 +340,29 @@ RSpec.describe Status do context 'when given one tag' do it 'returns the expected statuses' do - expect(described_class.tagged_with_none([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_tagged_with_zebras.id, status_without_tags.id) - expect(described_class.tagged_with_none([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_tagged_with_zebras.id, status_without_tags.id) - expect(described_class.tagged_with_none([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_tag_dogs.id, status_without_tags.id) + expect(described_class.tagged_with_none([tag_cats.id])) + .to include(status_with_tag_dogs, status_tagged_with_zebras, status_without_tags) + .and not_include(status_with_all_tags) + expect(described_class.tagged_with_none([tag_dogs.id])) + .to include(status_with_tag_cats, status_tagged_with_zebras, status_without_tags) + .and not_include(status_with_all_tags) + expect(described_class.tagged_with_none([tag_zebras.id])) + .to include(status_with_tag_cats, status_with_tag_dogs, status_without_tags) + .and not_include(status_with_all_tags) end end context 'when given multiple tags' do it 'returns the expected statuses' do - expect(described_class.tagged_with_none([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id, status_without_tags.id) - expect(described_class.tagged_with_none([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_without_tags.id) - expect(described_class.tagged_with_none([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_without_tags.id) + expect(described_class.tagged_with_none([tag_cats.id, tag_dogs.id])) + .to include(status_tagged_with_zebras, status_without_tags) + .and not_include(status_with_all_tags) + expect(described_class.tagged_with_none([tag_cats.id, tag_zebras.id])) + .to include(status_with_tag_dogs, status_without_tags) + .and not_include(status_with_all_tags) + expect(described_class.tagged_with_none([tag_dogs.id, tag_zebras.id])) + .to include(status_with_tag_cats, status_without_tags) + .and not_include(status_with_all_tags) end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 0e68fbe12..c9352a4d5 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -153,6 +153,7 @@ RSpec::Sidekiq.configure do |config| end RSpec::Matchers.define_negated_matcher :not_change, :change +RSpec::Matchers.define_negated_matcher :not_include, :include def request_fixture(name) Rails.root.join('spec', 'fixtures', 'requests', name).read From a4d49c236d1c3bd53dc944ba1062d4d7e76007ce Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 22 Dec 2023 07:57:29 -0500 Subject: [PATCH 19/22] Fix `RSpec/LetSetup` cop in ap/fetch_featured_collection_service (#28461) --- .rubocop_todo.yml | 1 - .../activitypub/fetch_featured_collection_service_spec.rb | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 11bacb3b1..6dcdb3b83 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -48,7 +48,6 @@ RSpec/ExampleLength: RSpec/LetSetup: Exclude: - 'spec/models/account_statuses_cleanup_policy_spec.rb' - - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb' RSpec/MultipleExpectations: Max: 8 diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb index 466da891a..a98108cea 100644 --- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb +++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb @@ -87,6 +87,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do 'https://example.com/account/pinned/unknown-inlined', 'https://example.com/account/pinned/unknown-reachable' ) + expect(actor.pinned_statuses).to_not include(known_status) end end From 2bf84b93d43a33e53f27893f62f3f1774294bd1b Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Fri, 22 Dec 2023 16:10:39 +0100 Subject: [PATCH 20/22] Fix media attachment order of remote posts (#28469) --- app/lib/activitypub/activity/create.rb | 5 ++++- app/models/status.rb | 4 +++- spec/lib/activitypub/activity/create_spec.rb | 10 ++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index fedfa39de..f6262899a 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -110,6 +110,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def process_status_params @status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: @account.followers_url) + attachment_ids = process_attachments.take(4).map(&:id) + @params = { uri: @status_parser.uri, url: @status_parser.url || @status_parser.uri, @@ -125,7 +127,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity visibility: @status_parser.visibility, thread: replied_to_status, conversation: conversation_from_uri(@object['conversation']), - media_attachment_ids: process_attachments.take(4).map(&:id), + media_attachment_ids: attachment_ids, + ordered_media_attachment_ids: attachment_ids, poll: process_poll, } end diff --git a/app/models/status.rb b/app/models/status.rb index 4bdadae06..3faa50700 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -277,7 +277,9 @@ class Status < ApplicationRecord def ordered_media_attachments if ordered_media_attachment_ids.nil? - media_attachments + # NOTE: sort Ruby-side to avoid hitting the database when the status is + # not persisted to database yet + media_attachments.sort_by(&:id) else map = media_attachments.index_by(&:id) ordered_media_attachment_ids.filter_map { |media_attachment_id| map[media_attachment_id] } diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index bdc1c45ea..8084427d0 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -532,15 +532,21 @@ RSpec.describe ActivityPub::Activity::Create do mediaType: 'image/png', url: 'http://example.com/attachment.png', }, + { + type: 'Document', + mediaType: 'image/png', + url: 'http://example.com/emoji.png', + }, ], } end - it 'creates status' do + it 'creates status with correctly-ordered media attachments' do status = sender.statuses.first expect(status).to_not be_nil - expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png') + expect(status.ordered_media_attachments.map(&:remote_url)).to eq ['http://example.com/attachment.png', 'http://example.com/emoji.png'] + expect(status.ordered_media_attachment_ids).to be_present end end From bb8077e784f3f351e1f7e8905ea44d851b4ed456 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 22 Dec 2023 10:29:50 -0500 Subject: [PATCH 21/22] Fix `RSpec/LetSetup` cop in models/account_status_cleanup_policy (#28470) --- .rubocop_todo.yml | 4 ---- .../account_statuses_cleanup_policy_spec.rb | 24 +++++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6dcdb3b83..bc66bc4ad 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -45,10 +45,6 @@ Metrics/PerceivedComplexity: RSpec/ExampleLength: Max: 22 -RSpec/LetSetup: - Exclude: - - 'spec/models/account_statuses_cleanup_policy_spec.rb' - RSpec/MultipleExpectations: Max: 8 diff --git a/spec/models/account_statuses_cleanup_policy_spec.rb b/spec/models/account_statuses_cleanup_policy_spec.rb index 74fff30c9..da2a774b2 100644 --- a/spec/models/account_statuses_cleanup_policy_spec.rb +++ b/spec/models/account_statuses_cleanup_policy_spec.rb @@ -235,13 +235,17 @@ RSpec.describe AccountStatusesCleanupPolicy do describe '#compute_cutoff_id' do subject { account_statuses_cleanup_policy.compute_cutoff_id } - let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) } let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) } + before { Fabricate(:status, created_at: 3.years.ago) } + context 'when the account has posted multiple toots' do - let!(:very_old_status) { Fabricate(:status, created_at: 3.years.ago, account: account) } - let!(:old_status) { Fabricate(:status, created_at: 3.weeks.ago, account: account) } - let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) } + let!(:old_status) { Fabricate(:status, created_at: 3.weeks.ago, account: account) } + + before do + Fabricate(:status, created_at: 3.years.ago, account: account) + Fabricate(:status, created_at: 2.days.ago, account: account) + end it 'returns the most recent id that is still below policy age' do expect(subject).to eq old_status.id @@ -270,16 +274,16 @@ RSpec.describe AccountStatusesCleanupPolicy do let!(:faved_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) } let!(:reblogged_primary) { Fabricate(:status, created_at: 1.year.ago, account: account) } let!(:reblogged_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) } - let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) } - - let!(:media_attachment) { Fabricate(:media_attachment, account: account, status: status_with_media) } - let!(:status_pin) { Fabricate(:status_pin, account: account, status: pinned_status) } - let!(:favourite) { Fabricate(:favourite, account: account, status: self_faved) } - let!(:bookmark) { Fabricate(:bookmark, account: account, status: self_bookmarked) } + let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) } let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) } before do + Fabricate(:media_attachment, account: account, status: status_with_media) + Fabricate(:status_pin, account: account, status: pinned_status) + Fabricate(:favourite, account: account, status: self_faved) + Fabricate(:bookmark, account: account, status: self_bookmarked) + faved_primary.status_stat.update(favourites_count: 4) faved_secondary.status_stat.update(favourites_count: 5) reblogged_primary.status_stat.update(reblogs_count: 4) From a2624ff739ea555888f27b13a44f7d6b1af35a86 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Fri, 22 Dec 2023 19:56:22 +0100 Subject: [PATCH 22/22] Convert signature verification specs to request specs (#28443) --- .../concerns/signature_verification_spec.rb | 305 ---------------- spec/requests/signature_verification_spec.rb | 332 ++++++++++++++++++ 2 files changed, 332 insertions(+), 305 deletions(-) delete mode 100644 spec/controllers/concerns/signature_verification_spec.rb create mode 100644 spec/requests/signature_verification_spec.rb diff --git a/spec/controllers/concerns/signature_verification_spec.rb b/spec/controllers/concerns/signature_verification_spec.rb deleted file mode 100644 index 650cd21ea..000000000 --- a/spec/controllers/concerns/signature_verification_spec.rb +++ /dev/null @@ -1,305 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe SignatureVerification do - let(:wrapped_actor_class) do - Class.new do - attr_reader :wrapped_account - - def initialize(wrapped_account) - @wrapped_account = wrapped_account - end - - delegate :uri, :keypair, to: :wrapped_account - end - end - - controller(ApplicationController) do - include SignatureVerification - - before_action :require_actor_signature!, only: [:signature_required] - - def success - head 200 - end - - def alternative_success - head 200 - end - - def signature_required - head 200 - end - end - - before do - routes.draw do - match :via => [:get, :post], 'success' => 'anonymous#success' - match :via => [:get, :post], 'signature_required' => 'anonymous#signature_required' - end - end - - context 'without signature header' do - before do - get :success - end - - describe '#signed_request?' do - it 'returns false' do - expect(controller.signed_request?).to be false - end - end - - describe '#signed_request_account' do - it 'returns nil' do - expect(controller.signed_request_account).to be_nil - end - end - end - - context 'with signature header' do - let!(:author) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') } - - context 'without body' do - before do - get :success - - fake_request = Request.new(:get, request.url) - fake_request.on_behalf_of(author) - - request.headers.merge!(fake_request.headers) - end - - describe '#signed_request?' do - it 'returns true' do - expect(controller.signed_request?).to be true - end - end - - describe '#signed_request_account' do - it 'returns an account' do - expect(controller.signed_request_account).to eq author - end - - it 'returns nil when path does not match' do - request.path = '/alternative-path' - expect(controller.signed_request_account).to be_nil - end - - it 'returns nil when method does not match' do - post :success - expect(controller.signed_request_account).to be_nil - end - end - end - - context 'with a valid actor that is not an Account' do - let(:actor) { wrapped_actor_class.new(author) } - - before do - get :success - - fake_request = Request.new(:get, request.url) - fake_request.on_behalf_of(author) - - request.headers.merge!(fake_request.headers) - - allow(ActivityPub::TagManager.instance).to receive(:uri_to_actor).with(anything) do - actor - end - end - - describe '#signed_request?' do - it 'returns true' do - expect(controller.signed_request?).to be true - end - end - - describe '#signed_request_account' do - it 'returns nil' do - expect(controller.signed_request_account).to be_nil - end - end - - describe '#signed_request_actor' do - it 'returns the expected actor' do - expect(controller.signed_request_actor).to eq actor - end - end - end - - context 'with request with unparsable Date header' do - before do - get :success - - fake_request = Request.new(:get, request.url) - fake_request.add_headers({ 'Date' => 'wrong date' }) - fake_request.on_behalf_of(author) - - request.headers.merge!(fake_request.headers) - end - - describe '#signed_request?' do - it 'returns true' do - expect(controller.signed_request?).to be true - end - end - - describe '#signed_request_account' do - it 'returns nil' do - expect(controller.signed_request_account).to be_nil - end - end - - describe '#signature_verification_failure_reason' do - it 'contains an error description' do - controller.signed_request_account - expect(controller.signature_verification_failure_reason[:error]).to eq 'Invalid Date header: not RFC 2616 compliant date: "wrong date"' - end - end - end - - context 'with request older than a day' do - before do - get :success - - fake_request = Request.new(:get, request.url) - fake_request.add_headers({ 'Date' => 2.days.ago.utc.httpdate }) - fake_request.on_behalf_of(author) - - request.headers.merge!(fake_request.headers) - end - - describe '#signed_request?' do - it 'returns true' do - expect(controller.signed_request?).to be true - end - end - - describe '#signed_request_account' do - it 'returns nil' do - expect(controller.signed_request_account).to be_nil - end - end - - describe '#signature_verification_failure_reason' do - it 'contains an error description' do - controller.signed_request_account - expect(controller.signature_verification_failure_reason[:error]).to eq 'Signed request date outside acceptable time window' - end - end - end - - context 'with inaccessible key' do - before do - get :success - - author = Fabricate(:account, domain: 'localhost:5000', uri: 'http://localhost:5000/actor') - fake_request = Request.new(:get, request.url) - fake_request.on_behalf_of(author) - author.destroy - - request.headers.merge!(fake_request.headers) - - stub_request(:get, 'http://localhost:5000/actor#main-key').to_raise(Mastodon::HostValidationError) - end - - describe '#signed_request?' do - it 'returns true' do - expect(controller.signed_request?).to be true - end - end - - describe '#signed_request_account' do - it 'returns nil' do - expect(controller.signed_request_account).to be_nil - end - end - end - - context 'with body' do - before do - allow(controller).to receive(:actor_refresh_key!).and_return(author) - post :success, body: 'Hello world' - - fake_request = Request.new(:post, request.url, body: 'Hello world') - fake_request.on_behalf_of(author) - - request.headers.merge!(fake_request.headers) - end - - describe '#signed_request?' do - it 'returns true' do - expect(controller.signed_request?).to be true - end - end - - describe '#signed_request_account' do - it 'returns an account' do - expect(controller.signed_request_account).to eq author - end - end - - context 'when path does not match' do - before do - request.path = '/alternative-path' - end - - describe '#signed_request_account' do - it 'returns nil' do - expect(controller.signed_request_account).to be_nil - end - end - - describe '#signature_verification_failure_reason' do - it 'contains an error description' do - controller.signed_request_account - expect(controller.signature_verification_failure_reason[:error]).to include('using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)') - expect(controller.signature_verification_failure_reason[:signed_string]).to include("(request-target): post /alternative-path\n") - end - end - end - - context 'when method does not match' do - before do - get :success - end - - describe '#signed_request_account' do - it 'returns nil' do - expect(controller.signed_request_account).to be_nil - end - end - end - - context 'when body has been tampered' do - before do - post :success, body: 'doo doo doo' - end - - describe '#signed_request_account' do - it 'returns nil when body has been tampered' do - expect(controller.signed_request_account).to be_nil - end - end - end - end - end - - context 'when a signature is required' do - before do - get :signature_required - end - - context 'without signature header' do - it 'returns HTTP 401' do - expect(response).to have_http_status(401) - end - - it 'returns an error' do - expect(Oj.load(response.body)['error']).to eq 'Request not signed' - end - end - end -end diff --git a/spec/requests/signature_verification_spec.rb b/spec/requests/signature_verification_spec.rb new file mode 100644 index 000000000..b753750b8 --- /dev/null +++ b/spec/requests/signature_verification_spec.rb @@ -0,0 +1,332 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'signature verification concern' do + before do + stub_tests_controller + + # Signature checking is time-dependent, so travel to a fixed date + travel_to '2023-12-20T10:00:00Z' + end + + after { Rails.application.reload_routes! } + + # Include the private key so the tests can be easily adjusted and reviewed + let(:actor_keypair) do + OpenSSL::PKey.read(<<~PEM_TEXT) + -----BEGIN RSA PRIVATE KEY----- + MIIEowIBAAKCAQEAqIAYvNFGbZ5g4iiK6feSdXD4bDStFM58A7tHycYXaYtzZQpI + eHXAmaXuZzXIwtrP4N0gIk8JNwZvXj2UPS+S07t0V9wNK94he01LV5EMz/GN4eNn + FmDL64HIEuKLvV8TvgjbUPRD6Y5X0UpKi2ZIFLSb96Q5w0Z/k7ntpVKV52y8kz5F + jr/O/0JuHryZe0yItzJh8kzFfeMf0EXzfSnaKvT7P9jhgC6uTre+jXyvVZjiHDrn + qvvucdI3I7DRfXo1OqARBrLjy+TdseUAjNYJ+OuPRI1URIWQI01DCHqcohVu9+Ar + +BiCjFp3ua+XMuJvrvbD61d1Fvig/9nbBRR+8QIDAQABAoIBAAgySHnFWI6gItR3 + fkfiqIm80cHCN3Xk1C6iiVu+3oBOZbHpW9R7vl9e/WOA/9O+LPjiSsQOegtWnVvd + RRjrl7Hj20VDlZKv5Mssm6zOGAxksrcVbqwdj+fUJaNJCL0AyyseH0x/IE9T8rDC + I1GH+3tB3JkhkIN/qjipdX5ab8MswEPu8IC4ViTpdBgWYY/xBcAHPw4xuL0tcwzh + FBlf4DqoEVQo8GdK5GAJ2Ny0S4xbXHUURzx/R4y4CCts7niAiLGqd9jmLU1kUTMk + QcXfQYK6l+unLc7wDYAz7sFEHh04M48VjWwiIZJnlCqmQbLda7uhhu8zkF1DqZTu + ulWDGQECgYEA0TIAc8BQBVab979DHEEmMdgqBwxLY3OIAk0b+r50h7VBGWCDPRsC + STD73fQY3lNet/7/jgSGwwAlAJ5PpMXxXiZAE3bUwPmHzgF7pvIOOLhA8O07tHSO + L2mvQe6NPzjZ+6iAO2U9PkClxcvGvPx2OBvisfHqZLmxC9PIVxzruQECgYEAzjM6 + BTUXa6T/qHvLFbN699BXsUOGmHBGaLRapFDBfVvgZrwqYQcZpBBhesLdGTGSqwE7 + gWsITPIJ+Ldo+38oGYyVys+w/V67q6ud7hgSDTW3hSvm+GboCjk6gzxlt9hQ0t9X + 8vfDOYhEXvVUJNv3mYO60ENqQhILO4bQ0zi+VfECgYBb/nUccfG+pzunU0Cb6Dp3 + qOuydcGhVmj1OhuXxLFSDG84Tazo7juvHA9mp7VX76mzmDuhpHPuxN2AzB2SBEoE + cSW0aYld413JRfWukLuYTc6hJHIhBTCRwRQFFnae2s1hUdQySm8INT2xIc+fxBXo + zrp+Ljg5Wz90SAnN5TX0AQKBgDaatDOq0o/r+tPYLHiLtfWoE4Dau+rkWJDjqdk3 + lXWn/e3WyHY3Vh/vQpEqxzgju45TXjmwaVtPATr+/usSykCxzP0PMPR3wMT+Rm1F + rIoY/odij+CaB7qlWwxj0x/zRbwB7x1lZSp4HnrzBpxYL+JUUwVRxPLIKndSBTza + GvVRAoGBAIVBcNcRQYF4fvZjDKAb4fdBsEuHmycqtRCsnkGOz6ebbEQznSaZ0tZE + +JuouZaGjyp8uPjNGD5D7mIGbyoZ3KyG4mTXNxDAGBso1hrNDKGBOrGaPhZx8LgO + 4VXJ+ybXrATf4jr8ccZYsZdFpOphPzz+j55Mqg5vac5P1XjmsGTb + -----END RSA PRIVATE KEY----- + PEM_TEXT + end + + context 'without a Signature header' do + it 'does not treat the request as signed' do + get '/activitypub/success' + + expect(response).to have_http_status(200) + expect(body_as_json).to match( + signed_request: false, + signature_actor_id: nil, + error: 'Request not signed' + ) + end + + context 'when a signature is required' do + it 'returns http unauthorized with appropriate error' do + get '/activitypub/signature_required' + + expect(response).to have_http_status(401) + expect(body_as_json).to match( + error: 'Request not signed' + ) + end + end + end + + context 'with an HTTP Signature from a known account' do + let!(:actor) { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) } + + context 'with a valid signature on a GET request' do + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength + end + + it 'successfuly verifies signature', :aggregate_failures do + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' }) + + get '/activitypub/success', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Signature' => signature_header, + } + + expect(response).to have_http_status(200) + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: actor.id.to_s + ) + end + end + + context 'with a mismatching path' do + it 'fails to verify signature', :aggregate_failures do + get '/activitypub/alternative-path', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: anything + ) + end + end + + context 'with a mismatching method' do + it 'fails to verify signature', :aggregate_failures do + post '/activitypub/success', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: anything + ) + end + end + + context 'with an unparsable date' do + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="d4B7nfx8RJcfdJDu1J//5WzPzK/hgtPkdzZx49lu5QhnE7qdV3lgyVimmhCFrO16bwvzIp9iRMyRLkNFxLiEeVaa1gqeKbldGSnU0B0OMjx7rFBa65vLuzWQOATDitVGiBEYqoK4v0DMuFCz2DtFaA/DIUZ3sty8bZ/Ea3U1nByLOO6MacARA3zhMSI0GNxGqsSmZmG0hPLavB3jIXoE3IDoQabMnC39jrlcO/a8h1iaxBm2WD8TejrImJullgqlJIFpKhIHI3ipQkvTGPlm9dx0y+beM06qBvWaWQcmT09eRIUefVsOAzIhUtS/7FVb/URhZvircIJDa7vtiFcmZQ=="' # rubocop:disable Layout/LineLength + end + + it 'fails to verify signature', :aggregate_failures do + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'wrong date', 'Host' => 'www.example.com' }) + + get '/activitypub/success', headers: { + 'Host' => 'www.example.com', + 'Date' => 'wrong date', + 'Signature' => signature_header, + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: 'Invalid Date header: not RFC 2616 compliant date: "wrong date"' + ) + end + end + + context 'with a request older than a day' do + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="G1NuJv4zgoZ3B/ZIjzDWZHK4RC+5pYee74q8/LJEMCWXhcnAomcb9YHaqk1QYfQvcBUIXw3UZ3Q9xO8F9y0i8G5mzJHfQ+OgHqCoJk8EmGwsUXJMh5s1S5YFCRt8TT12TmJZz0VMqLq85ubueSYBM7QtUE/FzFIVLvz4RysgXxaXQKzdnM6+gbUEEKdCURpXdQt2NXQhp4MAmZH3+0lQoR6VxdsK0hx0Ji2PNp1nuqFTlYqNWZazVdLBN+9rETLRmvGXknvg9jOxTTppBVWnkAIl26HtLS3wwFVvz4pJzi9OQDOvLziehVyLNbU61hky+oJ215e2HuKSe2hxHNl1MA=="' # rubocop:disable Layout/LineLength + end + + it 'fails to verify signature', :aggregate_failures do + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' }) + + get '/activitypub/success', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT', + 'Signature' => signature_header, + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: 'Signed request date outside acceptable time window' + ) + end + end + + context 'with a valid signature on a POST request' do + let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' } + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength + end + + it 'successfuly verifies signature', :aggregate_failures do + expect(digest_header).to eq digest_value('Hello world') + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header }) + + post '/activitypub/success', params: 'Hello world', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Digest' => digest_header, + 'Signature' => signature_header, + } + + expect(response).to have_http_status(200) + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: actor.id.to_s + ) + end + end + + context 'when the Digest of a POST request is not signed' do + let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' } + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date (request-target)",signature="CPD704CG8aCm8X8qIP8kkkiGp1qwFLk/wMVQHOGP0Txxan8c2DZtg/KK7eN8RG8tHx8br/yS2hJs51x4kXImYukGzNJd7ihE3T8lp+9RI1tCcdobTzr/VcVJHDFySdQkg266GCMijRQRZfNvqlJLiisr817PI+gNVBI5qV+vnVd1XhWCEZ+YSmMe8UqYARXAYNqMykTheojqGpTeTFGPUpTQA2Fmt2BipwIjcFDm2Hpihl2kB0MUS0x3zPmHDuadvzoBbN6m3usPDLgYrpALlh+wDs1dYMntcwdwawRKY1oE1XNtgOSum12wntDq3uYL4gya2iPdcw3c929b4koUzw=="' # rubocop:disable Layout/LineLength + end + + it 'fails to verify signature', :aggregate_failures do + expect(digest_header).to eq digest_value('Hello world') + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT' }) + + post '/activitypub/success', params: 'Hello world', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Digest' => digest_header, + 'Signature' => signature_header, + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: 'Mastodon requires the Digest header to be signed when doing a POST request' + ) + end + end + + context 'with a tampered body on a POST request' do + let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' } + let(:signature_header) do + 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength + end + + it 'fails to verify signature', :aggregate_failures do + expect(digest_header).to_not eq digest_value('Hello world!') + expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header }) + + post '/activitypub/success', params: 'Hello world!', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=', + 'Signature' => signature_header, + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: 'Invalid Digest value. Computed SHA-256 digest: wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro=; given: ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' + ) + end + end + + context 'with a tampered path in a POST request' do + it 'fails to verify signature', :aggregate_failures do + post '/activitypub/alternative-path', params: 'Hello world', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=', + 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="', # rubocop:disable Layout/LineLength + } + + expect(response).to have_http_status(200) + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: anything + ) + end + end + end + + context 'with an inaccessible key' do + before do + stub_request(:get, 'https://remote.domain/users/alice#main-key').to_return(status: 404) + end + + it 'fails to verify signature', :aggregate_failures do + get '/activitypub/success', headers: { + 'Host' => 'www.example.com', + 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', + 'Signature' => 'keyId="https://remote.domain/users/alice#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength + } + + expect(body_as_json).to match( + signed_request: true, + signature_actor_id: nil, + error: 'Unable to fetch key JSON at https://remote.domain/users/alice#main-key' + ) + end + end + + private + + def stub_tests_controller + stub_const('ActivityPub::TestsController', activitypub_tests_controller) + + Rails.application.routes.draw do + # NOTE: RouteSet#draw removes all routes, so we need to re-insert one + resource :instance_actor, path: 'actor', only: [:show] + + match :via => [:get, :post], '/activitypub/success' => 'activitypub/tests#success' + match :via => [:get, :post], '/activitypub/alternative-path' => 'activitypub/tests#alternative_success' + match :via => [:get, :post], '/activitypub/signature_required' => 'activitypub/tests#signature_required' + end + end + + def activitypub_tests_controller + Class.new(ApplicationController) do + include SignatureVerification + + before_action :require_actor_signature!, only: [:signature_required] + + def success + render json: { + signed_request: signed_request?, + signature_actor_id: signed_request_actor&.id&.to_s, + }.merge(signature_verification_failure_reason || {}) + end + + alias_method :alternative_success, :success + alias_method :signature_required, :success + end + end + + def digest_value(body) + "SHA-256=#{Digest::SHA256.base64digest(body)}" + end + + def build_signature_string(keypair, key_id, request_target, headers) + algorithm = 'rsa-sha256' + signed_headers = headers.merge({ '(request-target)' => request_target }) + signed_string = signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n") + signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string)) + + "keyId=\"#{key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\"" + end +end