diff --git a/.circleci/config.yml b/.circleci/config.yml index 4fcc8c618..b9228f996 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - ruby: circleci/ruby@1.4.0 + ruby: circleci/ruby@1.4.1 node: circleci/node@5.0.1 executors: diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a62a213..dd0ccc5f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Changelog All notable changes to this project will be documented in this file. -## Unreleased +## [3.5.0] - 2022-03-30 ### Added - **Add support for incoming edited posts** ([Gargron](https://github.com/mastodon/mastodon/pull/16697), [Gargron](https://github.com/mastodon/mastodon/pull/17727), [Gargron](https://github.com/mastodon/mastodon/pull/17728), [Gargron](https://github.com/mastodon/mastodon/pull/17320), [Gargron](https://github.com/mastodon/mastodon/pull/17404), [Gargron](https://github.com/mastodon/mastodon/pull/17390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17335), [Gargron](https://github.com/mastodon/mastodon/pull/17696), [Gargron](https://github.com/mastodon/mastodon/pull/17745), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17740), [Gargron](https://github.com/mastodon/mastodon/pull/17697), [Gargron](https://github.com/mastodon/mastodon/pull/17648), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17531), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17499), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17498), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17380), [Gargron](https://github.com/mastodon/mastodon/pull/17373), [Gargron](https://github.com/mastodon/mastodon/pull/17334), [Gargron](https://github.com/mastodon/mastodon/pull/17333), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17699), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17748)) @@ -81,6 +81,7 @@ All notable changes to this project will be documented in this file. - Add lazy loading for emoji picker in web UI ([mashirozx](https://github.com/mastodon/mastodon/pull/16907), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17011)) - Add single option votes tooltip in polls in web UI ([Brawaru](https://github.com/mastodon/mastodon/pull/16849)) - Add confirmation modal when closing media edit modal with unsaved changes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16518)) +- Add hint about missing media attachment description in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17845)) - Add support for fetching Create and Announce activities by URI in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16383)) - Add `S3_FORCE_SINGLE_REQUEST` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16866)) - Add `OMNIAUTH_ONLY` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17288), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17345)) @@ -130,6 +131,11 @@ All notable changes to this project will be documented in this file. ### Fixed +- Fix IDN domains not being rendered correctly in a few left-over places ([Gargron](https://github.com/mastodon/mastodon/pull/17848)) +- Fix Sanskrit translation not being used in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17820)) +- Fix Kurdish languages having the wrong language codes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17812)) +- Fix pghero making database schema suggestions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17807)) +- Fix encoding glitch in the OpenGraph description of a profile page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17821)) - Fix web manifest not permitting PWA usage from alternate domains ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16714)) - Fix not being able to edit media attachments for scheduled posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17690)) - Fix subscribed relay activities being recorded as boosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17571)) @@ -191,6 +197,11 @@ All notable changes to this project will be documented in this file. - Fix hashtag autocomplete overriding user-typed case ([weex](https://github.com/mastodon/mastodon/pull/16460)) - Fix WebAuthn authentication setup to not prompt for PIN ([truongnmt](https://github.com/mastodon/mastodon/pull/16545)) +### Security + +- Fix being able to post URLs longer than 4096 characters ([Gargron](https://github.com/mastodon/mastodon/pull/17908)) +- Fix being able to bypass e-mail restrictions ([Gargron](https://github.com/mastodon/mastodon/pull/17909)) + ## [3.4.6] - 2022-02-03 ### Fixed diff --git a/Gemfile b/Gemfile index 8bb361f49..4c8cd2f5a 100644 --- a/Gemfile +++ b/Gemfile @@ -139,7 +139,7 @@ group :development do gem 'brakeman', '~> 5.2', require: false gem 'bundler-audit', '~> 0.9', require: false - gem 'capistrano', '~> 3.16' + gem 'capistrano', '~> 3.17' gem 'capistrano-rails', '~> 1.6' gem 'capistrano-rbenv', '~> 2.2' gem 'capistrano-yarn', '~> 2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 5ee2b9c26..c1012ef73 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,7 +96,7 @@ GEM aws-sigv4 (~> 1.4) aws-sigv4 (1.4.0) aws-eventstream (~> 1, >= 1.0.2) - bcrypt (3.1.16) + bcrypt (3.1.17) better_errors (2.9.1) coderay (>= 1.0.0) erubi (>= 1.0.0) @@ -121,7 +121,7 @@ GEM bundler (>= 1.2.0, < 3) thor (~> 1.0) byebug (11.1.3) - capistrano (3.16.0) + capistrano (3.17.0) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -157,7 +157,7 @@ GEM climate_control (0.2.0) coderay (1.1.3) color_diff (0.1) - concurrent-ruby (1.1.9) + concurrent-ruby (1.1.10) connection_pool (2.2.5) cose (1.0.0) cbor (~> 0.5.9) @@ -174,11 +174,11 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) - devise-two-factor (4.0.1) - activesupport (< 6.2) + devise-two-factor (4.0.2) + activesupport (< 7.1) attr_encrypted (>= 1.3, < 4, != 2) devise (~> 4.0) - railties (< 6.2) + railties (< 7.1) rotp (~> 6.0) devise_pam_authenticatable2 (9.2.0) devise (>= 4.0.0) @@ -433,7 +433,7 @@ GEM openssl-signature_algorithm (0.4.0) orm_adapter (0.5.0) ox (2.14.10) - parallel (1.22.0) + parallel (1.22.1) parser (3.1.1.0) ast (~> 2.4.1) parslet (2.0.0) @@ -562,7 +562,7 @@ GEM rspec-support (3.11.0) rspec_junit_formatter (0.5.1) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.26.0) + rubocop (1.26.1) parallel (~> 1.10) parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) @@ -735,7 +735,7 @@ DEPENDENCIES browser bullet (~> 7.0) bundler-audit (~> 0.9) - capistrano (~> 3.16) + capistrano (~> 3.17) capistrano-rails (~> 1.6) capistrano-rbenv (~> 2.2) capistrano-yarn (~> 2.0) diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 65cbb6fcd..1381a96ed 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class StatusesIndex < Chewy::Index + include FormattingHelper + settings index: { refresh_interval: '15m' }, analysis: { filter: { english_stop: { @@ -57,7 +59,7 @@ class StatusesIndex < Chewy::Index field :id, type: 'long' field :account_id, type: 'long' - field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.ordered_media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do + field :text, type: 'text', value: ->(status) { status.searchable_text } do field :stemmed, type: 'text', analyzer: 'content' end diff --git a/app/controllers/api/v1/admin/accounts_controller.rb b/app/controllers/api/v1/admin/accounts_controller.rb index 65330b8c8..4b6dab208 100644 --- a/app/controllers/api/v1/admin/accounts_controller.rb +++ b/app/controllers/api/v1/admin/accounts_controller.rb @@ -104,13 +104,27 @@ class Api::V1::Admin::AccountsController < Api::BaseController end def filtered_accounts - AccountFilter.new(filter_params).results + AccountFilter.new(translated_filter_params).results end def filter_params params.permit(*FILTER_PARAMS) end + def translated_filter_params + translated_params = { origin: 'local', status: 'active' }.merge(filter_params.slice(*AccountFilter::KEYS)) + + translated_params[:origin] = 'remote' if params[:remote].present? + + %i(active pending disabled silenced suspended).each do |status| + translated_params[:status] = status.to_s if params[status].present? + end + + translated_params[:permissions] = 'staff' if params[:staff].present? + + translated_params + end + def insert_pagination_headers set_pagination_headers(next_path, prev_path) end diff --git a/app/controllers/api/v1/trends/links_controller.rb b/app/controllers/api/v1/trends/links_controller.rb index ad20e7f8b..b1cde5a4b 100644 --- a/app/controllers/api/v1/trends/links_controller.rb +++ b/app/controllers/api/v1/trends/links_controller.rb @@ -3,6 +3,10 @@ class Api::V1::Trends::LinksController < Api::BaseController before_action :set_links + after_action :insert_pagination_headers + + DEFAULT_LINKS_LIMIT = 10 + def index render json: @links, each_serializer: REST::Trends::LinkSerializer end @@ -20,6 +24,26 @@ class Api::V1::Trends::LinksController < Api::BaseController end def links_from_trends - Trends.links.query.allowed.in_locale(content_locale).limit(limit_param(10)) + Trends.links.query.allowed.in_locale(content_locale).offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT)) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + + def next_path + api_v1_trends_links_url pagination_params(offset: offset_param + limit_param(DEFAULT_LINKS_LIMIT)) + end + + def prev_path + api_v1_trends_links_url pagination_params(offset: offset_param - limit_param(DEFAULT_LINKS_LIMIT)) if offset_param > limit_param(DEFAULT_LINKS_LIMIT) + end + + def offset_param + params[:offset].to_i end end diff --git a/app/controllers/api/v1/trends/statuses_controller.rb b/app/controllers/api/v1/trends/statuses_controller.rb index d4ec97ae5..4977803fb 100644 --- a/app/controllers/api/v1/trends/statuses_controller.rb +++ b/app/controllers/api/v1/trends/statuses_controller.rb @@ -3,6 +3,8 @@ class Api::V1::Trends::StatusesController < Api::BaseController before_action :set_statuses + after_action :insert_pagination_headers + def index render json: @statuses, each_serializer: REST::StatusSerializer end @@ -22,6 +24,26 @@ class Api::V1::Trends::StatusesController < Api::BaseController def statuses_from_trends scope = Trends.statuses.query.allowed.in_locale(content_locale) scope = scope.filtered_for(current_account) if user_signed_in? - scope.limit(limit_param(DEFAULT_STATUSES_LIMIT)) + scope.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + + def next_path + api_v1_trends_statuses_url pagination_params(offset: offset_param + limit_param(DEFAULT_STATUSES_LIMIT)) + end + + def prev_path + api_v1_trends_statuses_url pagination_params(offset: offset_param - limit_param(DEFAULT_STATUSES_LIMIT)) if offset_param > limit_param(DEFAULT_STATUSES_LIMIT) + end + + def offset_param + params[:offset].to_i end end diff --git a/app/controllers/api/v1/trends/tags_controller.rb b/app/controllers/api/v1/trends/tags_controller.rb index 1334b72d2..d77857871 100644 --- a/app/controllers/api/v1/trends/tags_controller.rb +++ b/app/controllers/api/v1/trends/tags_controller.rb @@ -3,6 +3,10 @@ class Api::V1::Trends::TagsController < Api::BaseController before_action :set_tags + after_action :insert_pagination_headers + + DEFAULT_TAGS_LIMIT = 10 + def index render json: @tags, each_serializer: REST::TagSerializer end @@ -12,10 +16,30 @@ class Api::V1::Trends::TagsController < Api::BaseController def set_tags @tags = begin if Setting.trends - Trends.tags.query.allowed.limit(limit_param(10)) + Trends.tags.query.allowed.limit(limit_param(DEFAULT_TAGS_LIMIT)) else [] end end end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + + def next_path + api_v1_trends_tags_url pagination_params(offset: offset_param + limit_param(DEFAULT_TAGS_LIMIT)) + end + + def prev_path + api_v1_trends_tags_url pagination_params(offset: offset_param - limit_param(DEFAULT_TAGS_LIMIT)) if offset_param > limit_param(DEFAULT_TAGS_LIMIT) + end + + def offset_param + params[:offset].to_i + end end diff --git a/app/controllers/api/v2/admin/accounts_controller.rb b/app/controllers/api/v2/admin/accounts_controller.rb new file mode 100644 index 000000000..a89e6835e --- /dev/null +++ b/app/controllers/api/v2/admin/accounts_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController + FILTER_PARAMS = %i( + origin + status + permissions + username + by_domain + display_name + email + ip + invited_by + ).freeze + + PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze + + private + + def filtered_accounts + AccountFilter.new(filter_params).results + end + + def filter_params + params.permit(*FILTER_PARAMS) + end + + def pagination_params(core_params) + params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params) + end +end diff --git a/app/controllers/api/web/embeds_controller.rb b/app/controllers/api/web/embeds_controller.rb index 741ba910f..58f6345e6 100644 --- a/app/controllers/api/web/embeds_controller.rb +++ b/app/controllers/api/web/embeds_controller.rb @@ -15,7 +15,7 @@ class Api::Web::EmbedsController < Api::Web::BaseController return not_found if oembed.nil? begin - oembed[:html] = Formatter.instance.sanitize(oembed[:html], Sanitize::Config::MASTODON_OEMBED) + oembed[:html] = Sanitize.fragment(oembed[:html], Sanitize::Config::MASTODON_OEMBED) rescue ArgumentError return not_found end diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb index bb2374c0e..f0becf8bd 100644 --- a/app/helpers/accounts_helper.rb +++ b/app/helpers/accounts_helper.rb @@ -2,10 +2,12 @@ module AccountsHelper def display_name(account, **options) + str = account.display_name.presence || account.username + if options[:custom_emojify] - Formatter.instance.format_display_name(account, **options) + prerender_custom_emojis(h(str), account.emojis) else - account.display_name.presence || account.username + str end end diff --git a/app/helpers/admin/trends/statuses_helper.rb b/app/helpers/admin/trends/statuses_helper.rb index d16e3dd12..214c1e2a6 100644 --- a/app/helpers/admin/trends/statuses_helper.rb +++ b/app/helpers/admin/trends/statuses_helper.rb @@ -12,9 +12,6 @@ module Admin::Trends::StatusesHelper return '' if text.blank? - html = Formatter.instance.send(:encode, text) - html = Formatter.instance.send(:encode_custom_emojis, html, status.emojis, prefers_autoplay?) - - html.html_safe # rubocop:disable Rails/OutputSafety + prerender_custom_emojis(h(text), status.emojis) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index eace78af6..d482ad1a2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -240,4 +240,8 @@ module ApplicationHelper end end.values end + + def prerender_custom_emojis(html, custom_emojis) + EmojiFormatter.new(html, custom_emojis, animate: prefers_autoplay?).to_s + end end diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb new file mode 100644 index 000000000..53e100dd2 --- /dev/null +++ b/app/helpers/formatting_helper.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module FormattingHelper + def html_aware_format(text, local, options = {}) + HtmlAwareFormatter.new(text, local, options).to_s + end + + def linkify(text, options = {}) + TextFormatter.new(text, options).to_s + end + + def extract_status_plain_text(status) + PlainTextFormatter.new(status.text, status.local?).to_s + end + module_function :extract_status_plain_text + + def status_content_format(status) + html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []), content_type: status.content_type) + end + + def account_bio_format(account) + html_aware_format(account.note, account.local?) + end + + def account_field_value_format(field, with_rel_me: true) + html_aware_format(field.value, field.account.local?, with_rel_me: with_rel_me, with_domains: true, multiline: false) + end +end diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb index fb24a1b28..f95f46a56 100644 --- a/app/helpers/routing_helper.rb +++ b/app/helpers/routing_helper.rb @@ -2,6 +2,7 @@ module RoutingHelper extend ActiveSupport::Concern + include Rails.application.routes.url_helpers include ActionView::Helpers::AssetTagHelper include Webpacker::Helper @@ -22,8 +23,6 @@ module RoutingHelper full_asset_url(asset_pack_path(source, **options)) end - private - def use_storage? Rails.configuration.x.use_s3 || Rails.configuration.x.use_swift end diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index d328f89b7..e92b4c839 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -113,20 +113,6 @@ module StatusesHelper end end - private - - def simplified_text(text) - text.dup.tap do |new_text| - URI.extract(new_text).each do |url| - new_text.gsub!(url, '') - end - - new_text.gsub!(Account::MENTION_RE, '') - new_text.gsub!(Tag::HASHTAG_RE, '') - new_text.gsub!(/\s+/, '') - end - end - def embedded_view? params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION end diff --git a/app/javascript/flavours/glitch/components/admin/Counter.js b/app/javascript/flavours/glitch/components/admin/Counter.js index ecb242950..a4d6cef41 100644 --- a/app/javascript/flavours/glitch/components/admin/Counter.js +++ b/app/javascript/flavours/glitch/components/admin/Counter.js @@ -33,6 +33,7 @@ export default class Counter extends React.PureComponent { label: PropTypes.string.isRequired, href: PropTypes.string, params: PropTypes.object, + target: PropTypes.string, }; state = { @@ -54,7 +55,7 @@ export default class Counter extends React.PureComponent { } render () { - const { label, href } = this.props; + const { label, href, target } = this.props; const { loading, data } = this.state; let content; @@ -100,7 +101,7 @@ export default class Counter extends React.PureComponent { if (href) { return ( - + {inner} ); diff --git a/app/javascript/mastodon/components/admin/Counter.js b/app/javascript/mastodon/components/admin/Counter.js index 6edb7bcfc..5a5b2b869 100644 --- a/app/javascript/mastodon/components/admin/Counter.js +++ b/app/javascript/mastodon/components/admin/Counter.js @@ -33,6 +33,7 @@ export default class Counter extends React.PureComponent { label: PropTypes.string.isRequired, href: PropTypes.string, params: PropTypes.object, + target: PropTypes.string, }; state = { @@ -54,7 +55,7 @@ export default class Counter extends React.PureComponent { } render () { - const { label, href } = this.props; + const { label, href, target } = this.props; const { loading, data } = this.state; let content; @@ -100,7 +101,7 @@ export default class Counter extends React.PureComponent { if (href) { return ( - + {inner} ); diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index d48780042..c6ffaa6f2 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -75,7 +75,7 @@ "column.domain_blocks": "Blokované domény", "column.favourites": "Oblíbené", "column.follow_requests": "Žádosti o sledování", - "column.home": "Domů", + "column.home": "Domovská časová osa", "column.lists": "Seznamy", "column.mutes": "Skrytí uživatelé", "column.notifications": "Oznámení", @@ -294,7 +294,7 @@ "navigation_bar.discover": "Objevujte", "navigation_bar.domain_blocks": "Blokované domény", "navigation_bar.edit_profile": "Upravit profil", - "navigation_bar.explore": "Explore", + "navigation_bar.explore": "Objevování", "navigation_bar.favourites": "Oblíbené", "navigation_bar.filters": "Skrytá slova", "navigation_bar.follow_requests": "Žádosti o sledování", @@ -318,7 +318,7 @@ "notification.poll": "Anketa, ve které jste hlasovali, skončila", "notification.reblog": "Uživatel {name} boostnul váš příspěvek", "notification.status": "Nový příspěvek od {name}", - "notification.update": "{name} edited a post", + "notification.update": "uživatel {name} upravil příspěvek", "notifications.clear": "Smazat oznámení", "notifications.clear_confirmation": "Opravdu chcete trvale smazat všechna vaše oznámení?", "notifications.column_settings.admin.sign_up": "New sign-ups:", @@ -338,7 +338,7 @@ "notifications.column_settings.status": "Nové příspěvky:", "notifications.column_settings.unread_notifications.category": "Nepřečtená oznámení", "notifications.column_settings.unread_notifications.highlight": "Zvýraznit nepřečtená oznámení", - "notifications.column_settings.update": "Edits:", + "notifications.column_settings.update": "Úpravy:", "notifications.filter.all": "Vše", "notifications.filter.boosts": "Boosty", "notifications.filter.favourites": "Oblíbení", @@ -380,7 +380,7 @@ "relative_time.days": "{number} d", "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago", "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago", - "relative_time.full.just_now": "just now", + "relative_time.full.just_now": "právě teď", "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago", "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago", "relative_time.hours": "{number} h", @@ -391,11 +391,11 @@ "reply_indicator.cancel": "Zrušit", "report.block": "Block", "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.", - "report.categories.other": "Other", + "report.categories.other": "Ostatní", "report.categories.spam": "Spam", - "report.categories.violation": "Content violates one or more server rules", - "report.category.subtitle": "Choose the best match", - "report.category.title": "Tell us what's going on with this {type}", + "report.categories.violation": "Obsah porušuje jedno nebo více pravidel serveru", + "report.category.subtitle": "Vyberte nejbližší možnost", + "report.category.title": "Povězte nám, proč chcete {type} nahlásit", "report.category.title_account": "profile", "report.category.title_status": "post", "report.close": "Done", @@ -404,20 +404,20 @@ "report.forward_hint": "Tento účet je z jiného serveru. Chcete na něj také poslat anonymizovanou kopii hlášení?", "report.mute": "Mute", "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.", - "report.next": "Next", + "report.next": "Dále", "report.placeholder": "Dodatečné komentáře", - "report.reasons.dislike": "I don't like it", - "report.reasons.dislike_description": "It is not something you want to see", - "report.reasons.other": "It's something else", - "report.reasons.other_description": "The issue does not fit into other categories", - "report.reasons.spam": "It's spam", - "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies", - "report.reasons.violation": "It violates server rules", - "report.reasons.violation_description": "You are aware that it breaks specific rules", - "report.rules.subtitle": "Select all that apply", - "report.rules.title": "Which rules are being violated?", - "report.statuses.subtitle": "Select all that apply", - "report.statuses.title": "Are there any posts that back up this report?", + "report.reasons.dislike": "Nelíbí se mi", + "report.reasons.dislike_description": "Není to něco, co chcete vidět", + "report.reasons.other": "Jde o něco jiného", + "report.reasons.other_description": "Problém neodpovídá ostatním kategoriím", + "report.reasons.spam": "Je to spam", + "report.reasons.spam_description": "Škodlivé odkazy, falešné interakce nebo opakované odpovědi", + "report.reasons.violation": "Porušuje pravidla serveru", + "report.reasons.violation_description": "Máte za to, že porušuje konkrétní pravidla", + "report.rules.subtitle": "Vyberte všechna relevantní", + "report.rules.title": "Která pravidla porušuje?", + "report.statuses.subtitle": "Vyberte všechny relevantní", + "report.statuses.title": "Existují příspěvky dokládající toto hlášení?", "report.submit": "Odeslat", "report.target": "Nahlášení uživatele {target}", "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:", @@ -490,7 +490,7 @@ "suggestions.dismiss": "Odmítnout návrh", "suggestions.header": "Mohlo by vás zajímat…", "tabs_bar.federated_timeline": "Federovaná", - "tabs_bar.home": "Domů", + "tabs_bar.home": "Domovská", "tabs_bar.local_timeline": "Místní", "tabs_bar.notifications": "Oznámení", "tabs_bar.search": "Hledat", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 820b90640..73a65ed78 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -3,16 +3,16 @@ "account.add_or_remove_from_list": "افزودن یا برداشتن از سیاههها", "account.badges.bot": "روبات", "account.badges.group": "گروه", - "account.block": "مسدود کردن @{name}", + "account.block": "مسدود کردن @{name}", "account.block_domain": "مسدود کردن دامنهٔ {domain}", "account.blocked": "مسدود", "account.browse_more_on_origin_server": "مرور بیشتر روی نمایهٔ اصلی", "account.cancel_follow_request": "لغو درخواست پیگیری", - "account.direct": "پیام مستقیم به @{name}", - "account.disable_notifications": "آگاهی به من هنگام فرستادنهای @{name} پایان یابد", + "account.direct": "پیام مستقیم به @{name}", + "account.disable_notifications": "آگاه کردن من هنگام فرستههای @{name} را متوقّف کن", "account.domain_blocked": "دامنه مسدود شد", "account.edit_profile": "ویرایش نمایه", - "account.enable_notifications": "هنگام فرستههای @{name} مرا آگاه کن", + "account.enable_notifications": "هنگام فرستههای @{name} مرا آگاه کن", "account.endorse": "معرّفی در نمایه", "account.follow": "پیگیری", "account.followers": "پیگیرندگان", @@ -22,34 +22,34 @@ "account.following_counter": "{count, plural, one {{counter} پیگرفته} other {{counter} پیگرفته}}", "account.follows.empty": "این کاربر هنوز پیگیر کسی نیست.", "account.follows_you": "پی میگیردتان", - "account.hide_reblogs": "نهفتن تقویتهای @{name}", + "account.hide_reblogs": "نهفتن تقویتهای @{name}", "account.joined": "پیوسته از {date}", "account.link_verified_on": "مالکیت این پیوند در {date} بررسی شد", "account.locked_info": "این حساب خصوصی است. صاحبش تصمیم میگیرد که چه کسی پیگیرش باشد.", "account.media": "رسانه", - "account.mention": "نامبردن از @{name}", + "account.mention": "نامبردن از @{name}", "account.moved_to": "{name} منتقل شده به:", - "account.mute": "خموشاندن @{name}", - "account.mute_notifications": "خموشاندن آگاهیها از @{name}", + "account.mute": "خموشاندن @{name}", + "account.mute_notifications": "خموشاندن آگاهیهای @{name}", "account.muted": "خموش", "account.posts": "فرسته", "account.posts_with_replies": "فرستهها و پاسخها", - "account.report": "گزارش @{name}", + "account.report": "گزارش @{name}", "account.requested": "منتظر پذیرش است. برای لغو درخواست پیگیری کلیک کنید", - "account.share": "همرسانی نمایهٔ @{name}", - "account.show_reblogs": "نمایش تقویتهای @{name}", + "account.share": "همرسانی نمایهٔ @{name}", + "account.show_reblogs": "نمایش تقویتهای @{name}", "account.statuses_counter": "{count, plural, one {{counter} فرسته} other {{counter} فرسته}}", - "account.unblock": "رفع مسدودیت @{name}", + "account.unblock": "رفع مسدودیت @{name}", "account.unblock_domain": "رفع مسدودیت دامنهٔ {domain}", "account.unblock_short": "رفع مسدودیت", "account.unendorse": "معرّفی نکردن در نمایه", "account.unfollow": "ناپیگیری", - "account.unmute": "ناخموشی @{name}", - "account.unmute_notifications": "ناخموشی آگاهیها از @{name}", + "account.unmute": "ناخموشی @{name}", + "account.unmute_notifications": "ناخموشی آگاهیهای @{name}", "account.unmute_short": "ناخموشی", "account_note.placeholder": "برای افزودن یادداشت کلیک کنید", - "admin.dashboard.daily_retention": "User retention rate by day after sign-up", - "admin.dashboard.monthly_retention": "User retention rate by month after sign-up", + "admin.dashboard.daily_retention": "نرخ حفظ کاربر در روز پس از ثبت نام", + "admin.dashboard.monthly_retention": "نرخ حفظ کاربر در ماه پس از ثبت نام", "admin.dashboard.retention.average": "میانگین", "admin.dashboard.retention.cohort": "ماه ثبتنام", "admin.dashboard.retention.cohort_size": "کاربران جدید", @@ -79,13 +79,13 @@ "column.lists": "سیاههها", "column.mutes": "کاربران خموش", "column.notifications": "آگاهیها", - "column.pins": "فرستههای سنجاقشده", + "column.pins": "فرستههای سنجاق شده", "column.public": "خط زمانی همگانی", "column_back_button.label": "بازگشت", "column_header.hide_settings": "نهفتن تنظیمات", "column_header.moveLeft_settings": "جابهجایی ستون به چپ", "column_header.moveRight_settings": "جابهجایی ستون به راست", - "column_header.pin": "سنجاقکردن", + "column_header.pin": "سنجاق کردن", "column_header.show_settings": "نمایش تنظیمات", "column_header.unpin": "برداشتن سنجاق", "column_subheading.settings": "تنظیمات", @@ -94,7 +94,7 @@ "community.column_settings.remote_only": "تنها دوردست", "compose_form.direct_message_warning": "این فرسته تنها به کاربرانی که از آنها نام برده شده فرستاده خواهد شد.", "compose_form.direct_message_warning_learn_more": "بیشتر بدانید", - "compose_form.hashtag_warning": "از آنجا که این فرسته فهرستنشده است، در نتایج جستوجوی برچسبها پیدا نخواهد شد. تنها فرستههای عمومی را میتوان با جستوجوی برچسب یافت.", + "compose_form.hashtag_warning": "از آنجا که این فرسته فهرست نشده است، در نتایج جستوجوی هشتگها پیدا نخواهد شد. تنها فرستههای عمومی را میتوان با جستوجوی هشتگ یافت.", "compose_form.lock_disclaimer": "حسابتان {locked} نیست. هر کسی میتواند پیگیرتان شده و فرستههای ویژهٔ پیگیرانتان را ببیند.", "compose_form.lock_disclaimer.lock": "قفلشده", "compose_form.placeholder": "تازه چه خبر؟", @@ -144,7 +144,7 @@ "directory.local": "تنها از {domain}", "directory.new_arrivals": "تازهواردان", "directory.recently_active": "کاربران فعال اخیر", - "embed.instructions": "برای جاگذاری این فرسته در سایت خودتان، کد زیر را کپی کنید.", + "embed.instructions": "برای جاسازی این فرسته در سایت خودتان، کد زیر را رونوشت کنید.", "embed.preview": "این گونه دیده خواهد شد:", "emoji_button.activity": "فعالیت", "emoji_button.custom": "سفارشی", @@ -164,11 +164,11 @@ "empty_column.account_timeline": "هیچ فرستهای اینجا نیست!", "empty_column.account_unavailable": "نمایهٔ موجود نیست", "empty_column.blocks": "هنوز کسی را مسدود نکردهاید.", - "empty_column.bookmarked_statuses": "هنوز هیچ فرستهٔ نشانشدهای ندارید. هنگامی که فرستهای را نشانکنید، اینجا نشان داده خواهد شد.", + "empty_column.bookmarked_statuses": "هنوز هیچ فرستهٔ نشانهگذاری شدهای ندارید. هنگامی که فرستهای را نشانهگذاری کنید، اینجا نشان داده خواهد شد.", "empty_column.community": "خط زمانی محلّی خالی است. چیزی بنویسید تا چرخش بچرخد!", "empty_column.direct": "هنوز هیچ پیام مستقیمی ندارید. هنگامی که چنین پیامی بگیرید یا بفرستید اینجا نشان داده خواهد شد.", "empty_column.domain_blocks": "هنوز هیچ دامنهای مسدود نشده است.", - "empty_column.explore_statuses": "Nothing is trending right now. Check back later!", + "empty_column.explore_statuses": "الآن چیزی پرطرفدار نیست. بعداً دوباره بررسی کنید!", "empty_column.favourited_statuses": "شما هنوز هیچ فرستهای را نپسندیدهاید. هنگامی که فرستهای را بپسندید، اینجا نشان داده خواهد شد.", "empty_column.favourites": "هنوز هیچ کسی این فرسته را نپسندیده است. هنگامی که کسی آن را بپسندد، اینجا نشان داده خواهد شد.", "empty_column.follow_recommendations": "ظاهرا هیچ پیشنهادی برای شما نمیتوانیم تولید کنیم. میتوانید از امکان جستوجو برای یافتن افرادی که ممکن است بشناسید و یا کاوش میان برچسبهای داغ استفاده کنید.", @@ -247,7 +247,7 @@ "keyboard_shortcuts.my_profile": "گشودن نمایهتان", "keyboard_shortcuts.notifications": "گشودن ستون آگاهیها", "keyboard_shortcuts.open_media": "گشودن رسانه", - "keyboard_shortcuts.pinned": "گشودن سیاههٔ فرستههای سنجاق شده", + "keyboard_shortcuts.pinned": "گشودن فهرست فرستههای سنجاق شده", "keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده", "keyboard_shortcuts.reply": "پاسخ به فرسته", "keyboard_shortcuts.requests": "گشودن سیاههٔ درخواستهای پیگیری", @@ -305,7 +305,7 @@ "navigation_bar.logout": "خروج", "navigation_bar.mutes": "کاربران خموشانده", "navigation_bar.personal": "شخصی", - "navigation_bar.pins": "فرستههای سنجاقشده", + "navigation_bar.pins": "فرستههای سنجاق شده", "navigation_bar.preferences": "ترجیحات", "navigation_bar.public_timeline": "خط زمانی همگانی", "navigation_bar.security": "امنیت", @@ -392,40 +392,40 @@ "report.block": "مسدود کردن", "report.block_explanation": "شما فرستههایشان را نخواهید دید. آنها نمیتوانند فرستههایتان را ببینند یا شما را پیبگیرند. آنها میتوانند بگویند که مسدود شدهاند.", "report.categories.other": "غیره", - "report.categories.spam": "Spam", - "report.categories.violation": "Content violates one or more server rules", - "report.category.subtitle": "Choose the best match", - "report.category.title": "Tell us what's going on with this {type}", - "report.category.title_account": "profile", - "report.category.title_status": "post", - "report.close": "Done", - "report.comment.title": "Is there anything else you think we should know?", + "report.categories.spam": "هرزنامه", + "report.categories.violation": "محتوا یک یا چند قانون کارساز را نقض میکند", + "report.category.subtitle": "منطبقترین را انتخاب کنید", + "report.category.title": "به ما بگویید با این {type} چه مشکلی دارید", + "report.category.title_account": "نمایه", + "report.category.title_status": "فرسته", + "report.close": "انجام شد", + "report.comment.title": "آیا چیز دیگری هست که فکر میکنید باید بدانیم؟", "report.forward": "فرستادن به {target}", "report.forward_hint": "این حساب در کارساز دیگری ثبت شده. آیا میخواهید رونوشتی ناشناس از این گزارش به آنجا هم فرستاده شود؟", - "report.mute": "Mute", - "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.", - "report.next": "Next", + "report.mute": "خموش", + "report.mute_explanation": "شما فرستههای آنها را نخواهید دید. آنها همچنان میتوانند شما را پیبگیرند و فرستههایتان را ببینند و نمیدانند که خموش شدهاند.", + "report.next": "بعدی", "report.placeholder": "توضیحات اضافه", - "report.reasons.dislike": "I don't like it", - "report.reasons.dislike_description": "It is not something you want to see", - "report.reasons.other": "It's something else", - "report.reasons.other_description": "The issue does not fit into other categories", - "report.reasons.spam": "It's spam", - "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies", - "report.reasons.violation": "It violates server rules", - "report.reasons.violation_description": "You are aware that it breaks specific rules", - "report.rules.subtitle": "Select all that apply", - "report.rules.title": "Which rules are being violated?", - "report.statuses.subtitle": "Select all that apply", - "report.statuses.title": "Are there any posts that back up this report?", + "report.reasons.dislike": "من آن را دوست ندارم", + "report.reasons.dislike_description": "این چیزی نیست که بخواهید ببینید", + "report.reasons.other": "بخواطر چیز دیگری است", + "report.reasons.other_description": "این موضوع در دستهبندیهای دیگر نمیگنجد", + "report.reasons.spam": "این هرزنامه است", + "report.reasons.spam_description": "پیوندهای مخرب، تعامل جعلی یا پاسخهای تکراری", + "report.reasons.violation": "قوانین کارساز را نقض میکند", + "report.reasons.violation_description": "شما آگاه هستید که قوانین خاصی را زیر پا میگذارد", + "report.rules.subtitle": "همهٔ موارد انجام شده را برگزینید", + "report.rules.title": "کدام قوانین نقض شدهاند؟", + "report.statuses.subtitle": "همهٔ موارد انجام شده را برگزینید", + "report.statuses.title": "آیا فرستهای وجود دارد که از این گزارش پشتیبانی کند؟", "report.submit": "فرستادن", "report.target": "در حال گزارش {target}", - "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:", - "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:", - "report.thanks.title": "Don't want to see this?", - "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.", - "report.unfollow": "Unfollow @{name}", - "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.", + "report.thanks.take_action": "در اینجا گزینههایی برای کنترل آنچه در ماستودون میبینید، وجود دارد:", + "report.thanks.take_action_actionable": "در حالی که ما این مورد را بررسی میکنیم، میتوانید علیه @{name} اقدام کنید:", + "report.thanks.title": "نمیخواهید این را ببینید؟", + "report.thanks.title_actionable": "ممنون بابت گزارش، ما آن را بررسی خواهیم کرد.", + "report.unfollow": "ناپیگیری @{name}", + "report.unfollow_explanation": "شما این حساب را پیگرفتهاید، برای اینکه دیگر فرستههایش را در خوراک خانهتان نبینید؛ آن را پینگیرید.", "search.placeholder": "جستوجو", "search_popout.search_format": "راهنمای جستوجوی پیشرفته", "search_popout.tips.full_text": "جستوجوی متنی ساده فرستههایی که نوشته، پسندیده، تقویتکرده یا در آنها نامبرده شدهاید را به علاوهٔ نامهای کاربری، نامهای نمایشی و برچسبها برمیگرداند.", @@ -434,39 +434,39 @@ "search_popout.tips.text": "جستوجوی متنی ساده برای نامها، نامهای کاربری، و برچسبها", "search_popout.tips.user": "کاربر", "search_results.accounts": "افراد", - "search_results.all": "All", + "search_results.all": "همه", "search_results.hashtags": "برچسبها", - "search_results.nothing_found": "Could not find anything for these search terms", + "search_results.nothing_found": "چیزی برای این عبارت جستوجو یافت نشد", "search_results.statuses": "فرستهها", - "search_results.statuses_fts_disabled": "جستوجوی محتوای فرستهها در این کارساز ماستودون فعال نشده است.", + "search_results.statuses_fts_disabled": "جستوجوی محتوای فرستهها در این کارساز ماستودون به کار انداخته نشده است.", "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}", - "status.admin_account": "گشودن واسط مدیریت برای @{name}", + "status.admin_account": "گشودن واسط مدیریت برای @{name}", "status.admin_status": "گشودن این فرسته در واسط مدیریت", - "status.block": "مسدود کردن @{name}", + "status.block": "مسدود کردن @{name}", "status.bookmark": "نشانک", - "status.cancel_reblog_private": "لغو تقویت", + "status.cancel_reblog_private": "ناتقویت", "status.cannot_reblog": "این فرسته قابل تقویت نیست", - "status.copy": "رونویسی از نشانی فرسته", + "status.copy": "رونوشت پیوند فرسته", "status.delete": "حذف", "status.detailed_status": "نمایش کامل گفتگو", - "status.direct": "پیام مستقیم به @{name}", - "status.edit": "Edit", - "status.edited": "Edited {date}", - "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}", - "status.embed": "جاگذاری", + "status.direct": "پیام مستقیم به @{name}", + "status.edit": "ویرایش", + "status.edited": "ویرایش شده در {date}", + "status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد", + "status.embed": "جاسازی", "status.favourite": "پسندیدن", "status.filtered": "پالوده", - "status.history.created": "{name} created {date}", - "status.history.edited": "{name} edited {date}", + "status.history.created": "توسط {name} در {date} ایجاد شد", + "status.history.edited": "توسط {name} در {date} ویرایش شد", "status.load_more": "بار کردن بیشتر", "status.media_hidden": "رسانهٔ نهفته", - "status.mention": "نامبردن از @{name}", + "status.mention": "نامبردن از @{name}", "status.more": "بیشتر", - "status.mute": "خموشاندن @{name}", + "status.mute": "خموشاندن @{name}", "status.mute_conversation": "خموشاندن گفتوگو", "status.open": "گسترش این فرسته", - "status.pin": "سنجاقکردن در نمایه", - "status.pinned": "فرستهٔ سنجاقشده", + "status.pin": "سنجاق کردن در نمایه", + "status.pinned": "فرستهٔ سنجاق شده", "status.read_more": "بیشتر بخوانید", "status.reblog": "تقویت", "status.reblog_private": "تقویت برای مخاطبان نخستین", @@ -476,7 +476,7 @@ "status.remove_bookmark": "برداشتن نشانک", "status.reply": "پاسخ", "status.replyAll": "پاسخ به رشته", - "status.report": "گزارش @{name}", + "status.report": "گزارش @{name}", "status.sensitive_warning": "محتوای حساس", "status.share": "همرسانی", "status.show_less": "نمایش کمتر", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 65d3c6ea1..13d89502b 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -229,7 +229,7 @@ "keyboard_shortcuts.blocked": "Ouvrir la liste des comptes bloqués", "keyboard_shortcuts.boost": "Partager le message", "keyboard_shortcuts.column": "Se placer dans une colonne", - "keyboard_shortcuts.compose": "se placer dans la zone de rédaction", + "keyboard_shortcuts.compose": "Se placer dans la zone de rédaction", "keyboard_shortcuts.description": "Description", "keyboard_shortcuts.direct": "Ouvrir la colonne des messages directs", "keyboard_shortcuts.down": "Descendre dans la liste", @@ -246,7 +246,7 @@ "keyboard_shortcuts.muted": "Ouvrir la liste des comptes masqués", "keyboard_shortcuts.my_profile": "Ouvrir votre profil", "keyboard_shortcuts.notifications": "Ouvrir la colonne de notifications", - "keyboard_shortcuts.open_media": "ouvrir le média", + "keyboard_shortcuts.open_media": "Ouvrir le média", "keyboard_shortcuts.pinned": "Ouvrir la liste des messages épinglés", "keyboard_shortcuts.profile": "Ouvrir le profil de l’auteur·rice", "keyboard_shortcuts.reply": "Répondre au message", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index aae73a23e..9ca41b2f6 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -18,7 +18,7 @@ "account.followers": "Luchd-leantainn", "account.followers.empty": "Chan eil neach sam bith a’ leantainn air a’ chleachdaiche seo fhathast.", "account.followers_counter": "{count, plural, one {{counter} neach-leantainn} two {{counter} neach-leantainn} few {{counter} luchd-leantainn} other {{counter} luchd-leantainn}}", - "account.following": "Following", + "account.following": "A’ leantainn", "account.following_counter": "{count, plural, one {A’ leantainn air {counter}} two {A’ leantainn air {counter}} few {A’ leantainn air {counter}} other {A’ leantainn air {counter}}}", "account.follows.empty": "Chan eil an cleachdaiche seo a’ leantainn air neach sam bith fhathast.", "account.follows_you": "’Gad leantainn", @@ -41,12 +41,12 @@ "account.statuses_counter": "{count, plural, one {{counter} phost} two {{counter} phost} few {{counter} postaichean} other {{counter} post}}", "account.unblock": "Dì-bhac @{name}", "account.unblock_domain": "Dì-bhac an àrainn {domain}", - "account.unblock_short": "Unblock", + "account.unblock_short": "Dì-bhac", "account.unendorse": "Na brosnaich air a’ phròifil", "account.unfollow": "Na lean tuilleadh", "account.unmute": "Dì-mhùch @{name}", "account.unmute_notifications": "Dì-mhùch na brathan o @{name}", - "account.unmute_short": "Unmute", + "account.unmute_short": "Dì-mhùch", "account_note.placeholder": "Briog airson nòta a chur ris", "admin.dashboard.daily_retention": "Reat glèidheadh nan cleachdaichean às dèidh an clàradh a-rèir latha", "admin.dashboard.monthly_retention": "Reat glèidheadh nan cleachdaichean às dèidh an clàradh a-rèir mìos", @@ -294,7 +294,7 @@ "navigation_bar.discover": "Fidir", "navigation_bar.domain_blocks": "Àrainnean bacte", "navigation_bar.edit_profile": "Deasaich a’ phròifil", - "navigation_bar.explore": "Explore", + "navigation_bar.explore": "Rùraich", "navigation_bar.favourites": "Na h-annsachdan", "navigation_bar.filters": "Faclan mùchte", "navigation_bar.follow_requests": "Iarrtasan leantainn", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 4a388f92b..e08c8bb33 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -192,7 +192,7 @@ "errors.unexpected_crash.copy_stacktrace": "スタックトレースをクリップボードにコピー", "errors.unexpected_crash.report_issue": "問題を報告", "explore.search_results": "検索結果", - "explore.suggested_follows": "あなたに", + "explore.suggested_follows": "おすすめ", "explore.title": "エクスプローラー", "explore.trending_links": "ニュース", "explore.trending_statuses": "投稿", diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json index 0298c51ec..2bf8fc520 100644 --- a/app/javascript/mastodon/locales/ku.json +++ b/app/javascript/mastodon/locales/ku.json @@ -342,7 +342,7 @@ "notifications.filter.all": "Hemû", "notifications.filter.boosts": "Bilindkirî", "notifications.filter.favourites": "Bijarte", - "notifications.filter.follows": "Şopîner", + "notifications.filter.follows": "Dişopîne", "notifications.filter.mentions": "Qalkirin", "notifications.filter.polls": "Encamên rapirsiyê", "notifications.filter.statuses": "Ji kesên tu dişopînî re rojanekirin", @@ -501,7 +501,7 @@ "time_remaining.seconds": "{number, plural, one {# çirke} other {# çirke}} maye", "timeline_hint.remote_resource_not_displayed": "{resource} Ji rajekerên din nayê dîtin.", "timeline_hint.resources.followers": "Şopîner", - "timeline_hint.resources.follows": "Şopîner", + "timeline_hint.resources.follows": "Dişopîne", "timeline_hint.resources.statuses": "Şandiyên kevn", "trends.counter_by_accounts": "{count, plural, one {{counter} kes} other {{counter} kes}} diaxivin", "trends.trending_now": "Rojev", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index f30755286..d1cfdd5f4 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -9,44 +9,44 @@ "account.browse_more_on_origin_server": "Meer op het originele profiel bekijken", "account.cancel_follow_request": "Volgverzoek annuleren", "account.direct": "@{name} een direct bericht sturen", - "account.disable_notifications": "Geef geen melding meer wanneer @{name} toot", + "account.disable_notifications": "Geef geen melding meer wanneer @{name} een bericht plaatst", "account.domain_blocked": "Domein geblokkeerd", "account.edit_profile": "Profiel bewerken", - "account.enable_notifications": "Geef een melding wanneer @{name} toot", + "account.enable_notifications": "Geef een melding wanneer @{name} een bericht plaatst", "account.endorse": "Op profiel weergeven", "account.follow": "Volgen", "account.followers": "Volgers", "account.followers.empty": "Niemand volgt nog deze gebruiker.", "account.followers_counter": "{count, plural, one {{counter} volger} other {{counter} volgers}}", - "account.following": "Following", + "account.following": "Volgend", "account.following_counter": "{count, plural, one {{counter} volgend} other {{counter} volgend}}", "account.follows.empty": "Deze gebruiker volgt nog niemand.", "account.follows_you": "Volgt jou", "account.hide_reblogs": "Boosts van @{name} verbergen", "account.joined": "Geregistreerd in {date}", "account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}", - "account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.", + "account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie diegene kan volgen.", "account.media": "Media", "account.mention": "@{name} vermelden", "account.moved_to": "{name} is verhuisd naar:", "account.mute": "@{name} negeren", "account.mute_notifications": "Meldingen van @{name} negeren", "account.muted": "Genegeerd", - "account.posts": "Toots", - "account.posts_with_replies": "Toots en reacties", + "account.posts": "Berichten", + "account.posts_with_replies": "Berichten en reacties", "account.report": "@{name} rapporteren", "account.requested": "Wacht op goedkeuring. Klik om het volgverzoek te annuleren", "account.share": "Profiel van @{name} delen", "account.show_reblogs": "Boosts van @{name} tonen", - "account.statuses_counter": "{count, plural, one {{counter} toot} other {{counter} toots}}", + "account.statuses_counter": "{count, plural, one {{counter} bericht} other {{counter} berichten}}", "account.unblock": "@{name} deblokkeren", "account.unblock_domain": "{domain} niet langer verbergen", - "account.unblock_short": "Unblock", + "account.unblock_short": "Deblokkeren", "account.unendorse": "Niet op profiel weergeven", "account.unfollow": "Ontvolgen", "account.unmute": "@{name} niet langer negeren", "account.unmute_notifications": "Meldingen van @{name} niet langer negeren", - "account.unmute_short": "Unmute", + "account.unmute_short": "Niet langer negeren", "account_note.placeholder": "Klik om een opmerking toe te voegen", "admin.dashboard.daily_retention": "User retention rate by day after sign-up", "admin.dashboard.monthly_retention": "User retention rate by month after sign-up", @@ -79,7 +79,7 @@ "column.lists": "Lijsten", "column.mutes": "Genegeerde gebruikers", "column.notifications": "Meldingen", - "column.pins": "Vastgezette toots", + "column.pins": "Vastgezette berichten", "column.public": "Globale tijdlijn", "column_back_button.label": "Terug", "column_header.hide_settings": "Instellingen verbergen", @@ -92,10 +92,10 @@ "community.column_settings.local_only": "Alleen lokaal", "community.column_settings.media_only": "Alleen media", "community.column_settings.remote_only": "Alleen andere servers", - "compose_form.direct_message_warning": "Deze toot wordt alleen naar vermelde gebruikers verstuurd.", + "compose_form.direct_message_warning": "Dit bericht wordt alleen naar vermelde gebruikers verstuurd.", "compose_form.direct_message_warning_learn_more": "Meer leren", - "compose_form.hashtag_warning": "Deze toot valt niet onder een hashtag te bekijken, omdat deze niet op openbare tijdlijnen wordt getoond. Alleen openbare toots kunnen via hashtags gevonden worden.", - "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en kan de toots zien die je alleen aan jouw volgers hebt gericht.", + "compose_form.hashtag_warning": "Dit bericht valt niet onder een hashtag te bekijken, omdat deze niet op openbare tijdlijnen wordt getoond. Alleen openbare berichten kunnen via hashtags gevonden worden.", + "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en kan de berichten zien die je alleen aan jouw volgers hebt gericht.", "compose_form.lock_disclaimer.lock": "besloten", "compose_form.placeholder": "Wat wil je kwijt?", "compose_form.poll.add_option": "Keuze toevoegen", @@ -106,7 +106,7 @@ "compose_form.poll.switch_to_single": "Poll wijzigen om een enkele keuze toe te staan", "compose_form.publish": "Toot", "compose_form.publish_loud": "{publish}!", - "compose_form.save_changes": "Save changes", + "compose_form.save_changes": "Wijzigingen opslaan", "compose_form.sensitive.hide": "{count, plural, one {Media als gevoelig markeren} other {Media als gevoelig markeren}}", "compose_form.sensitive.marked": "{count, plural, one {Media is als gevoelig gemarkeerd} other {Media is als gevoelig gemarkeerd}}", "compose_form.sensitive.unmarked": "{count, plural, one {Media is niet als gevoelig gemarkeerd} other {Media is niet als gevoelig gemarkeerd}}", @@ -118,22 +118,22 @@ "confirmations.block.confirm": "Blokkeren", "confirmations.block.message": "Weet je het zeker dat je {name} wilt blokkeren?", "confirmations.delete.confirm": "Verwijderen", - "confirmations.delete.message": "Weet je het zeker dat je deze toot wilt verwijderen?", + "confirmations.delete.message": "Weet je het zeker dat je dit bericht wilt verwijderen?", "confirmations.delete_list.confirm": "Verwijderen", "confirmations.delete_list.message": "Weet je zeker dat je deze lijst definitief wilt verwijderen?", "confirmations.discard_edit_media.confirm": "Weggooien", "confirmations.discard_edit_media.message": "Je hebt niet-opgeslagen wijzigingen in de mediabeschrijving of voorvertonning, wil je deze toch weggooien?", "confirmations.domain_block.confirm": "Verberg alles van deze server", - "confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wilt negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en beter. Je zult geen toots van deze server op openbare tijdlijnen zien of in jouw meldingen. Jouw volgers van deze server worden verwijderd.", + "confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wilt negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en beter. Je zult geen berichten van deze server op openbare tijdlijnen zien of in jouw meldingen. Jouw volgers van deze server worden verwijderd.", "confirmations.logout.confirm": "Uitloggen", "confirmations.logout.message": "Weet je zeker dat je wilt uitloggen?", "confirmations.mute.confirm": "Negeren", - "confirmations.mute.explanation": "Dit verbergt toots van hen en toots waar hen in wordt vermeld, maar hen kan nog steeds jouw toots bekijken en jou volgen.", + "confirmations.mute.explanation": "Dit verbergt diens berichten en berichten waar diegene in wordt vermeld, maar diegene kan nog steeds jouw berichten bekijken en jou volgen.", "confirmations.mute.message": "Weet je het zeker dat je {name} wilt negeren?", "confirmations.redraft.confirm": "Verwijderen en herschrijven", - "confirmations.redraft.message": "Weet je zeker dat je deze toot wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en de reacties op de originele toot zitten niet meer aan de nieuwe toot vast.", + "confirmations.redraft.message": "Weet je zeker dat je dit bericht wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en de reacties op het originele bericht raak je kwijt.", "confirmations.reply.confirm": "Reageren", - "confirmations.reply.message": "Door nu te reageren overschrijf je de toot die je op dit moment aan het schrijven bent. Weet je zeker dat je verder wil gaan?", + "confirmations.reply.message": "Door nu te reageren overschrijf je het bericht dat je op dit moment aan het schrijven bent. Weet je zeker dat je verder wil gaan?", "confirmations.unfollow.confirm": "Ontvolgen", "confirmations.unfollow.message": "Weet je het zeker dat je {name} wilt ontvolgen?", "conversation.delete": "Gesprek verwijderen", @@ -144,7 +144,7 @@ "directory.local": "Alleen {domain}", "directory.new_arrivals": "Nieuwe accounts", "directory.recently_active": "Onlangs actief", - "embed.instructions": "Embed deze toot op jouw website, door de onderstaande code te kopiëren.", + "embed.instructions": "Embed dit bericht op jouw website door de onderstaande code te kopiëren.", "embed.preview": "Zo komt het eruit te zien:", "emoji_button.activity": "Activiteiten", "emoji_button.custom": "Lokale emoji’s", @@ -161,41 +161,41 @@ "emoji_button.symbols": "Symbolen", "emoji_button.travel": "Reizen en locaties", "empty_column.account_suspended": "Account opgeschort", - "empty_column.account_timeline": "Hier zijn geen toots!", + "empty_column.account_timeline": "Hier zijn geen berichten!", "empty_column.account_unavailable": "Profiel is niet beschikbaar", "empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.", - "empty_column.bookmarked_statuses": "Jij hebt nog geen toots aan je bladwijzers toegevoegd. Wanneer je er een aan jouw bladwijzers toevoegt, valt deze hier te zien.", - "empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de spits af te bijten!", + "empty_column.bookmarked_statuses": "Jij hebt nog geen berichten aan je bladwijzers toegevoegd. Wanneer je er een aan jouw bladwijzers toevoegt, valt deze hier te zien.", + "empty_column.community": "De lokale tijdlijn is nog leeg. Plaats een openbaar bericht om de spits af te bijten!", "empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, zijn deze hier te zien.", "empty_column.domain_blocks": "Er zijn nog geen geblokkeerde domeinen.", - "empty_column.explore_statuses": "Nothing is trending right now. Check back later!", - "empty_column.favourited_statuses": "Jij hebt nog geen favoriete toots. Wanneer je er een aan jouw favorieten toevoegt, valt deze hier te zien.", - "empty_column.favourites": "Niemand heeft deze toot nog aan hun favorieten toegevoegd. Wanneer iemand dit doet, valt dat hier te zien.", + "empty_column.explore_statuses": "Momenteel zijn er geen trends. Kom later terug!", + "empty_column.favourited_statuses": "Jij hebt nog geen favoriete berichten. Wanneer je er een aan jouw favorieten toevoegt, valt deze hier te zien.", + "empty_column.favourites": "Niemand heeft dit bericht nog aan diens favorieten toegevoegd. Wanneer iemand dit doet, valt dat hier te zien.", "empty_column.follow_recommendations": "Het lijkt er op dat er geen aanbevelingen voor jou aangemaakt kunnen worden. Je kunt proberen te zoeken naar mensen die je wellicht kent, zoeken op hashtags, de lokale en globale tijdlijnen bekijken of de gebruikersgids doorbladeren.", "empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.", "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.", "empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen. {suggestions}", "empty_column.home.suggestions": "Enkele aanbevelingen bekijken", - "empty_column.list": "Er is nog niks te zien in deze lijst. Wanneer lijstleden nieuwe toots publiceren, zijn deze hier te zien.", + "empty_column.list": "Er is nog niks te zien in deze lijst. Wanneer lijstleden nieuwe berichten plaatsen, zijn deze hier te zien.", "empty_column.lists": "Jij hebt nog geen enkele lijst. Wanneer je er eentje hebt aangemaakt, valt deze hier te zien.", "empty_column.mutes": "Jij hebt nog geen gebruikers genegeerd.", "empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.", - "empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere servers om het te vullen", + "empty_column.public": "Er is hier helemaal niks! Plaatst een openbaar bericht of volg mensen van andere servers om het te vullen", "error.unexpected_crash.explanation": "Als gevolg van een bug in onze broncode of als gevolg van een compatibiliteitsprobleem met jouw webbrowser, kan deze pagina niet goed worden weergegeven.", "error.unexpected_crash.explanation_addons": "Deze pagina kon niet correct geladen worden. Deze fout wordt waarschijnlijk door een browser-add-on of een automatische vertalingshulpmiddel veroorzaakt.", "error.unexpected_crash.next_steps": "Probeer deze pagina te vernieuwen. Wanneer dit niet helpt is het nog steeds mogelijk om Mastodon in een andere webbrowser of mobiele app te gebruiken.", "error.unexpected_crash.next_steps_addons": "Probeer deze uit te schakelen en de pagina te verversen. Wanneer dat niet helpt, kun je Mastodon nog altijd met een andere webbrowser of mobiele app gebruiken.", "errors.unexpected_crash.copy_stacktrace": "Stacktrace naar klembord kopiëren", "errors.unexpected_crash.report_issue": "Technisch probleem melden", - "explore.search_results": "Search results", - "explore.suggested_follows": "For you", - "explore.title": "Explore", - "explore.trending_links": "News", - "explore.trending_statuses": "Posts", + "explore.search_results": "Zoekresultaten", + "explore.suggested_follows": "Voor jou", + "explore.title": "Verkennen", + "explore.trending_links": "Nieuws", + "explore.trending_statuses": "Berichten", "explore.trending_tags": "Hashtags", "follow_recommendations.done": "Klaar", - "follow_recommendations.heading": "Volg mensen waarvan je graag toots wil zien! Hier zijn enkele aanbevelingen.", - "follow_recommendations.lead": "Toots van mensen die je volgt zullen in chronologische volgorde onder start verschijnen. Wees niet bang om hierin fouten te maken, want je kunt mensen op elk moment net zo eenvoudig ontvolgen!", + "follow_recommendations.heading": "Volg mensen waarvan je graag berichten wil zien! Hier zijn enkele aanbevelingen.", + "follow_recommendations.lead": "Berichten van mensen die je volgt zullen in chronologische volgorde onder start verschijnen. Wees niet bang om hierin fouten te maken, want je kunt mensen op elk moment net zo eenvoudig ontvolgen!", "follow_request.authorize": "Goedkeuren", "follow_request.reject": "Afkeuren", "follow_requests.unlocked_explanation": "Ook al is jouw account niet besloten, de medewerkers van {domain} denken dat jij misschien de volgende volgverzoeken handmatig wil controleren.", @@ -227,13 +227,13 @@ "intervals.full.minutes": "{number, plural, one {# minuut} other {# minuten}}", "keyboard_shortcuts.back": "Ga terug", "keyboard_shortcuts.blocked": "Geblokkeerde gebruikers tonen", - "keyboard_shortcuts.boost": "Toot boosten", + "keyboard_shortcuts.boost": "Bericht boosten", "keyboard_shortcuts.column": "Op één van de kolommen focussen", - "keyboard_shortcuts.compose": "Tekstveld voor toots focussen", + "keyboard_shortcuts.compose": "Tekstveld om een bericht te schrijven focussen", "keyboard_shortcuts.description": "Omschrijving", "keyboard_shortcuts.direct": "Jouw directe berichten tonen", "keyboard_shortcuts.down": "Naar beneden in de lijst bewegen", - "keyboard_shortcuts.enter": "Toot volledig tonen", + "keyboard_shortcuts.enter": "Volledig bericht tonen", "keyboard_shortcuts.favourite": "Aan jouw favorieten toevoegen", "keyboard_shortcuts.favourites": "Favorieten tonen", "keyboard_shortcuts.federated": "Globale tijdlijn tonen", @@ -247,7 +247,7 @@ "keyboard_shortcuts.my_profile": "Jouw profiel tonen", "keyboard_shortcuts.notifications": "Meldingen tonen", "keyboard_shortcuts.open_media": "Media openen", - "keyboard_shortcuts.pinned": "Jouw vastgezette toots tonen", + "keyboard_shortcuts.pinned": "Jouw vastgemaakte berichten tonen", "keyboard_shortcuts.profile": "Gebruikersprofiel auteur openen", "keyboard_shortcuts.reply": "Reageren", "keyboard_shortcuts.requests": "Jouw volgverzoeken tonen", @@ -256,7 +256,7 @@ "keyboard_shortcuts.start": "\"Aan de slag\" tonen", "keyboard_shortcuts.toggle_hidden": "Inhoudswaarschuwing tonen/verbergen", "keyboard_shortcuts.toggle_sensitivity": "Media tonen/verbergen", - "keyboard_shortcuts.toot": "Nieuwe toot schrijven", + "keyboard_shortcuts.toot": "Nieuw bericht schrijven", "keyboard_shortcuts.unfocus": "Tekst- en zoekveld ontfocussen", "keyboard_shortcuts.up": "Naar boven in de lijst bewegen", "lightbox.close": "Sluiten", @@ -289,12 +289,12 @@ "navigation_bar.blocks": "Geblokkeerde gebruikers", "navigation_bar.bookmarks": "Bladwijzers", "navigation_bar.community_timeline": "Lokale tijdlijn", - "navigation_bar.compose": "Nieuw toot schrijven", + "navigation_bar.compose": "Nieuw bericht schrijven", "navigation_bar.direct": "Directe berichten", "navigation_bar.discover": "Ontdekken", "navigation_bar.domain_blocks": "Geblokkeerde domeinen", "navigation_bar.edit_profile": "Profiel bewerken", - "navigation_bar.explore": "Explore", + "navigation_bar.explore": "Verkennen", "navigation_bar.favourites": "Favorieten", "navigation_bar.filters": "Filters", "navigation_bar.follow_requests": "Volgverzoeken", @@ -305,23 +305,23 @@ "navigation_bar.logout": "Uitloggen", "navigation_bar.mutes": "Genegeerde gebruikers", "navigation_bar.personal": "Persoonlijk", - "navigation_bar.pins": "Vastgezette toots", + "navigation_bar.pins": "Vastgemaakte berichten", "navigation_bar.preferences": "Instellingen", "navigation_bar.public_timeline": "Globale tijdlijn", "navigation_bar.security": "Beveiliging", - "notification.admin.sign_up": "{name} signed up", - "notification.favourite": "{name} voegde jouw toot als favoriet toe", + "notification.admin.sign_up": "{name} heeft zich aangemeld", + "notification.favourite": "{name} voegde jouw bericht als favoriet toe", "notification.follow": "{name} volgt jou nu", "notification.follow_request": "{name} wil jou graag volgen", "notification.mention": "{name} vermeldde jou", "notification.own_poll": "Jouw poll is beëindigd", "notification.poll": "Een poll waaraan jij hebt meegedaan is beëindigd", - "notification.reblog": "{name} boostte jouw toot", - "notification.status": "{name} heeft zojuist een toot geplaatst", - "notification.update": "{name} edited a post", + "notification.reblog": "{name} boostte jouw bericht", + "notification.status": "{name} heeft zojuist een bericht geplaatst", + "notification.update": "{name} heeft een bericht bewerkt", "notifications.clear": "Meldingen verwijderen", "notifications.clear_confirmation": "Weet je het zeker dat je al jouw meldingen wilt verwijderen?", - "notifications.column_settings.admin.sign_up": "New sign-ups:", + "notifications.column_settings.admin.sign_up": "Nieuwe aanmeldingen:", "notifications.column_settings.alert": "Desktopmeldingen", "notifications.column_settings.favourite": "Favorieten:", "notifications.column_settings.filter_bar.advanced": "Alle categorieën tonen", @@ -335,10 +335,10 @@ "notifications.column_settings.reblog": "Boosts:", "notifications.column_settings.show": "In kolom tonen", "notifications.column_settings.sound": "Geluid afspelen", - "notifications.column_settings.status": "Nieuwe toots:", + "notifications.column_settings.status": "Nieuwe berichten:", "notifications.column_settings.unread_notifications.category": "Ongelezen meldingen", "notifications.column_settings.unread_notifications.highlight": "Ongelezen meldingen markeren", - "notifications.column_settings.update": "Edits:", + "notifications.column_settings.update": "Bewerkingen:", "notifications.filter.all": "Alles", "notifications.filter.boosts": "Boosts", "notifications.filter.favourites": "Favorieten", @@ -365,7 +365,7 @@ "poll.votes": "{votes, plural, one {# stem} other {# stemmen}}", "poll_button.add_poll": "Poll toevoegen", "poll_button.remove_poll": "Poll verwijderen", - "privacy.change": "Zichtbaarheid van toot aanpassen", + "privacy.change": "Zichtbaarheid van bericht aanpassen", "privacy.direct.long": "Alleen aan vermelde gebruikers tonen", "privacy.direct.short": "Direct", "privacy.private.long": "Alleen aan volgers tonen", @@ -378,100 +378,100 @@ "regeneration_indicator.label": "Aan het laden…", "regeneration_indicator.sublabel": "Jouw tijdlijn wordt aangemaakt!", "relative_time.days": "{number}d", - "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago", - "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago", - "relative_time.full.just_now": "just now", - "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago", - "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago", + "relative_time.full.days": "{number, plural, one {# dag} other {# dagen}} geleden", + "relative_time.full.hours": "{number, plural, one {# uur} other {# uur}} geleden", + "relative_time.full.just_now": "zojuist", + "relative_time.full.minutes": "{number, plural, one {# minuut} other {# minuten}} geleden", + "relative_time.full.seconds": "{number, plural, one {# seconde} other {# seconden}} geleden", "relative_time.hours": "{number}u", "relative_time.just_now": "nu", "relative_time.minutes": "{number}m", "relative_time.seconds": "{number}s", "relative_time.today": "vandaag", "reply_indicator.cancel": "Annuleren", - "report.block": "Block", - "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.", - "report.categories.other": "Other", + "report.block": "Blokkeren", + "report.block_explanation": "Je kunt diens berichten niet zien. Je kunt door diegene niet gevolgd worden en jouw berichten zijn onzichtbaar. Diegene kan zien dat die door jou is geblokkeerd.", + "report.categories.other": "Overig", "report.categories.spam": "Spam", - "report.categories.violation": "Content violates one or more server rules", - "report.category.subtitle": "Choose the best match", - "report.category.title": "Tell us what's going on with this {type}", - "report.category.title_account": "profile", - "report.category.title_status": "post", - "report.close": "Done", - "report.comment.title": "Is there anything else you think we should know?", + "report.categories.violation": "De inhoud overtreedt een of meerdere serverregels", + "report.category.subtitle": "Kies wat het meeste overeenkomt", + "report.category.title": "Vertel ons wat er met dit {type} aan de hand is", + "report.category.title_account": "profiel", + "report.category.title_status": "bericht", + "report.close": "Klaar", + "report.comment.title": "Zijn er nog andere dingen waarvan je denkt dat wij dat moeten weten?", "report.forward": "Naar {target} doorsturen", "report.forward_hint": "Het account bevindt zich op een andere server. Wil je daar eveneens een geanonimiseerde kopie van deze rapportage naar toe sturen?", - "report.mute": "Mute", - "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.", - "report.next": "Next", + "report.mute": "Negeren", + "report.mute_explanation": "Je kunt diens berichten niet zien. Je kunt nog wel gevolgd worden en jouw berichten zijn nog zichtbaar, maar diegene kan niet zien dat die wordt genegeerd.", + "report.next": "Volgende", "report.placeholder": "Extra opmerkingen", - "report.reasons.dislike": "I don't like it", - "report.reasons.dislike_description": "It is not something you want to see", - "report.reasons.other": "It's something else", - "report.reasons.other_description": "The issue does not fit into other categories", - "report.reasons.spam": "It's spam", - "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies", - "report.reasons.violation": "It violates server rules", - "report.reasons.violation_description": "You are aware that it breaks specific rules", - "report.rules.subtitle": "Select all that apply", - "report.rules.title": "Which rules are being violated?", - "report.statuses.subtitle": "Select all that apply", - "report.statuses.title": "Are there any posts that back up this report?", + "report.reasons.dislike": "Ik vind het niet leuk", + "report.reasons.dislike_description": "Het is iets wat je niet wilt zien", + "report.reasons.other": "Het is iets anders", + "report.reasons.other_description": "Het probleem past niet in een andere categorie", + "report.reasons.spam": "Het is spam", + "report.reasons.spam_description": "Schadelijke links, reclame, misleiding of herhalende antwoorden", + "report.reasons.violation": "Het schendt de serverregels", + "report.reasons.violation_description": "Je weet dat het specifieke regels schendt", + "report.rules.subtitle": "Selecteer wat van toepassing is", + "report.rules.title": "Welke regels worden geschonden?", + "report.statuses.subtitle": "Selecteer wat van toepassing is", + "report.statuses.title": "Zijn er berichten die deze rapportage ondersteunen?", "report.submit": "Verzenden", "report.target": "{target} rapporteren", - "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:", - "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:", - "report.thanks.title": "Don't want to see this?", - "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.", - "report.unfollow": "Unfollow @{name}", - "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.", + "report.thanks.take_action": "Hier zijn jouw opties waarmee je kunt bepalen wat je in Mastodon wilt zien:", + "report.thanks.take_action_actionable": "Terwijl wij jouw rapportage beroordelen, kun je deze acties ondernemen tegen @{name}:", + "report.thanks.title": "Wil je dit niet zien?", + "report.thanks.title_actionable": "Dank je voor het rapporteren. Wij gaan er naar kijken.", + "report.unfollow": "@{name} ontvolgen", + "report.unfollow_explanation": "Je volgt dit account. Om diens berichten niet meer op jouw starttijdlijn te zien, kun je diegene ontvolgen.", "search.placeholder": "Zoeken", "search_popout.search_format": "Geavanceerd zoeken", - "search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken in jouw toots, gebooste toots, favorieten en in toots waarin je bent vermeldt, en tevens naar gebruikersnamen, weergavenamen en hashtags.", + "search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken in jouw berichten, gebooste berichten, favorieten en in berichten waarin je bent vermeldt, en tevens naar gebruikersnamen, weergavenamen en hashtags.", "search_popout.tips.hashtag": "hashtag", - "search_popout.tips.status": "toot", + "search_popout.tips.status": "bericht", "search_popout.tips.text": "Gebruik gewone tekst om te zoeken op weergavenamen, gebruikersnamen en hashtags", "search_popout.tips.user": "gebruiker", "search_results.accounts": "Gebruikers", - "search_results.all": "All", + "search_results.all": "Alles", "search_results.hashtags": "Hashtags", - "search_results.nothing_found": "Could not find anything for these search terms", - "search_results.statuses": "Toots", - "search_results.statuses_fts_disabled": "Het zoeken in toots is op deze Mastodon-server niet ingeschakeld.", + "search_results.nothing_found": "Deze zoektermen leveren geen resultaat op", + "search_results.statuses": "Berichten", + "search_results.statuses_fts_disabled": "Het zoeken in berichten is op deze Mastodon-server niet ingeschakeld.", "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}", "status.admin_account": "Moderatie-omgeving van @{name} openen", - "status.admin_status": "Deze toot in de moderatie-omgeving openen", + "status.admin_status": "Dit bericht in de moderatie-omgeving openen", "status.block": "@{name} blokkeren", "status.bookmark": "Bladwijzer toevoegen", "status.cancel_reblog_private": "Niet langer boosten", - "status.cannot_reblog": "Deze toot kan niet geboost worden", - "status.copy": "Link naar toot kopiëren", + "status.cannot_reblog": "Dit bericht kan niet geboost worden", + "status.copy": "Link naar bericht kopiëren", "status.delete": "Verwijderen", "status.detailed_status": "Uitgebreide gespreksweergave", "status.direct": "@{name} een direct bericht sturen", - "status.edit": "Edit", - "status.edited": "Edited {date}", - "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}", + "status.edit": "Bewerken", + "status.edited": "Bewerkt op {date}", + "status.edited_x_times": "{count, plural, one {{count} keer} other {{count} keer}} bewerkt", "status.embed": "Insluiten", "status.favourite": "Favoriet", "status.filtered": "Gefilterd", - "status.history.created": "{name} created {date}", - "status.history.edited": "{name} edited {date}", + "status.history.created": "{name} plaatste dit {date}", + "status.history.edited": "{name} bewerkte dit {date}", "status.load_more": "Meer laden", "status.media_hidden": "Media verborgen", "status.mention": "@{name} vermelden", "status.more": "Meer", "status.mute": "@{name} negeren", "status.mute_conversation": "Negeer gesprek", - "status.open": "Volledige toot tonen", + "status.open": "Volledig bericht tonen", "status.pin": "Aan profielpagina vastmaken", - "status.pinned": "Vastgemaakte toot", + "status.pinned": "Vastgemaakt bericht", "status.read_more": "Meer lezen", "status.reblog": "Boosten", "status.reblog_private": "Boost naar oorspronkelijke ontvangers", "status.reblogged_by": "{name} boostte", - "status.reblogs.empty": "Niemand heeft deze toot nog geboost. Wanneer iemand dit doet, valt dat hier te zien.", + "status.reblogs.empty": "Niemand heeft dit bericht nog geboost. Wanneer iemand dit doet, valt dat hier te zien.", "status.redraft": "Verwijderen en herschrijven", "status.remove_bookmark": "Bladwijzer verwijderen", "status.reply": "Reageren", @@ -502,7 +502,7 @@ "timeline_hint.remote_resource_not_displayed": "{resource} van andere servers worden niet getoond.", "timeline_hint.resources.followers": "Volgers", "timeline_hint.resources.follows": "Volgend", - "timeline_hint.resources.statuses": "Oudere toots", + "timeline_hint.resources.statuses": "Oudere berichten", "trends.counter_by_accounts": "{count, plural, one {{counter} persoon} other {{counter} personen}} zijn aan het praten", "trends.trending_now": "Huidige trends", "ui.beforeunload": "Je concept gaat verloren wanneer je Mastodon verlaat.", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index c597aa20d..2b9755c06 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -18,7 +18,7 @@ "account.followers": "Seguidores", "account.followers.empty": "Nada aqui.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", - "account.following": "Following", + "account.following": "Seguindo", "account.following_counter": "{count, plural, one {segue {counter}} other {segue {counter}}}", "account.follows.empty": "Nada aqui.", "account.follows_you": "te segue", @@ -41,14 +41,14 @@ "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", "account.unblock": "Desbloquear @{name}", "account.unblock_domain": "Desbloquear domínio {domain}", - "account.unblock_short": "Unblock", + "account.unblock_short": "Desbloquear", "account.unendorse": "Remover", "account.unfollow": "Deixar de seguir", "account.unmute": "Dessilenciar @{name}", "account.unmute_notifications": "Mostrar notificações de @{name}", - "account.unmute_short": "Unmute", + "account.unmute_short": "Reativar", "account_note.placeholder": "Nota pessoal sobre este perfil aqui", - "admin.dashboard.daily_retention": "User retention rate by day after sign-up", + "admin.dashboard.daily_retention": "Taxa de retenção de usuários por dia, após a inscrição", "admin.dashboard.monthly_retention": "Taxa de retenção de usuários por mês, após a inscrição", "admin.dashboard.retention.average": "Média", "admin.dashboard.retention.cohort": "Mês de inscrição", @@ -168,7 +168,7 @@ "empty_column.community": "A linha local está vazia. Publique algo para começar!", "empty_column.direct": "Nada aqui. Quando você enviar ou receber toots diretos, eles aparecerão aqui.", "empty_column.domain_blocks": "Nada aqui.", - "empty_column.explore_statuses": "Nothing is trending right now. Check back later!", + "empty_column.explore_statuses": "Nada está em alta no momento. Volte mais tarde!", "empty_column.favourited_statuses": "Nada aqui. Quando você favoritar um toot, ele aparecerá aqui.", "empty_column.favourites": "Nada aqui. Quando alguém favoritar, o autor aparecerá aqui.", "empty_column.follow_recommendations": "Parece que não há sugestões para você. Tente usar a pesquisa para encontrar pessoas que você possa conhecer ou explorar hashtags.", @@ -294,7 +294,7 @@ "navigation_bar.discover": "Descobrir", "navigation_bar.domain_blocks": "Domínios bloqueados", "navigation_bar.edit_profile": "Editar perfil", - "navigation_bar.explore": "Explore", + "navigation_bar.explore": "Explorar", "navigation_bar.favourites": "Favoritos", "navigation_bar.filters": "Palavras filtradas", "navigation_bar.follow_requests": "Seguidores pendentes", @@ -390,42 +390,42 @@ "relative_time.today": "hoje", "reply_indicator.cancel": "Cancelar", "report.block": "Bloquear", - "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.", + "report.block_explanation": "Você não verá suas postagens. Eles não poderão ver suas postagens ou segui-lo. Eles serão capazes de perceber que estão bloqueados.", "report.categories.other": "Outro", "report.categories.spam": "Spam", "report.categories.violation": "O conteúdo viola uma ou mais regras do servidor", - "report.category.subtitle": "Choose the best match", - "report.category.title": "Tell us what's going on with this {type}", + "report.category.subtitle": "Escolha a alternativa de melhor correspondência", + "report.category.title": "Conte-nos o que está acontecendo com esse {type}", "report.category.title_account": "perfil", "report.category.title_status": "publicação", "report.close": "Concluído", - "report.comment.title": "Is there anything else you think we should know?", + "report.comment.title": "Há algo mais que você acredita que devemos saber?", "report.forward": "Encaminhar para {target}", "report.forward_hint": "A conta está em outra instância. Enviar uma cópia anônima da denúncia para lá?", "report.mute": "Silenciar", - "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.", + "report.mute_explanation": "Você não verá suas postagens. Eles ainda podem seguir você e ver suas postagens e não saberão que estão silenciados.", "report.next": "Próximo", "report.placeholder": "Comentários adicionais aqui", "report.reasons.dislike": "Eu não gosto disso", - "report.reasons.dislike_description": "It is not something you want to see", - "report.reasons.other": "It's something else", + "report.reasons.dislike_description": "Não é algo que você quer ver", + "report.reasons.other": "É outra coisa", "report.reasons.other_description": "O problema não se encaixa em outras categorias", "report.reasons.spam": "É spam", - "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies", - "report.reasons.violation": "It violates server rules", + "report.reasons.spam_description": "Links maliciosos, envolvimento falso ou respostas repetitivas", + "report.reasons.violation": "Viola as regras do servidor", "report.reasons.violation_description": "Você está ciente de que isso quebra regras específicas", "report.rules.subtitle": "Selecione tudo que se aplica", - "report.rules.title": "Which rules are being violated?", + "report.rules.title": "Que regras estão sendo violadas?", "report.statuses.subtitle": "Selecione tudo que se aplica", - "report.statuses.title": "Are there any posts that back up this report?", + "report.statuses.title": "Existem postagens que respaldam esse relatório?", "report.submit": "Enviar", "report.target": "Denunciando {target}", - "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:", + "report.thanks.take_action": "Aqui estão suas opções para controlar o que você vê no Mastodon:", "report.thanks.take_action_actionable": "Enquanto revisamos isso, você pode tomar medidas contra @{name}:", "report.thanks.title": "Não quer ver isto?", "report.thanks.title_actionable": "Obrigado por reportar. Vamos analisar.", - "report.unfollow": "Unfollow @{name}", - "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.", + "report.unfollow": "Deixar de seguir @{name}", + "report.unfollow_explanation": "Você está seguindo esta conta. Para não mais ver os posts dele em sua página inicial, deixe de segui-lo.", "search.placeholder": "Pesquisar", "search_popout.search_format": "Formato de pesquisa avançada", "search_popout.tips.full_text": "Texto simples retorna toots que você escreveu, favoritou, deu boost, ou em que foi mencionado, assim como nomes de usuário e de exibição, e hashtags correspondentes.", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index d1047f5da..ce3cdabd0 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -18,7 +18,7 @@ "account.followers": "Ndjekës", "account.followers.empty": "Këtë përdorues ende s’e ndjek kush.", "account.followers_counter": "{count, plural, one {{counter} Ndjekës} other {{counter} Ndjekës}}", - "account.following": "Following", + "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", @@ -41,12 +41,12 @@ "account.statuses_counter": "{count, plural, one {{counter} Mesazh} other {{counter} Mesazhe}}", "account.unblock": "Zhbllokoje @{name}", "account.unblock_domain": "Zhblloko përkatësinë {domain}", - "account.unblock_short": "Unblock", + "account.unblock_short": "Zhbllokoje", "account.unendorse": "Mos e përfshi në profil", "account.unfollow": "Resht së ndjekuri", "account.unmute": "Ktheji zërin @{name}", "account.unmute_notifications": "Hiqua ndalimin e shfaqjes njoftimeve nga @{name}", - "account.unmute_short": "Unmute", + "account.unmute_short": "Çheshtoje", "account_note.placeholder": "Klikoni për të shtuar shënim", "admin.dashboard.daily_retention": "Shkallë mbajtjeje përdoruesi, në ditë, pas regjistrimit", "admin.dashboard.monthly_retention": "Shkallë mbajtjeje përdoruesi, në muaj, pas regjistrimit", @@ -294,7 +294,7 @@ "navigation_bar.discover": "Zbuloni", "navigation_bar.domain_blocks": "Përkatësi të bllokuara", "navigation_bar.edit_profile": "Përpunoni profilin", - "navigation_bar.explore": "Explore", + "navigation_bar.explore": "Eksploroni", "navigation_bar.favourites": "Të parapëlqyer", "navigation_bar.filters": "Fjalë të heshtuara", "navigation_bar.follow_requests": "Kërkesa për ndjekje", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index f7eddd11b..f8477e8fd 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -128,7 +128,7 @@ "confirmations.logout.confirm": "ออกจากระบบ", "confirmations.logout.message": "คุณแน่ใจหรือไม่ว่าต้องการออกจากระบบ?", "confirmations.mute.confirm": "ซ่อน", - "confirmations.mute.explanation": "นี่จะซ่อนโพสต์จากเขาและโพสต์ที่กล่าวถึงเขา แต่จะยังอนุญาตให้เขาเห็นโพสต์ของคุณและติดตามคุณ", + "confirmations.mute.explanation": "นี่จะซ่อนโพสต์จากเขาและโพสต์ที่กล่าวถึงเขา แต่จะยังคงอนุญาตให้เขาเห็นโพสต์ของคุณและติดตามคุณ", "confirmations.mute.message": "คุณแน่ใจหรือไม่ว่าต้องการซ่อน {name}?", "confirmations.redraft.confirm": "ลบแล้วร่างใหม่", "confirmations.redraft.message": "คุณแน่ใจหรือไม่ว่าต้องการลบโพสต์นี้แล้วร่างโพสต์ใหม่? รายการโปรดและการดันจะหายไป และการตอบกลับโพสต์ดั้งเดิมจะไม่มีความเกี่ยวพัน", @@ -403,7 +403,7 @@ "report.forward": "ส่งต่อไปยัง {target}", "report.forward_hint": "บัญชีมาจากเซิร์ฟเวอร์อื่น ส่งสำเนาของรายงานที่ไม่ระบุตัวตนไปที่นั่นด้วย?", "report.mute": "ซ่อน", - "report.mute_explanation": "คุณจะไม่เห็นโพสต์ของเขา เขายังสามารถติดตามคุณและเห็นโพสต์ของคุณและจะไม่ทราบว่ามีการซ่อนเขา", + "report.mute_explanation": "คุณจะไม่เห็นโพสต์ของเขา เขายังคงสามารถติดตามคุณและเห็นโพสต์ของคุณและจะไม่ทราบว่ามีการซ่อนเขา", "report.next": "ถัดไป", "report.placeholder": "ความคิดเห็นเพิ่มเติม", "report.reasons.dislike": "ฉันไม่ชอบโพสต์", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 876f8aa8a..e2e1386e6 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -18,7 +18,7 @@ "account.followers": "Підписники", "account.followers.empty": "Ніхто ще не підписався на цього користувача.", "account.followers_counter": "{count, plural, one {{counter} Підписник} few {{counter} Підписники} many {{counter} Підписників} other {{counter} Підписники}}", - "account.following": "Following", + "account.following": "Стежите", "account.following_counter": "{count, plural, one {{counter} Підписка} few {{counter} Підписки} many {{counter} Підписок} other {{counter} Підписки}}", "account.follows.empty": "Цей користувач ще ні на кого не підписався.", "account.follows_you": "Підписаний(-а) на вас", @@ -402,7 +402,7 @@ "report.comment.title": "Is there anything else you think we should know?", "report.forward": "Надіслати до {target}", "report.forward_hint": "Це акаунт з іншого серверу. Відправити анонімізовану копію скарги і туди?", - "report.mute": "Mute", + "report.mute": "Заглушити", "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.", "report.next": "Далі", "report.placeholder": "Додаткові коментарі", @@ -415,8 +415,8 @@ "report.reasons.violation": "It violates server rules", "report.reasons.violation_description": "You are aware that it breaks specific rules", "report.rules.subtitle": "Select all that apply", - "report.rules.title": "Which rules are being violated?", - "report.statuses.subtitle": "Select all that apply", + "report.rules.title": "Які правила порушено?", + "report.statuses.subtitle": "Виберіть усі варіанти, що підходять", "report.statuses.title": "Are there any posts that back up this report?", "report.submit": "Відправити", "report.target": "Скаржимося на {target}", @@ -424,7 +424,7 @@ "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:", "report.thanks.title": "Don't want to see this?", "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.", - "report.unfollow": "Unfollow @{name}", + "report.unfollow": "Відписатися від @{name}", "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.", "search.placeholder": "Пошук", "search_popout.search_format": "Розширений формат пошуку", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 713d24c0f..442ffd2c0 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1009,6 +1009,43 @@ .audio-player { margin-top: 8px; } + + &.light { + .status__relative-time, + .status__visibility-icon { + color: $light-text-color; + } + + .status__display-name { + color: $inverted-text-color; + } + + .display-name { + color: $light-text-color; + + strong { + color: $inverted-text-color; + } + } + + .status__content { + color: $inverted-text-color; + + a { + color: $highlight-text-color; + } + + a.status__content__spoiler-link { + color: $primary-text-color; + background: $ui-primary-color; + + &:hover, + &:focus { + background: lighten($ui-primary-color, 8%); + } + } + } + } } .status__relative-time, @@ -1154,43 +1191,6 @@ .audio-player { margin-top: 8px; } - - &.light { - .status__relative-time, - .status__visibility-icon { - color: $light-text-color; - } - - .status__display-name { - color: $inverted-text-color; - } - - .display-name { - color: $light-text-color; - - strong { - color: $inverted-text-color; - } - } - - .status__content { - color: $inverted-text-color; - - a { - color: $highlight-text-color; - } - - a.status__content__spoiler-link { - color: $primary-text-color; - background: $ui-primary-color; - - &:hover, - &:focus { - background: lighten($ui-primary-color, 8%); - } - } - } - } } .detailed-status__meta { diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index cf31b6ff6..1ac509f18 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class ActivityPub::Activity::Create < ActivityPub::Activity + include FormattingHelper + def perform dereference_object! @@ -367,7 +369,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def converted_text - Formatter.instance.linkify([@status_parser.title.presence, @status_parser.spoiler_text.presence, @status_parser.url || @status_parser.uri].compact.join("\n\n")) + linkify([@status_parser.title.presence, @status_parser.spoiler_text.presence, @status_parser.url || @status_parser.uri].compact.join("\n\n")) end def unsupported_media_type?(mime_type) diff --git a/app/lib/activitypub/parser/media_attachment_parser.rb b/app/lib/activitypub/parser/media_attachment_parser.rb index 1798e58a4..30bea1f0e 100644 --- a/app/lib/activitypub/parser/media_attachment_parser.rb +++ b/app/lib/activitypub/parser/media_attachment_parser.rb @@ -27,7 +27,9 @@ class ActivityPub::Parser::MediaAttachmentParser end def description - @json['summary'].presence || @json['name'].presence + str = @json['summary'].presence || @json['name'].presence + str = str.strip[0...MediaAttachment::MAX_DESCRIPTION_LENGTH] if str.present? + str end def focus diff --git a/app/lib/admin/system_check.rb b/app/lib/admin/system_check.rb index afb20cb47..877a42ef6 100644 --- a/app/lib/admin/system_check.rb +++ b/app/lib/admin/system_check.rb @@ -5,6 +5,7 @@ class Admin::SystemCheck Admin::SystemCheck::DatabaseSchemaCheck, Admin::SystemCheck::SidekiqProcessCheck, Admin::SystemCheck::RulesCheck, + Admin::SystemCheck::ElasticsearchCheck, ].freeze def self.perform diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb new file mode 100644 index 000000000..1b48a5415 --- /dev/null +++ b/app/lib/admin/system_check/elasticsearch_check.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck + def pass? + return true unless Chewy.enabled? + + running_version.present? && compatible_version? + end + + def message + if running_version.present? + Admin::SystemCheck::Message.new(:elasticsearch_version_check, I18n.t('admin.system_checks.elasticsearch_version_check.version_comparison', running_version: running_version, required_version: required_version)) + else + Admin::SystemCheck::Message.new(:elasticsearch_running_check) + end + end + + private + + def running_version + @running_version ||= begin + Chewy.client.info['version']['number'] + rescue Faraday::ConnectionFailed + nil + end + end + + def required_version + '7.x' + end + + def compatible_version? + Gem::Version.new(running_version) >= Gem::Version.new(required_version) + end + + def missing_queues + @missing_queues ||= Sidekiq::ProcessSet.new.reduce(SIDEKIQ_QUEUES) { |queues, process| queues - process['queues'] } + end +end diff --git a/app/lib/advanced_text_formatter.rb b/app/lib/advanced_text_formatter.rb new file mode 100644 index 000000000..728400819 --- /dev/null +++ b/app/lib/advanced_text_formatter.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +class AdvancedTextFormatter < TextFormatter + class HTMLRenderer < Redcarpet::Render::HTML + def initialize(options, &block) + super(options) + @format_link = block + end + + def block_code(code, _language) + <<~HTML.squish +
#{ERB::Util.h(code).gsub("\n", '
')}
+ HTML
+ end
+
+ def autolink(link, link_type)
+ return link if link_type == :email
+ @format_link.call(link)
+ end
+ end
+
+ # @param [String] text
+ # @param [Hash] options
+ # @option options [Boolean] :multiline
+ # @option options [Boolean] :with_domains
+ # @option options [Boolean] :with_rel_me
+ # @option options [Array#{encode(code).gsub("\n", "
")}
"
- end
-
- def autolink(link, link_type)
- return link if link_type == :email
- Formatter.instance.link_url(link)
- rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
- encode(link)
- end
-
- private
-
- def html_entities
- @html_entities ||= HTMLEntities.new
- end
-
- def encode(html)
- html_entities.encode(html)
- end
-end
-
-class Formatter
- include Singleton
- include RoutingHelper
-
- include ActionView::Helpers::TextHelper
-
- def format(status, **options)
- if status.respond_to?(:reblog?) && status.reblog?
- prepend_reblog = status.reblog.account.acct
- status = status.proper
- else
- prepend_reblog = false
- end
-
- raw_content = status.text
-
- if options[:inline_poll_options] && status.preloadable_poll
- raw_content = raw_content + "\n\n" + status.preloadable_poll.options.map { |title| "[ ] #{title}" }.join("\n")
- end
-
- return '' if raw_content.blank?
-
- unless status.local?
- html = reformat(raw_content)
- html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
- return html.html_safe # rubocop:disable Rails/OutputSafety
- end
-
- linkable_accounts = status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []
- linkable_accounts << status.account
-
- html = raw_content
- html = "RT @#{prepend_reblog} #{html}" if prepend_reblog
- html = format_markdown(html) if status.content_type == 'text/markdown'
- html = encode_and_link_urls(html, linkable_accounts, keep_html: %w(text/markdown text/html).include?(status.content_type))
- html = reformat(html, true) if %w(text/markdown text/html).include?(status.content_type)
- html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
-
- unless %w(text/markdown text/html).include?(status.content_type)
- html = simple_format(html, {}, sanitize: false)
- html = html.delete("\n")
- end
-
- html.html_safe # rubocop:disable Rails/OutputSafety
- end
-
- def format_markdown(html)
- html = markdown_formatter.render(html)
- html.delete("\r").delete("\n")
- end
-
- def reformat(html, outgoing = false)
- sanitize(html, Sanitize::Config::MASTODON_STRICT.merge(outgoing: outgoing))
- rescue ArgumentError
- ''
- end
-
- def plaintext(status)
- return status.text if status.local?
-
- text = status.text.gsub(/(#{poll_options_html}
" + end + + "#{html}#{after_html}" + end + end end diff --git a/app/lib/text_formatter.rb b/app/lib/text_formatter.rb new file mode 100644 index 000000000..48e2fc233 --- /dev/null +++ b/app/lib/text_formatter.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +class TextFormatter + include ActionView::Helpers::TextHelper + include ERB::Util + include RoutingHelper + + URL_PREFIX_REGEX = /\A(https?:\/\/(www\.)?|xmpp:)/.freeze + + DEFAULT_REL = %w(nofollow noopener noreferrer).freeze + + DEFAULT_OPTIONS = { + multiline: true, + }.freeze + + attr_reader :text, :options + + # @param [String] text + # @param [Hash] options + # @option options [Boolean] :multiline + # @option options [Boolean] :with_domains + # @option options [Boolean] :with_rel_me + # @option options [Array<script>alert("Hello")</script>
' - end - end - - context 'given text containing HTML (XSS attack)' do - let(:text) { %q{} } - - it 'escapes the HTML' do - is_expected.to include '<img src="javascript:alert('XSS');">
' - end - end - - context 'given an invalid URL' do - let(:text) { 'http://www\.google\.com' } - - it 'outputs the raw URL' do - is_expected.to eq 'http://www\.google\.com
' - end - end - - context 'given text containing a hashtag' do - let(:text) { '#hashtag' } - - it 'creates a hashtag link' do - is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#hashtag' - end - end - - context 'given text containing a hashtag with Unicode chars' do - let(:text) { '#hashtagタグ' } - - it 'creates a hashtag link' do - is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#hashtagタグ' - end - end - - context 'given a stand-alone xmpp: URI' do - let(:text) { 'xmpp:user@instance.com' } - - it 'matches the full URI' do - is_expected.to include 'href="xmpp:user@instance.com"' - end - end - - context 'given a an xmpp: URI with a query-string' do - let(:text) { 'please join xmpp:muc@instance.com?join right now' } - - it 'matches the full URI' do - is_expected.to include 'href="xmpp:muc@instance.com?join"' - end - end - - context 'given text containing a magnet: URI' do - let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' } - - it 'matches the full URI' do - is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"' - end - end - end - - describe '#format_spoiler' do - subject { Formatter.instance.format_spoiler(status) } - - context 'given a post containing plain text' do - let(:status) { Fabricate(:status, text: 'text', spoiler_text: 'Secret!', uri: nil) } - - it 'Returns the spoiler text' do - is_expected.to eq 'Secret!' - end - end - - context 'given a post with an emoji shortcode at the start' do - let!(:emoji) { Fabricate(:custom_emoji) } - let(:status) { Fabricate(:status, text: 'text', spoiler_text: ':coolcat: Secret!', uri: nil) } - let(:text) { ':coolcat: Beep boop' } - - it 'converts the shortcode to an image tag' do - is_expected.to match(/@alicetext
' - end - end - - context 'given a post containing line feeds' do - let(:status) { Fabricate(:status, text: "line\nfeed", uri: nil) } - - it 'removes line feeds' do - is_expected.not_to include "\n" - end - end - - context 'given a post containing linkable mentions' do - let(:status) { Fabricate(:status, mentions: [ Fabricate(:mention, account: local_account) ], text: '@alice') } - - it 'creates a mention link' do - is_expected.to include '@alice' - end - end - - context 'given a post containing unlinkable mentions' do - let(:status) { Fabricate(:status, text: '@alice', uri: nil) } - - it 'does not create a mention link' do - is_expected.to include '@alice' - end - end - - context do - let(:content_type) { 'text/plain' } - - subject do - status = Fabricate(:status, text: text, content_type: content_type, uri: nil) - Formatter.instance.format(status) - end - - context 'given an invalid URL (invalid port)' do - let(:text) { 'https://foo.bar:X/' } - let(:content_type) { 'text/markdown' } - - it 'outputs the raw URL' do - is_expected.to eq 'https://foo.bar:X/
' - end - end - - include_examples 'encode and link URLs' - end - - context 'given a post with custom_emojify option' do - let!(:emoji) { Fabricate(:custom_emoji) } - let(:status) { Fabricate(:status, account: local_account, text: text) } - - subject { Formatter.instance.format(status, custom_emojify: true) } - - context 'given a post with an emoji shortcode at the start' do - let(:text) { ':coolcat: Beep boop' } - - it 'converts the shortcode to an image tag' do - is_expected.to match(/:coolcat: Beep boop
' }
-
- it 'converts the shortcode to an image tag' do
- is_expected.to match(/
Beep :coolcat: boop
' } - - it 'converts the shortcode to an image tag' do - is_expected.to match(/Beep :coolcat::coolcat:' } - - it 'does not touch the shortcodes' do - is_expected.to match(/:coolcat::coolcat:<\/p>/) - end - end - - context 'given a post with an emoji shortcode at the end' do - let(:text) { '
Beep boop
:coolcat:
a text by a nerd who uses an HTML tag in text
', content_type: content_type, uri: nil) } - let(:content_type) { 'text/plain' } - - it 'returns the raw text' do - is_expected.to eq 'a text by a nerd who uses an HTML tag in text
' - end - end - - context 'given a post with remote status' do - let(:status) { Fabricate(:status, account: remote_account, text: '') } - - it 'returns tag-stripped text' do - is_expected.to eq '' - end - end - end - - describe '#simplified_format' do - subject { Formatter.instance.simplified_format(account) } - - context 'given a post with local status' do - let(:account) { Fabricate(:account, domain: nil, note: text) } - - context 'given a post containing linkable mentions for local accounts' do - let(:text) { '@alice' } - - before { local_account } - - it 'creates a mention link' do - is_expected.to eq '' - end - end - - context 'given a post containing linkable mentions for remote accounts' do - let(:text) { '@bob@remote.test' } - - before { remote_account } - - it 'creates a mention link' do - is_expected.to eq '' - end - end - - context 'given a post containing unlinkable mentions' do - let(:text) { '@alice' } - - it 'does not create a mention link' do - is_expected.to eq '@alice
' - end - end - - context 'given a post with custom_emojify option' do - let!(:emoji) { Fabricate(:custom_emoji) } - - before { account.note = text } - subject { Formatter.instance.simplified_format(account, custom_emojify: true) } - - context 'given a post with an emoji shortcode at the start' do - let(:text) { ':coolcat: Beep boop' } - - it 'converts the shortcode to an image tag' do - is_expected.to match(/alert("Hello")' } - let(:account) { Fabricate(:account, domain: 'remote', note: text) } - - it 'reformats' do - is_expected.to_not include '' - end - - context 'with custom_emojify option' do - let!(:emoji) { Fabricate(:custom_emoji, domain: remote_account.domain) } - - before { remote_account.note = text } - - subject { Formatter.instance.simplified_format(remote_account, custom_emojify: true) } - - context 'given a post with an emoji shortcode at the start' do - let(:text) { '
:coolcat: Beep boop
' }
-
- it 'converts shortcode to image tag' do
- is_expected.to match(/
Beep :coolcat: boop
' } - - it 'converts shortcode to image tag' do - is_expected.to match(/Beep :coolcat::coolcat:' } - - it 'does not touch the shortcodes' do - is_expected.to match(/:coolcat::coolcat:<\/p>/) - end - end - - context 'given a post with an emoji shortcode at the end' do - let(:text) { '
Beep boop
:coolcat:
Foo bar
' + end + end + + context 'when remote' do + let(:local) { false } + + context 'given plain text' do + let(:text) { 'Beep boop' } + + it 'keeps the plain text' do + is_expected.to include 'Beep boop' + end + end + + context 'given text containing script tags' do + let(:text) { '' } + + it 'strips the scripts' do + is_expected.to_not include '' + end + end + + context 'given text containing malicious classes' do + let(:text) { 'Show more' } + + it 'strips the malicious classes' do + is_expected.to_not include 'status__content__spoiler-link' + end + end + end + end +end diff --git a/spec/lib/link_details_extractor_spec.rb b/spec/lib/link_details_extractor_spec.rb index 84bb4579c..7ea867c61 100644 --- a/spec/lib/link_details_extractor_spec.rb +++ b/spec/lib/link_details_extractor_spec.rb @@ -25,6 +25,14 @@ RSpec.describe LinkDetailsExtractor do expect(subject.canonical_url).to eq 'https://foo.com/article' end end + + context 'when canonical URL is set to "null"' do + let(:html) { '' } + + it 'ignores the canonical URLs' do + expect(subject.canonical_url).to eq original_url + end + end end context 'when structured data is present' do diff --git a/spec/lib/plain_text_formatter_spec.rb b/spec/lib/plain_text_formatter_spec.rb new file mode 100644 index 000000000..c3d0ee630 --- /dev/null +++ b/spec/lib/plain_text_formatter_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +RSpec.describe PlainTextFormatter do + describe '#to_s' do + subject { described_class.new(status.text, status.local?).to_s } + + context 'given a post with local status' do + let(:status) { Fabricate(:status, text: 'a text by a nerd who uses an HTML tag in text
', uri: nil) } + + it 'returns the raw text' do + is_expected.to eq 'a text by a nerd who uses an HTML tag in text
' + end + end + + context 'given a post with remote status' do + let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') } + let(:status) { Fabricate(:status, account: remote_account, text: 'Hello
') } + + it 'returns tag-stripped text' do + is_expected.to eq 'Hello' + end + end + end +end diff --git a/spec/lib/rss/serializer_spec.rb b/spec/lib/rss/serializer_spec.rb index 0364d13de..1da45d302 100644 --- a/spec/lib/rss/serializer_spec.rb +++ b/spec/lib/rss/serializer_spec.rb @@ -13,13 +13,6 @@ describe RSS::Serializer do subject { RSS::Serializer.new.send(:status_title, status) } - context 'if destroyed?' do - it 'returns "#{account.acct} deleted status"' do - status.destroy! - expect(subject).to eq "#{account.acct} deleted status" - end - end - context 'on a toot with long text' do let(:text) { "This toot's text is longer than the allowed number of characters" } diff --git a/spec/lib/sanitize_config_spec.rb b/spec/lib/sanitize_config_spec.rb index 8bcffb2e5..dc6418e5b 100644 --- a/spec/lib/sanitize_config_spec.rb +++ b/spec/lib/sanitize_config_spec.rb @@ -41,18 +41,8 @@ describe Sanitize::Config do end end - describe '::MASTODON_STRICT' do - subject { Sanitize::Config::MASTODON_STRICT } - - it_behaves_like 'common HTML sanitization' - - it 'keeps a with href and rel tag' do - expect(Sanitize.fragment('Test', subject)).to eq 'Test' - end - end - - describe '::MASTODON_STRICT with outgoing toots' do - subject { Sanitize::Config::MASTODON_STRICT.merge(outgoing: true) } + describe '::MASTODON_OUTGOING' do + subject { Sanitize::Config::MASTODON_OUTGOING } around do |example| original_web_domain = Rails.configuration.x.web_domain @@ -62,9 +52,9 @@ describe Sanitize::Config do it_behaves_like 'common HTML sanitization' - it 'keeps a with href and rel tag, not adding to rel if url is local' do + it 'keeps a with href and rel tag, not adding to rel or target if url is local' do Rails.configuration.x.web_domain = 'domain.test' - expect(Sanitize.fragment('Test', subject)).to eq 'Test' + expect(Sanitize.fragment('Test', subject)).to eq 'Test' end end end diff --git a/spec/lib/text_formatter_spec.rb b/spec/lib/text_formatter_spec.rb new file mode 100644 index 000000000..52a9d2498 --- /dev/null +++ b/spec/lib/text_formatter_spec.rb @@ -0,0 +1,313 @@ +require 'rails_helper' + +RSpec.describe TextFormatter do + describe '#to_s' do + let(:preloaded_accounts) { nil } + + subject { described_class.new(text, preloaded_accounts: preloaded_accounts).to_s } + + context 'given text containing plain text' do + let(:text) { 'text' } + + it 'paragraphizes the text' do + is_expected.to eq 'text
' + end + end + + context 'given text containing line feeds' do + let(:text) { "line\nfeed" } + + it 'removes line feeds' do + is_expected.not_to include "\n" + end + end + + context 'given text containing linkable mentions' do + let(:preloaded_accounts) { [Fabricate(:account, username: 'alice')] } + let(:text) { '@alice' } + + it 'creates a mention link' do + is_expected.to include '@alice' + end + end + + context 'given text containing unlinkable mentions' do + let(:preloaded_accounts) { [] } + let(:text) { '@alice' } + + it 'does not create a mention link' do + is_expected.to include '@alice' + end + end + + context 'given a stand-alone medium URL' do + let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' } + + it 'matches the full URL' do + is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"' + end + end + + context 'given a stand-alone google URL' do + let(:text) { 'http://google.com' } + + it 'matches the full URL' do + is_expected.to include 'href="http://google.com"' + end + end + + context 'given a stand-alone URL with a newer TLD' do + let(:text) { 'http://example.gay' } + + it 'matches the full URL' do + is_expected.to include 'href="http://example.gay"' + end + end + + context 'given a stand-alone IDN URL' do + let(:text) { 'https://nic.みんな/' } + + it 'matches the full URL' do + is_expected.to include 'href="https://nic.みんな/"' + end + + it 'has display URL' do + is_expected.to include 'nic.みんな/' + end + end + + context 'given a URL with a trailing period' do + let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' } + + it 'matches the full URL but not the period' do + is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"' + end + end + + context 'given a URL enclosed with parentheses' do + let(:text) { '(http://google.com/)' } + + it 'matches the full URL but not the parentheses' do + is_expected.to include 'href="http://google.com/"' + end + end + + context 'given a URL with a trailing exclamation point' do + let(:text) { 'http://www.google.com!' } + + it 'matches the full URL but not the exclamation point' do + is_expected.to include 'href="http://www.google.com"' + end + end + + context 'given a URL with a trailing single quote' do + let(:text) { "http://www.google.com'" } + + it 'matches the full URL but not the single quote' do + is_expected.to include 'href="http://www.google.com"' + end + end + + context 'given a URL with a trailing angle bracket' do + let(:text) { 'http://www.google.com>' } + + it 'matches the full URL but not the angle bracket' do + is_expected.to include 'href="http://www.google.com"' + end + end + + context 'given a URL with a query string' do + context 'with escaped unicode character' do + let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' } + + it 'matches the full URL' do + is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"' + end + end + + context 'with unicode character' do + let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' } + + it 'matches the full URL' do + is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&q=autolink"' + end + end + + context 'with unicode character at the end' do + let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' } + + it 'matches the full URL' do + is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"' + end + end + + context 'with escaped and not escaped unicode characters' do + let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' } + + it 'preserves escaped unicode characters' do + is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink"' + end + end + end + + context 'given a URL with parentheses in it' do + let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' } + + it 'matches the full URL' do + is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"' + end + end + + context 'given a URL in quotation marks' do + let(:text) { '"https://example.com/"' } + + it 'does not match the quotation marks' do + is_expected.to include 'href="https://example.com/"' + end + end + + context 'given a URL in angle brackets' do + let(:text) { '<script>alert("Hello")</script>
' + end + end + + context 'given text containing HTML (XSS attack)' do + let(:text) { %q{} } + + it 'escapes the HTML' do + is_expected.to include '<img src="javascript:alert('XSS');">
' + end + end + + context 'given an invalid URL' do + let(:text) { 'http://www\.google\.com' } + + it 'outputs the raw URL' do + is_expected.to eq 'http://www\.google\.com
' + end + end + + context 'given text containing a hashtag' do + let(:text) { '#hashtag' } + + it 'creates a hashtag link' do + is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#hashtag' + end + end + + context 'given text containing a hashtag with Unicode chars' do + let(:text) { '#hashtagタグ' } + + it 'creates a hashtag link' do + is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#hashtagタグ' + end + end + + context 'given text with a stand-alone xmpp: URI' do + let(:text) { 'xmpp:user@instance.com' } + + it 'matches the full URI' do + is_expected.to include 'href="xmpp:user@instance.com"' + end + end + + context 'given text with an xmpp: URI with a query-string' do + let(:text) { 'please join xmpp:muc@instance.com?join right now' } + + it 'matches the full URI' do + is_expected.to include 'href="xmpp:muc@instance.com?join"' + end + end + + context 'given text containing a magnet: URI' do + let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' } + + it 'matches the full URI' do + is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"' + end + end + end +end diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index 7360b23cf..cbd9a09c5 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -186,14 +186,6 @@ RSpec.describe MediaAttachment, type: :model do expect(media.valid?).to be false end - describe 'descriptions for remote attachments' do - it 'are cut off at 1500 characters' do - media = Fabricate(:media_attachment, description: 'foo' * 1000, remote_url: 'http://example.com/blah.jpg') - - expect(media.description.size).to be <= 1_500 - end - end - describe 'size limit validation' do it 'rejects video files that are too large' do stub_const 'MediaAttachment::IMAGE_LIMIT', 100.megabytes diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index 788c7c9d9..f87adcae1 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -46,6 +46,29 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do expect(status.reload.spoiler_text).to eq 'Show more' end + context 'with no changes at all' do + let(:payload) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Note', + content: 'Hello world', + } + end + + before do + subject.call(status, json) + end + + it 'does not create any edits' do + expect(status.reload.edits).to be_empty + end + + it 'does not mark status as edited' do + expect(status.edited?).to be false + end + end + context 'with no changes and originally with no ordered_media_attachment_ids' do let(:payload) do { @@ -61,8 +84,12 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do subject.call(status, json) end - it 'does not record an update' do - expect(status.reload.edited?).to be false + it 'does not create any edits' do + expect(status.reload.edits).to be_empty + end + + it 'does not mark status as edited' do + expect(status.edited?).to be false end end diff --git a/spec/services/after_block_service_spec.rb b/spec/services/after_block_service_spec.rb index fe5b26b2b..c09425d7c 100644 --- a/spec/services/after_block_service_spec.rb +++ b/spec/services/after_block_service_spec.rb @@ -1,9 +1,7 @@ require 'rails_helper' RSpec.describe AfterBlockService, type: :service do - subject do - -> { described_class.new.call(account, target_account) } - end + subject { described_class.new.call(account, target_account) } let(:account) { Fabricate(:account) } let(:target_account) { Fabricate(:account) } @@ -24,7 +22,7 @@ RSpec.describe AfterBlockService, type: :service do FeedManager.instance.push_to_home(account, other_account_status) FeedManager.instance.push_to_home(account, other_account_reblog) - is_expected.to change { + expect { subject }.to change { Redis.current.zrange(home_timeline_key, 0, -1) }.from([status.id.to_s, other_account_status.id.to_s, other_account_reblog.id.to_s]).to([other_account_status.id.to_s]) end @@ -43,7 +41,7 @@ RSpec.describe AfterBlockService, type: :service do FeedManager.instance.push_to_list(list, other_account_status) FeedManager.instance.push_to_list(list, other_account_reblog) - is_expected.to change { + expect { subject }.to change { Redis.current.zrange(list_timeline_key, 0, -1) }.from([status.id.to_s, other_account_status.id.to_s, other_account_reblog.id.to_s]).to([other_account_status.id.to_s]) end diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb index 9c785fc17..1fbe4d07c 100644 --- a/spec/services/delete_account_service_spec.rb +++ b/spec/services/delete_account_service_spec.rb @@ -23,12 +23,10 @@ RSpec.describe DeleteAccountService, type: :service do let!(:account_note) { Fabricate(:account_note, account: account) } - subject do - -> { described_class.new.call(account) } - end + subject { described_class.new.call(account) } it 'deletes associated owned records' do - is_expected.to change { + expect { subject }.to change { [ account.statuses, account.media_attachments, @@ -43,7 +41,7 @@ RSpec.describe DeleteAccountService, type: :service do end it 'deletes associated target records' do - is_expected.to change { + expect { subject }.to change { [ AccountPin.where(target_account: account), ].map(&:count) @@ -51,7 +49,7 @@ RSpec.describe DeleteAccountService, type: :service do end it 'deletes associated target notifications' do - is_expected.to change { + expect { subject }.to change { [ 'poll', 'favourite', 'status', 'mention', 'follow' ].map { |type| Notification.where(type: type).count } @@ -73,7 +71,7 @@ RSpec.describe DeleteAccountService, type: :service do let!(:local_follower) { Fabricate(:account) } it 'sends a delete actor activity to all known inboxes' do - subject.call + 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 end @@ -91,7 +89,7 @@ RSpec.describe DeleteAccountService, type: :service do let!(:local_follower) { Fabricate(:account) } it 'sends a reject follow to follower inboxes' do - subject.call + subject expect(a_request(:post, account.inbox_url)).to have_been_made.once end end diff --git a/spec/services/mute_service_spec.rb b/spec/services/mute_service_spec.rb index 4bb839b8d..bdec1c67b 100644 --- a/spec/services/mute_service_spec.rb +++ b/spec/services/mute_service_spec.rb @@ -1,9 +1,7 @@ require 'rails_helper' RSpec.describe MuteService, type: :service do - subject do - -> { described_class.new.call(account, target_account) } - end + subject { described_class.new.call(account, target_account) } let(:account) { Fabricate(:account) } let(:target_account) { Fabricate(:account) } @@ -21,45 +19,41 @@ RSpec.describe MuteService, type: :service do FeedManager.instance.push_to_home(account, status) FeedManager.instance.push_to_home(account, other_account_status) - is_expected.to change { + expect { subject }.to change { Redis.current.zrange(home_timeline_key, 0, -1) }.from([status.id.to_s, other_account_status.id.to_s]).to([other_account_status.id.to_s]) end end it 'mutes account' do - is_expected.to change { + expect { subject }.to change { account.muting?(target_account) }.from(false).to(true) end context 'without specifying a notifications parameter' do it 'mutes notifications from the account' do - is_expected.to change { + expect { subject }.to change { account.muting_notifications?(target_account) }.from(false).to(true) end end context 'with a true notifications parameter' do - subject do - -> { described_class.new.call(account, target_account, notifications: true) } - end + subject { described_class.new.call(account, target_account, notifications: true) } it 'mutes notifications from the account' do - is_expected.to change { + expect { subject }.to change { account.muting_notifications?(target_account) }.from(false).to(true) end end context 'with a false notifications parameter' do - subject do - -> { described_class.new.call(account, target_account, notifications: false) } - end + subject { described_class.new.call(account, target_account, notifications: false) } it 'does not mute notifications from the account' do - is_expected.to_not change { + expect { subject }.to_not change { account.muting_notifications?(target_account) }.from(false) end diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index 83e62ff36..67dd0483b 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -1,9 +1,7 @@ require 'rails_helper' RSpec.describe NotifyService, type: :service do - subject do - -> { described_class.new.call(recipient, type, activity) } - end + subject { described_class.new.call(recipient, type, activity) } let(:user) { Fabricate(:user) } let(:recipient) { user.account } @@ -11,42 +9,42 @@ RSpec.describe NotifyService, type: :service do let(:activity) { Fabricate(:follow, account: sender, target_account: recipient) } let(:type) { :follow } - it { is_expected.to change(Notification, :count).by(1) } + it { expect { subject }.to change(Notification, :count).by(1) } it 'does not notify when sender is blocked' do recipient.block!(sender) - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end it 'does not notify when sender is muted with hide_notifications' do recipient.mute!(sender, notifications: true) - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end it 'does notify when sender is muted without hide_notifications' do recipient.mute!(sender, notifications: false) - is_expected.to change(Notification, :count) + expect { subject }.to change(Notification, :count) end it 'does not notify when sender\'s domain is blocked' do recipient.block_domain!(sender.domain) - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end it 'does still notify when sender\'s domain is blocked but sender is followed' do recipient.block_domain!(sender.domain) recipient.follow!(sender) - is_expected.to change(Notification, :count) + expect { subject }.to change(Notification, :count) end it 'does not notify when sender is silenced and not followed' do sender.silence! - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end it 'does not notify when recipient is suspended' do recipient.suspend! - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end context 'for direct messages' do @@ -61,7 +59,7 @@ RSpec.describe NotifyService, type: :service do let(:enabled) { true } it 'does not notify' do - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end context 'if the message chain is initiated by recipient, but is not direct message' do @@ -70,7 +68,7 @@ RSpec.describe NotifyService, type: :service do let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does not notify' do - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end end @@ -81,7 +79,7 @@ RSpec.describe NotifyService, type: :service do let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: dummy_reply)) } it 'does not notify' do - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end end @@ -91,7 +89,7 @@ RSpec.describe NotifyService, type: :service do let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does notify' do - is_expected.to change(Notification, :count) + expect { subject }.to change(Notification, :count) end end end @@ -100,7 +98,7 @@ RSpec.describe NotifyService, type: :service do let(:enabled) { false } it 'does notify' do - is_expected.to change(Notification, :count) + expect { subject }.to change(Notification, :count) end end end @@ -112,17 +110,17 @@ RSpec.describe NotifyService, type: :service do it 'shows reblogs by default' do recipient.follow!(sender) - is_expected.to change(Notification, :count) + expect { subject }.to change(Notification, :count) end it 'shows reblogs when explicitly enabled' do recipient.follow!(sender, reblogs: true) - is_expected.to change(Notification, :count) + expect { subject }.to change(Notification, :count) end it 'shows reblogs when disabled' do recipient.follow!(sender, reblogs: false) - is_expected.to change(Notification, :count) + expect { subject }.to change(Notification, :count) end end @@ -134,12 +132,12 @@ RSpec.describe NotifyService, type: :service do it 'does not notify when conversation is muted' do recipient.mute_conversation!(activity.status.conversation) - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end it 'does not notify when it is a reply to a blocked user' do recipient.block!(asshole) - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end end @@ -147,7 +145,7 @@ RSpec.describe NotifyService, type: :service do let(:sender) { recipient } it 'does not notify when recipient is the sender' do - is_expected.to_not change(Notification, :count) + expect { subject }.to_not change(Notification, :count) end end @@ -163,7 +161,7 @@ RSpec.describe NotifyService, type: :service do let(:enabled) { true } it 'sends email' do - is_expected.to change(ActionMailer::Base.deliveries, :count).by(1) + expect { subject }.to change(ActionMailer::Base.deliveries, :count).by(1) end end @@ -171,7 +169,7 @@ RSpec.describe NotifyService, type: :service do let(:enabled) { false } it "doesn't send email" do - is_expected.to_not change(ActionMailer::Base.deliveries, :count).from(0) + expect { subject }.to_not change(ActionMailer::Base.deliveries, :count).from(0) end end end diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index cf7eb257a..5d45e4ffd 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -5,9 +5,7 @@ RSpec.describe SuspendAccountService, type: :service do let!(:local_follower) { Fabricate(:user, current_sign_in_at: 1.hour.ago).account } let!(:list) { Fabricate(:list, account: local_follower) } - subject do - -> { described_class.new.call(account) } - end + subject { described_class.new.call(account) } before do allow(FeedManager.instance).to receive(:unmerge_from_home).and_return(nil) @@ -18,13 +16,13 @@ RSpec.describe SuspendAccountService, type: :service do end it "unmerges from local followers' feeds" do - subject.call + subject expect(FeedManager.instance).to have_received(:unmerge_from_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:unmerge_from_list).with(account, list) end it 'marks account as suspended' do - is_expected.to change { account.suspended? }.from(false).to(true) + expect { subject }.to change { account.suspended? }.from(false).to(true) end end @@ -51,7 +49,7 @@ RSpec.describe SuspendAccountService, type: :service do end it 'sends an update actor to followers and reporters' do - subject.call + subject expect(a_request(:post, remote_follower.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once expect(a_request(:post, remote_reporter.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once end @@ -77,7 +75,7 @@ RSpec.describe SuspendAccountService, type: :service do end it 'sends a reject follow' do - subject.call + subject expect(a_request(:post, account.inbox_url).with { |req| match_reject_follow_request(req, account, local_followee) }).to have_been_made.once end end diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb index 0593beb6f..3ac4cc085 100644 --- a/spec/services/unsuspend_account_service_spec.rb +++ b/spec/services/unsuspend_account_service_spec.rb @@ -5,9 +5,7 @@ RSpec.describe UnsuspendAccountService, type: :service do let!(:local_follower) { Fabricate(:user, current_sign_in_at: 1.hour.ago).account } let!(:list) { Fabricate(:list, account: local_follower) } - subject do - -> { described_class.new.call(account) } - end + subject { described_class.new.call(account) } before do allow(FeedManager.instance).to receive(:merge_into_home).and_return(nil) @@ -33,7 +31,7 @@ RSpec.describe UnsuspendAccountService, type: :service do end it 'marks account as unsuspended' do - is_expected.to change { account.suspended? }.from(true).to(false) + expect { subject }.to change { account.suspended? }.from(true).to(false) end include_examples 'common behavior' do @@ -47,13 +45,13 @@ RSpec.describe UnsuspendAccountService, type: :service do end it "merges back into local followers' feeds" do - subject.call + subject expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) end it 'sends an update actor to followers and reporters' do - subject.call + subject expect(a_request(:post, remote_follower.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once expect(a_request(:post, remote_reporter.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once end @@ -75,18 +73,18 @@ RSpec.describe UnsuspendAccountService, type: :service do end it 're-fetches the account' do - subject.call + subject expect(resolve_account_service).to have_received(:call).with(account) end it "merges back into local followers' feeds" do - subject.call + subject expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) end it 'marks account as unsuspended' do - is_expected.to change { account.suspended? }.from(true).to(false) + expect { subject }.to change { account.suspended? }.from(true).to(false) end end @@ -99,18 +97,18 @@ RSpec.describe UnsuspendAccountService, type: :service do end it 're-fetches the account' do - subject.call + subject expect(resolve_account_service).to have_received(:call).with(account) end it "does not merge back into local followers' feeds" do - subject.call + subject expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) end it 'does not mark the account as unsuspended' do - is_expected.not_to change { account.suspended? } + expect { subject }.not_to change { account.suspended? } end end @@ -120,12 +118,12 @@ RSpec.describe UnsuspendAccountService, type: :service do end it 're-fetches the account' do - subject.call + subject expect(resolve_account_service).to have_received(:call).with(account) end it "does not merge back into local followers' feeds" do - subject.call + subject expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) end diff --git a/spec/services/update_status_service_spec.rb b/spec/services/update_status_service_spec.rb index 78cc89cd4..71a73be5b 100644 --- a/spec/services/update_status_service_spec.rb +++ b/spec/services/update_status_service_spec.rb @@ -3,6 +3,23 @@ require 'rails_helper' RSpec.describe UpdateStatusService, type: :service do subject { described_class.new } + context 'when nothing changes' do + let!(:status) { Fabricate(:status, text: 'Foo', language: 'en') } + + before do + allow(ActivityPub::DistributionWorker).to receive(:perform_async) + subject.call(status, status.account_id, text: 'Foo') + end + + it 'does not create an edit' do + expect(status.reload.edits).to be_empty + end + + it 'does not notify anyone' do + expect(ActivityPub::DistributionWorker).to_not have_received(:perform_async) + end + end + context 'when text changes' do let!(:status) { Fabricate(:status, text: 'Foo') } let(:preview_card) { Fabricate(:preview_card) } diff --git a/spec/validators/status_length_validator_spec.rb b/spec/validators/status_length_validator_spec.rb index 643ea6d22..4c80a59e6 100644 --- a/spec/validators/status_length_validator_spec.rb +++ b/spec/validators/status_length_validator_spec.rb @@ -55,6 +55,13 @@ describe StatusLengthValidator do expect(status.errors).to have_received(:add) end + it 'does not count overly long URLs as 23 characters flat' do + text = "http://example.com/valid?#{'#foo?' * 1000}" + status = double(spoiler_text: '', text: text, errors: double(add: nil), local?: true, reblog?: false) + subject.validate(status) + expect(status.errors).to have_received(:add) + end + it 'counts only the front part of remote usernames' do username = '@alice' chars = StatusLengthValidator::MAX_CHARS - 1 - username.length @@ -64,5 +71,13 @@ describe StatusLengthValidator do subject.validate(status) expect(status.errors).to_not have_received(:add) end + + it 'does count both parts of remote usernames for overly long domains' do + text = "@alice@#{'b' * 500}.com" + status = double(spoiler_text: '', text: text, errors: double(add: nil), local?: true, reblog?: false) + + subject.validate(status) + expect(status.errors).to have_received(:add) + end end end diff --git a/yarn.lock b/yarn.lock index 1c0eee6f4..d913e470e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1490,10 +1490,10 @@ lz-string "^1.4.4" pretty-format "^27.0.2" -"@testing-library/jest-dom@^5.16.2": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.2.tgz#f329b36b44aa6149cd6ced9adf567f8b6aa1c959" - integrity sha512-6ewxs1MXWwsBFZXIk4nKKskWANelkdUehchEOokHsN8X7c2eKXGw+77aRV63UU8f/DTSVUPLaGxdrj4lN7D/ug== +"@testing-library/jest-dom@^5.16.3": + version "5.16.3" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.3.tgz#b76851a909586113c20486f1679ffb4d8ec27bfa" + integrity sha512-u5DfKj4wfSt6akfndfu1eG06jsdyA/IUrlX2n3pyq5UXgXMhXY+NJb8eNK/7pqPWAhCKsCGWDdDO0zKMKAYkEA== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" @@ -2366,13 +2366,13 @@ babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" -babel-loader@^8.2.3: - version "8.2.3" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" - integrity sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw== +babel-loader@^8.2.4: + version "8.2.4" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.4.tgz#95f5023c791b2e9e2ca6f67b0984f39c82ff384b" + integrity sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A== dependencies: find-cache-dir "^3.3.1" - loader-utils "^1.4.0" + loader-utils "^2.0.0" make-dir "^3.1.0" schema-utils "^2.6.5" @@ -2948,7 +2948,7 @@ caniuse-api@^3.0.0: caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001271, caniuse-lite@^1.0.30001286: version "1.0.30001310" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001310.tgz" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001310.tgz#da02cd07432c9eece6992689d1b84ca18139eea8" integrity sha512-cb9xTV8k9HTIUA3GnPUJCk0meUnrHL5gy5QePfDjxHyNBcnzPzrHFv5GqfP7ue5b1ZyzZL0RJboD6hQlPXjhjg== chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: @@ -8661,10 +8661,10 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4" - integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A== +prettier@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17" + integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A== pretty-format@^25.2.1, pretty-format@^25.5.0: version "25.5.0" @@ -11546,10 +11546,10 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1: - version "17.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" - integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== +yargs@^17.4.0: + version "17.4.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.0.tgz#9fc9efc96bd3aa2c1240446af28499f0e7593d00" + integrity sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA== dependencies: cliui "^7.0.2" escalade "^3.1.1"